From 15a86559315d5ae52c514f0682a1df2849940fa8 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 25 Oct 2023 12:49:28 +0200 Subject: [PATCH 1/5] Do not prompt for database download by default This changes the skeleton query wizard to not prompt for database download after creating a query by default. Instead, it will show a message with a button to download a database which will launch the same prompt. --- .../local-queries/skeleton-query-wizard.ts | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 4f24ff48b71..9a86f44cec9 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -1,5 +1,5 @@ import { join } from "path"; -import { Uri, workspace, window as Window } from "vscode"; +import { Uri, window as Window, window, workspace } from "vscode"; import { CodeQLCliServer } from "../codeql-cli/cli"; import { BaseLogger } from "../common/logging"; import { Credentials } from "../common/authentication"; @@ -14,6 +14,7 @@ import { DatabaseItem, DatabaseManager } from "../databases/local-databases"; import { ProgressCallback, UserCancellationException, + withProgress, } from "../common/vscode/progress"; import { askForGitHubRepo, @@ -26,6 +27,7 @@ import { } from "../config"; import { existsSync } from "fs-extra"; import { askForLanguage } from "../codeql-cli/query-language"; +import { showInformationMessageWithAction } from "../common/vscode/dialog"; type QueryLanguagesToDatabaseMap = Record; @@ -105,7 +107,9 @@ export class SkeletonQueryWizard { ); void workspace.openTextDocument(queryFileUri).then((doc) => { - void Window.showTextDocument(doc); + void Window.showTextDocument(doc, { + preview: false, + }); }); } @@ -227,7 +231,24 @@ export class SkeletonQueryWizard { return `example${qlFiles.length + 1}.ql`; } - private async downloadDatabase() { + private async promptDownloadDatabase() { + if (this.qlPackStoragePath === undefined) { + throw new Error("QL Pack storage path is undefined"); + } + + const openFileLink = this.openFileMarkdownLink; + + const action = await showInformationMessageWithAction( + `New CodeQL query for ${this.language} ${openFileLink} created, but no CodeQL databases for ${this.language} were detected in your workspace. Would you like to download a CodeQL database for ${this.language} to analyze with ${openFileLink}?`, + "Download database", + ); + + if (action) { + void withProgress((progress) => this.downloadDatabase(progress)); + } + } + + private async downloadDatabase(progress: ProgressCallback) { if (this.qlPackStoragePath === undefined) { throw new Error("QL Pack storage path is undefined"); } @@ -240,10 +261,10 @@ export class SkeletonQueryWizard { throw new Error("Language is undefined"); } - this.progress({ + progress({ message: "Downloading database", - step: 3, - maxStep: 3, + step: 1, + maxStep: 2, }); const githubRepoNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[this.language]; @@ -258,7 +279,7 @@ export class SkeletonQueryWizard { this.databaseManager, this.databaseStoragePath, this.credentials, - this.progress, + progress, this.cliServer, this.language, ); @@ -280,12 +301,32 @@ export class SkeletonQueryWizard { ); if (existingDatabaseItem) { - // select the found database - await this.databaseManager.setCurrentDatabaseItem(existingDatabaseItem); + const openFileLink = this.openFileMarkdownLink; + + if (this.databaseManager.currentDatabaseItem !== existingDatabaseItem) { + // select the found database + await this.databaseManager.setCurrentDatabaseItem(existingDatabaseItem); + + void window.showInformationMessage( + `New CodeQL query for ${this.language} ${openFileLink} created. We have automatically selected your existing CodeQL ${this.language} database ${existingDatabaseItem.name} for you to analyze with ${openFileLink}.`, + ); + } } else { // download new database and select it - await this.downloadDatabase(); + void this.promptDownloadDatabase(); + } + } + + private get openFileMarkdownLink() { + if (this.qlPackStoragePath === undefined) { + throw new Error("QL Pack storage path is undefined"); } + + const openFileArgs = [ + join(this.qlPackStoragePath, this.folderName, this.fileName), + ]; + const queryString = encodeURI(JSON.stringify(openFileArgs)); + return `[${this.fileName}](command:vscode.open?${queryString})`; } public static async findDatabaseItemByNwo( From f3eefc94183398060a4383cc801f7e6409ad8ae0 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 25 Oct 2023 14:22:31 +0200 Subject: [PATCH 2/5] Add tests for prompting for database download --- .../local-queries/skeleton-query-wizard.ts | 15 +- .../skeleton-query-wizard.test.ts | 183 +++++++++++++++--- 2 files changed, 166 insertions(+), 32 deletions(-) diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 9a86f44cec9..6e4efc65101 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -45,6 +45,7 @@ export const QUERY_LANGUAGE_TO_DATABASE_REPO: QueryLanguagesToDatabaseMap = { export class SkeletonQueryWizard { private fileName = "example.ql"; private qlPackStoragePath: string | undefined; + private downloadPromise: Promise | undefined; constructor( private readonly cliServer: CodeQLCliServer, @@ -60,6 +61,16 @@ export class SkeletonQueryWizard { return `codeql-custom-queries-${this.language}`; } + /** + * Wait for the download process to complete by waiting for the user to select + * either "Download database" or closing the dialog. This is used for testing. + */ + public async waitForDownload() { + if (this.downloadPromise) { + await this.downloadPromise; + } + } + public async execute() { if (!this.language) { // show quick pick to choose language @@ -313,7 +324,9 @@ export class SkeletonQueryWizard { } } else { // download new database and select it - void this.promptDownloadDatabase(); + this.downloadPromise = this.promptDownloadDatabase().finally(() => { + this.downloadPromise = undefined; + }); } } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index 122bc8e7ae7..c6e1806c488 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -5,7 +5,13 @@ import { } from "../../../../src/local-queries/skeleton-query-wizard"; import { mockedObject, mockedQuickPickItem } from "../../utils/mocking.helpers"; import * as tmp from "tmp"; -import { TextDocument, window, workspace, WorkspaceFolder } from "vscode"; +import { + MessageItem, + TextDocument, + window, + workspace, + WorkspaceFolder, +} from "vscode"; import { extLogger } from "../../../../src/common/logging/vscode"; import { QlPackGenerator } from "../../../../src/local-queries/qlpack-generator"; import * as workspaceFolders from "../../../../src/common/vscode/workspace-folders"; @@ -31,6 +37,9 @@ describe("SkeletonQueryWizard", () => { let storagePath: string; let quickPickSpy: jest.SpiedFunction; let showInputBoxSpy: jest.SpiedFunction; + let showInformationMessageSpy: jest.SpiedFunction< + typeof window.showInformationMessage + >; let generateSpy: jest.SpiedFunction< typeof QlPackGenerator.prototype.generate >; @@ -97,6 +106,9 @@ describe("SkeletonQueryWizard", () => { showInputBoxSpy = jest .spyOn(window, "showInputBox") .mockResolvedValue(storagePath); + showInformationMessageSpy = jest + .spyOn(window, "showInformationMessage") + .mockResolvedValue(undefined); generateSpy = jest .spyOn(QlPackGenerator.prototype, "generate") .mockResolvedValue(undefined); @@ -168,9 +180,32 @@ describe("SkeletonQueryWizard", () => { expect(generateSpy).toHaveBeenCalled(); }); - it("should download database for selected language", async () => { + it("should prompt for download database", async () => { await wizard.execute(); + expect(showInformationMessageSpy).toHaveBeenCalledWith( + expect.stringMatching(/a CodeQL database/i), + expect.objectContaining({ + title: "Download database", + }), + ); + expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); + }); + + it("should download database for selected language when selecting download in prompt", async () => { + showInformationMessageSpy.mockImplementation( + async (_message, options, item) => { + if (item === undefined) { + return options as MessageItem; + } + + return item; + }, + ); + + await wizard.execute(); + await wizard.waitForDownload(); + expect(downloadGitHubDatabaseSpy).toHaveBeenCalled(); }); @@ -259,51 +294,126 @@ describe("SkeletonQueryWizard", () => { name: databaseNwo, language: chosenLanguage, } as DatabaseItem; + }); - mockDatabaseManagerWithItems = mockedObject({ - setCurrentDatabaseItem: jest.fn(), - databaseItems: [databaseItem] as DatabaseItem[], + describe("with database selected", () => { + beforeEach(async () => { + mockDatabaseManagerWithItems = mockedObject({ + currentDatabaseItem: databaseItem, + setCurrentDatabaseItem: jest.fn(), + databaseItems: [databaseItem] as DatabaseItem[], + }); + + wizard = new SkeletonQueryWizard( + mockCli, + jest.fn(), + credentials, + extLogger, + mockDatabaseManagerWithItems, + storagePath, + ); }); - wizard = new SkeletonQueryWizard( - mockCli, - jest.fn(), - credentials, - extLogger, - mockDatabaseManagerWithItems, - storagePath, - ); - }); + it("should not download a new database for language", async () => { + await wizard.execute(); - it("should not download a new database for language", async () => { - await wizard.execute(); + expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); + }); - expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); - }); + it("should not select the database", async () => { + await wizard.execute(); - it("should select an existing database", async () => { - await wizard.execute(); + expect( + mockDatabaseManagerWithItems.setCurrentDatabaseItem, + ).not.toHaveBeenCalled(); + }); + + it("should open the new query file", async () => { + await wizard.execute(); + + expect(openTextDocumentSpy).toHaveBeenCalledWith( + expect.objectContaining({ + path: expect.stringMatching("example2.ql"), + }), + ); + }); + + it("should not show an information message", async () => { + await wizard.execute(); - expect( - mockDatabaseManagerWithItems.setCurrentDatabaseItem, - ).toHaveBeenCalledWith(databaseItem); + expect(showInformationMessageSpy).not.toHaveBeenCalled(); + }); }); - it("should open the new query file", async () => { - await wizard.execute(); + describe("with database not selected", () => { + beforeEach(async () => { + mockDatabaseManagerWithItems = mockedObject({ + currentDatabaseItem: undefined, + setCurrentDatabaseItem: jest.fn(), + databaseItems: [databaseItem] as DatabaseItem[], + }); + + wizard = new SkeletonQueryWizard( + mockCli, + jest.fn(), + credentials, + extLogger, + mockDatabaseManagerWithItems, + storagePath, + ); + }); - expect(openTextDocumentSpy).toHaveBeenCalledWith( - expect.objectContaining({ - path: expect.stringMatching("example2.ql"), - }), - ); + it("should not download a new database for language", async () => { + await wizard.execute(); + + expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); + }); + + it("should select an existing database", async () => { + await wizard.execute(); + + expect( + mockDatabaseManagerWithItems.setCurrentDatabaseItem, + ).toHaveBeenCalledWith(databaseItem); + }); + + it("should open the new query file", async () => { + await wizard.execute(); + + expect(openTextDocumentSpy).toHaveBeenCalledWith( + expect.objectContaining({ + path: expect.stringMatching("example2.ql"), + }), + ); + }); + + it("should show an information message", async () => { + await wizard.execute(); + + expect(showInformationMessageSpy).toHaveBeenCalledWith( + expect.stringMatching(new RegExp(databaseNwo)), + ); + }); }); }); describe("if database is missing", () => { - describe("if the user choses to downloaded the suggested database from GitHub", () => { + describe("if the user chooses to downloaded the suggested database from GitHub", () => { + beforeEach(() => { + showInformationMessageSpy.mockImplementation( + async (_message, options, item) => { + if (item === undefined) { + return options as MessageItem; + } + + return item; + }, + ); + }); + it("should download a new database for language", async () => { await wizard.execute(); + await wizard.waitForDownload(); expect(askForGitHubRepoSpy).toHaveBeenCalled(); expect(downloadGitHubDatabaseSpy).toHaveBeenCalled(); @@ -312,6 +422,16 @@ describe("SkeletonQueryWizard", () => { describe("if the user choses to download a different database from GitHub than the one suggested", () => { beforeEach(() => { + showInformationMessageSpy.mockImplementation( + async (_message, options, item) => { + if (item === undefined) { + return options as MessageItem; + } + + return item; + }, + ); + const chosenGitHubRepo = "pickles-owner/pickles-repo"; askForGitHubRepoSpy = jest @@ -321,6 +441,7 @@ describe("SkeletonQueryWizard", () => { it("should download the newly chosen database", async () => { await wizard.execute(); + await wizard.waitForDownload(); expect(askForGitHubRepoSpy).toHaveBeenCalled(); expect(downloadGitHubDatabaseSpy).toHaveBeenCalled(); From 42f1e81fdc7913d195e616c139cc63e16fc9e7e7 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 27 Oct 2023 10:04:37 +0200 Subject: [PATCH 3/5] Handle errors when downloading database --- .../src/local-queries/local-queries.ts | 2 +- .../local-queries/skeleton-query-wizard.ts | 34 +++++++++++++++---- .../ql-vscode/test/__mocks__/appMock.ts | 2 +- .../skeleton-query-wizard.test.ts | 15 ++++---- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index e63a111826e..782a68226a3 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -330,7 +330,7 @@ export class LocalQueries extends DisposableObject { this.cliServer, progress, credentials, - this.app.logger, + this.app, this.databaseManager, contextStoragePath, language, diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 6e4efc65101..6fa9d1adbe1 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -1,14 +1,14 @@ import { join } from "path"; import { Uri, window as Window, window, workspace } from "vscode"; import { CodeQLCliServer } from "../codeql-cli/cli"; -import { BaseLogger } from "../common/logging"; +import { showAndLogExceptionWithTelemetry } from "../common/logging"; import { Credentials } from "../common/authentication"; import { QueryLanguage } from "../common/query-language"; import { getFirstWorkspaceFolder, isFolderAlreadyInWorkspace, } from "../common/vscode/workspace-folders"; -import { getErrorMessage } from "../common/helpers-pure"; +import { asError, getErrorMessage } from "../common/helpers-pure"; import { QlPackGenerator } from "./qlpack-generator"; import { DatabaseItem, DatabaseManager } from "../databases/local-databases"; import { @@ -28,6 +28,8 @@ import { import { existsSync } from "fs-extra"; import { askForLanguage } from "../codeql-cli/query-language"; import { showInformationMessageWithAction } from "../common/vscode/dialog"; +import { redactableError } from "../common/errors"; +import { App } from "../common/app"; type QueryLanguagesToDatabaseMap = Record; @@ -51,7 +53,7 @@ export class SkeletonQueryWizard { private readonly cliServer: CodeQLCliServer, private readonly progress: ProgressCallback, private readonly credentials: Credentials | undefined, - private readonly logger: BaseLogger, + private readonly app: App, private readonly databaseManager: DatabaseManager, private readonly databaseStoragePath: string | undefined, private language: QueryLanguage | undefined = undefined, @@ -99,7 +101,7 @@ export class SkeletonQueryWizard { try { await this.openExampleFile(); } catch (e: unknown) { - void this.logger.log( + void this.app.logger.log( `Could not open example query file: ${getErrorMessage(e)}`, ); } @@ -190,7 +192,7 @@ export class SkeletonQueryWizard { await qlPackGenerator.generate(); } catch (e: unknown) { - void this.logger.log( + void this.app.logger.log( `Could not create skeleton QL pack: ${getErrorMessage(e)}`, ); } @@ -222,7 +224,7 @@ export class SkeletonQueryWizard { this.fileName = await this.determineNextFileName(this.folderName); await qlPackGenerator.createExampleQlFile(this.fileName); } catch (e: unknown) { - void this.logger.log( + void this.app.logger.log( `Could not create skeleton QL pack: ${getErrorMessage(e)}`, ); } @@ -255,7 +257,25 @@ export class SkeletonQueryWizard { ); if (action) { - void withProgress((progress) => this.downloadDatabase(progress)); + void withProgress(async (progress) => { + try { + await this.downloadDatabase(progress); + } catch (e: unknown) { + if (e instanceof UserCancellationException) { + return; + } + + void showAndLogExceptionWithTelemetry( + this.app.logger, + this.app.telemetry, + redactableError( + asError(e), + )`An error occurred while downloading the GitHub repository: ${getErrorMessage( + e, + )}`, + ); + } + }); } } diff --git a/extensions/ql-vscode/test/__mocks__/appMock.ts b/extensions/ql-vscode/test/__mocks__/appMock.ts index 797ea3d13ce..89f5cd534af 100644 --- a/extensions/ql-vscode/test/__mocks__/appMock.ts +++ b/extensions/ql-vscode/test/__mocks__/appMock.ts @@ -34,7 +34,7 @@ export function createMockApp({ environment?: EnvironmentContext; logger?: NotificationLogger; telemetry?: AppTelemetry; -}): App { +} = {}): App { return { mode: AppMode.Test, logger, diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index c6e1806c488..465be9a3aee 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -12,7 +12,6 @@ import { workspace, WorkspaceFolder, } from "vscode"; -import { extLogger } from "../../../../src/common/logging/vscode"; import { QlPackGenerator } from "../../../../src/local-queries/qlpack-generator"; import * as workspaceFolders from "../../../../src/common/vscode/workspace-folders"; import { createFileSync, ensureDirSync, removeSync } from "fs-extra"; @@ -28,9 +27,12 @@ import { createMockDB } from "../../../factories/databases/databases"; import { asError } from "../../../../src/common/helpers-pure"; import { Setting } from "../../../../src/config"; import { QueryLanguage } from "../../../../src/common/query-language"; +import { App } from "../../../../src/common/app"; +import { createMockApp } from "../../../__mocks__/appMock"; describe("SkeletonQueryWizard", () => { let mockCli: CodeQLCliServer; + let mockApp: App; let wizard: SkeletonQueryWizard; let mockDatabaseManager: DatabaseManager; let dir: tmp.DirResult; @@ -59,8 +61,6 @@ describe("SkeletonQueryWizard", () => { const credentials = testCredentialsWithStub(); const chosenLanguage = "ruby"; - jest.spyOn(extLogger, "log").mockResolvedValue(undefined); - beforeEach(async () => { mockCli = mockedObject({ resolveLanguages: jest @@ -76,6 +76,7 @@ describe("SkeletonQueryWizard", () => { ]), getSupportedLanguages: jest.fn(), }); + mockApp = createMockApp(); mockDatabaseManager = mockedObject({ setCurrentDatabaseItem: jest.fn(), @@ -126,7 +127,7 @@ describe("SkeletonQueryWizard", () => { mockCli, jest.fn(), credentials, - extLogger, + mockApp, mockDatabaseManager, storagePath, ); @@ -153,7 +154,7 @@ describe("SkeletonQueryWizard", () => { mockCli, jest.fn(), credentials, - extLogger, + mockApp, mockDatabaseManager, storagePath, QueryLanguage.Swift, @@ -308,7 +309,7 @@ describe("SkeletonQueryWizard", () => { mockCli, jest.fn(), credentials, - extLogger, + mockApp, mockDatabaseManagerWithItems, storagePath, ); @@ -357,7 +358,7 @@ describe("SkeletonQueryWizard", () => { mockCli, jest.fn(), credentials, - extLogger, + mockApp, mockDatabaseManagerWithItems, storagePath, ); From 1151432ca2a9073512ed1e0d3969d248a4d8a2b3 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 27 Oct 2023 10:07:22 +0200 Subject: [PATCH 4/5] Use language display name in notifications --- .../src/local-queries/skeleton-query-wizard.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 6fa9d1adbe1..415fe21ffa0 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -3,7 +3,10 @@ import { Uri, window as Window, window, workspace } from "vscode"; import { CodeQLCliServer } from "../codeql-cli/cli"; import { showAndLogExceptionWithTelemetry } from "../common/logging"; import { Credentials } from "../common/authentication"; -import { QueryLanguage } from "../common/query-language"; +import { + getLanguageDisplayName, + QueryLanguage, +} from "../common/query-language"; import { getFirstWorkspaceFolder, isFolderAlreadyInWorkspace, @@ -249,10 +252,15 @@ export class SkeletonQueryWizard { throw new Error("QL Pack storage path is undefined"); } + if (this.language === undefined) { + throw new Error("Language is undefined"); + } + const openFileLink = this.openFileMarkdownLink; + const displayLanguage = getLanguageDisplayName(this.language); const action = await showInformationMessageWithAction( - `New CodeQL query for ${this.language} ${openFileLink} created, but no CodeQL databases for ${this.language} were detected in your workspace. Would you like to download a CodeQL database for ${this.language} to analyze with ${openFileLink}?`, + `New CodeQL query for ${displayLanguage} ${openFileLink} created, but no CodeQL databases for ${displayLanguage} were detected in your workspace. Would you like to download a CodeQL database for ${displayLanguage} to analyze with ${openFileLink}?`, "Download database", ); @@ -338,8 +346,9 @@ export class SkeletonQueryWizard { // select the found database await this.databaseManager.setCurrentDatabaseItem(existingDatabaseItem); + const displayLanguage = getLanguageDisplayName(this.language); void window.showInformationMessage( - `New CodeQL query for ${this.language} ${openFileLink} created. We have automatically selected your existing CodeQL ${this.language} database ${existingDatabaseItem.name} for you to analyze with ${openFileLink}.`, + `New CodeQL query for ${displayLanguage} ${openFileLink} created. We have automatically selected your existing CodeQL ${displayLanguage} database ${existingDatabaseItem.name} for you to analyze with ${openFileLink}.`, ); } } else { From 8a87db6cb41d17dd0598397a31dc7bb0806a1b28 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 27 Oct 2023 10:15:58 +0200 Subject: [PATCH 5/5] Use URI for open file link --- .../src/local-queries/skeleton-query-wizard.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 415fe21ffa0..2d86c3fd992 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -364,9 +364,14 @@ export class SkeletonQueryWizard { throw new Error("QL Pack storage path is undefined"); } - const openFileArgs = [ - join(this.qlPackStoragePath, this.folderName, this.fileName), - ]; + const queryPath = join( + this.qlPackStoragePath, + this.folderName, + this.fileName, + ); + const queryPathUri = Uri.file(queryPath); + + const openFileArgs = [queryPathUri.toString(true)]; const queryString = encodeURI(JSON.stringify(openFileArgs)); return `[${this.fileName}](command:vscode.open?${queryString})`; }