Skip to content

Commit 19d85a7

Browse files
authored
CodeQL model editor: Use suggest box for type path suggestions (#3316)
1 parent 6030137 commit 19d85a7

File tree

4 files changed

+84
-5
lines changed

4 files changed

+84
-5
lines changed

extensions/ql-vscode/src/view/model-editor/MethodRow.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
278278
<ModelInputSuggestBox
279279
modeledMethod={modeledMethod}
280280
suggestions={inputAccessPathSuggestions}
281+
typePathSuggestions={outputAccessPathSuggestions ?? []}
281282
onChange={modeledMethodChangedHandlers[index]}
282283
/>
283284
)}

extensions/ql-vscode/src/view/model-editor/ModelInputSuggestBox.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
calculateNewProvenance,
55
modeledMethodSupportsInput,
66
} from "../../model-editor/modeled-method";
7-
import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
87
import type { AccessPathOption } from "../../model-editor/suggestions";
98
import { SuggestBox } from "../common/SuggestBox";
109
import { useDebounceCallback } from "../common/useDebounceCallback";
@@ -14,10 +13,12 @@ import {
1413
validateAccessPath,
1514
} from "../../model-editor/shared/access-paths";
1615
import { ModelSuggestionIcon } from "./ModelSuggestionIcon";
16+
import { ModelTypePathSuggestBox } from "./ModelTypePathSuggestBox";
1717

1818
type Props = {
1919
modeledMethod: ModeledMethod | undefined;
2020
suggestions: AccessPathOption[];
21+
typePathSuggestions: AccessPathOption[];
2122
onChange: (modeledMethod: ModeledMethod) => void;
2223
};
2324

@@ -33,6 +34,7 @@ const getDetails = (option: AccessPathOption) => option.details;
3334
export const ModelInputSuggestBox = ({
3435
modeledMethod,
3536
suggestions,
37+
typePathSuggestions,
3638
onChange,
3739
}: Props) => {
3840
const [value, setValue] = useState<string | undefined>(
@@ -75,7 +77,13 @@ export const ModelInputSuggestBox = ({
7577
);
7678

7779
if (modeledMethod?.type === "type") {
78-
return <ReadonlyDropdown value={modeledMethod.path} aria-label="Path" />;
80+
return (
81+
<ModelTypePathSuggestBox
82+
modeledMethod={modeledMethod}
83+
suggestions={typePathSuggestions}
84+
onChange={onChange}
85+
/>
86+
);
7987
}
8088

8189
return (

extensions/ql-vscode/src/view/model-editor/ModelOutputSuggestBox.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
calculateNewProvenance,
55
modeledMethodSupportsOutput,
66
} from "../../model-editor/modeled-method";
7-
import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
87
import type { AccessPathOption } from "../../model-editor/suggestions";
98
import { SuggestBox } from "../common/SuggestBox";
109
import { useDebounceCallback } from "../common/useDebounceCallback";
@@ -14,6 +13,7 @@ import {
1413
validateAccessPath,
1514
} from "../../model-editor/shared/access-paths";
1615
import { ModelSuggestionIcon } from "./ModelSuggestionIcon";
16+
import { ModelTypeTextbox } from "./ModelTypeTextbox";
1717

1818
type Props = {
1919
modeledMethod: ModeledMethod | undefined;
@@ -76,8 +76,10 @@ export const ModelOutputSuggestBox = ({
7676

7777
if (modeledMethod?.type === "type") {
7878
return (
79-
<ReadonlyDropdown
80-
value={modeledMethod.relatedTypeName}
79+
<ModelTypeTextbox
80+
modeledMethod={modeledMethod}
81+
typeInfo="relatedTypeName"
82+
onChange={onChange}
8183
aria-label="Related type name"
8284
/>
8385
);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useEffect, useState } from "react";
2+
import type { TypeModeledMethod } from "../../model-editor/modeled-method";
3+
import type { AccessPathOption } from "../../model-editor/suggestions";
4+
import { SuggestBox } from "../common/SuggestBox";
5+
import { useDebounceCallback } from "../common/useDebounceCallback";
6+
import type { AccessPathDiagnostic } from "../../model-editor/shared/access-paths";
7+
import {
8+
parseAccessPathTokens,
9+
validateAccessPath,
10+
} from "../../model-editor/shared/access-paths";
11+
import { ModelSuggestionIcon } from "./ModelSuggestionIcon";
12+
13+
type Props = {
14+
modeledMethod: TypeModeledMethod;
15+
suggestions: AccessPathOption[];
16+
onChange: (modeledMethod: TypeModeledMethod) => void;
17+
};
18+
19+
const parseValueToTokens = (value: string) =>
20+
parseAccessPathTokens(value).map((t) => t.text);
21+
22+
const getIcon = (option: AccessPathOption) => (
23+
<ModelSuggestionIcon name={option.icon} />
24+
);
25+
26+
const getDetails = (option: AccessPathOption) => option.details;
27+
28+
export const ModelTypePathSuggestBox = ({
29+
modeledMethod,
30+
suggestions,
31+
onChange,
32+
}: Props) => {
33+
const [value, setValue] = useState<string | undefined>(modeledMethod.path);
34+
35+
useEffect(() => {
36+
setValue(modeledMethod.path);
37+
}, [modeledMethod]);
38+
39+
// Debounce the callback to avoid updating the model too often.
40+
// Not doing this results in a lot of lag when typing.
41+
useDebounceCallback(
42+
value,
43+
(path: string | undefined) => {
44+
if (path === undefined) {
45+
return;
46+
}
47+
48+
onChange({
49+
...modeledMethod,
50+
path,
51+
});
52+
},
53+
500,
54+
);
55+
56+
return (
57+
<SuggestBox<AccessPathOption, AccessPathDiagnostic>
58+
value={value}
59+
options={suggestions}
60+
onChange={setValue}
61+
parseValueToTokens={parseValueToTokens}
62+
validateValue={validateAccessPath}
63+
getIcon={getIcon}
64+
getDetails={getDetails}
65+
aria-label="Path"
66+
/>
67+
);
68+
};

0 commit comments

Comments
 (0)