Autodetect language using "resolve queries"
Also use autodection in relevant places - When running on multiple databases - When running a remote query
This commit is contained in:
@@ -8,7 +8,7 @@ import { Readable } from 'stream';
|
||||
import { StringDecoder } from 'string_decoder';
|
||||
import * as tk from 'tree-kill';
|
||||
import { promisify } from 'util';
|
||||
import { CancellationToken, Disposable } from 'vscode';
|
||||
import { CancellationToken, Disposable, Uri } from 'vscode';
|
||||
|
||||
import { BQRSInfo, DecodedBqrsChunk } from './pure/bqrs-cli-types';
|
||||
import { CliConfig } from './config';
|
||||
@@ -43,6 +43,16 @@ export interface QuerySetup {
|
||||
compilationCache?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The expected output of `codeql resolve queries --format bylanguage`.
|
||||
*/
|
||||
export interface QueryInfoByLanguage {
|
||||
// Using `unknown` as a placeholder. For now, the value is only ever an empty object.
|
||||
byLanguage: Record<string, Record<string, unknown>>;
|
||||
noDeclaredLanguage: Record<string, unknown>;
|
||||
multipleDeclaredLanguages: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The expected output of `codeql resolve database`.
|
||||
*/
|
||||
@@ -71,6 +81,11 @@ export interface UpgradesInfo {
|
||||
*/
|
||||
export type QlpacksInfo = { [name: string]: string[] };
|
||||
|
||||
/**
|
||||
* The expected output of `codeql resolve languages`.
|
||||
*/
|
||||
export type LanguagesInfo = { [name: string]: string[] };
|
||||
|
||||
/**
|
||||
* The expected output of `codeql resolve qlref`.
|
||||
*/
|
||||
@@ -482,6 +497,20 @@ export class CodeQLCliServer implements Disposable {
|
||||
return await this.runJsonCodeQlCliCommand<QuerySetup>(['resolve', 'library-path'], subcommandArgs, 'Resolving library paths');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the language for a query.
|
||||
* @param queryUri The URI of the query
|
||||
*/
|
||||
async resolveQueryByLanguage(workspaces: string[], queryUri: Uri): Promise<QueryInfoByLanguage> {
|
||||
const subcommandArgs = [
|
||||
'--format', 'bylanguage',
|
||||
queryUri.fsPath,
|
||||
'--additional-packs',
|
||||
workspaces.join(path.delimiter)
|
||||
];
|
||||
return JSON.parse(await this.runCodeQlCliCommand(['resolve', 'queries'], subcommandArgs, 'Resolving query by language'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all available QL tests in a given directory.
|
||||
* @param testPath Root of directory tree to search for tests.
|
||||
@@ -724,6 +753,14 @@ export class CodeQLCliServer implements Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about the available languages.
|
||||
* @returns A dictionary mapping language name to the directory it comes from
|
||||
*/
|
||||
async resolveLanguages(): Promise<LanguagesInfo> {
|
||||
return await this.runJsonCodeQlCliCommand<LanguagesInfo>(['resolve', 'languages'], [], 'Resolving languages');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about queries in a query suite.
|
||||
* @param suite The suite to resolve.
|
||||
|
||||
@@ -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,13 @@ async function activateWithInstalledDistribution(
|
||||
token: CancellationToken,
|
||||
uri: Uri | undefined
|
||||
) => {
|
||||
const quickPickItems = dbm.databaseItems.map<DatabaseQuickPickItem>(dbItem => (
|
||||
const queryLanguage = await findLanguage(cliServer, uri);
|
||||
const filteredDBs = dbm.databaseItems.filter(db => db.language === queryLanguage);
|
||||
if (filteredDBs.length === 0) {
|
||||
void helpers.showAndLogErrorMessage(`No databases found for language ${queryLanguage}`);
|
||||
return;
|
||||
}
|
||||
const quickPickItems = filteredDBs.map<DatabaseQuickPickItem>(dbItem => (
|
||||
{
|
||||
databaseItem: dbItem,
|
||||
label: dbItem.name,
|
||||
@@ -707,7 +713,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);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,20 +1,51 @@
|
||||
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(
|
||||
cliServer: cli.CodeQLCliServer,
|
||||
queryUri: Uri | undefined
|
||||
): Promise<string> {
|
||||
const uri = queryUri || window.activeTextEditor?.document.uri;
|
||||
if (uri !== undefined) {
|
||||
try {
|
||||
const queryInfo = await cliServer.resolveQueryByLanguage(getOnDiskWorkspaceFolders(), uri);
|
||||
return (Object.keys(queryInfo.byLanguage))[0];
|
||||
} 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.');
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
export async function runRemoteQuery(cliServer: cli.CodeQLCliServer, credentials: Credentials, uri?: Uri) {
|
||||
if (!uri?.fsPath.endsWith('.ql')) {
|
||||
return;
|
||||
}
|
||||
@@ -34,7 +65,7 @@ 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;
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user