mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-06 22:54:50 +01:00
256 lines
9.2 KiB
TypeScript
256 lines
9.2 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* 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 {
|
|
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, FormattingOptions, RequestType, NotificationType,
|
|
CompletionList, Position, Hover
|
|
} from 'vscode-languageserver';
|
|
|
|
import { HTMLDocument, getLanguageService, CompletionConfiguration, HTMLFormatConfiguration, DocumentContext, TextDocument } from 'vscode-html-languageservice';
|
|
import { getLanguageModelCache } from './languageModelCache';
|
|
import { getEmbeddedContent, getEmbeddedLanguageAtPosition, hasEmbeddedContent } from './embeddedSupport';
|
|
import * as url from 'url';
|
|
import * as path from 'path';
|
|
import uri from 'vscode-uri';
|
|
|
|
import * as nls from 'vscode-nls';
|
|
nls.config(process.env['VSCODE_NLS_CONFIG']);
|
|
|
|
interface EmbeddedCompletionParams {
|
|
uri: string;
|
|
version: number;
|
|
embeddedLanguageId: string;
|
|
position: Position;
|
|
}
|
|
|
|
namespace EmbeddedCompletionRequest {
|
|
export const type: RequestType<EmbeddedCompletionParams, CompletionList, any> = { get method() { return 'embedded/completion'; } };
|
|
}
|
|
|
|
interface EmbeddedHoverParams {
|
|
uri: string;
|
|
version: number;
|
|
embeddedLanguageId: string;
|
|
position: Position;
|
|
}
|
|
|
|
namespace EmbeddedHoverRequest {
|
|
export const type: RequestType<EmbeddedCompletionParams, Hover, any> = { get method() { return 'embedded/hover'; } };
|
|
}
|
|
|
|
interface EmbeddedContentParams {
|
|
uri: string;
|
|
embeddedLanguageId: string;
|
|
}
|
|
|
|
interface EmbeddedContent {
|
|
content: string;
|
|
version: number;
|
|
}
|
|
|
|
namespace EmbeddedContentRequest {
|
|
export const type: RequestType<EmbeddedContentParams, EmbeddedContent, any> = { get method() { return 'embedded/content'; } };
|
|
}
|
|
|
|
interface EmbeddedContentChangedParams {
|
|
uri: string;
|
|
version: number;
|
|
embeddedLanguageIds: string[];
|
|
}
|
|
|
|
namespace EmbeddedContentChangedNotification {
|
|
export const type: NotificationType<EmbeddedContentChangedParams> = { get method() { return 'embedded/contentchanged'; } };
|
|
}
|
|
|
|
// Create a connection for the server
|
|
let connection: IConnection = createConnection();
|
|
|
|
console.log = connection.console.log.bind(connection.console);
|
|
console.error = connection.console.error.bind(connection.console);
|
|
|
|
// Create a simple text document manager. The text document manager
|
|
// supports full document sync only
|
|
let documents: TextDocuments = new TextDocuments();
|
|
// Make the text document manager listen on the connection
|
|
// for open, change and close text document events
|
|
documents.listen(connection);
|
|
|
|
let htmlDocuments = getLanguageModelCache<HTMLDocument>(10, 60, document => getLanguageService().parseHTMLDocument(document));
|
|
documents.onDidClose(e => {
|
|
htmlDocuments.onDocumentRemoved(e.document);
|
|
});
|
|
connection.onShutdown(() => {
|
|
htmlDocuments.dispose();
|
|
});
|
|
|
|
let workspacePath: string;
|
|
let embeddedLanguages: { [languageId: string]: boolean };
|
|
|
|
// 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 => {
|
|
workspacePath = params.rootPath;
|
|
embeddedLanguages = params.initializationOptions.embeddedLanguages;
|
|
return {
|
|
capabilities: {
|
|
// Tell the client that the server works in FULL text document sync mode
|
|
textDocumentSync: documents.syncKind,
|
|
completionProvider: { resolveProvider: false, triggerCharacters: ['.', ':', '<', '"', '=', '/'] },
|
|
hoverProvider: true,
|
|
documentHighlightProvider: true,
|
|
documentRangeFormattingProvider: params.initializationOptions['format.enable'],
|
|
documentLinkProvider: true
|
|
}
|
|
};
|
|
});
|
|
|
|
// create the JSON language service
|
|
var languageService = getLanguageService();
|
|
|
|
// The settings interface describes the server relevant settings part
|
|
interface Settings {
|
|
html: LanguageSettings;
|
|
}
|
|
|
|
interface LanguageSettings {
|
|
suggest: CompletionConfiguration;
|
|
format: HTMLFormatConfiguration;
|
|
}
|
|
|
|
let languageSettings: LanguageSettings;
|
|
|
|
// The settings have changed. Is send on server activation as well.
|
|
connection.onDidChangeConfiguration((change) => {
|
|
var settings = <Settings>change.settings;
|
|
languageSettings = settings.html;
|
|
});
|
|
|
|
let pendingValidationRequests: { [uri: string]: NodeJS.Timer } = {};
|
|
const validationDelayMs = 200;
|
|
|
|
// The content of a text document has changed. This event is emitted
|
|
// when the text document first opened or when its content has changed.
|
|
documents.onDidChangeContent(change => {
|
|
triggerValidation(change.document);
|
|
});
|
|
|
|
// a document has closed: clear all diagnostics
|
|
documents.onDidClose(event => {
|
|
cleanPendingValidation(event.document);
|
|
//connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
|
|
if (embeddedLanguages) {
|
|
connection.sendNotification(EmbeddedContentChangedNotification.type, { uri: event.document.uri, version: event.document.version, embeddedLanguageIds: [] });
|
|
}
|
|
});
|
|
|
|
function cleanPendingValidation(textDocument: TextDocument): void {
|
|
let request = pendingValidationRequests[textDocument.uri];
|
|
if (request) {
|
|
clearTimeout(request);
|
|
delete pendingValidationRequests[textDocument.uri];
|
|
}
|
|
}
|
|
|
|
function triggerValidation(textDocument: TextDocument): void {
|
|
cleanPendingValidation(textDocument);
|
|
pendingValidationRequests[textDocument.uri] = setTimeout(() => {
|
|
delete pendingValidationRequests[textDocument.uri];
|
|
validateTextDocument(textDocument);
|
|
}, validationDelayMs);
|
|
}
|
|
|
|
function validateTextDocument(textDocument: TextDocument): void {
|
|
let htmlDocument = htmlDocuments.get(textDocument);
|
|
//let diagnostics = languageService.doValidation(textDocument, htmlDocument);
|
|
// Send the computed diagnostics to VSCode.
|
|
//connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
|
if (embeddedLanguages) {
|
|
let embeddedLanguageIds = hasEmbeddedContent(languageService, textDocument, htmlDocument, embeddedLanguages);
|
|
let p = { uri: textDocument.uri, version: textDocument.version, embeddedLanguageIds };
|
|
connection.sendNotification(EmbeddedContentChangedNotification.type, p);
|
|
}
|
|
}
|
|
|
|
connection.onCompletion(textDocumentPosition => {
|
|
let document = documents.get(textDocumentPosition.textDocument.uri);
|
|
let htmlDocument = htmlDocuments.get(document);
|
|
let options = languageSettings && languageSettings.suggest;
|
|
let list = languageService.doComplete(document, textDocumentPosition.position, htmlDocument, options);
|
|
if (list.items.length === 0 && embeddedLanguages) {
|
|
let embeddedLanguageId = getEmbeddedLanguageAtPosition(languageService, document, htmlDocument, textDocumentPosition.position);
|
|
if (embeddedLanguageId && embeddedLanguages[embeddedLanguageId]) {
|
|
return connection.sendRequest(EmbeddedCompletionRequest.type, { uri: document.uri, version: document.version, embeddedLanguageId, position: textDocumentPosition.position });
|
|
}
|
|
}
|
|
return list;
|
|
});
|
|
|
|
connection.onHover(textDocumentPosition => {
|
|
let document = documents.get(textDocumentPosition.textDocument.uri);
|
|
let htmlDocument = htmlDocuments.get(document);
|
|
let hover = languageService.doHover(document, textDocumentPosition.position, htmlDocument);
|
|
if (!hover && embeddedLanguages) {
|
|
let embeddedLanguageId = getEmbeddedLanguageAtPosition(languageService, document, htmlDocument, textDocumentPosition.position);
|
|
if (embeddedLanguageId && embeddedLanguages[embeddedLanguageId]) {
|
|
return connection.sendRequest(EmbeddedHoverRequest.type, { uri: document.uri, version: document.version, embeddedLanguageId, position: textDocumentPosition.position });
|
|
}
|
|
}
|
|
return hover;
|
|
});
|
|
|
|
connection.onRequest(EmbeddedContentRequest.type, parms => {
|
|
let document = documents.get(parms.uri);
|
|
if (document) {
|
|
let htmlDocument = htmlDocuments.get(document);
|
|
return { content: getEmbeddedContent(languageService, document, htmlDocument, parms.embeddedLanguageId), version: document.version };
|
|
}
|
|
return void 0;
|
|
});
|
|
|
|
connection.onDocumentHighlight(documentHighlightParams => {
|
|
let document = documents.get(documentHighlightParams.textDocument.uri);
|
|
let htmlDocument = htmlDocuments.get(document);
|
|
return languageService.findDocumentHighlights(document, documentHighlightParams.position, htmlDocument);
|
|
});
|
|
|
|
function merge(src: any, dst: any): any {
|
|
for (var key in src) {
|
|
if (src.hasOwnProperty(key)) {
|
|
dst[key] = src[key];
|
|
}
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
function getFormattingOptions(formatParams: FormattingOptions) {
|
|
let formatSettings = languageSettings && languageSettings.format;
|
|
if (!formatSettings) {
|
|
return formatParams;
|
|
}
|
|
return merge(formatParams, merge(formatSettings, {}));
|
|
}
|
|
|
|
connection.onDocumentRangeFormatting(formatParams => {
|
|
let document = documents.get(formatParams.textDocument.uri);
|
|
return languageService.format(document, formatParams.range, getFormattingOptions(formatParams.options));
|
|
});
|
|
|
|
connection.onDocumentLinks(documentLinkParam => {
|
|
let document = documents.get(documentLinkParam.textDocument.uri);
|
|
let documentContext: DocumentContext = {
|
|
resolveReference: ref => {
|
|
if (ref[0] === '/') {
|
|
return uri.file(path.join(workspacePath, ref)).toString();
|
|
}
|
|
return url.resolve(document.uri, ref);
|
|
}
|
|
};
|
|
return languageService.findDocumentLinks(document, documentContext);
|
|
});
|
|
|
|
|
|
// Listen on the connection
|
|
connection.listen(); |