Keep reference to original items in usage data provider

This changes the usage data provider tree items to keep a reference to
the method and usage instead of only including their properties in the
tree item. This makes it easier to find the original method and usage
when revealing an item in the tree. It also removes the `getParent` call
in `getTreeItem`.

The main reason for this fix is to ensure
`codeQLModelEditor.jumpToMethod` gets the correct `usage` argument.
It received the tree item before, but now we can actually pass the
usage that was clicked on.
This commit is contained in:
Koen Vlaswinkel
2023-10-17 16:03:57 +02:00
parent a7f8019bf4
commit a852f16eb1
3 changed files with 33 additions and 23 deletions

View File

@@ -89,26 +89,26 @@ export class MethodsUsageDataProvider
getTreeItem(item: MethodsUsageTreeViewItem): TreeItem {
if (isMethodTreeViewItem(item)) {
const { method } = item;
return {
label: `${item.packageName}.${item.typeName}.${item.methodName}${item.methodParameters}`,
label: `${method.packageName}.${method.typeName}.${method.methodName}${method.methodParameters}`,
collapsibleState: TreeItemCollapsibleState.Collapsed,
iconPath: this.getModelingStatusIcon(item),
iconPath: this.getModelingStatusIcon(method),
};
} else {
const method = this.getParent(item);
if (!method || !isMethodTreeViewItem(method)) {
throw new Error("Parent not found for tree item");
}
const { method, usage } = item;
return {
label: item.label,
description: `${this.relativePathWithinDatabase(item.url.uri)} [${
item.url.startLine
}, ${item.url.endLine}]`,
label: usage.label,
description: `${this.relativePathWithinDatabase(usage.url.uri)} [${
usage.url.startLine
}, ${usage.url.endLine}]`,
collapsibleState: TreeItemCollapsibleState.None,
command: {
title: "Show usage",
command: "codeQLModelEditor.jumpToMethod",
arguments: [method, item, this.databaseItem],
arguments: [method, usage, this.databaseItem],
},
};
}
@@ -146,7 +146,7 @@ export class MethodsUsageDataProvider
getChildren(item?: MethodsUsageTreeViewItem): MethodsUsageTreeViewItem[] {
if (item === undefined) {
if (this.hideModeledMethods) {
return this.sortedTreeItems.filter((api) => !api.supported);
return this.sortedTreeItems.filter((api) => !api.method.supported);
} else {
return [...this.sortedTreeItems];
}
@@ -172,21 +172,24 @@ export class MethodsUsageDataProvider
usage: Usage,
): UsageTreeViewItem | undefined {
const method = this.sortedTreeItems.find(
(m) => m.signature === methodSignature,
(m) => m.method.signature === methodSignature,
);
if (!method) {
return undefined;
}
return method.children.find((u) => usagesAreEqual(u, usage));
return method.children.find((u) => usagesAreEqual(u.usage, usage));
}
}
type MethodTreeViewItem = Method & {
type MethodTreeViewItem = {
method: Method;
children: UsageTreeViewItem[];
};
type UsageTreeViewItem = Usage & {
type UsageTreeViewItem = {
method: Method;
usage: Usage;
parent: MethodTreeViewItem;
};
@@ -195,7 +198,7 @@ export type MethodsUsageTreeViewItem = MethodTreeViewItem | UsageTreeViewItem;
function isMethodTreeViewItem(
item: MethodsUsageTreeViewItem,
): item is MethodTreeViewItem {
return "children" in item && "usages" in item;
return "children" in item && "method" in item;
}
function usagesAreEqual(u1: Usage, u2: Usage): boolean {
@@ -225,12 +228,13 @@ function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] {
function createTreeItems(methods: readonly Method[]): MethodTreeViewItem[] {
return methods.map((method) => {
const newMethod: MethodTreeViewItem = {
...method,
method,
children: [],
};
newMethod.children = method.usages.map((usage) => ({
...usage,
method,
usage,
// This needs to be a reference to the parent method, not a copy of it.
parent: newMethod,
}));

View File

@@ -246,12 +246,13 @@ describe("MethodsUsageDataProvider", () => {
const usage = createUsage({});
const methodTreeItem: MethodsUsageTreeViewItem = {
...supportedMethod,
method: supportedMethod,
children: [],
};
const usageTreeItem: MethodsUsageTreeViewItem = {
...usage,
method: supportedMethod,
usage,
parent: methodTreeItem,
};
methodTreeItem.children = [usageTreeItem];
@@ -383,7 +384,9 @@ describe("MethodsUsageDataProvider", () => {
expect(
dataProvider
.getChildren()
.map((item) => (item as Method).signature),
.map(
(item) => (item as MethodsUsageTreeViewItem).method.signature,
),
).toEqual(["b.a.C.d()", "b.a.C.b()", "b.a.C.a()", "a.b.C.d()"]);
// reasoning for sort order:
// b.a.C.d() has more usages than b.a.C.b()

View File

@@ -86,7 +86,10 @@ describe("MethodsUsagePanel", () => {
await panel.revealItem(method.signature, usage);
expect(mockTreeView.reveal).toHaveBeenCalledWith(
expect.objectContaining(usage),
expect.objectContaining({
method,
usage,
}),
);
});