typescript-template-language-service-decorator
Version:
Framework for decorating a TypeScript language service with support for languages embedded in template strings
168 lines (167 loc) • 7.52 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
const nodes_1 = require("./nodes");
const memoize_1 = require("./util/memoize");
class PlaceholderSubstituter {
static replacePlaceholders(typescript, settings, node) {
const literalContents = node.getText().slice(1, -1);
if (node.kind === typescript.SyntaxKind.NoSubstitutionTemplateLiteral) {
return literalContents;
}
return PlaceholderSubstituter.getSubstitutions(settings, literalContents, PlaceholderSubstituter.getPlaceholderSpans(node));
}
static getPlaceholderSpans(node) {
const spans = [];
const stringStart = node.getStart() + 1;
let nodeStart = node.head.end - stringStart - 2;
for (const child of node.templateSpans.map(x => x.literal)) {
const start = child.getStart() - stringStart + 1;
spans.push({ start: nodeStart, end: start });
nodeStart = child.getEnd() - stringStart - 2;
}
return spans;
}
static getSubstitutions(settings, contents, locations) {
if (settings.getSubstitutions) {
return settings.getSubstitutions(contents, locations);
}
const parts = [];
let lastIndex = 0;
for (const span of locations) {
parts.push(contents.slice(lastIndex, span.start));
parts.push(this.getSubstitution(settings, contents, span.start, span.end));
lastIndex = span.end;
}
parts.push(contents.slice(lastIndex));
return parts.join('');
}
static getSubstitution(settings, templateString, start, end) {
return settings.getSubstitution
? settings.getSubstitution(templateString, start, end)
: 'x'.repeat(end - start);
}
}
class StandardTemplateContext {
constructor(typescript, fileName, node, helper, templateSettings) {
this.typescript = typescript;
this.fileName = fileName;
this.node = node;
this.helper = helper;
this.templateSettings = templateSettings;
}
toOffset(position) {
const docOffset = this.helper.getOffset(this.fileName, position.line + this.stringBodyPosition.line, position.line === 0 ? this.stringBodyPosition.character + position.character : position.character);
return docOffset - this.stringBodyOffset;
}
toPosition(offset) {
const docPosition = this.helper.getLineAndChar(this.fileName, this.stringBodyOffset + offset);
return (0, nodes_1.relative)(this.stringBodyPosition, docPosition);
}
get stringBodyOffset() {
return this.node.getStart() + 1;
}
get stringBodyPosition() {
return this.helper.getLineAndChar(this.fileName, this.stringBodyOffset);
}
get text() {
return PlaceholderSubstituter.replacePlaceholders(this.typescript, this.templateSettings, this.node);
}
get rawText() {
return this.node.getText().slice(1, -1);
}
}
__decorate([
memoize_1.memoize
], StandardTemplateContext.prototype, "stringBodyOffset", null);
__decorate([
memoize_1.memoize
], StandardTemplateContext.prototype, "stringBodyPosition", null);
__decorate([
memoize_1.memoize
], StandardTemplateContext.prototype, "text", null);
__decorate([
memoize_1.memoize
], StandardTemplateContext.prototype, "rawText", null);
class StandardTemplateSourceHelper {
constructor(typescript, templateStringSettings, helper, _logger) {
this.typescript = typescript;
this.templateStringSettings = templateStringSettings;
this.helper = helper;
}
getTemplate(fileName, position) {
const node = this.getValidTemplateNode(this.templateStringSettings, this.helper.getNode(fileName, position));
if (!node) {
return undefined;
}
// Make sure we are inside the template string
if (position <= node.pos) {
return undefined;
}
// Make sure we are not inside of a placeholder
if (node.kind === this.typescript.SyntaxKind.TemplateExpression) {
let start = node.head.end;
for (const child of node.templateSpans.map(x => x.literal)) {
const nextStart = child.getStart();
if (position >= start && position <= nextStart) {
return undefined;
}
start = child.getEnd();
}
}
return new StandardTemplateContext(this.typescript, fileName, node, this.helper, this.templateStringSettings);
}
getAllTemplates(fileName) {
const out = [];
for (const node of this.helper.getAllNodes(fileName, n => this.getValidTemplateNode(this.templateStringSettings, n) !== undefined)) {
const validNode = this.getValidTemplateNode(this.templateStringSettings, node);
if (validNode) {
out.push(new StandardTemplateContext(this.typescript, fileName, validNode, this.helper, this.templateStringSettings));
}
}
return out;
}
getRelativePosition(context, offset) {
const baseLC = this.helper.getLineAndChar(context.fileName, context.node.getStart() + 1);
const cursorLC = this.helper.getLineAndChar(context.fileName, offset);
return (0, nodes_1.relative)(baseLC, cursorLC);
}
getValidTemplateNode(templateStringSettings, node) {
if (!node) {
return undefined;
}
switch (node.kind) {
case this.typescript.SyntaxKind.TaggedTemplateExpression:
if ((0, nodes_1.isTagged)(node, templateStringSettings.tags)) {
return node.template;
}
return undefined;
case this.typescript.SyntaxKind.NoSubstitutionTemplateLiteral:
if ((0, nodes_1.isTaggedLiteral)(this.typescript, node, templateStringSettings.tags)) {
return node;
}
return undefined;
case this.typescript.SyntaxKind.TemplateHead:
if (templateStringSettings.enableForStringWithSubstitutions && node.parent && node.parent.parent) {
return this.getValidTemplateNode(templateStringSettings, node.parent.parent);
}
return undefined;
case this.typescript.SyntaxKind.TemplateMiddle:
case this.typescript.SyntaxKind.TemplateTail:
if (templateStringSettings.enableForStringWithSubstitutions && node.parent && node.parent.parent) {
return this.getValidTemplateNode(templateStringSettings, node.parent.parent.parent);
}
return undefined;
default:
return undefined;
}
}
}
exports.default = StandardTemplateSourceHelper;