Skip to content

Commit 8419f80

Browse files
committed
feat: implement unsaved changes detection and fix webview disposal error in SQL Editor
1 parent f5d82b0 commit 8419f80

4 files changed

Lines changed: 41 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ All notable changes to the MS SQL Manager extension will be documented in this f
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.19.12] - 2026-05-29
9+
10+
### Fixed
11+
12+
- **SQL Editor — Unsaved changes detection (dirty state) now works for `.sql` files**
13+
- Editing SQL content in the custom SQL Editor webview did not mark the underlying document as modified, so VS Code never showed the "Do you want to save changes?" dialog when closing a file with unsaved edits.
14+
- Root cause: the React webview sends a `contentChanged` message when the Monaco editor content changes, but the extension host only handled the `documentChanged` message type — the content change was silently dropped and the `TextDocument` was never updated.
15+
- Fix: added `onChange` handler to the Monaco editor component that posts `contentChanged` to the extension on every edit, and added `contentChanged` as a recognized message type in the extension host (both file-backed and untitled query paths).
16+
- Added echo-suppression guard to prevent the `onDidChangeTextDocument``update` round-trip from bouncing content back to the webview after each keystroke.
17+
18+
- **SQL Editor — "Webview is disposed" error on close**
19+
- Closing a SQL Editor tab could throw `Error: Webview is disposed` when a pending document-change event tried to post a message to the already-disposed webview.
20+
- Added a disposed-webview guard in the `onDidChangeTextDocument` listener to silently skip the update when the webview is no longer alive.
21+
822
## [0.19.11] - 2026-05-29
923

1024
### Removed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "ms-sql-manager",
33
"displayName": "MS SQL Manager",
44
"description": "Manage Microsoft SQL Server databases with connection management, schema browsing, and query execution.",
5-
"version": "0.19.11",
5+
"version": "0.19.12",
66
"publisher": "jakubkozera",
77
"icon": "ms-sql-manager.png",
88
"sponsor": {

src/sqlEditorProvider.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,22 @@ export class SqlEditorProvider implements vscode.CustomTextEditorProvider {
5353
// Track webview to document mapping
5454
this.webviewToDocument.set(webviewPanel.webview, document.uri);
5555

56-
// Update webview content when document changes
56+
// Guard flag to prevent echo-back: when the webview sends a content change,
57+
// we apply it to the document, which triggers onDidChangeTextDocument;
58+
// without this flag that would send 'update' back to the webview causing
59+
// unnecessary round-trips and potential cursor jumps.
60+
let suppressNextUpdate = false;
61+
62+
// Update webview content when document changes (external edits, not webview-originated)
5763
const changeDocumentSubscription = vscode.workspace.onDidChangeTextDocument(e => {
5864
if (e.document.uri.toString() === document.uri.toString()) {
65+
if (suppressNextUpdate) {
66+
suppressNextUpdate = false;
67+
return;
68+
}
69+
if (this.disposedWebviews.has(webviewPanel.webview)) {
70+
return;
71+
}
5972
webviewPanel.webview.postMessage({
6073
type: 'update',
6174
content: document.getText()
@@ -117,8 +130,10 @@ export class SqlEditorProvider implements vscode.CustomTextEditorProvider {
117130
// to give explicit control over when queries execute
118131
break;
119132

133+
case 'contentChanged':
120134
case 'documentChanged':
121135
// Update the document when the webview content changes
136+
suppressNextUpdate = true;
122137
const edit = new vscode.WorkspaceEdit();
123138
edit.replace(
124139
document.uri,
@@ -2110,6 +2125,7 @@ COMMIT TRANSACTION;
21102125
}
21112126
break;
21122127

2128+
case 'contentChanged':
21132129
case 'documentChanged':
21142130
currentContent = message.content;
21152131
// Update state for persistence

webview/sqlEditor-react/src/components/Editor/SqlEditor.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,14 @@ export const SqlEditor = forwardRef<SqlEditorHandle, SqlEditorProps>(
417417
}
418418
}, [initialValue, editorReady]);
419419

420+
// Notify extension host when editor content changes so the underlying
421+
// TextDocument is updated and VS Code can track dirty state.
422+
const handleEditorChange = useCallback((value: string | undefined) => {
423+
if (value !== undefined) {
424+
postMessage({ type: 'contentChanged', content: value });
425+
}
426+
}, [postMessage]);
427+
420428
return (
421429
<div className="sql-editor-container">
422430
<MonacoEditor
@@ -426,6 +434,7 @@ export const SqlEditor = forwardRef<SqlEditorHandle, SqlEditorProps>(
426434
defaultValue={initialValue}
427435
beforeMount={handleEditorWillMount}
428436
onMount={handleEditorMount}
437+
onChange={handleEditorChange}
429438
options={{
430439
automaticLayout: true,
431440
fixedOverflowWidgets: true,

0 commit comments

Comments
 (0)