From 12c006e144e79642027e879cf02583a0c76a8aa3 Mon Sep 17 00:00:00 2001 From: Alex Bates Date: Thu, 26 Mar 2026 17:48:39 +0000 Subject: [PATCH] improve hover and inlay hint styling - Render doc comment prose in proportional font with paragraph margins - Linkify [SymbolName] references in doc comments as navigable links that jump directly to the symbol via workspace symbol lookup - Tighten inline code horizontal margins in hover widget - Force inlay hint font to sans-serif at user scope --- .../browser/c-language/doc-comment-hover.ts | 58 ++++++++++++++++++- .../src/browser/c-language/inlay-hints.ts | 12 ++-- .../studio/src/browser/themes/editor.css | 28 +++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/extensions/studio/src/browser/c-language/doc-comment-hover.ts b/extensions/studio/src/browser/c-language/doc-comment-hover.ts index 055608d..fe6ecb3 100644 --- a/extensions/studio/src/browser/c-language/doc-comment-hover.ts +++ b/extensions/studio/src/browser/c-language/doc-comment-hover.ts @@ -4,6 +4,10 @@ import { injectable, inject } from "@theia/core/shared/inversify"; import { FrontendApplicationContribution } from "@theia/core/lib/browser"; +import { CommandRegistry } from "@theia/core/lib/common/command"; +import { OpenerService, open } from "@theia/core/lib/browser/opener-service"; +import URI from "@theia/core/lib/common/uri"; +import { MonacoLanguages } from "@theia/monaco/lib/browser/monaco-languages"; import { StandaloneServices } from "@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices"; import { ILanguageFeaturesService } from "@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures"; import type { @@ -19,8 +23,21 @@ import { stripEvtTags, } from "./c-declaration-service"; +const GO_TO_SYMBOL_COMMAND = "starHaven.goToSymbol"; + +/** Replace `[SymbolName]` and [``SymbolName``] with links to that symbol. */ +function linkifySymbolRefs(text: string): string { + const linkify = (_: string, name: string) => { + const args = encodeURIComponent(JSON.stringify(name)); + return `[\`${name}\`](command:${GO_TO_SYMBOL_COMMAND}?${args})`; + }; + return text + .replace(/\[`(\w+)`\](?!\()/g, linkify) + .replace(/\[(\w+)\](?!\()/g, linkify); +} + function formatHover(signature: string, docLines?: string[]): string { - const body = docLines?.join("\n").trim(); + const body = docLines ? linkifySymbolRefs(docLines.join("\n").trim()) : ""; return body ? `\`\`\`c\n${signature}\n\`\`\`\n\n${body}` : `\`\`\`c\n${signature}\n\`\`\``; @@ -109,8 +126,45 @@ export class DocCommentHoverContribution @inject(CDeclarationService) protected readonly declarationService!: CDeclarationService; + @inject(CommandRegistry) + protected readonly commandRegistry!: CommandRegistry; + + @inject(OpenerService) + protected readonly openerService!: OpenerService; + + @inject(MonacoLanguages) + protected readonly monacoLanguages!: MonacoLanguages; + onStart(): void { const featuresService = StandaloneServices.get(ILanguageFeaturesService); + + this.commandRegistry.registerCommand( + { id: GO_TO_SYMBOL_COMMAND }, + { + execute: async (symbolName: string) => { + const token = { isCancellationRequested: false } as any; + for (const provider of this.monacoLanguages + .workspaceSymbolProviders) { + const symbols = await provider.provideWorkspaceSymbols( + { query: symbolName }, + token, + ); + if (!symbols || symbols.length === 0) { + continue; + } + const match = + symbols.find((s) => s.name === symbolName) || symbols[0]; + const uri = match.location.uri; + const line = match.location.range.start.line; + await open( + this.openerService, + new URI(uri).withFragment(`L${line + 1}`), + ); + return; + } + }, + }, + ); const registry = featuresService.hoverProvider; // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; @@ -147,7 +201,7 @@ export class DocCommentHoverContribution ); if (docHover) { return { - contents: [{ value: docHover }], + contents: [{ value: docHover, isTrusted: true }], range: original?.range ?? self.wordRange(model, position), }; } diff --git a/extensions/studio/src/browser/c-language/inlay-hints.ts b/extensions/studio/src/browser/c-language/inlay-hints.ts index 5d926b9..7e4b956 100644 --- a/extensions/studio/src/browser/c-language/inlay-hints.ts +++ b/extensions/studio/src/browser/c-language/inlay-hints.ts @@ -144,13 +144,11 @@ export class InlayHintsContribution implements FrontendApplicationContribution { } }); - if (!this.preferenceService.get("editor.inlayHints.fontFamily")) { - this.preferenceService.set( - "editor.inlayHints.fontFamily", - "system-ui, sans-serif", - PreferenceScope.User, - ); - } + this.preferenceService.set( + "editor.inlayHints.fontFamily", + "system-ui, sans-serif", + PreferenceScope.User, + ); // Theia's standalone theme service doesn't propagate the theme's // semanticHighlighting setting, so force it on globally. diff --git a/extensions/studio/src/browser/themes/editor.css b/extensions/studio/src/browser/themes/editor.css index 8b12362..139b739 100644 --- a/extensions/studio/src/browser/themes/editor.css +++ b/extensions/studio/src/browser/themes/editor.css @@ -5,3 +5,31 @@ .monaco-editor .evt-keyword { color: var(--star-haven-keyword-foreground) !important; } + +/* Hover widget: use proportional font for doc comment prose. + Code blocks keep their monospace font via inline styles. */ +.monaco-hover .rendered-markdown { + font-family: + system-ui, + -apple-system, + sans-serif; + font-size: 13px; + line-height: 1.5; +} + +.monaco-hover .rendered-markdown p { + margin: 0.4em 0; +} + +.monaco-hover .rendered-markdown p:first-child { + margin-top: 0; +} + +.monaco-hover .rendered-markdown p:last-child { + margin-bottom: 0; +} + +.monaco-hover .rendered-markdown code { + margin: 0 1px; + padding: 0 3px; +} -- 2.51.2