Extract creation of method argument options to languages

This moves the creation of possible method argument options from the
view to the languages. This allows differentiating between the
languages, for example by using `Argument[self]` for Ruby instead of
`Argument[this]`.
This commit is contained in:
Koen Vlaswinkel
2023-11-03 13:50:08 +01:00
parent 146732fa29
commit b348356876
9 changed files with 104 additions and 43 deletions

View File

@@ -1,4 +1,4 @@
import { MethodDefinition } from "../method"; import { MethodArgument, MethodDefinition } from "../method";
import { import {
ModeledMethod, ModeledMethod,
NeutralModeledMethod, NeutralModeledMethod,
@@ -45,6 +45,11 @@ export type ModelsAsDataLanguagePredicates = {
neutral?: ModelsAsDataLanguagePredicate<NeutralModeledMethod>; neutral?: ModelsAsDataLanguagePredicate<NeutralModeledMethod>;
}; };
export type MethodArgumentOptions = {
options: MethodArgument[];
defaultArgumentPath: string;
};
export type ModelsAsDataLanguage = { export type ModelsAsDataLanguage = {
/** /**
* The modes that are available for this language. If not specified, all * The modes that are available for this language. If not specified, all
@@ -54,4 +59,9 @@ export type ModelsAsDataLanguage = {
createMethodSignature: (method: MethodDefinition) => string; createMethodSignature: (method: MethodDefinition) => string;
predicates: ModelsAsDataLanguagePredicates; predicates: ModelsAsDataLanguagePredicates;
modelGeneration?: ModelsAsDataLanguageModelGeneration; modelGeneration?: ModelsAsDataLanguageModelGeneration;
/**
* Returns the list of valid arguments that can be selected for the given method.
* @param method The method to get the valid arguments for.
*/
getArgumentOptions: (method: MethodDefinition) => MethodArgumentOptions;
}; };

View File

@@ -2,6 +2,7 @@ import { ModelsAsDataLanguage } from "../models-as-data";
import { sharedExtensiblePredicates, sharedKinds } from "../shared"; import { sharedExtensiblePredicates, sharedKinds } from "../shared";
import { Mode } from "../../shared/mode"; import { Mode } from "../../shared/mode";
import { parseGenerateModelResults } from "./generate"; import { parseGenerateModelResults } from "./generate";
import { getArgumentsList, MethodArgument } from "../../method";
function parseRubyMethodFromPath(path: string): string { function parseRubyMethodFromPath(path: string): string {
const match = path.match(/Method\[([^\]]+)].*/); const match = path.match(/Method\[([^\]]+)].*/);
@@ -157,4 +158,25 @@ export const ruby: ModelsAsDataLanguage = {
}, },
parseResults: parseGenerateModelResults, parseResults: parseGenerateModelResults,
}, },
getArgumentOptions: (method) => {
const argumentsList = getArgumentsList(method.methodParameters).map(
(argument, index): MethodArgument => ({
path: `Argument[${index}]`,
label: `Argument[${index}]: ${argument}`,
}),
);
return {
options: [
{
path: "Argument[self]",
label: "Argument[self]",
},
...argumentsList,
],
// If there are no arguments, we will default to "Argument[self]"
defaultArgumentPath:
argumentsList.length > 0 ? argumentsList[0].path : "Argument[self]",
};
},
}; };

View File

@@ -3,6 +3,7 @@ import { Provenance } from "../../modeled-method";
import { DataTuple } from "../../model-extension-file"; import { DataTuple } from "../../model-extension-file";
import { sharedExtensiblePredicates, sharedKinds } from "../shared"; import { sharedExtensiblePredicates, sharedKinds } from "../shared";
import { filterFlowModelQueries, parseFlowModelResults } from "./generate"; import { filterFlowModelQueries, parseFlowModelResults } from "./generate";
import { getArgumentsList, MethodArgument } from "../../method";
function readRowToMethod(row: DataTuple[]): string { function readRowToMethod(row: DataTuple[]): string {
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`; return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
@@ -145,4 +146,25 @@ export const staticLanguage: ModelsAsDataLanguage = {
filterQueries: filterFlowModelQueries, filterQueries: filterFlowModelQueries,
parseResults: parseFlowModelResults, parseResults: parseFlowModelResults,
}, },
getArgumentOptions: (method) => {
const argumentsList = getArgumentsList(method.methodParameters).map(
(argument, index): MethodArgument => ({
path: `Argument[${index}]`,
label: `Argument[${index}]: ${argument}`,
}),
);
return {
options: [
{
path: "Argument[this]",
label: "Argument[this]",
},
...argumentsList,
],
// If there are no arguments, we will default to "Argument[this]"
defaultArgumentPath:
argumentsList.length > 0 ? argumentsList[0].path : "Argument[this]",
};
},
}; };

View File

@@ -61,6 +61,11 @@ export interface Method extends MethodSignature {
readonly usages: readonly Usage[]; readonly usages: readonly Usage[];
} }
export interface MethodArgument {
path: string;
label: string;
}
export function getArgumentsList(methodParameters: string): string[] { export function getArgumentsList(methodParameters: string): string[] {
if (methodParameters === "()") { if (methodParameters === "()") {
return []; return [];

View File

@@ -39,6 +39,7 @@ export const MethodModelingInputs = ({
onChange, onChange,
}: MethodModelingInputsProps): JSX.Element => { }: MethodModelingInputsProps): JSX.Element => {
const inputProps = { const inputProps = {
language,
method, method,
modeledMethod, modeledMethod,
onChange, onChange,
@@ -82,7 +83,7 @@ export const MethodModelingInputs = ({
{isModelingInProgress ? ( {isModelingInProgress ? (
<InProgressDropdown /> <InProgressDropdown />
) : ( ) : (
<ModelKindDropdown language={language} {...inputProps} /> <ModelKindDropdown {...inputProps} />
)} )}
</Input> </Input>
</Container> </Container>

View File

@@ -234,6 +234,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
<DataGridRow key={index} focused={focusedIndex === index}> <DataGridRow key={index} focused={focusedIndex === index}>
<DataGridCell> <DataGridCell>
<ModelTypeDropdown <ModelTypeDropdown
language={viewState.language}
method={method} method={method}
modeledMethod={modeledMethod} modeledMethod={modeledMethod}
onChange={modeledMethodChangedHandlers[index]} onChange={modeledMethodChangedHandlers[index]}
@@ -241,6 +242,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
</DataGridCell> </DataGridCell>
<DataGridCell> <DataGridCell>
<ModelInputDropdown <ModelInputDropdown
language={viewState.language}
method={method} method={method}
modeledMethod={modeledMethod} modeledMethod={modeledMethod}
onChange={modeledMethodChangedHandlers[index]} onChange={modeledMethodChangedHandlers[index]}
@@ -248,6 +250,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
</DataGridCell> </DataGridCell>
<DataGridCell> <DataGridCell>
<ModelOutputDropdown <ModelOutputDropdown
language={viewState.language}
method={method} method={method}
modeledMethod={modeledMethod} modeledMethod={modeledMethod}
onChange={modeledMethodChangedHandlers[index]} onChange={modeledMethodChangedHandlers[index]}

View File

@@ -5,34 +5,33 @@ import {
ModeledMethod, ModeledMethod,
modeledMethodSupportsInput, modeledMethodSupportsInput,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
import { Method, getArgumentsList } from "../../model-editor/method"; import { Method } from "../../model-editor/method";
import { QueryLanguage } from "../../common/query-language";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
type Props = { type Props = {
language: QueryLanguage;
method: Method; method: Method;
modeledMethod: ModeledMethod | undefined; modeledMethod: ModeledMethod | undefined;
onChange: (modeledMethod: ModeledMethod) => void; onChange: (modeledMethod: ModeledMethod) => void;
}; };
export const ModelInputDropdown = ({ export const ModelInputDropdown = ({
language,
method, method,
modeledMethod, modeledMethod,
onChange, onChange,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const argumentsList = useMemo( const options = useMemo(() => {
() => getArgumentsList(method.methodParameters), const modelsAsDataLanguage = getModelsAsDataLanguage(language);
[method.methodParameters],
);
const options = useMemo( return modelsAsDataLanguage
() => [ .getArgumentOptions(method)
{ value: "Argument[this]", label: "Argument[this]" }, .options.map((option) => ({
...argumentsList.map((argument, index) => ({ value: option.path,
value: `Argument[${index}]`, label: option.label,
label: `Argument[${index}]: ${argument}`, }));
})), }, [language, method]);
],
[argumentsList],
);
const enabled = useMemo( const enabled = useMemo(
() => modeledMethod && modeledMethodSupportsInput(modeledMethod), () => modeledMethod && modeledMethodSupportsInput(modeledMethod),

View File

@@ -5,35 +5,34 @@ import {
ModeledMethod, ModeledMethod,
modeledMethodSupportsOutput, modeledMethodSupportsOutput,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
import { Method, getArgumentsList } from "../../model-editor/method"; import { Method } from "../../model-editor/method";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
import { QueryLanguage } from "../../common/query-language";
type Props = { type Props = {
language: QueryLanguage;
method: Method; method: Method;
modeledMethod: ModeledMethod | undefined; modeledMethod: ModeledMethod | undefined;
onChange: (modeledMethod: ModeledMethod) => void; onChange: (modeledMethod: ModeledMethod) => void;
}; };
export const ModelOutputDropdown = ({ export const ModelOutputDropdown = ({
language,
method, method,
modeledMethod, modeledMethod,
onChange, onChange,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const argumentsList = useMemo( const options = useMemo(() => {
() => getArgumentsList(method.methodParameters), const modelsAsDataLanguage = getModelsAsDataLanguage(language);
[method.methodParameters],
);
const options = useMemo( const options = modelsAsDataLanguage
() => [ .getArgumentOptions(method)
{ value: "ReturnValue", label: "ReturnValue" }, .options.map((option) => ({
{ value: "Argument[this]", label: "Argument[this]" }, value: option.path,
...argumentsList.map((argument, index) => ({ label: option.label,
value: `Argument[${index}]`, }));
label: `Argument[${index}]: ${argument}`, return [{ value: "ReturnValue", label: "ReturnValue" }, ...options];
})), }, [language, method]);
],
[argumentsList],
);
const enabled = useMemo( const enabled = useMemo(
() => modeledMethod && modeledMethodSupportsOutput(modeledMethod), () => modeledMethod && modeledMethodSupportsOutput(modeledMethod),

View File

@@ -1,5 +1,5 @@
import * as React from "react"; import * as React from "react";
import { ChangeEvent, useCallback, useMemo } from "react"; import { ChangeEvent, useCallback } from "react";
import { Dropdown } from "../common/Dropdown"; import { Dropdown } from "../common/Dropdown";
import { import {
ModeledMethod, ModeledMethod,
@@ -7,9 +7,11 @@ import {
ModeledMethodType, ModeledMethodType,
Provenance, Provenance,
} from "../../model-editor/modeled-method"; } from "../../model-editor/modeled-method";
import { Method, getArgumentsList } from "../../model-editor/method"; import { Method } from "../../model-editor/method";
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty"; import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
import { Mutable } from "../../common/mutable"; import { Mutable } from "../../common/mutable";
import { QueryLanguage } from "../../common/query-language";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
const options: Array<{ value: ModeledMethodType; label: string }> = [ const options: Array<{ value: ModeledMethodType; label: string }> = [
{ value: "none", label: "Unmodeled" }, { value: "none", label: "Unmodeled" },
@@ -20,23 +22,22 @@ const options: Array<{ value: ModeledMethodType; label: string }> = [
]; ];
type Props = { type Props = {
language: QueryLanguage;
method: Method; method: Method;
modeledMethod: ModeledMethod | undefined; modeledMethod: ModeledMethod | undefined;
onChange: (modeledMethod: ModeledMethod) => void; onChange: (modeledMethod: ModeledMethod) => void;
}; };
export const ModelTypeDropdown = ({ export const ModelTypeDropdown = ({
language,
method, method,
modeledMethod, modeledMethod,
onChange, onChange,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const argumentsList = useMemo(
() => getArgumentsList(method.methodParameters),
[method.methodParameters],
);
const handleChange = useCallback( const handleChange = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => { (e: ChangeEvent<HTMLSelectElement>) => {
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
let newProvenance: Provenance = "manual"; let newProvenance: Provenance = "manual";
if (modeledMethod && modeledMethodSupportsProvenance(modeledMethod)) { if (modeledMethod && modeledMethodSupportsProvenance(modeledMethod)) {
if (modeledMethod.provenance === "df-generated") { if (modeledMethod.provenance === "df-generated") {
@@ -54,9 +55,8 @@ export const ModelTypeDropdown = ({
...emptyModeledMethod, ...emptyModeledMethod,
}; };
if ("input" in updatedModeledMethod) { if ("input" in updatedModeledMethod) {
// If there are no arguments, we will default to "Argument[this]"
updatedModeledMethod.input = updatedModeledMethod.input =
argumentsList.length === 0 ? "Argument[this]" : "Argument[0]"; modelsAsDataLanguage.getArgumentOptions(method).defaultArgumentPath;
} }
if ("output" in updatedModeledMethod) { if ("output" in updatedModeledMethod) {
updatedModeledMethod.output = "ReturnValue"; updatedModeledMethod.output = "ReturnValue";
@@ -70,7 +70,7 @@ export const ModelTypeDropdown = ({
onChange(updatedModeledMethod); onChange(updatedModeledMethod);
}, },
[onChange, method, modeledMethod, argumentsList], [onChange, method, modeledMethod, language],
); );
return ( return (