Skip to content

Commit 543bada

Browse files
author
Dave Bartolomeo
committed
Add comments
1 parent 4019e6c commit 543bada

1 file changed

Lines changed: 148 additions & 8 deletions

File tree

extensions/ql-vscode/src/local-queries.ts

Lines changed: 148 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
Uri,
88
window,
99
} from "vscode";
10-
import { BaseLogger, extLogger, TeeLogger } from "./common";
10+
import { BaseLogger, extLogger, Logger, TeeLogger } from "./common";
1111
import { MAX_QUERIES } from "./config";
1212
import { gatherQlFiles } from "./pure/files";
1313
import { basename } from "path";
@@ -72,13 +72,112 @@ function formatResultMessage(result: CoreQueryResults): string {
7272
}
7373
}
7474

75+
/**
76+
* Tracks the evaluation of a local query, including its interactions with the UI.
77+
*
78+
* The client creates an instance of `LocalQueryRun` when the evaluation starts, and then invokes
79+
* the `complete()` function once the query has completed (successfully or otherwise).
80+
*
81+
* Having the client tell the `LocalQueryRun` when the evaluation is complete, rather than having
82+
* the `LocalQueryRun` manage the evaluation itself, may seem a bit clunky. It's done this way
83+
* because once we move query evaluation into a Debug Adapter, the debugging UI drives the
84+
* evaluation, and we can only respond to events from the debug adapter.
85+
*/
86+
export class LocalQueryRun {
87+
public constructor(
88+
private readonly outputDir: QueryOutputDir,
89+
private readonly localQueries: LocalQueries,
90+
private readonly queryInfo: LocalQueryInfo,
91+
private readonly dbItem: DatabaseItem,
92+
public readonly logger: Logger,
93+
) {}
94+
95+
/**
96+
* Updates the UI based on the results of the query evaluation. This creates the evaluator log
97+
* summaries, updates the query history item for the evaluation with the results and evaluation
98+
* time, and displays the results view.
99+
*
100+
* This function must be called when the evaluation completes, whether the evaluation was
101+
* successful or not.
102+
* */
103+
public async complete(results: CoreQueryResults): Promise<void> {
104+
const evalLogPaths = await this.localQueries.summarizeEvalLog(
105+
results.resultType,
106+
this.outputDir,
107+
this.logger,
108+
);
109+
if (evalLogPaths !== undefined) {
110+
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
111+
}
112+
const queryWithResults = await this.getCompletedQueryInfo(results);
113+
this.localQueries.queryHistoryManager.completeQuery(
114+
this.queryInfo,
115+
queryWithResults,
116+
);
117+
await this.localQueries.showResultsForCompletedQuery(
118+
this.queryInfo as CompletedLocalQueryInfo,
119+
WebviewReveal.Forced,
120+
);
121+
// Note we must update the query history view after showing results as the
122+
// display and sorting might depend on the number of results
123+
await this.localQueries.queryHistoryManager.refreshTreeView();
124+
}
125+
126+
/**
127+
* Gets a `QueryWithResults` containing information about the evaluation of the query and its
128+
* result, in the form expected by the query history UI.
129+
*/
130+
private async getCompletedQueryInfo(
131+
results: CoreQueryResults,
132+
): Promise<QueryWithResults> {
133+
// Read the query metadata if possible, to use in the UI.
134+
const metadata = await tryGetQueryMetadata(
135+
this.localQueries.cliServer,
136+
this.queryInfo.initialInfo.queryPath,
137+
);
138+
const query = new QueryEvaluationInfo(
139+
this.outputDir.querySaveDir,
140+
this.dbItem.databaseUri.fsPath,
141+
await this.dbItem.hasMetadataFile(),
142+
this.queryInfo.initialInfo.quickEvalPosition,
143+
metadata,
144+
);
145+
146+
if (results.resultType !== QueryResultType.SUCCESS) {
147+
const message = results.message
148+
? redactableError`${results.message}`
149+
: redactableError`Failed to run query`;
150+
void extLogger.log(message.fullMessage);
151+
void showAndLogExceptionWithTelemetry(
152+
redactableError`Failed to run query: ${message}`,
153+
);
154+
}
155+
const message = formatResultMessage(results);
156+
const successful = results.resultType === QueryResultType.SUCCESS;
157+
return {
158+
query,
159+
result: {
160+
evaluationTime: results.evaluationTime,
161+
queryId: 0,
162+
resultType: successful
163+
? QueryResultType.SUCCESS
164+
: QueryResultType.OTHER_ERROR,
165+
runId: 0,
166+
message,
167+
},
168+
message,
169+
successful,
170+
};
171+
}
172+
}
173+
75174
export class LocalQueries extends DisposableObject {
76175
public constructor(
77176
private readonly app: App,
78177
private readonly queryRunner: QueryRunner,
79-
private readonly queryHistoryManager: QueryHistoryManager,
178+
public readonly queryHistoryManager: QueryHistoryManager,
80179
private readonly databaseManager: DatabaseManager,
81-
private readonly cliServer: CodeQLCliServer,
180+
public readonly cliServer: CodeQLCliServer,
82181
private readonly databaseUI: DatabaseUI,
83182
private readonly localQueryResultsView: ResultsView,
84183
private readonly queryStorageDir: string,
@@ -234,7 +333,48 @@ export class LocalQueries extends DisposableObject {
234333
};
235334
}
236335

237-
async compileAndRunQuery(
336+
/**
337+
* Creates a new `LocalQueryRun` object to track a query evaluation. This creates a timestamp
338+
* file in the query's output directory, creates a `LocalQueryInfo` object, and registers that
339+
* object with the query history manager.
340+
*
341+
* Once the evaluation is complete, the client must call `complete()` on the `LocalQueryRun`
342+
* object to update the UI based on the results of the query.
343+
*/
344+
public async createLocalQueryRun(
345+
queryPath: string,
346+
quickEval: boolean,
347+
range: Range | undefined,
348+
dbItem: DatabaseItem,
349+
outputDir: string,
350+
tokenSource: CancellationTokenSource,
351+
): Promise<LocalQueryRun> {
352+
const queryOutputDir = new QueryOutputDir(outputDir);
353+
354+
await createTimestampFile(outputDir);
355+
356+
const initialInfo = await createInitialQueryInfo(
357+
Uri.file(queryPath),
358+
{
359+
databaseUri: dbItem.databaseUri.toString(),
360+
name: dbItem.name,
361+
},
362+
quickEval,
363+
range,
364+
);
365+
366+
// When cancellation is requested from the query history view, we just stop the debug session.
367+
const queryInfo = new LocalQueryInfo(initialInfo, tokenSource);
368+
this.queryHistoryManager.addQuery(queryInfo);
369+
370+
const logger = new TeeLogger(
371+
this.queryRunner.logger,
372+
queryOutputDir.logPath,
373+
);
374+
return new LocalQueryRun(queryOutputDir, this, queryInfo, dbItem, logger);
375+
}
376+
377+
public async compileAndRunQuery(
238378
quickEval: boolean,
239379
selectedQuery: Uri | undefined,
240380
progress: ProgressCallback,
@@ -296,7 +436,7 @@ export class LocalQueries extends DisposableObject {
296436
}
297437
}
298438

299-
async compileAndRunQueryOnMultipleDatabases(
439+
private async compileAndRunQueryOnMultipleDatabases(
300440
progress: ProgressCallback,
301441
token: CancellationToken,
302442
uri: Uri | undefined,
@@ -364,14 +504,14 @@ export class LocalQueries extends DisposableObject {
364504
}
365505
}
366506

367-
async showResultsForCompletedQuery(
507+
public async showResultsForCompletedQuery(
368508
query: CompletedLocalQueryInfo,
369509
forceReveal: WebviewReveal,
370510
): Promise<void> {
371511
await this.localQueryResultsView.showResults(query, forceReveal, false);
372512
}
373513

374-
async compileAndRunQueryAgainstDatabase(
514+
private async compileAndRunQueryAgainstDatabase(
375515
db: DatabaseItem,
376516
initialInfo: InitialQueryInfo,
377517
queryStorageDir: string,
@@ -466,7 +606,7 @@ export class LocalQueries extends DisposableObject {
466606
* Gets a `QueryWithResults` containing information about the evaluation of the query and its
467607
* result, in the form expected by the query history UI.
468608
*/
469-
public async getCompletedQueryInfo(
609+
private async getCompletedQueryInfo(
470610
dbItem: DatabaseItem,
471611
queryTarget: CoreQueryTarget,
472612
outputDir: QueryOutputDir,

0 commit comments

Comments
 (0)