diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 5f3260f5c..9ff1d459d 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] +- If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) - Add a prompt for downloading a GitHub database when opening a GitHub repository. [#3138](https://github.com/github/vscode-codeql/pull/3138) - Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125) - Add comparison of alerts when comparing query results. This allows viewing path explanations for differences in alerts. [#3113](https://github.com/github/vscode-codeql/pull/3113) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 608626bd8..674c1ecd3 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -5,6 +5,7 @@ import type { ProviderResult, TreeDataProvider, CancellationToken, + QuickPickItem, } from "vscode"; import { EventEmitter, @@ -28,7 +29,11 @@ import type { ProgressCallback, ProgressContext, } from "../common/vscode/progress"; -import { withInheritedProgress, withProgress } from "../common/vscode/progress"; +import { + UserCancellationException, + withInheritedProgress, + withProgress, +} from "../common/vscode/progress"; import { isLikelyDatabaseRoot, isLikelyDbLanguageFolder, @@ -52,7 +57,10 @@ import { createMultiSelectionCommand, createSingleSelectionCommand, } from "../common/vscode/selection-commands"; -import { tryGetQueryLanguage } from "../common/query-language"; +import { + getLanguageDisplayName, + tryGetQueryLanguage, +} from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; enum SortOrder { @@ -227,6 +235,18 @@ async function chooseDatabaseDir(byFolder: boolean): Promise { return getFirst(chosen); } +interface DatabaseSelectionQuickPickItem extends QuickPickItem { + databaseKind: "new" | "existing"; +} + +export interface DatabaseQuickPickItem extends QuickPickItem { + databaseItem: DatabaseItem; +} + +interface DatabaseImportQuickPickItems extends QuickPickItem { + importType: "URL" | "github" | "archive" | "folder"; +} + export class DatabaseUI extends DisposableObject { private treeDataProvider: DatabaseTreeDataProvider; @@ -794,13 +814,120 @@ export class DatabaseUI extends DisposableObject { * notification if it tries to perform any long-running operations. */ private async getDatabaseItemInternal( - progress: ProgressContext | undefined, + progressContext: ProgressContext | undefined, ): Promise { if (this.databaseManager.currentDatabaseItem === undefined) { - await this.chooseAndSetDatabase(false, progress); + progressContext?.progress({ + maxStep: 2, + step: 1, + message: "Choosing database", + }); + await this.promptForDatabase(); + } + return this.databaseManager.currentDatabaseItem; + } + + private async promptForDatabase(): Promise { + const quickPickItems: DatabaseSelectionQuickPickItem[] = [ + { + label: "$(database) Existing database", + detail: "Select an existing database from your workspace", + alwaysShow: true, + databaseKind: "existing", + }, + { + label: "$(arrow-down) New database", + detail: "Import a new database from the cloud or your local machine", + alwaysShow: true, + databaseKind: "new", + }, + ]; + const selectedOption = + await window.showQuickPick( + quickPickItems, + { + placeHolder: "Select an option", + ignoreFocusOut: true, + }, + ); + + if (!selectedOption) { + throw new UserCancellationException("No database selected", true); } - return this.databaseManager.currentDatabaseItem; + if (selectedOption.databaseKind === "existing") { + await this.selectExistingDatabase(); + } else if (selectedOption.databaseKind === "new") { + await this.importNewDatabase(); + } + } + + private async selectExistingDatabase() { + const dbItems: DatabaseQuickPickItem[] = + this.databaseManager.databaseItems.map((dbItem) => ({ + label: dbItem.name, + description: getLanguageDisplayName(dbItem.language), + databaseItem: dbItem, + })); + + const selectedDatabase = await window.showQuickPick(dbItems, { + placeHolder: "Select a database", + ignoreFocusOut: true, + }); + + if (!selectedDatabase) { + throw new UserCancellationException("No database selected", true); + } + + await this.databaseManager.setCurrentDatabaseItem( + selectedDatabase.databaseItem, + ); + } + + private async importNewDatabase() { + const importOptions: DatabaseImportQuickPickItems[] = [ + { + label: "$(github) GitHub", + detail: "Import a database from a GitHub repository", + alwaysShow: true, + importType: "github", + }, + { + label: "$(link) URL", + detail: "Import a database archive or folder from a remote URL", + alwaysShow: true, + importType: "URL", + }, + { + label: "$(file-zip) Archive", + detail: "Import a database from a local ZIP archive", + alwaysShow: true, + importType: "archive", + }, + { + label: "$(folder) Folder", + detail: "Import a database from a local folder", + alwaysShow: true, + importType: "folder", + }, + ]; + const selectedImportOption = + await window.showQuickPick(importOptions, { + placeHolder: "Import a database from...", + ignoreFocusOut: true, + }); + if (!selectedImportOption) { + throw new UserCancellationException("No database selected", true); + } + if (selectedImportOption.importType === "github") { + await this.handleChooseDatabaseGithub(); + } else if (selectedImportOption.importType === "URL") { + await this.handleChooseDatabaseInternet(); + } else if (selectedImportOption.importType === "archive") { + await this.handleChooseDatabaseArchive(); + } else if (selectedImportOption.importType === "folder") { + await this.handleChooseDatabaseFolder(); + } } /** diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index 7e42e3160..5b3e44d97 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -3,12 +3,7 @@ import type { ProgressUpdate, } from "../common/vscode/progress"; import { withProgress } from "../common/vscode/progress"; -import type { - CancellationToken, - QuickPickItem, - Range, - TabInputText, -} from "vscode"; +import type { CancellationToken, Range, TabInputText } from "vscode"; import { CancellationTokenSource, Uri, window } from "vscode"; import { TeeLogger, @@ -23,7 +18,10 @@ import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { displayQuickQuery } from "./quick-query"; import type { CoreCompletedQuery, QueryRunner } from "../query-server"; import type { QueryHistoryManager } from "../query-history/query-history-manager"; -import type { DatabaseUI } from "../databases/local-databases-ui"; +import type { + DatabaseQuickPickItem, + DatabaseUI, +} from "../databases/local-databases-ui"; import type { ResultsView } from "./results-view"; import type { DatabaseItem, @@ -55,10 +53,6 @@ import { tryGetQueryLanguage } from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; import type { ExtensionApp } from "../common/vscode/vscode-app"; -interface DatabaseQuickPickItem extends QuickPickItem { - databaseItem: DatabaseItem; -} - export enum QuickEvalType { None, QuickEval,