You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
4.9 KiB
134 lines
4.9 KiB
function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) { |
|
let spaceBefore = false; |
|
let atNewline = startOnNewline; |
|
let hasSpace = startOnNewline; |
|
let comment = ''; |
|
let commentSep = ''; |
|
let hasNewline = false; |
|
let hasNewlineAfterProp = false; |
|
let reqSpace = false; |
|
let anchor = null; |
|
let tag = null; |
|
let comma = null; |
|
let found = null; |
|
let start = null; |
|
for (const token of tokens) { |
|
if (reqSpace) { |
|
if (token.type !== 'space' && |
|
token.type !== 'newline' && |
|
token.type !== 'comma') |
|
onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space'); |
|
reqSpace = false; |
|
} |
|
switch (token.type) { |
|
case 'space': |
|
// At the doc level, tabs at line start may be parsed |
|
// as leading white space rather than indentation. |
|
// In a flow collection, only the parser handles indent. |
|
if (!flow && |
|
atNewline && |
|
indicator !== 'doc-start' && |
|
token.source[0] === '\t') |
|
onError(token, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation'); |
|
hasSpace = true; |
|
break; |
|
case 'comment': { |
|
if (!hasSpace) |
|
onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters'); |
|
const cb = token.source.substring(1) || ' '; |
|
if (!comment) |
|
comment = cb; |
|
else |
|
comment += commentSep + cb; |
|
commentSep = ''; |
|
atNewline = false; |
|
break; |
|
} |
|
case 'newline': |
|
if (atNewline) { |
|
if (comment) |
|
comment += token.source; |
|
else |
|
spaceBefore = true; |
|
} |
|
else |
|
commentSep += token.source; |
|
atNewline = true; |
|
hasNewline = true; |
|
if (anchor || tag) |
|
hasNewlineAfterProp = true; |
|
hasSpace = true; |
|
break; |
|
case 'anchor': |
|
if (anchor) |
|
onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor'); |
|
if (token.source.endsWith(':')) |
|
onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true); |
|
anchor = token; |
|
if (start === null) |
|
start = token.offset; |
|
atNewline = false; |
|
hasSpace = false; |
|
reqSpace = true; |
|
break; |
|
case 'tag': { |
|
if (tag) |
|
onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag'); |
|
tag = token; |
|
if (start === null) |
|
start = token.offset; |
|
atNewline = false; |
|
hasSpace = false; |
|
reqSpace = true; |
|
break; |
|
} |
|
case indicator: |
|
// Could here handle preceding comments differently |
|
if (anchor || tag) |
|
onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`); |
|
if (found) |
|
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`); |
|
found = token; |
|
atNewline = false; |
|
hasSpace = false; |
|
break; |
|
case 'comma': |
|
if (flow) { |
|
if (comma) |
|
onError(token, 'UNEXPECTED_TOKEN', `Unexpected , in ${flow}`); |
|
comma = token; |
|
atNewline = false; |
|
hasSpace = false; |
|
break; |
|
} |
|
// else fallthrough |
|
default: |
|
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.type} token`); |
|
atNewline = false; |
|
hasSpace = false; |
|
} |
|
} |
|
const last = tokens[tokens.length - 1]; |
|
const end = last ? last.offset + last.source.length : offset; |
|
if (reqSpace && |
|
next && |
|
next.type !== 'space' && |
|
next.type !== 'newline' && |
|
next.type !== 'comma' && |
|
(next.type !== 'scalar' || next.source !== '')) |
|
onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space'); |
|
return { |
|
comma, |
|
found, |
|
spaceBefore, |
|
comment, |
|
hasNewline, |
|
hasNewlineAfterProp, |
|
anchor, |
|
tag, |
|
end, |
|
start: start ?? end |
|
}; |
|
} |
|
|
|
export { resolveProps };
|
|
|