refactor: minor clean up of editor (#43513)
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							bfb70e422e
						
					
				
				
					commit
					222fc3f255
				
			| @@ -35,10 +35,6 @@ | |||||||
|   color: var(--secondary-background); |   color: var(--secondary-background); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* [widgetid='my.overlay.widget'] { |  | ||||||
|   padding: 10px; |  | ||||||
| } */ |  | ||||||
|  |  | ||||||
| .editor-upper-jaw, | .editor-upper-jaw, | ||||||
| .editor-lower-jaw { | .editor-lower-jaw { | ||||||
|   padding: 15px 15px 15px 0px; |   padding: 15px 15px 15px 0px; | ||||||
|   | |||||||
| @@ -74,16 +74,16 @@ interface EditorProps { | |||||||
| interface EditorProperties { | interface EditorProperties { | ||||||
|   editor?: editor.IStandaloneCodeEditor; |   editor?: editor.IStandaloneCodeEditor; | ||||||
|   model?: editor.ITextModel; |   model?: editor.ITextModel; | ||||||
|   viewZoneId: string; |   descriptionZoneId: string; | ||||||
|   startEditDecId: string; |   startEditDecId: string; | ||||||
|   endEditDecId: string; |   endEditDecId: string; | ||||||
|   insideEditDecId: string; |   insideEditDecId: string; | ||||||
|   viewZoneHeight: number; |   descriptionZoneHeight: number; | ||||||
|   outputZoneHeight: number; |   outputZoneHeight: number; | ||||||
|   outputZoneId: string; |   outputZoneId: string; | ||||||
|   descriptionNode?: HTMLDivElement; |   descriptionNode?: HTMLDivElement; | ||||||
|   outputNode?: HTMLDivElement; |   outputNode?: HTMLDivElement; | ||||||
|   overlayWidget?: editor.IOverlayWidget; |   descriptionWidget?: editor.IOverlayWidget; | ||||||
|   outputWidget?: editor.IOverlayWidget; |   outputWidget?: editor.IOverlayWidget; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -174,11 +174,11 @@ const toLastLine = (range: RangeType) => { | |||||||
|  |  | ||||||
| // TODO: properly initialise data with values not null | // TODO: properly initialise data with values not null | ||||||
| const initialData: EditorProperties = { | const initialData: EditorProperties = { | ||||||
|   viewZoneId: '', |   descriptionZoneId: '', | ||||||
|   startEditDecId: '', |   startEditDecId: '', | ||||||
|   endEditDecId: '', |   endEditDecId: '', | ||||||
|   insideEditDecId: '', |   insideEditDecId: '', | ||||||
|   viewZoneHeight: 0, |   descriptionZoneHeight: 0, | ||||||
|   outputZoneId: '', |   outputZoneId: '', | ||||||
|   outputZoneHeight: 0 |   outputZoneHeight: 0 | ||||||
| }; | }; | ||||||
| @@ -207,8 +207,6 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|   // automatically be first, but  if there's jsx and js (for some reason) it |   // automatically be first, but  if there's jsx and js (for some reason) it | ||||||
|   //  will be [jsx, js]. |   //  will be [jsx, js]. | ||||||
|  |  | ||||||
|   // NOTE: the ARIA state is controlled by fileKey, so changes to it must |  | ||||||
|   // trigger a re-render.  Hence state: |  | ||||||
|   const options: editor.IStandaloneEditorConstructionOptions = { |   const options: editor.IStandaloneEditorConstructionOptions = { | ||||||
|     fontSize: 18, |     fontSize: 18, | ||||||
|     scrollBeyondLastLine: false, |     scrollBeyondLastLine: false, | ||||||
| @@ -400,99 +398,89 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|         }; |         }; | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
|       const domNode = createDescription(editor); |       const descriptionNode = createDescription(editor); | ||||||
|  |  | ||||||
|       const outputNode = createOutputNode(editor); |       const outputNode = createOutputNode(editor); | ||||||
|  |  | ||||||
|       const overlayWidget = createWidget( |       const descriptionWidget = createWidget( | ||||||
|         'my.overlay.widget', |         'description.widget', | ||||||
|         domNode, |         descriptionNode, | ||||||
|         getViewZoneTop |         getDescriptionZoneTop | ||||||
|       ); |       ); | ||||||
|       data.overlayWidget = overlayWidget; |       data.descriptionWidget = descriptionWidget; | ||||||
|       const outputWidget = createWidget( |       const outputWidget = createWidget( | ||||||
|         'my.output.widget', |         'output.widget', | ||||||
|         outputNode, |         outputNode, | ||||||
|         getOutputZoneTop |         getOutputZoneTop | ||||||
|       ); |       ); | ||||||
|       data.outputWidget = outputWidget; |       data.outputWidget = outputWidget; | ||||||
|  |  | ||||||
|       editor.addOverlayWidget(overlayWidget); |       editor.addOverlayWidget(descriptionWidget); | ||||||
|  |  | ||||||
|       // TODO: order of insertion into the DOM probably matters, revisit once |       // TODO: order of insertion into the DOM probably matters, revisit once | ||||||
|       // the tabs have been fixed! |       // the tabs have been fixed! | ||||||
|  |  | ||||||
|       editor.addOverlayWidget(outputWidget); |       editor.addOverlayWidget(outputWidget); | ||||||
|  |  | ||||||
|       editor.changeViewZones(viewZoneCallback); |       editor.changeViewZones(descriptionZoneCallback); | ||||||
|       editor.changeViewZones(outputZoneCallback); |       editor.changeViewZones(outputZoneCallback); | ||||||
|  |  | ||||||
|       editor.onDidScrollChange(() => { |       editor.onDidScrollChange(() => { | ||||||
|         editor.layoutOverlayWidget(overlayWidget); |         editor.layoutOverlayWidget(descriptionWidget); | ||||||
|         editor.layoutOverlayWidget(outputWidget); |         editor.layoutOverlayWidget(outputWidget); | ||||||
|       }); |       }); | ||||||
|       showEditableRegion(editableBoundaries); |       showEditableRegion(editableBoundaries); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const viewZoneCallback = (changeAccessor: editor.IViewZoneChangeAccessor) => { |   const descriptionZoneCallback = ( | ||||||
|  |     changeAccessor: editor.IViewZoneChangeAccessor | ||||||
|  |   ) => { | ||||||
|     const editor = data.editor; |     const editor = data.editor; | ||||||
|     if (!editor) return; |     if (!editor) return; | ||||||
|     // TODO: is there any point creating this here? I know it's cached, but |  | ||||||
|     // would it not be better just sourced from the overlayWidget? |  | ||||||
|     const domNode = createDescription(editor); |     const domNode = createDescription(editor); | ||||||
|  |  | ||||||
|     // make sure the overlayWidget has resized before using it to set the height |     // make sure the overlayWidget has resized before using it to set the height | ||||||
|  |  | ||||||
|     domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`; |     domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`; | ||||||
|  |  | ||||||
|     // TODO: set via onComputedHeight? |     data.descriptionZoneHeight = domNode.offsetHeight; | ||||||
|     data.viewZoneHeight = domNode.offsetHeight; |  | ||||||
|  |  | ||||||
|     const background = document.createElement('div'); |  | ||||||
|     // background.style.background = 'lightgreen'; |  | ||||||
|  |  | ||||||
|     // We have to wait for the viewZone to finish rendering before adjusting the |     // We have to wait for the viewZone to finish rendering before adjusting the | ||||||
|     // position of the overlayWidget (i.e. trigger it via onComputedHeight). If |     // position of the overlayWidget (i.e. trigger it via onComputedHeight). If | ||||||
|     // not the editor may report the wrong value for position of the lines. |     // not the editor may report the wrong value for position of the lines. | ||||||
|     const viewZone = { |     const viewZone = { | ||||||
|       afterLineNumber: getLineAfterViewZone() - 1, |       afterLineNumber: getLineAfterDescriptionZone() - 1, | ||||||
|       heightInPx: domNode.offsetHeight, |       heightInPx: domNode.offsetHeight, | ||||||
|       domNode: background, |       domNode: document.createElement('div'), | ||||||
|       onComputedHeight: () => |       onComputedHeight: () => | ||||||
|         data.overlayWidget && editor.layoutOverlayWidget(data.overlayWidget) |         data.descriptionWidget && | ||||||
|  |         editor.layoutOverlayWidget(data.descriptionWidget) | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     data.viewZoneId = changeAccessor.addZone(viewZone); |     data.descriptionZoneId = changeAccessor.addZone(viewZone); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   // TODO: this is basically the same as viewZoneCallback, so DRY them out. |  | ||||||
|   const outputZoneCallback = ( |   const outputZoneCallback = ( | ||||||
|     changeAccessor: editor.IViewZoneChangeAccessor |     changeAccessor: editor.IViewZoneChangeAccessor | ||||||
|   ) => { |   ) => { | ||||||
|     const editor = data.editor; |     const editor = data.editor; | ||||||
|     if (!editor) return; |     if (!editor) return; | ||||||
|     // TODO: is there any point creating this here? I know it's cached, but |  | ||||||
|     // would it not be better just sourced from the overlayWidget? |  | ||||||
|     const outputNode = createOutputNode(editor); |     const outputNode = createOutputNode(editor); | ||||||
|  |  | ||||||
|     // make sure the overlayWidget has resized before using it to set the height |     // make sure the overlayWidget has resized before using it to set the height | ||||||
|  |  | ||||||
|     outputNode.style.width = `${editor.getLayoutInfo().contentWidth}px`; |     outputNode.style.width = `${editor.getLayoutInfo().contentWidth}px`; | ||||||
|  |  | ||||||
|     // TODO: set via onComputedHeight? |  | ||||||
|     data.outputZoneHeight = outputNode.offsetHeight; |     data.outputZoneHeight = outputNode.offsetHeight; | ||||||
|  |  | ||||||
|     const background = document.createElement('div'); |  | ||||||
|     // background.style.background = 'lightpink'; |  | ||||||
|  |  | ||||||
|     // We have to wait for the viewZone to finish rendering before adjusting the |     // We have to wait for the viewZone to finish rendering before adjusting the | ||||||
|     // position of the overlayWidget (i.e. trigger it via onComputedHeight). If |     // position of the overlayWidget (i.e. trigger it via onComputedHeight). If | ||||||
|     // not the editor may report the wrong value for position of the lines. |     // not the editor may report the wrong value for position of the lines. | ||||||
|     const viewZone = { |     const viewZone = { | ||||||
|       afterLineNumber: getLineAfterEditableRegion() - 1, |       afterLineNumber: getLineAfterEditableRegion() - 1, | ||||||
|       heightInPx: outputNode.offsetHeight, |       heightInPx: outputNode.offsetHeight, | ||||||
|       domNode: background, |       domNode: document.createElement('div'), | ||||||
|       onComputedHeight: () => |       onComputedHeight: () => | ||||||
|         data.outputWidget && editor.layoutOverlayWidget(data.outputWidget) |         data.outputWidget && editor.layoutOverlayWidget(data.outputWidget) | ||||||
|     }; |     }; | ||||||
| @@ -505,7 +493,6 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     const { description, title } = props; |     const { description, title } = props; | ||||||
|     const jawHeading = document.createElement('h3'); |     const jawHeading = document.createElement('h3'); | ||||||
|     jawHeading.innerText = title; |     jawHeading.innerText = title; | ||||||
|     // TODO: var was used here. Should it? |  | ||||||
|     const domNode = document.createElement('div'); |     const domNode = document.createElement('div'); | ||||||
|     const desc = document.createElement('div'); |     const desc = document.createElement('div'); | ||||||
|     const descContainer = document.createElement('div'); |     const descContainer = document.createElement('div'); | ||||||
| @@ -515,8 +502,6 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     descContainer.appendChild(jawHeading); |     descContainer.appendChild(jawHeading); | ||||||
|     descContainer.appendChild(desc); |     descContainer.appendChild(desc); | ||||||
|     desc.innerHTML = description; |     desc.innerHTML = description; | ||||||
|     // desc.style.background = 'white'; |  | ||||||
|     // domNode.style.background = 'lightgreen'; |  | ||||||
|     // TODO: the solution is probably just to use an overlay that's forced to |     // TODO: the solution is probably just to use an overlay that's forced to | ||||||
|     // follow the decorations. |     // follow the decorations. | ||||||
|     // TODO: this is enough for Firefox, but Chrome needs more before the |     // TODO: this is enough for Firefox, but Chrome needs more before the | ||||||
| @@ -526,12 +511,10 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     domNode.style.zIndex = '10'; |     domNode.style.zIndex = '10'; | ||||||
|  |  | ||||||
|     domNode.setAttribute('aria-hidden', 'true'); |     domNode.setAttribute('aria-hidden', 'true'); | ||||||
|  |  | ||||||
|     // domNode.style.background = 'lightYellow'; |  | ||||||
|     domNode.style.left = `${editor.getLayoutInfo().contentLeft}px`; |     domNode.style.left = `${editor.getLayoutInfo().contentLeft}px`; | ||||||
|     domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`; |     domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`; | ||||||
|  |  | ||||||
|     domNode.style.top = getViewZoneTop(); |     domNode.style.top = getDescriptionZoneTop(); | ||||||
|     data.descriptionNode = domNode; |     data.descriptionNode = domNode; | ||||||
|     return domNode; |     return domNode; | ||||||
|   } |   } | ||||||
| @@ -672,14 +655,14 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // NOTE: this is where the view zone *should* be, not necessarily were it |   // NOTE: this is where the view zone *should* be, not necessarily were it | ||||||
|   // currently is. (see getLineAfterViewZone) |   // currently is. (see getLineAfterDescriptionZone) | ||||||
|   // TODO: DRY this and getOutputZoneTop out. |   // TODO: DRY this and getOutputZoneTop out. | ||||||
|   function getViewZoneTop() { |   function getDescriptionZoneTop() { | ||||||
|     const editor = data.editor; |     const editor = data.editor; | ||||||
|     const heightDelta = data.viewZoneHeight; |     const heightDelta = data.descriptionZoneHeight; | ||||||
|     if (editor) { |     if (editor) { | ||||||
|       const top = `${ |       const top = `${ | ||||||
|         editor.getTopForLineNumber(getLineAfterViewZone()) - |         editor.getTopForLineNumber(getLineAfterDescriptionZone()) - | ||||||
|         heightDelta - |         heightDelta - | ||||||
|         editor.getScrollTop() |         editor.getScrollTop() | ||||||
|       }px`; |       }px`; | ||||||
| @@ -705,9 +688,7 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|  |  | ||||||
|   // It's not possible to directly access the current view zone so we track |   // It's not possible to directly access the current view zone so we track | ||||||
|   // the region it should cover instead. |   // the region it should cover instead. | ||||||
|   // TODO: DRY |   function getLineAfterDescriptionZone() { | ||||||
|   function getLineAfterViewZone() { |  | ||||||
|     // TODO: abstract away the data, ids etc. |  | ||||||
|     const range = data.model?.getDecorationRange(data.startEditDecId); |     const range = data.model?.getDecorationRange(data.startEditDecId); | ||||||
|     // if the first decoration is missing, this implies the region reaches the |     // if the first decoration is missing, this implies the region reaches the | ||||||
|     // start of the editor. |     // start of the editor. | ||||||
| @@ -731,7 +712,6 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     return monacoRef.current?.Range.lift(iRange); |     return monacoRef.current?.Range.lift(iRange); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   // TODO: TESTS! |  | ||||||
|   // Make 100% sure this is inclusive. |   // Make 100% sure this is inclusive. | ||||||
|   const getLinesBetweenRanges = ( |   const getLinesBetweenRanges = ( | ||||||
|     firstRange: RangeType, |     firstRange: RangeType, | ||||||
| @@ -785,7 +765,6 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   // TODO: do this once after _monaco has been created. |  | ||||||
|   const getStartOfEditor = () => |   const getStartOfEditor = () => | ||||||
|     monacoRef.current?.Range.lift({ |     monacoRef.current?.Range.lift({ | ||||||
|       startLineNumber: 1, |       startLineNumber: 1, | ||||||
| @@ -869,7 +848,6 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|       return newLines.map(({ range }) => range); |       return newLines.map(({ range }) => range); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // TODO refactor this mess |  | ||||||
|     // TODO this listener needs to be replaced on reset. |     // TODO this listener needs to be replaced on reset. | ||||||
|     model.onDidChangeContent(e => { |     model.onDidChangeContent(e => { | ||||||
|       // TODO: it would be nice if undoing could remove the warning, but |       // TODO: it would be nice if undoing could remove the warning, but | ||||||
| @@ -893,7 +871,7 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|       if (e.isUndoing) { |       if (e.isUndoing) { | ||||||
|         // TODO: can we be more targeted? Only update when they could get out of |         // TODO: can we be more targeted? Only update when they could get out of | ||||||
|         // sync |         // sync | ||||||
|         updateViewZone(); |         updateDescriptionZone(); | ||||||
|         updateOutputZone(); |         updateOutputZone(); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
| @@ -922,7 +900,7 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|       // have changed if a line has been added or removed |       // have changed if a line has been added or removed | ||||||
|       const handleDescriptionZoneChange = () => { |       const handleDescriptionZoneChange = () => { | ||||||
|         if (newLineRanges.length > 0 || deletedLine > 0) { |         if (newLineRanges.length > 0 || deletedLine > 0) { | ||||||
|           updateViewZone(); |           updateDescriptionZone(); | ||||||
|         } |         } | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
| @@ -1117,7 +1095,7 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     const editor = data.editor; |     const editor = data.editor; | ||||||
|     editor?.layout(); |     editor?.layout(); | ||||||
|     if (data.startEditDecId) { |     if (data.startEditDecId) { | ||||||
|       updateViewZone(); |       updateDescriptionZone(); | ||||||
|       updateOutputZone(); |       updateOutputZone(); | ||||||
|     } |     } | ||||||
|     // eslint-disable-next-line react-hooks/exhaustive-deps |     // eslint-disable-next-line react-hooks/exhaustive-deps | ||||||
| @@ -1132,11 +1110,11 @@ const Editor = (props: EditorProps): JSX.Element => { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   function updateViewZone() { |   function updateDescriptionZone() { | ||||||
|     const editor = data.editor; |     const editor = data.editor; | ||||||
|     editor?.changeViewZones(changeAccessor => { |     editor?.changeViewZones(changeAccessor => { | ||||||
|       changeAccessor.removeZone(data.viewZoneId); |       changeAccessor.removeZone(data.descriptionZoneId); | ||||||
|       viewZoneCallback(changeAccessor); |       descriptionZoneCallback(changeAccessor); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user