From e3b7c92f2785ecea0999fcffea475d120a1d13a4 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 6 Nov 2024 16:25:39 -0700 Subject: [PATCH 1/5] Add new help topic provider for Quarto files --- apps/vscode/src/@types/hooks.d.ts | 12 ++++++++ apps/vscode/src/host/hooks.ts | 48 ++++++++++++++++++++++++++++++- apps/vscode/src/host/index.ts | 18 +++++++++++- apps/vscode/src/lsp/client.ts | 1 + apps/vscode/src/vdoc/vdoc.ts | 3 +- 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/apps/vscode/src/@types/hooks.d.ts b/apps/vscode/src/@types/hooks.d.ts index 66abfa90..8efba98e 100644 --- a/apps/vscode/src/@types/hooks.d.ts +++ b/apps/vscode/src/@types/hooks.d.ts @@ -25,6 +25,10 @@ declare module 'positron' { selector: vscode.DocumentSelector, provider: StatementRangeProvider ): vscode.Disposable; + registerHelpTopicProvider( + selector: vscode.DocumentSelector, + provider: HelpTopicProvider + ): vscode.Disposable; } export interface StatementRangeProvider { @@ -35,6 +39,14 @@ declare module 'positron' { ): vscode.ProviderResult; } + export interface HelpTopicProvider { + provideHelpTopic( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): vscode.ProviderResult; + } + export interface StatementRange { readonly range: vscode.Range; readonly code?: string; diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index 23ff087c..71199312 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -16,7 +16,7 @@ import * as vscode from 'vscode'; import * as hooks from 'positron'; -import { ExtensionHost, HostWebviewPanel, HostStatementRangeProvider } from '.'; +import { ExtensionHost, HostWebviewPanel, HostStatementRangeProvider, HostHelpTopicProvider } from '.'; import { CellExecutor, cellExecutorForLanguage, executableLanguages, isKnitrDocument, pythonWithReticulate } from './executors'; import { ExecuteQueue } from './execute-queue'; import { MarkdownEngine } from '../markdown/engine'; @@ -99,6 +99,15 @@ export function hooksExtensionHost(): ExtensionHost { return new vscode.Disposable(() => { }); }, + registerHelpTopicProvider: (engine: MarkdownEngine): vscode.Disposable => { + const hooks = hooksApi(); + if (hooks) { + return hooks.languages.registerHelpTopicProvider('quarto', + new EmbeddedHelpTopicProvider(engine)); + } + return new vscode.Disposable(() => { }); + }, + createPreviewPanel: ( viewType: string, title: string, @@ -186,3 +195,40 @@ async function getStatementRange( ); return { range: unadjustedRange(language, result.range), code: result.code }; } + +class EmbeddedHelpTopicProvider implements HostHelpTopicProvider { + private readonly _engine: MarkdownEngine; + + constructor( + readonly engine: MarkdownEngine, + ) { + this._engine = engine; + } + + async provideHelpTopic( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken): Promise { + const vdoc = await virtualDoc(document, position, this._engine); + if (vdoc) { + const vdocUri = await virtualDocUri(vdoc, document.uri, "helpTopic"); + try { + const res = await vscode.commands.executeCommand( + "positron.executeHelpTopicProvider", + vdocUri.uri, + adjustedPosition(vdoc.language, position), + vdoc.language + ); + return res; + } catch (error) { + return undefined; + } finally { + if (vdocUri.cleanup) { + await vdocUri.cleanup(); + } + } + } else { + return undefined; + } + }; +} diff --git a/apps/vscode/src/host/index.ts b/apps/vscode/src/host/index.ts index bfbd8bf5..929d0f1c 100644 --- a/apps/vscode/src/host/index.ts +++ b/apps/vscode/src/host/index.ts @@ -47,6 +47,14 @@ export interface HostStatementRange { readonly code?: string; } +export interface HostHelpTopicProvider { + provideHelpTopic( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): vscode.ProviderResult; +} + export interface ExtensionHost { // code execution @@ -63,6 +71,11 @@ export interface ExtensionHost { engine: MarkdownEngine, ): vscode.Disposable; + // help topic provider + registerHelpTopicProvider( + engine: MarkdownEngine, + ): vscode.Disposable; + // preview createPreviewPanel( viewType: string, @@ -99,10 +112,13 @@ function defaultExtensionHost(): ExtensionHost { return languages.filter(language => knitr || !visualMode || (language !== "python")); }, cellExecutorForLanguage, - // in the default extension host, this is a noop: + // in the default extension host, both of these are just a noop: registerStatementRangeProvider: (engine: MarkdownEngine): vscode.Disposable => { return new vscode.Disposable(() => { }); }, + registerHelpTopicProvider: (engine: MarkdownEngine): vscode.Disposable => { + return new vscode.Disposable(() => { }); + }, createPreviewPanel, }; } diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index d7384000..0203ab07 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -110,6 +110,7 @@ export async function activateLsp( middleware.provideSignatureHelp = embeddedSignatureHelpProvider(engine); } extensionHost().registerStatementRangeProvider(engine); + extensionHost().registerHelpTopicProvider(engine); // create client options const initializationOptions: LspInitializationOptions = { diff --git a/apps/vscode/src/vdoc/vdoc.ts b/apps/vscode/src/vdoc/vdoc.ts index 9287c67b..33f36791 100644 --- a/apps/vscode/src/vdoc/vdoc.ts +++ b/apps/vscode/src/vdoc/vdoc.ts @@ -117,7 +117,8 @@ export type VirtualDocAction = "signature" | "definition" | "format" | - "statementRange"; + "statementRange" | + "helpTopic"; export type VirtualDocUri = { uri: Uri, cleanup?: () => Promise }; From a99f8f6deed5c8d199e54fcccb311ec8d43a8266 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 6 Nov 2024 16:29:43 -0700 Subject: [PATCH 2/5] Fix up return --- apps/vscode/src/host/hooks.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index 71199312..d78da787 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -213,13 +213,12 @@ class EmbeddedHelpTopicProvider implements HostHelpTopicProvider { if (vdoc) { const vdocUri = await virtualDocUri(vdoc, document.uri, "helpTopic"); try { - const res = await vscode.commands.executeCommand( + return await vscode.commands.executeCommand( "positron.executeHelpTopicProvider", vdocUri.uri, adjustedPosition(vdoc.language, position), vdoc.language ); - return res; } catch (error) { return undefined; } finally { From 323a5cab51a69672b28a160154f89e0ac32c179c Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Fri, 8 Nov 2024 19:35:00 -0500 Subject: [PATCH 3/5] Help topic provider with doc (#601) * More usefully define `withVirtualDocUri()` * Use `withVirtualDocUri()` in `provideHelpTopic()` --- apps/vscode/src/core/hover.ts | 11 ++++++----- apps/vscode/src/host/hooks.ts | 16 +++++----------- apps/vscode/src/lsp/client.ts | 4 ++-- .../vscode/src/providers/assist/render-assist.ts | 8 ++++---- apps/vscode/src/providers/format.ts | 3 +-- apps/vscode/src/vdoc/vdoc-completion.ts | 5 +---- apps/vscode/src/vdoc/vdoc.ts | 15 +++++++++++---- 7 files changed, 30 insertions(+), 32 deletions(-) diff --git a/apps/vscode/src/core/hover.ts b/apps/vscode/src/core/hover.ts index b0825488..6f1a484a 100644 --- a/apps/vscode/src/core/hover.ts +++ b/apps/vscode/src/core/hover.ts @@ -15,17 +15,18 @@ import { Hover, MarkdownString, MarkedString, Position, SignatureHelp, commands } from "vscode"; -import { VirtualDocUri, adjustedPosition, unadjustedRange } from "../vdoc/vdoc"; +import { adjustedPosition, unadjustedRange } from "../vdoc/vdoc"; import { EmbeddedLanguage } from "../vdoc/languages"; +import { Uri } from "vscode"; export async function getHover( - vdocUri: VirtualDocUri, + uri: Uri, language: EmbeddedLanguage, position: Position ) { const hovers = await commands.executeCommand( "vscode.executeHoverProvider", - vdocUri.uri, + uri, adjustedPosition(language, position) ); if (hovers && hovers.length > 0) { @@ -45,14 +46,14 @@ export async function getHover( } export async function getSignatureHelpHover( - vdocUri: VirtualDocUri, + uri: Uri, language: EmbeddedLanguage, position: Position, triggerCharacter?: string ) { return await commands.executeCommand( "vscode.executeSignatureHelpProvider", - vdocUri.uri, + uri, adjustedPosition(language, position), triggerCharacter ); diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index d78da787..fca9b9f0 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -20,7 +20,7 @@ import { ExtensionHost, HostWebviewPanel, HostStatementRangeProvider, HostHelpTo import { CellExecutor, cellExecutorForLanguage, executableLanguages, isKnitrDocument, pythonWithReticulate } from './executors'; import { ExecuteQueue } from './execute-queue'; import { MarkdownEngine } from '../markdown/engine'; -import { virtualDoc, virtualDocUri, adjustedPosition, unadjustedRange } from "../vdoc/vdoc"; +import { virtualDoc, virtualDocUri, adjustedPosition, unadjustedRange, withVirtualDocUri } from "../vdoc/vdoc"; import { EmbeddedLanguage } from '../vdoc/languages'; declare global { @@ -210,22 +210,16 @@ class EmbeddedHelpTopicProvider implements HostHelpTopicProvider { position: vscode.Position, token: vscode.CancellationToken): Promise { const vdoc = await virtualDoc(document, position, this._engine); + if (vdoc) { - const vdocUri = await virtualDocUri(vdoc, document.uri, "helpTopic"); - try { + return await withVirtualDocUri(vdoc, document.uri, "helpTopic", async (uri: vscode.Uri) => { return await vscode.commands.executeCommand( "positron.executeHelpTopicProvider", - vdocUri.uri, + uri, adjustedPosition(vdoc.language, position), vdoc.language ); - } catch (error) { - return undefined; - } finally { - if (vdocUri.cleanup) { - await vdocUri.cleanup(); - } - } + }); } else { return undefined; } diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index 0203ab07..8d4f8d97 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -227,7 +227,7 @@ function embeddedHoverProvider(engine: MarkdownEngine) { // execute hover try { - return getHover(vdocUri, vdoc.language, position); + return getHover(vdocUri.uri, vdoc.language, position); } catch (error) { console.log(error); } finally { @@ -254,7 +254,7 @@ function embeddedSignatureHelpProvider(engine: MarkdownEngine) { if (vdoc) { const vdocUri = await virtualDocUri(vdoc, document.uri, "signature"); try { - return getSignatureHelpHover(vdocUri, vdoc.language, position, context.triggerCharacter); + return getSignatureHelpHover(vdocUri.uri, vdoc.language, position, context.triggerCharacter); } catch (error) { return undefined; } finally { diff --git a/apps/vscode/src/providers/assist/render-assist.ts b/apps/vscode/src/providers/assist/render-assist.ts index a067d969..46e0159d 100644 --- a/apps/vscode/src/providers/assist/render-assist.ts +++ b/apps/vscode/src/providers/assist/render-assist.ts @@ -110,13 +110,13 @@ export async function renderCodeViewAssist( const language = embeddedLanguage(context.language); if (language) { const vdoc = virtualDocForCode(context.code, language); - const vdocUri = await virtualDocUri(vdoc, Uri.file(context.filepath), "hover"); - return await withVirtualDocUri(vdocUri, async () => { + const parentUri = Uri.file(context.filepath); + return await withVirtualDocUri(vdoc, parentUri, "hover", async (uri: Uri) => { try { const position = new Position(context.selection.start.line, context.selection.start.character); // check for hover - const hover = await getHover(vdocUri, language, position); + const hover = await getHover(uri, language, position); if (hover) { const assist = getAssistFromHovers([hover], asWebviewUri); if (assist) { @@ -129,7 +129,7 @@ export async function renderCodeViewAssist( } // check for signature tip - const signatureHover = await getSignatureHelpHover(vdocUri, language, position); + const signatureHover = await getSignatureHelpHover(uri, language, position); if (signatureHover) { return getAssistFromSignatureHelp(signatureHover); } diff --git a/apps/vscode/src/providers/format.ts b/apps/vscode/src/providers/format.ts index d36b07be..acb0c691 100644 --- a/apps/vscode/src/providers/format.ts +++ b/apps/vscode/src/providers/format.ts @@ -178,8 +178,7 @@ async function executeFormatDocumentProvider( document: TextDocument, options: FormattingOptions ): Promise { - const vdocUri = await virtualDocUri(vdoc, document.uri, "format"); - const edits = await withVirtualDocUri(vdocUri, async (uri: Uri) => { + const edits = await withVirtualDocUri(vdoc, document.uri, "format", async (uri: Uri) => { return await commands.executeCommand( "vscode.executeFormatDocumentProvider", uri, diff --git a/apps/vscode/src/vdoc/vdoc-completion.ts b/apps/vscode/src/vdoc/vdoc-completion.ts index 84310c74..a8f2b0fd 100644 --- a/apps/vscode/src/vdoc/vdoc-completion.ts +++ b/apps/vscode/src/vdoc/vdoc-completion.ts @@ -24,10 +24,7 @@ export async function vdocCompletions( language: EmbeddedLanguage, parentUri: Uri ) { - - const vdocUri = await virtualDocUri(vdoc, parentUri, "completion"); - - const completions = await withVirtualDocUri(vdocUri, async (uri: Uri) => { + const completions = await withVirtualDocUri(vdoc, parentUri, "completion", async (uri: Uri) => { return await commands.executeCommand( "vscode.executeCompletionItemProvider", uri, diff --git a/apps/vscode/src/vdoc/vdoc.ts b/apps/vscode/src/vdoc/vdoc.ts index 33f36791..bfe942a9 100644 --- a/apps/vscode/src/vdoc/vdoc.ts +++ b/apps/vscode/src/vdoc/vdoc.ts @@ -122,12 +122,19 @@ export type VirtualDocAction = export type VirtualDocUri = { uri: Uri, cleanup?: () => Promise }; -export async function withVirtualDocUri(virtualDocUri: VirtualDocUri, f: (uri: Uri) => Promise) { +export async function withVirtualDocUri( + vdoc: VirtualDoc, + parentUri: Uri, + action: VirtualDocAction, + f: (uri: Uri) => Promise +) { + const vdocUri = await virtualDocUri(vdoc, parentUri, action); + try { - return await f(virtualDocUri.uri); + return await f(vdocUri.uri); } finally { - if (virtualDocUri.cleanup) { - virtualDocUri.cleanup(); + if (vdocUri.cleanup) { + vdocUri.cleanup(); } } } From 3e6b431380bb813d54eedacb5f20d7ebcc429c66 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Fri, 8 Nov 2024 17:46:57 -0700 Subject: [PATCH 4/5] Use new version of `withVirtualDocUri()` for statement range provider as well --- apps/vscode/src/host/hooks.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index fca9b9f0..9c826762 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -163,20 +163,13 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider { token: vscode.CancellationToken): Promise { const vdoc = await virtualDoc(document, position, this._engine); if (vdoc) { - const vdocUri = await virtualDocUri(vdoc, document.uri, "statementRange"); - try { + return await withVirtualDocUri(vdoc, document.uri, "statementRange", async (uri: vscode.Uri) => { return getStatementRange( - vdocUri.uri, + uri, adjustedPosition(vdoc.language, position), vdoc.language ); - } catch (error) { - return undefined; - } finally { - if (vdocUri.cleanup) { - await vdocUri.cleanup(); - } - } + }); } else { return undefined; } From b77025a521d86e1268278c6afbe8475d92d0895c Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Fri, 8 Nov 2024 17:48:56 -0700 Subject: [PATCH 5/5] Update changelog --- apps/vscode/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/vscode/CHANGELOG.md b/apps/vscode/CHANGELOG.md index 613b9a4f..8e204361 100644 --- a/apps/vscode/CHANGELOG.md +++ b/apps/vscode/CHANGELOG.md @@ -2,6 +2,8 @@ ## 1.118.0 (unreleased) +- Provide F1 help at cursor in Positron () + ## 1.117.0 (Release on 2024-11-07) - Fix issue with temp files for LSP request virtual documents ()