diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 86ef0d0ca0d..c885683fb0c 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -24,6 +24,7 @@ import type { Row, UrlValueResolvable, } from "./raw-result-types"; +import type { AccessPathSuggestionOptions } from "../model-editor/suggestions"; /** * This module contains types and code that are shared between @@ -614,13 +615,19 @@ interface RevealMethodMessage { methodSignature: string; } +interface SetAccessPathSuggestionsMessage { + t: "setAccessPathSuggestions"; + accessPathSuggestions: AccessPathSuggestionOptions; +} + export type ToModelEditorMessage = | SetExtensionPackStateMessage | SetMethodsMessage | SetModeledMethodsMessage | SetModifiedMethodsMessage | SetInProgressMethodsMessage - | RevealMethodMessage; + | RevealMethodMessage + | SetAccessPathSuggestionsMessage; export type FromModelEditorMessage = | CommonFromViewMessages diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index a97ed2b7ee4..0680f8f0930 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -1,6 +1,7 @@ import type { CancellationToken, Tab } from "vscode"; import { CancellationTokenSource, + ProgressLocation, TabInputWebview, Uri, ViewColumn, @@ -53,6 +54,9 @@ import { getModelsAsDataLanguage } from "./languages"; import { runGenerateQueries } from "./generate"; import { ResponseError } from "vscode-jsonrpc"; import { LSPErrorCodes } from "vscode-languageclient"; +import type { AccessPathSuggestionOptions } from "./suggestions"; +import { runSuggestionsQuery } from "./suggestion-queries"; +import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs"; export class ModelEditorView extends AbstractWebview< ToModelEditorMessage, @@ -305,6 +309,17 @@ export class ModelEditorView extends AbstractWebview< withProgress((progress) => this.loadMethods(progress), { cancellable: false, }), + // Only load access path suggestions if the feature is enabled + this.modelConfig.enableAccessPathSuggestions + ? withProgress( + (progress) => this.loadAccessPathSuggestions(progress), + { + cancellable: false, + location: ProgressLocation.Window, + title: "Loading access path suggestions", + }, + ) + : undefined, ]); void telemetryListener?.sendUIInteraction("model-editor-switch-modes"); @@ -348,6 +363,14 @@ export class ModelEditorView extends AbstractWebview< cancellable: true, }), this.loadExistingModeledMethods(), + // Only load access path suggestions if the feature is enabled + this.modelConfig.enableAccessPathSuggestions + ? withProgress((progress) => this.loadAccessPathSuggestions(progress), { + cancellable: false, + location: ProgressLocation.Window, + title: "Loading access path suggestions", + }) + : undefined, ]); } @@ -485,6 +508,60 @@ export class ModelEditorView extends AbstractWebview< } } + protected async loadAccessPathSuggestions( + progress: ProgressCallback, + ): Promise { + const tokenSource = new CancellationTokenSource(); + + const mode = this.modelingStore.getMode(this.databaseItem); + + const modelsAsDataLanguage = getModelsAsDataLanguage(this.language); + const accessPathSuggestions = modelsAsDataLanguage.accessPathSuggestions; + if (!accessPathSuggestions) { + return; + } + + try { + const suggestions = await runSuggestionsQuery(mode, { + parseResults: (results) => + accessPathSuggestions.parseResults( + results, + modelsAsDataLanguage, + this.app.logger, + ), + cliServer: this.cliServer, + queryRunner: this.queryRunner, + queryStorageDir: this.queryStorageDir, + databaseItem: this.databaseItem, + progress, + token: tokenSource.token, + logger: this.app.logger, + }); + + if (!suggestions) { + return; + } + + const options: AccessPathSuggestionOptions = { + input: parseAccessPathSuggestionRowsToOptions(suggestions.input), + output: parseAccessPathSuggestionRowsToOptions(suggestions.output), + }; + + await this.postMessage({ + t: "setAccessPathSuggestions", + accessPathSuggestions: options, + }); + } catch (e: unknown) { + void showAndLogExceptionWithTelemetry( + this.app.logger, + this.app.telemetry, + redactableError( + asError(e), + )`Failed to fetch access path suggestions: ${getErrorMessage(e)}`, + ); + } + } + protected async generateModeledMethods(): Promise { await withProgress( async (progress) => { diff --git a/extensions/ql-vscode/src/model-editor/suggestions.ts b/extensions/ql-vscode/src/model-editor/suggestions.ts index 24ce3022820..9b1e334e941 100644 --- a/extensions/ql-vscode/src/model-editor/suggestions.ts +++ b/extensions/ql-vscode/src/model-editor/suggestions.ts @@ -38,6 +38,11 @@ export type AccessPathOption = { followup?: AccessPathOption[]; }; +export type AccessPathSuggestionOptions = { + input: Record; + output: Record; +}; + export function isDefinitionType( value: string, ): value is AccessPathSuggestionDefinitionType { diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index c0d868f5476..cf4f9dc0176 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -145,7 +145,9 @@ export function ModelEditor({ } case "revealMethod": setRevealedMethodSignature(msg.methodSignature); - + break; + case "setAccessPathSuggestions": + // TODO break; default: assertNever(msg);