From fca55a51421c9a949cd3208c0b12f9bc4c486166 Mon Sep 17 00:00:00 2001 From: squidfunk Date: Sat, 19 Sep 2026 18:37:32 +0200 Subject: [PATCH] feature: add project exclusion and inclusion settings (#124) Signed-off-by: squidfunk --- integrations/code/package.json | 14 +++++ integrations/code/src/extension/client.ts | 3 +- integrations/code/src/extension/project.ts | 63 +++++++++++++++++++++- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/integrations/code/package.json b/integrations/code/package.json index 7f00d3e..4c61a41 100644 --- a/integrations/code/package.json +++ b/integrations/code/package.json @@ -492,6 +492,20 @@ "type": "string", "default": "", "description": "Path to Zensical Studio, optional" + }, + "zensical.studio.projects.include": { + "type": "array", + "items": { "type": "string" }, + "default": [], + "scope": "resource", + "description": "Project-root globs relative to each workspace folder. Empty includes all discovered projects." + }, + "zensical.studio.projects.exclude": { + "type": "array", + "items": { "type": "string" }, + "default": [], + "scope": "resource", + "description": "Project-root globs relative to each workspace folder. Excludes take precedence over includes." } } } diff --git a/integrations/code/src/extension/client.ts b/integrations/code/src/extension/client.ts index a229c02..a90e219 100644 --- a/integrations/code/src/extension/client.ts +++ b/integrations/code/src/extension/client.ts @@ -34,6 +34,7 @@ import { import type { Context } from "./context"; import type { Studio } from "./studio"; +import { projectSelections } from "./project"; /* ---------------------------------------------------------------------------- * Functions @@ -82,7 +83,7 @@ export function createLanguageClient( { scheme: "file", language: "yaml", pattern: "**/mkdocs.yaml" }, ], outputChannel: context.getOutput(), - initializationOptions: { token: studio.token }, + initializationOptions: { token: studio.token, ...projectSelections() }, errorHandler: { error: (_error, _message, count) => { if (typeof count === "number" && count <= 3) { diff --git a/integrations/code/src/extension/project.ts b/integrations/code/src/extension/project.ts index 7a05c04..b8f1f26 100644 --- a/integrations/code/src/extension/project.ts +++ b/integrations/code/src/extension/project.ts @@ -58,6 +58,32 @@ interface DocumentState { * Functions * ------------------------------------------------------------------------- */ +/** + * Project-root patterns for every VS Code workspace folder. + * + * @returns Resource-scoped selections for server initialization and reloads + */ +export function projectSelections(): { + projects: Array<{ + workspaceUri: string; + include: string[]; + exclude: string[]; + }>; +} { + return { + projects: (vscode.workspace.workspaceFolders ?? []).map((folder) => { + const settings = vscode.workspace.getConfiguration( + "zensical.studio.projects", folder.uri, + ); + return { + workspaceUri: folder.uri.toString(), + include: settings.get("include", []), + exclude: settings.get("exclude", []), + }; + }), + }; +} + /** * Activate project-aware Markdown tagging. * @@ -72,6 +98,20 @@ export async function activateProjectMarkdown( const openDocuments = new Map(); const changingLanguage = new Set(); const roots = new Map(); + let projectUpdates = Promise.resolve(); + let projectUpdateQueued = false; + + // Refresh project selections in Studio when the configuration changes + const refreshProjects = (): void => { + if (projectUpdateQueued) return; + projectUpdateQueued = true; + projectUpdates = projectUpdates.then(async () => { + projectUpdateQueued = false; + await client.sendRequest("zensical/workspace/projects", projectSelections()); + }).catch((error) => { + context.log(`Failed to update project selection: ${String(error)}`); + }); + }; // Refresh managed Markdown roots for all workspace folders const refreshScopes = async (): Promise => { @@ -118,6 +158,11 @@ export async function activateProjectMarkdown( // Register event listeners for workspace changes and document lifecycle const subscriptions: Disposable[] = [ + vscode.workspace.onDidChangeConfiguration((event) => { + if (event.affectsConfiguration("zensical.studio.projects")) { + refreshProjects(); + } + }), client.onNotification("zensical/workspace/scopesChanged", () => { void refreshScopes(); }), @@ -210,9 +255,25 @@ async function tagDocument( return; } - // Skip documents that are not inside a managed root + // Restore documents Studio tagged before their project was excluded. const folderRoots = roots.get(folder.uri.toString()) ?? []; if (!isManaged(document.uri, folderRoots)) { + const key = document.uri.toString(); + const state = openDocuments.get(key); + if ( + state?.automaticallyTagged && !state.userOverride && + !changingLanguage.has(key) && + document.languageId === "python-markdown" + ) { + changingLanguage.add(key); + try { + await vscode.languages.setTextDocumentLanguage(document, "markdown"); + state.languageId = "markdown"; + state.automaticallyTagged = false; + } finally { + changingLanguage.delete(key); + } + } return; }