From 744a516a44a94330d585b64d11b32d5e9d114eaf Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 17 Apr 2023 16:35:46 +0200 Subject: [PATCH 1/2] Show extension pack name in data extensions editor This will add the extension pack name to the data extensions editor and allow the user to click on it to go to the folder of the extension pack in the explorer panel. --- extensions/ql-vscode/src/common/commands.ts | 1 + .../data-extensions-editor-view.ts | 30 +++++++++----- .../extension-pack-picker.ts | 39 ++++++++++++++----- .../ql-vscode/src/pure/interface-types.ts | 6 +++ .../DataExtensionsEditor.stories.tsx | 1 + .../DataExtensionsEditor.tsx | 18 +++++++++ 6 files changed, 76 insertions(+), 19 deletions(-) diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index 47447c0c7..a8b4b54b9 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -87,6 +87,7 @@ export type BuiltInVsCodeCommands = { ) => Promise; "vscode.open": (uri: Uri) => Promise; "vscode.openFolder": (uri: Uri) => Promise; + revealInExplorer: (uri: Uri) => Promise; }; // Commands that are available before the extension is fully activated. diff --git a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts index 2594306ff..9185f81f9 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts @@ -1,6 +1,7 @@ import { CancellationTokenSource, ExtensionContext, + Uri, ViewColumn, window, workspace, @@ -34,6 +35,7 @@ import { readQueryResults, runQuery } from "./external-api-usage-query"; import { createDataExtensionYaml, loadDataExtensionYaml } from "./yaml"; import { ExternalApiUsage } from "./external-api-usage"; import { ModeledMethod } from "./modeled-method"; +import { ExtensionPackModelFile } from "./extension-pack-picker"; function getQlSubmoduleFolder(): WorkspaceFolder | undefined { const workspaceFolder = workspace.workspaceFolders?.find( @@ -60,7 +62,7 @@ export class DataExtensionsEditorView extends AbstractWebview< private readonly queryRunner: QueryRunner, private readonly queryStorageDir: string, private readonly databaseItem: DatabaseItem, - private readonly modelFilename: string, + private readonly modelFile: ExtensionPackModelFile, ) { super(ctx); } @@ -93,10 +95,17 @@ export class DataExtensionsEditorView extends AbstractWebview< case "viewLoaded": await this.onWebViewLoaded(); + break; + case "openExtensionPack": + await this.app.commands.execute( + "revealInExplorer", + Uri.file(this.modelFile.extensionPack.path), + ); + break; case "openModelFile": await window.showTextDocument( - await workspace.openTextDocument(this.modelFilename), + await workspace.openTextDocument(this.modelFile.filename), ); break; @@ -127,7 +136,8 @@ export class DataExtensionsEditorView extends AbstractWebview< await Promise.all([ this.postMessage({ t: "setDataExtensionEditorInitialData", - modelFilename: this.modelFilename, + extensionPackName: this.modelFile.extensionPack.name, + modelFilename: this.modelFile.filename, }), this.loadExternalApiUsages(), this.loadExistingModeledMethods(), @@ -164,24 +174,26 @@ export class DataExtensionsEditorView extends AbstractWebview< modeledMethods, ); - await outputFile(this.modelFilename, yaml); + await outputFile(this.modelFile.filename, yaml); - void extLogger.log(`Saved data extension YAML to ${this.modelFilename}`); + void extLogger.log( + `Saved data extension YAML to ${this.modelFile.filename}`, + ); } protected async loadExistingModeledMethods(): Promise { try { - const yaml = await readFile(this.modelFilename, "utf8"); + const yaml = await readFile(this.modelFile.filename, "utf8"); const data = loadYaml(yaml, { - filename: this.modelFilename, + filename: this.modelFile.filename, }); const existingModeledMethods = loadDataExtensionYaml(data); if (!existingModeledMethods) { void showAndLogErrorMessage( - `Failed to parse data extension YAML ${this.modelFilename}.`, + `Failed to parse data extension YAML ${this.modelFile.filename}.`, ); return; } @@ -193,7 +205,7 @@ export class DataExtensionsEditorView extends AbstractWebview< } catch (e: unknown) { void showAndLogErrorMessage( `Unable to read data extension YAML ${ - this.modelFilename + this.modelFile.filename }: ${getErrorMessage(e)}`, ); } diff --git a/extensions/ql-vscode/src/data-extensions-editor/extension-pack-picker.ts b/extensions/ql-vscode/src/data-extensions-editor/extension-pack-picker.ts index 87f2d7220..99df583a2 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/extension-pack-picker.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/extension-pack-picker.ts @@ -21,26 +21,36 @@ const packNameRegex = new RegExp( ); const packNameLength = 128; +export interface ExtensionPack { + name: string; + path: string; +} + +export interface ExtensionPackModelFile { + filename: string; + extensionPack: ExtensionPack; +} + export async function pickExtensionPackModelFile( cliServer: Pick, databaseItem: Pick, progress: ProgressCallback, token: CancellationToken, -): Promise { - const extensionPackPath = await pickExtensionPack( +): Promise { + const extensionPack = await pickExtensionPack( cliServer, databaseItem, progress, token, ); - if (!extensionPackPath) { - return; + if (!extensionPack) { + return undefined; } const modelFile = await pickModelFile( cliServer, databaseItem, - extensionPackPath, + extensionPack.path, progress, token, ); @@ -48,7 +58,10 @@ export async function pickExtensionPackModelFile( return; } - return modelFile; + return { + filename: modelFile, + extensionPack, + }; } async function pickExtensionPack( @@ -56,7 +69,7 @@ async function pickExtensionPack( databaseItem: Pick, progress: ProgressCallback, token: CancellationToken, -): Promise { +): Promise { progress({ message: "Resolving extension packs...", step: 1, @@ -117,7 +130,10 @@ async function pickExtensionPack( return undefined; } - return extensionPackPaths[0]; + return { + name: extensionPackOption.extensionPack, + path: extensionPackPaths[0], + }; } async function pickModelFile( @@ -186,7 +202,7 @@ async function pickModelFile( async function pickNewExtensionPack( databaseItem: Pick, token: CancellationToken, -): Promise { +): Promise { const workspaceFolders = getOnDiskWorkspaceFoldersObjects(); const workspaceFolderOptions = workspaceFolders.map((folder) => ({ label: folder.name, @@ -263,7 +279,10 @@ async function pickNewExtensionPack( }), ); - return packPath; + return { + name: packName, + path: packPath, + }; } async function pickNewModelFile( diff --git a/extensions/ql-vscode/src/pure/interface-types.ts b/extensions/ql-vscode/src/pure/interface-types.ts index c02be2c37..6dda76926 100644 --- a/extensions/ql-vscode/src/pure/interface-types.ts +++ b/extensions/ql-vscode/src/pure/interface-types.ts @@ -483,6 +483,7 @@ export type FromDataFlowPathsMessage = CommonFromViewMessages; export interface SetDataExtensionEditorInitialDataMessage { t: "setDataExtensionEditorInitialData"; + extensionPackName: string; modelFilename: string; } @@ -516,6 +517,10 @@ export interface JumpToUsageMessage { location: ResolvableLocationValue; } +export interface OpenExtensionPackMessage { + t: "openExtensionPack"; +} + export interface OpenModelFileMessage { t: "openModelFile"; } @@ -539,6 +544,7 @@ export type ToDataExtensionsEditorMessage = export type FromDataExtensionsEditorMessage = | ViewLoadedMsg | OpenModelFileMessage + | OpenExtensionPackMessage | JumpToUsageMessage | SaveModeledMethods | GenerateExternalApiMessage; diff --git a/extensions/ql-vscode/src/stories/data-extensions-editor/DataExtensionsEditor.stories.tsx b/extensions/ql-vscode/src/stories/data-extensions-editor/DataExtensionsEditor.stories.tsx index 928a75ad6..46b10ff5c 100644 --- a/extensions/ql-vscode/src/stories/data-extensions-editor/DataExtensionsEditor.stories.tsx +++ b/extensions/ql-vscode/src/stories/data-extensions-editor/DataExtensionsEditor.stories.tsx @@ -15,6 +15,7 @@ const Template: ComponentStory = ( export const DataExtensionsEditor = Template.bind({}); DataExtensionsEditor.args = { + initialExtensionPackName: "codeql/sql2o-models", initialModelFilename: "/home/user/vscode-codeql-starter/codeql-custom-queries-java/sql2o/models/sql2o.yml", initialExternalApiUsages: [ diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx b/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx index 4eca345a4..129f69f36 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx +++ b/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx @@ -47,16 +47,21 @@ const ProgressBar = styled.div` `; type Props = { + initialExtensionPackName?: string; initialModelFilename?: string; initialExternalApiUsages?: ExternalApiUsage[]; initialModeledMethods?: Record; }; export function DataExtensionsEditor({ + initialExtensionPackName, initialModelFilename, initialExternalApiUsages = [], initialModeledMethods = {}, }: Props): JSX.Element { + const [extensionPackName, setExtensionPackName] = useState< + string | undefined + >(initialExtensionPackName); const [modelFilename, setModelFilename] = useState( initialModelFilename, ); @@ -79,6 +84,7 @@ export function DataExtensionsEditor({ const msg: ToDataExtensionsEditorMessage = evt.data; switch (msg.t) { case "setDataExtensionEditorInitialData": + setExtensionPackName(msg.extensionPackName); setModelFilename(msg.modelFilename); break; case "setExternalApiUsages": @@ -150,6 +156,12 @@ export function DataExtensionsEditor({ }); }, []); + const onOpenExtensionPackClick = useCallback(() => { + vscode.postMessage({ + t: "openExtensionPack", + }); + }, []); + const onOpenModelFileClick = useCallback(() => { vscode.postMessage({ t: "openModelFile", @@ -169,6 +181,12 @@ export function DataExtensionsEditor({ <> Data extensions editor + {extensionPackName && ( + + + {extensionPackName} + + )} {modelFilename && ( From 1ae55897282e18f42e4ea6d14b4d52c6d29da0da Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 18 Apr 2023 10:31:28 +0200 Subject: [PATCH 2/2] Update extension pack picker tests --- .../extension-pack-picker.test.ts | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/extension-pack-picker.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/extension-pack-picker.test.ts index 88ae01773..7963902c1 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/extension-pack-picker.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/extension-pack-picker.test.ts @@ -73,7 +73,13 @@ describe("pickExtensionPackModelFile", () => { progress, token, ), - ).toEqual("/a/b/c/my-extension-pack/models/model.yml"); + ).toEqual({ + filename: "/a/b/c/my-extension-pack/models/model.yml", + extensionPack: { + name: "my-extension-pack", + path: "/a/b/c/my-extension-pack", + }, + }); expect(showQuickPickSpy).toHaveBeenCalledTimes(2); expect(showQuickPickSpy).toHaveBeenCalledWith( [ @@ -174,7 +180,13 @@ describe("pickExtensionPackModelFile", () => { progress, token, ), - ).toEqual(join(tmpDir.path, "models/my-model.yml")); + ).toEqual({ + filename: join(tmpDir.path, "models/my-model.yml"), + extensionPack: { + name: "my-extension-pack", + path: tmpDir.path, + }, + }); expect(showQuickPickSpy).toHaveBeenCalledTimes(2); expect(showQuickPickSpy).toHaveBeenCalledWith( [ @@ -264,7 +276,18 @@ describe("pickExtensionPackModelFile", () => { progress, token, ), - ).toEqual(join(tmpDir.path, "my-extension-pack", "models", "my-model.yml")); + ).toEqual({ + filename: join( + tmpDir.path, + "my-extension-pack", + "models", + "my-model.yml", + ), + extensionPack: { + name: "my-extension-pack", + path: join(tmpDir.path, "my-extension-pack"), + }, + }); expect(showQuickPickSpy).toHaveBeenCalledTimes(1); expect(showInputBoxSpy).toHaveBeenCalledTimes(2); expect(showInputBoxSpy).toHaveBeenCalledWith( @@ -329,7 +352,18 @@ describe("pickExtensionPackModelFile", () => { progress, token, ), - ).toEqual(join(tmpDir.path, "my-extension-pack", "models", "my-model.yml")); + ).toEqual({ + filename: join( + tmpDir.path, + "my-extension-pack", + "models", + "my-model.yml", + ), + extensionPack: { + name: "my-extension-pack", + path: join(tmpDir.path, "my-extension-pack"), + }, + }); expect(showQuickPickSpy).toHaveBeenCalledTimes(1); expect(showInputBoxSpy).toHaveBeenCalledTimes(2); expect(showInputBoxSpy).toHaveBeenCalledWith( @@ -508,7 +542,13 @@ describe("pickExtensionPackModelFile", () => { progress, token, ), - ).toEqual(join(tmpDir.path, "models/my-model.yml")); + ).toEqual({ + filename: join(tmpDir.path, "models", "my-model.yml"), + extensionPack: { + name: "my-extension-pack", + path: tmpDir.path, + }, + }); expect(showQuickPickSpy).toHaveBeenCalledTimes(1); expect(showInputBoxSpy).toHaveBeenCalledWith( {