From 61044c81e965213e128240f4d53543f41178c121 Mon Sep 17 00:00:00 2001 From: RheeseyB <1044774+Rheeseyb@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:26:59 +0100 Subject: [PATCH 1/3] fix(editor) don't render when code ahead --- editor/src/components/assets.ts | 35 +++++++- editor/src/templates/editor.tsx | 143 +++++++++++++++++--------------- 2 files changed, 109 insertions(+), 69 deletions(-) diff --git a/editor/src/components/assets.ts b/editor/src/components/assets.ts index 91ac6a474218..d5a662dad7fe 100644 --- a/editor/src/components/assets.ts +++ b/editor/src/components/assets.ts @@ -7,7 +7,12 @@ import type { AssetFile, ParseSuccess, } from '../core/shared/project-file-types' -import { directory, isDirectory, isImageFile } from '../core/shared/project-file-types' +import { + directory, + isDirectory, + isImageFile, + RevisionsState, +} from '../core/shared/project-file-types' import { isTextFile, isParseSuccess, isAssetFile } from '../core/shared/project-file-types' import Utils from '../utils/utils' import { dropLeadingSlash } from './filebrowser/filepath-utils' @@ -28,6 +33,8 @@ import type { ProjectContentsTree, PathAndFileEntry, } from 'utopia-shared/src/types/assets' +import { filtered, fromField, fromTypeGuard } from '../core/shared/optics/optic-creators' +import { anyBy, toArrayOf } from '../core/shared/optics/optic-utilities' export type { AssetFileWithFileName, ProjectContentTreeRoot, @@ -391,6 +398,32 @@ export const contentsTreeOptic: Optic }, ) +export function anyCodeAhead(tree: ProjectContentTreeRoot): boolean { + const revisionsStateOptic = contentsTreeOptic + .compose(fromField('file')) + .compose(fromTypeGuard(isTextFile)) + .compose(fromField('fileContents')) + .compose(filtered((f) => f.parsed.type === 'PARSE_SUCCESS')) + .compose(fromField('revisionsState')) + + return anyBy( + revisionsStateOptic, + (revisionsState) => { + switch (revisionsState) { + case 'BOTH_MATCH': + case 'PARSED_AHEAD': + return false + case 'CODE_AHEAD': + case 'CODE_AHEAD_BUT_PLEASE_TELL_VSCODE_ABOUT_IT': + return true + default: + assertNever(revisionsState) + } + }, + tree, + ) +} + export function walkContentsTreeForParseSuccess( tree: ProjectContentTreeRoot, onElement: (fullPath: string, parseSuccess: ParseSuccess) => void, diff --git a/editor/src/templates/editor.tsx b/editor/src/templates/editor.tsx index d64b56adfeea..7c6c83d8a0b7 100644 --- a/editor/src/templates/editor.tsx +++ b/editor/src/templates/editor.tsx @@ -127,6 +127,7 @@ import { hasReactRouterErrorBeenLogged } from '../core/shared/runtime-report-log import { InitialOnlineState, startOnlineStatusPolling } from '../components/editor/online-status' import { useAnimate } from 'framer-motion' import { AnimationContext } from '../components/canvas/ui-jsx-canvas-renderer/animation-context' +import { anyCodeAhead } from '../components/assets' if (PROBABLY_ELECTRON) { let { webFrame } = requireElectron() @@ -450,7 +451,12 @@ export class Editor { const reactRouterErrorPreviouslyLogged = hasReactRouterErrorBeenLogged() const runDomWalker = shouldRunDOMWalker(dispatchedActions, dispatchResult) - const shouldRerender = !dispatchResult.nothingChanged || runDomWalker + + const somethingChanged = !dispatchResult.nothingChanged + + const shouldRerender = + (somethingChanged || runDomWalker) && + anyCodeAhead(dispatchResult.unpatchedEditor.projectContents) const updateId = canvasUpdateId++ if (shouldRerender) { @@ -467,89 +473,90 @@ export class Editor { }) }) }) + } - // run the dom-walker - if (runDomWalker) { - const domWalkerDispatchResult = runDomWalkerAndSaveResults( + // run the dom-walker + if (runDomWalker) { + const domWalkerDispatchResult = runDomWalkerAndSaveResults( + this.boundDispatch, + this.domWalkerMutableState, + this.storedState, + this.spyCollector, + ElementsToRerenderGLOBAL.current, + ) + + if (domWalkerDispatchResult != null) { + this.storedState = domWalkerDispatchResult + entireUpdateFinished = Promise.all([ + entireUpdateFinished, + domWalkerDispatchResult.entireUpdateFinished, + ]) + } + } + + // true up groups if needed + if (this.storedState.unpatchedEditor.trueUpElementsAfterDomWalkerRuns.length > 0) { + // updated editor with trued up groups + Measure.taskTime(`Group true up ${updateId}`, () => { + const projectContentsBeforeGroupTrueUp = this.storedState.unpatchedEditor.projectContents + const dispatchResultWithTruedUpGroups = editorDispatchActionRunner( this.boundDispatch, - this.domWalkerMutableState, + [{ action: 'TRUE_UP_ELEMENTS' }], this.storedState, this.spyCollector, - ElementsToRerenderGLOBAL.current, ) + this.storedState = dispatchResultWithTruedUpGroups + + entireUpdateFinished = Promise.all([ + entireUpdateFinished, + dispatchResultWithTruedUpGroups.entireUpdateFinished, + ]) - if (domWalkerDispatchResult != null) { - this.storedState = domWalkerDispatchResult - entireUpdateFinished = Promise.all([ - entireUpdateFinished, - domWalkerDispatchResult.entireUpdateFinished, - ]) + if ( + projectContentsBeforeGroupTrueUp === this.storedState.unpatchedEditor.projectContents + ) { + // no group-related re-render / re-measure is needed, bail out + return } - } - // true up groups if needed - if (this.storedState.unpatchedEditor.trueUpElementsAfterDomWalkerRuns.length > 0) { - // updated editor with trued up groups - Measure.taskTime(`Group true up ${updateId}`, () => { - const projectContentsBeforeGroupTrueUp = - this.storedState.unpatchedEditor.projectContents - const dispatchResultWithTruedUpGroups = editorDispatchActionRunner( + // re-render the canvas + Measure.taskTime(`Canvas re-render because of groups ${updateId}`, () => { + ElementsToRerenderGLOBAL.current = fixElementsToRerender( + this.storedState.patchedEditor.canvas.elementsToRerender, + dispatchedActions, + ) // Mutation! + + ReactDOM.flushSync(() => { + ReactDOM.unstable_batchedUpdates(() => { + this.canvasStore.setState( + patchedStoreFromFullStore(this.storedState, 'canvas-store'), + ) + }) + }) + }) + + // re-run the dom-walker + Measure.taskTime(`Dom walker re-run because of groups ${updateId}`, () => { + const domWalkerDispatchResult = runDomWalkerAndSaveResults( this.boundDispatch, - [{ action: 'TRUE_UP_ELEMENTS' }], + this.domWalkerMutableState, this.storedState, this.spyCollector, + ElementsToRerenderGLOBAL.current, ) - this.storedState = dispatchResultWithTruedUpGroups - - entireUpdateFinished = Promise.all([ - entireUpdateFinished, - dispatchResultWithTruedUpGroups.entireUpdateFinished, - ]) - if ( - projectContentsBeforeGroupTrueUp === this.storedState.unpatchedEditor.projectContents - ) { - // no group-related re-render / re-measure is needed, bail out - return + if (domWalkerDispatchResult != null) { + this.storedState = domWalkerDispatchResult + entireUpdateFinished = Promise.all([ + entireUpdateFinished, + domWalkerDispatchResult.entireUpdateFinished, + ]) } - - // re-render the canvas - Measure.taskTime(`Canvas re-render because of groups ${updateId}`, () => { - ElementsToRerenderGLOBAL.current = fixElementsToRerender( - this.storedState.patchedEditor.canvas.elementsToRerender, - dispatchedActions, - ) // Mutation! - - ReactDOM.flushSync(() => { - ReactDOM.unstable_batchedUpdates(() => { - this.canvasStore.setState( - patchedStoreFromFullStore(this.storedState, 'canvas-store'), - ) - }) - }) - }) - - // re-run the dom-walker - Measure.taskTime(`Dom walker re-run because of groups ${updateId}`, () => { - const domWalkerDispatchResult = runDomWalkerAndSaveResults( - this.boundDispatch, - this.domWalkerMutableState, - this.storedState, - this.spyCollector, - ElementsToRerenderGLOBAL.current, - ) - - if (domWalkerDispatchResult != null) { - this.storedState = domWalkerDispatchResult - entireUpdateFinished = Promise.all([ - entireUpdateFinished, - domWalkerDispatchResult.entireUpdateFinished, - ]) - } - }) }) - } + }) + } + if (somethingChanged) { this.storedState = editorDispatchClosingOut( this.boundDispatch, dispatchedActions, From db36d50f9aad8077c12a1df99e9e7d3b6455f7e4 Mon Sep 17 00:00:00 2001 From: Sean Parsons Date: Wed, 10 Jul 2024 14:10:33 +0100 Subject: [PATCH 2/3] fix(editor) Tweaked the condition for shouldRerender. --- editor/src/templates/editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/templates/editor.tsx b/editor/src/templates/editor.tsx index 7c6c83d8a0b7..2be10ea88b5e 100644 --- a/editor/src/templates/editor.tsx +++ b/editor/src/templates/editor.tsx @@ -456,7 +456,7 @@ export class Editor { const shouldRerender = (somethingChanged || runDomWalker) && - anyCodeAhead(dispatchResult.unpatchedEditor.projectContents) + !anyCodeAhead(dispatchResult.unpatchedEditor.projectContents) const updateId = canvasUpdateId++ if (shouldRerender) { From 1c41ac1cc8707366cf9a23bf66cc0e621866e870 Mon Sep 17 00:00:00 2001 From: RheeseyB <1044774+Rheeseyb@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:12:43 +0100 Subject: [PATCH 3/3] fix(editor.tsx) Fix gating of dom walker, remove gating of dispatch closing out --- editor/src/templates/editor.tsx | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/editor/src/templates/editor.tsx b/editor/src/templates/editor.tsx index 2be10ea88b5e..83e2416f118d 100644 --- a/editor/src/templates/editor.tsx +++ b/editor/src/templates/editor.tsx @@ -476,7 +476,7 @@ export class Editor { } // run the dom-walker - if (runDomWalker) { + if (shouldRerender || runDomWalker) { const domWalkerDispatchResult = runDomWalkerAndSaveResults( this.boundDispatch, this.domWalkerMutableState, @@ -556,19 +556,17 @@ export class Editor { }) } - if (somethingChanged) { - this.storedState = editorDispatchClosingOut( - this.boundDispatch, - dispatchedActions, - oldEditorState, - { - ...this.storedState, - entireUpdateFinished: entireUpdateFinished, - nothingChanged: dispatchResult.nothingChanged, - }, - reactRouterErrorPreviouslyLogged, - ) - } + this.storedState = editorDispatchClosingOut( + this.boundDispatch, + dispatchedActions, + oldEditorState, + { + ...this.storedState, + entireUpdateFinished: entireUpdateFinished, + nothingChanged: dispatchResult.nothingChanged, + }, + reactRouterErrorPreviouslyLogged, + ) Measure.taskTime(`Update Editor ${updateId}`, () => { ReactDOM.flushSync(() => {