[html] add embedded JavaScript support

This commit is contained in:
Martin Aeschlimann 2016-11-16 10:58:34 +01:00
parent ab8e320022
commit 03813455b7
14 changed files with 64801 additions and 209 deletions

View file

@ -85,7 +85,8 @@ const copyrightFilter = [
'!**/*.opts',
'!**/*.disabled',
'!resources/win32/bin/code.js',
'!extensions/markdown/media/tomorrow.css'
'!extensions/markdown/media/tomorrow.css',
'!extensions/html/server/src/modes/typescript/*'
];
const tslintFilter = [

View file

@ -33,13 +33,13 @@ export function activate(context: ExtensionContext) {
};
let documentSelector = ['html', 'handlebars', 'razor'];
let embeddedLanguages = { 'css': true };
let embeddedLanguages = { css: true, javascript: true };
// Options to control the language client
let clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {
configurationSection: ['html'], // Synchronize the setting section 'html' to the server
configurationSection: ['html', 'css', 'javascript'], // the settings to synchronize
},
initializationOptions: {
embeddedLanguages,

View file

@ -6,7 +6,7 @@
import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType } from 'vscode-languageserver';
import { DocumentContext, TextDocument, Diagnostic, DocumentLink, Range } from 'vscode-html-languageservice';
import { getLanguageModes } from './languageModes';
import { getLanguageModes, LanguageModes } from './modes/languageModes';
import * as url from 'url';
import * as path from 'path';
@ -32,22 +32,24 @@ let documents: TextDocuments = new TextDocuments();
// for open, change and close text document events
documents.listen(connection);
let languageModes = getLanguageModes({ 'css': true });
documents.onDidClose(e => {
languageModes.getAllModes().forEach(m => m.onDocumentRemoved(e.document));
});
connection.onShutdown(() => {
languageModes.getAllModes().forEach(m => m.dispose());
});
let workspacePath: string;
var languageModes: LanguageModes;
// After the server has started the client sends an initilize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilites
connection.onInitialize((params: InitializeParams): InitializeResult => {
let initializationOptions = params.initializationOptions;
workspacePath = params.rootPath;
languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true });
documents.onDidClose(e => {
languageModes.getAllModes().forEach(m => m.onDocumentRemoved(e.document));
});
connection.onShutdown(() => {
languageModes.getAllModes().forEach(m => m.dispose());
});
return {
capabilities: {
// Tell the client that the server works in FULL text document sync mode
@ -55,8 +57,11 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
completionProvider: { resolveProvider: false, triggerCharacters: ['.', ':', '<', '"', '=', '/'] },
hoverProvider: true,
documentHighlightProvider: true,
documentRangeFormattingProvider: params.initializationOptions['format.enable'],
documentLinkProvider: true
documentRangeFormattingProvider: initializationOptions && initializationOptions['format.enable'],
documentLinkProvider: true,
definitionProvider: true,
signatureHelpProvider: { triggerCharacters: ['('] },
referencesProvider: true
}
};
});
@ -64,7 +69,12 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
languageModes.configure(change.settings);
languageModes.getAllModes().forEach(m => {
if (m.configure) {
m.configure(change.settings);
}
});
documents.all().forEach(triggerValidation);
});
let pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
@ -135,6 +145,33 @@ connection.onDocumentHighlight(documentHighlightParams => {
return [];
});
connection.onDefinition(definitionParams => {
let document = documents.get(definitionParams.textDocument.uri);
let mode = languageModes.getModeAtPosition(document, definitionParams.position);
if (mode && mode.findDefinition) {
return mode.findDefinition(document, definitionParams.position);
}
return [];
});
connection.onReferences(referenceParams => {
let document = documents.get(referenceParams.textDocument.uri);
let mode = languageModes.getModeAtPosition(document, referenceParams.position);
if (mode && mode.findReferences) {
return mode.findReferences(document, referenceParams.position);
}
return [];
});
connection.onSignatureHelp(signatureHelpParms => {
let document = documents.get(signatureHelpParms.textDocument.uri);
let mode = languageModes.getModeAtPosition(document, signatureHelpParms.position);
if (mode && mode.doSignatureHelp) {
return mode.doSignatureHelp(document, signatureHelpParms.position);
}
return null;
});
connection.onDocumentRangeFormatting(formatParams => {
let document = documents.get(formatParams.textDocument.uri);
let startMode = languageModes.getModeAtPosition(document, formatParams.range.start);

View file

@ -1,164 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
TextDocument, Position, HTMLDocument, Diagnostic, CompletionList, Hover, getLanguageService as getHTMLLanguageService,
DocumentHighlight, DocumentLink, DocumentContext, Range, TextEdit, FormattingOptions, CompletionConfiguration, HTMLFormatConfiguration
} from 'vscode-html-languageservice';
import { getCSSLanguageService, Stylesheet } from 'vscode-css-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { getEmbeddedDocument, getEmbeddedLanguageAtPosition, hasEmbeddedContent } from './embeddedSupport';
export interface LanguageMode {
doValidation?: (document: TextDocument) => Diagnostic[];
doComplete?: (document: TextDocument, position: Position) => CompletionList;
doHover?: (document: TextDocument, position: Position) => Hover;
findDocumentHighlight?: (document: TextDocument, position: Position) => DocumentHighlight[];
findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[];
format?: (document: TextDocument, range: Range, options: FormattingOptions) => TextEdit[];
findColorSymbols?: (document: TextDocument) => Range[];
onDocumentRemoved(document: TextDocument): void;
dispose(): void;
}
// The settings interface describes the server relevant settings part
export interface Settings {
html: HTMLLanguageSettings;
css: any;
}
export interface HTMLLanguageSettings {
suggest: CompletionConfiguration;
format: HTMLFormatConfiguration;
}
export interface LanguageModes {
getModeAtPosition(document: TextDocument, position: Position): LanguageMode;
getAllModesInDocument(document: TextDocument): LanguageMode[];
getAllModes(): LanguageMode[];
getMode(languageId: string): LanguageMode;
configure(options: Settings): void;
}
export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }): LanguageModes {
var htmlLanguageService = getHTMLLanguageService();
let htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));
let modes: { [id: string]: LanguageMode } = {};
let settings: any = {};
supportedLanguages['html'] = true;
modes['html'] = {
doComplete(document: TextDocument, position: Position) {
let options = settings && settings.html && settings.html.suggest;
return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options);
},
doHover(document: TextDocument, position: Position) {
return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));
},
findDocumentHighlight(document: TextDocument, position: Position) {
return htmlLanguageService.findDocumentHighlights(document, position, htmlDocuments.get(document));
},
findDocumentLinks(document: TextDocument, documentContext: DocumentContext) {
return htmlLanguageService.findDocumentLinks(document, documentContext);
},
format(document: TextDocument, range: Range, formatParams: FormattingOptions) {
let formatSettings = settings && settings.html && settings.html.format;
if (!formatSettings) {
formatSettings = formatParams;
} else {
formatSettings = merge(formatParams, merge(formatSettings, {}));
}
return htmlLanguageService.format(document, range, formatSettings);
},
onDocumentRemoved(document: TextDocument) {
htmlDocuments.onDocumentRemoved(document);
},
dispose() {
htmlDocuments.dispose();
}
};
if (supportedLanguages['css']) {
let cssLanguageService = getCSSLanguageService();
let cssStylesheets = getLanguageModelCache<Stylesheet>(10, 60, document => cssLanguageService.parseStylesheet(document));
let getEmbeddedCSSDocument = (document: TextDocument) => getEmbeddedDocument(htmlLanguageService, document, htmlDocuments.get(document), 'css');
modes['css'] = {
doValidation(document: TextDocument) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded));
},
doComplete(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.doComplete(embedded, position, cssStylesheets.get(embedded));
},
doHover(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded));
},
findDocumentHighlight(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.findDocumentHighlights(embedded, position, cssStylesheets.get(embedded));
},
findColorSymbols(document: TextDocument) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.findColorSymbols(embedded, cssStylesheets.get(embedded));
},
onDocumentRemoved(document: TextDocument) {
cssStylesheets.onDocumentRemoved(document);
},
dispose() {
cssStylesheets.dispose();
}
};
};
return {
getModeAtPosition(document: TextDocument, position: Position): LanguageMode {
let languageId = getEmbeddedLanguageAtPosition(htmlLanguageService, document, htmlDocuments.get(document), position);
if (supportedLanguages[languageId]) {
return modes[languageId];
}
return null;
},
getAllModesInDocument(document: TextDocument): LanguageMode[] {
let result = [modes['html']];
let embeddedLanguageIds = hasEmbeddedContent(htmlLanguageService, document, htmlDocuments.get(document));
for (let languageId of embeddedLanguageIds) {
if (supportedLanguages[languageId]) {
result.push(modes[languageId]);
}
}
return result;
},
getAllModes(): LanguageMode[] {
let result = [];
for (let languageId in modes) {
if (supportedLanguages[languageId]) {
result.push(modes[languageId]);
}
}
return result;
},
getMode(languageId: string): LanguageMode {
return modes[languageId];
},
configure(options: any): void {
settings = options;
}
};
}
function merge(src: any, dst: any): any {
for (var key in src) {
if (src.hasOwnProperty(key)) {
dst[key] = src[key];
}
}
return dst;
}

View file

@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'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 {
let cssLanguageService = getCSSLanguageService();
let cssStylesheets = getLanguageModelCache<Stylesheet>(10, 60, document => cssLanguageService.parseStylesheet(document));
let getEmbeddedCSSDocument = (document: TextDocument) => getEmbeddedDocument(htmlLanguageService, document, htmlDocuments.get(document), 'css');
return {
configure(options: any) {
cssLanguageService.configure(options && options.css);
},
doValidation(document: TextDocument) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded));
},
doComplete(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.doComplete(embedded, position, cssStylesheets.get(embedded));
},
doHover(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded));
},
findDocumentHighlight(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.findDocumentHighlights(embedded, position, cssStylesheets.get(embedded));
},
findDefinition(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.findDefinition(embedded, position, cssStylesheets.get(embedded));
},
findReferences(document: TextDocument, position: Position) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.findReferences(embedded, position, cssStylesheets.get(embedded));
},
findColorSymbols(document: TextDocument) {
let embedded = getEmbeddedCSSDocument(document);
return cssLanguageService.findColorSymbols(embedded, cssStylesheets.get(embedded));
},
onDocumentRemoved(document: TextDocument) {
cssStylesheets.onDocumentRemoved(document);
},
dispose() {
cssStylesheets.dispose();
}
};
};

View file

@ -7,7 +7,7 @@
import { TextDocument, Position, HTMLDocument, Node, LanguageService, TokenType } from 'vscode-html-languageservice';
export function getEmbeddedLanguageAtPosition(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument, position: Position): 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 && node.children.length === 0) {
@ -19,8 +19,8 @@ export function getEmbeddedLanguageAtPosition(languageService: LanguageService,
return 'html';
}
export function hasEmbeddedContent(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument): string[] {
let embeddedLanguageIds: { [languageId: string]: boolean } = {};
export function getLanguagesInContent(languageService: LanguageService, document: TextDocument, htmlDocument: HTMLDocument): string[] {
let embeddedLanguageIds: { [languageId: string]: boolean } = { html: true };
function collectEmbeddedLanguages(node: Node): void {
let c = getEmbeddedContentForNode(languageService, document, node);
if (c && !isWhitespace(document.getText().substring(c.start, c.end))) {

View file

@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { LanguageModelCache } 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 {
let settings: any = {};
return {
configure(options: any) {
settings = options && options.html;
},
doComplete(document: TextDocument, position: Position) {
let options = settings && settings.html && settings.html.suggest;
return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options);
},
doHover(document: TextDocument, position: Position) {
return htmlLanguageService.doHover(document, position, htmlDocuments.get(document));
},
findDocumentHighlight(document: TextDocument, position: Position) {
return htmlLanguageService.findDocumentHighlights(document, position, htmlDocuments.get(document));
},
findDocumentLinks(document: TextDocument, documentContext: DocumentContext) {
return htmlLanguageService.findDocumentLinks(document, documentContext);
},
format(document: TextDocument, range: Range, formatParams: FormattingOptions) {
let formatSettings = settings && settings.format;
if (!formatSettings) {
formatSettings = formatParams;
} else {
formatSettings = merge(formatParams, merge(formatSettings, {}));
}
return htmlLanguageService.format(document, range, formatSettings);
},
onDocumentRemoved(document: TextDocument) {
htmlDocuments.onDocumentRemoved(document);
},
dispose() {
htmlDocuments.dispose();
}
};
};
function merge(src: any, dst: any): any {
for (var key in src) {
if (src.hasOwnProperty(key)) {
dst[key] = src[key];
}
}
return dst;
}

View file

@ -0,0 +1,253 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache';
import { LanguageService as HTMLLanguageService, HTMLDocument } from 'vscode-html-languageservice';
import { getEmbeddedDocument } from './embeddedSupport';
import { 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 ts = require('./typescript/typescriptServices');
import { contents as libdts } from './typescript/lib-ts';
const DEFAULT_LIB = {
NAME: 'defaultLib:lib.d.ts',
CONTENTS: libdts
};
const FILE_NAME = 'typescript://singlefile/1'; // the same 'file' is used for all contents
export function getJavascriptMode(htmlLanguageService: HTMLLanguageService, htmlDocuments: LanguageModelCache<HTMLDocument>): LanguageMode {
let compilerOptions = { allowNonTsExtensions: true, allowJs: true, target: ts.ScriptTarget.Latest };
let currentTextDocument: TextDocument;
let host = {
getCompilationSettings: () => compilerOptions,
getScriptFileNames: () => [FILE_NAME],
getScriptVersion: (fileName: string) => {
if (fileName === FILE_NAME) {
return String(currentTextDocument.version);
}
return '1'; // default lib is static
},
getScriptSnapshot: (fileName: string) => {
let text = fileName === FILE_NAME ? currentTextDocument.getText() : DEFAULT_LIB.CONTENTS;
return {
getText: (start, end) => text.substring(start, end),
getLength: () => text.length,
getChangeRange: () => void 0
};
},
getCurrentDirectory: () => '',
getDefaultLibFileName: options => DEFAULT_LIB.NAME
};
let jsLanguageService = ts.createLanguageService(host);
let jsDocuments = getLanguageModelCache<TextDocument>(10, 60, document => {
return getEmbeddedDocument(htmlLanguageService, document, htmlDocuments.get(document), 'javascript');
});
let settings: any = {};
return {
configure(options: any) {
settings = options && options.html;
},
doValidation(document: TextDocument): Diagnostic[] {
currentTextDocument = jsDocuments.get(document);
const diagnostics = jsLanguageService.getSyntacticDiagnostics(FILE_NAME);
return diagnostics.map(diag => {
return {
range: convertRange(currentTextDocument, diag),
severity: DiagnosticSeverity.Error,
message: ts.flattenDiagnosticMessageText(diag.messageText, '\n')
};
});
},
doComplete(document: TextDocument, position: Position): CompletionList {
currentTextDocument = jsDocuments.get(document);
let completions = jsLanguageService.getCompletionsAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
return {
isIncomplete: false,
items: !completions ? [] : completions.entries.map(entry => {
return {
uri: document.uri,
position: position,
label: entry.name,
sortText: entry.sortText,
kind: convertKind(entry.kind)
};
})
};
},
doHover(document: TextDocument, position: Position): Hover {
currentTextDocument = jsDocuments.get(document);
let info = jsLanguageService.getQuickInfoAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (info) {
let contents = ts.displayPartsToString(info.displayParts);
return {
range: convertRange(currentTextDocument, info.textSpan),
contents: MarkedString.fromPlainText(contents)
};
}
return null;
},
doSignatureHelp(document: TextDocument, position: Position): SignatureHelp {
currentTextDocument = jsDocuments.get(document);
let signHelp = jsLanguageService.getSignatureHelpItems(FILE_NAME, currentTextDocument.offsetAt(position));
if (signHelp) {
let ret: SignatureHelp = {
activeSignature: signHelp.selectedItemIndex,
activeParameter: signHelp.argumentIndex,
signatures: []
};
signHelp.items.forEach(item => {
let signature: SignatureInformation = {
label: '',
documentation: null,
parameters: []
};
signature.label += ts.displayPartsToString(item.prefixDisplayParts);
item.parameters.forEach((p, i, a) => {
let label = ts.displayPartsToString(p.displayParts);
let parameter: ParameterInformation = {
label: label,
documentation: ts.displayPartsToString(p.documentation)
};
signature.label += label;
signature.parameters.push(parameter);
if (i < a.length - 1) {
signature.label += ts.displayPartsToString(item.separatorDisplayParts);
}
});
signature.label += ts.displayPartsToString(item.suffixDisplayParts);
ret.signatures.push(signature);
});
return ret;
};
return null;
},
findDocumentHighlight(document: TextDocument, position: Position): DocumentHighlight[] {
currentTextDocument = jsDocuments.get(document);
let occurrences = jsLanguageService.getOccurrencesAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (occurrences) {
return occurrences.map(entry => {
return {
range: convertRange(currentTextDocument, entry.textSpan),
kind: entry.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Text
};
});
};
return null;
},
findDefinition(document: TextDocument, position: Position): Definition {
currentTextDocument = jsDocuments.get(document);
let definition = jsLanguageService.getDefinitionAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (definition) {
return definition.filter(d => d.fileName === FILE_NAME).map(d => {
return {
uri: document.uri,
range: convertRange(currentTextDocument, d.textSpan)
};
});
}
return null;
},
findReferences(document: TextDocument, position: Position): Location[] {
currentTextDocument = jsDocuments.get(document);
let references = jsLanguageService.getReferencesAtPosition(FILE_NAME, currentTextDocument.offsetAt(position));
if (references) {
return references.filter(d => d.fileName === FILE_NAME).map(d => {
return {
uri: document.uri,
range: convertRange(currentTextDocument, d.textSpan)
};
});
}
return null;
},
format(document: TextDocument, range: Range, formatParams: FormattingOptions): TextEdit[] {
currentTextDocument = jsDocuments.get(document);
let formatSettings = convertOptions(formatParams, settings && settings.format);
let start = currentTextDocument.offsetAt(range.start);
let end = currentTextDocument.offsetAt(range.end);
let edits = jsLanguageService.getFormattingEditsForRange(FILE_NAME, start, end, formatSettings);
if (edits) {
return edits.map(e => ({
range: convertRange(currentTextDocument, e.span),
newText: e.newText
}));
}
return null;
},
onDocumentRemoved(document: TextDocument) {
jsDocuments.onDocumentRemoved(document);
},
dispose() {
jsDocuments.dispose();
jsLanguageService.dispose();
}
};
};
function convertRange(document: TextDocument, span: { start: number, length: number }): Range {
let startPosition = document.positionAt(span.start);
let endPosition = document.positionAt(span.start + span.length);
return Range.create(startPosition, endPosition);
}
function convertKind(kind: string): CompletionItemKind {
switch (kind) {
case 'primitive type':
case 'keyword':
return CompletionItemKind.Keyword;
case 'var':
case 'local var':
return CompletionItemKind.Variable;
case 'property':
case 'getter':
case 'setter':
return CompletionItemKind.Field;
case 'function':
case 'method':
case 'construct':
case 'call':
case 'index':
return CompletionItemKind.Function;
case 'enum':
return CompletionItemKind.Enum;
case 'module':
return CompletionItemKind.Module;
case 'class':
return CompletionItemKind.Class;
case 'interface':
return CompletionItemKind.Interface;
case 'warning':
return CompletionItemKind.File;
}
return CompletionItemKind.Property;
}
function convertOptions(options: FormattingOptions, formatSettings?: any): ts.FormatCodeOptions {
return {
ConvertTabsToSpaces: options.insertSpaces,
TabSize: options.tabSize,
IndentSize: options.tabSize,
IndentStyle: ts.IndentStyle.Smart,
NewLineCharacter: '\n',
BaseIndentSize: 1, //
InsertSpaceAfterCommaDelimiter: !formatSettings || formatSettings.insertSpaceAfterCommaDelimiter,
InsertSpaceAfterSemicolonInForStatements: !formatSettings || formatSettings.insertSpaceAfterSemicolonInForStatements,
InsertSpaceBeforeAndAfterBinaryOperators: !formatSettings || formatSettings.insertSpaceBeforeAndAfterBinaryOperators,
InsertSpaceAfterKeywordsInControlFlowStatements: !formatSettings || formatSettings.insertSpaceAfterKeywordsInControlFlowStatements,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: !formatSettings || formatSettings.insertSpaceAfterFunctionKeywordForAnonymousFunctions,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces,
PlaceOpenBraceOnNewLineForControlBlocks: formatSettings && formatSettings.placeOpenBraceOnNewLineForFunctions,
PlaceOpenBraceOnNewLineForFunctions: formatSettings && formatSettings.placeOpenBraceOnNewLineForControlBlocks
};
}

View file

@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { HTMLDocument, getLanguageService as getHTMLLanguageService, DocumentContext } from 'vscode-html-languageservice';
import { Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, Hover, DocumentHighlight, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types';
import { getLanguageModelCache } from '../languageModelCache';
import { getLanguageAtPosition, getLanguagesInContent } from './embeddedSupport';
import { getCSSMode } from './cssMode';
import { getJavascriptMode } from './javascriptMode';
import { getHTMLMode } from './htmlMode';
export interface LanguageMode {
configure?: (options: any) => void;
doValidation?: (document: TextDocument) => Diagnostic[];
doComplete?: (document: TextDocument, position: Position) => CompletionList;
doHover?: (document: TextDocument, position: Position) => Hover;
doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp;
findDocumentHighlight?: (document: TextDocument, position: Position) => DocumentHighlight[];
findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[];
findDefinition?: (document: TextDocument, position: Position) => Definition;
findReferences?: (document: TextDocument, position: Position) => Location[];
format?: (document: TextDocument, range: Range, options: FormattingOptions) => TextEdit[];
findColorSymbols?: (document: TextDocument) => Range[];
onDocumentRemoved(document: TextDocument): void;
dispose(): void;
}
export interface LanguageModes {
getModeAtPosition(document: TextDocument, position: Position): LanguageMode;
getAllModesInDocument(document: TextDocument): LanguageMode[];
getAllModes(): LanguageMode[];
getMode(languageId: string): LanguageMode;
}
export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }): LanguageModes {
var htmlLanguageService = getHTMLLanguageService();
let htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => htmlLanguageService.parseHTMLDocument(document));
let modes = {
'html': getHTMLMode(htmlLanguageService, htmlDocuments),
'css': supportedLanguages['css'] && getCSSMode(htmlLanguageService, htmlDocuments),
'javascript': supportedLanguages['javascript'] && getJavascriptMode(htmlLanguageService, htmlDocuments)
};
return {
getModeAtPosition(document: TextDocument, position: Position): LanguageMode {
let languageId = getLanguageAtPosition(htmlLanguageService, document, htmlDocuments.get(document), 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;
},
getAllModes(): LanguageMode[] {
let result = [];
for (let languageId in modes) {
let mode = modes[languageId];
if (mode) {
result.push(mode);
}
}
return result;
},
getMode(languageId: string): LanguageMode {
return modes[languageId];
}
};
}

View file

@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export declare var contents: string;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -5,15 +5,15 @@
'use strict';
import * as assert from 'assert';
import * as embeddedSupport from '../embeddedSupport';
import {TextDocument} from 'vscode-languageserver-types';
import { getLanguageService} from 'vscode-html-languageservice';
import * as embeddedSupport from '../modes/embeddedSupport';
import { TextDocument } from 'vscode-languageserver-types';
import { getLanguageService } from 'vscode-html-languageservice';
suite('HTML Embedded Support', () => {
var htmlLanguageService = getLanguageService();
function assertEmbeddedLanguageId(value: string, expectedLanguageId: string): void {
function assertLanguageId(value: string, expectedLanguageId: string): void {
let offset = value.indexOf('|');
value = value.substr(0, offset) + value.substr(offset + 1);
@ -23,7 +23,7 @@ suite('HTML Embedded Support', () => {
let ls = getLanguageService();
let htmlDoc = ls.parseHTMLDocument(document);
let languageId = embeddedSupport.getEmbeddedLanguageAtPosition(htmlLanguageService, document, htmlDoc, position);
let languageId = embeddedSupport.getLanguageAtPosition(htmlLanguageService, document, htmlDoc, position);
assert.equal(languageId, expectedLanguageId);
}
@ -39,13 +39,13 @@ suite('HTML Embedded Support', () => {
}
test('Styles', function (): any {
assertEmbeddedLanguageId('|<html><style>foo { }</style></html>', 'html');
assertEmbeddedLanguageId('<html|><style>foo { }</style></html>', 'html');
assertEmbeddedLanguageId('<html><st|yle>foo { }</style></html>', 'html');
assertEmbeddedLanguageId('<html><style>|foo { }</style></html>', 'css');
assertEmbeddedLanguageId('<html><style>foo| { }</style></html>', 'css');
assertEmbeddedLanguageId('<html><style>foo { }|</style></html>', 'css');
assertEmbeddedLanguageId('<html><style>foo { }</sty|le></html>', 'html');
assertLanguageId('|<html><style>foo { }</style></html>', 'html');
assertLanguageId('<html|><style>foo { }</style></html>', 'html');
assertLanguageId('<html><st|yle>foo { }</style></html>', 'html');
assertLanguageId('<html><style>|foo { }</style></html>', 'css');
assertLanguageId('<html><style>foo| { }</style></html>', 'css');
assertLanguageId('<html><style>foo { }|</style></html>', 'css');
assertLanguageId('<html><style>foo { }</sty|le></html>', 'html');
});
test('Style content', function (): any {
@ -57,20 +57,20 @@ suite('HTML Embedded Support', () => {
});
test('Scripts', function (): any {
assertEmbeddedLanguageId('|<html><script>var i = 0;</script></html>', 'html');
assertEmbeddedLanguageId('<html|><script>var i = 0;</script></html>', 'html');
assertEmbeddedLanguageId('<html><scr|ipt>var i = 0;</script></html>', 'html');
assertEmbeddedLanguageId('<html><script>|var i = 0;</script></html>', 'javascript');
assertEmbeddedLanguageId('<html><script>var| i = 0;</script></html>', 'javascript');
assertEmbeddedLanguageId('<html><script>var i = 0;|</script></html>', 'javascript');
assertEmbeddedLanguageId('<html><script>var i = 0;</scr|ipt></html>', 'html');
assertLanguageId('|<html><script>var i = 0;</script></html>', 'html');
assertLanguageId('<html|><script>var i = 0;</script></html>', 'html');
assertLanguageId('<html><scr|ipt>var i = 0;</script></html>', 'html');
assertLanguageId('<html><script>|var i = 0;</script></html>', 'javascript');
assertLanguageId('<html><script>var| i = 0;</script></html>', 'javascript');
assertLanguageId('<html><script>var i = 0;|</script></html>', 'javascript');
assertLanguageId('<html><script>var i = 0;</scr|ipt></html>', 'html');
assertEmbeddedLanguageId('<script type="text/javascript">var| i = 0;</script>', 'javascript');
assertEmbeddedLanguageId('<script type="text/ecmascript">var| i = 0;</script>', 'javascript');
assertEmbeddedLanguageId('<script type="application/javascript">var| i = 0;</script>', 'javascript');
assertEmbeddedLanguageId('<script type="application/ecmascript">var| i = 0;</script>', 'javascript');
assertEmbeddedLanguageId('<script type="application/typescript">var| i = 0;</script>', 'html');
assertEmbeddedLanguageId('<script type=\'text/javascript\'>var| i = 0;</script>', 'javascript');
assertLanguageId('<script type="text/javascript">var| i = 0;</script>', 'javascript');
assertLanguageId('<script type="text/ecmascript">var| i = 0;</script>', 'javascript');
assertLanguageId('<script type="application/javascript">var| i = 0;</script>', 'javascript');
assertLanguageId('<script type="application/ecmascript">var| i = 0;</script>', 'javascript');
assertLanguageId('<script type="application/typescript">var| i = 0;</script>', void 0);
assertLanguageId('<script type=\'text/javascript\'>var| i = 0;</script>', 'javascript');
});
test('Script content', function (): any {