Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 1.133.0

- Add custom pair colorization and highlighting for divs in qmds (<https://github.com/quarto-dev/quarto/pull/973>).


## 1.132.0 (Release on 2026-05-05)

- Added clickable document links for file paths in `_quarto.yml` files. File paths are now clickable and navigate directly to the referenced file (<https://github.com/quarto-dev/quarto/pull/906>).
Expand Down
41 changes: 41 additions & 0 deletions apps/vscode/src/core/throttle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* throttle.ts
*
* Copyright (C) 2026 by Posit Software, PBC
*
* Unless you have received this program directly from Posit Software pursuant
* to the terms of a commercial license agreement with Posit Software, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/

/**
* Creates a throttled version of a function.
* First call executes immediately, subsequent calls within the delay are coalesced.
*/
export function createThrottle(
fn: () => any,
getDelay: () => number
): () => any {
let timer: NodeJS.Timeout | undefined;
let pending = false;

return () => {
if (timer === undefined) {
fn();
timer = setTimeout(() => {
if (pending) {
fn();
pending = false;
}
timer = undefined;
}, getDelay());
} else {
pending = true;
}
};
}
4 changes: 4 additions & 0 deletions apps/vscode/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { activateBackgroundHighlighter } from "./providers/background";
import { activateYamlLinks } from "./providers/yaml-links";
import { activateYamlFilepathCompletions } from "./providers/yaml-filepath-completions";
import { activateContextKeySetter } from "./providers/context-keys";
import { activateDivBracketDecorations } from "./providers/div-brackets";
import { CommandManager } from "./core/command";
import { createQuartoExtensionApi, QuartoExtensionApi } from "./api";

Expand Down Expand Up @@ -221,6 +222,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<Quarto
// context setter
activateContextKeySetter(context, engine);

// div bracket decorations
activateDivBracketDecorations(context);

// commands
const commandManager = new CommandManager();
for (const cmd of commands) {
Expand Down
62 changes: 31 additions & 31 deletions apps/vscode/src/providers/background.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* background.ts
*
* Copyright (C) 2022 by Posit Software, PBC
* Copyright (C) 2026 by Posit Software, PBC
* Copyright (c) [2021] [Chris Bain] (https://github.com/baincd/vscode-markdown-color-plus/)
*
* Unless you have received this program directly from Posit Software pursuant
Expand All @@ -16,12 +16,12 @@


import * as vscode from "vscode";
import debounce from "lodash.debounce";

import { isQuartoDoc, kQuartoDocSelector } from "../core/doc";
import { MarkdownEngine } from "../markdown/engine";
import { isExecutableLanguageBlock } from "quarto-core";
import { vscRange } from "../core/range";
import { createThrottle } from "../core/throttle";

export function activateBackgroundHighlighter(
context: vscode.ExtensionContext,
Expand All @@ -32,7 +32,7 @@ export function activateBackgroundHighlighter(
vscode.workspace.onDidChangeConfiguration(
() => {
highlightingConfig.sync();
triggerUpdateAllEditorsDecorations(engine);
updateAllEditorsDecorationsThrottled(engine);
},
null,
context.subscriptions
Expand All @@ -45,10 +45,9 @@ export function activateBackgroundHighlighter(
if (!isQuartoDoc(doc)) {
clearEditorHighlightDecorations(vscode.window.activeTextEditor);
} else {
triggerUpdateActiveEditorDecorations(
updateActiveEditorDecorationsThrottled(
vscode.window.activeTextEditor,
engine,
highlightingConfig.delayMs()
engine
);
}
}
Expand All @@ -59,8 +58,13 @@ export function activateBackgroundHighlighter(

// update highlighting when visible text editors change
vscode.window.onDidChangeVisibleTextEditors(
(_editors) => {
triggerUpdateAllEditorsDecorations(engine);
(visibleEditors) => {
for (const editor of editorThrottledFunctions.keys()) {
if (!visibleEditors.includes(editor)) {
editorThrottledFunctions.delete(editor);
}
}
updateAllEditorsDecorationsThrottled(engine);
},
null,
context.subscriptions
Expand All @@ -73,11 +77,9 @@ export function activateBackgroundHighlighter(
return editor.document.uri.toString() === event.document.uri.toString();
});
if (visibleEditor) {
triggerUpdateActiveEditorDecorations(
updateActiveEditorDecorationsThrottled(
visibleEditor,
engine,
highlightingConfig.delayMs(),
true,
event.contentChanges.length === 1
? event.contentChanges[0].range.start
: undefined
Expand All @@ -97,11 +99,9 @@ export function activateBackgroundHighlighter(
token: vscode.CancellationToken
) {
if (document === vscode.window.activeTextEditor?.document) {
triggerUpdateActiveEditorDecorations(
updateActiveEditorDecorationsThrottled(
vscode.window.activeTextEditor,
engine,
highlightingConfig.delayMs(),
true,
position,
token
);
Expand All @@ -112,32 +112,32 @@ export function activateBackgroundHighlighter(
);

// highlight all editors at activation time
triggerUpdateAllEditorsDecorations(engine);
updateAllEditorsDecorationsThrottled(engine);
}

function triggerUpdateActiveEditorDecorations(
// Map of editors to their throttled update functions
const editorThrottledFunctions = new Map<vscode.TextEditor, () => void>();
function updateActiveEditorDecorationsThrottled(
editor: vscode.TextEditor,
engine: MarkdownEngine,
delay: number,
immediate?: boolean,
pos?: vscode.Position,
token?: vscode.CancellationToken
) {
debounce(
() => setEditorHighlightDecorations(editor, engine, pos, token),
delay,
{
leading: !!immediate,
}
)();
let throttled = editorThrottledFunctions.get(editor);
if (!throttled) {
throttled = createThrottle(
() => setEditorHighlightDecorations(editor, engine, pos, token),
() => highlightingConfig.delayMs()
);
editorThrottledFunctions.set(editor, throttled);
}
throttled();
}

function triggerUpdateAllEditorsDecorations(engine: MarkdownEngine) {
debounce(async () => {
for (const editor of vscode.window.visibleTextEditors) {
await setEditorHighlightDecorations(editor, engine);
}
}, highlightingConfig.delayMs())();
function updateAllEditorsDecorationsThrottled(engine: MarkdownEngine) {
for (const editor of vscode.window.visibleTextEditors) {
updateActiveEditorDecorationsThrottled(editor, engine);
}
}

async function setEditorHighlightDecorations(
Expand Down
1 change: 1 addition & 0 deletions apps/vscode/src/providers/context-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export function activateContextKeySetter(
vscode.workspace.onDidChangeTextDocument(event => {
const activeEditor = vscode.window.activeTextEditor;
if (activeEditor) {
// TODO: this debounce is being created and called immediately, which is not correct.
debounce(
() => {
setEditorContextKeys(activeEditor, engine);
Expand Down
Loading
Loading