From 739c9f84cfae345bda737c9b4364ee4758d984f9 Mon Sep 17 00:00:00 2001 From: abose Date: Thu, 8 Jan 2026 19:40:29 +0530 Subject: [PATCH] fix: sometimes the html live doc goes into an inconsistant state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Fixed BaseServer.remove() Bug (BaseServer.js:149) Before: Used lookup result as key for deletion After: Correctly uses the key string returned by _documentKey() This fix ensures LiveHTMLDocuments are actually removed from the cache when closed, preventing memory leaks and stale references. 2. Fixed _markText() Function (HTMLInstrumentation.js:823-847) ✩ PRIMARY FIX Key change: Now detects when an editor has no marks and allows marking even when fullBuild = false The logic now: - Checks if the editor has any existing tagID marks - Allows marking if: DOM is a full build OR editor has no marks - Still refuses to re-mark if editor already has marks and DOM is stale (preserves safety) This solves the core issue where switching editor panes or reopening documents would leave the new editor without marks. 3. Added Defensive Fallback (HTMLInstrumentation.js:758-769) Added a safety net that: - Detects when markCache is empty but DOM has nodes - Automatically forces re-marking if this happens - Logs a warning so we know it triggered How This Fixes the Issue The error "couldn't find existing mark for tagID" occurred because: 1. Document edited -> cache has fullBuild = false 2. User switches panes -> new editor has NO marks 3. Old _markText() refused to mark the new editor 4. generateInstrumentedHTML() tried to use non-existent marks -> ERROR Now: - _markText() detects the editor has no marks and marks it anyway (even with fullBuild = false) - If marks are somehow still missing, the defensive fallback catches it - BaseServer properly cleans up old documents --- .../language/HTMLInstrumentation.js | 26 ++++++++++++++++++- src/LiveDevelopment/Servers/BaseServer.js | 4 +-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/LiveDevelopment/MultiBrowserImpl/language/HTMLInstrumentation.js b/src/LiveDevelopment/MultiBrowserImpl/language/HTMLInstrumentation.js index 13934ac7ee..401a60787b 100644 --- a/src/LiveDevelopment/MultiBrowserImpl/language/HTMLInstrumentation.js +++ b/src/LiveDevelopment/MultiBrowserImpl/language/HTMLInstrumentation.js @@ -754,6 +754,19 @@ define(function (require, exports, module) { markCache[mark.tagID] = { mark: mark, range: mark.find() }; } }); + + // DEFENSIVE CHECK: If we expected marks but found none, force re-marking + if (Object.keys(markCache).length === 0 && dom.nodeMap && Object.keys(dom.nodeMap).length > 0) { + console.warn("generateInstrumentedHTML: No marks found, forcing re-mark"); + _markTextFromDOM(editor, dom); + // Rebuild markCache with new marks + markCache = {}; + editor._codeMirror.getAllMarks().forEach(function (mark) { + if (mark.tagID) { + markCache[mark.tagID] = { mark: mark, range: mark.find() }; + } + }); + } } // Walk through the dom nodes and insert the 'data-brackets-id' attribute at the @@ -827,7 +840,18 @@ define(function (require, exports, module) { if (!dom) { console.error("Couldn't find the dom for " + editor.document.file.fullPath); return; - } else if (!dom.fullBuild) { + } + + // Check if editor has any tagID marks + var existingMarks = editor._codeMirror.getAllMarks().filter(function (mark) { + return mark.hasOwnProperty("tagID"); + }); + + // Allow marking if: + // 1. DOM is a full build (offsets are accurate), OR + // 2. Editor has no marks (new editor instance needs marking) + if (!dom.fullBuild && existingMarks.length > 0) { + // Editor already has marks and DOM is stale - don't re-mark console.error("Tried to mark text from a stale DOM for " + editor.document.file.fullPath); return; } diff --git a/src/LiveDevelopment/Servers/BaseServer.js b/src/LiveDevelopment/Servers/BaseServer.js index 2b5ad1a68d..11d047a319 100644 --- a/src/LiveDevelopment/Servers/BaseServer.js +++ b/src/LiveDevelopment/Servers/BaseServer.js @@ -146,9 +146,9 @@ define(function (require, exports, module) { return; } - var key = this._liveDocuments[this._documentKey(liveDocument.doc.file.fullPath)]; + const key = this._documentKey(liveDocument.doc.file.fullPath); - if (key) { + if (this._liveDocuments[key]) { delete this._liveDocuments[key]; } };