diff --git a/apps/vscode/CHANGELOG.md b/apps/vscode/CHANGELOG.md index a7e3592a..cde2e3c5 100644 --- a/apps/vscode/CHANGELOG.md +++ b/apps/vscode/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.138.0 (Unreleased) - In Positron, running a Python cell in a knitr document now respects the `quarto.cells.useReticulate` setting, instead of always routing it through reticulate on the R console (). +- In Positron, fixed how the "Render on Save" checkbox works in the visual editor (). ## 1.137.0 (Release on 2026-09-04) diff --git a/apps/vscode/package.json b/apps/vscode/package.json index 1902a494..a81429df 100644 --- a/apps/vscode/package.json +++ b/apps/vscode/package.json @@ -456,7 +456,7 @@ "command": "quarto.toggleRenderOnSave", "title": "Render on Save", "category": "Quarto", - "enablement": "editorLangId == quarto", + "enablement": "editorLangId == quarto || activeCustomEditorId == 'quarto.visualEditor'", "actionBarOptions": { "controlType": "checkbox", "checked": "(quarto.editor.type == quarto && quarto.editor.renderOnSave) || (quarto.editor.type == 'quarto-shiny' && quarto.editor.renderOnSaveShiny)" diff --git a/apps/vscode/src/providers/context-keys.ts b/apps/vscode/src/providers/context-keys.ts index 65e88379..416f8401 100644 --- a/apps/vscode/src/providers/context-keys.ts +++ b/apps/vscode/src/providers/context-keys.ts @@ -32,26 +32,36 @@ export function activateContextKeySetter( engine: MarkdownEngine ) { // set the initial context keys - setEditorContextKeys(vscode.window.activeTextEditor, engine); - setLanguageContextKeys(vscode.window.activeTextEditor, engine); + setEditorContextKeys(vscode.window.activeTextEditor?.document, engine); + setLanguageContextKeys(vscode.window.activeTextEditor?.document, engine); // register for quarto.render.renderOnSave or quarto.render.renderOnSaveShiny configuration change notification context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => { // if the change affects quarto.render.renderOnSave or quarto.render.renderOnSaveShiny, set the editor context keys. if (event.affectsConfiguration('quarto.render.renderOnSave') || event.affectsConfiguration('quarto.render.renderOnSaveShiny')) { - setEditorContextKeys(vscode.window.activeTextEditor, engine); + const document = vscode.window.activeTextEditor?.document ?? VisualEditorProvider.activeEditor()?.document; + setEditorContextKeys(document, engine); } })); // set context keys when active text editor changes vscode.window.onDidChangeActiveTextEditor(activeTextEditor => { - setEditorContextKeys(activeTextEditor, engine); - setLanguageContextKeys(activeTextEditor, engine); + setEditorContextKeys(activeTextEditor?.document, engine); + setLanguageContextKeys(activeTextEditor?.document, engine); }, null, context.subscriptions ); + // set context keys when a visual editor becomes active (custom editors + // don't fire onDidChangeActiveTextEditor) + context.subscriptions.push( + VisualEditorProvider.onDidChangeActiveEditor()(editor => { + setEditorContextKeys(editor.document, engine); + setLanguageContextKeys(editor.document, engine); + }) + ); + // set context keys on changes to the document (if it's active) vscode.workspace.onDidChangeTextDocument(event => { const activeEditor = vscode.window.activeTextEditor; @@ -59,8 +69,8 @@ export function activateContextKeySetter( // TODO: this debounce is being created and called immediately, which is not correct. debounce( () => { - setEditorContextKeys(activeEditor, engine); - setLanguageContextKeys(activeEditor, engine); + setEditorContextKeys(activeEditor.document, engine); + setLanguageContextKeys(activeEditor.document, engine); }, debounceOnDidChangeDocumentMs )(); @@ -110,11 +120,11 @@ export function toggleRenderOnSaveOverride() { } // sets editor context keys -function setEditorContextKeys(activeTextEditor: vscode.TextEditor | undefined, engine: MarkdownEngine) { +function setEditorContextKeys(document: vscode.TextDocument | undefined, engine: MarkdownEngine) { // if a Quarto doc is active, set the editor context keys - if (isQuartoDoc(activeTextEditor?.document)) { + if (isQuartoDoc(document)) { // set the quarto.editor.type context key - quartoEditorType = !isQuartoShinyDoc(engine, activeTextEditor?.document) + quartoEditorType = !isQuartoShinyDoc(engine, document) ? 'quarto' : 'quarto-shiny'; vscode.commands.executeCommand( @@ -145,13 +155,13 @@ function setEditorContextKeys(activeTextEditor: vscode.TextEditor | undefined, e } } -function setLanguageContextKeys(activeTextEditor: vscode.TextEditor | undefined, engine: MarkdownEngine) { - if (!activeTextEditor || !isQuartoDoc(activeTextEditor.document)) { +function setLanguageContextKeys(document: vscode.TextDocument | undefined, engine: MarkdownEngine) { + if (!document || !isQuartoDoc(document)) { return; } // expose main language for use in keybindings, etc - const tokens = engine.parse(activeTextEditor.document); + const tokens = engine.parse(document); const language = mainLanguage(tokens); vscode.commands.executeCommand( 'setContext', diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index eebc811f..5c60fb33 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -26,7 +26,9 @@ import { Selection, TextEditorRevealType, GlobPattern, - TabInputText + TabInputText, + Event, + EventEmitter } from "vscode"; import { projectDirForDocument, QuartoContext } from "quarto-core"; @@ -268,6 +270,11 @@ export class VisualEditorProvider implements CustomTextEditorProvider { this.editorPendingSwitchToVisual.add(document.uri.toString()); } + // fires when a visual editor's webview panel becomes active + public static onDidChangeActiveEditor(): Event { + return this.visualEditors.onDidChangeActiveEditor; + } + public static activeEditor(includeVisible?: boolean): QuartoVisualEditor | undefined { const editor = this.visualEditors.activeEditor(includeVisible); if (editor) { @@ -677,17 +684,27 @@ interface VisualEditorTracker { track: (document: TextDocument, webviewPanel: WebviewPanel, editor: VSCodeVisualEditor) => Disposable; editorForUri: (uri: Uri) => TrackedEditor | undefined; activeEditor: (includeVisible?: boolean) => TrackedEditor | undefined; + onDidChangeActiveEditor: Event; } function visualEditorTracker(): VisualEditorTracker { const activeEditors = new Array(); + const onDidChangeActiveEditorEmitter = new EventEmitter(); return { track: (document: TextDocument, webviewPanel: WebviewPanel, editor: VSCodeVisualEditor): Disposable => { - activeEditors.push({ document, webviewPanel, editor }); + const trackedEditor = { document, webviewPanel, editor }; + activeEditors.push(trackedEditor); + // notify when this editor's webview panel becomes active + const viewStateDisposable = webviewPanel.onDidChangeViewState(e => { + if (e.webviewPanel.active) { + onDidChangeActiveEditorEmitter.fire(trackedEditor); + } + }); return { dispose: () => { + viewStateDisposable.dispose(); const idx = activeEditors.findIndex(editor => editor.webviewPanel === webviewPanel); if (idx !== -1) { activeEditors.splice(idx, 1); @@ -711,7 +728,8 @@ function visualEditorTracker(): VisualEditorTracker { } }); - } + }, + onDidChangeActiveEditor: onDidChangeActiveEditorEmitter.event }; }