-
Notifications
You must be signed in to change notification settings - Fork 202
Add a language label next to databases in the UI #697
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 2 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 |
---|---|---|
|
@@ -50,6 +50,7 @@ export interface DbInfo { | |
sourceArchiveRoot: string; | ||
datasetFolder: string; | ||
logsFolder: string; | ||
languages: string[]; | ||
} | ||
|
||
/** | ||
|
@@ -123,6 +124,11 @@ export class CodeQLCliServer implements Disposable { | |
*/ | ||
private static CLI_VERSION_WITH_DECOMPILE_KIND_DIL = new SemVer('2.3.0'); | ||
|
||
/** | ||
* CLI version where --kind=DIL was introduced | ||
*/ | ||
private static CLI_VERSION_WITH_LANGUAGE = new SemVer('2.4.1'); | ||
adityasharad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** The process for the cli server, or undefined if one doesn't exist yet */ | ||
process?: child_process.ChildProcessWithoutNullStreams; | ||
/** Queue of future commands*/ | ||
|
@@ -689,7 +695,7 @@ export class CodeQLCliServer implements Disposable { | |
} | ||
|
||
async generateDil(qloFile: string, outFile: string): Promise<void> { | ||
const extraArgs = (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_DECOMPILE_KIND_DIL) >= 0 | ||
const extraArgs = await this.supportsDecompileDil() | ||
? ['--kind', 'dil', '-o', outFile, qloFile] | ||
: ['-o', outFile, qloFile]; | ||
await this.runCodeQlCliCommand( | ||
|
@@ -706,6 +712,14 @@ export class CodeQLCliServer implements Disposable { | |
return this._version; | ||
} | ||
|
||
private async supportsDecompileDil() { | ||
return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_DECOMPILE_KIND_DIL) >= 0; | ||
} | ||
|
||
public async supportsLangaugeName() { | ||
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. typo |
||
return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_LANGUAGE) >= 0; | ||
} | ||
|
||
private async refreshVersion() { | ||
const distribution = await this.distributionProvider.getDistribution(); | ||
switch (distribution.kind) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,14 @@ import * as path from 'path'; | |
import * as vscode from 'vscode'; | ||
import * as cli from './cli'; | ||
import { ExtensionContext } from 'vscode'; | ||
import { showAndLogErrorMessage, showAndLogWarningMessage, showAndLogInformationMessage, ProgressCallback, withProgress } from './helpers'; | ||
import { | ||
showAndLogErrorMessage, | ||
showAndLogWarningMessage, | ||
showAndLogInformationMessage, | ||
isLikelyDatabaseRoot, | ||
ProgressCallback, | ||
withProgress | ||
} from './helpers'; | ||
import { zipArchiveScheme, encodeArchiveBasePath, decodeSourceArchiveUri, encodeSourceArchiveUri } from './archive-filesystem-provider'; | ||
import { DisposableObject } from './vscode-utils/disposable-object'; | ||
import { Logger, logger } from './logging'; | ||
|
@@ -37,11 +44,13 @@ export interface DatabaseOptions { | |
displayName?: string; | ||
ignoreSourceArchive?: boolean; | ||
dateAdded?: number | undefined; | ||
language?: string; | ||
} | ||
|
||
export interface FullDatabaseOptions extends DatabaseOptions { | ||
ignoreSourceArchive: boolean; | ||
dateAdded: number | undefined; | ||
language: string | undefined; | ||
} | ||
|
||
interface PersistedDatabaseItem { | ||
|
@@ -194,6 +203,9 @@ export interface DatabaseItem { | |
readonly databaseUri: vscode.Uri; | ||
/** The name of the database to be displayed in the UI */ | ||
name: string; | ||
|
||
/** The primary language of the database or empty string if unknown */ | ||
readonly language: string; | ||
/** The URI of the database's source archive, or `undefined` if no source archive is to be used. */ | ||
readonly sourceArchive: vscode.Uri | undefined; | ||
/** | ||
|
@@ -433,6 +445,10 @@ export class DatabaseItemImpl implements DatabaseItem { | |
return dbInfo.datasetFolder; | ||
} | ||
|
||
public get language() { | ||
return this.options.language || ''; | ||
} | ||
|
||
/** | ||
* Returns the root uri of the virtual filesystem for this database's source archive. | ||
*/ | ||
|
@@ -491,7 +507,8 @@ export class DatabaseManager extends DisposableObject { | |
constructor( | ||
private readonly ctx: ExtensionContext, | ||
private readonly qs: QueryServerClient, | ||
public readonly logger: Logger | ||
private readonly cli: cli.CodeQLCliServer, | ||
public logger: Logger | ||
) { | ||
super(); | ||
|
||
|
@@ -502,18 +519,16 @@ export class DatabaseManager extends DisposableObject { | |
progress: ProgressCallback, | ||
token: vscode.CancellationToken, | ||
uri: vscode.Uri, | ||
options?: DatabaseOptions | ||
): Promise<DatabaseItem> { | ||
|
||
const contents = await resolveDatabaseContents(uri); | ||
const realOptions = options || {}; | ||
adityasharad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Ignore the source archive for QLTest databases by default. | ||
const isQLTestDatabase = path.extname(uri.fsPath) === '.testproj'; | ||
const fullOptions: FullDatabaseOptions = { | ||
ignoreSourceArchive: (realOptions.ignoreSourceArchive !== undefined) ? | ||
realOptions.ignoreSourceArchive : isQLTestDatabase, | ||
displayName: realOptions.displayName, | ||
dateAdded: realOptions.dateAdded || Date.now() | ||
ignoreSourceArchive: isQLTestDatabase, | ||
// displayName is only set if a user explicitly renames a database | ||
displayName: undefined, | ||
dateAdded: Date.now(), | ||
language: await this.getPrimaryLanguage(uri.fsPath) | ||
}; | ||
const databaseItem = new DatabaseItemImpl(uri, contents, fullOptions, (event) => { | ||
this._onDidChangeDatabaseItem.fire(event); | ||
|
@@ -573,6 +588,7 @@ export class DatabaseManager extends DisposableObject { | |
let displayName: string | undefined = undefined; | ||
let ignoreSourceArchive = false; | ||
let dateAdded = undefined; | ||
let language = undefined; | ||
if (state.options) { | ||
if (typeof state.options.displayName === 'string') { | ||
displayName = state.options.displayName; | ||
|
@@ -583,13 +599,22 @@ export class DatabaseManager extends DisposableObject { | |
if (typeof state.options.dateAdded === 'number') { | ||
dateAdded = state.options.dateAdded; | ||
} | ||
language = state.options.language; | ||
} | ||
|
||
const dbBaseUri = vscode.Uri.parse(state.uri, true); | ||
if (language === undefined) { | ||
// we haven't been successful yet at getting the language. try again | ||
language = await this.getPrimaryLanguage(dbBaseUri.fsPath); | ||
} | ||
|
||
const fullOptions: FullDatabaseOptions = { | ||
ignoreSourceArchive, | ||
displayName, | ||
dateAdded | ||
dateAdded, | ||
language | ||
}; | ||
const item = new DatabaseItemImpl(vscode.Uri.parse(state.uri, true), undefined, fullOptions, | ||
const item = new DatabaseItemImpl(dbBaseUri, undefined, fullOptions, | ||
(event) => { | ||
this._onDidChangeDatabaseItem.fire(event); | ||
}); | ||
|
@@ -804,6 +829,15 @@ export class DatabaseManager extends DisposableObject { | |
} | ||
return false; | ||
} | ||
|
||
private async getPrimaryLanguage(dbPath: string) { | ||
if (!(await this.cli.supportsLangaugeName())) { | ||
// return undefined so that we continually recalculate until the cli version is bumped | ||
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. What do you mean by recalculate? 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'll explain better. |
||
return undefined; | ||
} | ||
const dbInfo = await this.cli.resolveDatabase(dbPath); | ||
return dbInfo.languages?.[0] || ''; | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -815,23 +849,3 @@ export function getUpgradesDirectories(scripts: string[]): vscode.Uri[] { | |
const uniqueParentDirs = new Set(parentDirs); | ||
return Array.from(uniqueParentDirs).map(filePath => vscode.Uri.file(filePath)); | ||
} | ||
|
||
|
||
// TODO: Get the list of supported languages from a list that will be auto-updated. | ||
|
||
export async function isLikelyDatabaseRoot(fsPath: string) { | ||
const [a, b, c] = (await Promise.all([ | ||
// databases can have either .dbinfo or codeql-database.yml. | ||
fs.pathExists(path.join(fsPath, '.dbinfo')), | ||
fs.pathExists(path.join(fsPath, 'codeql-database.yml')), | ||
|
||
// they *must* have a db-language folder | ||
(await fs.readdir(fsPath)).some(isLikelyDbLanguageFolder) | ||
])); | ||
|
||
return (a || b) && c; | ||
} | ||
|
||
export function isLikelyDbLanguageFolder(dbPath: string) { | ||
return !!path.basename(dbPath).startsWith('db-'); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -358,12 +358,6 @@ function createRateLimitedResult(): RateLimitedResult { | |||||
}; | ||||||
} | ||||||
|
||||||
|
||||||
export type DatasetFolderInfo = { | ||||||
dbscheme: string; | ||||||
qlpack: string; | ||||||
} | ||||||
|
||||||
export async function getQlPackForDbscheme(cliServer: CodeQLCliServer, dbschemePath: string): Promise<string> { | ||||||
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders()); | ||||||
const packs: { packDir: string | undefined; packName: string }[] = | ||||||
|
@@ -391,7 +385,7 @@ export async function getQlPackForDbscheme(cliServer: CodeQLCliServer, dbschemeP | |||||
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`); | ||||||
} | ||||||
|
||||||
export async function resolveDatasetFolder(cliServer: CodeQLCliServer, datasetFolder: string): Promise<DatasetFolderInfo> { | ||||||
export async function getPrimaryDbscheme(datasetFolder: string): Promise<string> { | ||||||
const dbschemes = await glob(path.join(datasetFolder, '*.dbscheme')); | ||||||
|
||||||
if (dbschemes.length < 1) { | ||||||
|
@@ -400,12 +394,11 @@ export async function resolveDatasetFolder(cliServer: CodeQLCliServer, datasetFo | |||||
|
||||||
dbschemes.sort(); | ||||||
const dbscheme = dbschemes[0]; | ||||||
|
||||||
if (dbschemes.length > 1) { | ||||||
Window.showErrorMessage(`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`); | ||||||
} | ||||||
|
||||||
const qlpack = await getQlPackForDbscheme(cliServer, dbscheme); | ||||||
return { dbscheme, qlpack }; | ||||||
return dbscheme; | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -464,3 +457,74 @@ export class CachedOperation<U> { | |||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
|
||||||
|
||||||
/** | ||||||
* The following functions al heuristically determine metadata about databases. | ||||||
*/ | ||||||
|
||||||
/** | ||||||
* Note that this heuristic is only being used for backwards compatibility with | ||||||
* CLI versions before the langauge name was introduced to dbInfo. Implementations | ||||||
* that do not require backwards compatibility should call | ||||||
* `cli.CodeQLCliServer.resolveDatabase` and use the first entry in the | ||||||
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.
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. Features sounds good. |
||||||
* `languages` property. | ||||||
* | ||||||
* @see cli.CodeQLCliServer.supportsLangaugeName | ||||||
* @see cli.CodeQLCliServer.resolveDatabase | ||||||
*/ | ||||||
const dbSchemeToLanguage = { | ||||||
'semmlecode.javascript.dbscheme': 'javascript', | ||||||
'semmlecode.cpp.dbscheme': 'cpp', | ||||||
'semmlecode.dbscheme': 'java', | ||||||
'semmlecode.python.dbscheme': 'python', | ||||||
'semmlecode.csharp.dbscheme': 'csharp', | ||||||
'go.dbscheme': 'go' | ||||||
}; | ||||||
|
||||||
/** | ||||||
* Returns the initial contents for an empty query, based on the language of the selected | ||||||
* databse. | ||||||
* | ||||||
* First try to get the contents text based on language. if that fails, try to get based on | ||||||
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.
Suggested change
|
||||||
* dbscheme. Otherwise return no import statement. | ||||||
* | ||||||
* @param language the database language or empty string if unknown | ||||||
* @param dbscheme path to the dbscheme file | ||||||
* | ||||||
* @returns an import and empty select statement appropriate for the selected language | ||||||
*/ | ||||||
export function getInitialQueryContents(language: string, dbscheme: string) { | ||||||
if (!language) { | ||||||
const dbschemeBase = path.basename(dbscheme) as keyof typeof dbSchemeToLanguage; | ||||||
language = dbSchemeToLanguage[dbschemeBase]; | ||||||
} | ||||||
|
||||||
return language | ||||||
? `import ${language}\n\nselect ""` | ||||||
: 'select ""'; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Heuristically determines if the directory passed in corresponds | ||||||
* to a database root. | ||||||
* | ||||||
* @param maybeRoot | ||||||
*/ | ||||||
export async function isLikelyDatabaseRoot(maybeRoot: string) { | ||||||
const [a, b, c] = (await Promise.all([ | ||||||
// databases can have either .dbinfo or codeql-database.yml. | ||||||
fs.pathExists(path.join(maybeRoot, '.dbinfo')), | ||||||
fs.pathExists(path.join(maybeRoot, 'codeql-database.yml')), | ||||||
|
||||||
// they *must* have a db-{language} folder | ||||||
glob('db-*/', { cwd: maybeRoot }) | ||||||
])); | ||||||
|
||||||
return !!((a || b) && c); | ||||||
} | ||||||
|
||||||
export function isLikelyDbLanguageFolder(dbPath: string) { | ||||||
return !!path.basename(dbPath).startsWith('db-'); | ||||||
} |
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.
Copy-paste comment.