Merge pull request #2976 from github/koesie10/methods-usage-panel-parent
Introduce separate tree item types in the methods usage panel
This commit is contained in:
@@ -27,7 +27,7 @@ export class MethodsUsageDataProvider
|
|||||||
private methods: readonly Method[] = [];
|
private methods: readonly Method[] = [];
|
||||||
// sortedMethods is a separate field so we can check if the methods have changed
|
// sortedMethods is a separate field so we can check if the methods have changed
|
||||||
// by reference, which is faster than checking if the methods have changed by value.
|
// by reference, which is faster than checking if the methods have changed by value.
|
||||||
private sortedMethods: readonly Method[] = [];
|
private sortedTreeItems: readonly MethodTreeViewItem[] = [];
|
||||||
private databaseItem: DatabaseItem | undefined = undefined;
|
private databaseItem: DatabaseItem | undefined = undefined;
|
||||||
private sourceLocationPrefix: string | undefined = undefined;
|
private sourceLocationPrefix: string | undefined = undefined;
|
||||||
private hideModeledMethods: boolean = INITIAL_HIDE_MODELED_METHODS_VALUE;
|
private hideModeledMethods: boolean = INITIAL_HIDE_MODELED_METHODS_VALUE;
|
||||||
@@ -72,7 +72,9 @@ export class MethodsUsageDataProvider
|
|||||||
this.modifiedMethodSignatures !== modifiedMethodSignatures
|
this.modifiedMethodSignatures !== modifiedMethodSignatures
|
||||||
) {
|
) {
|
||||||
this.methods = methods;
|
this.methods = methods;
|
||||||
this.sortedMethods = sortMethodsInGroups(methods, mode);
|
this.sortedTreeItems = createTreeItems(
|
||||||
|
sortMethodsInGroups(methods, mode),
|
||||||
|
);
|
||||||
this.databaseItem = databaseItem;
|
this.databaseItem = databaseItem;
|
||||||
this.sourceLocationPrefix =
|
this.sourceLocationPrefix =
|
||||||
await this.databaseItem.getSourceLocationPrefix(this.cliServer);
|
await this.databaseItem.getSourceLocationPrefix(this.cliServer);
|
||||||
@@ -86,7 +88,7 @@ export class MethodsUsageDataProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTreeItem(item: MethodsUsageTreeViewItem): TreeItem {
|
getTreeItem(item: MethodsUsageTreeViewItem): TreeItem {
|
||||||
if (isExternalApiUsage(item)) {
|
if (isMethodTreeViewItem(item)) {
|
||||||
return {
|
return {
|
||||||
label: `${item.packageName}.${item.typeName}.${item.methodName}${item.methodParameters}`,
|
label: `${item.packageName}.${item.typeName}.${item.methodName}${item.methodParameters}`,
|
||||||
collapsibleState: TreeItemCollapsibleState.Collapsed,
|
collapsibleState: TreeItemCollapsibleState.Collapsed,
|
||||||
@@ -94,7 +96,7 @@ export class MethodsUsageDataProvider
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const method = this.getParent(item);
|
const method = this.getParent(item);
|
||||||
if (!method || !isExternalApiUsage(method)) {
|
if (!method || !isMethodTreeViewItem(method)) {
|
||||||
throw new Error("Parent not found for tree item");
|
throw new Error("Parent not found for tree item");
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -144,12 +146,12 @@ export class MethodsUsageDataProvider
|
|||||||
getChildren(item?: MethodsUsageTreeViewItem): MethodsUsageTreeViewItem[] {
|
getChildren(item?: MethodsUsageTreeViewItem): MethodsUsageTreeViewItem[] {
|
||||||
if (item === undefined) {
|
if (item === undefined) {
|
||||||
if (this.hideModeledMethods) {
|
if (this.hideModeledMethods) {
|
||||||
return this.sortedMethods.filter((api) => !api.supported);
|
return this.sortedTreeItems.filter((api) => !api.supported);
|
||||||
} else {
|
} else {
|
||||||
return [...this.sortedMethods];
|
return [...this.sortedTreeItems];
|
||||||
}
|
}
|
||||||
} else if (isExternalApiUsage(item)) {
|
} else if (isMethodTreeViewItem(item)) {
|
||||||
return [...item.usages];
|
return item.children;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -158,29 +160,42 @@ export class MethodsUsageDataProvider
|
|||||||
getParent(
|
getParent(
|
||||||
item: MethodsUsageTreeViewItem,
|
item: MethodsUsageTreeViewItem,
|
||||||
): MethodsUsageTreeViewItem | undefined {
|
): MethodsUsageTreeViewItem | undefined {
|
||||||
if (isExternalApiUsage(item)) {
|
if (isMethodTreeViewItem(item)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} else {
|
||||||
return this.methods.find((e) => e.usages.includes(item));
|
return item.parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public resolveCanonicalUsage(usage: Usage): Usage | undefined {
|
public resolveUsageTreeViewItem(
|
||||||
for (const method of this.methods) {
|
methodSignature: string,
|
||||||
for (const u of method.usages) {
|
usage: Usage,
|
||||||
if (usagesAreEqual(u, usage)) {
|
): UsageTreeViewItem | undefined {
|
||||||
return u;
|
const method = this.sortedTreeItems.find(
|
||||||
}
|
(m) => m.signature === methodSignature,
|
||||||
}
|
);
|
||||||
|
if (!method) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
|
return method.children.find((u) => usagesAreEqual(u, usage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MethodsUsageTreeViewItem = Method | Usage;
|
type MethodTreeViewItem = Method & {
|
||||||
|
children: UsageTreeViewItem[];
|
||||||
|
};
|
||||||
|
|
||||||
function isExternalApiUsage(item: MethodsUsageTreeViewItem): item is Method {
|
type UsageTreeViewItem = Usage & {
|
||||||
return (item as any).usages !== undefined;
|
parent: MethodTreeViewItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MethodsUsageTreeViewItem = MethodTreeViewItem | UsageTreeViewItem;
|
||||||
|
|
||||||
|
function isMethodTreeViewItem(
|
||||||
|
item: MethodsUsageTreeViewItem,
|
||||||
|
): item is MethodTreeViewItem {
|
||||||
|
return "children" in item && "usages" in item;
|
||||||
}
|
}
|
||||||
|
|
||||||
function usagesAreEqual(u1: Usage, u2: Usage): boolean {
|
function usagesAreEqual(u1: Usage, u2: Usage): boolean {
|
||||||
@@ -206,3 +221,20 @@ function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] {
|
|||||||
return sortMethods(group);
|
return sortMethods(group);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTreeItems(methods: readonly Method[]): MethodTreeViewItem[] {
|
||||||
|
return methods.map((method) => {
|
||||||
|
const newMethod: MethodTreeViewItem = {
|
||||||
|
...method,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
newMethod.children = method.usages.map((usage) => ({
|
||||||
|
...usage,
|
||||||
|
// This needs to be a reference to the parent method, not a copy of it.
|
||||||
|
parent: newMethod,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return newMethod;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,10 +56,16 @@ export class MethodsUsagePanel extends DisposableObject {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async revealItem(usage: Usage): Promise<void> {
|
public async revealItem(
|
||||||
const canonicalUsage = this.dataProvider.resolveCanonicalUsage(usage);
|
methodSignature: string,
|
||||||
if (canonicalUsage !== undefined) {
|
usage: Usage,
|
||||||
await this.treeView.reveal(canonicalUsage);
|
): Promise<void> {
|
||||||
|
const usageTreeViewItem = this.dataProvider.resolveUsageTreeViewItem(
|
||||||
|
methodSignature,
|
||||||
|
usage,
|
||||||
|
);
|
||||||
|
if (usageTreeViewItem !== undefined) {
|
||||||
|
await this.treeView.reveal(usageTreeViewItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
method: Method,
|
method: Method,
|
||||||
usage: Usage,
|
usage: Usage,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.methodsUsagePanel.revealItem(usage);
|
await this.methodsUsagePanel.revealItem(method.signature, usage);
|
||||||
await this.methodModelingPanel.setMethod(databaseItem, method);
|
await this.methodModelingPanel.setMethod(databaseItem, method);
|
||||||
await showResolvableLocation(usage.url, databaseItem, this.app.logger);
|
await showResolvableLocation(usage.url, databaseItem, this.app.logger);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import {
|
|||||||
CallClassification,
|
CallClassification,
|
||||||
Method,
|
Method,
|
||||||
} from "../../../../../src/model-editor/method";
|
} from "../../../../../src/model-editor/method";
|
||||||
import { MethodsUsageDataProvider } from "../../../../../src/model-editor/methods-usage/methods-usage-data-provider";
|
import {
|
||||||
|
MethodsUsageDataProvider,
|
||||||
|
MethodsUsageTreeViewItem,
|
||||||
|
} from "../../../../../src/model-editor/methods-usage/methods-usage-data-provider";
|
||||||
import { DatabaseItem } from "../../../../../src/databases/local-databases";
|
import { DatabaseItem } from "../../../../../src/databases/local-databases";
|
||||||
import {
|
import {
|
||||||
createMethod,
|
createMethod,
|
||||||
@@ -242,13 +245,23 @@ describe("MethodsUsageDataProvider", () => {
|
|||||||
|
|
||||||
const usage = createUsage({});
|
const usage = createUsage({});
|
||||||
|
|
||||||
|
const methodTreeItem: MethodsUsageTreeViewItem = {
|
||||||
|
...supportedMethod,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const usageTreeItem: MethodsUsageTreeViewItem = {
|
||||||
|
...usage,
|
||||||
|
parent: methodTreeItem,
|
||||||
|
};
|
||||||
|
methodTreeItem.children = [usageTreeItem];
|
||||||
|
|
||||||
it("should return [] if item is a usage", async () => {
|
it("should return [] if item is a usage", async () => {
|
||||||
expect(dataProvider.getChildren(usage)).toEqual([]);
|
expect(dataProvider.getChildren(usageTreeItem)).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return usages if item is external api usage", async () => {
|
it("should return usages if item is method", async () => {
|
||||||
const method = createMethod({ usages: [usage] });
|
expect(dataProvider.getChildren(methodTreeItem)).toEqual([usageTreeItem]);
|
||||||
expect(dataProvider.getChildren(method)).toEqual([usage]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show all methods if hideModeledMethods is false and looking at the root", async () => {
|
it("should show all methods if hideModeledMethods is false and looking at the root", async () => {
|
||||||
|
|||||||
@@ -68,11 +68,10 @@ describe("MethodsUsagePanel", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should reveal the correct item in the tree view", async () => {
|
it("should reveal the correct item in the tree view", async () => {
|
||||||
const methods = [
|
const method = createMethod({
|
||||||
createMethod({
|
usages: [usage],
|
||||||
usages: [usage],
|
});
|
||||||
}),
|
const methods = [method];
|
||||||
];
|
|
||||||
|
|
||||||
const panel = new MethodsUsagePanel(modelingStore, mockCliServer);
|
const panel = new MethodsUsagePanel(modelingStore, mockCliServer);
|
||||||
await panel.setState(
|
await panel.setState(
|
||||||
@@ -84,13 +83,16 @@ describe("MethodsUsagePanel", () => {
|
|||||||
modifiedMethodSignatures,
|
modifiedMethodSignatures,
|
||||||
);
|
);
|
||||||
|
|
||||||
await panel.revealItem(usage);
|
await panel.revealItem(method.signature, usage);
|
||||||
|
|
||||||
expect(mockTreeView.reveal).toHaveBeenCalledWith(usage);
|
expect(mockTreeView.reveal).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining(usage),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should do nothing if usage cannot be found", async () => {
|
it("should do nothing if usage cannot be found", async () => {
|
||||||
const methods = [createMethod({})];
|
const method = createMethod({});
|
||||||
|
const methods = [method];
|
||||||
const panel = new MethodsUsagePanel(modelingStore, mockCliServer);
|
const panel = new MethodsUsagePanel(modelingStore, mockCliServer);
|
||||||
await panel.setState(
|
await panel.setState(
|
||||||
methods,
|
methods,
|
||||||
@@ -101,7 +103,7 @@ describe("MethodsUsagePanel", () => {
|
|||||||
modifiedMethodSignatures,
|
modifiedMethodSignatures,
|
||||||
);
|
);
|
||||||
|
|
||||||
await panel.revealItem(usage);
|
await panel.revealItem(method.signature, usage);
|
||||||
|
|
||||||
expect(mockTreeView.reveal).not.toHaveBeenCalled();
|
expect(mockTreeView.reveal).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user