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:
@@ -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 = {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user