Skip to content

Commit eb3ba1e

Browse files
committed
Merge branch 'main' into shati-nora/add-remote-repositories
2 parents 467d43c + 5a32486 commit eb3ba1e

6 files changed

Lines changed: 112 additions & 67 deletions

File tree

extensions/ql-vscode/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,7 @@
375375
},
376376
{
377377
"command": "codeQLDatabasesExperimental.setSelectedItem",
378-
"title": "Select Item",
379-
"icon": "$(circle-small-filled)"
378+
"title": ""
380379
},
381380
{
382381
"command": "codeQLDatabases.chooseDatabaseFolder",

extensions/ql-vscode/src/databases/db-item.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ export enum DbItemKind {
1111
RemoteRepo = "RemoteRepo",
1212
}
1313

14+
export const remoteDbKinds = [
15+
DbItemKind.RootRemote,
16+
DbItemKind.RemoteSystemDefinedList,
17+
DbItemKind.RemoteUserDefinedList,
18+
DbItemKind.RemoteOwner,
19+
DbItemKind.RemoteRepo,
20+
];
21+
22+
export const localDbKinds = [
23+
DbItemKind.RootLocal,
24+
DbItemKind.LocalList,
25+
DbItemKind.LocalDatabase,
26+
];
27+
1428
export interface RootLocalDbItem {
1529
kind: DbItemKind.RootLocal;
1630
expanded: boolean;

extensions/ql-vscode/src/databases/db-manager.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { App } from "../common/app";
22
import { AppEvent, AppEventEmitter } from "../common/events";
33
import { ValueResult } from "../common/value-result";
44
import { DbConfigStore } from "./config/db-config-store";
5-
import { DbItem } from "./db-item";
5+
import { DbItem, DbItemKind, remoteDbKinds } from "./db-item";
66
import { calculateNewExpandedState } from "./db-item-expansion";
77
import {
88
getSelectedDbItem,
@@ -83,16 +83,19 @@ export class DbManager {
8383
await this.dbConfigStore.addRemoteOwner(owner);
8484
}
8585

86-
public async addNewRemoteList(listName: string): Promise<void> {
87-
if (listName === "") {
88-
throw Error("List name cannot be empty");
86+
public async addNewList(kind: DbItemKind, listName: string): Promise<void> {
87+
if (remoteDbKinds.includes(kind)) {
88+
if (listName === "") {
89+
throw Error("List name cannot be empty");
90+
}
91+
if (this.dbConfigStore.doesRemoteListExist(listName)) {
92+
throw Error(`A list with the name '${listName}' already exists`);
93+
}
94+
95+
await this.dbConfigStore.addRemoteList(listName);
96+
} else {
97+
throw Error("Cannot add a local list");
8998
}
90-
91-
if (this.dbConfigStore.doesRemoteListExist(listName)) {
92-
throw Error(`A list with the name '${listName}' already exists`);
93-
}
94-
95-
await this.dbConfigStore.addRemoteList(listName);
9699
}
97100

98101
public doesRemoteListExist(listName: string): boolean {

extensions/ql-vscode/src/databases/ui/db-panel.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
QuickPickItem,
3+
TreeView,
34
TreeViewExpansionEvent,
45
window,
56
workspace,
@@ -11,8 +12,8 @@ import {
1112
getOwnerFromGitHubUrl,
1213
isValidGitHubOwner,
1314
} from "../../common/github-url-identifier-helper";
14-
import { showAndLogErrorMessage } from "../../helpers";
1515
import { DisposableObject } from "../../pure/disposable-object";
16+
import { DbItem, DbItemKind } from "../db-item";
1617
import { DbManager } from "../db-manager";
1718
import { DbTreeDataProvider } from "./db-tree-data-provider";
1819
import { DbTreeViewItem } from "./db-tree-view-item";
@@ -23,29 +24,30 @@ interface RemoteDatabaseQuickPickItem extends QuickPickItem {
2324

2425
export class DbPanel extends DisposableObject {
2526
private readonly dataProvider: DbTreeDataProvider;
27+
private readonly treeView: TreeView<DbTreeViewItem>;
2628

2729
public constructor(private readonly dbManager: DbManager) {
2830
super();
2931

3032
this.dataProvider = new DbTreeDataProvider(dbManager);
3133

32-
const treeView = window.createTreeView("codeQLDatabasesExperimental", {
34+
this.treeView = window.createTreeView("codeQLDatabasesExperimental", {
3335
treeDataProvider: this.dataProvider,
3436
canSelectMany: false,
3537
});
3638

3739
this.push(
38-
treeView.onDidCollapseElement(async (e) => {
40+
this.treeView.onDidCollapseElement(async (e) => {
3941
await this.onDidCollapseElement(e);
4042
}),
4143
);
4244
this.push(
43-
treeView.onDidExpandElement(async (e) => {
45+
this.treeView.onDidExpandElement(async (e) => {
4446
await this.onDidExpandElement(e);
4547
}),
4648
);
4749

48-
this.push(treeView);
50+
this.push(this.treeView);
4951
}
5052

5153
public async initialize(): Promise<void> {
@@ -158,13 +160,15 @@ export class DbPanel extends DisposableObject {
158160
return;
159161
}
160162

161-
if (this.dbManager.doesRemoteListExist(listName)) {
162-
void showAndLogErrorMessage(
163-
`A list with the name '${listName}' already exists`,
164-
);
165-
} else {
166-
await this.dbManager.addNewRemoteList(listName);
167-
}
163+
const highlightedItem = await this.getHighlightedDbItem();
164+
165+
// For now: we only support adding remote lists, so if no item is highlighted,
166+
// we default to the "RootRemote" kind.
167+
// In future: if the highlighted item is undefined, we'll show a quick pick where
168+
// a user can select whether to add a remote or local list.
169+
const listKind = highlightedItem?.kind || DbItemKind.RootRemote;
170+
171+
await this.dbManager.addNewList(listKind, listName);
168172
}
169173

170174
private async setSelectedItem(treeViewItem: DbTreeViewItem): Promise<void> {
@@ -197,4 +201,16 @@ export class DbPanel extends DisposableObject {
197201

198202
await this.dbManager.updateDbItemExpandedState(event.element.dbItem, true);
199203
}
204+
205+
/**
206+
* Gets the currently highlighted database item in the tree view.
207+
* The VS Code API calls this the "selection", but we already have a notion of selection
208+
* (i.e. which item has a check mark next to it), so we call this "highlighted".
209+
*
210+
* @returns The highlighted database item, or `undefined` if no item is highlighted.
211+
*/
212+
private async getHighlightedDbItem(): Promise<DbItem | undefined> {
213+
// You can only select one item at a time, so selection[0] gives the selection
214+
return this.treeView.selection[0]?.dbItem;
215+
}
200216
}

extensions/ql-vscode/src/databases/ui/db-selection-decoration-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class DbSelectionDecorationProvider implements FileDecorationProvider {
1313
): ProviderResult<FileDecoration> {
1414
if (uri?.query === "selected=true") {
1515
return {
16-
badge: "",
16+
badge: "",
1717
tooltip: "Currently selected",
1818
};
1919
}

extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -443,52 +443,63 @@ describe("db panel", () => {
443443
}
444444
});
445445

446-
it("should add a new list to the remote db list", async () => {
447-
const dbConfig: DbConfig = createDbConfig({
448-
remoteLists: [
449-
{
450-
name: "my-list-1",
451-
repositories: ["owner1/repo1", "owner1/repo2"],
446+
describe("addNewList", () => {
447+
it("should add a new remote list", async () => {
448+
const dbConfig: DbConfig = createDbConfig({
449+
remoteLists: [
450+
{
451+
name: "my-list-1",
452+
repositories: ["owner1/repo1", "owner1/repo2"],
453+
},
454+
],
455+
selected: {
456+
kind: SelectedDbItemKind.RemoteUserDefinedList,
457+
listName: "my-list-1",
452458
},
453-
],
454-
selected: {
455-
kind: SelectedDbItemKind.RemoteUserDefinedList,
456-
listName: "my-list-1",
457-
},
458-
});
459+
});
459460

460-
await saveDbConfig(dbConfig);
461+
await saveDbConfig(dbConfig);
461462

462-
const dbTreeItems = await dbTreeDataProvider.getChildren();
463+
const dbTreeItems = await dbTreeDataProvider.getChildren();
463464

464-
expect(dbTreeItems).toBeTruthy();
465-
const items = dbTreeItems!;
465+
expect(dbTreeItems).toBeTruthy();
466+
const items = dbTreeItems!;
466467

467-
const remoteRootNode = items[0];
468-
const remoteUserDefinedLists = remoteRootNode.children.filter(
469-
(c) => c.dbItem?.kind === DbItemKind.RemoteUserDefinedList,
470-
);
471-
const list1 = remoteRootNode.children.find(
472-
(c) =>
473-
c.dbItem?.kind === DbItemKind.RemoteUserDefinedList &&
474-
c.dbItem?.listName === "my-list-1",
475-
);
468+
const remoteRootNode = items[0];
469+
const remoteUserDefinedLists = remoteRootNode.children.filter(
470+
(c) => c.dbItem?.kind === DbItemKind.RemoteUserDefinedList,
471+
);
472+
const list1 = remoteRootNode.children.find(
473+
(c) =>
474+
c.dbItem?.kind === DbItemKind.RemoteUserDefinedList &&
475+
c.dbItem?.listName === "my-list-1",
476+
);
476477

477-
expect(remoteUserDefinedLists.length).toBe(1);
478-
expect(remoteUserDefinedLists[0]).toBe(list1);
478+
expect(remoteUserDefinedLists.length).toBe(1);
479+
expect(remoteUserDefinedLists[0]).toBe(list1);
479480

480-
await dbManager.addNewRemoteList("my-list-2");
481+
await dbManager.addNewList(DbItemKind.RootRemote, "my-list-2");
481482

482-
// Read the workspace databases JSON file directly to check that the new list has been added.
483-
// We can't use the dbConfigStore's `read` function here because it depends on the file watcher
484-
// picking up changes, and we don't control the timing of that.
485-
const dbConfigFileContents = await readJSON(dbConfigFilePath);
486-
expect(dbConfigFileContents.databases.remote.repositoryLists.length).toBe(
487-
2,
488-
);
489-
expect(dbConfigFileContents.databases.remote.repositoryLists[1]).toEqual({
490-
name: "my-list-2",
491-
repositories: [],
483+
// Read the workspace databases JSON file directly to check that the new list has been added.
484+
// We can't use the dbConfigStore's `read` function here because it depends on the file watcher
485+
// picking up changes, and we don't control the timing of that.
486+
const dbConfigFileContents = await readJSON(dbConfigFilePath);
487+
expect(dbConfigFileContents.databases.remote.repositoryLists.length).toBe(
488+
2,
489+
);
490+
expect(dbConfigFileContents.databases.remote.repositoryLists[1]).toEqual({
491+
name: "my-list-2",
492+
repositories: [],
493+
});
494+
});
495+
496+
it("should throw error when adding a new list to a local node", async () => {
497+
const dbConfig: DbConfig = createDbConfig();
498+
await saveDbConfig(dbConfig);
499+
500+
await expect(
501+
dbManager.addNewList(DbItemKind.RootLocal, ""),
502+
).rejects.toThrow(new Error("Cannot add a local list"));
492503
});
493504
});
494505

@@ -555,9 +566,9 @@ describe("db panel", () => {
555566

556567
await saveDbConfig(dbConfig);
557568

558-
await expect(dbManager.addNewRemoteList("")).rejects.toThrow(
559-
new Error("List name cannot be empty"),
560-
);
569+
await expect(
570+
dbManager.addNewList(DbItemKind.RootRemote, ""),
571+
).rejects.toThrow(new Error("List name cannot be empty"));
561572
});
562573

563574
it("should not allow adding a list with duplicate name", async () => {
@@ -572,7 +583,9 @@ describe("db panel", () => {
572583

573584
await saveDbConfig(dbConfig);
574585

575-
await expect(dbManager.addNewRemoteList("my-list-1")).rejects.toThrow(
586+
await expect(
587+
dbManager.addNewList(DbItemKind.RootRemote, "my-list-1"),
588+
).rejects.toThrow(
576589
new Error("A list with the name 'my-list-1' already exists"),
577590
);
578591
});

0 commit comments

Comments
 (0)