fix: typing
This commit is contained in:
parent
e4ac23c75f
commit
a8702b190c
|
@ -5,7 +5,7 @@ import React from 'react';
|
||||||
import useTypeRenderers from '~/client/hooks/useTypeRenderers';
|
import useTypeRenderers from '~/client/hooks/useTypeRenderers';
|
||||||
|
|
||||||
import propTypes from './prop-types';
|
import propTypes from './prop-types';
|
||||||
import {stepOptions, typeFromLiteral} from './typing';
|
import {stepsOptions, typeFromLiteral} from './typing';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
contempo(require('./literal.raw.scss')),
|
contempo(require('./literal.raw.scss')),
|
||||||
|
@ -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 = stepOptions(context, [], 0, type);
|
const [options] = stepsOptions(context, [{type: 'key'}], type);
|
||||||
options.push('<literal>');
|
options.push('<literal>');
|
||||||
return (
|
return (
|
||||||
<div className="literal">
|
<div className="literal">
|
||||||
|
|
|
@ -5,7 +5,7 @@ import React from 'react';
|
||||||
import Value from '~/client/value';
|
import Value from '~/client/value';
|
||||||
|
|
||||||
import propTypes from './prop-types';
|
import propTypes from './prop-types';
|
||||||
import {descriptionFromSteps, stepOptions, typeFromSteps} from './typing';
|
import {descriptionFromSteps, stepsOptions, typeFromSteps} from './typing';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
contempo(require('./traversal.raw.scss')),
|
contempo(require('./traversal.raw.scss')),
|
||||||
|
@ -19,6 +19,7 @@ const Traversal = (props) => {
|
||||||
value: {steps, value},
|
value: {steps, value},
|
||||||
} = props;
|
} = props;
|
||||||
const stepsType = type || typeFromSteps(context, steps);
|
const stepsType = type || typeFromSteps(context, steps);
|
||||||
|
const optionsList = stepsOptions(context, steps, stepsType);
|
||||||
return (
|
return (
|
||||||
<div className="traversal">
|
<div className="traversal">
|
||||||
{steps.map((step, i) => (
|
{steps.map((step, i) => (
|
||||||
|
@ -28,7 +29,7 @@ const Traversal = (props) => {
|
||||||
(() => {
|
(() => {
|
||||||
switch (step.type) {
|
switch (step.type) {
|
||||||
case 'key': {
|
case 'key': {
|
||||||
const options = stepOptions(context, steps, i, stepsType);
|
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 readOnly value={step.key}>
|
||||||
|
@ -42,7 +43,7 @@ const Traversal = (props) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'invoke': {
|
case 'invoke': {
|
||||||
const description = descriptionFromSteps(context, steps.slice(0, i + 1));
|
const description = descriptionFromSteps(context, steps);
|
||||||
return (
|
return (
|
||||||
<div className="invoke">
|
<div className="invoke">
|
||||||
<span className="paren open">(</span>
|
<span className="paren open">(</span>
|
||||||
|
|
|
@ -46,6 +46,59 @@ export function typeFromValue(v) {
|
||||||
return isBehaviorItem(v) ? v.type : undefined;
|
return isBehaviorItem(v) ? v.type : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fakeContextDescription = (context) => (
|
||||||
|
Object.entries(context.all())
|
||||||
|
.reduce((r, [key, tuple]) => ({...r, [key]: {type: tuple[1]}}), {})
|
||||||
|
);
|
||||||
|
|
||||||
|
export function descriptionFromSteps(context, steps) {
|
||||||
|
const isInvocation = 'invoke' === steps[steps.length - 1].type;
|
||||||
|
const keyStepsCount = steps.length - (isInvocation ? 1 : 0);
|
||||||
|
let description = fakeContextDescription(context);
|
||||||
|
let variable;
|
||||||
|
for (let i = 0; i < keyStepsCount; ++i) {
|
||||||
|
const {key} = steps[i];
|
||||||
|
variable = 0 === i ? context.get(key)[0] : variable[key];
|
||||||
|
const lookupDesc = Context.typeDescription(
|
||||||
|
description[key].type,
|
||||||
|
(isInvocation && keyStepsCount - 1 === i) ? undefined : variable,
|
||||||
|
);
|
||||||
|
description = 0 !== Object.keys(lookupDesc).length ? lookupDesc : description[key];
|
||||||
|
}
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function typeFits(reference, candidate) {
|
||||||
|
return 'any' === reference || -1 !== (reference.split('|') || []).indexOf(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stepsOptions(context, steps, type) {
|
||||||
|
const isInvocation = 'invoke' === steps[steps.length - 1].type;
|
||||||
|
const keyStepsCount = steps.length - (isInvocation ? 1 : 0);
|
||||||
|
const optionsList = [];
|
||||||
|
const inverted = cachedDigraph();
|
||||||
|
const candidates = (inverted[type] || []).concat(type);
|
||||||
|
for (let i = 0; i < keyStepsCount; ++i) {
|
||||||
|
let description = fakeContextDescription(context);
|
||||||
|
let variable;
|
||||||
|
for (let j = 0; j < i; ++j) {
|
||||||
|
const {key} = steps[j];
|
||||||
|
variable = 0 === j ? context.get(key)[0] : variable[key];
|
||||||
|
description = Context.typeDescription(description[key].type, variable);
|
||||||
|
}
|
||||||
|
const options = 'any' === type
|
||||||
|
? Object.keys(description)
|
||||||
|
: Object.entries(description)
|
||||||
|
.reduce((r, [key, spec]) => (
|
||||||
|
candidates.find((candidate) => typeFits(candidate, spec.type))
|
||||||
|
? r.concat(key)
|
||||||
|
: r
|
||||||
|
), []);
|
||||||
|
optionsList.push(options);
|
||||||
|
}
|
||||||
|
return optionsList;
|
||||||
|
}
|
||||||
|
|
||||||
const stepTo = ([variable, type], step) => {
|
const stepTo = ([variable, type], step) => {
|
||||||
const {key} = step;
|
const {key} = step;
|
||||||
if (key) {
|
if (key) {
|
||||||
|
@ -58,50 +111,6 @@ const stepTo = ([variable, type], step) => {
|
||||||
return [variable, type];
|
return [variable, type];
|
||||||
};
|
};
|
||||||
|
|
||||||
const fakeContextDescription = (context) => (
|
|
||||||
Object.entries(context.all())
|
|
||||||
.reduce((r, [key, tuple]) => ({...r, [key]: {type: tuple[1]}}), {})
|
|
||||||
);
|
|
||||||
|
|
||||||
export function descriptionFromSteps(context, steps) {
|
|
||||||
if (!steps || 0 === steps.length) {
|
|
||||||
return fakeContextDescription(context);
|
|
||||||
}
|
|
||||||
const max = Math.max(0, steps.length - 2);
|
|
||||||
const [variable, type] = steps.slice(0, max).reduce(stepTo, context.get(steps[0].key));
|
|
||||||
const description = Context.typeDescription(type, variable);
|
|
||||||
return 0 === max ? description : description[steps[max].key];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function typeFits(reference, candidate) {
|
|
||||||
return 'any' === reference || -1 !== (reference.split('|') || []).indexOf(candidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stepOptions(context, steps, i, type) {
|
|
||||||
let description;
|
|
||||||
if (0 === i) {
|
|
||||||
description = descriptionFromSteps(context, []);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
description = descriptionFromSteps(context, steps.slice(0, i));
|
|
||||||
}
|
|
||||||
let options;
|
|
||||||
if ('any' !== type) {
|
|
||||||
const inverted = cachedDigraph();
|
|
||||||
const candidates = (inverted[type] || []).concat(type);
|
|
||||||
options = Object.entries(description)
|
|
||||||
.reduce((r, [key, spec]) => (
|
|
||||||
candidates.find((candidate) => typeFits(candidate, spec.type))
|
|
||||||
? r.concat(key)
|
|
||||||
: r
|
|
||||||
), []);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
options = Object.keys(description);
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function typeFromSteps(context, steps) {
|
export function typeFromSteps(context, steps) {
|
||||||
if (!steps || 0 === steps.length) {
|
if (!steps || 0 === steps.length) {
|
||||||
return 'undefined';
|
return 'undefined';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user