mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
[html] refactor and simplify embeddedSupport
This commit is contained in:
parent
e8efbf474e
commit
8efb9e68d0
|
@ -44,10 +44,10 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
|
|||
|
||||
languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true });
|
||||
documents.onDidClose(e => {
|
||||
languageModes.getAllModes().forEach(m => m.onDocumentRemoved(e.document));
|
||||
languageModes.onDocumentRemoved(e.document);
|
||||
});
|
||||
connection.onShutdown(() => {
|
||||
languageModes.getAllModes().forEach(m => m.dispose());
|
||||
languageModes.dispose();
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -198,7 +198,7 @@ connection.onDocumentRangeFormatting(formatParams => {
|
|||
let result: TextEdit[] = [];
|
||||
ranges.forEach(r => {
|
||||
let mode = r.mode;
|
||||
if (mode && mode.format) {
|
||||
if (mode && mode.format && !r.attributeValue) {
|
||||
let edits = mode.format(document, r, formatParams.options);
|
||||
pushAll(result, edits);
|
||||
}
|
||||
|
|
|
@ -5,16 +5,13 @@
|
|||
'use strict';
|
||||
|
||||
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
|
||||
import { LanguageService as HTMLLanguageService, HTMLDocument } from 'vscode-html-languageservice';
|
||||
import { TextDocument, Position } from 'vscode-languageserver-types';
|
||||
import { getCSSLanguageService, Stylesheet } from 'vscode-css-languageservice';
|
||||
import { getEmbeddedDocument } from './embeddedSupport';
|
||||
import { LanguageMode } from './languageModes';
|
||||
|
||||
export function getCSSMode(htmlLanguageService: HTMLLanguageService, htmlDocuments: LanguageModelCache<HTMLDocument>): LanguageMode {
|
||||
export function getCSSMode(embeddedCSSDocuments: LanguageModelCache<TextDocument>): LanguageMode {
|
||||
let cssLanguageService = getCSSLanguageService();
|
||||
let cssStylesheets = getLanguageModelCache<Stylesheet>(10, 60, document => cssLanguageService.parseStylesheet(document));
|
||||
let getEmbeddedCSSDocument = (document: TextDocument) => getEmbeddedDocument(htmlLanguageService, document, htmlDocuments.get(document), 'css');
|
||||
let cssStylesheets = getLanguageModelCache<Stylesheet>(10, 60, document => cssStylesheets.get(document));
|
||||
|
||||
return {
|
||||
getId() {
|
||||
|
@ -24,31 +21,31 @@ export function getCSSMode(htmlLanguageService: HTMLLanguageService, htmlDocumen
|
|||
cssLanguageService.configure(options && options.css);
|
||||
},
|
||||
doValidation(document: TextDocument) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded));
|
||||
},
|
||||
doComplete(document: TextDocument, position: Position) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.doComplete(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
doHover(document: TextDocument, position: Position) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findDocumentHighlight(document: TextDocument, position: Position) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findDocumentHighlights(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findDefinition(document: TextDocument, position: Position) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findDefinition(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findReferences(document: TextDocument, position: Position) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findReferences(embedded, position, cssStylesheets.get(embedded));
|
||||
},
|
||||
findColorSymbols(document: TextDocument) {
|
||||
let embedded = getEmbeddedCSSDocument(document);
|
||||
let embedded = embeddedCSSDocuments.get(document);
|
||||
return cssLanguageService.findColorSymbols(embedded, cssStylesheets.get(embedded));
|
||||
},
|
||||
onDocumentRemoved(document: TextDocument) {
|
||||
|
|
|
@ -5,135 +5,119 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import { TextDocument, Position, HTMLDocument, Node, LanguageService, TokenType, Range, Scanner } from 'vscode-html-languageservice';
|
||||
import { TextDocument, Position, LanguageService, TokenType, Range } from 'vscode-html-languageservice';
|
||||
|
||||
export interface LanguageRange extends Range {
|
||||
languageId: string;
|
||||
attributeValue?: boolean;
|
||||
}
|
||||
|
||||
interface EmbeddedContent { languageId: string; start: number; end: number; attributeValue?: boolean; };
|
||||
export interface HTMLDocumentRegions {
|
||||
getEmbeddedDocument(languageId: string): TextDocument;
|
||||
getLanguageRanges(range: Range): LanguageRange[];
|
||||
getLanguageAtPosition(position: Position): string;
|
||||
getLanguagesInDocument(): string[];
|
||||
}
|
||||
|
||||
export function getLanguageAtPosition(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument, position: Position): string {
|
||||
let offset = document.offsetAt(position);
|
||||
let node = htmlDocument.findNodeAt(offset);
|
||||
if (node) {
|
||||
let embeddedContent = getEmbeddedContentForNode(languageService, document, node);
|
||||
if (embeddedContent) {
|
||||
for (let c of embeddedContent) {
|
||||
if (c.start <= offset && offset <= c.end) {
|
||||
return c.languageId;
|
||||
}
|
||||
interface EmbeddedRegion { languageId: string; start: number; end: number; attributeValue?: boolean; };
|
||||
|
||||
export function getDocumentRegions(languageService: LanguageService, document: TextDocument): HTMLDocumentRegions {
|
||||
let regions = getEmbeddedRegions(languageService, document);
|
||||
return {
|
||||
getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range),
|
||||
getEmbeddedDocument: (languageId: string) => getEmbeddedDocument(document, regions, languageId),
|
||||
getLanguageAtPosition: (position: Position) => getLanguageAtPosition(document, regions, position),
|
||||
getLanguagesInDocument: () => getLanguagesInDocument(document, regions)
|
||||
};
|
||||
}
|
||||
|
||||
function getLanguageRanges(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;
|
||||
}
|
||||
|
||||
function getLanguagesInDocument(document: TextDocument, regions: EmbeddedRegion[]): string[] {
|
||||
let result = [];
|
||||
for (let region of regions) {
|
||||
if (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): string {
|
||||
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';
|
||||
}
|
||||
|
||||
export function getLanguagesInContent(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument): string[] {
|
||||
let embeddedLanguageIds = ['html'];
|
||||
const maxEmbbeddedLanguages = 3;
|
||||
function collectEmbeddedLanguages(node: Node): void {
|
||||
if (embeddedLanguageIds.length < maxEmbbeddedLanguages) {
|
||||
let embeddedContent = getEmbeddedContentForNode(languageService, document, node);
|
||||
if (embeddedContent) {
|
||||
for (let c of embeddedContent) {
|
||||
if (!isWhitespace(document.getText(), c.start, c.end)) {
|
||||
if (embeddedLanguageIds.lastIndexOf(c.languageId) === -1) {
|
||||
embeddedLanguageIds.push(c.languageId);
|
||||
if (embeddedLanguageIds.length === maxEmbbeddedLanguages) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node.children.forEach(collectEmbeddedLanguages);
|
||||
}
|
||||
}
|
||||
|
||||
htmlDocument.roots.forEach(collectEmbeddedLanguages);
|
||||
return embeddedLanguageIds;
|
||||
}
|
||||
|
||||
export function getLanguagesInRange(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument, range: Range): LanguageRange[] {
|
||||
let ranges: LanguageRange[] = [];
|
||||
let currentPos = range.start;
|
||||
let currentOffset = document.offsetAt(currentPos);
|
||||
let rangeEndOffset = document.offsetAt(range.end);
|
||||
function collectEmbeddedNodes(node: Node): void {
|
||||
if (node.start < rangeEndOffset && node.end > currentOffset) {
|
||||
let embeddedContent = getEmbeddedContentForNode(languageService, document, node);
|
||||
if (embeddedContent) {
|
||||
for (let c of embeddedContent) {
|
||||
if (c.start < rangeEndOffset) {
|
||||
let startPos = document.positionAt(c.start);
|
||||
if (currentOffset < c.start) {
|
||||
ranges.push({
|
||||
start: currentPos,
|
||||
end: startPos,
|
||||
languageId: 'html'
|
||||
});
|
||||
}
|
||||
let end = Math.min(c.end, rangeEndOffset);
|
||||
let endPos = document.positionAt(end);
|
||||
if (end > c.start) {
|
||||
ranges.push({
|
||||
start: startPos,
|
||||
end: endPos,
|
||||
languageId: c.languageId
|
||||
});
|
||||
}
|
||||
currentOffset = end;
|
||||
currentPos = endPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node.children.forEach(collectEmbeddedNodes);
|
||||
}
|
||||
|
||||
htmlDocument.roots.forEach(collectEmbeddedNodes);
|
||||
if (currentOffset < rangeEndOffset) {
|
||||
ranges.push({
|
||||
start: currentPos,
|
||||
end: range.end,
|
||||
languageId: 'html'
|
||||
});
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
export function getEmbeddedDocument(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument, languageId: string): TextDocument {
|
||||
let contents: EmbeddedContent[] = [];
|
||||
function collectEmbeddedNodes(node: Node): void {
|
||||
let embeddedContent = getEmbeddedContentForNode(languageService, document, node);
|
||||
if (embeddedContent) {
|
||||
for (let c of embeddedContent) {
|
||||
if (c.languageId === languageId) {
|
||||
contents.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
node.children.forEach(collectEmbeddedNodes);
|
||||
}
|
||||
|
||||
htmlDocument.roots.forEach(collectEmbeddedNodes);
|
||||
|
||||
function getEmbeddedDocument(document: TextDocument, contents: EmbeddedRegion[], languageId: string): TextDocument {
|
||||
let currentPos = 0;
|
||||
let oldContent = document.getText();
|
||||
let result = '';
|
||||
let lastSuffix = '';
|
||||
for (let c of contents) {
|
||||
result = substituteWithWhitespace(result, currentPos, c.start, oldContent, lastSuffix, getPrefix(c));
|
||||
result += oldContent.substring(c.start, c.end);
|
||||
currentPos = c.end;
|
||||
lastSuffix = getSuffix(c);
|
||||
if (c.languageId === languageId) {
|
||||
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: EmbeddedContent) {
|
||||
function getPrefix(c: EmbeddedRegion) {
|
||||
if (c.attributeValue) {
|
||||
switch (c.languageId) {
|
||||
case 'css': return 'x{';
|
||||
|
@ -141,7 +125,7 @@ function getPrefix(c: EmbeddedContent) {
|
|||
}
|
||||
return '';
|
||||
}
|
||||
function getSuffix(c: EmbeddedContent) {
|
||||
function getSuffix(c: EmbeddedRegion) {
|
||||
if (c.attributeValue) {
|
||||
switch (c.languageId) {
|
||||
case 'css': return '}';
|
||||
|
@ -151,7 +135,6 @@ function getSuffix(c: EmbeddedContent) {
|
|||
return '';
|
||||
}
|
||||
|
||||
|
||||
function substituteWithWhitespace(result: string, start: number, end: number, oldContent: string, before: string, after: string) {
|
||||
let accumulatedWS = 0;
|
||||
result += before;
|
||||
|
@ -181,74 +164,56 @@ function append(result: string, str: string, n: number): string {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getEmbeddedContentForNode(languageService: LanguageService, document: TextDocument, node: Node): EmbeddedContent[] {
|
||||
if (node.tag === 'style') {
|
||||
let scanner = languageService.createScanner(document.getText().substring(node.start, node.end));
|
||||
let token = scanner.scan();
|
||||
while (token !== TokenType.EOS) {
|
||||
if (token === TokenType.Styles) {
|
||||
return [{ languageId: 'css', start: node.start + scanner.getTokenOffset(), end: node.start + scanner.getTokenEnd() }];
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
} else if (node.tag === 'script') {
|
||||
let scanner = languageService.createScanner(document.getText().substring(node.start, node.end));
|
||||
let token = scanner.scan();
|
||||
let isTypeAttribute = false;
|
||||
let languageId = 'javascript';
|
||||
while (token !== TokenType.EOS) {
|
||||
if (token === TokenType.AttributeName) {
|
||||
isTypeAttribute = scanner.getTokenText() === 'type';
|
||||
} else if (token === TokenType.AttributeValue) {
|
||||
if (isTypeAttribute) {
|
||||
function getEmbeddedRegions(languageService: LanguageService, document: TextDocument): EmbeddedRegion[] {
|
||||
let regions: EmbeddedRegion[] = [];
|
||||
let scanner = languageService.createScanner(document.getText());
|
||||
let lastTagName: string;
|
||||
let lastAttributeName: string;
|
||||
let languageIdFromType: 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 === 'type' && lastTagName.toLowerCase() === 'script') {
|
||||
if (/["'](text|application)\/(java|ecma)script["']/.test(scanner.getTokenText())) {
|
||||
languageId = 'javascript';
|
||||
languageIdFromType = 'javascript';
|
||||
} else {
|
||||
languageId = void 0;
|
||||
languageIdFromType = void 0;
|
||||
}
|
||||
}
|
||||
isTypeAttribute = false;
|
||||
} else if (token === TokenType.Script) {
|
||||
return [{ languageId, start: node.start + scanner.getTokenOffset(), end: node.start + scanner.getTokenEnd() }];
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
} else if (node.attributeNames) {
|
||||
let scanner: Scanner;
|
||||
let result;
|
||||
for (let name of node.attributeNames) {
|
||||
let languageId = getAttributeLanguage(name);
|
||||
if (languageId) {
|
||||
if (!scanner) {
|
||||
scanner = languageService.createScanner(document.getText().substring(node.start, node.end));
|
||||
}
|
||||
let token = scanner.scan();
|
||||
let lastAttribute;
|
||||
while (token !== TokenType.EOS) {
|
||||
if (token === TokenType.AttributeName) {
|
||||
lastAttribute = scanner.getTokenText();
|
||||
} else if (token === TokenType.AttributeValue && lastAttribute === name) {
|
||||
let start = scanner.getTokenOffset() + node.start;
|
||||
let end = scanner.getTokenEnd() + node.start;
|
||||
} else {
|
||||
let attributelLanguageId = getAttributeLanguage(lastAttributeName);
|
||||
if (attributelLanguageId) {
|
||||
let start = scanner.getTokenOffset();
|
||||
let end = scanner.getTokenEnd();
|
||||
let firstChar = document.getText()[start];
|
||||
if (firstChar === '\'' || firstChar === '"') {
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
if (!result) {
|
||||
result = [];
|
||||
}
|
||||
result.push({ languageId, start, end, attributeValue: true });
|
||||
lastAttribute = null;
|
||||
break;
|
||||
regions.push({ languageId: attributelLanguageId, start, end, attributeValue: true });
|
||||
}
|
||||
token = scanner.scan();
|
||||
}
|
||||
}
|
||||
lastAttributeName = null;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
token = scanner.scan();
|
||||
}
|
||||
return void 0;
|
||||
return regions;
|
||||
}
|
||||
|
||||
function getAttributeLanguage(attributeName: string): string {
|
||||
|
@ -257,11 +222,4 @@ function getAttributeLanguage(attributeName: string): string {
|
|||
return null;
|
||||
}
|
||||
return match[1] ? 'css' : 'javascript';
|
||||
}
|
||||
|
||||
function isWhitespace(str: string, start: number, end: number): boolean {
|
||||
if (start === end) {
|
||||
return true;
|
||||
}
|
||||
return !!str.substring(start, end).match(/^\s*$/);
|
||||
}
|
|
@ -4,14 +4,14 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { LanguageModelCache } from '../languageModelCache';
|
||||
import { getLanguageModelCache } from '../languageModelCache';
|
||||
import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions } from 'vscode-html-languageservice';
|
||||
import { TextDocument, Position, Range } from 'vscode-languageserver-types';
|
||||
import { LanguageMode } from './languageModes';
|
||||
|
||||
export function getHTMLMode(htmlLanguageService: HTMLLanguageService, htmlDocuments: LanguageModelCache<HTMLDocument>): LanguageMode {
|
||||
export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageMode {
|
||||
let settings: any = {};
|
||||
|
||||
let htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));
|
||||
return {
|
||||
getId() {
|
||||
return 'html';
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
|
||||
import { LanguageService as HTMLLanguageService, HTMLDocument } from 'vscode-html-languageservice';
|
||||
import { getEmbeddedDocument } from './embeddedSupport';
|
||||
import { LanguageModelCache } from '../languageModelCache';
|
||||
import { CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types';
|
||||
import { LanguageMode } from './languageModes';
|
||||
import { getWordAtText } from '../utils/words';
|
||||
|
@ -22,7 +20,7 @@ const FILE_NAME = 'typescript://singlefile/1'; // the same 'file' is used for a
|
|||
|
||||
const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g;
|
||||
|
||||
export function getJavascriptMode(htmlLanguageService: HTMLLanguageService, htmlDocuments: LanguageModelCache<HTMLDocument>): LanguageMode {
|
||||
export function getJavascriptMode(jsDocuments: LanguageModelCache<TextDocument>): LanguageMode {
|
||||
let compilerOptions = { allowNonTsExtensions: true, allowJs: true, target: ts.ScriptTarget.Latest };
|
||||
let currentTextDocument: TextDocument;
|
||||
let host = {
|
||||
|
@ -47,9 +45,6 @@ export function getJavascriptMode(htmlLanguageService: HTMLLanguageService, html
|
|||
};
|
||||
let jsLanguageService = ts.createLanguageService(host);
|
||||
|
||||
let jsDocuments = getLanguageModelCache<TextDocument>(10, 60, document => {
|
||||
return getEmbeddedDocument(htmlLanguageService, document, htmlDocuments.get(document), 'javascript');
|
||||
});
|
||||
let settings: any = {};
|
||||
|
||||
return {
|
||||
|
@ -217,10 +212,8 @@ export function getJavascriptMode(htmlLanguageService: HTMLLanguageService, html
|
|||
return null;
|
||||
},
|
||||
onDocumentRemoved(document: TextDocument) {
|
||||
jsDocuments.onDocumentRemoved(document);
|
||||
},
|
||||
dispose() {
|
||||
jsDocuments.dispose();
|
||||
jsLanguageService.dispose();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { HTMLDocument, getLanguageService as getHTMLLanguageService, DocumentContext } from 'vscode-html-languageservice';
|
||||
import { getLanguageService as getHTMLLanguageService, DocumentContext } from 'vscode-html-languageservice';
|
||||
import {
|
||||
CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range,
|
||||
Hover, DocumentHighlight, CompletionList, Position, FormattingOptions
|
||||
} from 'vscode-languageserver-types';
|
||||
|
||||
import { getLanguageModelCache } from '../languageModelCache';
|
||||
import { getLanguageAtPosition, getLanguagesInContent, getLanguagesInRange } from './embeddedSupport';
|
||||
import { getLanguageModelCache, LanguageModelCache } from '../languageModelCache';
|
||||
import { getDocumentRegions, HTMLDocumentRegions } from './embeddedSupport';
|
||||
import { getCSSMode } from './cssMode';
|
||||
import { getJavascriptMode } from './javascriptMode';
|
||||
import { getHTMLMode } from './htmlMode';
|
||||
|
@ -37,53 +37,59 @@ export interface LanguageMode {
|
|||
export interface LanguageModes {
|
||||
getModeAtPosition(document: TextDocument, position: Position): LanguageMode;
|
||||
getModesInRange(document: TextDocument, range: Range): LanguageModeRange[];
|
||||
getAllModesInDocument(document: TextDocument): LanguageMode[];
|
||||
getAllModes(): LanguageMode[];
|
||||
getAllModesInDocument(document: TextDocument): LanguageMode[];
|
||||
getMode(languageId: string): LanguageMode;
|
||||
onDocumentRemoved(document: TextDocument): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface LanguageModeRange extends Range {
|
||||
mode: LanguageMode;
|
||||
attributeValue?: boolean;
|
||||
}
|
||||
|
||||
export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }): LanguageModes {
|
||||
|
||||
var htmlLanguageService = getHTMLLanguageService();
|
||||
let htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));
|
||||
let documentRegions = getLanguageModelCache<HTMLDocumentRegions>(10, 60, document => getDocumentRegions(htmlLanguageService, document));
|
||||
|
||||
let modes = {
|
||||
'html': getHTMLMode(htmlLanguageService, htmlDocuments),
|
||||
'css': supportedLanguages['css'] && getCSSMode(htmlLanguageService, htmlDocuments),
|
||||
'javascript': supportedLanguages['javascript'] && getJavascriptMode(htmlLanguageService, htmlDocuments)
|
||||
};
|
||||
let modelCaches: LanguageModelCache<any>[] = [];
|
||||
modelCaches.push(documentRegions);
|
||||
|
||||
let modes = {};
|
||||
modes['html'] = getHTMLMode(htmlLanguageService);
|
||||
if (supportedLanguages['css']) {
|
||||
let embeddedCSSDocuments = getLanguageModelCache<TextDocument>(10, 60, document => documentRegions.get(document).getEmbeddedDocument('css'));
|
||||
modelCaches.push(embeddedCSSDocuments);
|
||||
modes['css'] = getCSSMode(embeddedCSSDocuments);
|
||||
}
|
||||
if (supportedLanguages['javascript']) {
|
||||
let embeddedJSDocuments = getLanguageModelCache<TextDocument>(10, 60, document => documentRegions.get(document).getEmbeddedDocument('javascript'));
|
||||
modelCaches.push(embeddedJSDocuments);
|
||||
modes['javascript'] = getJavascriptMode(embeddedJSDocuments);
|
||||
}
|
||||
return {
|
||||
getModeAtPosition(document: TextDocument, position: Position): LanguageMode {
|
||||
let languageId = getLanguageAtPosition(htmlLanguageService, document, htmlDocuments.get(document), position);
|
||||
let languageId = documentRegions.get(document).getLanguageAtPosition(position);;
|
||||
if (languageId) {
|
||||
return modes[languageId];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getAllModesInDocument(document: TextDocument): LanguageMode[] {
|
||||
let result = [];
|
||||
let languageIds = getLanguagesInContent(htmlLanguageService, document, htmlDocuments.get(document));
|
||||
for (let languageId of languageIds) {
|
||||
let mode = modes[languageId];
|
||||
if (mode) {
|
||||
result.push(mode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
getModesInRange(document: TextDocument, range: Range): LanguageModeRange[] {
|
||||
return getLanguagesInRange(htmlLanguageService, document, htmlDocuments.get(document), range).map(r => {
|
||||
return documentRegions.get(document).getLanguageRanges(range).map(r => {
|
||||
return {
|
||||
start: r.start,
|
||||
end: r.end,
|
||||
mode: modes[r.languageId]
|
||||
mode: modes[r.languageId],
|
||||
attributeValue: r.attributeValue
|
||||
};
|
||||
});
|
||||
},
|
||||
getAllModesInDocument(document: TextDocument): LanguageMode[] {
|
||||
return documentRegions.get(document).getLanguagesInDocument().map(languageId => modes[languageId]);
|
||||
},
|
||||
getAllModes(): LanguageMode[] {
|
||||
let result = [];
|
||||
for (let languageId in modes) {
|
||||
|
@ -96,6 +102,20 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo
|
|||
},
|
||||
getMode(languageId: string): LanguageMode {
|
||||
return modes[languageId];
|
||||
},
|
||||
onDocumentRemoved(document: TextDocument) {
|
||||
modelCaches.forEach(mc => mc.onDocumentRemoved(document));
|
||||
for (let mode in modes) {
|
||||
modes[mode].onDocumentRemoved(document);
|
||||
}
|
||||
},
|
||||
dispose(): void {
|
||||
modelCaches.forEach(mc => mc.dispose());
|
||||
modelCaches = [];
|
||||
for (let mode in modes) {
|
||||
modes[mode].dispose();
|
||||
}
|
||||
modes = {};
|
||||
}
|
||||
};
|
||||
}
|
|
@ -20,21 +20,18 @@ suite('HTML Embedded Support', () => {
|
|||
let document = TextDocument.create('test://test/test.html', 'html', 0, value);
|
||||
|
||||
let position = document.positionAt(offset);
|
||||
let ls = getLanguageService();
|
||||
let htmlDoc = ls.parseHTMLDocument(document);
|
||||
|
||||
let languageId = embeddedSupport.getLanguageAtPosition(htmlLanguageService, document, htmlDoc, position);
|
||||
let docRegions = embeddedSupport.getDocumentRegions(htmlLanguageService, document);
|
||||
let languageId = docRegions.getLanguageAtPosition(position);
|
||||
|
||||
assert.equal(languageId, expectedLanguageId);
|
||||
}
|
||||
|
||||
function assertEmbeddedLanguageContent(value: string, languageId: string, expectedContent: string): void {
|
||||
|
||||
let document = TextDocument.create('test://test/test.html', 'html', 0, value);
|
||||
|
||||
let ls = getLanguageService();
|
||||
let htmlDoc = ls.parseHTMLDocument(document);
|
||||
|
||||
let content = embeddedSupport.getEmbeddedDocument(ls, document, htmlDoc, languageId);
|
||||
let docRegions = embeddedSupport.getDocumentRegions(htmlLanguageService, document);
|
||||
let content = docRegions.getEmbeddedDocument(languageId);
|
||||
assert.equal(content.getText(), expectedContent);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ suite('HTML Embedded Formatting', () => {
|
|||
assertFormat('<html><head>\n <script>\nvar x=1;\nconsole.log("Hi");\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 1;\n console.log("Hi");\n</script>\n</head>\n\n</html>');
|
||||
|
||||
assertFormat('<html><head>\n |<script>\nvar x=1;\n</script>|</head></html>', '<html><head>\n <script>\n var x = 1;\n</script></head></html>');
|
||||
assertFormat('<html><head>\n <script>\n|var x=1;|\n</script></head></html>', '<html><head>\n <script>\n var x = 1;\n</script></head></html>');
|
||||
assertFormat('<html><head>\n <script>\n|var x=1;|\n</script></head></html>', '<html><head>\n <script>\n var x = 1;\n</script></head></html>');
|
||||
});
|
||||
|
||||
test('HTML & Multiple Scripts', function (): any {
|
||||
|
|
Loading…
Reference in a new issue