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