diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 09335770591..52ac7d45b88 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -27,11 +27,13 @@ "onView:codeQLQueryHistory", "onView:test-explorer", "onCommand:codeQL.checkForUpdatesToCLI", + "onCommand:codeQLDatabases.chooseDatabaseFolder", + "onCommand:codeQLDatabases.chooseDatabaseArchive", + "onCommand:codeQLDatabases.chooseDatabaseInternet", + "onCommand:codeQL.setCurrentDatabase", "onCommand:codeQL.chooseDatabaseFolder", "onCommand:codeQL.chooseDatabaseArchive", "onCommand:codeQL.chooseDatabaseInternet", - "onCommand:codeQL.setCurrentDatabase", - "onCommand:codeQL.downloadDatabase", "onCommand:codeQLDatabases.chooseDatabase", "onCommand:codeQLDatabases.setCurrentDatabase", "onCommand:codeQL.quickQuery", @@ -175,7 +177,7 @@ "title": "CodeQL: Quick Query" }, { - "command": "codeQL.chooseDatabaseFolder", + "command": "codeQLDatabases.chooseDatabaseFolder", "title": "Choose Database from Folder", "icon": { "light": "media/light/folder-opened-plus.svg", @@ -183,7 +185,7 @@ } }, { - "command": "codeQL.chooseDatabaseArchive", + "command": "codeQLDatabases.chooseDatabaseArchive", "title": "Choose Database from Archive", "icon": { "light": "media/light/archive-plus.svg", @@ -191,8 +193,8 @@ } }, { - "command": "codeQL.chooseDatabaseInternet", - "title": "Download database", + "command": "codeQLDatabases.chooseDatabaseInternet", + "title": "Download Database", "icon": { "light": "media/light/cloud-download.svg", "dark": "media/dark/cloud-download.svg" @@ -231,8 +233,16 @@ "title": "Show Database Directory" }, { - "command": "codeQL.downloadDatabase", - "title": "CodeQL: Download database" + "command": "codeQL.chooseDatabaseFolder", + "title": "CodeQL: Choose Database from Folder" + }, + { + "command": "codeQL.chooseDatabaseArchive", + "title": "CodeQL: Choose Database from Archive" + }, + { + "command": "codeQL.chooseDatabaseInternet", + "title": "CodeQL: Download Database" }, { "command": "codeQLDatabases.sortByName", @@ -312,17 +322,17 @@ "group": "navigation" }, { - "command": "codeQL.chooseDatabaseFolder", + "command": "codeQLDatabases.chooseDatabaseFolder", "when": "view == codeQLDatabases", "group": "navigation" }, { - "command": "codeQL.chooseDatabaseArchive", + "command": "codeQLDatabases.chooseDatabaseArchive", "when": "view == codeQLDatabases", "group": "navigation" }, { - "command": "codeQL.chooseDatabaseInternet", + "command": "codeQLDatabases.chooseDatabaseInternet", "when": "view == codeQLDatabases", "group": "navigation" } @@ -406,10 +416,6 @@ "command": "codeQL.runQuery", "when": "resourceLangId == ql && resourceExtname == .ql" }, - { - "command": "codeQL.downloadDatabase", - "when": "true" - }, { "command": "codeQL.quickEval", "when": "editorLangId == ql" @@ -442,6 +448,22 @@ "command": "codeQLDatabases.removeDatabase", "when": "false" }, + { + "command": "codeQLDatabases.chooseDatabaseFolder", + "when": "false" + }, + { + "command": "codeQLDatabases.chooseDatabaseArchive", + "when": "false" + }, + { + "command": "codeQLDatabases.chooseDatabaseInternet", + "when": "false" + }, + { + "command": "codeQLDatabases.upgradeDatabase", + "when": "false" + }, { "command": "codeQLQueryHistory.openQuery", "when": "false" diff --git a/extensions/ql-vscode/src/databases-ui.ts b/extensions/ql-vscode/src/databases-ui.ts index 0314089c315..62087189ceb 100644 --- a/extensions/ql-vscode/src/databases-ui.ts +++ b/extensions/ql-vscode/src/databases-ui.ts @@ -174,9 +174,9 @@ export class DatabaseUI extends DisposableObject { this.treeDataProvider = this.push(new DatabaseTreeDataProvider(ctx, databaseManager)); this.push(window.createTreeView('codeQLDatabases', { treeDataProvider: this.treeDataProvider })); - ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseFolder', this.handleChooseDatabaseFolder)); - ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseArchive', this.handleChooseDatabaseArchive)); - ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseInternet', this.handleChooseDatabaseInternet)); + ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseFolder', this.handleChooseDatabaseFolder)); + ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseArchive', this.handleChooseDatabaseArchive)); + ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseInternet', this.handleChooseDatabaseInternet)); ctx.subscriptions.push(commands.registerCommand('codeQL.setCurrentDatabase', this.handleSetCurrentDatabase)); ctx.subscriptions.push(commands.registerCommand('codeQL.upgradeCurrentDatabase', this.handleUpgradeCurrentDatabase)); ctx.subscriptions.push(commands.registerCommand('codeQL.clearCache', this.handleClearCache)); @@ -193,7 +193,7 @@ export class DatabaseUI extends DisposableObject { await this.databaseManager.setCurrentDatabaseItem(databaseItem); } - private handleChooseDatabaseFolder = async (): Promise => { + handleChooseDatabaseFolder = async (): Promise => { try { return await this.chooseAndSetDatabase(true); } catch (e) { @@ -202,7 +202,7 @@ export class DatabaseUI extends DisposableObject { } } - private handleChooseDatabaseArchive = async (): Promise => { + handleChooseDatabaseArchive = async (): Promise => { try { return await this.chooseAndSetDatabase(false); } catch (e) { @@ -211,7 +211,7 @@ export class DatabaseUI extends DisposableObject { } } - private handleChooseDatabaseInternet = async (): Promise => { + handleChooseDatabaseInternet = async (): Promise => { return await promptImportInternetDatabase(this.databaseManager, this.storagePath); } diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 152b2a0ef8c..461bc71b493 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -20,7 +20,6 @@ import { displayQuickQuery } from './quick-query'; import { compileAndRunQueryAgainstDatabase, tmpDirDisposal, UserCancellationException } from './run-queries'; import { QLTestAdapterFactory } from './test-adapter'; import { TestUIService } from './test-ui'; -import { promptImportInternetDatabase } from './databaseFetcher'; /** * extension.ts @@ -335,7 +334,9 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu await qs.restartQueryServer(); helpers.showAndLogInformationMessage('CodeQL Query Server restarted.', { outputLogger: queryServerLogger }); })); - ctx.subscriptions.push(commands.registerCommand('codeQL.downloadDatabase', () => promptImportInternetDatabase(dbm, getContextStoragePath(ctx)))); + ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseFolder', () => databaseUI.handleChooseDatabaseFolder())); + ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseArchive', () => databaseUI.handleChooseDatabaseArchive())); + ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseInternet', () => databaseUI.handleChooseDatabaseInternet())); ctx.subscriptions.push(client.start()); diff --git a/extensions/ql-vscode/test/pure-tests/command-lint.test.ts b/extensions/ql-vscode/test/pure-tests/command-lint.test.ts new file mode 100644 index 00000000000..45c3b445369 --- /dev/null +++ b/extensions/ql-vscode/test/pure-tests/command-lint.test.ts @@ -0,0 +1,101 @@ +import { expect } from 'chai'; +import * as path from 'path'; +import * as fs from 'fs-extra'; + +type CmdDecl = { + command: string; + when?: string; + title?: string; +} + +describe('commands declared in package.json', function() { + const manifest = fs.readJsonSync(path.join(__dirname, '../../package.json')); + const commands = manifest.contributes.commands; + const menus = manifest.contributes.menus; + + const disabledInPalette: Set = new Set(); + + // These commands should appear in the command palette, and so + // should be prefixed with 'CodeQL: '. + const paletteCmds: Set = new Set(); + + // These commands arising on context menus in non-CodeQL controlled + // panels, (e.g. file browser) and so should be prefixed with 'CodeQL: '. + const contribContextMenuCmds: Set = new Set(); + + // These are commands used in CodeQL controlled panels, and so don't need any prefixing in their title. + const scopedCmds: Set = new Set(); + const commandTitles: { [cmd: string]: string } = {}; + + commands.forEach((commandDecl: CmdDecl) => { + const { command, title } = commandDecl; + if (command.match(/^codeQL\./) + || command.match(/^codeQLQueryResults\./) + || command.match(/^codeQLTests\./)) { + paletteCmds.add(command); + expect(title).not.to.be.undefined; + commandTitles[command] = title!; + } + else if (command.match(/^codeQLDatabases\./) + || command.match(/^codeQLQueryHistory\./)) { + scopedCmds.add(command); + expect(title).not.to.be.undefined; + commandTitles[command] = title!; + } + else { + expect.fail(`Unexpected command name ${command}`); + } + }); + + menus['explorer/context'].forEach((commandDecl: CmdDecl) => { + const { command } = commandDecl; + paletteCmds.delete(command); + contribContextMenuCmds.add(command); + }); + + menus['editor/context'].forEach((commandDecl: CmdDecl) => { + const { command } = commandDecl; + paletteCmds.delete(command); + contribContextMenuCmds.add(command); + }); + + menus.commandPalette.forEach((commandDecl: CmdDecl) => { + if (commandDecl.when === 'false') + disabledInPalette.add(commandDecl.command); + }); + + + + it('should have commands appropriately prefixed', function() { + paletteCmds.forEach(command => { + expect(commandTitles[command], `command ${command} should be prefixed with 'CodeQL: ', since it is accessible from the command palette`).to.match(/^CodeQL: /); + }); + + contribContextMenuCmds.forEach(command => { + expect(commandTitles[command], `command ${command} should be prefixed with 'CodeQL: ', since it is accessible from a context menu in a non-extension-controlled context`).to.match(/^CodeQL: /); + }); + + scopedCmds.forEach(command => { + expect(commandTitles[command], `command ${command} should not be prefixed with 'CodeQL: ', since it is accessible from an extension-controlled context`).not.to.match(/^CodeQL: /); + }); + }); + + it('should have the right commands accessible from the command palette', function() { + paletteCmds.forEach(command => { + expect(disabledInPalette.has(command), `command ${command} should be enabled in the command palette`).to.be.false; + }); + + // Commands in contribContextMenuCmds may reasonbly be enabled or + // disabled in the command palette; for example, codeQL.runQuery + // is available there, since we heuristically figure out which + // query to run, but codeQL.setCurrentDatabase is not. + + scopedCmds.forEach(command => { + expect(disabledInPalette.has(command), `command ${command} should be disabled in the command palette`).to.be.true; + }); + }); + + +}); + +