Skip to content

Commit c668b39

Browse files
committed
Add language filter panel.
1 parent 4ee86c1 commit c668b39

7 files changed

Lines changed: 173 additions & 0 deletions

File tree

extensions/ql-vscode/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,10 @@
581581
"command": "codeQL.copyVersion",
582582
"title": "CodeQL: Copy Version Information"
583583
},
584+
{
585+
"command": "codeQLLanguageSelection.setSelectedItem",
586+
"title": "Select"
587+
},
584588
{
585589
"command": "codeQLQueries.runLocalQueryFromQueriesPanel",
586590
"title": "Run local query",
@@ -1163,6 +1167,11 @@
11631167
"when": "view == codeQLVariantAnalysisRepositories && viewItem =~ /canImportCodeSearch/",
11641168
"group": "2_qlContextMenu@1"
11651169
},
1170+
{
1171+
"command": "codeQLLanguageSelection.setSelectedItem",
1172+
"when": "view == codeQLLanguageSelection && viewItem =~ /canBeSelected/",
1173+
"group": "inline"
1174+
},
11661175
{
11671176
"command": "codeQLDatabases.setCurrentDatabase",
11681177
"group": "inline",
@@ -1511,6 +1520,10 @@
15111520
{
15121521
"command": "codeQL.openModelEditor"
15131522
},
1523+
{
1524+
"command": "codeQLLanguageSelection.setSelectedItem",
1525+
"when": "false"
1526+
},
15141527
{
15151528
"command": "codeQLQueries.runLocalQueryContextMenu",
15161529
"when": "false"
@@ -1977,6 +1990,11 @@
19771990
},
19781991
"views": {
19791992
"ql-container": [
1993+
{
1994+
"id": "codeQLLanguageSelection",
1995+
"name": "Language",
1996+
"when": "config.codeQL.canary && config.codeQL.showLanguageFilter"
1997+
},
19801998
{
19811999
"id": "codeQLDatabases",
19822000
"name": "Databases"

extensions/ql-vscode/src/common/commands.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
} from "../variant-analysis/shared/variant-analysis";
1313
import type { QLDebugConfiguration } from "../debugger/debug-configuration";
1414
import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
15+
import type { LanguageSelectionTreeViewItem } from "../language-selection-panel/language-selection-data-provider";
1516

1617
// A command function matching the signature that VS Code calls when
1718
// a command is invoked from a context menu on a TreeView with
@@ -198,6 +199,13 @@ export type QueryHistoryCommands = {
198199
"codeQL.exportSelectedVariantAnalysisResults": () => Promise<void>;
199200
};
200201

202+
// Commands user for the language selector panel
203+
export type LanguageSelectionCommands = {
204+
"codeQLLanguageSelection.setSelectedItem": (
205+
item: LanguageSelectionTreeViewItem,
206+
) => Promise<void>;
207+
};
208+
201209
// Commands used for the local databases panel
202210
export type LocalDatabasesCommands = {
203211
// Command palette commands

extensions/ql-vscode/src/extension.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ import { NewQueryRunner, QueryRunner, QueryServerClient } from "./query-server";
136136
import { QueriesModule } from "./queries-panel/queries-module";
137137
import { OpenReferencedFileCodeLensProvider } from "./local-queries/open-referenced-file-code-lens-provider";
138138
import { LanguageContextStore } from "./language-context-store";
139+
import { LanguageSelectionPanel } from "./language-selection-panel/language-selection-panel";
139140

140141
/**
141142
* extension.ts
@@ -779,6 +780,10 @@ async function activateWithInstalledDistribution(
779780
void extLogger.log("Initializing language context.");
780781
const languageContext = new LanguageContextStore(app);
781782

783+
void extLogger.log("Initializing language selector.");
784+
const languageSelectionPanel = new LanguageSelectionPanel(languageContext);
785+
ctx.subscriptions.push(languageSelectionPanel);
786+
782787
void extLogger.log("Initializing database panel.");
783788
const databaseUI = new DatabaseUI(
784789
app,
@@ -1016,6 +1021,7 @@ async function activateWithInstalledDistribution(
10161021
...getPackagingCommands({
10171022
cliServer,
10181023
}),
1024+
...languageSelectionPanel.getCommands(),
10191025
...modelEditorModule.getCommands(),
10201026
...evalLogViewer.getCommands(),
10211027
...summaryLanguageSupport.getCommands(),

extensions/ql-vscode/src/language-context-store.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,11 @@ export class LanguageContextStore extends DisposableObject {
4646
public shouldInclude(language: QueryLanguage | undefined): boolean {
4747
return this.languageFilter === "All" || this.languageFilter === language;
4848
}
49+
50+
public selectedLanguage(language: QueryLanguage | undefined): boolean {
51+
return (
52+
(this.languageFilter === "All" && language === undefined) ||
53+
this.languageFilter === language
54+
);
55+
}
4956
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { DisposableObject } from "../common/disposable-object";
2+
import { LanguageContextStore } from "../language-context-store";
3+
import {
4+
Event,
5+
EventEmitter,
6+
ThemeIcon,
7+
TreeDataProvider,
8+
TreeItem,
9+
} from "vscode";
10+
import {
11+
QueryLanguage,
12+
getLanguageDisplayName,
13+
} from "../common/query-language";
14+
15+
const ALL_LANGUAGE_SELECTION_OPTIONS = [
16+
undefined, // All langauges
17+
QueryLanguage.Cpp,
18+
QueryLanguage.CSharp,
19+
QueryLanguage.Go,
20+
QueryLanguage.Java,
21+
QueryLanguage.Javascript,
22+
QueryLanguage.Python,
23+
QueryLanguage.Ruby,
24+
QueryLanguage.Swift,
25+
];
26+
27+
// A tree view items consisting of of a language (or undefined for all languages
28+
// and a boolean indicating whether it is selected or not.
29+
export class LanguageSelectionTreeViewItem extends TreeItem {
30+
constructor(
31+
public readonly language: QueryLanguage | undefined,
32+
public readonly selected: boolean = false,
33+
) {
34+
const label = language ? getLanguageDisplayName(language) : "All languages";
35+
super(label);
36+
37+
this.iconPath = selected ? new ThemeIcon("check") : undefined;
38+
this.contextValue = selected ? undefined : "canBeSelected";
39+
}
40+
}
41+
42+
export class LanguageSelectionTreeDataProvider
43+
extends DisposableObject
44+
implements TreeDataProvider<LanguageSelectionTreeViewItem>
45+
{
46+
private treeItems: LanguageSelectionTreeViewItem[];
47+
private readonly onDidChangeTreeDataEmitter = this.push(
48+
new EventEmitter<void>(),
49+
);
50+
51+
public constructor(private readonly languageContext: LanguageContextStore) {
52+
super();
53+
54+
this.treeItems = this.createTree(languageContext);
55+
56+
// If the language context changes, we need to update the tree.
57+
this.push(
58+
this.languageContext.onLanguageContextChanged(() => {
59+
this.treeItems = this.createTree(languageContext);
60+
this.onDidChangeTreeDataEmitter.fire();
61+
}),
62+
);
63+
}
64+
65+
public get onDidChangeTreeData(): Event<void> {
66+
return this.onDidChangeTreeDataEmitter.event;
67+
}
68+
69+
public getTreeItem(item: LanguageSelectionTreeViewItem): TreeItem {
70+
return item;
71+
}
72+
73+
public getChildren(
74+
item?: LanguageSelectionTreeViewItem,
75+
): LanguageSelectionTreeViewItem[] {
76+
if (!item) {
77+
return this.treeItems;
78+
} else {
79+
return [];
80+
}
81+
}
82+
83+
private createTree(
84+
languageContext: LanguageContextStore,
85+
): LanguageSelectionTreeViewItem[] {
86+
return ALL_LANGUAGE_SELECTION_OPTIONS.map((language) => {
87+
return new LanguageSelectionTreeViewItem(
88+
language,
89+
languageContext.selectedLanguage(language),
90+
);
91+
});
92+
}
93+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { DisposableObject } from "../common/disposable-object";
2+
import { window } from "vscode";
3+
import {
4+
LanguageSelectionTreeDataProvider,
5+
LanguageSelectionTreeViewItem,
6+
} from "./language-selection-data-provider";
7+
import { LanguageContextStore } from "../language-context-store";
8+
import { LanguageSelectionCommands } from "../common/commands";
9+
10+
// This panel allows the selection of a single language, that will
11+
// then filter all other relevant views (e.g. db panel, query history).
12+
export class LanguageSelectionPanel extends DisposableObject {
13+
constructor(private readonly languageContext: LanguageContextStore) {
14+
super();
15+
16+
const dataProvider = new LanguageSelectionTreeDataProvider(languageContext);
17+
18+
const treeView = window.createTreeView("codeQLLanguageSelection", {
19+
treeDataProvider: dataProvider,
20+
});
21+
this.push(treeView);
22+
}
23+
24+
public getCommands(): LanguageSelectionCommands {
25+
return {
26+
"codeQLLanguageSelection.setSelectedItem":
27+
this.handleSetSelectedLanguage.bind(this),
28+
};
29+
}
30+
31+
private async handleSetSelectedLanguage(
32+
item: LanguageSelectionTreeViewItem,
33+
): Promise<void> {
34+
if (item.language) {
35+
await this.languageContext.setLanguageContext(item.language);
36+
} else {
37+
await this.languageContext.clearLanguageContext();
38+
}
39+
}
40+
}

extensions/ql-vscode/test/unit-tests/command-lint.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe("commands declared in package.json", () => {
3838
expect(title).toBeDefined();
3939
commandTitles[command] = title!;
4040
} else if (
41+
command.match(/^codeQLLanguageSelection\./) ||
4142
command.match(/^codeQLDatabases\./) ||
4243
command.match(/^codeQLQueries\./) ||
4344
command.match(/^codeQLVariantAnalysisRepositories\./) ||

0 commit comments

Comments
 (0)