From 2f084e1ded4f1cb718db24d32c94263b821b4bdf Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 29 Apr 2024 15:04:15 -0700 Subject: [PATCH 01/21] Show doc in vscode v1 - random doc shown --- vscode/package.json | 9 ++++++++ vscode/src/circuit.ts | 40 ++++++++++++++++++++++++++++++++++ vscode/src/webview/docview.tsx | 15 +++++++++++++ vscode/src/webview/webview.tsx | 25 ++++++++++++++++++++- vscode/src/webviewPanel.ts | 18 ++++++++++++--- 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 vscode/src/webview/docview.tsx diff --git a/vscode/package.json b/vscode/package.json index db5b02ab51..347f390977 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -199,6 +199,10 @@ "command": "qsharp-vscode.showCircuit", "when": "resourceLangId == qsharp" }, + { + "command": "qsharp-vscode.showDocumentation", + "when": "resourceLangId == qsharp" + }, { "command": "qsharp-vscode.setTargetProfile", "when": "resourceLangId == qsharp" @@ -316,6 +320,11 @@ "title": "Show circuit", "category": "Q#" }, + { + "command": "qsharp-vscode.showDocumentation", + "title": "Show documentation", + "category": "Q#" + }, { "command": "qsharp-vscode.workspacesRefresh", "category": "Q#", diff --git a/vscode/src/circuit.ts b/vscode/src/circuit.ts index 22b492ba32..6d8ee759c3 100644 --- a/vscode/src/circuit.ts +++ b/vscode/src/circuit.ts @@ -54,6 +54,46 @@ type CircuitOrError = { } ); +export async function showDocumentationCommand( + extensionUri: Uri, + operation: IOperationInfo | undefined, +) { + sendMessageToPanel( + "documentationPanelType", // This is needed to route the message to the proper panel + true, + null); + + const editor = window.activeTextEditor; + if (!editor || !isQsharpDocument(editor.document)) { + throw new Error("The currently active window is not a Q# file"); + } + + const docUri = editor.document.uri; + const program = await loadProject(docUri); + const targetProfile = getTarget(); + const programPath = docUri.path; + + const compilerWorkerScriptPath = Uri.joinPath( + extensionUri, + "./out/compilerWorker.js", + ).toString(); + const worker = getCompilerWorker(compilerWorkerScriptPath); + const docFiles = await worker.getDocumentation(); + + const contentByNamespace = new Map(); + contentByNamespace.set("Some.Namespace", "Hello!"); + + const message = { + command: "showDocumentationCommand", // This is handled in webview.tsx onMessage + contentToRender: docFiles[0].contents, + }; + + sendMessageToPanel( + "documentationPanelType", // This is needed to route the message to the proper panel + true, + message); +} + export async function showCircuitCommand( extensionUri: Uri, operation: IOperationInfo | undefined, diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx new file mode 100644 index 0000000000..3dd4b79bfe --- /dev/null +++ b/vscode/src/webview/docview.tsx @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export function DocumentationView(props: { + renderer: (input: string) => string; + contentToRender: string; +}) { + const renderedContent = { + __html: props.renderer(props.contentToRender), + }; + + return
; +} diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx index 783d51c2e2..2d11592b6b 100644 --- a/vscode/src/webview/webview.tsx +++ b/vscode/src/webview/webview.tsx @@ -14,11 +14,13 @@ import { type ReData, } from "qsharp-lang/ux"; import { HelpPage } from "./help"; +import { DocumentationView } from "./docview"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - there are no types for this import mk from "@vscode/markdown-it-katex"; import markdownIt from "markdown-it"; +import { log } from "console"; const md = markdownIt(); md.use(mk); @@ -49,12 +51,18 @@ type CircuitState = { props: CircuitProps; }; +type DocumentationState = { + viewType: "documentationView"; + contentToRender: string; +} + type State = | { viewType: "loading" } | { viewType: "help" } | HistogramState | EstimatesState - | CircuitState; + | CircuitState + | DocumentationState; const loadingState: State = { viewType: "loading" }; const helpState: State = { viewType: "help" }; let state: State = loadingState; @@ -121,6 +129,14 @@ function onMessage(event: any) { }; } break; + case "showDocumentationCommand": + { + state = { + viewType: "documentationView", + contentToRender: message.contentToRender, + } + } + break; default: console.error("Unknown command: ", message.command); return; @@ -179,6 +195,13 @@ function App({ state }: { state: State }) { return ; case "help": return ; + case "documentationView": + return ( + + ); default: console.error("Unknown view type in state", state); return
Loading error
; diff --git a/vscode/src/webviewPanel.ts b/vscode/src/webviewPanel.ts index dca9460936..3e105cbaf0 100644 --- a/vscode/src/webviewPanel.ts +++ b/vscode/src/webviewPanel.ts @@ -22,7 +22,7 @@ import { isQsharpDocument } from "./common"; import { loadProject } from "./projectSystem"; import { EventType, sendTelemetryEvent } from "./telemetry"; import { getRandomGuid } from "./utils"; -import { showCircuitCommand } from "./circuit"; +import { showCircuitCommand, showDocumentationCommand } from "./circuit"; const QSharpWebViewType = "qsharp-webview"; const compilerRunTimeoutMs = 1000 * 60 * 5; // 5 minutes @@ -379,9 +379,19 @@ export function registerWebViewCommands(context: ExtensionContext) { }, ), ); + + context.subscriptions.push( + commands.registerCommand( + "qsharp-vscode.showDocumentation", + async (operation?: IOperationInfo) => { + await showDocumentationCommand(context.extensionUri, operation); + }, + ), + ); + } -type PanelType = "histogram" | "estimates" | "help" | "circuit"; +type PanelType = "histogram" | "estimates" | "help" | "circuit" | "documentationPanelType"; const panelTypeToPanel: Record< PanelType, @@ -391,6 +401,7 @@ const panelTypeToPanel: Record< estimates: { title: "Q# Estimates", panel: undefined, state: {} }, circuit: { title: "Q# Circuit", panel: undefined, state: {} }, help: { title: "Q# Help", panel: undefined, state: {} }, + documentationPanelType: { title: "Q# Documentation", panel: undefined, state: {} }, }; export function sendMessageToPanel( @@ -528,7 +539,8 @@ export class QSharpViewViewPanelSerializer implements WebviewPanelSerializer { panelType !== "estimates" && panelType !== "histogram" && panelType !== "circuit" && - panelType !== "help" + panelType !== "help" && + panelType != "documentationPanelType" ) { // If it was loading when closed, that's fine if (panelType === "loading") { From 1c3ca88cce1f4931f14f2440747b88350ad92dfd Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 2 May 2024 13:21:32 -0700 Subject: [PATCH 02/21] Show entire documentation --- npm/qsharp/src/compiler/compiler.ts | 13 ++++++++++ vscode/src/circuit.ts | 40 ----------------------------- vscode/src/documentation.ts | 38 +++++++++++++++++++++++++++ vscode/src/webviewPanel.ts | 4 ++- 4 files changed, 54 insertions(+), 41 deletions(-) create mode 100644 vscode/src/documentation.ts diff --git a/npm/qsharp/src/compiler/compiler.ts b/npm/qsharp/src/compiler/compiler.ts index 2fea46a352..52cc16f10f 100644 --- a/npm/qsharp/src/compiler/compiler.ts +++ b/npm/qsharp/src/compiler/compiler.ts @@ -86,6 +86,7 @@ export interface ICompiler { ): Promise; getDocumentation(): Promise; + getCombinedDocumentation(): Promise; checkExerciseSolution( userCode: string, @@ -253,6 +254,17 @@ export class Compiler implements ICompiler { return this.wasm.generate_docs(); } + async getCombinedDocumentation(): Promise { + const docFiles: IDocFile[] = this.wasm.generate_docs(); + let content = ""; + for (const file of docFiles) { + if (file.metadata.indexOf("qsharp.name:") >= 0) { + content += file.contents + "\n---\n"; + } + } + return content; + } + async checkExerciseSolution( userCode: string, exerciseSources: string[], @@ -310,6 +322,7 @@ export const compilerProtocol: ServiceProtocol = { getEstimates: "request", getCircuit: "request", getDocumentation: "request", + getCombinedDocumentation: "request", run: "requestWithProgress", checkExerciseSolution: "requestWithProgress", }, diff --git a/vscode/src/circuit.ts b/vscode/src/circuit.ts index 6d8ee759c3..22b492ba32 100644 --- a/vscode/src/circuit.ts +++ b/vscode/src/circuit.ts @@ -54,46 +54,6 @@ type CircuitOrError = { } ); -export async function showDocumentationCommand( - extensionUri: Uri, - operation: IOperationInfo | undefined, -) { - sendMessageToPanel( - "documentationPanelType", // This is needed to route the message to the proper panel - true, - null); - - const editor = window.activeTextEditor; - if (!editor || !isQsharpDocument(editor.document)) { - throw new Error("The currently active window is not a Q# file"); - } - - const docUri = editor.document.uri; - const program = await loadProject(docUri); - const targetProfile = getTarget(); - const programPath = docUri.path; - - const compilerWorkerScriptPath = Uri.joinPath( - extensionUri, - "./out/compilerWorker.js", - ).toString(); - const worker = getCompilerWorker(compilerWorkerScriptPath); - const docFiles = await worker.getDocumentation(); - - const contentByNamespace = new Map(); - contentByNamespace.set("Some.Namespace", "Hello!"); - - const message = { - command: "showDocumentationCommand", // This is handled in webview.tsx onMessage - contentToRender: docFiles[0].contents, - }; - - sendMessageToPanel( - "documentationPanelType", // This is needed to route the message to the proper panel - true, - message); -} - export async function showCircuitCommand( extensionUri: Uri, operation: IOperationInfo | undefined, diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts new file mode 100644 index 0000000000..6797582b3b --- /dev/null +++ b/vscode/src/documentation.ts @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + IOperationInfo, + getCompilerWorker, +} from "qsharp-lang"; +import { Uri } from "vscode"; +import { sendMessageToPanel } from "./webviewPanel"; + +export async function showDocumentationCommand( + extensionUri: Uri, + operation: IOperationInfo | undefined, +) { + // Reveal panel an show 'Loading...' for immediate feedback. + sendMessageToPanel( + "documentationPanelType", // This is needed to route the message to the proper panel + true, + null); + + // Get std library documentation from compiler. + const compilerWorkerScriptPath = Uri.joinPath( + extensionUri, + "./out/compilerWorker.js", + ).toString(); + const worker = getCompilerWorker(compilerWorkerScriptPath); + let content = await worker.getCombinedDocumentation(); + + const message = { + command: "showDocumentationCommand", // This is handled in webview.tsx onMessage + contentToRender: content, + }; + + sendMessageToPanel( + "documentationPanelType", // This is needed to route the message to the proper panel + true, + message); +} diff --git a/vscode/src/webviewPanel.ts b/vscode/src/webviewPanel.ts index 3e105cbaf0..5b79c31bb3 100644 --- a/vscode/src/webviewPanel.ts +++ b/vscode/src/webviewPanel.ts @@ -22,7 +22,8 @@ import { isQsharpDocument } from "./common"; import { loadProject } from "./projectSystem"; import { EventType, sendTelemetryEvent } from "./telemetry"; import { getRandomGuid } from "./utils"; -import { showCircuitCommand, showDocumentationCommand } from "./circuit"; +import { showCircuitCommand } from "./circuit"; +import { showDocumentationCommand } from "./documentation"; const QSharpWebViewType = "qsharp-webview"; const compilerRunTimeoutMs = 1000 * 60 * 5; // 5 minutes @@ -421,6 +422,7 @@ export function sendMessageToPanel( { enableCommandUris: true, enableScripts: true, + enableFindWidget: true, retainContextWhenHidden: true, // Note: If retainContextWhenHidden is false, the webview gets reloaded // every time you hide it by switching to another tab and then switch From 78e4491ceed0f707f89a208883d1a2f4ec823be6 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 2 May 2024 13:30:08 -0700 Subject: [PATCH 03/21] Formatting, etc. --- npm/qsharp/src/compiler/compiler.ts | 6 ++-- vscode/src/documentation.ts | 51 ++++++++++++++--------------- vscode/src/webview/docview.tsx | 8 ++--- vscode/src/webview/webview.tsx | 5 ++- vscode/src/webviewPanel.ts | 14 ++++++-- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/npm/qsharp/src/compiler/compiler.ts b/npm/qsharp/src/compiler/compiler.ts index 52cc16f10f..8bed85620b 100644 --- a/npm/qsharp/src/compiler/compiler.ts +++ b/npm/qsharp/src/compiler/compiler.ts @@ -258,9 +258,9 @@ export class Compiler implements ICompiler { const docFiles: IDocFile[] = this.wasm.generate_docs(); let content = ""; for (const file of docFiles) { - if (file.metadata.indexOf("qsharp.name:") >= 0) { - content += file.contents + "\n---\n"; - } + if (file.metadata.indexOf("qsharp.name:") >= 0) { + content += file.contents + "\n---\n"; + } } return content; } diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 6797582b3b..b5533db7da 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -1,38 +1,37 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { - IOperationInfo, - getCompilerWorker, -} from "qsharp-lang"; +import { IOperationInfo, getCompilerWorker } from "qsharp-lang"; import { Uri } from "vscode"; import { sendMessageToPanel } from "./webviewPanel"; export async function showDocumentationCommand( - extensionUri: Uri, - operation: IOperationInfo | undefined, + extensionUri: Uri, + operation: IOperationInfo | undefined, ) { - // Reveal panel an show 'Loading...' for immediate feedback. - sendMessageToPanel( - "documentationPanelType", // This is needed to route the message to the proper panel - true, - null); + // Reveal panel an show 'Loading...' for immediate feedback. + sendMessageToPanel( + "documentationPanelType", // This is needed to route the message to the proper panel + true, + null, + ); - // Get std library documentation from compiler. - const compilerWorkerScriptPath = Uri.joinPath( - extensionUri, - "./out/compilerWorker.js", - ).toString(); - const worker = getCompilerWorker(compilerWorkerScriptPath); - let content = await worker.getCombinedDocumentation(); + // Get std library documentation from compiler. + const compilerWorkerScriptPath = Uri.joinPath( + extensionUri, + "./out/compilerWorker.js", + ).toString(); + const worker = getCompilerWorker(compilerWorkerScriptPath); + let content = await worker.getCombinedDocumentation(); - const message = { - command: "showDocumentationCommand", // This is handled in webview.tsx onMessage - contentToRender: content, - }; + const message = { + command: "showDocumentationCommand", // This is handled in webview.tsx onMessage + contentToRender: content, + }; - sendMessageToPanel( - "documentationPanelType", // This is needed to route the message to the proper panel - true, - message); + sendMessageToPanel( + "documentationPanelType", // This is needed to route the message to the proper panel + true, + message, + ); } diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx index 3dd4b79bfe..d6b4b762ed 100644 --- a/vscode/src/webview/docview.tsx +++ b/vscode/src/webview/docview.tsx @@ -2,14 +2,12 @@ // Licensed under the MIT License. export function DocumentationView(props: { - renderer: (input: string) => string; - contentToRender: string; + renderer: (input: string) => string; + contentToRender: string; }) { const renderedContent = { __html: props.renderer(props.contentToRender), }; - return
; + return
; } diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx index 2d11592b6b..9e68a68a63 100644 --- a/vscode/src/webview/webview.tsx +++ b/vscode/src/webview/webview.tsx @@ -20,7 +20,6 @@ import { DocumentationView } from "./docview"; // @ts-ignore - there are no types for this import mk from "@vscode/markdown-it-katex"; import markdownIt from "markdown-it"; -import { log } from "console"; const md = markdownIt(); md.use(mk); @@ -54,7 +53,7 @@ type CircuitState = { type DocumentationState = { viewType: "documentationView"; contentToRender: string; -} +}; type State = | { viewType: "loading" } @@ -134,7 +133,7 @@ function onMessage(event: any) { state = { viewType: "documentationView", contentToRender: message.contentToRender, - } + }; } break; default: diff --git a/vscode/src/webviewPanel.ts b/vscode/src/webviewPanel.ts index 5b79c31bb3..e8d6798686 100644 --- a/vscode/src/webviewPanel.ts +++ b/vscode/src/webviewPanel.ts @@ -389,10 +389,14 @@ export function registerWebViewCommands(context: ExtensionContext) { }, ), ); - } -type PanelType = "histogram" | "estimates" | "help" | "circuit" | "documentationPanelType"; +type PanelType = + | "histogram" + | "estimates" + | "help" + | "circuit" + | "documentationPanelType"; const panelTypeToPanel: Record< PanelType, @@ -402,7 +406,11 @@ const panelTypeToPanel: Record< estimates: { title: "Q# Estimates", panel: undefined, state: {} }, circuit: { title: "Q# Circuit", panel: undefined, state: {} }, help: { title: "Q# Help", panel: undefined, state: {} }, - documentationPanelType: { title: "Q# Documentation", panel: undefined, state: {} }, + documentationPanelType: { + title: "Q# Documentation", + panel: undefined, + state: {}, + }, }; export function sendMessageToPanel( From 98615e9b0f6aac056ee81e5a8b1d41c8477b244b Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 2 May 2024 13:50:14 -0700 Subject: [PATCH 04/21] Warnings --- vscode/src/documentation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index b5533db7da..a41cb260a7 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -7,7 +7,7 @@ import { sendMessageToPanel } from "./webviewPanel"; export async function showDocumentationCommand( extensionUri: Uri, - operation: IOperationInfo | undefined, + _operation: IOperationInfo | undefined, ) { // Reveal panel an show 'Loading...' for immediate feedback. sendMessageToPanel( @@ -22,7 +22,7 @@ export async function showDocumentationCommand( "./out/compilerWorker.js", ).toString(); const worker = getCompilerWorker(compilerWorkerScriptPath); - let content = await worker.getCombinedDocumentation(); + const content = await worker.getCombinedDocumentation(); const message = { command: "showDocumentationCommand", // This is handled in webview.tsx onMessage From b4102f26829b6044a896283539c57568daf1cb55 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 2 May 2024 13:59:54 -0700 Subject: [PATCH 05/21] Added comments --- npm/qsharp/src/compiler/compiler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/npm/qsharp/src/compiler/compiler.ts b/npm/qsharp/src/compiler/compiler.ts index 8bed85620b..996a562fd4 100644 --- a/npm/qsharp/src/compiler/compiler.ts +++ b/npm/qsharp/src/compiler/compiler.ts @@ -256,8 +256,13 @@ export class Compiler implements ICompiler { async getCombinedDocumentation(): Promise { const docFiles: IDocFile[] = this.wasm.generate_docs(); + // Create combined documentation in the worker let content = ""; for (const file of docFiles) { + // Some files may contain information other than documentation + // For example, table of content is a separate file in a special format + // We check presence of qsharp.name in metadata to make sure we take + // only files that contain documentation from some qsharp object. if (file.metadata.indexOf("qsharp.name:") >= 0) { content += file.contents + "\n---\n"; } From 1aad327e005067d7c9389ed44b055d0369b22bdc Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 2 May 2024 15:59:32 -0700 Subject: [PATCH 06/21] Removed unused parameter --- vscode/src/documentation.ts | 5 +---- vscode/src/webviewPanel.ts | 9 +++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index a41cb260a7..e6ec83e927 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -5,10 +5,7 @@ import { IOperationInfo, getCompilerWorker } from "qsharp-lang"; import { Uri } from "vscode"; import { sendMessageToPanel } from "./webviewPanel"; -export async function showDocumentationCommand( - extensionUri: Uri, - _operation: IOperationInfo | undefined, -) { +export async function showDocumentationCommand(extensionUri: Uri) { // Reveal panel an show 'Loading...' for immediate feedback. sendMessageToPanel( "documentationPanelType", // This is needed to route the message to the proper panel diff --git a/vscode/src/webviewPanel.ts b/vscode/src/webviewPanel.ts index e8d6798686..c3d37b5790 100644 --- a/vscode/src/webviewPanel.ts +++ b/vscode/src/webviewPanel.ts @@ -382,12 +382,9 @@ export function registerWebViewCommands(context: ExtensionContext) { ); context.subscriptions.push( - commands.registerCommand( - "qsharp-vscode.showDocumentation", - async (operation?: IOperationInfo) => { - await showDocumentationCommand(context.extensionUri, operation); - }, - ), + commands.registerCommand("qsharp-vscode.showDocumentation", async () => { + await showDocumentationCommand(context.extensionUri); + }), ); } From a7c83e89fc841cdbe5bbb34b05eb17b426512e47 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 2 May 2024 16:02:07 -0700 Subject: [PATCH 07/21] Removed unused parameter --- vscode/src/documentation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index e6ec83e927..52f267f532 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { IOperationInfo, getCompilerWorker } from "qsharp-lang"; +import { getCompilerWorker } from "qsharp-lang"; import { Uri } from "vscode"; import { sendMessageToPanel } from "./webviewPanel"; From 1dca8ff5078352fc593a96dc32392457248a7157 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Tue, 7 May 2024 13:47:28 -0700 Subject: [PATCH 08/21] Using new Markdown tag to render docs --- vscode/src/webview/docview.tsx | 11 +++-------- vscode/src/webview/webview.tsx | 7 +------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx index d6b4b762ed..dfb87c7219 100644 --- a/vscode/src/webview/docview.tsx +++ b/vscode/src/webview/docview.tsx @@ -1,13 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -export function DocumentationView(props: { - renderer: (input: string) => string; - contentToRender: string; -}) { - const renderedContent = { - __html: props.renderer(props.contentToRender), - }; +import { Markdown } from "qsharp-lang/ux"; - return
; +export function DocumentationView(props: { contentToRender: string }) { + return ; } diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx index 5f863d21cf..b8691cbf7b 100644 --- a/vscode/src/webview/webview.tsx +++ b/vscode/src/webview/webview.tsx @@ -194,12 +194,7 @@ function App({ state }: { state: State }) { case "help": return ; case "documentationView": - return ( - - ); + return ; default: console.error("Unknown view type in state", state); return
Loading error
; From de2a7b5fd91ee8e1add0b11c4a842ae76b00edff Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 8 May 2024 13:27:38 -0700 Subject: [PATCH 09/21] Including current project into doc generation --- compiler/qsc_doc_gen/src/generate_docs.rs | 44 ++++++++++++++++--- .../qsc_doc_gen/src/generate_docs/tests.rs | 2 +- npm/qsharp/src/compiler/compiler.ts | 41 ++++++++++++----- playground/src/main.tsx | 2 +- vscode/package.json | 2 +- vscode/src/documentation.ts | 24 +++++++++- wasm/src/lib.rs | 19 +++++++- wasm/src/tests.rs | 2 +- 8 files changed, 112 insertions(+), 24 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 311c2c7071..9f1cf785c0 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -7,8 +7,9 @@ mod tests; use crate::display::{increase_header_level, parse_doc_for_summary}; use crate::display::{CodeDisplay, Lookup}; use qsc_ast::ast; +use qsc_data_structures::language_features::LanguageFeatures; use qsc_data_structures::target::TargetCapabilityFlags; -use qsc_frontend::compile::{self, PackageStore}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_frontend::resolve; use qsc_hir::hir::{CallableKind, Item, ItemKind, Package, PackageId, Visibility}; use qsc_hir::{hir, ty}; @@ -28,9 +29,38 @@ struct Compilation { impl Compilation { /// Creates a new `Compilation` by compiling sources. - pub(crate) fn new() -> Self { + pub(crate) fn new( + additional_sources: Option, + capabilities: Option, + language_features: Option, + ) -> Self { let mut package_store = PackageStore::new(compile::core()); - package_store.insert(compile::std(&package_store, TargetCapabilityFlags::all())); + let actual_capabilities = match capabilities { + Some(c) => c, + None => TargetCapabilityFlags::default(), + }; + let std_unit = compile::std(&package_store, actual_capabilities); + let std_package_id = package_store.insert(std_unit); + + if let Some(sources) = additional_sources { + let actual_language_features = match language_features { + Some(l) => l, + None => LanguageFeatures::default(), + }; + + let unit = compile( + &package_store, + &[std_package_id], + sources, + actual_capabilities, + actual_language_features, + ); + // We ignore errors here (unit.errors vector) and use whatever + // documentation we can produce. If future we may consider + // displaying the fact of error presence on documentation page. + + package_store.insert(unit); + } Self { package_store } } @@ -104,8 +134,12 @@ impl Lookup for Compilation { } #[must_use] -pub fn generate_docs() -> Files { - let compilation = Compilation::new(); +pub fn generate_docs( + additional_sources: Option, + capabilities: Option, + language_features: Option, +) -> Files { + let compilation = Compilation::new(additional_sources, capabilities, language_features); let mut files: Files = vec![]; let display = &CodeDisplay { diff --git a/compiler/qsc_doc_gen/src/generate_docs/tests.rs b/compiler/qsc_doc_gen/src/generate_docs/tests.rs index b24f920e27..928e00cb30 100644 --- a/compiler/qsc_doc_gen/src/generate_docs/tests.rs +++ b/compiler/qsc_doc_gen/src/generate_docs/tests.rs @@ -8,7 +8,7 @@ use expect_test::expect; #[test] fn docs_generation() { - let files = generate_docs(); + let files = generate_docs(None, None, None); let (_, metadata, contents) = files .iter() .find(|(file_name, _, _)| &**file_name == "Microsoft.Quantum.Core/Length.md") diff --git a/npm/qsharp/src/compiler/compiler.ts b/npm/qsharp/src/compiler/compiler.ts index 9d9f8ff4e5..78f3720e06 100644 --- a/npm/qsharp/src/compiler/compiler.ts +++ b/npm/qsharp/src/compiler/compiler.ts @@ -93,8 +93,12 @@ export interface ICompiler { operation?: IOperationInfo, ): Promise; - getDocumentation(): Promise; - getCombinedDocumentation(): Promise; + getDocumentationFiles(): Promise; + getDocumentationContent( + additionalSources?: Array[], + targetProfile?: string, + languageFeatures?: string[], + ): Promise; checkExerciseSolution( userCode: string, @@ -274,24 +278,39 @@ export class Compiler implements ICompiler { ); } - async getDocumentation(): Promise { + // Returns all autogenerated documentation files for the standard library. + // This include file names and metadata, including specially formatted + // table of content file. + async getDocumentationFiles(): Promise { return this.wasm.generate_docs(); } - async getCombinedDocumentation(): Promise { - const docFiles: IDocFile[] = this.wasm.generate_docs(); - // Create combined documentation in the worker - let content = ""; + // Returns autogenerated documentation for the standard library + // and loaded project (if requested). Only the array of documentation + // content is returned. + async getDocumentationContent( + additionalSources?: Array[], + targetProfile?: string, + languageFeatures?: string[], + ): Promise { + // Get documentation from wasm layer + const docFiles: IDocFile[] = this.wasm.generate_docs( + additionalSources, + targetProfile, + languageFeatures, + ); + + let result: string[] = []; for (const file of docFiles) { // Some files may contain information other than documentation // For example, table of content is a separate file in a special format // We check presence of qsharp.name in metadata to make sure we take // only files that contain documentation from some qsharp object. if (file.metadata.indexOf("qsharp.name:") >= 0) { - content += file.contents + "\n---\n"; + result.push(file.contents); } } - return content; + return result; } async checkExerciseSolution( @@ -350,8 +369,8 @@ export const compilerProtocol: ServiceProtocol = { getQir: "request", getEstimates: "request", getCircuit: "request", - getDocumentation: "request", - getCombinedDocumentation: "request", + getDocumentationFiles: "request", + getDocumentationContent: "request", run: "requestWithProgress", checkExerciseSolution: "requestWithProgress", }, diff --git a/playground/src/main.tsx b/playground/src/main.tsx index b0a98964a9..5d4baf405c 100644 --- a/playground/src/main.tsx +++ b/playground/src/main.tsx @@ -117,7 +117,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) { createDocumentation(); }, []); async function createDocumentation() { - const docFiles = await compiler.getDocumentation(); + const docFiles = await compiler.getDocumentationFiles(); setDocumentation(processDocumentFiles(docFiles)); } diff --git a/vscode/package.json b/vscode/package.json index 55f314782f..ab82593fa2 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -322,7 +322,7 @@ }, { "command": "qsharp-vscode.showDocumentation", - "title": "Show documentation", + "title": "Generate API documentation", "category": "Q#" }, { diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 52f267f532..8497da0dbb 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -2,7 +2,10 @@ // Licensed under the MIT License. import { getCompilerWorker } from "qsharp-lang"; -import { Uri } from "vscode"; +import { isQsharpDocument } from "./common"; +import { getTarget } from "./config"; +import { Uri, window } from "vscode"; +import { loadProject } from "./projectSystem"; import { sendMessageToPanel } from "./webviewPanel"; export async function showDocumentationCommand(extensionUri: Uri) { @@ -12,6 +15,14 @@ export async function showDocumentationCommand(extensionUri: Uri) { true, null, ); + const editor = window.activeTextEditor; + if (!editor || !isQsharpDocument(editor.document)) { + throw new Error("The currently active window is not a Q# file"); + } + + const docUri = editor.document.uri; + const program = await loadProject(docUri); + const targetProfile = getTarget(); // Get std library documentation from compiler. const compilerWorkerScriptPath = Uri.joinPath( @@ -19,7 +30,16 @@ export async function showDocumentationCommand(extensionUri: Uri) { "./out/compilerWorker.js", ).toString(); const worker = getCompilerWorker(compilerWorkerScriptPath); - const content = await worker.getCombinedDocumentation(); + const documentation = await worker.getDocumentationContent( + program.sources, + targetProfile, + program.languageFeatures, + ); + + // Concatenate all documentation. + // The following adds an empty line and a horizontal line + // between documentation for different functions. + const content = documentation.join("\n
\n\n
\n---\n"); const message = { command: "showDocumentationCommand", // This is handled in webview.tsx onMessage diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 1e87d5f2d8..34d53c4576 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -19,6 +19,7 @@ use qsc::{ }, target::Profile, LanguageFeatures, PackageStore, PackageType, SourceContents, SourceMap, SourceName, SparseSim, + TargetCapabilityFlags, }; use qsc_codegen::qir_base::generate_qir; use resource_estimator::{self as re, estimate_entry}; @@ -458,8 +459,22 @@ serializable_type! { #[wasm_bindgen] #[must_use] -pub fn generate_docs() -> Vec { - let docs = qsc_doc_gen::generate_docs::generate_docs(); +pub fn generate_docs( + additionalSources: Option>, + targetProfile: Option, + languageFeatures: Option>, +) -> Vec { + let source_map: Option = additionalSources.map(|s| get_source_map(s, &None)); + + let target_profile: Option = targetProfile.map(|p| { + Profile::from_str(&p) + .expect("invalid target profile") + .into() + }); + + let features: Option = languageFeatures.map(LanguageFeatures::from_iter); + + let docs = qsc_doc_gen::generate_docs::generate_docs(source_map, target_profile, features); let mut result: Vec = vec![]; for (name, metadata, contents) in docs { diff --git a/wasm/src/tests.rs b/wasm/src/tests.rs index 0b30b0c7a4..c5335ab775 100644 --- a/wasm/src/tests.rs +++ b/wasm/src/tests.rs @@ -446,7 +446,7 @@ fn test_runtime_error_default_span() { #[test] fn test_doc_gen() { - let docs = qsc_doc_gen::generate_docs::generate_docs(); + let docs = qsc_doc_gen::generate_docs::generate_docs(None, None, None); assert!(docs.len() > 100); for (name, metadata, contents) in docs { // filename will be something like "Microsoft.Quantum.Canon/ApplyToEachC.md" From 017b8c5f4903eba6535773f7e3c53338d5f7f1b9 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 8 May 2024 13:33:38 -0700 Subject: [PATCH 10/21] Renamed command to be Show API documentation --- vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode/package.json b/vscode/package.json index ab82593fa2..78ebc7644b 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -322,7 +322,7 @@ }, { "command": "qsharp-vscode.showDocumentation", - "title": "Generate API documentation", + "title": "Show API documentation", "category": "Q#" }, { From 8240e43d48cb4172a447e5d8149a8ef36d683da4 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 8 May 2024 13:37:27 -0700 Subject: [PATCH 11/21] linter: const --- npm/qsharp/src/compiler/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/qsharp/src/compiler/compiler.ts b/npm/qsharp/src/compiler/compiler.ts index 78f3720e06..c8b0bccdaa 100644 --- a/npm/qsharp/src/compiler/compiler.ts +++ b/npm/qsharp/src/compiler/compiler.ts @@ -300,7 +300,7 @@ export class Compiler implements ICompiler { languageFeatures, ); - let result: string[] = []; + const result: string[] = []; for (const file of docFiles) { // Some files may contain information other than documentation // For example, table of content is a separate file in a special format From a4ca244911e56a77c4c59c6d4c2f11d533812683 Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Wed, 8 May 2024 18:20:30 -0700 Subject: [PATCH 12/21] Update docs view to use markdown style sheet --- playground/build.js | 2 +- vscode/build.mjs | 14 ++++---- vscode/src/documentation.ts | 6 ++-- vscode/src/webview/webview.tsx | 65 +++++++++++++++++++++++++++++++--- vscode/src/webviewPanel.ts | 8 ++--- 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/playground/build.js b/playground/build.js index 4329e570f0..e70a24e7c4 100644 --- a/playground/build.js +++ b/playground/build.js @@ -58,7 +58,7 @@ function copyLibs() { mkdirSync(monacoDest, { recursive: true }); cpSync(monacoBase, monacoDest, { recursive: true }); - copyKatex(join(thisDir, "public/libs/katex"), true); + copyKatex(join(thisDir, "public/libs/katex")); copyWasmToPlayground(); } diff --git a/vscode/build.mjs b/vscode/build.mjs index 7ce44728e6..8357ed965f 100644 --- a/vscode/build.mjs +++ b/vscode/build.mjs @@ -64,9 +64,8 @@ export function copyWasmToVsCode() { /** * * @param {string} [destDir] - * @param {boolean} [useLightTheme] */ -export function copyKatex(destDir, useLightTheme) { +export function copyKatex(destDir) { let katexBase = join(libsDir, `katex/dist`); let katexDest = destDir ?? join(thisDir, `out/katex`); @@ -78,12 +77,13 @@ export function copyKatex(destDir, useLightTheme) { ); // Also copy the GitHub markdown CSS - const cssFileName = useLightTheme - ? "github-markdown-light.css" - : "github-markdown.css"; copyFileSync( - join(libsDir, `github-markdown-css/${cssFileName}`), - join(katexDest, "github-markdown.css"), + join(libsDir, `github-markdown-css/github-markdown-light.css`), + join(katexDest, "github-markdown-light.css"), + ); + copyFileSync( + join(libsDir, `github-markdown-css/github-markdown-dark.css`), + join(katexDest, "github-markdown-dark.css"), ); const fontsDir = join(katexBase, "fonts"); diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 8497da0dbb..231682fb23 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -11,7 +11,7 @@ import { sendMessageToPanel } from "./webviewPanel"; export async function showDocumentationCommand(extensionUri: Uri) { // Reveal panel an show 'Loading...' for immediate feedback. sendMessageToPanel( - "documentationPanelType", // This is needed to route the message to the proper panel + "documentation", // This is needed to route the message to the proper panel true, null, ); @@ -39,7 +39,7 @@ export async function showDocumentationCommand(extensionUri: Uri) { // Concatenate all documentation. // The following adds an empty line and a horizontal line // between documentation for different functions. - const content = documentation.join("\n
\n\n
\n---\n"); + const content = documentation.join("
\n\n---\n\n"); const message = { command: "showDocumentationCommand", // This is handled in webview.tsx onMessage @@ -47,7 +47,7 @@ export async function showDocumentationCommand(extensionUri: Uri) { }; sendMessageToPanel( - "documentationPanelType", // This is needed to route the message to the proper panel + "documentation", // This is needed to route the message to the proper panel true, message, ); diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx index b8691cbf7b..469f7c1866 100644 --- a/vscode/src/webview/webview.tsx +++ b/vscode/src/webview/webview.tsx @@ -21,7 +21,7 @@ import { DocumentationView } from "./docview"; // @ts-ignore - there are no types for this import mk from "@vscode/markdown-it-katex"; import markdownIt from "markdown-it"; -const md = markdownIt("commonmark", { html: true, breaks: true }); +const md = markdownIt("commonmark"); md.use(mk, { enableMathBlockInHtml: true, enableMathInlineInHtml: true, @@ -51,7 +51,7 @@ type CircuitState = { }; type DocumentationState = { - viewType: "documentationView"; + viewType: "documentation"; contentToRender: string; }; @@ -66,9 +66,62 @@ const loadingState: State = { viewType: "loading" }; const helpState: State = { viewType: "help" }; let state: State = loadingState; +const themeAttribute = "data-vscode-theme-kind"; + +function updateGitHubTheme() { + let isDark = true; + + const themeType = document.body.getAttribute(themeAttribute); + + switch (themeType) { + case "vscode-light": + case "vscode-high-contrast-light": + isDark = false; + break; + default: + isDark = true; + } + + // Update the stylesheet href + document.head.querySelectorAll("link").forEach((el) => { + const ref = el.getAttribute("href"); + if (ref && ref.includes("github-markdown")) { + const newVal = ref.replace( + /(dark\.css)|(light\.css)/, + isDark ? "dark.css" : "light.css", + ); + el.setAttribute("href", newVal); + } + }); +} + +function setThemeStylesheet() { + // We need to add the right Markdown style-sheet for the theme. + + // For VS Code, there will be an attribute on the body called + // "data-vscode-theme-kind" that is "vscode-light" or "vscode-high-contrast-light" + // for light themes, else assume dark (will be "vscode-dark" or "vscode-high-contrast"). + + // Use a [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) + // to detect changes to the theme attribute. + const callback = (mutations: MutationRecord[]) => { + for (const mutation of mutations) { + if (mutation.attributeName === themeAttribute) { + updateGitHubTheme(); + } + } + }; + const observer = new MutationObserver(callback); + observer.observe(document.body, { attributeFilter: [themeAttribute] }); + + // Run it once for initial value + updateGitHubTheme(); +} + function main() { state = (vscodeApi.getState() as any) || loadingState; render(, document.body); + setThemeStylesheet(); vscodeApi.postMessage({ command: "ready" }); } @@ -131,7 +184,7 @@ function onMessage(event: any) { case "showDocumentationCommand": { state = { - viewType: "documentationView", + viewType: "documentation", contentToRender: message.contentToRender, }; } @@ -193,7 +246,11 @@ function App({ state }: { state: State }) { return ; case "help": return ; - case "documentationView": + case "documentation": + // Ideally we'd have this on all web views, but it makes the font a little + // too large in the others right now. Something to unify later. + document.body.classList.add("markdown-body"); + document.body.style.fontSize = "0.8em"; return ; default: console.error("Unknown view type in state", state); diff --git a/vscode/src/webviewPanel.ts b/vscode/src/webviewPanel.ts index c3d37b5790..551ffccf37 100644 --- a/vscode/src/webviewPanel.ts +++ b/vscode/src/webviewPanel.ts @@ -393,7 +393,7 @@ type PanelType = | "estimates" | "help" | "circuit" - | "documentationPanelType"; + | "documentation"; const panelTypeToPanel: Record< PanelType, @@ -403,7 +403,7 @@ const panelTypeToPanel: Record< estimates: { title: "Q# Estimates", panel: undefined, state: {} }, circuit: { title: "Q# Circuit", panel: undefined, state: {} }, help: { title: "Q# Help", panel: undefined, state: {} }, - documentationPanelType: { + documentation: { title: "Q# Documentation", panel: undefined, state: {}, @@ -476,7 +476,7 @@ export class QSharpWebViewPanel { } const katexCss = getUri(["out", "katex", "katex.min.css"]); - const githubCss = getUri(["out", "katex", "github-markdown.css"]); + const githubCss = getUri(["out", "katex", "github-markdown-dark.css"]); const webviewCss = getUri(["out", "webview", "webview.css"]); const webviewJs = getUri(["out", "webview", "webview.js"]); const resourcesUri = getUri(["resources"]); @@ -547,7 +547,7 @@ export class QSharpViewViewPanelSerializer implements WebviewPanelSerializer { panelType !== "histogram" && panelType !== "circuit" && panelType !== "help" && - panelType != "documentationPanelType" + panelType != "documentation" ) { // If it was loading when closed, that's fine if (panelType === "loading") { From d0e5d42fada51f97623a5ea032a788c19298f70a Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Wed, 8 May 2024 18:39:42 -0700 Subject: [PATCH 13/21] Fix playground CSS --- playground/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/public/index.html b/playground/public/index.html index 62ffcd0197..165b4cb347 100644 --- a/playground/public/index.html +++ b/playground/public/index.html @@ -16,7 +16,7 @@ rel="icon" href="data:image/gif;base64,R0lGODlhEAAQAAAAACwAAAAAEAAQAAACDvAxdbn9YZSTVntx1qcAADs=" /> - + Q# playground From b0b48e71c147d5c566798a3261a3e8a531c79330 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 9 May 2024 10:03:52 -0700 Subject: [PATCH 14/21] Fixed test and added doc comments to one sample --- npm/qsharp/test/basics.js | 2 +- samples/algorithms/BellState.qs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/npm/qsharp/test/basics.js b/npm/qsharp/test/basics.js index e84782d97c..32972445a2 100644 --- a/npm/qsharp/test/basics.js +++ b/npm/qsharp/test/basics.js @@ -47,7 +47,7 @@ export function runSingleShot(code, expr, useWorker) { test("autogenerated documentation", async () => { const compiler = getCompiler(); const regex = new RegExp("^qsharp.namespace: (.+)$", "m"); - const docFiles = await compiler.getDocumentation(); + const docFiles = await compiler.getDocumentationFiles(); var numberOfGoodFiles = 0; for (const doc of docFiles) { assert(doc, "Each documentation file should be present."); diff --git a/samples/algorithms/BellState.qs b/samples/algorithms/BellState.qs index c6ae047991..73661c5419 100644 --- a/samples/algorithms/BellState.qs +++ b/samples/algorithms/BellState.qs @@ -35,23 +35,31 @@ namespace Sample { return measurements; } + /// # Summary + /// Prepares |Φ+⟩ = (|00⟩+|11⟩)/√2 state assuming `register` is in |00⟩ state. operation PreparePhiPlus(register : Qubit[]) : Unit { H(register[0]); // |+0〉 CNOT(register[0], register[1]); // 1/sqrt(2)(|00〉 + |11〉) } + /// # Summary + /// Prepares |Φ−⟩ = (|00⟩-|11⟩)/√2 state assuming `register` is in |00⟩ state. operation PreparePhiMinus(register : Qubit[]) : Unit { H(register[0]); // |+0〉 Z(register[0]); // |-0〉 CNOT(register[0], register[1]); // 1/sqrt(2)(|00〉 - |11〉) } + /// # Summary + /// Prepares |Ψ+⟩ = (|01⟩+|10⟩)/√2 state assuming `register` is in |00⟩ state. operation PreparePsiPlus(register : Qubit[]) : Unit { H(register[0]); // |+0〉 X(register[1]); // |+1〉 CNOT(register[0], register[1]); // 1/sqrt(2)(|01〉 + |10〉) } + /// # Summary + /// Prepares |Ψ−⟩ = (|01⟩-|10⟩)/√2 state assuming `register` is in |00⟩ state. operation PreparePsiMinus(register : Qubit[]) : Unit { H(register[0]); // |+0〉 Z(register[0]); // |-0〉 From c9e3ff56f2f4b022f510014f8e13f9c444016a9b Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 9 May 2024 11:14:05 -0700 Subject: [PATCH 15/21] Moved concatenation to documentation component --- vscode/src/documentation.ts | 19 +++++++------------ vscode/src/webview/docview.tsx | 10 ++++++++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 231682fb23..16645bb242 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -9,11 +9,11 @@ import { loadProject } from "./projectSystem"; import { sendMessageToPanel } from "./webviewPanel"; export async function showDocumentationCommand(extensionUri: Uri) { - // Reveal panel an show 'Loading...' for immediate feedback. + // Reveal panel and show 'Loading...' for immediate feedback. sendMessageToPanel( "documentation", // This is needed to route the message to the proper panel - true, - null, + true, // Reveal panel + null, // With no message ); const editor = window.activeTextEditor; if (!editor || !isQsharpDocument(editor.document)) { @@ -24,7 +24,7 @@ export async function showDocumentationCommand(extensionUri: Uri) { const program = await loadProject(docUri); const targetProfile = getTarget(); - // Get std library documentation from compiler. + // Get API documentation from compiler. const compilerWorkerScriptPath = Uri.joinPath( extensionUri, "./out/compilerWorker.js", @@ -36,19 +36,14 @@ export async function showDocumentationCommand(extensionUri: Uri) { program.languageFeatures, ); - // Concatenate all documentation. - // The following adds an empty line and a horizontal line - // between documentation for different functions. - const content = documentation.join("
\n\n---\n\n"); - const message = { command: "showDocumentationCommand", // This is handled in webview.tsx onMessage - contentToRender: content, + contentToRender: documentation, }; sendMessageToPanel( "documentation", // This is needed to route the message to the proper panel - true, - message, + true, // Reveal panel + message, // And ask it to display documentation ); } diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx index dfb87c7219..e10913edbf 100644 --- a/vscode/src/webview/docview.tsx +++ b/vscode/src/webview/docview.tsx @@ -3,6 +3,12 @@ import { Markdown } from "qsharp-lang/ux"; -export function DocumentationView(props: { contentToRender: string }) { - return ; +export function DocumentationView(props: { contentFragments: string[] }) { + + // Concatenate all documentation. + // The following adds an empty line and a horizontal line + // between documentation for different functions. + const contentToRender = props.contentFragments.join("
\n\n---\n\n"); + + return ; } From 6af1b170c2c819f28d7cfb31d5638ce02217091d Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 9 May 2024 11:17:40 -0700 Subject: [PATCH 16/21] prettier --- vscode/src/webview/docview.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx index e10913edbf..3a755b067f 100644 --- a/vscode/src/webview/docview.tsx +++ b/vscode/src/webview/docview.tsx @@ -4,7 +4,6 @@ import { Markdown } from "qsharp-lang/ux"; export function DocumentationView(props: { contentFragments: string[] }) { - // Concatenate all documentation. // The following adds an empty line and a horizontal line // between documentation for different functions. From d53a82422a7d2390aca70d34a2642d6474b11ba6 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 9 May 2024 13:39:44 -0700 Subject: [PATCH 17/21] renamed parameters --- vscode/src/documentation.ts | 2 +- vscode/src/webview/docview.tsx | 5 +++-- vscode/src/webview/webview.tsx | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 16645bb242..0b628e8f74 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -38,7 +38,7 @@ export async function showDocumentationCommand(extensionUri: Uri) { const message = { command: "showDocumentationCommand", // This is handled in webview.tsx onMessage - contentToRender: documentation, + fragmentsToRender: documentation, }; sendMessageToPanel( diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx index 3a755b067f..94627a16be 100644 --- a/vscode/src/webview/docview.tsx +++ b/vscode/src/webview/docview.tsx @@ -3,11 +3,12 @@ import { Markdown } from "qsharp-lang/ux"; -export function DocumentationView(props: { contentFragments: string[] }) { +export function DocumentationView(props: { fragmentsToRender: string[] }) { // Concatenate all documentation. // The following adds an empty line and a horizontal line // between documentation for different functions. - const contentToRender = props.contentFragments.join("
\n\n---\n\n"); + // We may consider filtering of fragments later. + const contentToRender = props.fragmentsToRender.join("
\n\n---\n\n"); return ; } diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx index 469f7c1866..786c4bf6d3 100644 --- a/vscode/src/webview/webview.tsx +++ b/vscode/src/webview/webview.tsx @@ -52,7 +52,7 @@ type CircuitState = { type DocumentationState = { viewType: "documentation"; - contentToRender: string; + fragmentsToRender: string[]; }; type State = @@ -185,7 +185,7 @@ function onMessage(event: any) { { state = { viewType: "documentation", - contentToRender: message.contentToRender, + fragmentsToRender: message.fragmentsToRender, }; } break; @@ -251,7 +251,7 @@ function App({ state }: { state: State }) { // too large in the others right now. Something to unify later. document.body.classList.add("markdown-body"); document.body.style.fontSize = "0.8em"; - return ; + return ; default: console.error("Unknown view type in state", state); return
Loading error
; From 4a6a561e9e9a5a9cfdd5aa85d1cea1f29a922609 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 9 May 2024 14:11:32 -0700 Subject: [PATCH 18/21] Changed back to a single getDocumentation function --- npm/qsharp/src/compiler/compiler.ts | 43 ++++++++--------------------- npm/qsharp/test/basics.js | 2 +- playground/src/main.tsx | 2 +- vscode/src/documentation.ts | 13 ++++++++- 4 files changed, 25 insertions(+), 35 deletions(-) diff --git a/npm/qsharp/src/compiler/compiler.ts b/npm/qsharp/src/compiler/compiler.ts index c8b0bccdaa..0775d0bb8c 100644 --- a/npm/qsharp/src/compiler/compiler.ts +++ b/npm/qsharp/src/compiler/compiler.ts @@ -93,12 +93,11 @@ export interface ICompiler { operation?: IOperationInfo, ): Promise; - getDocumentationFiles(): Promise; - getDocumentationContent( - additionalSources?: Array[], + getDocumentation( + additionalSources?: [string, string][], targetProfile?: string, languageFeatures?: string[], - ): Promise; + ): Promise; checkExerciseSolution( userCode: string, @@ -278,39 +277,20 @@ export class Compiler implements ICompiler { ); } - // Returns all autogenerated documentation files for the standard library. - // This include file names and metadata, including specially formatted - // table of content file. - async getDocumentationFiles(): Promise { - return this.wasm.generate_docs(); - } - - // Returns autogenerated documentation for the standard library - // and loaded project (if requested). Only the array of documentation - // content is returned. - async getDocumentationContent( - additionalSources?: Array[], + // Returns all autogenerated documentation files for the standard library + // and loaded project (if requested). This include file names and metadata, + // including specially formatted table of content file. + async getDocumentation( + additionalSources?: [string, string][], targetProfile?: string, languageFeatures?: string[], - ): Promise { + ): Promise { // Get documentation from wasm layer - const docFiles: IDocFile[] = this.wasm.generate_docs( + return this.wasm.generate_docs( additionalSources, targetProfile, languageFeatures, ); - - const result: string[] = []; - for (const file of docFiles) { - // Some files may contain information other than documentation - // For example, table of content is a separate file in a special format - // We check presence of qsharp.name in metadata to make sure we take - // only files that contain documentation from some qsharp object. - if (file.metadata.indexOf("qsharp.name:") >= 0) { - result.push(file.contents); - } - } - return result; } async checkExerciseSolution( @@ -369,8 +349,7 @@ export const compilerProtocol: ServiceProtocol = { getQir: "request", getEstimates: "request", getCircuit: "request", - getDocumentationFiles: "request", - getDocumentationContent: "request", + getDocumentation: "request", run: "requestWithProgress", checkExerciseSolution: "requestWithProgress", }, diff --git a/npm/qsharp/test/basics.js b/npm/qsharp/test/basics.js index 32972445a2..e84782d97c 100644 --- a/npm/qsharp/test/basics.js +++ b/npm/qsharp/test/basics.js @@ -47,7 +47,7 @@ export function runSingleShot(code, expr, useWorker) { test("autogenerated documentation", async () => { const compiler = getCompiler(); const regex = new RegExp("^qsharp.namespace: (.+)$", "m"); - const docFiles = await compiler.getDocumentationFiles(); + const docFiles = await compiler.getDocumentation(); var numberOfGoodFiles = 0; for (const doc of docFiles) { assert(doc, "Each documentation file should be present."); diff --git a/playground/src/main.tsx b/playground/src/main.tsx index 5d4baf405c..b0a98964a9 100644 --- a/playground/src/main.tsx +++ b/playground/src/main.tsx @@ -117,7 +117,7 @@ function App(props: { katas: Kata[]; linkedCode?: string }) { createDocumentation(); }, []); async function createDocumentation() { - const docFiles = await compiler.getDocumentationFiles(); + const docFiles = await compiler.getDocumentation(); setDocumentation(processDocumentFiles(docFiles)); } diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 0b628e8f74..56fa45292a 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -30,12 +30,23 @@ export async function showDocumentationCommand(extensionUri: Uri) { "./out/compilerWorker.js", ).toString(); const worker = getCompilerWorker(compilerWorkerScriptPath); - const documentation = await worker.getDocumentationContent( + const docFiles = await worker.getDocumentation( program.sources, targetProfile, program.languageFeatures, ); + const documentation: string[] = []; + for (const file of docFiles) { + // Some files may contain information other than documentation + // For example, table of content is a separate file in a special format + // We check presence of qsharp.name in metadata to make sure we take + // only files that contain documentation from some qsharp object. + if (file.metadata.indexOf("qsharp.name:") >= 0) { + documentation.push(file.contents); + } + } + const message = { command: "showDocumentationCommand", // This is handled in webview.tsx onMessage fragmentsToRender: documentation, From c6e6565c7358c5059771cfd231fb0317c5c85773 Mon Sep 17 00:00:00 2001 From: DmitryVasilevsky <60718360+DmitryVasilevsky@users.noreply.github.com> Date: Mon, 13 May 2024 11:43:31 -0700 Subject: [PATCH 19/21] Apply suggestions from code review Co-authored-by: Alex Hansen --- compiler/qsc_doc_gen/src/generate_docs.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 9f1cf785c0..a390dadd89 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -35,18 +35,12 @@ impl Compilation { language_features: Option, ) -> Self { let mut package_store = PackageStore::new(compile::core()); - let actual_capabilities = match capabilities { - Some(c) => c, - None => TargetCapabilityFlags::default(), - }; + let actual_capabilities = capabilities.unwrap_or_default(); let std_unit = compile::std(&package_store, actual_capabilities); let std_package_id = package_store.insert(std_unit); if let Some(sources) = additional_sources { - let actual_language_features = match language_features { - Some(l) => l, - None => LanguageFeatures::default(), - }; + let actual_language_features = language_features.unwrap_or_default(); let unit = compile( &package_store, From 58a416156896e671fba76de2c1a3801cca130a71 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 13 May 2024 11:46:10 -0700 Subject: [PATCH 20/21] PR Feedback --- vscode/src/documentation.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 56fa45292a..d496f86cac 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -9,16 +9,17 @@ import { loadProject } from "./projectSystem"; import { sendMessageToPanel } from "./webviewPanel"; export async function showDocumentationCommand(extensionUri: Uri) { + const editor = window.activeTextEditor; + if (!editor || !isQsharpDocument(editor.document)) { + throw new Error("The currently active window is not a Q# file"); + } + // Reveal panel and show 'Loading...' for immediate feedback. sendMessageToPanel( "documentation", // This is needed to route the message to the proper panel true, // Reveal panel null, // With no message ); - const editor = window.activeTextEditor; - if (!editor || !isQsharpDocument(editor.document)) { - throw new Error("The currently active window is not a Q# file"); - } const docUri = editor.document.uri; const program = await loadProject(docUri); From 43729d4fbb97b71e577ceab9e6e06d49092b95f5 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 13 May 2024 12:20:44 -0700 Subject: [PATCH 21/21] Clarified additional sources --- compiler/qsc_doc_gen/src/generate_docs.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index a390dadd89..48a22ef3dd 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -28,7 +28,8 @@ struct Compilation { } impl Compilation { - /// Creates a new `Compilation` by compiling sources. + /// Creates a new `Compilation` by compiling standard library + /// and additional sources. pub(crate) fn new( additional_sources: Option, capabilities: Option, @@ -50,7 +51,7 @@ impl Compilation { actual_language_features, ); // We ignore errors here (unit.errors vector) and use whatever - // documentation we can produce. If future we may consider + // documentation we can produce. In future we may consider // displaying the fact of error presence on documentation page. package_store.insert(unit); @@ -127,6 +128,8 @@ impl Lookup for Compilation { } } +/// Generates and returns documentation files for the standard library +/// and additional sources (if specified.) #[must_use] pub fn generate_docs( additional_sources: Option,