mirror of
https://github.com/yldio/copilot.git
synced 2024-11-28 14:10:04 +02:00
8295bd6882
this shall be a progressive process
134 lines
3.0 KiB
JavaScript
134 lines
3.0 KiB
JavaScript
import { load, Kind } from 'yaml-ast-parser';
|
|
import isUndefined from 'lodash.isundefined';
|
|
import isNull from 'lodash.isnull';
|
|
import hasOwnProp from 'has-own-prop';
|
|
|
|
export const loc = Symbol('pseudo-yaml-ast-loc');
|
|
|
|
const isPrimitive = v =>
|
|
Number.isNaN(v) || isNull(v) || isUndefined(v) || typeof v === 'symbol';
|
|
|
|
const isPrimitiveNode = node =>
|
|
isPrimitive(node.value) || !hasOwnProp(node, 'value');
|
|
|
|
const getLoc = (input, { start = 0, end = 0 }) => {
|
|
const lines = input.split(/\n/);
|
|
|
|
const loc = {
|
|
start: {},
|
|
end: {}
|
|
};
|
|
|
|
const isBetween = (start, pos, end) => pos <= end && pos >= start;
|
|
|
|
let sum = 0;
|
|
|
|
for (const i of lines.keys()) {
|
|
const line = lines[i];
|
|
const ls = sum;
|
|
const le = sum + line.length + 1; // +1 because the break is also a char
|
|
|
|
if (isUndefined(loc.start.line) && isBetween(ls, start, le)) {
|
|
loc.start.line = i + 1;
|
|
loc.start.column = start - ls;
|
|
}
|
|
|
|
if (isUndefined(loc.end.line) && isBetween(ls, end, le)) {
|
|
loc.end.line = i + 1;
|
|
loc.end.column = end - ls;
|
|
}
|
|
|
|
sum = le;
|
|
}
|
|
|
|
return loc;
|
|
};
|
|
|
|
const visitors = {
|
|
MAP: (node = {}, input = '', ctx = {}) =>
|
|
Object.assign(walk(node.mappings, input), {
|
|
[loc]: getLoc(input, {
|
|
start: node.startPosition,
|
|
end: node.endPosition
|
|
})
|
|
}),
|
|
MAPPING: (node = {}, input = '', ctx = {}) => {
|
|
const value = walk([node.value], input);
|
|
|
|
if (!isPrimitive(value)) {
|
|
value[loc] = getLoc(input, {
|
|
start: node.startPosition,
|
|
end: node.endPosition
|
|
});
|
|
}
|
|
|
|
return Object.assign(ctx, {
|
|
[node.key.value]: value
|
|
});
|
|
},
|
|
SCALAR: (node = {}, input = '') => {
|
|
if (isPrimitiveNode(node)) {
|
|
return node.value;
|
|
}
|
|
|
|
const _loc = getLoc(input, {
|
|
start: node.startPosition,
|
|
end: node.endPosition
|
|
});
|
|
|
|
const wrappable = Constructor => () => {
|
|
const v = new Constructor(node.value);
|
|
v[loc] = _loc;
|
|
return v;
|
|
};
|
|
|
|
const object = () => {
|
|
node.value[loc] = _loc;
|
|
return node.value;
|
|
};
|
|
|
|
const types = {
|
|
boolean: wrappable(Boolean),
|
|
number: wrappable(Number),
|
|
string: wrappable(String),
|
|
function: object,
|
|
object
|
|
};
|
|
|
|
return types[typeof node.value]();
|
|
},
|
|
SEQ: (node = {}, input = '') => {
|
|
const items = walk(node.items, input, []);
|
|
|
|
items[loc] = getLoc(input, {
|
|
start: node.startPosition,
|
|
end: node.endPosition
|
|
});
|
|
|
|
return items;
|
|
}
|
|
};
|
|
|
|
const walk = (nodes = [], input, ctx = {}) => {
|
|
const onNode = (node, ctx, fallback) => {
|
|
const visitor = visitors[Kind[node.kind]];
|
|
return visitor ? visitor(node, input, ctx) : fallback;
|
|
};
|
|
|
|
const walkObj = () =>
|
|
nodes.reduce((sum, node) => {
|
|
return onNode(node, sum, sum);
|
|
}, ctx);
|
|
|
|
const walkArr = () =>
|
|
nodes
|
|
.map(node => {
|
|
return onNode(node, ctx, null);
|
|
}, ctx)
|
|
.filter(Boolean);
|
|
|
|
return Array.isArray(ctx) ? walkArr() : walkObj();
|
|
};
|
|
|
|
export default input => walk([load(input)], input);
|