Merge pull request #2688 from github/robertbrignull/data-view-jump

Implementing expanding/selecting item in details view when clicking "view"
This commit is contained in:
Robert
2023-08-14 10:15:17 +01:00
committed by GitHub
7 changed files with 61 additions and 25 deletions

View File

@@ -59,7 +59,6 @@ export type ExplorerSelectionCommandFunction<Item> = (
type BuiltInVsCodeCommands = {
// The codeQLDatabases.focus command is provided by VS Code because we've registered the custom view
"codeQLDatabases.focus": () => Promise<void>;
"codeQLModelDetails.focus": () => Promise<void>;
"markdown.showPreviewToSide": (uri: Uri) => Promise<void>;
"workbench.action.closeActiveEditor": () => Promise<void>;
revealFileInOS: (uri: Uri) => Promise<void>;

View File

@@ -17,7 +17,10 @@ import {
} from "../variant-analysis/shared/variant-analysis-filter-sort";
import { ErrorLike } from "../common/errors";
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
import {
ExternalApiUsage,
Usage,
} from "../data-extensions-editor/external-api-usage";
import { ModeledMethod } from "../data-extensions-editor/modeled-method";
import { DataExtensionEditorViewState } from "../data-extensions-editor/shared/view-state";
import { Mode } from "../data-extensions-editor/shared/mode";
@@ -523,7 +526,7 @@ interface SwitchModeMessage {
interface JumpToUsageMessage {
t: "jumpToUsage";
location: ResolvableLocationValue;
usage: Usage;
}
interface OpenDatabaseMessage {

View File

@@ -148,6 +148,7 @@ export class DataExtensionsEditorModule extends DisposableObject {
modelFile,
Mode.Application,
this.modelDetailsPanel.setState.bind(this.modelDetailsPanel),
this.modelDetailsPanel.revealItem.bind(this.modelDetailsPanel),
);
await view.openView();
},

View File

@@ -25,12 +25,11 @@ import { asError, assertNever, getErrorMessage } from "../common/helpers-pure";
import { generateFlowModel } from "./generate-flow-model";
import { promptImportGithubDatabase } from "../databases/database-fetcher";
import { App } from "../common/app";
import { ResolvableLocationValue } from "../common/bqrs-cli-types";
import { showResolvableLocation } from "../databases/local-databases/locations";
import { decodeBqrsToExternalApiUsages } from "./bqrs";
import { redactableError } from "../common/errors";
import { readQueryResults, runQuery } from "./external-api-usage-query";
import { ExternalApiUsage } from "./external-api-usage";
import { ExternalApiUsage, Usage } from "./external-api-usage";
import { ModeledMethod } from "./modeled-method";
import { ExtensionPack } from "./shared/extension-pack";
import {
@@ -66,6 +65,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
externalApiUsages: ExternalApiUsage[],
databaseItem: DatabaseItem,
) => Promise<void>,
private readonly revealItemInDetailsPanel: (usage: Usage) => Promise<void>,
) {
super(ctx);
@@ -146,7 +146,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
break;
case "jumpToUsage":
await this.handleJumpToUsage(msg.location);
await this.handleJumpToUsage(msg.usage);
break;
case "saveModeledMethods":
@@ -216,22 +216,11 @@ export class DataExtensionsEditorView extends AbstractWebview<
});
}
protected async handleJumpToUsage(location: ResolvableLocationValue) {
protected async handleJumpToUsage(usage: Usage) {
if (showModelDetailsView()) {
await this.openModelDetailsView();
} else {
await this.jumpToUsage(location);
await this.revealItemInDetailsPanel(usage);
}
}
protected async openModelDetailsView() {
await this.app.commands.execute("codeQLModelDetails.focus");
}
protected async jumpToUsage(
location: ResolvableLocationValue,
): Promise<void> {
await showResolvableLocation(location, this.databaseItem, this.app.logger);
await showResolvableLocation(usage.url, this.databaseItem, this.app.logger);
}
protected async loadExistingModeledMethods(): Promise<void> {
@@ -419,6 +408,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
modelFile,
Mode.Framework,
this.updateModelDetailsPanelState,
this.revealItemInDetailsPanel,
);
await view.openView();
});

View File

@@ -88,12 +88,45 @@ export class ModelDetailsDataProvider
return [];
}
}
getParent(
item: ModelDetailsTreeViewItem,
): ModelDetailsTreeViewItem | undefined {
if (isExternalApiUsage(item)) {
return undefined;
} else {
return this.externalApiUsages.find((e) => e.usages.includes(item));
}
}
public resolveCanonicalUsage(usage: Usage): Usage | undefined {
for (const externalApiUsage of this.externalApiUsages) {
for (const u of externalApiUsage.usages) {
if (usagesAreEqual(u, usage)) {
return u;
}
}
}
return undefined;
}
}
type ModelDetailsTreeViewItem = ExternalApiUsage | Usage;
export type ModelDetailsTreeViewItem = ExternalApiUsage | Usage;
function isExternalApiUsage(
item: ModelDetailsTreeViewItem,
): item is ExternalApiUsage {
return (item as any).usages !== undefined;
}
function usagesAreEqual(u1: Usage, u2: Usage): boolean {
return (
u1.label === u2.label &&
u1.classification === u2.classification &&
u1.url.uri === u2.url.uri &&
u1.url.startLine === u2.url.startLine &&
u1.url.startColumn === u2.url.startColumn &&
u1.url.endLine === u2.url.endLine &&
u1.url.endColumn === u2.url.endColumn
);
}

View File

@@ -1,13 +1,16 @@
import { TreeView, window } from "vscode";
import { DisposableObject } from "../../common/disposable-object";
import { ModelDetailsDataProvider } from "./model-details-data-provider";
import { DatabaseItem } from "../../databases/local-databases";
import {
ModelDetailsDataProvider,
ModelDetailsTreeViewItem,
} from "./model-details-data-provider";
import { ExternalApiUsage, Usage } from "../external-api-usage";
import { DatabaseItem } from "../../databases/local-databases";
import { CodeQLCliServer } from "../../codeql-cli/cli";
export class ModelDetailsPanel extends DisposableObject {
private readonly dataProvider: ModelDetailsDataProvider;
private readonly treeView: TreeView<ExternalApiUsage | Usage>;
private readonly treeView: TreeView<ModelDetailsTreeViewItem>;
public constructor(cliServer: CodeQLCliServer) {
super();
@@ -30,4 +33,11 @@ export class ModelDetailsPanel extends DisposableObject {
tooltip: "Number of external APIs",
};
}
public async revealItem(usage: Usage): Promise<void> {
const canonicalUsage = this.dataProvider.resolveCanonicalUsage(usage);
if (canonicalUsage !== undefined) {
await this.treeView.reveal(canonicalUsage);
}
}
}

View File

@@ -326,7 +326,7 @@ function sendJumpToUsageMessage(externalApiUsage: ExternalApiUsage) {
vscode.postMessage({
t: "jumpToUsage",
// In framework mode, the first and only usage is the definition of the method
location: externalApiUsage.usages[0].url,
usage: externalApiUsage.usages[0],
});
}