108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import { basename, dirname } from "path";
|
|
import { CodeQLCliServer, QuerySetup } from "../codeql-cli/cli";
|
|
import { Event } from "vscode";
|
|
import { QueryLanguage, dbSchemeToLanguage } from "../common/query-language";
|
|
import { FALLBACK_QLPACK_FILENAME, QLPACK_FILENAMES } from "../pure/ql";
|
|
import { FilePathDiscovery } from "../common/vscode/file-path-discovery";
|
|
import { getErrorMessage } from "../pure/helpers-pure";
|
|
import { extLogger } from "../common";
|
|
import { EOL } from "os";
|
|
import { containsPath } from "../common/files";
|
|
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
|
|
|
export interface QueryPack {
|
|
path: string;
|
|
language: QueryLanguage | undefined;
|
|
}
|
|
|
|
/**
|
|
* Discovers all query packs in the workspace.
|
|
*/
|
|
export class QueryPackDiscovery extends FilePathDiscovery<QueryPack> {
|
|
constructor(private readonly cliServer: CodeQLCliServer) {
|
|
super("Query Pack Discovery", `**/{${QLPACK_FILENAMES.join(",")}}`);
|
|
}
|
|
|
|
/**
|
|
* Event that fires when the set of query packs in the workspace changes.
|
|
*/
|
|
public get onDidChangeQueryPacks(): Event<void> {
|
|
return this.onDidChangePathData;
|
|
}
|
|
|
|
/**
|
|
* Given a path of a query file, locate the query pack that contains it and
|
|
* return the language of that pack. Returns undefined if no pack is found
|
|
* or the pack's language is unknown.
|
|
*/
|
|
public getLanguageForQueryFile(queryPath: string): QueryLanguage | undefined {
|
|
// Find all packs in a higher directory than the query
|
|
const packs = this.getPathData().filter((queryPack) =>
|
|
containsPath(dirname(queryPack.path), queryPath),
|
|
);
|
|
|
|
// Sort by descreasing path length to find the pack nearest the query
|
|
packs.sort((a, b) => b.path.length - a.path.length);
|
|
|
|
if (packs.length === 0) {
|
|
return undefined;
|
|
}
|
|
|
|
if (packs.length === 1) {
|
|
return packs[0].language;
|
|
}
|
|
|
|
// If the first two packs are from a different directory, then the first one is the nearest
|
|
if (dirname(packs[0].path) !== dirname(packs[1].path)) {
|
|
return packs[0].language;
|
|
}
|
|
|
|
// If the first two packs are from the same directory then look at the filenames
|
|
if (basename(packs[0].path) === FALLBACK_QLPACK_FILENAME) {
|
|
return packs[0].language;
|
|
} else {
|
|
return packs[1].language;
|
|
}
|
|
}
|
|
|
|
protected async getDataForPath(path: string): Promise<QueryPack> {
|
|
const language = await this.determinePackLanguage(path);
|
|
return { path, language };
|
|
}
|
|
|
|
private async determinePackLanguage(
|
|
path: string,
|
|
): Promise<QueryLanguage | undefined> {
|
|
let packInfo: QuerySetup | undefined = undefined;
|
|
try {
|
|
packInfo = await this.cliServer.resolveLibraryPath(
|
|
getOnDiskWorkspaceFolders(),
|
|
path,
|
|
true,
|
|
);
|
|
} catch (err) {
|
|
void extLogger.log(
|
|
`Query pack discovery failed to determine language for query pack: ${path}${EOL}Reason: ${getErrorMessage(
|
|
err,
|
|
)}`,
|
|
);
|
|
}
|
|
if (packInfo?.dbscheme === undefined) {
|
|
return undefined;
|
|
}
|
|
const dbscheme = basename(packInfo.dbscheme);
|
|
return dbSchemeToLanguage[dbscheme];
|
|
}
|
|
|
|
protected pathIsRelevant(path: string): boolean {
|
|
return QLPACK_FILENAMES.includes(basename(path));
|
|
}
|
|
|
|
protected shouldOverwriteExistingData(
|
|
newPack: QueryPack,
|
|
existingPack: QueryPack,
|
|
): boolean {
|
|
return existingPack.language !== newPack.language;
|
|
}
|
|
}
|