Merge pull request #2679 from github/robertbrignull/data-details-jump

Implementing jumping to location when clicking on a usage in the details panel
This commit is contained in:
Robert
2023-08-09 13:04:08 +01:00
committed by GitHub
7 changed files with 56 additions and 36 deletions

View File

@@ -12,6 +12,7 @@ import type {
} from "../variant-analysis/shared/variant-analysis"; } from "../variant-analysis/shared/variant-analysis";
import type { QLDebugConfiguration } from "../debugger/debug-configuration"; import type { QLDebugConfiguration } from "../debugger/debug-configuration";
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; 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 function matching the signature that VS Code calls when
// a command is invoked from a context menu on a TreeView with // a command is invoked from a context menu on a TreeView with
@@ -304,6 +305,10 @@ export type PackagingCommands = {
export type DataExtensionsEditorCommands = { export type DataExtensionsEditorCommands = {
"codeQL.openDataExtensionsEditor": () => Promise<void>; "codeQL.openDataExtensionsEditor": () => Promise<void>;
"codeQLDataExtensionsEditor.jumpToUsageLocation": (
usage: Usage,
databaseItem: DatabaseItem,
) => Promise<void>;
}; };
export type EvalLogViewerCommands = { export type EvalLogViewerCommands = {

View File

@@ -3,7 +3,7 @@ import { DataExtensionsEditorView } from "./data-extensions-editor-view";
import { DataExtensionsEditorCommands } from "../common/commands"; import { DataExtensionsEditorCommands } from "../common/commands";
import { CliVersionConstraint, CodeQLCliServer } from "../codeql-cli/cli"; import { CliVersionConstraint, CodeQLCliServer } from "../codeql-cli/cli";
import { QueryRunner } from "../query-server"; import { QueryRunner } from "../query-server";
import { DatabaseManager } from "../databases/local-databases"; import { DatabaseItem, DatabaseManager } from "../databases/local-databases";
import { ensureDir } from "fs-extra"; import { ensureDir } from "fs-extra";
import { join } from "path"; import { join } from "path";
import { App } from "../common/app"; import { App } from "../common/app";
@@ -23,6 +23,8 @@ import { setUpPack } from "./external-api-usage-query";
import { DisposableObject } from "../common/disposable-object"; import { DisposableObject } from "../common/disposable-object";
import { ModelDetailsPanel } from "./model-details/model-details-panel"; import { ModelDetailsPanel } from "./model-details/model-details-panel";
import { Mode } from "./shared/mode"; import { Mode } from "./shared/mode";
import { showResolvableLocation } from "../databases/local-databases/locations";
import { Usage } from "./external-api-usage";
const SUPPORTED_LANGUAGES: string[] = ["java", "csharp"]; const SUPPORTED_LANGUAGES: string[] = ["java", "csharp"];
@@ -145,7 +147,7 @@ export class DataExtensionsEditorModule extends DisposableObject {
db, db,
modelFile, modelFile,
Mode.Application, Mode.Application,
(usages) => this.modelDetailsPanel.setExternalApiUsages(usages), this.modelDetailsPanel.setState.bind(this.modelDetailsPanel),
); );
await view.openView(); 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);
},
}; };
} }

View File

@@ -70,8 +70,9 @@ export class DataExtensionsEditorView extends AbstractWebview<
private readonly databaseItem: DatabaseItem, private readonly databaseItem: DatabaseItem,
private readonly extensionPack: ExtensionPack, private readonly extensionPack: ExtensionPack,
private mode: Mode, private mode: Mode,
private readonly onExternalApiUsagesChanged: ( private readonly updateModelDetailsPanelState: (
externalApiUsages: ExternalApiUsage[], externalApiUsages: ExternalApiUsage[],
databaseItem: DatabaseItem,
) => void, ) => void,
) { ) {
super(ctx); super(ctx);
@@ -235,21 +236,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
protected async jumpToUsage( protected async jumpToUsage(
location: ResolvableLocationValue, location: ResolvableLocationValue,
): Promise<void> { ): Promise<void> {
try { await showResolvableLocation(location, this.databaseItem, this.app.logger);
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}`);
}
}
} }
protected async loadExistingModeledMethods(): Promise<void> { protected async loadExistingModeledMethods(): Promise<void> {
@@ -315,7 +302,10 @@ export class DataExtensionsEditorView extends AbstractWebview<
t: "setExternalApiUsages", t: "setExternalApiUsages",
externalApiUsages, externalApiUsages,
}); });
this.onExternalApiUsagesChanged(externalApiUsages); this.updateModelDetailsPanelState(
externalApiUsages,
this.databaseItem,
);
} catch (err) { } catch (err) {
void showAndLogExceptionWithTelemetry( void showAndLogExceptionWithTelemetry(
this.app.logger, this.app.logger,
@@ -503,7 +493,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
addedDatabase, addedDatabase,
modelFile, modelFile,
Mode.Framework, Mode.Framework,
this.onExternalApiUsagesChanged, this.updateModelDetailsPanelState,
); );
await view.openView(); await view.openView();
}); });

View File

@@ -7,12 +7,14 @@ import {
} from "vscode"; } from "vscode";
import { DisposableObject } from "../../common/disposable-object"; import { DisposableObject } from "../../common/disposable-object";
import { ExternalApiUsage, Usage } from "../external-api-usage"; import { ExternalApiUsage, Usage } from "../external-api-usage";
import { DatabaseItem } from "../../databases/local-databases";
export class ModelDetailsDataProvider export class ModelDetailsDataProvider
extends DisposableObject extends DisposableObject
implements TreeDataProvider<ModelDetailsTreeViewItem> implements TreeDataProvider<ModelDetailsTreeViewItem>
{ {
private externalApiUsages: ExternalApiUsage[] = []; private externalApiUsages: ExternalApiUsage[] = [];
private databaseItem: DatabaseItem | undefined = undefined;
private readonly onDidChangeTreeDataEmitter = this.push( private readonly onDidChangeTreeDataEmitter = this.push(
new EventEmitter<void>(), new EventEmitter<void>(),
@@ -22,8 +24,12 @@ export class ModelDetailsDataProvider
return this.onDidChangeTreeDataEmitter.event; return this.onDidChangeTreeDataEmitter.event;
} }
public setExternalApiUsages(externalApiUsages: ExternalApiUsage[]): void { public setState(
externalApiUsages: ExternalApiUsage[],
databaseItem: DatabaseItem,
): void {
this.externalApiUsages = externalApiUsages; this.externalApiUsages = externalApiUsages;
this.databaseItem = databaseItem;
this.onDidChangeTreeDataEmitter.fire(); this.onDidChangeTreeDataEmitter.fire();
} }
@@ -37,6 +43,11 @@ export class ModelDetailsDataProvider
return { return {
label: item.label, label: item.label,
collapsibleState: TreeItemCollapsibleState.None, collapsibleState: TreeItemCollapsibleState.None,
command: {
title: "Show usage",
command: "codeQLDataExtensionsEditor.jumpToUsageLocation",
arguments: [item, this.databaseItem],
},
}; };
} }
} }

View File

@@ -2,6 +2,7 @@ import { window } from "vscode";
import { DisposableObject } from "../../common/disposable-object"; import { DisposableObject } from "../../common/disposable-object";
import { ModelDetailsDataProvider } from "./model-details-data-provider"; import { ModelDetailsDataProvider } from "./model-details-data-provider";
import { ExternalApiUsage } from "../external-api-usage"; import { ExternalApiUsage } from "../external-api-usage";
import { DatabaseItem } from "../../databases/local-databases";
export class ModelDetailsPanel extends DisposableObject { export class ModelDetailsPanel extends DisposableObject {
private readonly dataProvider: ModelDetailsDataProvider; private readonly dataProvider: ModelDetailsDataProvider;
@@ -17,7 +18,10 @@ export class ModelDetailsPanel extends DisposableObject {
this.push(treeView); this.push(treeView);
} }
public setExternalApiUsages(externalApiUsages: ExternalApiUsage[]): void { public setState(
this.dataProvider.setExternalApiUsages(externalApiUsages); externalApiUsages: ExternalApiUsage[],
databaseItem: DatabaseItem,
): void {
this.dataProvider.setState(externalApiUsages, databaseItem);
} }
} }

View File

@@ -97,8 +97,19 @@ export function tryResolveLocation(
export async function showResolvableLocation( export async function showResolvableLocation(
loc: ResolvableLocationValue, loc: ResolvableLocationValue,
databaseItem: DatabaseItem, databaseItem: DatabaseItem,
logger: Logger,
): Promise<void> { ): Promise<void> {
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) { export async function showLocation(location?: Location) {
@@ -146,16 +157,6 @@ export async function jumpToLocation(
) { ) {
const databaseItem = databaseManager.findDatabaseItem(Uri.parse(databaseUri)); const databaseItem = databaseManager.findDatabaseItem(Uri.parse(databaseUri));
if (databaseItem !== undefined) { if (databaseItem !== undefined) {
try { await showResolvableLocation(loc, databaseItem, logger);
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)}`);
}
}
} }
} }

View File

@@ -44,7 +44,8 @@ describe("commands declared in package.json", () => {
command.match(/^codeQLQueryHistory\./) || command.match(/^codeQLQueryHistory\./) ||
command.match(/^codeQLAstViewer\./) || command.match(/^codeQLAstViewer\./) ||
command.match(/^codeQLEvalLogViewer\./) || command.match(/^codeQLEvalLogViewer\./) ||
command.match(/^codeQLTests\./) command.match(/^codeQLTests\./) ||
command.match(/^codeQLDataExtensionsEditor\./)
) { ) {
scopedCmds.add(command); scopedCmds.add(command);
expect(title).toBeDefined(); expect(title).toBeDefined();