|
@@ -3,413 +3,447 @@
|
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
-import { TextDocument, Position, Range } from 'vscode-languageclient';
|
|
|
-import { LanguageService, TokenType } from 'vscode-html-languageservice';
|
|
|
+import { TextDocument } from "vscode-languageserver-textdocument";
|
|
|
+import { Position, Range } from "vscode-languageclient";
|
|
|
+import { LanguageService, TokenType } from "vscode-html-languageservice";
|
|
|
|
|
|
export interface LanguageRange extends Range {
|
|
|
- languageId: string | undefined;
|
|
|
- attributeValue?: boolean;
|
|
|
+ languageId: string | undefined;
|
|
|
+ attributeValue?: boolean;
|
|
|
}
|
|
|
|
|
|
export interface HTMLDocumentRegions {
|
|
|
- getEmbeddedDocument(languageId: string, ignoreAttributeValues?: boolean): TextDocument;
|
|
|
- getLanguageRanges(range: Range): LanguageRange[];
|
|
|
- getLanguageAtPosition(position: Position): string | undefined;
|
|
|
- getLanguagesInDocument(): string[];
|
|
|
- getImportedScripts(): string[];
|
|
|
+ getEmbeddedDocument(
|
|
|
+ languageId: string,
|
|
|
+ ignoreAttributeValues?: boolean
|
|
|
+ ): TextDocument;
|
|
|
+ getLanguageRanges(range: Range): LanguageRange[];
|
|
|
+ getLanguageAtPosition(position: Position): string | undefined;
|
|
|
+ getLanguagesInDocument(): string[];
|
|
|
+ getImportedScripts(): string[];
|
|
|
}
|
|
|
|
|
|
-export const CSS_STYLE_RULE = '__';
|
|
|
+export const CSS_STYLE_RULE = "__";
|
|
|
|
|
|
interface EmbeddedRegion {
|
|
|
- languageId: string | undefined;
|
|
|
- start: number;
|
|
|
- end: number;
|
|
|
- attributeValue?: boolean;
|
|
|
+ languageId: string | undefined;
|
|
|
+ start: number;
|
|
|
+ end: number;
|
|
|
+ attributeValue?: boolean;
|
|
|
}
|
|
|
|
|
|
+// Check if the request is coming from inside a special region
|
|
|
export function isInsideStyleRegion(
|
|
|
- languageService: LanguageService,
|
|
|
- documentText: string,
|
|
|
- offset: number
|
|
|
+ languageService: LanguageService,
|
|
|
+ documentText: string,
|
|
|
+ offset: number
|
|
|
) {
|
|
|
- let scanner = languageService.createScanner(documentText);
|
|
|
+ let scanner = languageService.createScanner(documentText);
|
|
|
|
|
|
- let token = scanner.scan();
|
|
|
- while (token !== TokenType.EOS) {
|
|
|
- switch (token) {
|
|
|
- case TokenType.Styles:
|
|
|
- if (offset >= scanner.getTokenOffset() && offset <= scanner.getTokenEnd()) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- token = scanner.scan();
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
+ let token = scanner.scan();
|
|
|
+ while (token !== TokenType.EOS) {
|
|
|
+ switch (token) {
|
|
|
+ case TokenType.Styles:
|
|
|
+ if (
|
|
|
+ offset >= scanner.getTokenOffset() &&
|
|
|
+ offset <= scanner.getTokenEnd()
|
|
|
+ ) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ token = scanner.scan();
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
export function getCSSVirtualContent(
|
|
|
- languageService: LanguageService,
|
|
|
- documentText: string
|
|
|
+ languageService: LanguageService,
|
|
|
+ documentText: string
|
|
|
): string {
|
|
|
- let regions: EmbeddedRegion[] = [];
|
|
|
- let scanner = languageService.createScanner(documentText);
|
|
|
- let lastTagName: string = '';
|
|
|
- let lastAttributeName: string | null = null;
|
|
|
- let languageIdFromType: string | undefined = undefined;
|
|
|
- let importedScripts: string[] = [];
|
|
|
+ let regions: EmbeddedRegion[] = [];
|
|
|
+ let scanner = languageService.createScanner(documentText);
|
|
|
+ let lastTagName: string = "";
|
|
|
+ let lastAttributeName: string | null = null;
|
|
|
+ let languageIdFromType: string | undefined = undefined;
|
|
|
+ let importedScripts: string[] = [];
|
|
|
|
|
|
- let token = scanner.scan();
|
|
|
- while (token !== TokenType.EOS) {
|
|
|
- switch (token) {
|
|
|
- case TokenType.StartTag:
|
|
|
- lastTagName = scanner.getTokenText();
|
|
|
- lastAttributeName = null;
|
|
|
- languageIdFromType = 'javascript';
|
|
|
- break;
|
|
|
- case TokenType.Styles:
|
|
|
- regions.push({
|
|
|
- languageId: 'css',
|
|
|
- start: scanner.getTokenOffset(),
|
|
|
- end: scanner.getTokenEnd()
|
|
|
- });
|
|
|
- break;
|
|
|
- case TokenType.Script:
|
|
|
- regions.push({
|
|
|
- languageId: languageIdFromType,
|
|
|
- start: scanner.getTokenOffset(),
|
|
|
- end: scanner.getTokenEnd()
|
|
|
- });
|
|
|
- break;
|
|
|
- case TokenType.AttributeName:
|
|
|
- lastAttributeName = scanner.getTokenText();
|
|
|
- break;
|
|
|
- case 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|text\/babel)["']/.test(
|
|
|
- scanner.getTokenText()
|
|
|
- )
|
|
|
- ) {
|
|
|
- languageIdFromType = 'javascript';
|
|
|
- } else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
|
|
- languageIdFromType = 'typescript';
|
|
|
- } else {
|
|
|
- languageIdFromType = undefined;
|
|
|
- }
|
|
|
- } else {
|
|
|
- let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
|
|
- if (attributeLanguageId) {
|
|
|
- let start = scanner.getTokenOffset();
|
|
|
- let end = scanner.getTokenEnd();
|
|
|
- let firstChar = documentText[start];
|
|
|
- if (firstChar === "'" || firstChar === '"') {
|
|
|
- start++;
|
|
|
- end--;
|
|
|
- }
|
|
|
- regions.push({
|
|
|
- languageId: attributeLanguageId,
|
|
|
- start,
|
|
|
- end,
|
|
|
- attributeValue: true
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- lastAttributeName = null;
|
|
|
- break;
|
|
|
- }
|
|
|
- token = scanner.scan();
|
|
|
- }
|
|
|
+ let token = scanner.scan();
|
|
|
+ while (token !== TokenType.EOS) {
|
|
|
+ switch (token) {
|
|
|
+ case TokenType.StartTag:
|
|
|
+ lastTagName = scanner.getTokenText();
|
|
|
+ lastAttributeName = null;
|
|
|
+ languageIdFromType = "javascript";
|
|
|
+ break;
|
|
|
+ case TokenType.Styles:
|
|
|
+ regions.push({
|
|
|
+ languageId: "css",
|
|
|
+ start: scanner.getTokenOffset(),
|
|
|
+ end: scanner.getTokenEnd(),
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case TokenType.Script:
|
|
|
+ regions.push({
|
|
|
+ languageId: languageIdFromType,
|
|
|
+ start: scanner.getTokenOffset(),
|
|
|
+ end: scanner.getTokenEnd(),
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case TokenType.AttributeName:
|
|
|
+ lastAttributeName = scanner.getTokenText();
|
|
|
+ break;
|
|
|
+ case 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|text\/babel)["']/.test(
|
|
|
+ scanner.getTokenText()
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ languageIdFromType = "javascript";
|
|
|
+ } else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
|
|
+ languageIdFromType = "typescript";
|
|
|
+ } else {
|
|
|
+ languageIdFromType = undefined;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
|
|
+ if (attributeLanguageId) {
|
|
|
+ let start = scanner.getTokenOffset();
|
|
|
+ let end = scanner.getTokenEnd();
|
|
|
+ let firstChar = documentText[start];
|
|
|
+ if (firstChar === "'" || firstChar === '"') {
|
|
|
+ start++;
|
|
|
+ end--;
|
|
|
+ }
|
|
|
+ regions.push({
|
|
|
+ languageId: attributeLanguageId,
|
|
|
+ start,
|
|
|
+ end,
|
|
|
+ attributeValue: true,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lastAttributeName = null;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ token = scanner.scan();
|
|
|
+ }
|
|
|
|
|
|
- let content = documentText
|
|
|
- .split('\n')
|
|
|
- .map(line => {
|
|
|
- return ' '.repeat(line.length);
|
|
|
- }).join('\n');
|
|
|
+ let content = documentText
|
|
|
+ .split("\n")
|
|
|
+ .map((line) => {
|
|
|
+ return " ".repeat(line.length);
|
|
|
+ })
|
|
|
+ .join("\n");
|
|
|
|
|
|
- regions.forEach(r => {
|
|
|
- if (r.languageId === 'css') {
|
|
|
- content = content.slice(0, r.start) + documentText.slice(r.start, r.end) + content.slice(r.end);
|
|
|
- }
|
|
|
- });
|
|
|
+ regions.forEach((r) => {
|
|
|
+ if (r.languageId === "css") {
|
|
|
+ content =
|
|
|
+ content.slice(0, r.start) +
|
|
|
+ documentText.slice(r.start, r.end) +
|
|
|
+ content.slice(r.end);
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- return content;
|
|
|
+ return content;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
export function getDocumentRegions(
|
|
|
- languageService: LanguageService,
|
|
|
- document: TextDocument
|
|
|
+ languageService: LanguageService,
|
|
|
+ document: TextDocument
|
|
|
): HTMLDocumentRegions {
|
|
|
- let regions: EmbeddedRegion[] = [];
|
|
|
- let scanner = languageService.createScanner(document.getText());
|
|
|
- let lastTagName: string = '';
|
|
|
- let lastAttributeName: string | null = null;
|
|
|
- let languageIdFromType: string | undefined = undefined;
|
|
|
- let importedScripts: string[] = [];
|
|
|
+ let regions: EmbeddedRegion[] = [];
|
|
|
+ let scanner = languageService.createScanner(document.getText());
|
|
|
+ let lastTagName: string = "";
|
|
|
+ let lastAttributeName: string | null = null;
|
|
|
+ let languageIdFromType: string | undefined = undefined;
|
|
|
+ let importedScripts: string[] = [];
|
|
|
|
|
|
- let token = scanner.scan();
|
|
|
- while (token !== TokenType.EOS) {
|
|
|
- switch (token) {
|
|
|
- case TokenType.StartTag:
|
|
|
- lastTagName = scanner.getTokenText();
|
|
|
- lastAttributeName = null;
|
|
|
- languageIdFromType = 'javascript';
|
|
|
- break;
|
|
|
- case TokenType.Styles:
|
|
|
- regions.push({
|
|
|
- languageId: 'css',
|
|
|
- start: scanner.getTokenOffset(),
|
|
|
- end: scanner.getTokenEnd()
|
|
|
- });
|
|
|
- break;
|
|
|
- case TokenType.Script:
|
|
|
- regions.push({
|
|
|
- languageId: languageIdFromType,
|
|
|
- start: scanner.getTokenOffset(),
|
|
|
- end: scanner.getTokenEnd()
|
|
|
- });
|
|
|
- break;
|
|
|
- case TokenType.AttributeName:
|
|
|
- lastAttributeName = scanner.getTokenText();
|
|
|
- break;
|
|
|
- case 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|text\/babel)["']/.test(
|
|
|
- scanner.getTokenText()
|
|
|
- )
|
|
|
- ) {
|
|
|
- languageIdFromType = 'javascript';
|
|
|
- } else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
|
|
- languageIdFromType = 'typescript';
|
|
|
- } else {
|
|
|
- languageIdFromType = undefined;
|
|
|
- }
|
|
|
- } else {
|
|
|
- let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
|
|
- if (attributeLanguageId) {
|
|
|
- let start = scanner.getTokenOffset();
|
|
|
- let end = scanner.getTokenEnd();
|
|
|
- let firstChar = document.getText()[start];
|
|
|
- if (firstChar === "'" || firstChar === '"') {
|
|
|
- start++;
|
|
|
- end--;
|
|
|
- }
|
|
|
- regions.push({
|
|
|
- languageId: attributeLanguageId,
|
|
|
- start,
|
|
|
- end,
|
|
|
- attributeValue: true
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- lastAttributeName = null;
|
|
|
- break;
|
|
|
- }
|
|
|
- token = scanner.scan();
|
|
|
- }
|
|
|
- return {
|
|
|
- getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range),
|
|
|
- getEmbeddedDocument: (languageId: string, ignoreAttributeValues: boolean) =>
|
|
|
- getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues),
|
|
|
- getLanguageAtPosition: (position: Position) =>
|
|
|
- getLanguageAtPosition(document, regions, position),
|
|
|
- getLanguagesInDocument: () => getLanguagesInDocument(document, regions),
|
|
|
- getImportedScripts: () => importedScripts
|
|
|
- };
|
|
|
+ let token = scanner.scan();
|
|
|
+ while (token !== TokenType.EOS) {
|
|
|
+ switch (token) {
|
|
|
+ case TokenType.StartTag:
|
|
|
+ lastTagName = scanner.getTokenText();
|
|
|
+ lastAttributeName = null;
|
|
|
+ languageIdFromType = "javascript";
|
|
|
+ break;
|
|
|
+ case TokenType.Styles:
|
|
|
+ regions.push({
|
|
|
+ languageId: "css",
|
|
|
+ start: scanner.getTokenOffset(),
|
|
|
+ end: scanner.getTokenEnd(),
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case TokenType.Script:
|
|
|
+ regions.push({
|
|
|
+ languageId: languageIdFromType,
|
|
|
+ start: scanner.getTokenOffset(),
|
|
|
+ end: scanner.getTokenEnd(),
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case TokenType.AttributeName:
|
|
|
+ lastAttributeName = scanner.getTokenText();
|
|
|
+ break;
|
|
|
+ case 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|text\/babel)["']/.test(
|
|
|
+ scanner.getTokenText()
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ languageIdFromType = "javascript";
|
|
|
+ } else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
|
|
|
+ languageIdFromType = "typescript";
|
|
|
+ } else {
|
|
|
+ languageIdFromType = undefined;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
|
|
|
+ if (attributeLanguageId) {
|
|
|
+ let start = scanner.getTokenOffset();
|
|
|
+ let end = scanner.getTokenEnd();
|
|
|
+ let firstChar = document.getText()[start];
|
|
|
+ if (firstChar === "'" || firstChar === '"') {
|
|
|
+ start++;
|
|
|
+ end--;
|
|
|
+ }
|
|
|
+ regions.push({
|
|
|
+ languageId: attributeLanguageId,
|
|
|
+ start,
|
|
|
+ end,
|
|
|
+ attributeValue: true,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lastAttributeName = null;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ token = scanner.scan();
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ getLanguageRanges: (range: Range) =>
|
|
|
+ getLanguageRanges(document, regions, range),
|
|
|
+ getEmbeddedDocument: (languageId: string, ignoreAttributeValues: boolean) =>
|
|
|
+ getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues),
|
|
|
+ getLanguageAtPosition: (position: Position) =>
|
|
|
+ getLanguageAtPosition(document, regions, position),
|
|
|
+ getLanguagesInDocument: () => getLanguagesInDocument(document, regions),
|
|
|
+ getImportedScripts: () => importedScripts,
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
function getLanguageRanges(
|
|
|
- document: TextDocument,
|
|
|
- regions: EmbeddedRegion[],
|
|
|
- range: Range
|
|
|
+ document: TextDocument,
|
|
|
+ regions: EmbeddedRegion[],
|
|
|
+ range: Range
|
|
|
): LanguageRange[] {
|
|
|
- let result: LanguageRange[] = [];
|
|
|
- let currentPos = range ? range.start : Position.create(0, 0);
|
|
|
- let currentOffset = range ? document.offsetAt(range.start) : 0;
|
|
|
- let endOffset = range ? document.offsetAt(range.end) : document.getText().length;
|
|
|
- for (let region of regions) {
|
|
|
- if (region.end > currentOffset && region.start < endOffset) {
|
|
|
- let start = Math.max(region.start, currentOffset);
|
|
|
- let startPos = document.positionAt(start);
|
|
|
- if (currentOffset < region.start) {
|
|
|
- result.push({
|
|
|
- start: currentPos,
|
|
|
- end: startPos,
|
|
|
- languageId: 'html'
|
|
|
- });
|
|
|
- }
|
|
|
- let end = Math.min(region.end, endOffset);
|
|
|
- let endPos = document.positionAt(end);
|
|
|
- if (end > region.start) {
|
|
|
- result.push({
|
|
|
- start: startPos,
|
|
|
- end: endPos,
|
|
|
- languageId: region.languageId,
|
|
|
- attributeValue: region.attributeValue
|
|
|
- });
|
|
|
- }
|
|
|
- currentOffset = end;
|
|
|
- currentPos = endPos;
|
|
|
- }
|
|
|
- }
|
|
|
- if (currentOffset < endOffset) {
|
|
|
- let endPos = range ? range.end : document.positionAt(endOffset);
|
|
|
- result.push({
|
|
|
- start: currentPos,
|
|
|
- end: endPos,
|
|
|
- languageId: 'html'
|
|
|
- });
|
|
|
- }
|
|
|
- return result;
|
|
|
+ let result: LanguageRange[] = [];
|
|
|
+ let currentPos = range ? range.start : Position.create(0, 0);
|
|
|
+ let currentOffset = range ? document.offsetAt(range.start) : 0;
|
|
|
+ let endOffset = range
|
|
|
+ ? document.offsetAt(range.end)
|
|
|
+ : document.getText().length;
|
|
|
+ for (let region of regions) {
|
|
|
+ if (region.end > currentOffset && region.start < endOffset) {
|
|
|
+ let start = Math.max(region.start, currentOffset);
|
|
|
+ let startPos = document.positionAt(start);
|
|
|
+ if (currentOffset < region.start) {
|
|
|
+ result.push({
|
|
|
+ start: currentPos,
|
|
|
+ end: startPos,
|
|
|
+ languageId: "html",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ let end = Math.min(region.end, endOffset);
|
|
|
+ let endPos = document.positionAt(end);
|
|
|
+ if (end > region.start) {
|
|
|
+ result.push({
|
|
|
+ start: startPos,
|
|
|
+ end: endPos,
|
|
|
+ languageId: region.languageId,
|
|
|
+ attributeValue: region.attributeValue,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ currentOffset = end;
|
|
|
+ currentPos = endPos;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (currentOffset < endOffset) {
|
|
|
+ let endPos = range ? range.end : document.positionAt(endOffset);
|
|
|
+ result.push({
|
|
|
+ start: currentPos,
|
|
|
+ end: endPos,
|
|
|
+ languageId: "html",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
function getLanguagesInDocument(
|
|
|
- _document: TextDocument,
|
|
|
- regions: EmbeddedRegion[]
|
|
|
+ _document: TextDocument,
|
|
|
+ regions: EmbeddedRegion[]
|
|
|
): string[] {
|
|
|
- let result = [];
|
|
|
- for (let region of regions) {
|
|
|
- if (region.languageId && result.indexOf(region.languageId) === -1) {
|
|
|
- result.push(region.languageId);
|
|
|
- if (result.length === 3) {
|
|
|
- return result;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- result.push('html');
|
|
|
- return result;
|
|
|
+ let result = [];
|
|
|
+ for (let region of regions) {
|
|
|
+ if (region.languageId && result.indexOf(region.languageId) === -1) {
|
|
|
+ result.push(region.languageId);
|
|
|
+ if (result.length === 3) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.push("html");
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
function getLanguageAtPosition(
|
|
|
- document: TextDocument,
|
|
|
- regions: EmbeddedRegion[],
|
|
|
- position: Position
|
|
|
+ document: TextDocument,
|
|
|
+ regions: EmbeddedRegion[],
|
|
|
+ position: Position
|
|
|
): string | undefined {
|
|
|
- let offset = document.offsetAt(position);
|
|
|
- for (let region of regions) {
|
|
|
- if (region.start <= offset) {
|
|
|
- if (offset <= region.end) {
|
|
|
- return region.languageId;
|
|
|
- }
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- return 'html';
|
|
|
+ let offset = document.offsetAt(position);
|
|
|
+ for (let region of regions) {
|
|
|
+ if (region.start <= offset) {
|
|
|
+ if (offset <= region.end) {
|
|
|
+ return region.languageId;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "html";
|
|
|
}
|
|
|
|
|
|
function getEmbeddedDocument(
|
|
|
- document: TextDocument,
|
|
|
- contents: EmbeddedRegion[],
|
|
|
- languageId: string,
|
|
|
- ignoreAttributeValues: boolean
|
|
|
+ document: TextDocument,
|
|
|
+ contents: EmbeddedRegion[],
|
|
|
+ languageId: string,
|
|
|
+ ignoreAttributeValues: boolean
|
|
|
): TextDocument {
|
|
|
- let currentPos = 0;
|
|
|
- let oldContent = document.getText();
|
|
|
- let result = '';
|
|
|
- let lastSuffix = '';
|
|
|
- for (let 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);
|
|
|
- currentPos = c.end;
|
|
|
- lastSuffix = getSuffix(c);
|
|
|
- }
|
|
|
- }
|
|
|
- result = substituteWithWhitespace(
|
|
|
- result,
|
|
|
- currentPos,
|
|
|
- oldContent.length,
|
|
|
- oldContent,
|
|
|
- lastSuffix,
|
|
|
- ''
|
|
|
- );
|
|
|
- return TextDocument.create(document.uri, languageId, document.version, result);
|
|
|
+ let currentPos = 0;
|
|
|
+ let oldContent = document.getText();
|
|
|
+ let result = "";
|
|
|
+ let lastSuffix = "";
|
|
|
+ for (let 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);
|
|
|
+ currentPos = c.end;
|
|
|
+ lastSuffix = getSuffix(c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result = substituteWithWhitespace(
|
|
|
+ result,
|
|
|
+ currentPos,
|
|
|
+ oldContent.length,
|
|
|
+ oldContent,
|
|
|
+ lastSuffix,
|
|
|
+ ""
|
|
|
+ );
|
|
|
+ return TextDocument.create(
|
|
|
+ document.uri,
|
|
|
+ languageId,
|
|
|
+ document.version,
|
|
|
+ result
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
function getPrefix(c: EmbeddedRegion) {
|
|
|
- if (c.attributeValue) {
|
|
|
- switch (c.languageId) {
|
|
|
- case 'css':
|
|
|
- return CSS_STYLE_RULE + '{';
|
|
|
- }
|
|
|
- }
|
|
|
- return '';
|
|
|
+ if (c.attributeValue) {
|
|
|
+ switch (c.languageId) {
|
|
|
+ case "css":
|
|
|
+ return CSS_STYLE_RULE + "{";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "";
|
|
|
}
|
|
|
function getSuffix(c: EmbeddedRegion) {
|
|
|
- if (c.attributeValue) {
|
|
|
- switch (c.languageId) {
|
|
|
- case 'css':
|
|
|
- return '}';
|
|
|
- case 'javascript':
|
|
|
- return ';';
|
|
|
- }
|
|
|
- }
|
|
|
- return '';
|
|
|
+ if (c.attributeValue) {
|
|
|
+ switch (c.languageId) {
|
|
|
+ case "css":
|
|
|
+ return "}";
|
|
|
+ case "javascript":
|
|
|
+ return ";";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "";
|
|
|
}
|
|
|
|
|
|
function substituteWithWhitespace(
|
|
|
- result: string,
|
|
|
- start: number,
|
|
|
- end: number,
|
|
|
- oldContent: string,
|
|
|
- before: string,
|
|
|
- after: string
|
|
|
+ result: string,
|
|
|
+ start: number,
|
|
|
+ end: number,
|
|
|
+ oldContent: string,
|
|
|
+ before: string,
|
|
|
+ after: string
|
|
|
) {
|
|
|
- let accumulatedWS = 0;
|
|
|
- result += before;
|
|
|
- for (let i = start + before.length; i < end; i++) {
|
|
|
- let 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;
|
|
|
+ let accumulatedWS = 0;
|
|
|
+ result += before;
|
|
|
+ for (let i = start + before.length; i < end; i++) {
|
|
|
+ let 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: string, str: string, n: number): string {
|
|
|
- while (n > 0) {
|
|
|
- if (n & 1) {
|
|
|
- result += str;
|
|
|
- }
|
|
|
- n >>= 1;
|
|
|
- str += str;
|
|
|
- }
|
|
|
- return result;
|
|
|
+ while (n > 0) {
|
|
|
+ if (n & 1) {
|
|
|
+ result += str;
|
|
|
+ }
|
|
|
+ n >>= 1;
|
|
|
+ str += str;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
function getAttributeLanguage(attributeName: string): string | null {
|
|
|
- let match = attributeName.match(/^(style)$|^(on\w+)$/i);
|
|
|
- if (!match) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- return match[1] ? 'css' : 'javascript';
|
|
|
+ let match = attributeName.match(/^(style)$|^(on\w+)$/i);
|
|
|
+ if (!match) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return match[1] ? "css" : "javascript";
|
|
|
}
|