-
Notifications
You must be signed in to change notification settings - Fork 229
Avoid clobbering quick-query file when re-opened #747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,20 @@ | ||
| import * as fs from 'fs-extra'; | ||
| import * as yaml from 'js-yaml'; | ||
| import * as path from 'path'; | ||
| import { CancellationToken, ExtensionContext, window as Window, workspace, Uri } from 'vscode'; | ||
| import { | ||
| CancellationToken, | ||
| ExtensionContext, | ||
| window as Window, | ||
| workspace, | ||
| Uri | ||
| } from 'vscode'; | ||
| import { ErrorCodes, ResponseError } from 'vscode-languageclient'; | ||
| import { CodeQLCliServer } from './cli'; | ||
| import { DatabaseUI } from './databases-ui'; | ||
| import { logger } from './logging'; | ||
| import { | ||
| getInitialQueryContents, | ||
| getPrimaryDbscheme, | ||
| getQlPackForDbscheme, | ||
| showAndLogErrorMessage, | ||
| showBinaryChoiceDialog, | ||
| } from './helpers'; | ||
| import { | ||
|
|
@@ -21,23 +25,35 @@ import { | |
| const QUICK_QUERIES_DIR_NAME = 'quick-queries'; | ||
| const QUICK_QUERY_QUERY_NAME = 'quick-query.ql'; | ||
| const QUICK_QUERY_WORKSPACE_FOLDER_NAME = 'Quick Queries'; | ||
| const QLPACK_FILE_HEADER = '# This is an automatically generated file.\n\n'; | ||
|
|
||
| export function isQuickQueryPath(queryPath: string): boolean { | ||
| return path.basename(queryPath) === QUICK_QUERY_QUERY_NAME; | ||
| } | ||
|
|
||
| function getQuickQueriesDir(ctx: ExtensionContext): string { | ||
| async function getQuickQueriesDir(ctx: ExtensionContext): Promise<string> { | ||
| const storagePath = ctx.storagePath; | ||
| if (storagePath === undefined) { | ||
| throw new Error('Workspace storage path is undefined'); | ||
| } | ||
| const queriesPath = path.join(storagePath, QUICK_QUERIES_DIR_NAME); | ||
| fs.ensureDir(queriesPath, { mode: 0o700 }); | ||
| await fs.ensureDir(queriesPath, { mode: 0o700 }); | ||
| return queriesPath; | ||
| } | ||
|
|
||
| function updateQuickQueryDir(queriesDir: string, index: number, len: number) { | ||
| workspace.updateWorkspaceFolders( | ||
| index, | ||
| len, | ||
| { uri: Uri.file(queriesDir), name: QUICK_QUERY_WORKSPACE_FOLDER_NAME } | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| function findExistingQuickQueryEditor() { | ||
| return Window.visibleTextEditors.find(editor => | ||
| path.basename(editor.document.uri.fsPath) === QUICK_QUERY_QUERY_NAME | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Show a buffer the user can enter a simple query into. | ||
|
|
@@ -50,26 +66,18 @@ export async function displayQuickQuery( | |
| token: CancellationToken | ||
| ) { | ||
|
|
||
| function updateQuickQueryDir(queriesDir: string, index: number, len: number) { | ||
| workspace.updateWorkspaceFolders( | ||
| index, | ||
| len, | ||
| { uri: Uri.file(queriesDir), name: QUICK_QUERY_WORKSPACE_FOLDER_NAME } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const workspaceFolders = workspace.workspaceFolders || []; | ||
| const queriesDir = await getQuickQueriesDir(ctx); | ||
|
|
||
| // If there is already a quick query open, don't clobber it, just | ||
| // show it. | ||
| const existing = workspace.textDocuments.find(doc => path.basename(doc.uri.fsPath) === QUICK_QUERY_QUERY_NAME); | ||
| if (existing !== undefined) { | ||
| Window.showTextDocument(existing); | ||
| const existing = findExistingQuickQueryEditor(); | ||
| if (existing) { | ||
| await Window.showTextDocument(existing.document); | ||
| return; | ||
| } | ||
|
|
||
| const workspaceFolders = workspace.workspaceFolders || []; | ||
| const queriesDir = await getQuickQueriesDir(ctx); | ||
|
|
||
| // We need to have a multi-root workspace to make quick query work | ||
| // at all. Changing the workspace from single-root to multi-root | ||
| // causes a restart of the whole extension host environment, so we | ||
|
|
@@ -88,10 +96,11 @@ export async function displayQuickQuery( | |
| } | ||
|
|
||
| const index = workspaceFolders.findIndex(folder => folder.name === QUICK_QUERY_WORKSPACE_FOLDER_NAME); | ||
| if (index === -1) | ||
| if (index === -1) { | ||
| updateQuickQueryDir(queriesDir, workspaceFolders.length, 0); | ||
| else | ||
| } else { | ||
| updateQuickQueryDir(queriesDir, index, 1); | ||
| } | ||
|
|
||
| // We're going to infer which qlpack to use from the current database | ||
| const dbItem = await databaseUI.getDatabaseItem(progress, token); | ||
|
|
@@ -102,31 +111,38 @@ export async function displayQuickQuery( | |
| const datasetFolder = await dbItem.getDatasetFolder(cliServer); | ||
| const dbscheme = await getPrimaryDbscheme(datasetFolder); | ||
| const qlpack = await getQlPackForDbscheme(cliServer, dbscheme); | ||
|
|
||
| const quickQueryQlpackYaml: any = { | ||
| name: 'quick-query', | ||
| version: '1.0.0', | ||
| libraryPathDependencies: [qlpack] | ||
| }; | ||
|
|
||
| const qlFile = path.join(queriesDir, QUICK_QUERY_QUERY_NAME); | ||
| const qlPackFile = path.join(queriesDir, 'qlpack.yml'); | ||
| await fs.writeFile(qlFile, getInitialQueryContents(dbItem.language, dbscheme), 'utf8'); | ||
| await fs.writeFile(qlPackFile, yaml.safeDump(quickQueryQlpackYaml), 'utf8'); | ||
| Window.showTextDocument(await workspace.openTextDocument(qlFile)); | ||
| } | ||
|
|
||
| // TODO: clean up error handling for top-level commands like this | ||
| catch (e) { | ||
| if (e instanceof UserCancellationException) { | ||
| logger.log(e.message); | ||
| const qlFile = path.join(queriesDir, QUICK_QUERY_QUERY_NAME); | ||
| const shouldRewrite = await checkShouldRewrite(qlPackFile, qlpack); | ||
|
|
||
| // Only rewrite the qlpack file if the database has changed | ||
| if (shouldRewrite) { | ||
| const quickQueryQlpackYaml: any = { | ||
| name: 'quick-query', | ||
| version: '1.0.0', | ||
| libraryPathDependencies: [qlpack] | ||
| }; | ||
| await fs.writeFile(qlPackFile, QLPACK_FILE_HEADER + yaml.safeDump(quickQueryQlpackYaml), 'utf8'); | ||
| } | ||
| else if (e instanceof ResponseError && e.code == ErrorCodes.RequestCancelled) { | ||
| logger.log(e.message); | ||
|
|
||
| if (shouldRewrite || !(await fs.pathExists(qlFile))) { | ||
| await fs.writeFile(qlFile, getInitialQueryContents(dbItem.language, dbscheme), 'utf8'); | ||
| } | ||
| else if (e instanceof Error) | ||
| showAndLogErrorMessage(e.message); | ||
| else | ||
|
|
||
| await Window.showTextDocument(await workspace.openTextDocument(qlFile)); | ||
| } catch (e) { | ||
| if (e instanceof ResponseError && e.code == ErrorCodes.RequestCancelled) { | ||
| throw new UserCancellationException(e.message); | ||
| } else { | ||
| throw e; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| async function checkShouldRewrite(qlPackFile: string, newDependency: string) { | ||
| if (!(await fs.pathExists(qlPackFile))) { | ||
| return true; | ||
| } | ||
| const qlPackContents: any = yaml.safeLoad(await fs.readFile(qlPackFile, 'utf8')); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth having a simple type here instead of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it adds much value here. If we are going to start using qlpack files elsewhere in the code, then yes. |
||
| return qlPackContents.libraryPathDependencies?.[0] !== newDependency; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.