graphql-language-service
Version:
The official, runtime independent Language Service for GraphQL
211 lines • 6.59 kB
JavaScript
import { LexRules, ParseRules, isIgnored } from './Rules';
import { Kind } from 'graphql';
export default function onlineParser(options = {
eatWhitespace: stream => stream.eatWhile(isIgnored),
lexRules: LexRules,
parseRules: ParseRules,
editorConfig: {},
}) {
return {
startState() {
const initialState = {
level: 0,
step: 0,
name: null,
kind: null,
type: null,
rule: null,
needsSeparator: false,
prevState: null,
};
pushRule(options.parseRules, initialState, Kind.DOCUMENT);
return initialState;
},
token(stream, state) {
return getToken(stream, state, options);
},
};
}
function getToken(stream, state, options) {
var _a;
if (state.inBlockstring) {
if (stream.match(/.*"""/)) {
state.inBlockstring = false;
return 'string';
}
stream.skipToEnd();
return 'string';
}
const { lexRules, parseRules, eatWhitespace, editorConfig } = options;
if (state.rule && state.rule.length === 0) {
popRule(state);
}
else if (state.needsAdvance) {
state.needsAdvance = false;
advanceRule(state, true);
}
if (stream.sol()) {
const tabSize = (editorConfig === null || editorConfig === void 0 ? void 0 : editorConfig.tabSize) || 2;
state.indentLevel = Math.floor(stream.indentation() / tabSize);
}
if (eatWhitespace(stream)) {
return 'ws';
}
const token = lex(lexRules, stream);
if (!token) {
const matchedSomething = stream.match(/\S+/);
if (!matchedSomething) {
stream.match(/\s/);
}
pushRule(SpecialParseRules, state, 'Invalid');
return 'invalidchar';
}
if (token.kind === 'Comment') {
pushRule(SpecialParseRules, state, 'Comment');
return 'comment';
}
const backupState = assign({}, state);
if (token.kind === 'Punctuation') {
if (/^[{([]/.test(token.value)) {
if (state.indentLevel !== undefined) {
state.levels = (state.levels || []).concat(state.indentLevel + 1);
}
}
else if (/^[})\]]/.test(token.value)) {
const levels = (state.levels = (state.levels || []).slice(0, -1));
if (state.indentLevel &&
levels.length > 0 &&
levels.at(-1) < state.indentLevel) {
state.indentLevel = levels.at(-1);
}
}
}
while (state.rule) {
let expected = typeof state.rule === 'function'
? state.step === 0
? state.rule(token, stream)
: null
: state.rule[state.step];
if (state.needsSeparator) {
expected = expected === null || expected === void 0 ? void 0 : expected.separator;
}
if (expected) {
if (expected.ofRule) {
expected = expected.ofRule;
}
if (typeof expected === 'string') {
pushRule(parseRules, state, expected);
continue;
}
if ((_a = expected.match) === null || _a === void 0 ? void 0 : _a.call(expected, token)) {
if (expected.update) {
expected.update(state, token);
}
if (token.kind === 'Punctuation') {
advanceRule(state, true);
}
else {
state.needsAdvance = true;
}
return expected.style;
}
}
unsuccessful(state);
}
assign(state, backupState);
pushRule(SpecialParseRules, state, 'Invalid');
return 'invalidchar';
}
function assign(to, from) {
const keys = Object.keys(from);
for (let i = 0; i < keys.length; i++) {
to[keys[i]] = from[keys[i]];
}
return to;
}
const SpecialParseRules = {
Invalid: [],
Comment: [],
};
function pushRule(rules, state, ruleKind) {
if (!rules[ruleKind]) {
throw new TypeError('Unknown rule: ' + ruleKind);
}
state.prevState = Object.assign({}, state);
state.kind = ruleKind;
state.name = null;
state.type = null;
state.rule = rules[ruleKind];
state.step = 0;
state.needsSeparator = false;
}
function popRule(state) {
if (!state.prevState) {
return;
}
state.kind = state.prevState.kind;
state.name = state.prevState.name;
state.type = state.prevState.type;
state.rule = state.prevState.rule;
state.step = state.prevState.step;
state.needsSeparator = state.prevState.needsSeparator;
state.prevState = state.prevState.prevState;
}
function advanceRule(state, successful) {
var _a;
if (isList(state) && state.rule) {
const step = state.rule[state.step];
if (step.separator) {
const { separator } = step;
state.needsSeparator = !state.needsSeparator;
if (!state.needsSeparator && separator.ofRule) {
return;
}
}
if (successful) {
return;
}
}
state.needsSeparator = false;
state.step++;
while (state.rule &&
!(Array.isArray(state.rule) && state.step < state.rule.length)) {
popRule(state);
if (state.rule) {
if (isList(state)) {
if ((_a = state.rule) === null || _a === void 0 ? void 0 : _a[state.step].separator) {
state.needsSeparator = !state.needsSeparator;
}
}
else {
state.needsSeparator = false;
state.step++;
}
}
}
}
function isList(state) {
const step = Array.isArray(state.rule) &&
typeof state.rule[state.step] !== 'string' &&
state.rule[state.step];
return step && step.isList;
}
function unsuccessful(state) {
while (state.rule &&
!(Array.isArray(state.rule) && state.rule[state.step].ofRule)) {
popRule(state);
}
if (state.rule) {
advanceRule(state, false);
}
}
function lex(lexRules, stream) {
const kinds = Object.keys(lexRules);
for (let i = 0; i < kinds.length; i++) {
const match = stream.match(lexRules[kinds[i]]);
if (match && match instanceof Array) {
return { kind: kinds[i], value: match[0] };
}
}
}
//# sourceMappingURL=onlineParser.js.map