feat: some traversal mutation
This commit is contained in:
parent
f2071507b9
commit
bf371268cf
|
@ -19,7 +19,7 @@ const Literal = ({
|
||||||
const typeRenderers = useTypeRenderers();
|
const typeRenderers = useTypeRenderers();
|
||||||
const type = typeFromLiteral(value.value);
|
const type = typeFromLiteral(value.value);
|
||||||
const Component = typeRenderers[type];
|
const Component = typeRenderers[type];
|
||||||
const [options] = stepsOptions(context, [{type: 'key'}], type);
|
const [options] = stepsOptions(context, [], type);
|
||||||
options.push('<literal>');
|
options.push('<literal>');
|
||||||
return (
|
return (
|
||||||
<div className="literal">
|
<div className="literal">
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Value from '~/client/value';
|
||||||
|
|
||||||
import propTypes from './prop-types';
|
import propTypes from './prop-types';
|
||||||
import {
|
import {
|
||||||
|
defaultSteps,
|
||||||
descriptionFromSteps,
|
descriptionFromSteps,
|
||||||
stepsOptions,
|
stepsOptions,
|
||||||
toBehaviorItem,
|
toBehaviorItem,
|
||||||
|
@ -38,7 +39,23 @@ const Traversal = (props) => {
|
||||||
const options = optionsList[i];
|
const options = optionsList[i];
|
||||||
return (
|
return (
|
||||||
<span className="key" data-step-key={step.key}>
|
<span className="key" data-step-key={step.key}>
|
||||||
<select readOnly value={step.key}>
|
<select
|
||||||
|
onChange={(event) => {
|
||||||
|
const newSteps = [
|
||||||
|
...steps.slice(0, i),
|
||||||
|
{
|
||||||
|
...steps[i],
|
||||||
|
key: event.target.value,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return onChange({
|
||||||
|
...value,
|
||||||
|
steps: defaultSteps(context, stepsType, newSteps),
|
||||||
|
value: assignValue,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={step.key}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
options
|
options
|
||||||
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
|
.sort((l, r) => (l.toLowerCase() < r.toLowerCase() ? -1 : 1))
|
||||||
|
@ -59,7 +76,27 @@ const Traversal = (props) => {
|
||||||
{arg.label}
|
{arg.label}
|
||||||
<Value.Component
|
<Value.Component
|
||||||
context={context}
|
context={context}
|
||||||
onChange={onChange}
|
onChange={(argValue, event) => {
|
||||||
|
return onChange({
|
||||||
|
...value,
|
||||||
|
steps: [
|
||||||
|
...steps.slice(0, i),
|
||||||
|
{
|
||||||
|
...steps[i],
|
||||||
|
args: [
|
||||||
|
...steps[i].args.slice(0, j),
|
||||||
|
{
|
||||||
|
...steps[i].args[j],
|
||||||
|
...toBehaviorItem(argValue),
|
||||||
|
},
|
||||||
|
...steps[i].args.slice(j + 1),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...steps.slice(i + 1),
|
||||||
|
],
|
||||||
|
value: assignValue,
|
||||||
|
}, event);
|
||||||
|
}}
|
||||||
type={description.args[j][1].type}
|
type={description.args[j][1].type}
|
||||||
value={arg}
|
value={arg}
|
||||||
/>
|
/>
|
||||||
|
@ -83,11 +120,13 @@ const Traversal = (props) => {
|
||||||
<span className="op">=</span>
|
<span className="op">=</span>
|
||||||
<Value.Component
|
<Value.Component
|
||||||
context={context}
|
context={context}
|
||||||
onChange={(valueValue) => onChange({
|
onChange={(valueValue, event) => (
|
||||||
|
onChange({
|
||||||
...value,
|
...value,
|
||||||
steps,
|
steps,
|
||||||
value: toBehaviorItem(valueValue),
|
value: toBehaviorItem(valueValue),
|
||||||
})}
|
}, event)
|
||||||
|
)}
|
||||||
type={stepsType}
|
type={stepsType}
|
||||||
value={assignValue}
|
value={assignValue}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {Context} from '@avocado/behavior';
|
import {Context} from '@avocado/behavior';
|
||||||
|
import {Traversal} from '@avocado/behavior/item/traversal';
|
||||||
|
|
||||||
let digraph;
|
let digraph;
|
||||||
// eslint-disable-next-line no-return-assign
|
// eslint-disable-next-line no-return-assign
|
||||||
|
@ -17,7 +18,7 @@ export function isBehaviorItem(valueOrItem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toBehaviorItem(valueOrItem) {
|
export function toBehaviorItem(valueOrItem) {
|
||||||
return isBehaviorItem(valueOrItem) ? isBehaviorItem : {type: 'literal', value: valueOrItem};
|
return isBehaviorItem(valueOrItem) ? valueOrItem : {type: 'literal', value: valueOrItem};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function typeFromLiteral(value) {
|
export function typeFromLiteral(value) {
|
||||||
|
@ -42,33 +43,36 @@ export function typeFromLiteral(value) {
|
||||||
return 'object';
|
return 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function typeFromValue(v) {
|
|
||||||
return isBehaviorItem(v) ? v.type : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fakeContextDescription = (context) => (
|
const fakeContextDescription = (context) => (
|
||||||
Object.entries(context.all())
|
Object.entries(context.all())
|
||||||
.reduce((r, [key, tuple]) => ({...r, [key]: {type: tuple[1]}}), {})
|
.reduce((r, [key, tuple]) => ({...r, [key]: {type: tuple[1]}}), {})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const stepsValue = (context, steps) => (new Traversal({steps})).get(context);
|
||||||
|
|
||||||
export function descriptionFromSteps(context, steps) {
|
export function descriptionFromSteps(context, steps) {
|
||||||
|
let description = fakeContextDescription(context);
|
||||||
|
if (0 === steps.length) {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
const isInvocation = 'invoke' === steps[steps.length - 1].type;
|
const isInvocation = 'invoke' === steps[steps.length - 1].type;
|
||||||
const keyStepsCount = steps.length - (isInvocation ? 1 : 0);
|
const keyStepsCount = steps.length - (isInvocation ? 1 : 0);
|
||||||
let description = fakeContextDescription(context);
|
|
||||||
let variable;
|
let variable;
|
||||||
for (let i = 0; i < keyStepsCount; ++i) {
|
for (let i = 0; i < keyStepsCount; ++i) {
|
||||||
const {key} = steps[i];
|
const {key} = steps[i];
|
||||||
if (0 === i) {
|
variable = stepsValue(context, steps.slice(0, i + 1));
|
||||||
[variable] = context.get(key);
|
if ('function' === typeof variable && description[key].args) {
|
||||||
|
description = description[key];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
variable = 'object' === typeof variable ? variable[key] : undefined;
|
const candidateDescription = Context.typeDescription(
|
||||||
}
|
|
||||||
const lookupDesc = Context.typeDescription(
|
|
||||||
description[key].type,
|
description[key].type,
|
||||||
(isInvocation && keyStepsCount - 1 === i) ? undefined : variable,
|
'function' === description[key].type ? variable : undefined,
|
||||||
);
|
);
|
||||||
description = 0 !== Object.keys(lookupDesc).length ? lookupDesc : description[key];
|
description = 0 !== Object.keys(candidateDescription).length
|
||||||
|
? candidateDescription
|
||||||
|
: description[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
@ -77,26 +81,10 @@ export function typeFits(reference, candidate) {
|
||||||
return 'any' === reference || -1 !== (reference.split('|') || []).indexOf(candidate);
|
return 'any' === reference || -1 !== (reference.split('|') || []).indexOf(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stepsOptions(context, steps, type) {
|
const descriptionCandidates = (description, type) => {
|
||||||
const isInvocation = 'invoke' === steps[steps.length - 1].type;
|
|
||||||
const keyStepsCount = steps.length - (isInvocation ? 1 : 0);
|
|
||||||
const optionsList = [];
|
|
||||||
const inverted = cachedDigraph();
|
const inverted = cachedDigraph();
|
||||||
const candidates = (inverted[type] || []).concat(type);
|
const candidates = (inverted[type] || []).concat(type);
|
||||||
for (let i = 0; i < keyStepsCount; ++i) {
|
return 'any' === type
|
||||||
let description = fakeContextDescription(context);
|
|
||||||
let variable;
|
|
||||||
for (let j = 0; j < i; ++j) {
|
|
||||||
const {key} = steps[j];
|
|
||||||
if (0 === i) {
|
|
||||||
[variable] = context.get(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
variable = 'object' === typeof variable ? variable[key] : undefined;
|
|
||||||
}
|
|
||||||
description = Context.typeDescription(description[key].type, variable);
|
|
||||||
}
|
|
||||||
const options = 'any' === type
|
|
||||||
? Object.keys(description)
|
? Object.keys(description)
|
||||||
: Object.entries(description)
|
: Object.entries(description)
|
||||||
.reduce((r, [key, spec]) => (
|
.reduce((r, [key, spec]) => (
|
||||||
|
@ -104,7 +92,25 @@ export function stepsOptions(context, steps, type) {
|
||||||
? r.concat(key)
|
? r.concat(key)
|
||||||
: r
|
: r
|
||||||
), []);
|
), []);
|
||||||
optionsList.push(options);
|
};
|
||||||
|
|
||||||
|
export function stepsOptions(context, steps, type) {
|
||||||
|
const optionsList = [
|
||||||
|
descriptionCandidates(
|
||||||
|
fakeContextDescription(context),
|
||||||
|
type,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
if (0 === steps.length) {
|
||||||
|
return optionsList;
|
||||||
|
}
|
||||||
|
const isInvocation = 'invoke' === steps[steps.length - 1].type;
|
||||||
|
const keyStepsCount = steps.length - (isInvocation ? 1 : 0);
|
||||||
|
for (let i = 1; i < keyStepsCount; ++i) {
|
||||||
|
optionsList.push(descriptionCandidates(
|
||||||
|
descriptionFromSteps(context, steps.slice(0, i)),
|
||||||
|
type,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
return optionsList;
|
return optionsList;
|
||||||
}
|
}
|
||||||
|
@ -128,3 +134,65 @@ export function typeFromSteps(context, steps) {
|
||||||
const [, type] = steps.slice(1).reduce(stepTo, context.get(steps[0].key));
|
const [, type] = steps.slice(1).reduce(stepTo, context.get(steps[0].key));
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultSteps = (context, type, originalSteps = []) => {
|
||||||
|
const steps = [...originalSteps];
|
||||||
|
let stepsType = typeFromSteps(context, originalSteps);
|
||||||
|
const typesVisited = {[stepsType]: true};
|
||||||
|
while (!typeFits(type, stepsType) || 0 === steps.length) {
|
||||||
|
let variable = steps.length > 0 ? (new Traversal({steps})).get(context) : undefined;
|
||||||
|
if ('function' === typeof variable) {
|
||||||
|
if ('function' === type) {
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
const funcDescription = descriptionFromSteps(context, steps);
|
||||||
|
if (typeFits(type, funcDescription.type)) {
|
||||||
|
steps.push({
|
||||||
|
type: 'invoke',
|
||||||
|
args: funcDescription.args.map(() => ({
|
||||||
|
type: 'literal',
|
||||||
|
value: '',
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
stepsType = typeFromSteps(context, originalSteps);
|
||||||
|
variable = stepsValue(context, steps);
|
||||||
|
}
|
||||||
|
const description = steps.length > 0
|
||||||
|
? Context.typeDescription(stepsType, variable)
|
||||||
|
: fakeContextDescription(context);
|
||||||
|
const candidates = descriptionCandidates(description, type);
|
||||||
|
const key = candidates.find((candidate) => {
|
||||||
|
const {type: candidateType} = description[candidate];
|
||||||
|
if (typesVisited[candidateType]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeFits(type, candidateType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
typesVisited[candidateType] = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
steps.push({type: 'key', key});
|
||||||
|
stepsType = typeFromSteps(context, originalSteps);
|
||||||
|
variable = stepsValue(context, steps);
|
||||||
|
if ('function' === typeof variable) {
|
||||||
|
const funcDescription = descriptionFromSteps(context, steps);
|
||||||
|
steps.push({
|
||||||
|
type: 'invoke',
|
||||||
|
args: funcDescription.args.map(() => ({
|
||||||
|
type: 'literal',
|
||||||
|
value: '',
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
stepsType = typeFromSteps(context, steps);
|
||||||
|
}
|
||||||
|
return steps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function typeFromValue(v) {
|
||||||
|
return isBehaviorItem(v) ? v.type : undefined;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user