diff --git a/extensions/ql-vscode/src/databases/db-item.ts b/extensions/ql-vscode/src/databases/db-item.ts new file mode 100644 index 000000000..610305dd6 --- /dev/null +++ b/extensions/ql-vscode/src/databases/db-item.ts @@ -0,0 +1,53 @@ +// This file contains models that are used to represent the databases. + +export enum DbItemKind { + RootLocal = 'RootLocal', + RootRemote = 'RootRemote', + RemoteSystemDefinedList = 'RemoteSystemDefinedList', + RemoteUserDefinedList = 'RemoteUserDefinedList', + RemoteOwner = 'RemoteOwner', + RemoteRepo = 'RemoteRepo' +} + +export interface RootLocalDbItem { + kind: DbItemKind.RootLocal; +} + +export interface RootRemoteDbItem { + kind: DbItemKind.RootRemote; + children: RemoteDbItem[]; +} + +export type DbItem = + | RootLocalDbItem + | RootRemoteDbItem + | RemoteDbItem + +export type RemoteDbItem = + | RemoteSystemDefinedListDbItem + | RemoteUserDefinedListDbItem + | RemoteOwnerDbItem + | RemoteRepoDbItem; + +export interface RemoteSystemDefinedListDbItem { + kind: DbItemKind.RemoteSystemDefinedList; + listName: string; + listDisplayName: string; + listDescription: string; +} + +export interface RemoteUserDefinedListDbItem { + kind: DbItemKind.RemoteUserDefinedList; + listName: string; + repos: RemoteRepoDbItem[]; +} + +export interface RemoteOwnerDbItem { + kind: DbItemKind.RemoteOwner; + ownerName: string; +} + +export interface RemoteRepoDbItem { + kind: DbItemKind.RemoteRepo; + repoFullName: string; +} diff --git a/extensions/ql-vscode/src/databases/db-manager.ts b/extensions/ql-vscode/src/databases/db-manager.ts new file mode 100644 index 000000000..1a97388aa --- /dev/null +++ b/extensions/ql-vscode/src/databases/db-manager.ts @@ -0,0 +1,22 @@ +import { logger } from '../logging'; +import { DbConfigStore } from './db-config-store'; +import { DbItem } from './db-item'; + +export class DbManager { + constructor( + private readonly dbConfigStore: DbConfigStore + ) { + } + + public loadDatabases(): void { + const config = this.dbConfigStore.getConfig(); + void logger.log(`Loaded databases: ${JSON.stringify(config)}`); + + // This will be fleshed out in a future change. + } + + public getDbItems(): DbItem[] { + // This will be fleshed out in a future change. + return []; + } +} diff --git a/extensions/ql-vscode/src/databases/db-module.ts b/extensions/ql-vscode/src/databases/db-module.ts new file mode 100644 index 000000000..37c9f155e --- /dev/null +++ b/extensions/ql-vscode/src/databases/db-module.ts @@ -0,0 +1,44 @@ +import * as vscode from 'vscode'; +import { isCanary, isNewQueryRunExperienceEnabled } from '../config'; +import { logger } from '../logging'; +import { DisposableObject } from '../pure/disposable-object'; +import { DbConfigStore } from './db-config-store'; +import { DbManager } from './db-manager'; +import { DbPanel } from './ui/db-panel'; + +export class DbModule extends DisposableObject { + public async initialize( + extensionContext: vscode.ExtensionContext + ): Promise { + if (extensionContext.extensionMode !== vscode.ExtensionMode.Development || + !isCanary() || + !isNewQueryRunExperienceEnabled()) { + // Currently, we only want to expose the new database panel when we + // are in development and canary mode and the developer has enabled the + // new query run experience. + return; + } + + void logger.log('Initializing database module'); + + const storagePath = extensionContext.storageUri?.fsPath || extensionContext.globalStorageUri.fsPath; + const dbConfigStore = new DbConfigStore(storagePath); + await dbConfigStore.initialize(); + + const dbManager = new DbManager(dbConfigStore); + dbManager.loadDatabases(); + + const dbPanel = new DbPanel(dbManager); + + this.push(dbPanel); + this.push(dbConfigStore); + } +} + +export async function initializeDbModule( + extensionContext: vscode.ExtensionContext +): Promise { + const dbModule = new DbModule(); + await dbModule.initialize(extensionContext); + return dbModule; +} diff --git a/extensions/ql-vscode/src/databases/ui/db-panel.ts b/extensions/ql-vscode/src/databases/ui/db-panel.ts new file mode 100644 index 000000000..4f1896129 --- /dev/null +++ b/extensions/ql-vscode/src/databases/ui/db-panel.ts @@ -0,0 +1,23 @@ +import * as vscode from 'vscode'; +import { DisposableObject } from '../../pure/disposable-object'; +import { DbManager } from '../db-manager'; +import { DbTreeDataProvider } from './db-tree-data-provider'; + +export class DbPanel extends DisposableObject { + private readonly dataProvider: DbTreeDataProvider; + + public constructor( + dbManager: DbManager + ) { + super(); + + this.dataProvider = new DbTreeDataProvider(dbManager); + + const treeView = vscode.window.createTreeView('codeQLDatabasesExperimental', { + treeDataProvider: this.dataProvider, + canSelectMany: false + }); + + this.push(treeView); + } +} diff --git a/extensions/ql-vscode/src/databases/ui/db-tree-data-provider.ts b/extensions/ql-vscode/src/databases/ui/db-tree-data-provider.ts new file mode 100644 index 000000000..38ca55e1d --- /dev/null +++ b/extensions/ql-vscode/src/databases/ui/db-tree-data-provider.ts @@ -0,0 +1,46 @@ +import { logger } from '../../logging'; +import { ProviderResult, TreeDataProvider, TreeItem } from 'vscode'; +import { DbTreeViewItem } from './db-tree-view-item'; +import { DbManager } from '../db-manager'; + +export class DbTreeDataProvider implements TreeDataProvider { + private dbTreeItems: DbTreeViewItem[]; + + public constructor( + private readonly dbManager: DbManager + ) { + this.dbTreeItems = this.createTree(); + } + + /** + * Called when expanding a node (including the root node). + * @param node The node to expand. + * @returns The children of the node. + */ + public getChildren(node?: DbTreeViewItem): ProviderResult { + if (!node) { + // We're at the root. + return Promise.resolve(this.dbTreeItems); + } else { + return Promise.resolve(node.children); + } + } + + /** + * Returns the UI presentation of the element that gets displayed in the view. + * @param node The node to represent. + * @returns The UI presentation of the node. + */ + public getTreeItem(node: DbTreeViewItem): TreeItem | Thenable { + return node; + } + + private createTree(): DbTreeViewItem[] { + const dbItems = this.dbManager.getDbItems(); + + // This will be fleshed out in a future change. + void logger.log(`Creating database tree with ${dbItems.length} items`); + + return []; + } +} diff --git a/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts b/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts new file mode 100644 index 000000000..f5192dbcb --- /dev/null +++ b/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts @@ -0,0 +1,17 @@ +import * as vscode from 'vscode'; +import { DbItem } from '../db-item'; + +/** + * Represents an item in the database tree view. + */ +export class DbTreeViewItem extends vscode.TreeItem { + constructor( + public readonly dbItem: DbItem, + public readonly label: string, + public readonly tooltip: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly children: DbTreeViewItem[] + ) { + super(label, collapsibleState); + } +} diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index d771f45a9..455638ef2 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -118,6 +118,7 @@ import { VariantAnalysisManager } from './remote-queries/variant-analysis-manage import { createVariantAnalysisContentProvider } from './remote-queries/variant-analysis-content-provider'; import { VSCodeMockGitHubApiServer } from './mocks/vscode-mock-gh-api-server'; import { VariantAnalysisResultsManager } from './remote-queries/variant-analysis-results-manager'; +import { initializeDbModule } from './databases/db-module'; /** * extension.ts @@ -1229,6 +1230,9 @@ async function activateWithInstalledDistribution( void logger.log('Reading query history'); await qhm.readQueryHistory(); + const dbModule = await initializeDbModule(ctx); + ctx.subscriptions.push(dbModule); + void logger.log('Successfully finished extension initialization.'); return {