Skip to content

Commit 359ee76

Browse files
authored
Merge pull request #3401 from github/koesie10/hide-generated-type-models
Generate separate file for generated type models in Ruby by default
2 parents ab36153 + 0599adc commit 359ee76

File tree

10 files changed

+242
-72
lines changed

10 files changed

+242
-72
lines changed

extensions/ql-vscode/src/model-editor/generate.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@ import type { QueryRunner } from "../query-server";
55
import type { CodeQLCliServer } from "../codeql-cli/cli";
66
import type { ProgressCallback } from "../common/vscode/progress";
77
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
8-
import type { ModeledMethod } from "./modeled-method";
98
import { runQuery } from "../local-queries/run-query";
109
import type { QueryConstraints } from "../local-queries";
1110
import { resolveQueries } from "../local-queries";
1211
import type { DecodedBqrs } from "../common/bqrs-cli-types";
12+
1313
type GenerateQueriesOptions = {
1414
queryConstraints: QueryConstraints;
1515
filterQueries?: (queryPath: string) => boolean;
16-
parseResults: (
17-
queryPath: string,
18-
results: DecodedBqrs,
19-
) => ModeledMethod[] | Promise<ModeledMethod[]>;
20-
onResults: (results: ModeledMethod[]) => void | Promise<void>;
16+
onResults: (queryPath: string, results: DecodedBqrs) => void | Promise<void>;
2117

2218
cliServer: CodeQLCliServer;
2319
queryRunner: QueryRunner;
@@ -28,7 +24,7 @@ type GenerateQueriesOptions = {
2824
};
2925

3026
export async function runGenerateQueries(options: GenerateQueriesOptions) {
31-
const { queryConstraints, filterQueries, parseResults, onResults } = options;
27+
const { queryConstraints, filterQueries, onResults } = options;
3228

3329
options.progress({
3430
message: "Resolving queries",
@@ -55,7 +51,7 @@ export async function runGenerateQueries(options: GenerateQueriesOptions) {
5551

5652
const bqrs = await runSingleGenerateQuery(queryPath, i, maxStep, options);
5753
if (bqrs) {
58-
await onResults(await parseResults(queryPath, bqrs));
54+
await onResults(queryPath, bqrs);
5955
}
6056
}
6157
}

extensions/ql-vscode/src/model-editor/languages/models-as-data.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
SummaryModeledMethod,
88
TypeModeledMethod,
99
} from "../modeled-method";
10-
import type { DataTuple } from "../model-extension-file";
10+
import type { DataTuple, ModelExtension } from "../model-extension-file";
1111
import type { Mode } from "../shared/mode";
1212
import type { QueryConstraints } from "../../local-queries/query-constraints";
1313
import type {
@@ -68,6 +68,11 @@ export type ModelsAsDataLanguagePredicate<T> = {
6868
isHidden?: (context: IsHiddenContext) => boolean;
6969
};
7070

71+
export type GenerationContext = {
72+
mode: Mode;
73+
config: ModelConfig;
74+
};
75+
7176
type ParseGenerationResults = (
7277
// The path to the query that generated the results.
7378
queryPath: string,
@@ -78,24 +83,37 @@ type ParseGenerationResults = (
7883
modelsAsDataLanguage: ModelsAsDataLanguage,
7984
// The logger to use for logging.
8085
logger: BaseLogger,
86+
// Context about this invocation of the generation.
87+
context: GenerationContext,
8188
) => ModeledMethod[];
8289

8390
type ModelsAsDataLanguageModelGeneration = {
8491
queryConstraints: (mode: Mode) => QueryConstraints;
8592
filterQueries?: (queryPath: string) => boolean;
8693
parseResults: ParseGenerationResults;
94+
};
95+
96+
type ParseResultsToYaml = (
97+
// The path to the query that generated the results.
98+
queryPath: string,
99+
// The results of the query.
100+
bqrs: DecodedBqrs,
101+
// The language-specific predicate that was used to generate the results. This is passed to allow
102+
// sharing of code between different languages.
103+
modelsAsDataLanguage: ModelsAsDataLanguage,
104+
// The logger to use for logging.
105+
logger: BaseLogger,
106+
) => ModelExtension[];
107+
108+
type ModelsAsDataLanguageAutoModelGeneration = {
109+
queryConstraints: (mode: Mode) => QueryConstraints;
110+
filterQueries?: (queryPath: string) => boolean;
111+
parseResultsToYaml: ParseResultsToYaml;
87112
/**
88-
* If autoRun is not undefined, the query will be run automatically when the user starts the
89-
* model editor.
90-
*
91-
* This only applies to framework mode. Application mode will never run the query automatically.
113+
* By default, auto model generation is enabled for all modes. This function can be used to
114+
* override that behavior.
92115
*/
93-
autoRun?: {
94-
/**
95-
* If defined, will use a custom parsing function when the query is run automatically.
96-
*/
97-
parseResults?: ParseGenerationResults;
98-
};
116+
enabled?: (context: GenerationContext) => boolean;
99117
};
100118

101119
type ModelsAsDataLanguageAccessPathSuggestions = {
@@ -145,6 +163,7 @@ export type ModelsAsDataLanguage = {
145163
) => EndpointType | undefined;
146164
predicates: ModelsAsDataLanguagePredicates;
147165
modelGeneration?: ModelsAsDataLanguageModelGeneration;
166+
autoModelGeneration?: ModelsAsDataLanguageAutoModelGeneration;
148167
accessPathSuggestions?: ModelsAsDataLanguageAccessPathSuggestions;
149168
/**
150169
* Returns the list of valid arguments that can be selected for the given method.

extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type { BaseLogger } from "../../../common/logging";
22
import type { DecodedBqrs } from "../../../common/bqrs-cli-types";
3-
import type { ModelsAsDataLanguage } from "../models-as-data";
3+
import type {
4+
GenerationContext,
5+
ModelsAsDataLanguage,
6+
} from "../models-as-data";
47
import type { ModeledMethod } from "../../modeled-method";
58
import type { DataTuple } from "../../model-extension-file";
69

@@ -9,10 +12,21 @@ export function parseGenerateModelResults(
912
bqrs: DecodedBqrs,
1013
modelsAsDataLanguage: ModelsAsDataLanguage,
1114
logger: BaseLogger,
15+
{ config }: GenerationContext,
1216
): ModeledMethod[] {
1317
const modeledMethods: ModeledMethod[] = [];
1418

1519
for (const resultSetName in bqrs) {
20+
if (
21+
resultSetName ===
22+
modelsAsDataLanguage.predicates.type?.extensiblePredicate &&
23+
!config.showTypeModels
24+
) {
25+
// Don't load generated type results when type models are hidden. These are already
26+
// automatically generated on start-up.
27+
continue;
28+
}
29+
1630
const definition = Object.values(modelsAsDataLanguage.predicates).find(
1731
(definition) => definition.extensiblePredicate === resultSetName,
1832
);

extensions/ql-vscode/src/model-editor/languages/ruby/index.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -178,28 +178,40 @@ export const ruby: ModelsAsDataLanguage = {
178178
"tags contain all": ["modeleditor", "generate-model", modeTag(mode)],
179179
}),
180180
parseResults: parseGenerateModelResults,
181-
autoRun: {
182-
parseResults: (queryPath, bqrs, modelsAsDataLanguage, logger) => {
183-
// Only type models are generated automatically
184-
const typePredicate = modelsAsDataLanguage.predicates.type;
185-
if (!typePredicate) {
186-
throw new Error("Type predicate not found");
187-
}
181+
},
182+
autoModelGeneration: {
183+
queryConstraints: (mode) => ({
184+
kind: "table",
185+
"tags contain all": ["modeleditor", "generate-model", modeTag(mode)],
186+
}),
187+
parseResultsToYaml: (_queryPath, bqrs, modelsAsDataLanguage) => {
188+
const typePredicate = modelsAsDataLanguage.predicates.type;
189+
if (!typePredicate) {
190+
throw new Error("Type predicate not found");
191+
}
188192

189-
const filteredBqrs = Object.fromEntries(
190-
Object.entries(bqrs).filter(
191-
([key]) => key === typePredicate.extensiblePredicate,
192-
),
193-
);
193+
const typeTuples = bqrs[typePredicate.extensiblePredicate];
194+
if (!typeTuples) {
195+
return [];
196+
}
194197

195-
return parseGenerateModelResults(
196-
queryPath,
197-
filteredBqrs,
198-
modelsAsDataLanguage,
199-
logger,
200-
);
201-
},
198+
return [
199+
{
200+
addsTo: {
201+
pack: "codeql/ruby-all",
202+
extensible: typePredicate.extensiblePredicate,
203+
},
204+
data: typeTuples.tuples.filter((tuple): tuple is string[] => {
205+
return (
206+
tuple.filter((x) => typeof x === "string").length === tuple.length
207+
);
208+
}),
209+
},
210+
];
202211
},
212+
// Only enabled for framework mode when type models are hidden
213+
enabled: ({ mode, config }) =>
214+
mode === Mode.Framework && !config.showTypeModels,
203215
},
204216
accessPathSuggestions: {
205217
queryConstraints: (mode) => ({

extensions/ql-vscode/src/model-editor/model-editor-view.ts

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ import type { ModeledMethod } from "./modeled-method";
4141
import type { ExtensionPack } from "./shared/extension-pack";
4242
import type { ModelConfigListener } from "../config";
4343
import { Mode } from "./shared/mode";
44-
import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs";
44+
import {
45+
GENERATED_MODELS_SUFFIX,
46+
loadModeledMethods,
47+
saveModeledMethods,
48+
} from "./modeled-method-fs";
4549
import { pickExtensionPack } from "./extension-pack-picker";
4650
import type { QueryLanguage } from "../common/query-language";
4751
import { getLanguageDisplayName } from "../common/query-language";
@@ -60,6 +64,10 @@ import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs";
6064
import { ModelEvaluator } from "./model-evaluator";
6165
import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state";
6266
import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager";
67+
import type { ModelExtensionFile } from "./model-extension-file";
68+
import { modelExtensionFileToYaml } from "./yaml";
69+
import { outputFile } from "fs-extra";
70+
import { join } from "path";
6371

6472
export class ModelEditorView extends AbstractWebview<
6573
ToModelEditorMessage,
@@ -264,6 +272,7 @@ export class ModelEditorView extends AbstractWebview<
264272
modeledMethods,
265273
mode,
266274
this.cliServer,
275+
this.modelConfig,
267276
this.app.logger,
268277
);
269278

@@ -483,6 +492,7 @@ export class ModelEditorView extends AbstractWebview<
483492
this.extensionPack,
484493
this.language,
485494
this.cliServer,
495+
this.modelConfig,
486496
this.app.logger,
487497
);
488498
this.modelingStore.setModeledMethods(this.databaseItem, modeledMethods);
@@ -648,14 +658,18 @@ export class ModelEditorView extends AbstractWebview<
648658
await runGenerateQueries({
649659
queryConstraints: modelGeneration.queryConstraints(mode),
650660
filterQueries: modelGeneration.filterQueries,
651-
parseResults: (queryPath, results) =>
652-
modelGeneration.parseResults(
661+
onResults: async (queryPath, results) => {
662+
const modeledMethods = modelGeneration.parseResults(
653663
queryPath,
654664
results,
655665
modelsAsDataLanguage,
656666
this.app.logger,
657-
),
658-
onResults: async (modeledMethods) => {
667+
{
668+
mode,
669+
config: this.modelConfig,
670+
},
671+
);
672+
659673
this.addModeledMethodsFromArray(modeledMethods);
660674
},
661675
cliServer: this.cliServer,
@@ -681,15 +695,17 @@ export class ModelEditorView extends AbstractWebview<
681695

682696
protected async generateModeledMethodsOnStartup(): Promise<void> {
683697
const mode = this.modelingStore.getMode(this.databaseItem);
684-
if (mode !== Mode.Framework) {
698+
const modelsAsDataLanguage = getModelsAsDataLanguage(this.language);
699+
const autoModelGeneration = modelsAsDataLanguage.autoModelGeneration;
700+
701+
if (autoModelGeneration === undefined) {
685702
return;
686703
}
687704

688-
const modelsAsDataLanguage = getModelsAsDataLanguage(this.language);
689-
const modelGeneration = modelsAsDataLanguage.modelGeneration;
690-
const autoRun = modelGeneration?.autoRun;
691-
692-
if (modelGeneration === undefined || autoRun === undefined) {
705+
if (
706+
autoModelGeneration.enabled &&
707+
!autoModelGeneration.enabled({ mode, config: this.modelConfig })
708+
) {
693709
return;
694710
}
695711

@@ -701,22 +717,23 @@ export class ModelEditorView extends AbstractWebview<
701717
message: "Generating models",
702718
});
703719

704-
const parseResults =
705-
autoRun.parseResults ?? modelGeneration.parseResults;
720+
const extensionFile: ModelExtensionFile = {
721+
extensions: [],
722+
};
706723

707724
try {
708725
await runGenerateQueries({
709-
queryConstraints: modelGeneration.queryConstraints(mode),
710-
filterQueries: modelGeneration.filterQueries,
711-
parseResults: (queryPath, results) =>
712-
parseResults(
726+
queryConstraints: autoModelGeneration.queryConstraints(mode),
727+
filterQueries: autoModelGeneration.filterQueries,
728+
onResults: (queryPath, results) => {
729+
const extensions = autoModelGeneration.parseResultsToYaml(
713730
queryPath,
714731
results,
715732
modelsAsDataLanguage,
716733
this.app.logger,
717-
),
718-
onResults: async (modeledMethods) => {
719-
this.addModeledMethodsFromArray(modeledMethods);
734+
);
735+
736+
extensionFile.extensions.push(...extensions);
720737
},
721738
cliServer: this.cliServer,
722739
queryRunner: this.queryRunner,
@@ -733,7 +750,25 @@ export class ModelEditorView extends AbstractWebview<
733750
asError(e),
734751
)`Failed to auto-run generating models: ${getErrorMessage(e)}`,
735752
);
753+
return;
736754
}
755+
756+
progress({
757+
step: 4000,
758+
maxStep: 4000,
759+
message: "Saving generated models",
760+
});
761+
762+
const fileContents = `# This file was automatically generated from ${this.databaseItem.name}. Manual changes will not persist.\n\n${modelExtensionFileToYaml(extensionFile)}`;
763+
const filePath = join(
764+
this.extensionPack.path,
765+
"models",
766+
`${this.language}${GENERATED_MODELS_SUFFIX}`,
767+
);
768+
769+
await outputFile(filePath, fileContents);
770+
771+
void this.app.logger.log(`Saved generated model file to ${filePath}`);
737772
},
738773
{
739774
cancellable: false,

0 commit comments

Comments
 (0)