diff --git a/.gitignore b/.gitignore index eb0a1102..d87a955e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ public/dist /.luarc.json .vscode-test *.vsix +*.timestamp-*.mjs diff --git a/apps/lsp/src/config.ts b/apps/lsp/src/config.ts index 5b235803..7e199d30 100644 --- a/apps/lsp/src/config.ts +++ b/apps/lsp/src/config.ts @@ -42,7 +42,7 @@ export interface Settings { readonly mathjax: { readonly scale: number; readonly extensions: MathjaxSupportedExtension[]; - } + }; readonly symbols: { readonly exportToWorkspace: 'default' | 'all' | 'none'; }; @@ -125,7 +125,7 @@ function defaultSettings(): Settings { } } } - } + }; } @@ -136,6 +136,8 @@ export class ConfigurationManager extends Disposable { private _settings: Settings; private _logger: ILogger; + private _activeColorThemeKind: "light" | "dark" = "dark"; + private _themeExplicitlySet = false; constructor( private readonly connection_: Connection, @@ -182,6 +184,18 @@ export class ConfigurationManager extends Disposable { } } }; + + // Fallback: try to detect theme from name if we haven't received an explicit notification yet + // This is a best-effort approach for compatibility, but won't work with autoDetectColorScheme + // Only apply fallback if theme hasn't been explicitly set via notification + if (!this._themeExplicitlySet) { + if (this._settings.workbench.colorTheme.includes("Light")) { + this._activeColorThemeKind = "light"; + } else if (this._settings.workbench.colorTheme.includes("Dark")) { + this._activeColorThemeKind = "dark"; + } + } + this._onDidChangeConfiguration.fire(this._settings); } @@ -207,6 +221,18 @@ export class ConfigurationManager extends Disposable { public getSettings(): Settings { return this._settings; } + + public setActiveColorThemeKind(kind: "light" | "dark") { + if (this._activeColorThemeKind !== kind) { + this._activeColorThemeKind = kind; + this._themeExplicitlySet = true; + this._onDidChangeConfiguration.fire(this._settings); + } + } + + public getActiveColorThemeKind(): "light" | "dark" { + return this._activeColorThemeKind; + } } export function lsConfiguration(configManager: ConfigurationManager): LsConfiguration { @@ -231,8 +257,7 @@ export function lsConfiguration(configManager: ConfigurationManager): LsConfigur } }, get colorTheme(): "light" | "dark" { - const settings = configManager.getSettings(); - return settings.workbench.colorTheme.includes("Light") ? "light" : "dark"; + return configManager.getActiveColorThemeKind(); }, get mathjaxScale(): number { return configManager.getSettings().quarto.mathjax.scale; @@ -243,7 +268,7 @@ export function lsConfiguration(configManager: ConfigurationManager): LsConfigur get exportSymbolsToWorkspace(): 'default' | 'all' | 'none' { return configManager.getSettings().quarto.symbols.exportToWorkspace; } - } + }; } export function getDiagnosticsOptions(configManager: ConfigurationManager): DiagnosticOptions { diff --git a/apps/lsp/src/index.ts b/apps/lsp/src/index.ts index 0aabee6a..2b0b356e 100644 --- a/apps/lsp/src/index.ts +++ b/apps/lsp/src/index.ts @@ -224,6 +224,17 @@ connection.onInitialized(async () => { logger.setConfigurationManager(configManager); } + // listen for color theme changes from the client + connection.onNotification("quarto/didChangeActiveColorTheme", (params: { kind: string; }) => { + logger.logNotification('didChangeActiveColorTheme'); + // Validate the theme kind before using it + if (params.kind === "light" || params.kind === "dark") { + configManager.setActiveColorThemeKind(params.kind); + } else { + logger.logError(`Invalid theme kind received: ${params.kind}`); + } + }); + // initialize connection to quarto const workspaceFolders = await connection.workspace.getWorkspaceFolders(); const workspaceDir = workspaceFolders?.length diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index c38e124a..90ca06ec 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -24,7 +24,9 @@ import { Definition, LogOutputChannel, Uri, - Diagnostic + Diagnostic, + window, + ColorThemeKind } from "vscode"; import { LanguageClient, @@ -153,12 +155,33 @@ export async function activateLsp( clientOptions ); + // Helper to send current theme to LSP server + const sendThemeNotification = () => { + if (client) { + // Map VS Code theme kinds to light/dark. HighContrast themes are treated as dark + // since they typically have light text on dark backgrounds + const kind = (window.activeColorTheme.kind === ColorThemeKind.Light || window.activeColorTheme.kind === ColorThemeKind.HighContrastLight) ? "light" : "dark"; + client.sendNotification("quarto/didChangeActiveColorTheme", { kind }); + } + }; + + // Listen for theme changes and notify the server + context.subscriptions.push( + window.onDidChangeActiveColorTheme(() => { + sendThemeNotification(); + }) + ); + // return once the server is running return new Promise((resolve, reject) => { const handler = client.onDidChangeState(e => { if (e.newState === State.Running) { handler.dispose(); + // Send initial theme on startup, slightly delayed to ensure server is ready + setTimeout(() => { + sendThemeNotification(); + }, 100); resolve(client); } else if (e.newState === State.Stopped) { reject(new Error("Failed to start Quarto LSP Server"));