diff --git a/extensions/css/server/src/cssServerMain.ts b/extensions/css/server/src/cssServerMain.ts index 18f9eea6d89..fd325c05f18 100644 --- a/extensions/css/server/src/cssServerMain.ts +++ b/extensions/css/server/src/cssServerMain.ts @@ -10,7 +10,7 @@ import { } from 'vscode-languageserver'; import {getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService} from './cssLanguageService'; -import {Stylesheet} from './parser/cssNodes'; +import {getStylesheetCache} from './stylesheetCache'; import * as nls from 'vscode-nls'; nls.config(process.env['VSCODE_NLS_CONFIG']); @@ -29,6 +29,9 @@ export interface Settings { // stdin / stdout for message passing let connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process)); +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(); @@ -36,6 +39,13 @@ let documents: TextDocuments = new TextDocuments(); // for open, change and close text document events documents.listen(connection); +let stylesheets = getStylesheetCache(10, 60, document => getLanguageService(document).parseStylesheet(document)); +documents.onDidClose(e => { + stylesheets.onDocumentRemoved(e.document); +}); +connection.onShutdown(() => { + stylesheets.dispose(); +}); // 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. @@ -87,62 +97,58 @@ function updateConfiguration(settings: Settings) { } function validateTextDocument(textDocument: TextDocument): void { - let stylesheet = getStylesheet(textDocument); + let stylesheet = stylesheets.getStylesheet(textDocument); getLanguageService(textDocument).doValidation(textDocument, stylesheet).then(diagnostics => { // Send the computed diagnostics to VSCode. connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); }); } -function getStylesheet(document: TextDocument): Stylesheet { - return getLanguageService(document).parseStylesheet(document); -} - connection.onCompletion(textDocumentPosition => { let document = documents.get(textDocumentPosition.textDocument.uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).doComplete(document, textDocumentPosition.position, stylesheet); }); connection.onHover(textDocumentPosition => { let document = documents.get(textDocumentPosition.textDocument.uri); - let styleSheet = getStylesheet(document); + let styleSheet = stylesheets.getStylesheet(document); return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet); }); connection.onDocumentSymbol(documentSymbolParams => { let document = documents.get(documentSymbolParams.textDocument.uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).findDocumentSymbols(document, stylesheet); }); connection.onDefinition(documentSymbolParams => { let document = documents.get(documentSymbolParams.textDocument.uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).findDefinition(document, documentSymbolParams.position, stylesheet); }); connection.onDocumentHighlight(documentSymbolParams => { let document = documents.get(documentSymbolParams.textDocument.uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).findDocumentHighlights(document, documentSymbolParams.position, stylesheet); }); connection.onReferences(referenceParams => { let document = documents.get(referenceParams.textDocument.uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet); }); connection.onCodeAction(codeActionParams => { let document = documents.get(codeActionParams.textDocument.uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet); }); connection.onRequest(ColorSymbolRequest.type, uri => { let document = documents.get(uri); - let stylesheet = getStylesheet(document); + let stylesheet = stylesheets.getStylesheet(document); return getLanguageService(document).findColorSymbols(document, stylesheet); }); diff --git a/extensions/css/server/src/stylesheetCache.ts b/extensions/css/server/src/stylesheetCache.ts new file mode 100644 index 00000000000..1525ce3953e --- /dev/null +++ b/extensions/css/server/src/stylesheetCache.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * 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} from 'vscode-languageserver'; +import {Stylesheet} from './parser/cssNodes'; + +export interface StylesheetCache { + getStylesheet(document: TextDocument): Stylesheet; + onDocumentRemoved(document: TextDocument): void; + dispose(): void; +} + +export function getStylesheetCache(maxEntries: number, cleanupIntervalTimeInSec: number, parseStylesheet: (document: TextDocument) => Stylesheet) : StylesheetCache { + let styleSheets: { [uri:string]: {version:number, languageId: string, cTime: number, stylesheet: Stylesheet}} = {}; + let nStyleSheets = 0; + + let cleanupInterval = void 0; + if (cleanupIntervalTimeInSec > 0) { + cleanupInterval = setInterval(() => { + let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; + let uris = Object.keys(styleSheets); + for (let uri of uris) { + let stylesheetInfo = styleSheets[uri]; + if (stylesheetInfo.cTime < cutoffTime) { + delete styleSheets[uri]; + nStyleSheets--; + } + } + }, cleanupIntervalTimeInSec * 1000); + } + + return { + getStylesheet(document: TextDocument) { + let version = document.version; + let languageId = document.languageId; + let stylesheetInfo = styleSheets[document.uri]; + if (stylesheetInfo && stylesheetInfo.version === version && stylesheetInfo.languageId === languageId) { + stylesheetInfo.cTime = Date.now(); + return stylesheetInfo.stylesheet; + } + let stylesheet = parseStylesheet(document); + styleSheets[document.uri] = { stylesheet, version, languageId, cTime: Date.now()}; + if (!stylesheetInfo) { + nStyleSheets++; + } + + if (nStyleSheets === maxEntries) { + let oldestTime = Number.MAX_VALUE; + let oldestUri = null; + for (let uri in styleSheets) { + let stylesheetInfo = styleSheets[uri]; + if (stylesheetInfo.cTime < oldestTime) { + oldestUri = uri; + oldestTime = stylesheetInfo.cTime; + } + } + if (oldestUri) { + delete styleSheets[oldestUri]; + nStyleSheets--; + } + } + return stylesheet; + + }, + onDocumentRemoved(document: TextDocument) { + let uri = document.uri; + if (styleSheets[uri]) { + delete styleSheets[uri]; + nStyleSheets--; + } + }, + dispose() { + if (typeof cleanupInterval !== 'undefined') { + clearInterval(cleanupInterval); + cleanupInterval = void 0; + styleSheets = {}; + nStyleSheets = 0; + } + } + }; +} \ No newline at end of file