CodeQL model editor: Add support for free text input in type models (#3217)

This commit is contained in:
Shati Patel
2024-01-16 13:03:53 +00:00
committed by GitHub
parent 8c0a8e07b2
commit c5517e0aea
4 changed files with 93 additions and 5 deletions

View File

@@ -0,0 +1,27 @@
import { useEffect, useRef } from "react";
/**
* Call the callback after the value has not changed for a certain amount of time.
* @param value
* @param callback
* @param delay
*/
export function useDebounceCallback<T>(
value: T,
callback: (value: T) => void,
delay?: number,
) {
const callbackRef = useRef<(value: T) => void>(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
const timer = setTimeout(() => callbackRef.current(value), delay || 500);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
}

View File

@@ -7,11 +7,11 @@ import {
modeledMethodSupportsInput,
} from "../../model-editor/modeled-method";
import type { Method } from "../../model-editor/method";
import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
import type { QueryLanguage } from "../../common/query-language";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
import { InputDropdown } from "./InputDropdown";
import { ModelTypeTextbox } from "./ModelTypeTextbox";
type Props = {
language: QueryLanguage;
@@ -67,7 +67,14 @@ export const ModelInputDropdown = ({
: undefined;
if (modeledMethod?.type === "type") {
return <ReadonlyDropdown value={modeledMethod.path} aria-label="Path" />;
return (
<ModelTypeTextbox
modeledMethod={modeledMethod}
typeInfo="path"
onChange={onChange}
aria-label="Path"
/>
);
}
const modelAccepted = isModelAccepted(modeledMethod, modelingStatus);

View File

@@ -7,11 +7,11 @@ import {
modeledMethodSupportsOutput,
} from "../../model-editor/modeled-method";
import type { Method } from "../../model-editor/method";
import { ReadonlyDropdown } from "../common/ReadonlyDropdown";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
import type { QueryLanguage } from "../../common/query-language";
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
import { InputDropdown } from "./InputDropdown";
import { ModelTypeTextbox } from "./ModelTypeTextbox";
type Props = {
language: QueryLanguage;
@@ -69,8 +69,10 @@ export const ModelOutputDropdown = ({
if (modeledMethod?.type === "type") {
return (
<ReadonlyDropdown
value={modeledMethod.relatedTypeName}
<ModelTypeTextbox
modeledMethod={modeledMethod}
typeInfo="relatedTypeName"
onChange={onChange}
aria-label="Related type name"
/>
);

View File

@@ -0,0 +1,52 @@
import type { ChangeEvent } from "react";
import { useCallback, useEffect, useState } from "react";
import type {
ModeledMethod,
TypeModeledMethod,
} from "../../model-editor/modeled-method";
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react";
import { useDebounceCallback } from "../common/useDebounceCallback";
type Props = {
modeledMethod: TypeModeledMethod;
typeInfo: "path" | "relatedTypeName";
onChange: (modeledMethod: ModeledMethod) => void;
"aria-label"?: string;
};
export const ModelTypeTextbox = ({
modeledMethod,
typeInfo,
onChange,
...props
}: Props): JSX.Element => {
const [value, setValue] = useState<string | undefined>(
modeledMethod[typeInfo],
);
useEffect(() => {
setValue(modeledMethod[typeInfo]);
}, [modeledMethod, typeInfo]);
const handleChange = useCallback((e: ChangeEvent<HTMLSelectElement>) => {
const target = e.target as HTMLSelectElement;
setValue(target.value);
}, []);
// Debounce the callback to avoid updating the model too often.
// Not doing this results in a lot of lag when typing.
useDebounceCallback(
value,
(newValue: string | undefined) => {
onChange({
...modeledMethod,
[typeInfo]: newValue ?? "",
});
},
500,
);
return <VSCodeTextField value={value} onInput={handleChange} {...props} />;
};