-
Notifications
You must be signed in to change notification settings - Fork 189
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
Autodetect what language a query is for #915
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 |
---|---|---|
|
@@ -73,7 +73,7 @@ import { | |
import { CodeQlStatusBarHandler } from './status-bar'; | ||
|
||
import { Credentials } from './authentication'; | ||
import runRemoteQuery from './run-remote-query'; | ||
import { runRemoteQuery, findLanguage } from './run-remote-query'; | ||
|
||
/** | ||
* extension.ts | ||
|
@@ -570,7 +570,21 @@ async function activateWithInstalledDistribution( | |
token: CancellationToken, | ||
uri: Uri | undefined | ||
) => { | ||
const quickPickItems = dbm.databaseItems.map<DatabaseQuickPickItem>(dbItem => ( | ||
let filteredDBs = dbm.databaseItems; | ||
if (filteredDBs.length === 0) { | ||
void helpers.showAndLogErrorMessage('No databases found. Please add a suitable database to your workspace.'); | ||
return; | ||
} | ||
// If possible, only show databases with the right language (otherwise show all databases). | ||
const queryLanguage = await findLanguage(cliServer, uri); | ||
if (queryLanguage) { | ||
filteredDBs = dbm.databaseItems.filter(db => db.language === queryLanguage); | ||
if (filteredDBs.length === 0) { | ||
void helpers.showAndLogErrorMessage(`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`); | ||
return; | ||
} | ||
} | ||
const quickPickItems = filteredDBs.map<DatabaseQuickPickItem>(dbItem => ( | ||
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. Note that if the user has no databases and doesn't pick a language when prompted, we try to display this prompt with nothing in it. There should probably be an extra check to display an error in this case and return, similarly to what happens with the filtered list above. 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. Woops, good point! Thanks 😄 |
||
{ | ||
databaseItem: dbItem, | ||
label: dbItem.name, | ||
|
@@ -582,7 +596,7 @@ async function activateWithInstalledDistribution( | |
*/ | ||
const quickpick = await window.showQuickPick<DatabaseQuickPickItem>( | ||
quickPickItems, | ||
{ canPickMany: true } | ||
{ canPickMany: true, ignoreFocusOut: true } | ||
); | ||
if (quickpick !== undefined) { | ||
// Collect all skipped databases and display them at the end (instead of popping up individual errors) | ||
|
@@ -707,7 +721,7 @@ async function activateWithInstalledDistribution( | |
) => { | ||
if (isCanary()) { | ||
const credentials = await Credentials.initialize(ctx); | ||
await runRemoteQuery(credentials, uri || window.activeTextEditor?.document.uri); | ||
await runRemoteQuery(cliServer, credentials, uri || window.activeTextEditor?.document.uri); | ||
} | ||
}) | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,53 @@ | ||
import { Uri } from 'vscode'; | ||
import { Uri, window } from 'vscode'; | ||
import * as yaml from 'js-yaml'; | ||
import * as fs from 'fs-extra'; | ||
import { showAndLogErrorMessage, showAndLogInformationMessage } from './helpers'; | ||
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, showAndLogInformationMessage } from './helpers'; | ||
import { Credentials } from './authentication'; | ||
import * as cli from './cli'; | ||
import { logger } from './logging'; | ||
|
||
interface Config { | ||
repositories: string[]; | ||
ref?: string; | ||
language: string; | ||
language?: string; | ||
} | ||
|
||
// Test "controller" repository and workflow. | ||
const OWNER = 'dsp-testing'; | ||
const REPO = 'qc-controller'; | ||
|
||
export default async function runRemoteQuery(credentials: Credentials, uri?: Uri) { | ||
/** | ||
* Finds the language that a query targets. | ||
* If it can't be autodetected, prompt the user to specify the language manually. | ||
*/ | ||
export async function findLanguage( | ||
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. Since you asked about tests: I agree that having tests for this function would be handy, and give you confidence in its behaviour. However we don't yet have a test module for I'll record some pointers here about how you could create those tests, but I'm not expecting you to do it in this PR. We can look at test coverage together after this PR.
|
||
cliServer: cli.CodeQLCliServer, | ||
queryUri: Uri | undefined | ||
): Promise<string | undefined> { | ||
const uri = queryUri || window.activeTextEditor?.document.uri; | ||
if (uri !== undefined) { | ||
try { | ||
const queryInfo = await cliServer.resolveQueryByLanguage(getOnDiskWorkspaceFolders(), uri); | ||
const language = (Object.keys(queryInfo.byLanguage))[0]; | ||
void logger.log(`Detected query language: ${language}`); | ||
return language; | ||
} catch (e) { | ||
void logger.log('Could not autodetect query language. Select language manually.'); | ||
} | ||
} | ||
const availableLanguages = Object.keys(await cliServer.resolveLanguages()); | ||
const language = await window.showQuickPick( | ||
availableLanguages, | ||
{ placeHolder: 'Select target language for your query', ignoreFocusOut: true } | ||
); | ||
if (!language) { | ||
// This only happens if the user cancels the quick pick. | ||
void showAndLogErrorMessage('Language not found. Language must be specified manually.'); | ||
shati-patel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return language; | ||
} | ||
|
||
export async function runRemoteQuery(cliServer: cli.CodeQLCliServer, credentials: Credentials, uri?: Uri) { | ||
if (!uri?.fsPath.endsWith('.ql')) { | ||
return; | ||
} | ||
|
@@ -34,9 +67,13 @@ export default async function runRemoteQuery(credentials: Credentials, uri?: Uri | |
const config = yaml.safeLoad(await fs.readFile(repositoriesFile, 'utf8')) as Config; | ||
|
||
const ref = config.ref || 'main'; | ||
const language = config.language; | ||
const language = config.language || await findLanguage(cliServer, uri); | ||
const repositories = config.repositories; | ||
|
||
if (!language) { | ||
return; // No error message needed, since `findlanguage` already displays one. | ||
} | ||
|
||
try { | ||
await octokit.request( | ||
'POST /repos/:owner/:repo/code-scanning/codeql/queries', | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you asked about tests:
extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts
is a good place to add integration tests for theresolveQueryByLanguage
andresolveLanguages
functions.resolveLanguages
is probably easier to write a test for because there's no query as input.resolveQueryByLanguage
will require a test query: there are some examples in thedata
folders, but as far as I can tell none of them depend on an actual language. So you may need to add one, or save this test for later.