volar-service-pug
Version:
Integrate Pug into Volar
276 lines • 10.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.baseParse = baseParse;
const muggle_string_1 = require("muggle-string");
const pugLex = require("pug-lexer");
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const buildMappings_1 = require("./buildMappings");
const pugParser = require('pug-parser');
function baseParse(pugCode) {
const fileName = 'foo.pug';
const pugTextDocument = vscode_languageserver_textdocument_1.TextDocument.create('file:///a.pug', 'jade', 0, pugCode);
const codes = [];
let error;
let emptyLineEnds = [];
let attrsBlocks;
let ast;
try {
const tokens = pugLex(pugCode, { filename: fileName });
emptyLineEnds = collectEmptyLineEnds(tokens);
attrsBlocks = collectAttrsBlocks(tokens);
ast = pugParser(tokens, { filename: fileName, src: pugCode });
visitNode(ast, undefined, undefined);
codes.push([
'',
undefined,
pugCode.trimEnd().length,
]);
}
catch (e) {
const _error = e;
error = {
..._error,
line: _error.line - 1,
column: _error.column - 1,
};
}
;
return {
htmlCode: (0, muggle_string_1.toString)(codes),
mappings: (0, buildMappings_1.buildMappings)(codes),
pugTextDocument,
error,
ast,
emptyLineEnds,
};
function visitNode(node, next, parent) {
if (node.type === 'Block') {
for (let i = 0; i < node.nodes.length; i++) {
visitNode(node.nodes[i], node.nodes[i + 1], node);
}
}
else if (node.type === 'Mixin') {
if (node.block) {
visitNode(node.block, undefined, node);
}
}
else if (node.type === 'Tag') {
const pugTagRange = getDocRange(node.line, node.column, node.name.length);
codes.push([
'',
undefined,
pugTagRange.start,
]);
const selfClosing = node.block.nodes.length === 0;
addStartTag(node, selfClosing);
if (!selfClosing) {
visitNode(node.block, next, parent);
addEndTag(node, next, parent);
}
codes.push([
'',
undefined,
pugTagRange.start,
]);
}
else if (node.type === 'Text') {
codes.push([
node.val,
undefined,
getDocOffset(node.line, node.column),
]);
}
}
function addStartTag(node, selfClosing) {
codes.push([
'',
undefined,
getDocOffset(node.line, node.column),
]);
codes.push('<');
const tagRange = getDocRange(node.line, node.column, node.name.length);
if (pugCode.substring(tagRange.start, tagRange.end) === node.name) {
codes.push([
node.name,
undefined,
tagRange.start,
]);
}
else {
codes.push(node.name);
}
const noTitleAttrs = node.attrs.filter(attr => !attr.mustEscape && attr.name !== 'class');
const noTitleClassAttrs = node.attrs.filter(attr => !attr.mustEscape && attr.name === 'class');
const attrsBlock = attrsBlocks.get(getDocOffset(node.line, node.column)); // support attr auto-complete in spaces
const hasClassAttr = attrsBlock && attrsBlock.text.match(/\bclass\b\s*=/i);
if (!hasClassAttr) {
addClassesOrStyles(noTitleClassAttrs, 'class');
}
for (const attr of noTitleAttrs) {
codes.push(' ');
codes.push(attr.name);
if (typeof attr.val !== 'boolean') {
codes.push('=');
codes.push([
attr.val,
undefined,
getDocOffset(attr.line, attr.column),
]);
}
}
if (attrsBlock) {
codes.push(' ');
codes.push([
attrsBlock.text,
undefined,
attrsBlock.offset,
]);
}
if (selfClosing) {
codes.push(' />');
}
else {
codes.push('>');
}
}
function addEndTag(node, next, parent) {
let nextStart;
if (next) {
if (next.type === 'Block') {
nextStart = getDocOffset(next.line, 1);
}
else {
nextStart = getDocOffset(next.line, next.column);
}
}
else if (!parent) {
nextStart = pugCode.length;
}
if (nextStart !== undefined) {
codes.push([
'',
undefined,
nextStart,
]);
}
codes.push(`</${node.name}>`);
}
function addClassesOrStyles(attrs, attrName) {
if (!attrs.length) {
return;
}
codes.push(' ');
codes.push(attrName);
codes.push('=');
codes.push('"');
for (const attr of attrs) {
if (typeof attr.val !== 'boolean') {
codes.push(' ');
codes.push([
attr.val.slice(1, -1), // remove "
undefined,
getDocOffset(attr.line, attr.column + 1),
]);
}
}
codes.push('"');
}
function collectEmptyLineEnds(tokens) {
const ends = [];
for (const token of tokens) {
if (token.type === 'newline' || token.type === 'outdent') {
let currentLine = token.loc.start.line - 2;
let prevLine = getLineText(pugTextDocument, currentLine);
while (prevLine.trim() === '') {
ends.push(pugTextDocument.offsetAt({ line: currentLine + 1, character: 0 }) - 1);
if (currentLine <= 0) {
break;
}
currentLine--;
prevLine = getLineText(pugTextDocument, currentLine);
}
}
}
return ends.sort((a, b) => a - b);
}
function collectAttrsBlocks(tokens) {
const blocks = new Map();
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token.type === 'start-attributes') {
let tagStart = token;
for (let j = i - 1; j >= 0; j--) {
const prevToken = tokens[j];
if (prevToken.type === 'newline'
|| prevToken.type === 'indent'
|| prevToken.type === 'outdent'
|| prevToken.type === ':') {
break;
}
tagStart = prevToken;
if (prevToken.type === 'tag') {
break;
}
}
let prevToken = token;
let text = '';
for (i++; i < tokens.length; i++) {
const attrToken = tokens[i];
addPrevSpace(attrToken);
if (attrToken.type === 'attribute') {
let attrText = pugCode.substring(getDocOffset(attrToken.loc.start.line, attrToken.loc.start.column), getDocOffset(attrToken.loc.end.line, attrToken.loc.end.column));
if (typeof attrToken.val === 'string' && attrText.indexOf('=') >= 0) {
let valText = attrToken.val;
if (valText.startsWith('`') && valText.endsWith('`')) {
if (valText.indexOf('"') === -1) {
valText = `"${valText.slice(1, -1)}"`;
}
else {
valText = `'${valText.slice(1, -1)}'`;
}
}
valText = valText.replace(/ \\\n/g, '//\n');
text += attrText.substring(0, attrText.lastIndexOf(attrToken.val)) + valText;
}
else {
text += attrText;
}
}
else if (attrToken.type === 'end-attributes') {
blocks.set(getDocOffset(tagStart.loc.start.line, tagStart.loc.start.column), {
offset: getDocOffset(token.loc.end.line, token.loc.end.column),
text,
});
break;
}
prevToken = attrToken;
}
function addPrevSpace(currentToken) {
text += pugCode.substring(getDocOffset(prevToken.loc.end.line, prevToken.loc.end.column), getDocOffset(currentToken.loc.start.line, currentToken.loc.start.column)).replace(/,/g, '\n');
}
}
}
return blocks;
}
function getDocOffset(pugLine, pugColumn) {
return pugTextDocument.offsetAt({ line: pugLine - 1, character: pugColumn - 1 });
}
function getDocRange(pugLine, pugColumn, length) {
const start = getDocOffset(pugLine, pugColumn);
const end = start + length;
return {
start,
end,
};
}
}
function getLineText(document, line) {
const endOffset = document.offsetAt({ line: line + 1, character: 0 });
const end = document.positionAt(endOffset);
const text = document.getText({
start: { line: line, character: 0 },
end: end.line === line ? end : document.positionAt(endOffset - 1),
});
return text;
}
//# sourceMappingURL=baseParse.js.map