@@ -8,6 +8,8 @@ import type { CodeQLCliServer } from "../codeql-cli/cli";
88import { pathExists } from "fs-extra" ;
99import { withProgress , progressUpdate } from "../common/vscode/progress" ;
1010import type { ProgressCallback } from "../common/vscode/progress" ;
11+ import { join , dirname , parse } from "path" ;
12+ import { tryGetQueryMetadata } from "../codeql-cli/query-metadata" ;
1113
1214/**
1315 * Generates autofixes for the results of a variant analysis.
@@ -28,6 +30,16 @@ export async function viewAutofixesForVariantAnalysisResults(
2830 progress ( progressUpdate ( 1 , 4 , `Checking for local autofix installation` ) ) ;
2931 const localAutofixPath = findLocalAutofix ( ) ;
3032
33+ // Get the variant analysis with the given id.
34+ const variantAnalysis = variantAnalyses . get ( variantAnalysisId ) ;
35+ if ( ! variantAnalysis ) {
36+ throw new Error ( `No variant analysis with id: ${ variantAnalysisId } ` ) ;
37+ }
38+
39+ // Generate the query help and output it to the override directory.
40+ progress ( progressUpdate ( 2 , 4 , `Generating query help override` ) ) ;
41+ await overrideQueryHelp ( variantAnalysis , cliServer , localAutofixPath ) ;
42+
3143 // TODO
3244 } ,
3345 {
@@ -53,3 +65,68 @@ function findLocalAutofix(): string {
5365 }
5466 return localAutofixPath ;
5567}
68+
69+ /**
70+ * Overrides the query help from a given variant analysis
71+ * at a location within the `localAutofixPath` directory .
72+ */
73+ async function overrideQueryHelp (
74+ variantAnalysis : VariantAnalysis ,
75+ cliServer : CodeQLCliServer ,
76+ localAutofixPath : string ,
77+ ) : Promise < void > {
78+ // Get path to the query used by the variant analysis.
79+ const queryFilePath = variantAnalysis . query . filePath ;
80+ if ( ! ( await pathExists ( queryFilePath ) ) ) {
81+ throw new Error ( `Query file used by variant analysis not found.` ) ;
82+ }
83+ const queryFilePathNoExt = join (
84+ dirname ( queryFilePath ) ,
85+ parse ( queryFilePath ) . name ,
86+ ) ;
87+
88+ // Get the path to the query help, which may be either a `.qhelp` or a `.md` file.
89+ // Note: we assume that the name of the query file is the same as the name of the query help file.
90+ const queryHelpFilePathQhelp = `${ queryFilePathNoExt } .qhelp` ;
91+ const queryHelpFilePathMarkdown = `${ queryFilePathNoExt } .md` ;
92+
93+ // Set `queryHelpFilePath` to the existing extension type.
94+ let queryHelpFilePath : string ;
95+ if ( await pathExists ( queryHelpFilePathQhelp ) ) {
96+ queryHelpFilePath = queryHelpFilePathQhelp ;
97+ } else if ( await pathExists ( queryHelpFilePathMarkdown ) ) {
98+ queryHelpFilePath = queryHelpFilePathMarkdown ;
99+ } else {
100+ throw new Error (
101+ `Could not find query help file at either ${ queryHelpFilePathQhelp } or ${ queryHelpFilePathMarkdown } . Check that the query help file exists and is named correctly.` ,
102+ ) ;
103+ }
104+
105+ // Get the query metadata.
106+ const metadata = await tryGetQueryMetadata ( cliServer , queryFilePath ) ;
107+ if ( ! metadata ) {
108+ throw new Error ( `Could not get query metadata for ${ queryFilePath } .` ) ;
109+ }
110+ // Get the query ID (used for the overridden query help's filename).
111+ const queryId = metadata . id ;
112+ if ( ! queryId ) {
113+ throw new Error ( `Query metadata for ${ queryFilePath } is missing an ID.` ) ;
114+ }
115+ // Replace `/` with `-` for use with the overridden query help's filename.
116+ // Use `replaceAll` since some query IDs have multiple slashes.
117+ const queryIdWithDash = queryId . replaceAll ( "/" , "-" ) ;
118+
119+ // Get the path to the output directory for overriding the query help.
120+ // Note: the path to this directory may change in the future.
121+ const queryHelpOverrideDirectory = join (
122+ localAutofixPath ,
123+ "prompt-templates" ,
124+ "qhelps" ,
125+ `${ queryIdWithDash } .md` ,
126+ ) ;
127+
128+ await cliServer . generateQueryHelp (
129+ queryHelpFilePath ,
130+ queryHelpOverrideDirectory ,
131+ ) ;
132+ }
0 commit comments