typescript-lit-html-plugin
Version:
TypeScript language service plugin that adds IntelliSense for html tagged templates
189 lines • 7.57 kB
JavaScript
;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Original code forked from https://github.com/vscode-langservers/vscode-html-languageserver/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDocumentRegions = exports.CSS_STYLE_RULE = void 0;
const vscode_html_languageservice_1 = require("vscode-html-languageservice");
exports.CSS_STYLE_RULE = '__';
/**
* For template expressions, typescript-template-language-service-decorator
* will replace them with placeholder `x`'s, when the line does not consist only of
* expressions and whitespace.
*
* This regex can be used to check if CSS content contains only expressions and whitspace.
*/
const onlyPlaceholdersRegex = /^ *x{3,}( *x{3,})* *$/;
function getDocumentRegions(languageService, document) {
const regions = [];
const scanner = languageService.createScanner(document.getText());
let lastTagName = '';
let lastAttributeName = null;
let languageIdFromType;
const importedScripts = [];
let token = scanner.scan();
while (token !== vscode_html_languageservice_1.TokenType.EOS) {
switch (token) {
case vscode_html_languageservice_1.TokenType.StartTag:
lastTagName = scanner.getTokenText();
lastAttributeName = null;
languageIdFromType = 'javascript';
break;
case vscode_html_languageservice_1.TokenType.Styles:
regions.push({
languageId: 'css',
start: scanner.getTokenOffset(),
end: scanner.getTokenEnd(),
onlyPlaceholders: onlyPlaceholdersRegex.test(scanner.getTokenText()),
});
break;
case vscode_html_languageservice_1.TokenType.Script:
regions.push({
languageId: languageIdFromType,
start: scanner.getTokenOffset(),
end: scanner.getTokenEnd(),
onlyPlaceholders: onlyPlaceholdersRegex.test(scanner.getTokenText()),
});
break;
case vscode_html_languageservice_1.TokenType.AttributeName:
lastAttributeName = scanner.getTokenText();
break;
case vscode_html_languageservice_1.TokenType.AttributeValue:
if (lastAttributeName === 'src' &&
lastTagName.toLowerCase() === 'script') {
let value = scanner.getTokenText();
if (value[0] === '\'' || value[0] === '"') {
value = value.substr(1, value.length - 1);
}
importedScripts.push(value);
}
else if (lastAttributeName === 'type' &&
lastTagName.toLowerCase() === 'script') {
if (/["'](module|(text|application)\/(java|ecma)script)["']/.test(scanner.getTokenText())) {
languageIdFromType = 'javascript';
}
else {
languageIdFromType = void 0;
}
}
else {
const attributeLanguageId = getAttributeLanguage(lastAttributeName);
if (attributeLanguageId) {
let start = scanner.getTokenOffset();
let end = scanner.getTokenEnd();
const firstChar = document.getText()[start];
if (firstChar === '\'' || firstChar === '"') {
start++;
end--;
}
const onlyPlaceholders = onlyPlaceholdersRegex.test(document.getText().slice(start, end));
regions.push({
languageId: attributeLanguageId,
start,
end,
onlyPlaceholders,
attributeValue: true,
});
}
}
lastAttributeName = null;
break;
}
token = scanner.scan();
}
return {
getEmbeddedDocument: (languageId, ignoreAttributeValues) => getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues),
getLanguageAtPosition: (position) => getLanguageAtPosition(document, regions, position),
};
}
exports.getDocumentRegions = getDocumentRegions;
function getLanguageAtPosition(document, regions, position) {
const offset = document.offsetAt(position);
for (const region of regions) {
if (region.start <= offset) {
if (offset <= region.end) {
return region.languageId;
}
}
else {
break;
}
}
return 'html';
}
function getEmbeddedDocument(document, contents, languageId, ignoreAttributeValues) {
let currentPos = 0;
const oldContent = document.getText();
let result = '';
let lastSuffix = '';
for (const c of contents) {
if (c.languageId === languageId &&
(!ignoreAttributeValues || !c.attributeValue)) {
result = substituteWithWhitespace(result, currentPos, c.start, oldContent, lastSuffix, getPrefix(c));
result += oldContent.substring(c.start, c.end)
.replace(onlyPlaceholdersRegex, match => ' '.repeat(match.length));
currentPos = c.end;
lastSuffix = getSuffix(c);
}
}
result = substituteWithWhitespace(result, currentPos, oldContent.length, oldContent, lastSuffix, '');
return vscode_html_languageservice_1.TextDocument.create(document.uri, languageId, document.version, result);
}
function getPrefix(c) {
if (c.attributeValue && !c.onlyPlaceholders) {
switch (c.languageId) {
case 'css':
return exports.CSS_STYLE_RULE + '{';
}
}
return '';
}
function getSuffix(c) {
if (c.attributeValue && !c.onlyPlaceholders) {
switch (c.languageId) {
case 'css':
return '}';
case 'javascript':
return ';';
}
}
return '';
}
function substituteWithWhitespace(result, start, end, oldContent, before, after) {
let accumulatedWS = 0;
result += before;
for (let i = start + before.length; i < end; i++) {
const ch = oldContent[i];
if (ch === '\n' || ch === '\r') {
// only write new lines, skip the whitespace
accumulatedWS = 0;
result += ch;
}
else {
accumulatedWS++;
}
}
result = append(result, ' ', accumulatedWS - after.length);
result += after;
return result;
}
function append(result, str, n) {
while (n > 0) {
if (n & 1) {
result += str;
}
n >>= 1;
str += str;
}
return result;
}
function getAttributeLanguage(attributeName) {
const match = attributeName.match(/^(style)$|^(on\w+)$/i);
if (!match) {
return null;
}
return match[1] ? 'css' : 'javascript';
}
//# sourceMappingURL=embeddedSupport.js.map