From 56fc3ecfffb69e4dca2839280b043ed4086ed9ff Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 14:43:58 +0200 Subject: [PATCH 01/20] remove unused `includeVisible` arg --- apps/vscode/src/core/doc.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index 1ad3cf72..29f36035 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -200,7 +200,6 @@ export function findQuartoEditor( engine: MarkdownEngine, context: QuartoContext, filter: (doc: vscode.TextDocument) => boolean, - includeVisible = true ): QuartoEditor | undefined { // first check for an active visual editor const activeVisualEditor = VisualEditorProvider.activeEditor(); @@ -231,7 +230,7 @@ export function findQuartoEditor( if (textEditor && filter(textEditor.document)) { return quartoEditor(textEditor, engine, context); // check visible text editors - } else if (includeVisible) { + } else { // visible visual editor (sometime it loses track of 'active' so we need to use 'visible') const visibleVisualEditor = VisualEditorProvider.activeEditor(true); if (visibleVisualEditor && filter(visibleVisualEditor.document)) { @@ -247,8 +246,6 @@ export function findQuartoEditor( } else { return undefined; } - } else { - return undefined; } } From 06a63c44220e72ec4a6a1cfd6b4b1a62d696fdfa Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 15:26:55 +0200 Subject: [PATCH 02/20] update `canPreviewDoc` to support `NotebookDocument` --- apps/vscode/src/core/doc.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index 29f36035..08cc76bc 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -23,9 +23,7 @@ import { VisualEditorProvider } from "../providers/editor/editor"; import { extname } from "./path"; import { MarkdownEngine } from "../markdown/engine"; import { QuartoContext, projectDirForDocument } from "quarto-core"; -import { TextDocument } from "vscode"; import { workspace } from "vscode"; -import { NotebookDocument } from "vscode"; import { isJupyterPercentScript, isKnitrSpinScript } from "core-node"; export const kQuartoLanguageId = "quarto"; @@ -58,18 +56,23 @@ function isLanguageDoc(languageId: string, doc?: vscode.TextDocument) { return !!doc && doc.languageId === languageId; } -export function isNotebook(doc?: vscode.TextDocument) { - return !!doc && isNotebookUri(doc.uri); +export function isNotebook(doc?: vscode.TextDocument | vscode.NotebookDocument) { + return isNotebookDoc(doc) && isNotebookUri(doc.uri); +} + +function isNotebookDoc(doc?: vscode.TextDocument | vscode.NotebookDocument): doc is vscode.NotebookDocument { + return !!doc && 'notebookType' in doc; } export function isNotebookUri(uri: Uri) { return extname(uri.fsPath).toLowerCase() === ".ipynb"; } - -export function canPreviewDoc(doc?: TextDocument) { +export function canPreviewDoc(doc?: vscode.TextDocument | vscode.NotebookDocument) { if (doc) { - if (isQuartoDoc(doc) || isNotebook(doc)) { + if (isNotebookDoc(doc)) { + return isNotebookUri(doc.uri); + } else if (isQuartoDoc(doc)) { return true; } else if (validatateQuartoCanRender(doc)) { return true; From 36752ffcf62cf062b79f9d880d2dda32e796e8ff Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 15:28:30 +0200 Subject: [PATCH 03/20] make `isNotebookUri` private --- apps/vscode/src/core/doc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index 08cc76bc..6eb69ae7 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -64,7 +64,7 @@ function isNotebookDoc(doc?: vscode.TextDocument | vscode.NotebookDocument): doc return !!doc && 'notebookType' in doc; } -export function isNotebookUri(uri: Uri) { +function isNotebookUri(uri: Uri) { return extname(uri.fsPath).toLowerCase() === ".ipynb"; } From f9af11cdf9cd02e0ef3e65f74e24240d811eb643 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 15:38:45 +0200 Subject: [PATCH 04/20] remove version gaurds; 1.67 is 4 years old now --- .../src/providers/preview/preview-util.ts | 9 +------- apps/vscode/src/providers/preview/preview.ts | 21 +++++++------------ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/apps/vscode/src/providers/preview/preview-util.ts b/apps/vscode/src/providers/preview/preview-util.ts index c1b43b79..b4c278dc 100644 --- a/apps/vscode/src/providers/preview/preview-util.ts +++ b/apps/vscode/src/providers/preview/preview-util.ts @@ -63,7 +63,7 @@ export function isQuartoShinyKnitrDoc( export async function renderOnSave(engine: MarkdownEngine, document: TextDocument) { // if its a notebook and we don't have a save hook for notebooks then don't // allow renderOnSave (b/c we can't detect the saves) - if (isNotebook(document) && !haveNotebookSaveEvents()) { + if (isNotebook(document)) { return false; } @@ -102,13 +102,6 @@ export async function renderOnSave(engine: MarkdownEngine, document: TextDocumen : getRenderOnSaveShiny(); } -export function haveNotebookSaveEvents() { - return ( - semver.gte(vscode.version, "1.67.0") && - !!(workspace as any).onDidSaveNotebookDocument - ); -} - function readRenderOnSave(yaml: Record) { if (typeof yaml["editor"] === "object") { const yamlObj = yaml["editor"] as Record; diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 5a36daf4..3dba2aca 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -71,7 +71,6 @@ import { QuartoPreviewWebviewManager, } from "./preview-webview"; import { - haveNotebookSaveEvents, isQuartoShinyDoc, isQuartoShinyKnitrDoc, renderOnSave, @@ -132,17 +131,13 @@ export function activatePreview( await onSave(doc.uri); }) ); - // we use 1.66 as our minimum version (and type import) but - // onDidSaveNotebookDocument was introduced in 1.67 - if (haveNotebookSaveEvents()) { - context.subscriptions.push( - (vscode.workspace as any).onDidSaveNotebookDocument( - async (notebook: NotebookDocument) => { - await onSave(notebook.uri); - } - ) - ); - } + context.subscriptions.push( + vscode.workspace.onDidSaveNotebookDocument( + async (notebook: NotebookDocument) => { + await onSave(notebook.uri); + } + ) + ); // monitor active document to see whether it can be rendered by quarto const updateRenderDocActive = (editor?: vscode.TextEditor) => { @@ -589,7 +584,7 @@ class PreviewManager { } } } - this.progressDismiss() + this.progressDismiss(); } private progressShow(uri: Uri) { From cbf7f6eea16a3462ec6a249bc47fd9d1e00694a8 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 15:45:13 +0200 Subject: [PATCH 05/20] rename `isNotebook` to `isNotebookCell` --- apps/vscode/src/core/doc.ts | 11 ++++++++--- apps/vscode/src/providers/preview/commands.ts | 5 +++-- apps/vscode/src/providers/preview/preview-util.ts | 11 +++-------- apps/vscode/src/providers/preview/preview.ts | 12 ++++++------ apps/vscode/src/providers/render.ts | 6 +++--- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index 6eb69ae7..44886813 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -56,7 +56,12 @@ function isLanguageDoc(languageId: string, doc?: vscode.TextDocument) { return !!doc && doc.languageId === languageId; } -export function isNotebook(doc?: vscode.TextDocument | vscode.NotebookDocument) { +// TODO: This should actually be called isNotebookCell and we should call it less often +export function isNotebookCell(doc?: vscode.TextDocument) { + return !!doc && isNotebookUri(doc.uri); +} + +function isNotebook(doc?: vscode.TextDocument | vscode.NotebookDocument) { return isNotebookDoc(doc) && isNotebookUri(doc.uri); } @@ -170,7 +175,7 @@ export function preserveEditorFocus(editor?: QuartoEditor) { ? quartoEditor(vscode.window.activeTextEditor) : undefined); if (editor) { - if (!isNotebook(editor?.document)) { + if (!isNotebookCell(editor?.document)) { setTimeout(() => { if (editor) { editor.activate(); @@ -256,7 +261,7 @@ export function quartoEditor( editor: vscode.TextEditor, engine?: MarkdownEngine, context?: QuartoContext, - notebook?: NotebookDocument + notebook?: vscode.NotebookDocument ) { return { document: editor.document, diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index 5117263b..17cdaa6d 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -25,7 +25,7 @@ import { previewDoc, } from "./preview"; import { MarkdownEngine } from "../../markdown/engine"; -import { canPreviewDoc, findQuartoEditor, isNotebook } from "../../core/doc"; +import { canPreviewDoc, findQuartoEditor, isNotebookCell } from "../../core/doc"; import { renderOnSave } from "./preview-util"; import { documentFrontMatterYaml } from "../../markdown/document"; import { FormatQuickPickItem, RenderCommand } from "../render"; @@ -110,7 +110,8 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { } } else { // show the editor - if (!isNotebook(targetEditor.document)) { + // TODO: Why can't we show notebooks too? Maybe because activate shows a cell's text editor? + if (!isNotebookCell(targetEditor.document)) { await targetEditor.activate(); } diff --git a/apps/vscode/src/providers/preview/preview-util.ts b/apps/vscode/src/providers/preview/preview-util.ts index b4c278dc..32f5e588 100644 --- a/apps/vscode/src/providers/preview/preview-util.ts +++ b/apps/vscode/src/providers/preview/preview-util.ts @@ -23,7 +23,7 @@ import { metadataFilesForDocument, yamlFromMetadataFile, } from "quarto-core"; -import { isNotebook } from "../../core/doc"; +import { isNotebookCell } from "../../core/doc"; import { MarkdownEngine } from "../../markdown/engine"; import { documentFrontMatter } from "../../markdown/document"; @@ -61,14 +61,9 @@ export function isQuartoShinyKnitrDoc( } export async function renderOnSave(engine: MarkdownEngine, document: TextDocument) { - // if its a notebook and we don't have a save hook for notebooks then don't - // allow renderOnSave (b/c we can't detect the saves) - if (isNotebook(document)) { - return false; - } - + // TODO: Should notebooks be treated same as text documents? // notebooks automatically get renderOnSave - if (isNotebook(document)) { + if (isNotebookCell(document)) { return true; } diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 3dba2aca..544934a2 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -48,7 +48,7 @@ import { Command } from "../../core/command"; import { canPreviewDoc, findQuartoEditor, - isNotebook, + isNotebookCell, preserveEditorFocus, previewDirForDocument, quartoCanRenderScript, @@ -189,7 +189,7 @@ export async function previewDoc( ) { // set the slide index from the source editor so we can // navigate to it in the preview frame - const slideIndex = !isNotebook(editor.document) + const slideIndex = !isNotebookCell(editor.document) ? await editor.slideIndex() : undefined; previewManager.setSlideIndex(slideIndex); @@ -202,7 +202,7 @@ export async function previewDoc( // if this wasn't a renderOnSave then activate the editor and save if (!renderOnSave) { // activate the editor - if (!isNotebook(editor.document)) { + if (!isNotebookCell(editor.document)) { await editor.activate(); } @@ -221,7 +221,7 @@ export async function previewDoc( if (previewEditor) { // error if we didn't save using a valid quarto extension if ( - !isNotebook(previewEditor.document) && + !isNotebookCell(previewEditor.document) && !validatateQuartoCanRender(previewEditor.document) ) { window.showErrorMessage("Unsupported File Extension", { @@ -243,7 +243,7 @@ export async function previewDoc( // focus the editor (sometimes the terminal steals focus) if (!renderOnSave) { - if (!isNotebook(previewEditor.document)) { + if (!isNotebookCell(previewEditor.document)) { await previewEditor.activate(); } } @@ -616,7 +616,7 @@ class PreviewManager { private async detectErrorNavigation(output: string) { // bail if this is a notebook or we don't have a previewDoc - if (!this.previewDoc_ || isNotebook(this.previewDoc_)) { + if (!this.previewDoc_ || isNotebookCell(this.previewDoc_)) { return; } diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index 3b944d62..c38c9ea3 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -23,7 +23,7 @@ import { Command } from "../core/command"; import { MarkdownEngine } from "../markdown/engine"; import { promptForQuartoInstallation } from "../core/quarto"; -import { QuartoEditor, canPreviewDoc, findQuartoEditor, isNotebook } from "../core/doc"; +import { QuartoEditor, canPreviewDoc, findQuartoEditor, isNotebookCell } from "../core/doc"; import { commands } from "vscode"; import { killTerminal, sendTerminalCommand, terminalCommand, terminalEnv, terminalOptions } from "../core/terminal"; import { QuickPickItem } from "vscode"; @@ -93,7 +93,7 @@ class RenderDocumentCommand extends RenderCommand if (targetEditor) { // show the editor and save - if (!isNotebook(targetEditor.document)) { + if (!isNotebookCell(targetEditor.document)) { await targetEditor.activate(); } await commands.executeCommand("workbench.action.files.save"); @@ -123,7 +123,7 @@ class RenderDocumentCommand extends RenderCommand await sendTerminalCommand(terminal, env, this.quartoContext(), cmd); // focus the editor (sometimes the terminal steals focus) - if (!isNotebook(targetEditor.document)) { + if (!isNotebookCell(targetEditor.document)) { await targetEditor.activate(); } From 4d638311d28cb31fc5a934341dcdbb27aeb9e960 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 17:03:17 +0200 Subject: [PATCH 06/20] split quarto editor interfaces by type --- apps/vscode/src/core/doc.ts | 105 +++++++++++++----- apps/vscode/src/providers/editor/editor.ts | 8 +- apps/vscode/src/providers/preview/commands.ts | 8 +- apps/vscode/src/providers/preview/preview.ts | 13 ++- apps/vscode/src/providers/render.ts | 8 +- 5 files changed, 98 insertions(+), 44 deletions(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index 44886813..d2953ec1 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -19,7 +19,7 @@ import fs from "node:fs"; import * as vscode from "vscode"; import { Uri } from "vscode"; import { revealSlideIndex } from "../markdown/reveal"; -import { VisualEditorProvider } from "../providers/editor/editor"; +import { QuartoVisualEditor, VisualEditorProvider } from "../providers/editor/editor"; import { extname } from "./path"; import { MarkdownEngine } from "../markdown/engine"; import { QuartoContext, projectDirForDocument } from "quarto-core"; @@ -172,14 +172,12 @@ export function preserveEditorFocus(editor?: QuartoEditor) { editor = editor || (vscode.window.activeTextEditor - ? quartoEditor(vscode.window.activeTextEditor) + ? quartoTextEditor(vscode.window.activeTextEditor) : undefined); if (editor) { - if (!isNotebookCell(editor?.document)) { + if (!isQuartoNotebookEditor(editor)) { setTimeout(() => { - if (editor) { - editor.activate(); - } + editor.activate(); }, 200); } } else { @@ -195,13 +193,37 @@ export function preserveEditorFocus(editor?: QuartoEditor) { } } -export interface QuartoEditor { +export type QuartoEditor = QuartoTextEditor | QuartoNotebookEditor | QuartoVisualEditor; + +export interface QuartoEditorBase { document: vscode.TextDocument; activate: () => Promise; - slideIndex: () => Promise; viewColumn?: vscode.ViewColumn; - textEditor?: vscode.TextEditor; - notebook?: vscode.NotebookDocument; + slideIndex: () => Promise; +} + +export interface QuartoTextEditor extends QuartoEditorBase { + type: 'text'; + textEditor: vscode.TextEditor; +} + +export interface QuartoNotebookEditor extends QuartoEditorBase { + type: 'notebook'; + notebook: vscode.NotebookDocument; + // TODO: Need this one? + textEditor: vscode.TextEditor; +} + +export function isQuartoNotebookEditor(editor: QuartoEditor): editor is QuartoNotebookEditor { + return editor.type === 'notebook'; +} + +export function isQuartoTextEditor(editor: QuartoEditor): editor is QuartoTextEditor { + return editor.type === 'text'; +} + +export function isQuartoVisualEditor(editor: QuartoEditor): editor is QuartoVisualEditor { + return editor.type === 'visual'; } export function findQuartoEditor( @@ -216,27 +238,21 @@ export function findQuartoEditor( } // then check for active notebook editor - const notebookEditor = (vscode.window as any).activeNotebookEditor as - | vscode.NotebookEditor - | undefined; + const notebookEditor = vscode.window.activeNotebookEditor; if (notebookEditor) { - const notebookDocument = (notebookEditor as any).notebook as - | vscode.NotebookDocument - | undefined; - if (notebookDocument) { - const textEditor = vscode.window.visibleTextEditors.find((editor) => { - return editor.document.uri.fsPath.includes(notebookDocument.uri.fsPath); - }); - if (textEditor && filter(textEditor.document)) { - return quartoEditor(textEditor, engine, context, notebookDocument); - } + const notebookDocument = notebookEditor.notebook; + const textEditor = vscode.window.visibleTextEditors.find((editor) => { + return editor.document.uri.fsPath.includes(notebookDocument.uri.fsPath); + }); + if (textEditor && filter(textEditor.document)) { + return quartoNotebookEditor(textEditor, notebookDocument, engine, context); } } // active text editor const textEditor = vscode.window.activeTextEditor; if (textEditor && filter(textEditor.document)) { - return quartoEditor(textEditor, engine, context); + return quartoTextEditor(textEditor, engine, context); // check visible text editors } else { // visible visual editor (sometime it loses track of 'active' so we need to use 'visible') @@ -250,20 +266,53 @@ export function findQuartoEditor( filter(editor.document) ); if (visibleEditor) { - return quartoEditor(visibleEditor, engine, context); + return quartoTextEditor(visibleEditor, engine, context); } else { return undefined; } } } -export function quartoEditor( +function quartoTextEditor( + editor: vscode.TextEditor, + engine?: MarkdownEngine, + context?: QuartoContext, +): QuartoTextEditor { + return { + type: 'text', + document: editor.document, + activate: async () => { + await vscode.window.showTextDocument( + editor.document, + editor.viewColumn, + false + ); + }, + slideIndex: async () => { + if (engine && context) { + return await revealSlideIndex( + editor.selection.active, + editor.document, + engine, + context + ); + } else { + return 0; + } + }, + viewColumn: editor.viewColumn, + textEditor: editor, + }; +} + +function quartoNotebookEditor( editor: vscode.TextEditor, + notebook: vscode.NotebookDocument, engine?: MarkdownEngine, context?: QuartoContext, - notebook?: vscode.NotebookDocument -) { +): QuartoNotebookEditor { return { + type: 'notebook', document: editor.document, activate: async () => { await vscode.window.showTextDocument( diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index 83c7af1b..94e6ff4f 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -13,7 +13,7 @@ * */ -import path, { extname, win32 } from "path"; +import path, { extname } from "path"; import { determineMode } from "./toggle"; import debounce from "lodash.debounce"; @@ -46,7 +46,7 @@ import { CodeViewActiveBlockContext, CodeViewSelectionAction, HostContext, NavLo import { getNonce } from "../../core/nonce"; import { isWindows } from "../../core/platform"; -import { isQuartoDoc, QuartoEditor } from "../../core/doc"; +import { isQuartoDoc, QuartoEditorBase } from "../../core/doc"; import { Command } from "../../core/command"; import { visualEditorClient, visualEditorServer } from "./connection"; @@ -70,7 +70,8 @@ import { TabInputCustom } from "vscode"; const kVisualModeConfirmed = "visualModeConfirmed"; -export interface QuartoVisualEditor extends QuartoEditor { +export interface QuartoVisualEditor extends QuartoEditorBase { + type: 'visual'; hasFocus(): Promise; getActiveBlockContext(): Promise; setBlockSelection(context: CodeViewActiveBlockContext, action: CodeViewSelectionAction): Promise; @@ -287,6 +288,7 @@ export class VisualEditorProvider implements CustomTextEditorProvider { const editor = this.visualEditors.activeEditor(includeVisible); if (editor) { return { + type: 'visual', document: editor.document, hasFocus: async () => { return await editor.editor.isFocused(); diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index 17cdaa6d..c5c3844d 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -16,7 +16,7 @@ import * as path from "path"; import * as fs from "fs"; -import { TextDocument, window, Uri, workspace, commands, QuickPickItem } from "vscode"; +import { TextDocument, window, Uri, workspace, commands } from "vscode"; import { QuartoContext, QuartoFormatInfo, quartoDocumentFormats } from "quarto-core"; import { Command } from "../../core/command"; @@ -25,7 +25,7 @@ import { previewDoc, } from "./preview"; import { MarkdownEngine } from "../../markdown/engine"; -import { canPreviewDoc, findQuartoEditor, isNotebookCell } from "../../core/doc"; +import { canPreviewDoc, findQuartoEditor, isQuartoNotebookEditor } from "../../core/doc"; import { renderOnSave } from "./preview-util"; import { documentFrontMatterYaml } from "../../markdown/document"; import { FormatQuickPickItem, RenderCommand } from "../render"; @@ -64,7 +64,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { if (render) { if (format === kChooseFormat) { - const frontMatter = targetEditor.notebook + const frontMatter = isQuartoNotebookEditor(targetEditor) ? targetEditor.notebook.cellAt(0)?.document.getText() || "" : documentFrontMatterYaml(this.engine_, targetEditor.document); @@ -111,7 +111,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { } else { // show the editor // TODO: Why can't we show notebooks too? Maybe because activate shows a cell's text editor? - if (!isNotebookCell(targetEditor.document)) { + if (!isQuartoNotebookEditor(targetEditor)) { await targetEditor.activate(); } diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 544934a2..5f62c88d 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -49,6 +49,8 @@ import { canPreviewDoc, findQuartoEditor, isNotebookCell, + isQuartoNotebookEditor, + isQuartoTextEditor, preserveEditorFocus, previewDirForDocument, quartoCanRenderScript, @@ -189,7 +191,7 @@ export async function previewDoc( ) { // set the slide index from the source editor so we can // navigate to it in the preview frame - const slideIndex = !isNotebookCell(editor.document) + const slideIndex = !isQuartoNotebookEditor(editor) ? await editor.slideIndex() : undefined; previewManager.setSlideIndex(slideIndex); @@ -202,7 +204,7 @@ export async function previewDoc( // if this wasn't a renderOnSave then activate the editor and save if (!renderOnSave) { // activate the editor - if (!isNotebookCell(editor.document)) { + if (!isQuartoNotebookEditor(editor)) { await editor.activate(); } @@ -221,7 +223,7 @@ export async function previewDoc( if (previewEditor) { // error if we didn't save using a valid quarto extension if ( - !isNotebookCell(previewEditor.document) && + !isQuartoNotebookEditor(previewEditor) && !validatateQuartoCanRender(previewEditor.document) ) { window.showErrorMessage("Unsupported File Extension", { @@ -243,7 +245,7 @@ export async function previewDoc( // focus the editor (sometimes the terminal steals focus) if (!renderOnSave) { - if (!isNotebookCell(previewEditor.document)) { + if (!isQuartoNotebookEditor(previewEditor)) { await previewEditor.activate(); } } @@ -646,7 +648,8 @@ class PreviewManager { (doc) => doc.uri.fsPath === fileUri.fsPath ); if (editor) { - if (editor.textEditor) { + // TODO: Should probably handle notebooks and visual editors here + if (isQuartoTextEditor(editor)) { // if the current selection is outside of the error region then // navigate to the top of the error region const errPos = new Position(errorLoc.lineBegin - 1, 0); diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index c38c9ea3..c2db6a48 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -23,7 +23,7 @@ import { Command } from "../core/command"; import { MarkdownEngine } from "../markdown/engine"; import { promptForQuartoInstallation } from "../core/quarto"; -import { QuartoEditor, canPreviewDoc, findQuartoEditor, isNotebookCell } from "../core/doc"; +import { QuartoEditor, canPreviewDoc, findQuartoEditor, isQuartoNotebookEditor } from "../core/doc"; import { commands } from "vscode"; import { killTerminal, sendTerminalCommand, terminalCommand, terminalEnv, terminalOptions } from "../core/terminal"; import { QuickPickItem } from "vscode"; @@ -93,7 +93,7 @@ class RenderDocumentCommand extends RenderCommand if (targetEditor) { // show the editor and save - if (!isNotebookCell(targetEditor.document)) { + if (!isQuartoNotebookEditor(targetEditor)) { await targetEditor.activate(); } await commands.executeCommand("workbench.action.files.save"); @@ -123,7 +123,7 @@ class RenderDocumentCommand extends RenderCommand await sendTerminalCommand(terminal, env, this.quartoContext(), cmd); // focus the editor (sometimes the terminal steals focus) - if (!isNotebookCell(targetEditor.document)) { + if (!isQuartoNotebookEditor(targetEditor)) { await targetEditor.activate(); } @@ -134,7 +134,7 @@ class RenderDocumentCommand extends RenderCommand private async resolveFormat(targetEditor: QuartoEditor) { return new Promise((resolve) => { - const frontMatter = targetEditor.notebook + const frontMatter = isQuartoNotebookEditor(targetEditor) ? targetEditor.notebook.cellAt(0)?.document.getText() || "" : documentFrontMatterYaml(this.engine_, targetEditor.document); From 897e759defae5f288de9a317fb0b24025461c33f Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:06:48 +0200 Subject: [PATCH 07/20] extract notebookFrontMatterYaml --- apps/vscode/src/markdown/notebook.ts | 5 +++++ apps/vscode/src/providers/preview/commands.ts | 3 ++- apps/vscode/src/providers/render.ts | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 apps/vscode/src/markdown/notebook.ts diff --git a/apps/vscode/src/markdown/notebook.ts b/apps/vscode/src/markdown/notebook.ts new file mode 100644 index 00000000..4ef915bc --- /dev/null +++ b/apps/vscode/src/markdown/notebook.ts @@ -0,0 +1,5 @@ +import * as vscode from "vscode"; + +export function notebookFrontMatterYaml(notebook: vscode.NotebookDocument): string { + return notebook.cellAt(0)?.document.getText() || ""; +} diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index c5c3844d..b25d348e 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -30,6 +30,7 @@ import { renderOnSave } from "./preview-util"; import { documentFrontMatterYaml } from "../../markdown/document"; import { FormatQuickPickItem, RenderCommand } from "../render"; import { QuickPickItemKind } from "vscode"; +import { notebookFrontMatterYaml } from "../../markdown/notebook"; export function previewCommands( quartoContext: QuartoContext, @@ -65,7 +66,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { if (format === kChooseFormat) { const frontMatter = isQuartoNotebookEditor(targetEditor) - ? targetEditor.notebook.cellAt(0)?.document.getText() || "" + ? notebookFrontMatterYaml(targetEditor.notebook) : documentFrontMatterYaml(this.engine_, targetEditor.document); const formats = quartoDocumentFormats(this.quartoContext(), targetEditor.document.uri.fsPath, frontMatter); diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index c2db6a48..a94728d1 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -30,6 +30,7 @@ import { QuickPickItem } from "vscode"; import { documentFrontMatterYaml } from "../markdown/document"; import { QuickPickItemKind } from "vscode"; import { Uri } from "vscode"; +import { notebookFrontMatterYaml } from "../markdown/notebook"; export function activateRender(quartoContext: QuartoContext, engine: MarkdownEngine): Command[] { @@ -135,7 +136,7 @@ class RenderDocumentCommand extends RenderCommand return new Promise((resolve) => { const frontMatter = isQuartoNotebookEditor(targetEditor) - ? targetEditor.notebook.cellAt(0)?.document.getText() || "" + ? notebookFrontMatterYaml(targetEditor.notebook) : documentFrontMatterYaml(this.engine_, targetEditor.document); const kDeclaredFormats = "Declared Formats"; From 60b1b86cb7d16e854052521de9c951413559206d Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:07:04 +0200 Subject: [PATCH 08/20] remove unused code --- apps/vscode/src/lsp/client.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index e9b26ba1..6737fb3b 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -538,21 +538,3 @@ function unadjustSymbolRanges( }; }); } - -/** - * Creates a diagnostic handler middleware that filters out diagnostics from virtual documents - * - * @returns A handler function for the middleware - */ -export function createDiagnosticFilter() { - return (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { - // If this is not a virtual document, pass through all diagnostics - if (!isVirtualDoc(uri)) { - next(uri, diagnostics); - return; - } - - // For virtual documents, filter out all diagnostics - next(uri, []); - }; -} From 93feecadf29949221d0d680dafd253184b6f0809 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:30:23 +0200 Subject: [PATCH 09/20] set quarto notebook editor document to first cell doc --- apps/vscode/src/core/doc.ts | 65 ++++++++++++------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index d2953ec1..bc4a4f48 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -56,27 +56,22 @@ function isLanguageDoc(languageId: string, doc?: vscode.TextDocument) { return !!doc && doc.languageId === languageId; } -// TODO: This should actually be called isNotebookCell and we should call it less often export function isNotebookCell(doc?: vscode.TextDocument) { - return !!doc && isNotebookUri(doc.uri); -} - -function isNotebook(doc?: vscode.TextDocument | vscode.NotebookDocument) { - return isNotebookDoc(doc) && isNotebookUri(doc.uri); + return !!doc && isIpynbUri(doc.uri); } function isNotebookDoc(doc?: vscode.TextDocument | vscode.NotebookDocument): doc is vscode.NotebookDocument { return !!doc && 'notebookType' in doc; } -function isNotebookUri(uri: Uri) { +function isIpynbUri(uri: Uri) { return extname(uri.fsPath).toLowerCase() === ".ipynb"; } export function canPreviewDoc(doc?: vscode.TextDocument | vscode.NotebookDocument) { if (doc) { if (isNotebookDoc(doc)) { - return isNotebookUri(doc.uri); + return isIpynbUri(doc.uri); } else if (isQuartoDoc(doc)) { return true; } else if (validatateQuartoCanRender(doc)) { @@ -177,7 +172,9 @@ export function preserveEditorFocus(editor?: QuartoEditor) { if (editor) { if (!isQuartoNotebookEditor(editor)) { setTimeout(() => { - editor.activate(); + if (editor) { + editor.activate(); + } }, 200); } } else { @@ -199,7 +196,7 @@ export interface QuartoEditorBase { document: vscode.TextDocument; activate: () => Promise; viewColumn?: vscode.ViewColumn; - slideIndex: () => Promise; + slideIndex?: () => Promise; } export interface QuartoTextEditor extends QuartoEditorBase { @@ -210,8 +207,6 @@ export interface QuartoTextEditor extends QuartoEditorBase { export interface QuartoNotebookEditor extends QuartoEditorBase { type: 'notebook'; notebook: vscode.NotebookDocument; - // TODO: Need this one? - textEditor: vscode.TextEditor; } export function isQuartoNotebookEditor(editor: QuartoEditor): editor is QuartoNotebookEditor { @@ -229,7 +224,7 @@ export function isQuartoVisualEditor(editor: QuartoEditor): editor is QuartoVisu export function findQuartoEditor( engine: MarkdownEngine, context: QuartoContext, - filter: (doc: vscode.TextDocument) => boolean, + filter: (doc: vscode.TextDocument | vscode.NotebookDocument) => boolean, ): QuartoEditor | undefined { // first check for an active visual editor const activeVisualEditor = VisualEditorProvider.activeEditor(); @@ -239,13 +234,11 @@ export function findQuartoEditor( // then check for active notebook editor const notebookEditor = vscode.window.activeNotebookEditor; - if (notebookEditor) { - const notebookDocument = notebookEditor.notebook; - const textEditor = vscode.window.visibleTextEditors.find((editor) => { - return editor.document.uri.fsPath.includes(notebookDocument.uri.fsPath); - }); - if (textEditor && filter(textEditor.document)) { - return quartoNotebookEditor(textEditor, notebookDocument, engine, context); + if (notebookEditor && filter(notebookEditor.notebook)) { + // TODO: Why is cellAt always defined?... + const firstCellDoc = notebookEditor.notebook.cellAt(0)?.document; + if (firstCellDoc) { + return quartoNotebookEditor(notebookEditor, firstCellDoc); } } @@ -306,36 +299,20 @@ function quartoTextEditor( } function quartoNotebookEditor( - editor: vscode.TextEditor, - notebook: vscode.NotebookDocument, - engine?: MarkdownEngine, - context?: QuartoContext, + notebookEditor: vscode.NotebookEditor, + firstCellDoc: vscode.TextDocument, ): QuartoNotebookEditor { return { type: 'notebook', - document: editor.document, + document: firstCellDoc, activate: async () => { - await vscode.window.showTextDocument( - editor.document, - editor.viewColumn, - false + await vscode.window.showNotebookDocument( + notebookEditor.notebook, + { viewColumn: notebookEditor.viewColumn, preserveFocus: false } ); }, - slideIndex: async () => { - if (engine && context) { - return await revealSlideIndex( - editor.selection.active, - editor.document, - engine, - context - ); - } else { - return 0; - } - }, - viewColumn: editor.viewColumn, - textEditor: editor, - notebook, + viewColumn: notebookEditor.viewColumn, + notebook: notebookEditor.notebook, }; } From 5b19c623ccec34f8d5e1aba7d610e49d2bb630fb Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:30:45 +0200 Subject: [PATCH 10/20] preview depends on editor instead of text doc --- apps/vscode/src/providers/preview/commands.ts | 2 +- .../src/providers/preview/preview-util.ts | 13 ++++------ apps/vscode/src/providers/preview/preview.ts | 26 +++++++++---------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index b25d348e..2d332626 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -57,7 +57,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { protected async renderFormat(format?: string | null, onShow?: () => void) { const targetEditor = findQuartoEditor(this.engine_, this.quartoContext(), canPreviewDoc); if (targetEditor) { - const hasRenderOnSave = await renderOnSave(this.engine_, targetEditor.document); + const hasRenderOnSave = await renderOnSave(this.engine_, targetEditor); const render = !hasRenderOnSave || (hasRenderOnSave && format) || diff --git a/apps/vscode/src/providers/preview/preview-util.ts b/apps/vscode/src/providers/preview/preview-util.ts index 32f5e588..52f768c4 100644 --- a/apps/vscode/src/providers/preview/preview-util.ts +++ b/apps/vscode/src/providers/preview/preview-util.ts @@ -13,17 +13,14 @@ * */ -import semver from "semver"; - -import vscode from "vscode"; -import { TextDocument, Uri, workspace } from "vscode"; +import { TextDocument } from "vscode"; import { projectDirForDocument, metadataFilesForDocument, yamlFromMetadataFile, } from "quarto-core"; -import { isNotebookCell } from "../../core/doc"; +import { isQuartoNotebookEditor, QuartoEditor } from "../../core/doc"; import { MarkdownEngine } from "../../markdown/engine"; import { documentFrontMatter } from "../../markdown/document"; @@ -60,14 +57,14 @@ export function isQuartoShinyKnitrDoc( } -export async function renderOnSave(engine: MarkdownEngine, document: TextDocument) { - // TODO: Should notebooks be treated same as text documents? +export async function renderOnSave(engine: MarkdownEngine, editor: QuartoEditor) { // notebooks automatically get renderOnSave - if (isNotebookCell(document)) { + if (isQuartoNotebookEditor(editor)) { return true; } // first look for document level editor setting + const { document } = editor; const docYaml = documentFrontMatter(engine, document); const docSetting = readRenderOnSave(docYaml); if (docSetting !== undefined) { diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 5f62c88d..109ef39f 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -121,7 +121,7 @@ export function activatePreview( if (editor) { if ( canPreviewDoc(editor.document) && - (await renderOnSave(engine, editor.document)) && + (await renderOnSave(engine, editor)) && (await previewManager.isPreviewRunningForDoc(editor.document)) ) { await previewDoc(editor, undefined, true, engine, quartoContext); @@ -191,9 +191,7 @@ export async function previewDoc( ) { // set the slide index from the source editor so we can // navigate to it in the preview frame - const slideIndex = !isQuartoNotebookEditor(editor) - ? await editor.slideIndex() - : undefined; + const slideIndex = editor.slideIndex && await editor.slideIndex(); previewManager.setSlideIndex(slideIndex); // set onShow if provided @@ -237,8 +235,7 @@ export async function previewDoc( // run the preview await previewManager.preview( - previewEditor.document.uri, - previewEditor.document, + previewEditor, format, slideIndex ); @@ -252,10 +249,6 @@ export async function previewDoc( } } -export async function previewProject(target: Uri, format?: string) { - await previewManager.preview(target, undefined, format); -} - class PreviewManager { constructor( context: ExtensionContext, @@ -298,13 +291,17 @@ class PreviewManager { this.outputSink_.dispose(); } + private get previewDoc_() { + return this.previewEditor_?.document; + } + public async preview( - uri: Uri, - doc: TextDocument | undefined, + editor: QuartoEditor, format: string | null | undefined, slideIndex?: number ) { // resolve format if we need to + const uri = editor.document.uri; if (format === undefined) { format = this.previewFormats_.get(uri.fsPath) || null; } else { @@ -314,8 +311,9 @@ class PreviewManager { this.progressDismiss(); this.progressCancellationToken_ = undefined; this.previewOutput_ = ""; - this.previewDoc_ = doc; + this.previewEditor_ = editor; const previewEnv = await this.previewEnvManager_.previewEnv(uri); + const doc = editor?.document; if (doc && (await this.canReuseRunningPreview(doc, previewEnv))) { try { const response = await this.previewRenderRequest(doc, format); @@ -781,7 +779,7 @@ class PreviewManager { } private previewOutput_ = ""; - private previewDoc_: TextDocument | undefined; + private previewEditor_: QuartoEditor | undefined; private previewEnv_: PreviewEnv | undefined; private previewTarget_: Uri | undefined; private previewUrl_: string | undefined; From d56732d4a2dc9c2d73375e7a0e3b41eafd0a06c9 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:35:23 +0200 Subject: [PATCH 11/20] remove `isNotebookCell` and preview manager `previewDoc_` --- apps/vscode/src/core/doc.ts | 8 ++------ apps/vscode/src/providers/preview/preview.ts | 14 ++++---------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index bc4a4f48..f7b5beb6 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -56,11 +56,7 @@ function isLanguageDoc(languageId: string, doc?: vscode.TextDocument) { return !!doc && doc.languageId === languageId; } -export function isNotebookCell(doc?: vscode.TextDocument) { - return !!doc && isIpynbUri(doc.uri); -} - -function isNotebookDoc(doc?: vscode.TextDocument | vscode.NotebookDocument): doc is vscode.NotebookDocument { +function isNotebook(doc?: vscode.TextDocument | vscode.NotebookDocument): doc is vscode.NotebookDocument { return !!doc && 'notebookType' in doc; } @@ -70,7 +66,7 @@ function isIpynbUri(uri: Uri) { export function canPreviewDoc(doc?: vscode.TextDocument | vscode.NotebookDocument) { if (doc) { - if (isNotebookDoc(doc)) { + if (isNotebook(doc)) { return isIpynbUri(doc.uri); } else if (isQuartoDoc(doc)) { return true; diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 109ef39f..c0c727ae 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -48,7 +48,6 @@ import { Command } from "../../core/command"; import { canPreviewDoc, findQuartoEditor, - isNotebookCell, isQuartoNotebookEditor, isQuartoTextEditor, preserveEditorFocus, @@ -291,10 +290,6 @@ class PreviewManager { this.outputSink_.dispose(); } - private get previewDoc_() { - return this.previewEditor_?.document; - } - public async preview( editor: QuartoEditor, format: string | null | undefined, @@ -546,7 +541,7 @@ class PreviewManager { if (browseMatch) { // earlier versions of quarto serve didn't print out vscode urls // correctly so we compenstate for that here - if (isQuartoShinyDoc(this.engine_, this.previewDoc_)) { + if (isQuartoShinyDoc(this.engine_, this.previewEditor_?.document)) { this.previewUrl_ = vsCodeWebUrl(browseMatch[2]); } else { this.previewUrl_ = browseMatch[2]; @@ -615,16 +610,15 @@ class PreviewManager { } private async detectErrorNavigation(output: string) { - // bail if this is a notebook or we don't have a previewDoc - if (!this.previewDoc_ || isNotebookCell(this.previewDoc_)) { + // bail if this is a notebook or we don't have a previewEditor + if (!this.previewEditor_ || isQuartoNotebookEditor(this.previewEditor_)) { return; } - // normalize output = normalizeNewlines(output); // run all of our tests - const previewFile = this.previewDoc_.uri.fsPath; + const previewFile = this.previewEditor_.document.uri.fsPath; const previewDir = this.previewDir_ || this.targetDir(); const errorLoc = yamlErrorLocation(output, previewFile, previewDir) || From 9bd938776f84ebe8aa9eb56e20a1eee3162fb8d4 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:45:50 +0200 Subject: [PATCH 12/20] extract `quartoEditor` module --- apps/vscode/src/core/doc.ts | 159 +---------------- apps/vscode/src/core/quartoEditor.ts | 164 ++++++++++++++++++ apps/vscode/src/providers/assist/commands.ts | 3 +- apps/vscode/src/providers/convert.ts | 3 +- apps/vscode/src/providers/editor/editor.ts | 3 +- apps/vscode/src/providers/preview/commands.ts | 3 +- .../src/providers/preview/preview-util.ts | 2 +- apps/vscode/src/providers/preview/preview.ts | 11 +- apps/vscode/src/providers/render.ts | 3 +- apps/vscode/src/providers/webview.ts | 2 +- 10 files changed, 183 insertions(+), 170 deletions(-) create mode 100644 apps/vscode/src/core/quartoEditor.ts diff --git a/apps/vscode/src/core/doc.ts b/apps/vscode/src/core/doc.ts index f7b5beb6..a28affae 100644 --- a/apps/vscode/src/core/doc.ts +++ b/apps/vscode/src/core/doc.ts @@ -18,11 +18,8 @@ import fs from "node:fs"; import * as vscode from "vscode"; import { Uri } from "vscode"; -import { revealSlideIndex } from "../markdown/reveal"; -import { QuartoVisualEditor, VisualEditorProvider } from "../providers/editor/editor"; import { extname } from "./path"; -import { MarkdownEngine } from "../markdown/engine"; -import { QuartoContext, projectDirForDocument } from "quarto-core"; +import { projectDirForDocument } from "quarto-core"; import { workspace } from "vscode"; import { isJupyterPercentScript, isKnitrSpinScript } from "core-node"; @@ -158,160 +155,6 @@ export function getWholeRange(doc: vscode.TextDocument) { return new vscode.Range(begin, end); } -export function preserveEditorFocus(editor?: QuartoEditor) { - // focus the editor (sometimes the terminal steals focus) - editor = - editor || - (vscode.window.activeTextEditor - ? quartoTextEditor(vscode.window.activeTextEditor) - : undefined); - if (editor) { - if (!isQuartoNotebookEditor(editor)) { - setTimeout(() => { - if (editor) { - editor.activate(); - } - }, 200); - } - } else { - // see if there is a visual editor we should be preserving focus for - const visualEditor = VisualEditorProvider.activeEditor(); - if (visualEditor) { - setTimeout(async () => { - if (!(await visualEditor.hasFocus())) { - await visualEditor.activate(); - } - }, 200); - } - } -} - -export type QuartoEditor = QuartoTextEditor | QuartoNotebookEditor | QuartoVisualEditor; - -export interface QuartoEditorBase { - document: vscode.TextDocument; - activate: () => Promise; - viewColumn?: vscode.ViewColumn; - slideIndex?: () => Promise; -} - -export interface QuartoTextEditor extends QuartoEditorBase { - type: 'text'; - textEditor: vscode.TextEditor; -} - -export interface QuartoNotebookEditor extends QuartoEditorBase { - type: 'notebook'; - notebook: vscode.NotebookDocument; -} - -export function isQuartoNotebookEditor(editor: QuartoEditor): editor is QuartoNotebookEditor { - return editor.type === 'notebook'; -} - -export function isQuartoTextEditor(editor: QuartoEditor): editor is QuartoTextEditor { - return editor.type === 'text'; -} - -export function isQuartoVisualEditor(editor: QuartoEditor): editor is QuartoVisualEditor { - return editor.type === 'visual'; -} - -export function findQuartoEditor( - engine: MarkdownEngine, - context: QuartoContext, - filter: (doc: vscode.TextDocument | vscode.NotebookDocument) => boolean, -): QuartoEditor | undefined { - // first check for an active visual editor - const activeVisualEditor = VisualEditorProvider.activeEditor(); - if (activeVisualEditor && filter(activeVisualEditor.document)) { - return activeVisualEditor; - } - - // then check for active notebook editor - const notebookEditor = vscode.window.activeNotebookEditor; - if (notebookEditor && filter(notebookEditor.notebook)) { - // TODO: Why is cellAt always defined?... - const firstCellDoc = notebookEditor.notebook.cellAt(0)?.document; - if (firstCellDoc) { - return quartoNotebookEditor(notebookEditor, firstCellDoc); - } - } - - // active text editor - const textEditor = vscode.window.activeTextEditor; - if (textEditor && filter(textEditor.document)) { - return quartoTextEditor(textEditor, engine, context); - // check visible text editors - } else { - // visible visual editor (sometime it loses track of 'active' so we need to use 'visible') - const visibleVisualEditor = VisualEditorProvider.activeEditor(true); - if (visibleVisualEditor && filter(visibleVisualEditor.document)) { - return visibleVisualEditor; - } - - // visible text editors - const visibleEditor = vscode.window.visibleTextEditors.find((editor) => - filter(editor.document) - ); - if (visibleEditor) { - return quartoTextEditor(visibleEditor, engine, context); - } else { - return undefined; - } - } -} - -function quartoTextEditor( - editor: vscode.TextEditor, - engine?: MarkdownEngine, - context?: QuartoContext, -): QuartoTextEditor { - return { - type: 'text', - document: editor.document, - activate: async () => { - await vscode.window.showTextDocument( - editor.document, - editor.viewColumn, - false - ); - }, - slideIndex: async () => { - if (engine && context) { - return await revealSlideIndex( - editor.selection.active, - editor.document, - engine, - context - ); - } else { - return 0; - } - }, - viewColumn: editor.viewColumn, - textEditor: editor, - }; -} - -function quartoNotebookEditor( - notebookEditor: vscode.NotebookEditor, - firstCellDoc: vscode.TextDocument, -): QuartoNotebookEditor { - return { - type: 'notebook', - document: firstCellDoc, - activate: async () => { - await vscode.window.showNotebookDocument( - notebookEditor.notebook, - { viewColumn: notebookEditor.viewColumn, preserveFocus: false } - ); - }, - viewColumn: notebookEditor.viewColumn, - notebook: notebookEditor.notebook, - }; -} - async function tryResolveUriToQuartoDoc( resource: vscode.Uri ): Promise { diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts new file mode 100644 index 00000000..a9f221d7 --- /dev/null +++ b/apps/vscode/src/core/quartoEditor.ts @@ -0,0 +1,164 @@ +import * as vscode from "vscode"; +import { QuartoContext } from "quarto-core"; +import { MarkdownEngine } from "../markdown/engine"; +import { revealSlideIndex } from "../markdown/reveal"; +import { QuartoVisualEditor, VisualEditorProvider } from "../providers/editor/editor"; + +export type QuartoEditor = QuartoTextEditor | QuartoNotebookEditor | QuartoVisualEditor; + +export interface QuartoEditorBase { + document: vscode.TextDocument; + activate: () => Promise; + viewColumn?: vscode.ViewColumn; + + /** + * Get the slide index for the current cursor position. + * + * This method is undefined for editors that don't yet support + * slide parsing e.g. `QuartoNotebookEditor`. + */ + slideIndex?: () => Promise; +} + +export interface QuartoTextEditor extends QuartoEditorBase { + type: 'text'; + textEditor: vscode.TextEditor; +} + +export interface QuartoNotebookEditor extends QuartoEditorBase { + type: 'notebook'; + notebook: vscode.NotebookDocument; +} + +export function isQuartoNotebookEditor(editor: QuartoEditor): editor is QuartoNotebookEditor { + return editor.type === 'notebook'; +} + +export function isQuartoTextEditor(editor: QuartoEditor): editor is QuartoTextEditor { + return editor.type === 'text'; +} + +export function isQuartoVisualEditor(editor: QuartoEditor): editor is QuartoVisualEditor { + return editor.type === 'visual'; +} + +export function findQuartoEditor( + engine: MarkdownEngine, + context: QuartoContext, + filter: (doc: vscode.TextDocument | vscode.NotebookDocument) => boolean +): QuartoEditor | undefined { + // first check for an active visual editor + const activeVisualEditor = VisualEditorProvider.activeEditor(); + if (activeVisualEditor && filter(activeVisualEditor.document)) { + return activeVisualEditor; + } + + // then check for active notebook editor + const notebookEditor = vscode.window.activeNotebookEditor; + if (notebookEditor && filter(notebookEditor.notebook)) { + // TODO: Why is cellAt always defined?... + const firstCellDoc = notebookEditor.notebook.cellAt(0)?.document; + if (firstCellDoc) { + return quartoNotebookEditor(notebookEditor, firstCellDoc); + } + } + + // active text editor + const textEditor = vscode.window.activeTextEditor; + if (textEditor && filter(textEditor.document)) { + return quartoTextEditor(textEditor, engine, context); + // check visible text editors + } else { + // visible visual editor (sometime it loses track of 'active' so we need to use 'visible') + const visibleVisualEditor = VisualEditorProvider.activeEditor(true); + if (visibleVisualEditor && filter(visibleVisualEditor.document)) { + return visibleVisualEditor; + } + + // visible text editors + const visibleEditor = vscode.window.visibleTextEditors.find((editor) => filter(editor.document) + ); + if (visibleEditor) { + return quartoTextEditor(visibleEditor, engine, context); + } else { + return undefined; + } + } +} + +export function quartoTextEditor( + editor: vscode.TextEditor, + engine?: MarkdownEngine, + context?: QuartoContext): QuartoTextEditor { + return { + type: 'text', + document: editor.document, + activate: async () => { + await vscode.window.showTextDocument( + editor.document, + editor.viewColumn, + false + ); + }, + slideIndex: async () => { + if (engine && context) { + return await revealSlideIndex( + editor.selection.active, + editor.document, + engine, + context + ); + } else { + return 0; + } + }, + viewColumn: editor.viewColumn, + textEditor: editor, + }; +} + +function quartoNotebookEditor( + notebookEditor: vscode.NotebookEditor, + firstCellDoc: vscode.TextDocument +): QuartoNotebookEditor { + return { + type: 'notebook', + document: firstCellDoc, + activate: async () => { + await vscode.window.showNotebookDocument( + notebookEditor.notebook, + { viewColumn: notebookEditor.viewColumn, preserveFocus: false } + ); + }, + viewColumn: notebookEditor.viewColumn, + notebook: notebookEditor.notebook, + }; +} + +export function preserveEditorFocus(editor?: QuartoEditor) { + // focus the editor (sometimes the terminal steals focus) + editor = + editor || + (vscode.window.activeTextEditor + ? quartoTextEditor(vscode.window.activeTextEditor) + : undefined); + if (editor) { + if (!isQuartoNotebookEditor(editor)) { + setTimeout(() => { + if (editor) { + editor.activate(); + } + }, 200); + } + } else { + // see if there is a visual editor we should be preserving focus for + const visualEditor = VisualEditorProvider.activeEditor(); + if (visualEditor) { + setTimeout(async () => { + if (!(await visualEditor.hasFocus())) { + await visualEditor.activate(); + } + }, 200); + } + } +} diff --git a/apps/vscode/src/providers/assist/commands.ts b/apps/vscode/src/providers/assist/commands.ts index 07ed689b..61d04f5c 100644 --- a/apps/vscode/src/providers/assist/commands.ts +++ b/apps/vscode/src/providers/assist/commands.ts @@ -15,7 +15,8 @@ import { Position, Selection, window, commands } from "vscode"; import { Command } from "../../core/command"; -import { isQuartoDoc, preserveEditorFocus } from "../../core/doc"; +import { isQuartoDoc } from "../../core/doc"; +import { preserveEditorFocus } from "../../core/quartoEditor"; import { MarkdownEngine } from "../../markdown/engine"; import { QuartoAssistViewProvider } from "./webview"; import { CodeViewCellContext } from "editor-types"; diff --git a/apps/vscode/src/providers/convert.ts b/apps/vscode/src/providers/convert.ts index e59c46fd..87fb036d 100644 --- a/apps/vscode/src/providers/convert.ts +++ b/apps/vscode/src/providers/convert.ts @@ -20,7 +20,8 @@ import { commands, LogOutputChannel, Uri, window } from "vscode"; import { QuartoContext } from "quarto-core"; import { Command } from "../core/command"; -import { canPreviewDoc, findQuartoEditor } from "../core/doc"; +import { canPreviewDoc } from "../core/doc"; +import { findQuartoEditor } from "../core/quartoEditor"; import { promptForQuartoInstallation } from "../core/quarto"; import { MarkdownEngine } from "../markdown/engine"; diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index 94e6ff4f..2f835fb2 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -46,7 +46,8 @@ import { CodeViewActiveBlockContext, CodeViewSelectionAction, HostContext, NavLo import { getNonce } from "../../core/nonce"; import { isWindows } from "../../core/platform"; -import { isQuartoDoc, QuartoEditorBase } from "../../core/doc"; +import { isQuartoDoc } from "../../core/doc"; +import { QuartoEditorBase } from "../../core/quartoEditor"; import { Command } from "../../core/command"; import { visualEditorClient, visualEditorServer } from "./connection"; diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index 2d332626..d2c9f3e9 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -25,7 +25,8 @@ import { previewDoc, } from "./preview"; import { MarkdownEngine } from "../../markdown/engine"; -import { canPreviewDoc, findQuartoEditor, isQuartoNotebookEditor } from "../../core/doc"; +import { canPreviewDoc } from "../../core/doc"; +import { findQuartoEditor, isQuartoNotebookEditor } from "../../core/quartoEditor"; import { renderOnSave } from "./preview-util"; import { documentFrontMatterYaml } from "../../markdown/document"; import { FormatQuickPickItem, RenderCommand } from "../render"; diff --git a/apps/vscode/src/providers/preview/preview-util.ts b/apps/vscode/src/providers/preview/preview-util.ts index 52f768c4..9a2188af 100644 --- a/apps/vscode/src/providers/preview/preview-util.ts +++ b/apps/vscode/src/providers/preview/preview-util.ts @@ -20,7 +20,7 @@ import { metadataFilesForDocument, yamlFromMetadataFile, } from "quarto-core"; -import { isQuartoNotebookEditor, QuartoEditor } from "../../core/doc"; +import { isQuartoNotebookEditor, QuartoEditor } from "../../core/quartoEditor"; import { MarkdownEngine } from "../../markdown/engine"; import { documentFrontMatter } from "../../markdown/document"; diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index c0c727ae..3d0f3a0a 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -47,15 +47,16 @@ import { previewCommands } from "./commands"; import { Command } from "../../core/command"; import { canPreviewDoc, - findQuartoEditor, - isQuartoNotebookEditor, - isQuartoTextEditor, - preserveEditorFocus, previewDirForDocument, quartoCanRenderScript, - QuartoEditor, validatateQuartoCanRender, } from "../../core/doc"; +import { preserveEditorFocus } from "../../core/quartoEditor"; +import { + findQuartoEditor, + isQuartoNotebookEditor, + isQuartoTextEditor, QuartoEditor +} from "../../core/quartoEditor"; import { PreviewOutputSink } from "./preview-output"; import { isHtmlContent, isTextContent, isPdfContent } from "core-node"; diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index a94728d1..b511b1c8 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -23,7 +23,8 @@ import { Command } from "../core/command"; import { MarkdownEngine } from "../markdown/engine"; import { promptForQuartoInstallation } from "../core/quarto"; -import { QuartoEditor, canPreviewDoc, findQuartoEditor, isQuartoNotebookEditor } from "../core/doc"; +import { canPreviewDoc } from "../core/doc"; +import { QuartoEditor, findQuartoEditor, isQuartoNotebookEditor } from "../core/quartoEditor"; import { commands } from "vscode"; import { killTerminal, sendTerminalCommand, terminalCommand, terminalEnv, terminalOptions } from "../core/terminal"; import { QuickPickItem } from "vscode"; diff --git a/apps/vscode/src/providers/webview.ts b/apps/vscode/src/providers/webview.ts index fea04d17..a2a1c1c9 100644 --- a/apps/vscode/src/providers/webview.ts +++ b/apps/vscode/src/providers/webview.ts @@ -24,7 +24,7 @@ import vscode, { } from "vscode"; import { Disposable } from "../core/dispose"; -import { preserveEditorFocus } from "../core/doc"; +import { preserveEditorFocus } from "../core/quartoEditor"; import { getNonce } from "../core/nonce"; import { ExtensionHost, HostWebviewPanel } from "../host"; From 0c6044e67f80c4df533992a5d117d86f90ba1ac6 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 18:58:30 +0200 Subject: [PATCH 13/20] `viewColumn` is only used by visual editor --- apps/vscode/src/core/quartoEditor.ts | 5 +---- apps/vscode/src/providers/editor/editor.ts | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index a9f221d7..83807d32 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -9,7 +9,6 @@ export type QuartoEditor = QuartoTextEditor | QuartoNotebookEditor | QuartoVisua export interface QuartoEditorBase { document: vscode.TextDocument; activate: () => Promise; - viewColumn?: vscode.ViewColumn; /** * Get the slide index for the current cursor position. @@ -112,7 +111,6 @@ export function quartoTextEditor( return 0; } }, - viewColumn: editor.viewColumn, textEditor: editor, }; } @@ -127,10 +125,9 @@ function quartoNotebookEditor( activate: async () => { await vscode.window.showNotebookDocument( notebookEditor.notebook, - { viewColumn: notebookEditor.viewColumn, preserveFocus: false } + { preserveFocus: false } ); }, - viewColumn: notebookEditor.viewColumn, notebook: notebookEditor.notebook, }; } diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index 2f835fb2..ea9c53a5 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -76,6 +76,7 @@ export interface QuartoVisualEditor extends QuartoEditorBase { hasFocus(): Promise; getActiveBlockContext(): Promise; setBlockSelection(context: CodeViewActiveBlockContext, action: CodeViewSelectionAction): Promise; + viewColumn: ViewColumn | undefined; } export function activateEditor( From f1101950275ba8a1d5e53b8a37babf051b451ff4 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:02:37 +0200 Subject: [PATCH 14/20] replace `QuartoNotebookEditor.notebook` with `notebookEditor` --- apps/vscode/src/core/quartoEditor.ts | 4 ++-- apps/vscode/src/providers/preview/commands.ts | 2 +- apps/vscode/src/providers/render.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index 83807d32..7c67b53e 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -26,7 +26,7 @@ export interface QuartoTextEditor extends QuartoEditorBase { export interface QuartoNotebookEditor extends QuartoEditorBase { type: 'notebook'; - notebook: vscode.NotebookDocument; + notebookEditor: vscode.NotebookEditor; } export function isQuartoNotebookEditor(editor: QuartoEditor): editor is QuartoNotebookEditor { @@ -128,7 +128,7 @@ function quartoNotebookEditor( { preserveFocus: false } ); }, - notebook: notebookEditor.notebook, + notebookEditor, }; } diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index d2c9f3e9..129b1fc3 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -67,7 +67,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { if (format === kChooseFormat) { const frontMatter = isQuartoNotebookEditor(targetEditor) - ? notebookFrontMatterYaml(targetEditor.notebook) + ? notebookFrontMatterYaml(targetEditor.notebookEditor.notebook) : documentFrontMatterYaml(this.engine_, targetEditor.document); const formats = quartoDocumentFormats(this.quartoContext(), targetEditor.document.uri.fsPath, frontMatter); diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index b511b1c8..93f874a6 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -137,7 +137,7 @@ class RenderDocumentCommand extends RenderCommand return new Promise((resolve) => { const frontMatter = isQuartoNotebookEditor(targetEditor) - ? notebookFrontMatterYaml(targetEditor.notebook) + ? notebookFrontMatterYaml(targetEditor.notebookEditor.notebook) : documentFrontMatterYaml(this.engine_, targetEditor.document); const kDeclaredFormats = "Declared Formats"; From d7e5df6098b9a1d499325f07de0b4abfbae92429 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:08:33 +0200 Subject: [PATCH 15/20] extract `editorFrontMatterYaml` --- apps/vscode/src/core/quartoEditor.ts | 10 ++++++++++ apps/vscode/src/providers/preview/commands.ts | 8 ++------ apps/vscode/src/providers/render.ts | 8 ++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index 7c67b53e..2bf1e1fb 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -3,6 +3,8 @@ import { QuartoContext } from "quarto-core"; import { MarkdownEngine } from "../markdown/engine"; import { revealSlideIndex } from "../markdown/reveal"; import { QuartoVisualEditor, VisualEditorProvider } from "../providers/editor/editor"; +import { notebookFrontMatterYaml } from "../markdown/notebook"; +import { documentFrontMatterYaml } from "../markdown/document"; export type QuartoEditor = QuartoTextEditor | QuartoNotebookEditor | QuartoVisualEditor; @@ -159,3 +161,11 @@ export function preserveEditorFocus(editor?: QuartoEditor) { } } } + + +export function editorFrontMatterYaml(editor: QuartoEditor, engine: MarkdownEngine): string { + if (isQuartoNotebookEditor(editor)) { + return notebookFrontMatterYaml(editor.notebookEditor.notebook); + } + return documentFrontMatterYaml(engine, editor.document); +} diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index 129b1fc3..b5755615 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -26,12 +26,10 @@ import { } from "./preview"; import { MarkdownEngine } from "../../markdown/engine"; import { canPreviewDoc } from "../../core/doc"; -import { findQuartoEditor, isQuartoNotebookEditor } from "../../core/quartoEditor"; +import { editorFrontMatterYaml, findQuartoEditor, isQuartoNotebookEditor } from "../../core/quartoEditor"; import { renderOnSave } from "./preview-util"; -import { documentFrontMatterYaml } from "../../markdown/document"; import { FormatQuickPickItem, RenderCommand } from "../render"; import { QuickPickItemKind } from "vscode"; -import { notebookFrontMatterYaml } from "../../markdown/notebook"; export function previewCommands( quartoContext: QuartoContext, @@ -66,9 +64,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { if (render) { if (format === kChooseFormat) { - const frontMatter = isQuartoNotebookEditor(targetEditor) - ? notebookFrontMatterYaml(targetEditor.notebookEditor.notebook) - : documentFrontMatterYaml(this.engine_, targetEditor.document); + const frontMatter = editorFrontMatterYaml(targetEditor, this.engine_); const formats = quartoDocumentFormats(this.quartoContext(), targetEditor.document.uri.fsPath, frontMatter); if (formats) { diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index 93f874a6..b85ec9ed 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -24,14 +24,12 @@ import { Command } from "../core/command"; import { MarkdownEngine } from "../markdown/engine"; import { promptForQuartoInstallation } from "../core/quarto"; import { canPreviewDoc } from "../core/doc"; -import { QuartoEditor, findQuartoEditor, isQuartoNotebookEditor } from "../core/quartoEditor"; +import { QuartoEditor, editorFrontMatterYaml, findQuartoEditor, isQuartoNotebookEditor } from "../core/quartoEditor"; import { commands } from "vscode"; import { killTerminal, sendTerminalCommand, terminalCommand, terminalEnv, terminalOptions } from "../core/terminal"; import { QuickPickItem } from "vscode"; -import { documentFrontMatterYaml } from "../markdown/document"; import { QuickPickItemKind } from "vscode"; import { Uri } from "vscode"; -import { notebookFrontMatterYaml } from "../markdown/notebook"; export function activateRender(quartoContext: QuartoContext, engine: MarkdownEngine): Command[] { @@ -136,9 +134,7 @@ class RenderDocumentCommand extends RenderCommand private async resolveFormat(targetEditor: QuartoEditor) { return new Promise((resolve) => { - const frontMatter = isQuartoNotebookEditor(targetEditor) - ? notebookFrontMatterYaml(targetEditor.notebookEditor.notebook) - : documentFrontMatterYaml(this.engine_, targetEditor.document); + const frontMatter = editorFrontMatterYaml(targetEditor, this.engine_); const kDeclaredFormats = "Declared Formats"; const kOtherFormats = "Other Formats"; From 23b69efc987bc3f8e660fef854855214cb3783e0 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:14:56 +0200 Subject: [PATCH 16/20] extract `selectAndRevealRange` --- apps/vscode/src/core/quartoEditor.ts | 18 ++++++++++++ apps/vscode/src/providers/preview/preview.ts | 30 ++++++-------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index 2bf1e1fb..d9bc59ea 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -169,3 +169,21 @@ export function editorFrontMatterYaml(editor: QuartoEditor, engine: MarkdownEngi } return documentFrontMatterYaml(engine, editor.document); } + +export function selectAndRevealRange(editor: QuartoEditor, range: vscode.Range): void { + if (isQuartoTextEditor(editor)) { + // if the current selection is outside of the error region then + // navigate to the top of the error region + const textEditor = editor.textEditor; + if ( + textEditor.selection.active.isBefore(range.start) || + textEditor.selection.active.isAfter(range.end) + ) { + textEditor.selection = new vscode.Selection(range.start, range.start); + textEditor.revealRange( + range, + vscode.TextEditorRevealType.InCenterIfOutsideViewport + ); + } + } +} diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 3d0f3a0a..7dde6c01 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -26,13 +26,11 @@ import vscode, { MessageItem, Terminal, TextDocument, - Selection, Range, Uri, ViewColumn, window, Position, - TextEditorRevealType, NotebookDocument, ProgressLocation, CancellationToken, @@ -51,11 +49,11 @@ import { quartoCanRenderScript, validatateQuartoCanRender, } from "../../core/doc"; -import { preserveEditorFocus } from "../../core/quartoEditor"; +import { preserveEditorFocus, selectAndRevealRange } from "../../core/quartoEditor"; import { findQuartoEditor, isQuartoNotebookEditor, - isQuartoTextEditor, QuartoEditor + QuartoEditor } from "../../core/quartoEditor"; import { PreviewOutputSink } from "./preview-output"; import { isHtmlContent, isTextContent, isPdfContent } from "core-node"; @@ -641,24 +639,12 @@ class PreviewManager { (doc) => doc.uri.fsPath === fileUri.fsPath ); if (editor) { - // TODO: Should probably handle notebooks and visual editors here - if (isQuartoTextEditor(editor)) { - // if the current selection is outside of the error region then - // navigate to the top of the error region - const errPos = new Position(errorLoc.lineBegin - 1, 0); - const errEndPos = new Position(errorLoc.lineEnd - 1, 0); - const textEditor = editor.textEditor; - if ( - textEditor.selection.active.isBefore(errPos) || - textEditor.selection.active.isAfter(errEndPos) - ) { - textEditor.selection = new Selection(errPos, errPos); - textEditor.revealRange( - new Range(errPos, errPos), - TextEditorRevealType.InCenterIfOutsideViewport - ); - } - } + // if the current selection is outside of the error region then + // navigate to the top of the error region + const errPos = new Position(errorLoc.lineBegin - 1, 0); + const errEndPos = new Position(errorLoc.lineEnd - 1, 0); + const errRange = new Range(errPos, errEndPos); + selectAndRevealRange(editor, errRange); preserveEditorFocus(editor); } } From 3d0f27306f3e449a686ffa01907bf31e415160a4 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:18:44 +0200 Subject: [PATCH 17/20] `activate` notebooks as well --- apps/vscode/src/providers/preview/commands.ts | 7 ++----- apps/vscode/src/providers/preview/preview.ts | 8 ++------ apps/vscode/src/providers/render.ts | 10 +++------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index b5755615..7e9bad1a 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -26,7 +26,7 @@ import { } from "./preview"; import { MarkdownEngine } from "../../markdown/engine"; import { canPreviewDoc } from "../../core/doc"; -import { editorFrontMatterYaml, findQuartoEditor, isQuartoNotebookEditor } from "../../core/quartoEditor"; +import { editorFrontMatterYaml, findQuartoEditor } from "../../core/quartoEditor"; import { renderOnSave } from "./preview-util"; import { FormatQuickPickItem, RenderCommand } from "../render"; import { QuickPickItemKind } from "vscode"; @@ -108,10 +108,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { } } else { // show the editor - // TODO: Why can't we show notebooks too? Maybe because activate shows a cell's text editor? - if (!isQuartoNotebookEditor(targetEditor)) { - await targetEditor.activate(); - } + await targetEditor.activate(); // save (will trigger render b/c renderOnSave is enabled) await commands.executeCommand("workbench.action.files.save"); diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index 7dde6c01..fe9a621c 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -200,9 +200,7 @@ export async function previewDoc( // if this wasn't a renderOnSave then activate the editor and save if (!renderOnSave) { // activate the editor - if (!isQuartoNotebookEditor(editor)) { - await editor.activate(); - } + await editor.activate(); await commands.executeCommand("workbench.action.files.save"); if (editor.document.isDirty) { @@ -240,9 +238,7 @@ export async function previewDoc( // focus the editor (sometimes the terminal steals focus) if (!renderOnSave) { - if (!isQuartoNotebookEditor(previewEditor)) { - await previewEditor.activate(); - } + await previewEditor.activate(); } } } diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index b85ec9ed..456b5eac 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -24,7 +24,7 @@ import { Command } from "../core/command"; import { MarkdownEngine } from "../markdown/engine"; import { promptForQuartoInstallation } from "../core/quarto"; import { canPreviewDoc } from "../core/doc"; -import { QuartoEditor, editorFrontMatterYaml, findQuartoEditor, isQuartoNotebookEditor } from "../core/quartoEditor"; +import { QuartoEditor, editorFrontMatterYaml, findQuartoEditor } from "../core/quartoEditor"; import { commands } from "vscode"; import { killTerminal, sendTerminalCommand, terminalCommand, terminalEnv, terminalOptions } from "../core/terminal"; import { QuickPickItem } from "vscode"; @@ -93,9 +93,7 @@ class RenderDocumentCommand extends RenderCommand if (targetEditor) { // show the editor and save - if (!isQuartoNotebookEditor(targetEditor)) { - await targetEditor.activate(); - } + await targetEditor.activate(); await commands.executeCommand("workbench.action.files.save"); // kill any existing terminal @@ -123,9 +121,7 @@ class RenderDocumentCommand extends RenderCommand await sendTerminalCommand(terminal, env, this.quartoContext(), cmd); // focus the editor (sometimes the terminal steals focus) - if (!isQuartoNotebookEditor(targetEditor)) { - await targetEditor.activate(); - } + await targetEditor.activate(); } } From 80511b5cd7ccd1300ba5e26de026fa4cada27c60 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:22:37 +0200 Subject: [PATCH 18/20] move `selectAndRevealRange` to `QuartoEditor` --- apps/vscode/src/core/quartoEditor.ts | 37 ++++++++++---------- apps/vscode/src/providers/editor/editor.ts | 3 ++ apps/vscode/src/providers/preview/preview.ts | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index d9bc59ea..3f45106a 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -19,6 +19,8 @@ export interface QuartoEditorBase { * slide parsing e.g. `QuartoNotebookEditor`. */ slideIndex?: () => Promise; + + selectAndRevealRange: (range: vscode.Range) => void; } export interface QuartoTextEditor extends QuartoEditorBase { @@ -113,6 +115,20 @@ export function quartoTextEditor( return 0; } }, + selectAndRevealRange: (range: vscode.Range) => { + // if the current selection is outside of the error region then + // navigate to the top of the error region + if ( + editor.selection.active.isBefore(range.start) || + editor.selection.active.isAfter(range.end) + ) { + editor.selection = new vscode.Selection(range.start, range.start); + editor.revealRange( + range, + vscode.TextEditorRevealType.InCenterIfOutsideViewport + ); + } + }, textEditor: editor, }; } @@ -130,6 +146,9 @@ function quartoNotebookEditor( { preserveFocus: false } ); }, + selectAndRevealRange: (range: vscode.Range) => { + // Not implemented yet. + }, notebookEditor, }; } @@ -169,21 +188,3 @@ export function editorFrontMatterYaml(editor: QuartoEditor, engine: MarkdownEngi } return documentFrontMatterYaml(engine, editor.document); } - -export function selectAndRevealRange(editor: QuartoEditor, range: vscode.Range): void { - if (isQuartoTextEditor(editor)) { - // if the current selection is outside of the error region then - // navigate to the top of the error region - const textEditor = editor.textEditor; - if ( - textEditor.selection.active.isBefore(range.start) || - textEditor.selection.active.isAfter(range.end) - ) { - textEditor.selection = new vscode.Selection(range.start, range.start); - textEditor.revealRange( - range, - vscode.TextEditorRevealType.InCenterIfOutsideViewport - ); - } - } -} diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index ea9c53a5..37e688b1 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -307,6 +307,9 @@ export class VisualEditorProvider implements CustomTextEditorProvider { setBlockSelection: async (context, action) => { await editor.editor.setBlockSelection(context, action); }, + selectAndRevealRange: (range: Range) => { + // Not implemented yet. + }, viewColumn: editor.webviewPanel.viewColumn }; } else { diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index fe9a621c..ee273ba5 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -640,7 +640,7 @@ class PreviewManager { const errPos = new Position(errorLoc.lineBegin - 1, 0); const errEndPos = new Position(errorLoc.lineEnd - 1, 0); const errRange = new Range(errPos, errEndPos); - selectAndRevealRange(editor, errRange); + editor.selectAndRevealRange(errRange); preserveEditorFocus(editor); } } From 09badcc9db75bb62e1d2b926d4234bc358e9263e Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:38:31 +0200 Subject: [PATCH 19/20] add `QuartoEditor.preseveEditorFocus` and extract `activeQuartoEditor` --- apps/vscode/src/core/quartoEditor.ts | 109 +++++++++---------- apps/vscode/src/providers/assist/commands.ts | 4 +- apps/vscode/src/providers/editor/editor.ts | 23 +++- apps/vscode/src/providers/preview/preview.ts | 3 +- apps/vscode/src/providers/webview.ts | 7 +- 5 files changed, 73 insertions(+), 73 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index 3f45106a..0c05ae73 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -21,6 +21,8 @@ export interface QuartoEditorBase { slideIndex?: () => Promise; selectAndRevealRange: (range: vscode.Range) => void; + + preserveEditorFocus(): void; } export interface QuartoTextEditor extends QuartoEditorBase { @@ -48,7 +50,32 @@ export function isQuartoVisualEditor(editor: QuartoEditor): editor is QuartoVisu export function findQuartoEditor( engine: MarkdownEngine, context: QuartoContext, - filter: (doc: vscode.TextDocument | vscode.NotebookDocument) => boolean + filter: (doc: vscode.TextDocument | vscode.NotebookDocument) => boolean = () => true, +): QuartoEditor | undefined { + const activeEditor = activeQuartoEditor(filter, engine, context); + if (activeEditor) { + return activeEditor; + } + + // visible visual editor (sometime it loses track of 'active' so we need to use 'visible') + const visibleVisualEditor = VisualEditorProvider.activeEditor(true); + if (visibleVisualEditor && filter(visibleVisualEditor.document)) { + return visibleVisualEditor; + } + + // visible text editors + const visibleEditor = vscode.window.visibleTextEditors.find((editor) => filter(editor.document)); + if (visibleEditor) { + return quartoTextEditor(visibleEditor, engine, context); + } + + return undefined; +} + +export function activeQuartoEditor( + filter: (doc: vscode.TextDocument | vscode.NotebookDocument) => boolean = () => true, + engine?: MarkdownEngine, + context?: QuartoContext ): QuartoEditor | undefined { // first check for an active visual editor const activeVisualEditor = VisualEditorProvider.activeEditor(); @@ -59,50 +86,34 @@ export function findQuartoEditor( // then check for active notebook editor const notebookEditor = vscode.window.activeNotebookEditor; if (notebookEditor && filter(notebookEditor.notebook)) { - // TODO: Why is cellAt always defined?... - const firstCellDoc = notebookEditor.notebook.cellAt(0)?.document; - if (firstCellDoc) { - return quartoNotebookEditor(notebookEditor, firstCellDoc); - } + return quartoNotebookEditor(notebookEditor); } // active text editor const textEditor = vscode.window.activeTextEditor; if (textEditor && filter(textEditor.document)) { return quartoTextEditor(textEditor, engine, context); - // check visible text editors - } else { - // visible visual editor (sometime it loses track of 'active' so we need to use 'visible') - const visibleVisualEditor = VisualEditorProvider.activeEditor(true); - if (visibleVisualEditor && filter(visibleVisualEditor.document)) { - return visibleVisualEditor; - } - - // visible text editors - const visibleEditor = vscode.window.visibleTextEditors.find((editor) => filter(editor.document) - ); - if (visibleEditor) { - return quartoTextEditor(visibleEditor, engine, context); - } else { - return undefined; - } } + + return undefined; } export function quartoTextEditor( editor: vscode.TextEditor, engine?: MarkdownEngine, context?: QuartoContext): QuartoTextEditor { + const activate = async () => { + await vscode.window.showTextDocument( + editor.document, + editor.viewColumn, + false + ); + }; + return { type: 'text', document: editor.document, - activate: async () => { - await vscode.window.showTextDocument( - editor.document, - editor.viewColumn, - false - ); - }, + activate, slideIndex: async () => { if (engine && context) { return await revealSlideIndex( @@ -129,14 +140,20 @@ export function quartoTextEditor( ); } }, + preserveEditorFocus: () => { + setTimeout(() => { + activate(); + }, 200); + }, textEditor: editor, }; } function quartoNotebookEditor( notebookEditor: vscode.NotebookEditor, - firstCellDoc: vscode.TextDocument ): QuartoNotebookEditor { + // TODO: Why is cellAt always defined?... + const firstCellDoc = notebookEditor.notebook.cellAt(0)?.document; return { type: 'notebook', document: firstCellDoc, @@ -149,39 +166,13 @@ function quartoNotebookEditor( selectAndRevealRange: (range: vscode.Range) => { // Not implemented yet. }, + preserveEditorFocus: () => { + // Not implemented yet. + }, notebookEditor, }; } -export function preserveEditorFocus(editor?: QuartoEditor) { - // focus the editor (sometimes the terminal steals focus) - editor = - editor || - (vscode.window.activeTextEditor - ? quartoTextEditor(vscode.window.activeTextEditor) - : undefined); - if (editor) { - if (!isQuartoNotebookEditor(editor)) { - setTimeout(() => { - if (editor) { - editor.activate(); - } - }, 200); - } - } else { - // see if there is a visual editor we should be preserving focus for - const visualEditor = VisualEditorProvider.activeEditor(); - if (visualEditor) { - setTimeout(async () => { - if (!(await visualEditor.hasFocus())) { - await visualEditor.activate(); - } - }, 200); - } - } -} - - export function editorFrontMatterYaml(editor: QuartoEditor, engine: MarkdownEngine): string { if (isQuartoNotebookEditor(editor)) { return notebookFrontMatterYaml(editor.notebookEditor.notebook); diff --git a/apps/vscode/src/providers/assist/commands.ts b/apps/vscode/src/providers/assist/commands.ts index 61d04f5c..fc07d551 100644 --- a/apps/vscode/src/providers/assist/commands.ts +++ b/apps/vscode/src/providers/assist/commands.ts @@ -16,7 +16,7 @@ import { Position, Selection, window, commands } from "vscode"; import { Command } from "../../core/command"; import { isQuartoDoc } from "../../core/doc"; -import { preserveEditorFocus } from "../../core/quartoEditor"; +import { activeQuartoEditor } from "../../core/quartoEditor"; import { MarkdownEngine } from "../../markdown/engine"; import { QuartoAssistViewProvider } from "./webview"; import { CodeViewCellContext } from "editor-types"; @@ -81,7 +81,7 @@ export class CodeViewAssistCommand implements Command { function activateAssistPanel(provider: QuartoAssistViewProvider) { // attempt to activate (if we fail to the view has been closed so // recreate it by calling focus) - preserveEditorFocus(); + activeQuartoEditor()?.preserveEditorFocus(); if (!provider.activate()) { commands.executeCommand("quarto-assist.focus"); } diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index 37e688b1..7939689d 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -289,15 +289,19 @@ export class VisualEditorProvider implements CustomTextEditorProvider { public static activeEditor(includeVisible?: boolean): QuartoVisualEditor | undefined { const editor = this.visualEditors.activeEditor(includeVisible); if (editor) { + const hasFocus = async () => { + return await editor.editor.isFocused(); + }; + + const activate = async () => { + activateVisualEditor(editor); + }; + return { type: 'visual', document: editor.document, - hasFocus: async () => { - return await editor.editor.isFocused(); - }, - activate: async () => { - activateVisualEditor(editor); - }, + hasFocus, + activate, slideIndex: async () => { return await editor.editor.getSlideIndex(); }, @@ -310,6 +314,13 @@ export class VisualEditorProvider implements CustomTextEditorProvider { selectAndRevealRange: (range: Range) => { // Not implemented yet. }, + preserveEditorFocus: () => { + setTimeout(async () => { + if (!(await hasFocus())) { + await activate(); + } + }, 200); + }, viewColumn: editor.webviewPanel.viewColumn }; } else { diff --git a/apps/vscode/src/providers/preview/preview.ts b/apps/vscode/src/providers/preview/preview.ts index ee273ba5..e0376a63 100644 --- a/apps/vscode/src/providers/preview/preview.ts +++ b/apps/vscode/src/providers/preview/preview.ts @@ -49,7 +49,6 @@ import { quartoCanRenderScript, validatateQuartoCanRender, } from "../../core/doc"; -import { preserveEditorFocus, selectAndRevealRange } from "../../core/quartoEditor"; import { findQuartoEditor, isQuartoNotebookEditor, @@ -641,7 +640,7 @@ class PreviewManager { const errEndPos = new Position(errorLoc.lineEnd - 1, 0); const errRange = new Range(errPos, errEndPos); editor.selectAndRevealRange(errRange); - preserveEditorFocus(editor); + editor.preserveEditorFocus(); } } } diff --git a/apps/vscode/src/providers/webview.ts b/apps/vscode/src/providers/webview.ts index a2a1c1c9..8ed1e7ed 100644 --- a/apps/vscode/src/providers/webview.ts +++ b/apps/vscode/src/providers/webview.ts @@ -13,20 +13,19 @@ * */ -import vscode, { +import { Uri, WebviewPanel, window, ViewColumn, EventEmitter, ExtensionContext, - WebviewPanelOnDidChangeViewStateEvent, } from "vscode"; import { Disposable } from "../core/dispose"; -import { preserveEditorFocus } from "../core/quartoEditor"; import { getNonce } from "../core/nonce"; import { ExtensionHost, HostWebviewPanel } from "../host"; +import { activeQuartoEditor } from "../core/quartoEditor"; export interface ShowOptions { readonly preserveFocus?: boolean; @@ -73,7 +72,7 @@ export class QuartoWebviewManager, S> { } this.resolveOnShow(); if (options?.preserveFocus) { - preserveEditorFocus(); + activeQuartoEditor()?.preserveEditorFocus(); } } From d8d1730bfa8fbc8214c1417717a4931c89016e32 Mon Sep 17 00:00:00 2001 From: seem Date: Fri, 12 Jun 2026 19:42:00 +0200 Subject: [PATCH 20/20] add `QuartoEditor.frontMatterYaml` --- apps/vscode/src/core/quartoEditor.ts | 15 +++++++-------- apps/vscode/src/providers/editor/editor.ts | 2 ++ apps/vscode/src/providers/preview/commands.ts | 4 ++-- apps/vscode/src/providers/render.ts | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/vscode/src/core/quartoEditor.ts b/apps/vscode/src/core/quartoEditor.ts index 0c05ae73..5509d68f 100644 --- a/apps/vscode/src/core/quartoEditor.ts +++ b/apps/vscode/src/core/quartoEditor.ts @@ -9,6 +9,8 @@ import { documentFrontMatterYaml } from "../markdown/document"; export type QuartoEditor = QuartoTextEditor | QuartoNotebookEditor | QuartoVisualEditor; export interface QuartoEditorBase { + // TODO: Can we remove `document: TextDocument` from QuartoNotebookEditor? + // Many things use it for `uri` (easy) and `getText()` (harder). document: vscode.TextDocument; activate: () => Promise; @@ -23,6 +25,8 @@ export interface QuartoEditorBase { selectAndRevealRange: (range: vscode.Range) => void; preserveEditorFocus(): void; + + frontMatterYaml(engine: MarkdownEngine): string; } export interface QuartoTextEditor extends QuartoEditorBase { @@ -145,6 +149,7 @@ export function quartoTextEditor( activate(); }, 200); }, + frontMatterYaml: (engine) => documentFrontMatterYaml(engine, editor.document), textEditor: editor, }; } @@ -163,19 +168,13 @@ function quartoNotebookEditor( { preserveFocus: false } ); }, - selectAndRevealRange: (range: vscode.Range) => { + selectAndRevealRange: () => { // Not implemented yet. }, preserveEditorFocus: () => { // Not implemented yet. }, + frontMatterYaml: () => notebookFrontMatterYaml(notebookEditor.notebook), notebookEditor, }; } - -export function editorFrontMatterYaml(editor: QuartoEditor, engine: MarkdownEngine): string { - if (isQuartoNotebookEditor(editor)) { - return notebookFrontMatterYaml(editor.notebookEditor.notebook); - } - return documentFrontMatterYaml(engine, editor.document); -} diff --git a/apps/vscode/src/providers/editor/editor.ts b/apps/vscode/src/providers/editor/editor.ts index 7939689d..8974398c 100644 --- a/apps/vscode/src/providers/editor/editor.ts +++ b/apps/vscode/src/providers/editor/editor.ts @@ -68,6 +68,7 @@ import { } from "./toggle"; import { ExtensionHost } from "../../host"; import { TabInputCustom } from "vscode"; +import { documentFrontMatterYaml } from "../../markdown/document"; const kVisualModeConfirmed = "visualModeConfirmed"; @@ -321,6 +322,7 @@ export class VisualEditorProvider implements CustomTextEditorProvider { } }, 200); }, + frontMatterYaml: (engine) => documentFrontMatterYaml(engine, editor.document), viewColumn: editor.webviewPanel.viewColumn }; } else { diff --git a/apps/vscode/src/providers/preview/commands.ts b/apps/vscode/src/providers/preview/commands.ts index 7e9bad1a..43c24bb2 100644 --- a/apps/vscode/src/providers/preview/commands.ts +++ b/apps/vscode/src/providers/preview/commands.ts @@ -26,7 +26,7 @@ import { } from "./preview"; import { MarkdownEngine } from "../../markdown/engine"; import { canPreviewDoc } from "../../core/doc"; -import { editorFrontMatterYaml, findQuartoEditor } from "../../core/quartoEditor"; +import { findQuartoEditor } from "../../core/quartoEditor"; import { renderOnSave } from "./preview-util"; import { FormatQuickPickItem, RenderCommand } from "../render"; import { QuickPickItemKind } from "vscode"; @@ -64,7 +64,7 @@ abstract class PreviewDocumentCommandBase extends RenderCommand { if (render) { if (format === kChooseFormat) { - const frontMatter = editorFrontMatterYaml(targetEditor, this.engine_); + const frontMatter = targetEditor.frontMatterYaml(this.engine_); const formats = quartoDocumentFormats(this.quartoContext(), targetEditor.document.uri.fsPath, frontMatter); if (formats) { diff --git a/apps/vscode/src/providers/render.ts b/apps/vscode/src/providers/render.ts index 456b5eac..0f772027 100644 --- a/apps/vscode/src/providers/render.ts +++ b/apps/vscode/src/providers/render.ts @@ -24,7 +24,7 @@ import { Command } from "../core/command"; import { MarkdownEngine } from "../markdown/engine"; import { promptForQuartoInstallation } from "../core/quarto"; import { canPreviewDoc } from "../core/doc"; -import { QuartoEditor, editorFrontMatterYaml, findQuartoEditor } from "../core/quartoEditor"; +import { QuartoEditor, findQuartoEditor } from "../core/quartoEditor"; import { commands } from "vscode"; import { killTerminal, sendTerminalCommand, terminalCommand, terminalEnv, terminalOptions } from "../core/terminal"; import { QuickPickItem } from "vscode"; @@ -130,7 +130,7 @@ class RenderDocumentCommand extends RenderCommand private async resolveFormat(targetEditor: QuartoEditor) { return new Promise((resolve) => { - const frontMatter = editorFrontMatterYaml(targetEditor, this.engine_); + const frontMatter = targetEditor.frontMatterYaml(this.engine_); const kDeclaredFormats = "Declared Formats"; const kOtherFormats = "Other Formats";