Skip to content

Commit d290b56

Browse files
Display custom text message when there are no queries found
1 parent cf49d5d commit d290b56

5 files changed

Lines changed: 97 additions & 36 deletions

File tree

extensions/ql-vscode/src/local-queries/local-queries.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ import { SkeletonQueryWizard } from "../skeleton-query-wizard";
4848
import { LocalQueryRun } from "./local-query-run";
4949
import { createMultiSelectionCommand } from "../common/vscode/selection-commands";
5050
import { findLanguage } from "../codeql-cli/query-language";
51-
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
51+
import {
52+
QueryTreeQueryItem,
53+
QueryTreeViewItem,
54+
} from "../queries-panel/query-tree-view-item";
5255

5356
interface DatabaseQuickPickItem extends QuickPickItem {
5457
databaseItem: DatabaseItem;
@@ -130,16 +133,20 @@ export class LocalQueries extends DisposableObject {
130133
private async runQueryFromQueriesPanel(
131134
queryTreeViewItem: QueryTreeViewItem,
132135
): Promise<void> {
133-
await this.runQuery(Uri.file(queryTreeViewItem.path));
136+
if (queryTreeViewItem instanceof QueryTreeQueryItem) {
137+
await this.runQuery(Uri.file(queryTreeViewItem.path));
138+
}
134139
}
135140

136141
private async runQueriesFromQueriesPanel(
137142
queryTreeViewItem: QueryTreeViewItem,
138143
): Promise<void> {
139-
const uris = queryTreeViewItem.children.map((child) =>
140-
Uri.file(child.path),
141-
);
142-
await this.runQueries(uris);
144+
if (queryTreeViewItem instanceof QueryTreeQueryItem) {
145+
const uris = queryTreeViewItem.children.map((child) =>
146+
Uri.file(child.path),
147+
);
148+
await this.runQueries(uris);
149+
}
143150
}
144151

145152
private async runQuery(uri: Uri | undefined): Promise<void> {

extensions/ql-vscode/src/queries-panel/query-tree-data-provider.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { Event, EventEmitter, TreeDataProvider, TreeItem } from "vscode";
2-
import { QueryTreeViewItem } from "./query-tree-view-item";
2+
import {
3+
QueryTreeQueryItem,
4+
QueryTreeTextItem,
5+
QueryTreeViewItem,
6+
} from "./query-tree-view-item";
37
import { DisposableObject } from "../common/disposable-object";
48
import { FileTreeNode } from "../common/file-tree-nodes";
59

@@ -37,21 +41,30 @@ export class QueryTreeDataProvider
3741
const queryTree = this.queryDiscoverer.buildQueryTree();
3842
if (queryTree === undefined) {
3943
return [];
44+
} else if (queryTree.length === 0) {
45+
return [this.noQueriesTreeViewItem()];
46+
} else {
47+
return queryTree.map(this.convertFileTreeNode.bind(this));
4048
}
41-
return queryTree.map(this.convertFileTreeNode.bind(this));
4249
}
4350

4451
private convertFileTreeNode(
4552
fileTreeDirectory: FileTreeNode<string>,
46-
): QueryTreeViewItem {
47-
return new QueryTreeViewItem(
53+
): QueryTreeQueryItem {
54+
return new QueryTreeQueryItem(
4855
fileTreeDirectory.name,
4956
fileTreeDirectory.path,
5057
fileTreeDirectory.data,
5158
fileTreeDirectory.children.map(this.convertFileTreeNode.bind(this)),
5259
);
5360
}
5461

62+
private noQueriesTreeViewItem(): QueryTreeViewItem {
63+
return new QueryTreeTextItem(
64+
"This workspace doesn't contain any CodeQL queries at the moment.",
65+
);
66+
}
67+
5568
/**
5669
* Returns the UI presentation of the element that gets displayed in the view.
5770
* @param item The item to represent.
@@ -70,8 +83,10 @@ export class QueryTreeDataProvider
7083
if (!item) {
7184
// We're at the root.
7285
return this.queryTreeItems;
73-
} else {
86+
} else if (item instanceof QueryTreeQueryItem) {
7487
return item.children;
88+
} else {
89+
return [];
7590
}
7691
}
7792
}

extensions/ql-vscode/src/queries-panel/query-tree-view-item.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import * as vscode from "vscode";
22

3-
export class QueryTreeViewItem extends vscode.TreeItem {
3+
export abstract class QueryTreeViewItem extends vscode.TreeItem {
4+
protected constructor(name: string) {
5+
super(name);
6+
}
7+
}
8+
9+
export class QueryTreeQueryItem extends QueryTreeViewItem {
410
constructor(
511
name: string,
612
public readonly path: string,
713
language: string | undefined,
8-
public readonly children: QueryTreeViewItem[],
14+
public readonly children: QueryTreeQueryItem[],
915
) {
1016
super(name);
1117
this.tooltip = path;
@@ -24,3 +30,9 @@ export class QueryTreeViewItem extends vscode.TreeItem {
2430
}
2531
}
2632
}
33+
34+
export class QueryTreeTextItem extends QueryTreeViewItem {
35+
constructor(text: string) {
36+
super(text);
37+
}
38+
}

extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ import {
7676
showAndLogInformationMessage,
7777
showAndLogWarningMessage,
7878
} from "../common/logging";
79-
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
79+
import {
80+
QueryTreeQueryItem,
81+
QueryTreeViewItem,
82+
} from "../queries-panel/query-tree-view-item";
8083

8184
const maxRetryCount = 3;
8285

@@ -191,7 +194,11 @@ export class VariantAnalysisManager
191194
private async runVariantAnalysisFromQueriesPanel(
192195
queryTreeViewItem: QueryTreeViewItem,
193196
): Promise<void> {
194-
await this.runVariantAnalysisFromCommand(Uri.file(queryTreeViewItem.path));
197+
if (queryTreeViewItem instanceof QueryTreeQueryItem) {
198+
await this.runVariantAnalysisFromCommand(
199+
Uri.file(queryTreeViewItem.path),
200+
);
201+
}
195202
}
196203

197204
public async runVariantAnalysis(

extensions/ql-vscode/test/vscode-tests/minimal-workspace/queries-panel/query-tree-data-provider.test.ts

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,35 @@ import {
44
FileTreeLeaf,
55
} from "../../../../src/common/file-tree-nodes";
66
import { QueryTreeDataProvider } from "../../../../src/queries-panel/query-tree-data-provider";
7+
import {
8+
QueryTreeQueryItem,
9+
QueryTreeTextItem,
10+
} from "../../../../src/queries-panel/query-tree-view-item";
711

812
describe("QueryTreeDataProvider", () => {
913
describe("getChildren", () => {
10-
it("returns no children when there are no queries", async () => {
14+
it("returns empty array when discovery has not yet happened", async () => {
1115
const dataProvider = new QueryTreeDataProvider({
12-
buildQueryTree: () => [],
16+
buildQueryTree: () => undefined,
1317
onDidChangeQueries: jest.fn(),
1418
});
1519

1620
expect(dataProvider.getChildren()).toEqual([]);
1721
});
1822

23+
it("returns a explanatory message when there are no queries", async () => {
24+
const dataProvider = new QueryTreeDataProvider({
25+
buildQueryTree: () => [],
26+
onDidChangeQueries: jest.fn(),
27+
});
28+
29+
expect(dataProvider.getChildren()).toEqual([
30+
new QueryTreeTextItem(
31+
"This workspace doesn't contain any CodeQL queries at the moment.",
32+
),
33+
]);
34+
});
35+
1936
it("converts FileTreeNode to QueryTreeViewItem", async () => {
2037
const dataProvider = new QueryTreeDataProvider({
2138
buildQueryTree: () => [
@@ -27,7 +44,7 @@ describe("QueryTreeDataProvider", () => {
2744
"javascript",
2845
),
2946
new FileTreeLeaf<string>(
30-
"dir1/dir2/file1",
47+
"dir1/dir2/file2",
3148
"file2",
3249
"javascript",
3350
),
@@ -40,24 +57,27 @@ describe("QueryTreeDataProvider", () => {
4057
onDidChangeQueries: jest.fn(),
4158
});
4259

43-
expect(dataProvider.getChildren().length).toEqual(2);
44-
45-
expect(dataProvider.getChildren()[0].label).toEqual("dir1");
46-
expect(dataProvider.getChildren()[0].children.length).toEqual(1);
47-
expect(dataProvider.getChildren()[0].children[0].label).toEqual("dir2");
48-
expect(dataProvider.getChildren()[0].children[0].children.length).toEqual(
49-
2,
50-
);
51-
expect(
52-
dataProvider.getChildren()[0].children[0].children[0].label,
53-
).toEqual("file1");
54-
expect(
55-
dataProvider.getChildren()[0].children[0].children[1].label,
56-
).toEqual("file2");
57-
58-
expect(dataProvider.getChildren()[1].label).toEqual("dir3");
59-
expect(dataProvider.getChildren()[1].children.length).toEqual(1);
60-
expect(dataProvider.getChildren()[1].children[0].label).toEqual("file3");
60+
expect(dataProvider.getChildren()).toEqual([
61+
new QueryTreeQueryItem("dir1", "dir1", undefined, [
62+
new QueryTreeQueryItem("dir2", "dir1/dir2", undefined, [
63+
new QueryTreeQueryItem(
64+
"file1",
65+
"dir1/dir2/file1",
66+
"javascript",
67+
[],
68+
),
69+
new QueryTreeQueryItem(
70+
"file2",
71+
"dir1/dir2/file2",
72+
"javascript",
73+
[],
74+
),
75+
]),
76+
]),
77+
new QueryTreeQueryItem("dir3", "dir3", undefined, [
78+
new QueryTreeQueryItem("file3", "dir3/file3", "javascript", []),
79+
]),
80+
]);
6181
});
6282
});
6383

0 commit comments

Comments
 (0)