Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@
"view/item/context": [
{
"command": "codeQLDatabasesExperimental.setSelectedItemContextMenu",
"when": "view == codeQLDatabasesExperimental && viewItem == canBeSelected"
"when": "view == codeQLDatabasesExperimental && viewItem =~ /canBeSelected/"
},
{
"command": "codeQLDatabases.setCurrentDatabase",
Expand Down Expand Up @@ -812,7 +812,7 @@
},
{
"command": "codeQLDatabasesExperimental.setSelectedItem",
"when": "view == codeQLDatabasesExperimental && viewItem == canBeSelected",
"when": "view == codeQLDatabasesExperimental && viewItem =~ /canBeSelected/",
"group": "inline"
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { DbItem, DbItemKind, isSelectableDbItem } from "../db-item";

export type DbTreeViewItemAction =
| "canBeSelected"
| "canBeRemoved"
| "canBeRenamed"
| "canBeOpenedOnGitHub";

export function getDbItemActions(dbItem: DbItem): DbTreeViewItemAction[] {
const actions: DbTreeViewItemAction[] = [];

if (canBeSelected(dbItem)) {
actions.push("canBeSelected");
}
if (canBeRemoved(dbItem)) {
actions.push("canBeRemoved");
}
if (canBeRenamed(dbItem)) {
actions.push("canBeRenamed");
}
if (canBeOpenedOnGitHub(dbItem)) {
actions.push("canBeOpenedOnGitHub");
}

return actions;
}

const dbItemKindsThatCanBeRemoved = [
DbItemKind.LocalList,
DbItemKind.RemoteUserDefinedList,
DbItemKind.LocalDatabase,
DbItemKind.RemoteRepo,
DbItemKind.RemoteOwner,
];

const dbItemKindsThatCanBeRenamed = [
DbItemKind.LocalList,
DbItemKind.RemoteUserDefinedList,
DbItemKind.LocalDatabase,
];

const dbItemKindsThatCanBeOpenedOnGitHub = [
DbItemKind.RemoteOwner,
DbItemKind.RemoteRepo,
];

function canBeSelected(dbItem: DbItem): boolean {
return isSelectableDbItem(dbItem) && !dbItem.selected;
}

function canBeRemoved(dbItem: DbItem): boolean {
return dbItemKindsThatCanBeRemoved.includes(dbItem.kind);
}

function canBeRenamed(dbItem: DbItem): boolean {
return dbItemKindsThatCanBeRenamed.includes(dbItem.kind);
}

function canBeOpenedOnGitHub(dbItem: DbItem): boolean {
return dbItemKindsThatCanBeOpenedOnGitHub.includes(dbItem.kind);
}
14 changes: 9 additions & 5 deletions extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RootLocalDbItem,
RootRemoteDbItem,
} from "../db-item";
import { getDbItemActions } from "./db-tree-view-item-action";

export const SELECTED_DB_ITEM_RESOURCE_URI = "codeql://databases?selected=true";

Expand All @@ -32,18 +33,21 @@ export class DbTreeViewItem extends vscode.TreeItem {
) {
super(label, collapsibleState);

if (dbItem && isSelectableDbItem(dbItem)) {
if (dbItem.selected) {
if (dbItem) {
this.contextValue = getContextValue(dbItem);
if (isSelectableDbItem(dbItem) && dbItem.selected) {
// Define the resource id to drive the UI to render this item as selected.
this.resourceUri = vscode.Uri.parse(SELECTED_DB_ITEM_RESOURCE_URI);
} else {
// Define a context value to drive the UI to show an action to select the item.
this.contextValue = "canBeSelected";
}
}
}
}

function getContextValue(dbItem: DbItem): string | undefined {
const actions = getDbItemActions(dbItem);
return actions.length > 0 ? actions.join(",") : undefined;
}

export function createDbTreeViewItemError(
label: string,
tooltip: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { getDbItemActions } from "../../../../src/databases/ui/db-tree-view-item-action";
import {
createLocalDatabaseDbItem,
createLocalListDbItem,
createRemoteOwnerDbItem,
createRemoteRepoDbItem,
createRemoteSystemDefinedListDbItem,
createRemoteUserDefinedListDbItem,
createRootLocalDbItem,
createRootRemoteDbItem,
} from "../../../factories/db-item-factories";

describe("getDbItemActions", () => {
it("should return an empty array for root remote node", () => {
const dbItem = createRootRemoteDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual([]);
});

it("should return an empty array for root local node", () => {
const dbItem = createRootLocalDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual([]);
});

it("should set canBeSelected, canBeRemoved and canBeRenamed for local user defined db list", () => {
const dbItem = createLocalListDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual(["canBeSelected", "canBeRemoved", "canBeRenamed"]);
});

it("should set canBeSelected, canBeRemoved and canBeRenamed for local db", () => {
const dbItem = createLocalDatabaseDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual(["canBeSelected", "canBeRemoved", "canBeRenamed"]);
});

it("should set canBeSelected for remote system defined db list", () => {
const dbItem = createRemoteSystemDefinedListDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual(["canBeSelected"]);
});

it("should not set canBeSelected for remote system defined list that is already selected", () => {
const dbItem = createRemoteSystemDefinedListDbItem({ selected: true });

const actions = getDbItemActions(dbItem);

expect(actions.length).toEqual(0);
});

it("should set canBeSelected, canBeRemoved and canBeRenamed for remote user defined db list", () => {
const dbItem = createRemoteUserDefinedListDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual(["canBeSelected", "canBeRemoved", "canBeRenamed"]);
});

it("should not set canBeSelected for remote user defined db list that is already selected", () => {
const dbItem = createRemoteUserDefinedListDbItem({ selected: true });

const actions = getDbItemActions(dbItem);

expect(actions.includes("canBeSelected")).toBeFalsy();
});

it("should set canBeSelected, canBeRemoved, canBeOpenedOnGitHub for remote owner", () => {
const dbItem = createRemoteOwnerDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual([
"canBeSelected",
"canBeRemoved",
"canBeOpenedOnGitHub",
]);
});

it("should set canBeSelected, canBeRemoved, canBeOpenedOnGitHub for remote db", () => {
const dbItem = createRemoteRepoDbItem();

const actions = getDbItemActions(dbItem);

expect(actions).toEqual([
"canBeSelected",
"canBeRemoved",
"canBeOpenedOnGitHub",
]);
});

it("should not set canBeSelected for remote db that is already selected", () => {
const dbItem = createRemoteRepoDbItem({ selected: true });

const actions = getDbItemActions(dbItem);

expect(actions.includes("canBeSelected")).toBeFalsy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ describe("db panel", () => {
expect(item.tooltip).toBe(`Top ${n} repositories of a language`);
expect(item.iconPath).toEqual(new ThemeIcon("github"));
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.None);
checkDbItemActions(item, ["canBeSelected"]);
}

function checkUserDefinedListItem(
Expand All @@ -783,6 +784,7 @@ describe("db panel", () => {
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toBeUndefined();
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.Collapsed);
checkDbItemActions(item, ["canBeSelected", "canBeRenamed", "canBeRemoved"]);
expect(item.children).toBeTruthy();
expect(item.children.length).toBe(repos.length);

Expand All @@ -796,6 +798,11 @@ describe("db panel", () => {
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toEqual(new ThemeIcon("organization"));
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.None);
checkDbItemActions(item, [
"canBeSelected",
"canBeRemoved",
"canBeOpenedOnGitHub",
]);
expect(item.children).toBeTruthy();
expect(item.children.length).toBe(0);
}
Expand All @@ -805,6 +812,11 @@ describe("db panel", () => {
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toEqual(new ThemeIcon("database"));
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.None);
checkDbItemActions(item, [
"canBeSelected",
"canBeRemoved",
"canBeOpenedOnGitHub",
]);
}

function checkLocalListItem(
Expand All @@ -816,6 +828,7 @@ describe("db panel", () => {
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toBeUndefined();
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.Collapsed);
checkDbItemActions(item, ["canBeSelected", "canBeRemoved", "canBeRenamed"]);
expect(item.children).toBeTruthy();
expect(item.children.length).toBe(databases.length);

Expand All @@ -832,6 +845,16 @@ describe("db panel", () => {
expect(item.tooltip).toBe(`Language: ${database.language}`);
expect(item.iconPath).toEqual(new ThemeIcon("database"));
expect(item.collapsibleState).toBe(TreeItemCollapsibleState.None);
checkDbItemActions(item, ["canBeSelected", "canBeRemoved", "canBeRenamed"]);
}

function checkDbItemActions(item: DbTreeViewItem, actions: string[]): void {
const itemActions = item.contextValue?.split(",");
expect(itemActions).toBeDefined();
expect(itemActions!.length).toBe(actions.length);
for (const action of actions) {
expect(itemActions).toContain(action);
}
}

function checkErrorItem(
Expand All @@ -852,14 +875,16 @@ describe("db panel", () => {
function isTreeViewItemSelectable(treeViewItem: DbTreeViewItem) {
return (
treeViewItem.resourceUri === undefined &&
treeViewItem.contextValue === "canBeSelected"
treeViewItem.contextValue?.includes("canBeSelected")
);
}

function isTreeViewItemSelected(treeViewItem: DbTreeViewItem) {
return (
treeViewItem.resourceUri?.toString(true) ===
SELECTED_DB_ITEM_RESOURCE_URI && treeViewItem.contextValue === undefined
SELECTED_DB_ITEM_RESOURCE_URI &&
(treeViewItem.contextValue === undefined ||
!treeViewItem.contextValue.includes("canBeSelected"))
);
}

Expand Down