svelte-language-server
Version:
A language server for Svelte
160 lines • 6.79 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseHtml = parseHtml;
exports.getAttributeContextAtPosition = getAttributeContextAtPosition;
const vscode_html_languageservice_1 = require("vscode-html-languageservice");
const utils_1 = require("./utils");
const parser = (0, vscode_html_languageservice_1.getLanguageService)();
/**
* Parses text as HTML
*/
function parseHtml(text) {
const preprocessed = preprocess(text);
// We can safely only set getText because only this is used for parsing
const parsedDoc = parser.parseHTMLDocument({ getText: () => preprocessed });
return parsedDoc;
}
const createScanner = parser.createScanner;
/**
* scan the text and remove any `>` or `<` that cause the tag to end short,
*/
function preprocess(text) {
let scanner = createScanner(text);
let token = scanner.scan();
let currentStartTagStart = null;
let moustacheCheckStart = 0;
let moustacheCheckEnd = 0;
let lastToken = token;
while (token !== vscode_html_languageservice_1.TokenType.EOS) {
const offset = scanner.getTokenOffset();
let blanked = false;
switch (token) {
case vscode_html_languageservice_1.TokenType.StartTagOpen:
if (shouldBlankStartOrEndTagLike(offset)) {
blankStartOrEndTagLike(offset);
blanked = true;
}
else {
currentStartTagStart = offset;
}
break;
case vscode_html_languageservice_1.TokenType.StartTagClose:
if (shouldBlankStartOrEndTagLike(offset)) {
blankStartOrEndTagLike(offset);
blanked = true;
}
else {
currentStartTagStart = null;
}
break;
case vscode_html_languageservice_1.TokenType.StartTagSelfClose:
currentStartTagStart = null;
break;
// <Foo checked={a < 1}>
// https://github.com/microsoft/vscode-html-languageservice/blob/71806ef57be07e1068ee40900ef8b0899c80e68a/src/parser/htmlScanner.ts#L327
case vscode_html_languageservice_1.TokenType.Unknown:
if (scanner.getScannerState() === vscode_html_languageservice_1.ScannerState.WithinTag &&
scanner.getTokenText() === '<' &&
shouldBlankStartOrEndTagLike(offset)) {
blankStartOrEndTagLike(offset);
blanked = true;
}
break;
case vscode_html_languageservice_1.TokenType.Content: {
moustacheCheckEnd = scanner.getTokenEnd();
if (token !== lastToken) {
moustacheCheckStart = offset;
}
break;
}
}
// blanked, so the token type is invalid
if (!blanked) {
lastToken = token;
}
token = scanner.scan();
}
return text;
function shouldBlankStartOrEndTagLike(offset) {
if (currentStartTagStart != null) {
return (0, utils_1.isInsideMoustacheTag)(text, currentStartTagStart, offset);
}
const index = text
.substring(moustacheCheckStart, moustacheCheckEnd)
.lastIndexOf('{', offset);
const lastMustacheTagStart = index === -1 ? null : moustacheCheckStart + index;
if (lastMustacheTagStart == null) {
return false;
}
return (0, utils_1.isInsideMoustacheTag)(text.substring(lastMustacheTagStart), null, offset - lastMustacheTagStart);
}
function blankStartOrEndTagLike(offset) {
text = text.substring(0, offset) + ' ' + text.substring(offset + 1);
scanner = createScanner(text, offset, currentStartTagStart != null ? vscode_html_languageservice_1.ScannerState.WithinTag : vscode_html_languageservice_1.ScannerState.WithinContent);
}
}
function getAttributeContextAtPosition(document, position) {
const offset = document.offsetAt(position);
const { html } = document;
const tag = html.findNodeAt(offset);
if (!inStartTag(offset, tag) || !tag.attributes) {
return null;
}
const text = document.getText();
const beforeStartTagEnd = text.substring(0, tag.start) + preprocess(text.substring(tag.start, tag.startTagEnd));
const scanner = createScanner(beforeStartTagEnd, tag.start);
let token = scanner.scan();
let currentAttributeName;
const inTokenRange = () => scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd();
while (token != vscode_html_languageservice_1.TokenType.EOS) {
// adopted from https://github.com/microsoft/vscode-html-languageservice/blob/2f7ae4df298ac2c299a40e9024d118f4a9dc0c68/src/services/htmlCompletion.ts#L402
if (token === vscode_html_languageservice_1.TokenType.AttributeName) {
currentAttributeName = scanner.getTokenText();
if (inTokenRange()) {
return {
elementTag: tag,
name: currentAttributeName,
inValue: false
};
}
}
else if (token === vscode_html_languageservice_1.TokenType.DelimiterAssign) {
if (scanner.getTokenEnd() === offset && currentAttributeName) {
const nextToken = scanner.scan();
return {
elementTag: tag,
name: currentAttributeName,
inValue: true,
valueRange: [
offset,
nextToken === vscode_html_languageservice_1.TokenType.AttributeValue ? scanner.getTokenEnd() : offset
]
};
}
}
else if (token === vscode_html_languageservice_1.TokenType.AttributeValue) {
if (inTokenRange() && currentAttributeName) {
let start = scanner.getTokenOffset();
let end = scanner.getTokenEnd();
const char = text[start];
if (char === '"' || char === "'") {
start++;
end--;
}
return {
elementTag: tag,
name: currentAttributeName,
inValue: true,
valueRange: [start, end]
};
}
currentAttributeName = undefined;
}
token = scanner.scan();
}
return null;
}
function inStartTag(offset, node) {
return offset > node.start && node.startTagEnd != undefined && offset < node.startTagEnd;
}
//# sourceMappingURL=parseHtml.js.map