Keep a JSON dictionary's Modified marks when one of its tabs closes - #31
Merged
Merged
Conversation
SlddTextEditorProvider tore down two pieces of DOCUMENT state from webviewPanel.onDidDispose, which fires per PANEL: the per-URI "Modified" baseline (slddBaseline) and the cross-document-move source deleter (editorHub). Open one .sldd in two tabs or split it, close one, and the surviving tab lost every Modified mark while the document stayed open and edited — with no baseline, computeModified answers "nothing is modified" by design — and a move dragged out of that tab could no longer delete its originals. The on-open capture had the same panel/document confusion in the other direction: `initialized` was a local of resolveCustomTextEditor, so each tab believed it owed the capture, and captureBaseline OVERWRITES by URI. Opening a second tab on an already-edited dictionary therefore re-baselined against the edited text and cleared the marks in every tab at once. BinarySlddEditorProvider already carries both facts on its custom document (`views` / `baselineCaptured`) for exactly these reasons; a CustomTextEditorProvider has no such object, so the same two facts now live in a URI-keyed openDocs map, and the teardown runs only when the last tab of a document closes. Pinned by test-integration/suite/twoTabBaseline.test.ts, which drives the real provider in real VS Code with two panels on one document: the surviving tab still reports Status "Modified" after the other closes, and deleteFromSource still completes. Both assertions fail on the previous code.
This was referenced Sep 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #23 — the half of it that is a plain bug rather than a product decision.
The bug
SlddTextEditorProvidertore down two pieces of document state fromwebviewPanel.onDidDispose, which fires per panel:slddBaseline)editorHub)So opening one
.slddin two tabs (or splitting it) and closing one left the survivingtab with no baseline — and with no baseline
computeModifiedanswers "nothing is modified"by design, so every Modified mark vanished from a tab the user was still editing, unsaved
edits intact underneath. The same dispose dropped the source deleter, so a move dragged out
of the surviving tab silently failed to delete its originals.
The on-open capture had the same panel/document confusion in the other direction:
initializedwas a local ofresolveCustomTextEditor, so each tab believed it owed thecapture, and
captureBaselineoverwrites by URI. Opening a second tab on analready-edited dictionary re-baselined it against the edited text and cleared the marks in
every tab at once.
The fix
BinarySlddEditorProvideralready carries both facts on its custom document(
views/baselineCaptured, teardown guarded bydocument.views.size === 0) and itscomment names the same two failures. A
CustomTextEditorProviderhas no custom documentobject to hang state on, so the same two facts now live in a URI-keyed
openDocsmap inSlddTextEditorProvider.ts, and the teardown runs only when a document's last tabcloses. One rule, both paths.
Tests
test-integration/suite/twoTabBaseline.test.ts— real provider, real VS Code, two panels onone document. Both assertions fail on the previous code:
marks; after the first tab closes the survivor still reports
Status: "Modified"deleteFromSourcestill completes after one of two tabs closesNo existing test was modified.
npm run verifygreen (2480 unit tests, typecheck, bothbundles, leak check).
Qualified in the browser
Guardrail: a visible fix is not landed until it is checked from the customer's side. Measured
against the shipped
dist/webviewbundle in Chromium with VS Code's real palette, rows builtby the real
buildRows()from a real.sldd— the state this fix restores vs. the state thebug left behind, which differ only in the
modifiedNamesset the host posts:No console errors, no failed requests. Screenshots in
/tmp/dex-harness/issue23-*.png.Out of scope, captured for the maintainer rather than changed here: the mark's colour is the
hard-coded
#b45309with no theme token, which measures 3.54 on Dark Modern and 4.18 onHC Black — both below WCAG AA for normal text. Choosing a token is a design decision, so
it is on the daytime agenda, not in this PR.
Still open on #23
The reporter's own video is most likely the other mechanism: "Modified" means differs from
the last save, and with
files.autoSave: afterDelay(1000 ms default) the file saves abouta second after typing stops, the baseline is re-captured, and the marks clear on their own.
That behaviour is pinned by two existing tests and changing it is a product decision, so #23
stays open with
agent:needs-discussion.