diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index d4035dcd1..fc8a7de4b 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -12,6 +12,7 @@ import type { } from "../variant-analysis/shared/variant-analysis"; import type { QLDebugConfiguration } from "../debugger/debug-configuration"; import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; +import type { Usage } from "../data-extensions-editor/external-api-usage"; // A command function matching the signature that VS Code calls when // a command is invoked from a context menu on a TreeView with @@ -304,6 +305,10 @@ export type PackagingCommands = { export type DataExtensionsEditorCommands = { "codeQL.openDataExtensionsEditor": () => Promise; + "codeQLDataExtensionsEditor.jumpToUsageLocation": ( + usage: Usage, + databaseItem: DatabaseItem, + ) => Promise; }; export type EvalLogViewerCommands = { diff --git a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-module.ts b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-module.ts index cd60d3c10..918e28c78 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-module.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-module.ts @@ -3,7 +3,7 @@ import { DataExtensionsEditorView } from "./data-extensions-editor-view"; import { DataExtensionsEditorCommands } from "../common/commands"; import { CliVersionConstraint, CodeQLCliServer } from "../codeql-cli/cli"; import { QueryRunner } from "../query-server"; -import { DatabaseManager } from "../databases/local-databases"; +import { DatabaseItem, DatabaseManager } from "../databases/local-databases"; import { ensureDir } from "fs-extra"; import { join } from "path"; import { App } from "../common/app"; @@ -23,6 +23,8 @@ import { setUpPack } from "./external-api-usage-query"; import { DisposableObject } from "../common/disposable-object"; import { ModelDetailsPanel } from "./model-details/model-details-panel"; import { Mode } from "./shared/mode"; +import { showResolvableLocation } from "../databases/local-databases/locations"; +import { Usage } from "./external-api-usage"; const SUPPORTED_LANGUAGES: string[] = ["java", "csharp"]; @@ -145,7 +147,7 @@ export class DataExtensionsEditorModule extends DisposableObject { db, modelFile, Mode.Application, - (usages) => this.modelDetailsPanel.setExternalApiUsages(usages), + this.modelDetailsPanel.setState.bind(this.modelDetailsPanel), ); await view.openView(); }, @@ -154,6 +156,12 @@ export class DataExtensionsEditorModule extends DisposableObject { }, ); }, + "codeQLDataExtensionsEditor.jumpToUsageLocation": async ( + usage: Usage, + databaseItem: DatabaseItem, + ) => { + await showResolvableLocation(usage.url, databaseItem, this.app.logger); + }, }; } 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 ab5883094..98fd1bc41 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 @@ -70,8 +70,9 @@ export class DataExtensionsEditorView extends AbstractWebview< private readonly databaseItem: DatabaseItem, private readonly extensionPack: ExtensionPack, private mode: Mode, - private readonly onExternalApiUsagesChanged: ( + private readonly updateModelDetailsPanelState: ( externalApiUsages: ExternalApiUsage[], + databaseItem: DatabaseItem, ) => void, ) { super(ctx); @@ -235,21 +236,7 @@ export class DataExtensionsEditorView extends AbstractWebview< protected async jumpToUsage( location: ResolvableLocationValue, ): Promise { - try { - await showResolvableLocation(location, this.databaseItem); - } catch (e) { - if (e instanceof Error) { - if (e.message.match(/File not found/)) { - void window.showErrorMessage( - "Original file of this result is not in the database's source archive.", - ); - } else { - void this.app.logger.log(`Unable to handleMsgFromView: ${e.message}`); - } - } else { - void this.app.logger.log(`Unable to handleMsgFromView: ${e}`); - } - } + await showResolvableLocation(location, this.databaseItem, this.app.logger); } protected async loadExistingModeledMethods(): Promise { @@ -315,7 +302,10 @@ export class DataExtensionsEditorView extends AbstractWebview< t: "setExternalApiUsages", externalApiUsages, }); - this.onExternalApiUsagesChanged(externalApiUsages); + this.updateModelDetailsPanelState( + externalApiUsages, + this.databaseItem, + ); } catch (err) { void showAndLogExceptionWithTelemetry( this.app.logger, @@ -503,7 +493,7 @@ export class DataExtensionsEditorView extends AbstractWebview< addedDatabase, modelFile, Mode.Framework, - this.onExternalApiUsagesChanged, + this.updateModelDetailsPanelState, ); await view.openView(); }); diff --git a/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-data-provider.ts b/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-data-provider.ts index 8f88f199d..b8569a0ee 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-data-provider.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-data-provider.ts @@ -7,12 +7,14 @@ import { } from "vscode"; import { DisposableObject } from "../../common/disposable-object"; import { ExternalApiUsage, Usage } from "../external-api-usage"; +import { DatabaseItem } from "../../databases/local-databases"; export class ModelDetailsDataProvider extends DisposableObject implements TreeDataProvider { private externalApiUsages: ExternalApiUsage[] = []; + private databaseItem: DatabaseItem | undefined = undefined; private readonly onDidChangeTreeDataEmitter = this.push( new EventEmitter(), @@ -22,8 +24,12 @@ export class ModelDetailsDataProvider return this.onDidChangeTreeDataEmitter.event; } - public setExternalApiUsages(externalApiUsages: ExternalApiUsage[]): void { + public setState( + externalApiUsages: ExternalApiUsage[], + databaseItem: DatabaseItem, + ): void { this.externalApiUsages = externalApiUsages; + this.databaseItem = databaseItem; this.onDidChangeTreeDataEmitter.fire(); } @@ -37,6 +43,11 @@ export class ModelDetailsDataProvider return { label: item.label, collapsibleState: TreeItemCollapsibleState.None, + command: { + title: "Show usage", + command: "codeQLDataExtensionsEditor.jumpToUsageLocation", + arguments: [item, this.databaseItem], + }, }; } } diff --git a/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-panel.ts b/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-panel.ts index 26c6c862d..c2755b8e7 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-panel.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/model-details/model-details-panel.ts @@ -2,6 +2,7 @@ import { window } from "vscode"; import { DisposableObject } from "../../common/disposable-object"; import { ModelDetailsDataProvider } from "./model-details-data-provider"; import { ExternalApiUsage } from "../external-api-usage"; +import { DatabaseItem } from "../../databases/local-databases"; export class ModelDetailsPanel extends DisposableObject { private readonly dataProvider: ModelDetailsDataProvider; @@ -17,7 +18,10 @@ export class ModelDetailsPanel extends DisposableObject { this.push(treeView); } - public setExternalApiUsages(externalApiUsages: ExternalApiUsage[]): void { - this.dataProvider.setExternalApiUsages(externalApiUsages); + public setState( + externalApiUsages: ExternalApiUsage[], + databaseItem: DatabaseItem, + ): void { + this.dataProvider.setState(externalApiUsages, databaseItem); } } diff --git a/extensions/ql-vscode/src/databases/local-databases/locations.ts b/extensions/ql-vscode/src/databases/local-databases/locations.ts index 5654b01b1..9625cca3d 100644 --- a/extensions/ql-vscode/src/databases/local-databases/locations.ts +++ b/extensions/ql-vscode/src/databases/local-databases/locations.ts @@ -97,8 +97,19 @@ export function tryResolveLocation( export async function showResolvableLocation( loc: ResolvableLocationValue, databaseItem: DatabaseItem, + logger: Logger, ): Promise { - await showLocation(tryResolveLocation(loc, databaseItem)); + try { + await showLocation(tryResolveLocation(loc, databaseItem)); + } catch (e) { + if (e instanceof Error && e.message.match(/File not found/)) { + void Window.showErrorMessage( + "Original file of this result is not in the database's source archive.", + ); + } else { + void logger.log(`Unable to jump to location: ${getErrorMessage(e)}`); + } + } } export async function showLocation(location?: Location) { @@ -146,16 +157,6 @@ export async function jumpToLocation( ) { const databaseItem = databaseManager.findDatabaseItem(Uri.parse(databaseUri)); if (databaseItem !== undefined) { - try { - await showResolvableLocation(loc, databaseItem); - } catch (e) { - if (e instanceof Error && e.message.match(/File not found/)) { - void Window.showErrorMessage( - "Original file of this result is not in the database's source archive.", - ); - } else { - void logger.log(`Unable to jump to location: ${getErrorMessage(e)}`); - } - } + await showResolvableLocation(loc, databaseItem, logger); } } diff --git a/extensions/ql-vscode/test/unit-tests/command-lint.test.ts b/extensions/ql-vscode/test/unit-tests/command-lint.test.ts index 747b144d2..1475b5b23 100644 --- a/extensions/ql-vscode/test/unit-tests/command-lint.test.ts +++ b/extensions/ql-vscode/test/unit-tests/command-lint.test.ts @@ -44,7 +44,8 @@ describe("commands declared in package.json", () => { command.match(/^codeQLQueryHistory\./) || command.match(/^codeQLAstViewer\./) || command.match(/^codeQLEvalLogViewer\./) || - command.match(/^codeQLTests\./) + command.match(/^codeQLTests\./) || + command.match(/^codeQLDataExtensionsEditor\./) ) { scopedCmds.add(command); expect(title).toBeDefined();