From 3b09010c7c601f7c2146240ddd444de8de74ced8 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 11 Nov 2016 17:35:11 -0800 Subject: [PATCH 1/2] enable syntactic features if project size exceeded the limit, send events when state of language service changes --- src/compiler/diagnosticMessages.json | 4 + .../unittests/tsserverProjectSystem.ts | 102 +++++++++++++- src/server/editorServices.ts | 43 +++++- src/server/lsHost.ts | 4 +- src/server/project.ts | 125 +++++++++++++++--- src/server/protocol.ts | 21 +++ src/server/session.ts | 12 +- src/server/utilities.ts | 63 --------- src/services/services.ts | 2 +- 9 files changed, 283 insertions(+), 93 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ce5e59e509c..be33335021b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3089,6 +3089,10 @@ "category": "Error", "code": 9003 }, + "Language service is disabled.": { + "category": "Error", + "code": 9004 + }, "JSX attributes must only be assigned a non-empty 'expression'.": { "category": "Error", "code": 17000 diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 9c3c7a4cd5b..32a78a07811 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -154,7 +154,6 @@ namespace ts.projectSystem { params.executingFilePath || getExecutingFilePathFromLibFile(), params.currentDirectory || "/", fileOrFolderList); - host.createFileOrFolder(safeList, /*createParentDirectory*/ true); return host; } @@ -355,7 +354,8 @@ namespace ts.projectSystem { reloadFS(filesOrFolders: FileOrFolder[]) { this.filesOrFolders = filesOrFolders; this.fs = createFileMap(); - for (const fileOrFolder of filesOrFolders) { + // always inject safelist file in the list of files + for (const fileOrFolder of filesOrFolders.concat(safeList)) { const path = this.toPath(fileOrFolder.path); const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); if (typeof fileOrFolder.content === "string") { @@ -1585,6 +1585,104 @@ namespace ts.projectSystem { projectService.closeClientFile(file1.path); checkNumberOfProjects(projectService, { configuredProjects: 0 }); }); + + it("language service disabled events are triggered", () => { + const f1 = { + path: "/a/app.js", + content: "let x = 1;" + }; + const f2 = { + path: "/a/largefile.js", + content: "" + }; + const config = { + path: "/a/jsconfig.json", + content: "{}" + }; + const configWithExclude = { + path: config.path, + content: JSON.stringify({ exclude: ["largefile.js"] }) + }; + const host = createServerHost([f1, f2, config]); + const originalGetFileSize = host.getFileSize; + host.getFileSize = (filePath: string) => + filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath); + + let lastEvent: server.ProjectLanguageServiceStateEvent; + const session = createSession(host, /*typingsInstaller*/ undefined, e => { + if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent) { + return; + } + assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); + lastEvent = e; + }); + session.executeCommand({ + seq: 0, + type: "request", + command: "open", + arguments: { file: f1.path } + }); + const projectService = session.getProjectService(); + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + const project = projectService.configuredProjects[0]; + assert.isFalse(project.languageServiceEnabled, "Language service enabled"); + assert.isTrue(!!lastEvent, "should receive event"); + assert.equal(lastEvent.data.project, project, "project name"); + assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state"); + + host.reloadFS([f1, f2, configWithExclude]); + host.triggerFileWatcherCallback(config.path, /*removed*/ false); + + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + assert.isTrue(project.languageServiceEnabled, "Language service enabled"); + assert.equal(lastEvent.data.project, project, "project"); + assert.isTrue(lastEvent.data.languageServiceEnabled, "Language service state"); + }); + + it("syntactic features work even if language service is disabled", () => { + const f1 = { + path: "/a/app.js", + content: "let x = 1;" + }; + const f2 = { + path: "/a/largefile.js", + content: "" + }; + const config = { + path: "/a/jsconfig.json", + content: "{}" + }; + const host = createServerHost([f1, f2, config]); + const originalGetFileSize = host.getFileSize; + host.getFileSize = (filePath: string) => + filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath); + let lastEvent: server.ProjectLanguageServiceStateEvent; + const session = createSession(host, /*typingsInstaller*/ undefined, e => { + if (e.eventName === server.ConfigFileDiagEvent) { + return; + } + assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); + lastEvent = e; + }); + session.executeCommand({ + seq: 0, + type: "request", + command: "open", + arguments: { file: f1.path } + }); + + const projectService = session.getProjectService(); + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + const project = projectService.configuredProjects[0]; + assert.isFalse(project.languageServiceEnabled, "Language service enabled"); + assert.isTrue(!!lastEvent, "should receive event"); + assert.equal(lastEvent.data.project, project, "project name"); + assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state"); + + const options = projectService.getFormatCodeOptions(); + const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options); + assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]); + }); }); describe("Proper errors", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d7dcfc810b4..f69b5c11547 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -10,8 +10,26 @@ namespace ts.server { export const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024; - export type ProjectServiceEvent = - { eventName: "context", data: { project: Project, fileName: NormalizedPath } } | { eventName: "configFileDiag", data: { triggerFile: string, configFileName: string, diagnostics: Diagnostic[] } }; + export const ContextEvent = "context"; + export const ConfigFileDiagEvent = "configFileDiag"; + export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState"; + + export interface ContextEvent { + eventName: typeof ContextEvent; + data: { project: Project; fileName: NormalizedPath }; + } + + export interface ConfigFileDiagEvent { + eventName: typeof ConfigFileDiagEvent; + data: { triggerFile: string, configFileName: string, diagnostics: Diagnostic[] }; + } + + export interface ProjectLanguageServiceStateEvent { + eventName: typeof ProjectLanguageServiceStateEvent; + data: { project: Project, languageServiceEnabled: boolean }; + } + + export type ProjectServiceEvent = ContextEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent; export interface ProjectServiceEventHandler { (event: ProjectServiceEvent): void; @@ -282,6 +300,16 @@ namespace ts.server { return this.compilerOptionsForInferredProjects; } + onUpdateLanguageServiceStateForProject(project: Project, languageServiceEnabled: boolean) { + if (!this.eventHandler) { + return; + } + this.eventHandler({ + eventName: ProjectLanguageServiceStateEvent, + data: { project, languageServiceEnabled } + }); + } + updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void { const project = this.findProject(response.projectName); if (!project) { @@ -430,7 +458,10 @@ namespace ts.server { } for (const openFile of this.openFiles) { - this.eventHandler({ eventName: "context", data: { project: openFile.getDefaultProject(), fileName: openFile.fileName } }); + this.eventHandler({ + eventName: ContextEvent, + data: { project: openFile.getDefaultProject(), fileName: openFile.fileName } + }); } } @@ -834,8 +865,8 @@ namespace ts.server { return; } - this.eventHandler({ - eventName: "configFileDiag", + this.eventHandler({ + eventName: ConfigFileDiagEvent, data: { configFileName, diagnostics: diagnostics || [], triggerFile } }); } @@ -1013,7 +1044,7 @@ namespace ts.server { const useExistingProject = this.useSingleInferredProject && this.inferredProjects.length; const project = useExistingProject ? this.inferredProjects[0] - : new InferredProject(this, this.documentRegistry, /*languageServiceEnabled*/ true, this.compilerOptionsForInferredProjects); + : new InferredProject(this, this.documentRegistry, this.compilerOptionsForInferredProjects); project.addRoot(root); diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index f1e80d95880..8f57cbf4074 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -3,9 +3,9 @@ /// namespace ts.server { - export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost, ServerLanguageServiceHost { + export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost { private compilationSettings: ts.CompilerOptions; - private readonly resolvedModuleNames= createFileMap>(); + private readonly resolvedModuleNames = createFileMap>(); private readonly resolvedTypeReferenceDirectives = createFileMap>(); private readonly getCanonicalFileName: (fileName: string) => string; diff --git a/src/server/project.ts b/src/server/project.ts index db862af7e0d..6239ba1be2e 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -90,16 +90,100 @@ namespace ts.server { } } + const emptyResult: any[] = []; + const getEmptyResult = () => emptyResult; + const getUndefined = () => undefined; + + export function createNoSemanticFeaturesWrapper(realLanguageService: LanguageService): LanguageService { + return { + cleanupSemanticCache: noop, + getSyntacticDiagnostics: (fileName) => + fileName ? realLanguageService.getSyntacticDiagnostics(fileName) : emptyResult, + getSemanticDiagnostics: getEmptyResult, + getCompilerOptionsDiagnostics: getEmptyResult, + getSyntacticClassifications: (fileName, span) => + realLanguageService.getSyntacticClassifications(fileName, span), + getEncodedSyntacticClassifications: (fileName, span) => + realLanguageService.getEncodedSyntacticClassifications(fileName, span), + getSemanticClassifications: getEmptyResult, + getEncodedSemanticClassifications: () => + ({ spans: [], endOfLineState: EndOfLineState.None }), + getCompletionsAtPosition: getUndefined, + findReferences: getEmptyResult, + getCompletionEntryDetails: getUndefined, + getQuickInfoAtPosition: getUndefined, + findRenameLocations: getEmptyResult, + getNameOrDottedNameSpan: (fileName, startPos, endPos) => + realLanguageService.getNameOrDottedNameSpan(fileName, startPos, endPos), + getBreakpointStatementAtPosition: (fileName, position) => + realLanguageService.getBreakpointStatementAtPosition(fileName, position), + getBraceMatchingAtPosition: (fileName, position) => + realLanguageService.getBraceMatchingAtPosition(fileName, position), + getSignatureHelpItems: getUndefined, + getDefinitionAtPosition: getEmptyResult, + getRenameInfo: () => ({ + canRename: false, + localizedErrorMessage: getLocaleSpecificMessage(Diagnostics.Language_service_is_disabled), + displayName: undefined, + fullDisplayName: undefined, + kind: undefined, + kindModifiers: undefined, + triggerSpan: undefined + }), + getTypeDefinitionAtPosition: getUndefined, + getReferencesAtPosition: getEmptyResult, + getDocumentHighlights: getEmptyResult, + getOccurrencesAtPosition: getEmptyResult, + getNavigateToItems: getEmptyResult, + getNavigationBarItems: fileName => + realLanguageService.getNavigationBarItems(fileName), + getNavigationTree: fileName => + realLanguageService.getNavigationTree(fileName), + getOutliningSpans: fileName => + realLanguageService.getOutliningSpans(fileName), + getTodoComments: getEmptyResult, + getIndentationAtPosition: (fileName, position, options) => + realLanguageService.getIndentationAtPosition(fileName, position, options), + getFormattingEditsForRange: (fileName, start, end, options) => + realLanguageService.getFormattingEditsForRange(fileName, start, end, options), + getFormattingEditsForDocument: (fileName, options) => + realLanguageService.getFormattingEditsForDocument(fileName, options), + getFormattingEditsAfterKeystroke: (fileName, position, key, options) => + realLanguageService.getFormattingEditsAfterKeystroke(fileName, position, key, options), + getDocCommentTemplateAtPosition: (fileName, position) => + realLanguageService.getDocCommentTemplateAtPosition(fileName, position), + isValidBraceCompletionAtPosition: (fileName, position, openingBrace) => + realLanguageService.isValidBraceCompletionAtPosition(fileName, position, openingBrace), + getEmitOutput: getUndefined, + getProgram: () => + realLanguageService.getProgram(), + getNonBoundSourceFile: fileName => + realLanguageService.getNonBoundSourceFile(fileName), + dispose: () => + realLanguageService.dispose(), + getCompletionEntrySymbol: getUndefined, + getImplementationAtPosition: getEmptyResult, + getSourceFile: fileName => + realLanguageService.getSourceFile(fileName), + getCodeFixesAtPosition: getEmptyResult + }; + } + export abstract class Project { private rootFiles: ScriptInfo[] = []; private rootFilesMap: FileMap = createFileMap(); - private lsHost: ServerLanguageServiceHost; + private lsHost: LSHost; private program: ts.Program; private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap(); private lastCachedUnresolvedImportsList: SortedReadonlyArray; - private languageService: LanguageService; + private readonly languageService: LanguageService; + // wrapper over the real language service that will suppress all semantic operations + private readonly noSemanticFeaturesLanguageService: LanguageService; + + public languageServiceEnabled = true; + builder: Builder; /** * Set of files that was returned from the last call to getChangesSinceVersion. @@ -147,7 +231,7 @@ namespace ts.server { readonly projectService: ProjectService, private documentRegistry: ts.DocumentRegistry, hasExplicitListOfFiles: boolean, - public languageServiceEnabled: boolean, + languageServiceEnabled: boolean, private compilerOptions: CompilerOptions, public compileOnSaveEnabled: boolean) { @@ -165,10 +249,13 @@ namespace ts.server { this.compilerOptions.noEmitForJsFiles = true; } - if (languageServiceEnabled) { - this.enableLanguageService(); - } - else { + this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken); + this.lsHost.setCompilationSettings(this.compilerOptions); + + this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry); + this.noSemanticFeaturesLanguageService = createNoSemanticFeaturesWrapper(this.languageService); + + if (!languageServiceEnabled) { this.disableLanguageService(); } @@ -184,7 +271,9 @@ namespace ts.server { if (ensureSynchronized) { this.updateGraph(); } - return this.languageService; + return this.languageServiceEnabled + ? this.languageService + : this.noSemanticFeaturesLanguageService; } getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] { @@ -200,18 +289,20 @@ namespace ts.server { } enableLanguageService() { - const lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken); - lsHost.setCompilationSettings(this.compilerOptions); - this.languageService = ts.createLanguageService(lsHost, this.documentRegistry); - - this.lsHost = lsHost; + if (this.languageServiceEnabled) { + return; + } this.languageServiceEnabled = true; + this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ true); } disableLanguageService() { - this.languageService = nullLanguageService; - this.lsHost = nullLanguageServiceHost; + if (!this.languageServiceEnabled) { + return; + } + this.languageService.cleanupSemanticCache(); this.languageServiceEnabled = false; + this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false); } abstract getProjectName(): string; @@ -676,12 +767,12 @@ namespace ts.server { // Used to keep track of what directories are watched for this project directoriesWatchedForTsconfig: string[] = []; - constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, languageServiceEnabled: boolean, compilerOptions: CompilerOptions) { + constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions) { super(ProjectKind.Inferred, projectService, documentRegistry, /*files*/ undefined, - languageServiceEnabled, + /*languageServiceEnabled*/ true, compilerOptions, /*compileOnSaveEnabled*/ false); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d13caf7f01b..de642a80bf9 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1814,6 +1814,27 @@ namespace ts.server.protocol { event: "configFileDiag"; } + export type ProjectLanguageServiceStateEventName = "projectLanguageServiceState"; + export interface ProjectLanguageServiceStateEvent extends Event { + event: ProjectLanguageServiceStateEventName; + body?: ProjectLanguageServiceStateEventBody; + } + + export interface ProjectLanguageServiceStateEventBody { + /** + * Project name that has changes in the state of language service. + * For configured projects this will be the config file path. + * For external projects this will be the name of the projects specified when project was open. + * For inferred projects this event is not raised. + */ + projectName: string; + /** + * True if language service state switched from disabled to enabled + * and false otherwise. + */ + languageServiceEnabled: boolean; + } + /** * Arguments for reload request. */ diff --git a/src/server/session.ts b/src/server/session.ts index b250393b7ff..36144b3212d 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -199,15 +199,23 @@ namespace ts.server { private defaultEventHandler(event: ProjectServiceEvent) { switch (event.eventName) { - case "context": + case ContextEvent: const { project, fileName } = event.data; this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`); this.updateErrorCheck([{ fileName, project }], this.changeSeq, (n) => n === this.changeSeq, 100); break; - case "configFileDiag": + case ConfigFileDiagEvent: const { triggerFile, configFileName, diagnostics } = event.data; this.configFileDiagnosticEvent(triggerFile, configFileName, diagnostics); + break; + case ProjectLanguageServiceStateEvent: + const eventName: protocol.ProjectLanguageServiceStateEventName = "projectLanguageServiceState"; + this.event({ + projectName: event.data.project.getProjectName(), + languageServiceEnabled: event.data.languageServiceEnabled + }, eventName); + break; } } diff --git a/src/server/utilities.ts b/src/server/utilities.ts index ac809652119..a52e5848de9 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -11,7 +11,6 @@ namespace ts.server { export const emptyArray: ReadonlyArray = []; - export interface Logger { close(): void; hasLevel(level: LogLevel): boolean; @@ -160,68 +159,6 @@ namespace ts.server { } }; } - function throwLanguageServiceIsDisabledError(): never { - throw new Error("LanguageService is disabled"); - } - - export const nullLanguageService: LanguageService = { - cleanupSemanticCache: throwLanguageServiceIsDisabledError, - getSyntacticDiagnostics: throwLanguageServiceIsDisabledError, - getSemanticDiagnostics: throwLanguageServiceIsDisabledError, - getCompilerOptionsDiagnostics: throwLanguageServiceIsDisabledError, - getSyntacticClassifications: throwLanguageServiceIsDisabledError, - getEncodedSyntacticClassifications: throwLanguageServiceIsDisabledError, - getSemanticClassifications: throwLanguageServiceIsDisabledError, - getEncodedSemanticClassifications: throwLanguageServiceIsDisabledError, - getCompletionsAtPosition: throwLanguageServiceIsDisabledError, - findReferences: throwLanguageServiceIsDisabledError, - getCompletionEntryDetails: throwLanguageServiceIsDisabledError, - getQuickInfoAtPosition: throwLanguageServiceIsDisabledError, - findRenameLocations: throwLanguageServiceIsDisabledError, - getNameOrDottedNameSpan: throwLanguageServiceIsDisabledError, - getBreakpointStatementAtPosition: throwLanguageServiceIsDisabledError, - getBraceMatchingAtPosition: throwLanguageServiceIsDisabledError, - getSignatureHelpItems: throwLanguageServiceIsDisabledError, - getDefinitionAtPosition: throwLanguageServiceIsDisabledError, - getRenameInfo: throwLanguageServiceIsDisabledError, - getTypeDefinitionAtPosition: throwLanguageServiceIsDisabledError, - getReferencesAtPosition: throwLanguageServiceIsDisabledError, - getDocumentHighlights: throwLanguageServiceIsDisabledError, - getOccurrencesAtPosition: throwLanguageServiceIsDisabledError, - getNavigateToItems: throwLanguageServiceIsDisabledError, - getNavigationBarItems: throwLanguageServiceIsDisabledError, - getNavigationTree: throwLanguageServiceIsDisabledError, - getOutliningSpans: throwLanguageServiceIsDisabledError, - getTodoComments: throwLanguageServiceIsDisabledError, - getIndentationAtPosition: throwLanguageServiceIsDisabledError, - getFormattingEditsForRange: throwLanguageServiceIsDisabledError, - getFormattingEditsForDocument: throwLanguageServiceIsDisabledError, - getFormattingEditsAfterKeystroke: throwLanguageServiceIsDisabledError, - getDocCommentTemplateAtPosition: throwLanguageServiceIsDisabledError, - isValidBraceCompletionAtPosition: throwLanguageServiceIsDisabledError, - getEmitOutput: throwLanguageServiceIsDisabledError, - getProgram: throwLanguageServiceIsDisabledError, - getNonBoundSourceFile: throwLanguageServiceIsDisabledError, - dispose: throwLanguageServiceIsDisabledError, - getCompletionEntrySymbol: throwLanguageServiceIsDisabledError, - getImplementationAtPosition: throwLanguageServiceIsDisabledError, - getSourceFile: throwLanguageServiceIsDisabledError, - getCodeFixesAtPosition: throwLanguageServiceIsDisabledError - }; - - export interface ServerLanguageServiceHost { - setCompilationSettings(options: CompilerOptions): void; - notifyFileRemoved(info: ScriptInfo): void; - startRecordingFilesWithChangedResolutions(): void; - finishRecordingFilesWithChangedResolutions(): Path[]; - } - - export const nullLanguageServiceHost: ServerLanguageServiceHost = { - setCompilationSettings: () => undefined, - notifyFileRemoved: () => undefined, - startRecordingFilesWithChangedResolutions: () => undefined, - finishRecordingFilesWithChangedResolutions: () => undefined - }; export interface ProjectOptions { /** diff --git a/src/services/services.ts b/src/services/services.ts index 56e604abeb3..3c0b7838314 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1205,7 +1205,7 @@ namespace ts { } function cleanupSemanticCache(): void { - // TODO: Should we jettison the program (or it's type checker) here? + program = undefined; } function dispose(): void { From 36511838a437b0807992dc8d61d5fdd1523a80d1 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 14 Nov 2016 10:41:48 -0800 Subject: [PATCH 2/2] allow getting compiler options diagnostics when language service is disabled --- src/server/project.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index 6239ba1be2e..3e5c810e805 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -93,6 +93,7 @@ namespace ts.server { const emptyResult: any[] = []; const getEmptyResult = () => emptyResult; const getUndefined = () => undefined; + const emptyEncodedSemanticClassifications = { spans: emptyResult, endOfLineState: EndOfLineState.None }; export function createNoSemanticFeaturesWrapper(realLanguageService: LanguageService): LanguageService { return { @@ -100,14 +101,15 @@ namespace ts.server { getSyntacticDiagnostics: (fileName) => fileName ? realLanguageService.getSyntacticDiagnostics(fileName) : emptyResult, getSemanticDiagnostics: getEmptyResult, - getCompilerOptionsDiagnostics: getEmptyResult, + getCompilerOptionsDiagnostics: () => + realLanguageService.getCompilerOptionsDiagnostics(), getSyntacticClassifications: (fileName, span) => realLanguageService.getSyntacticClassifications(fileName, span), getEncodedSyntacticClassifications: (fileName, span) => realLanguageService.getEncodedSyntacticClassifications(fileName, span), getSemanticClassifications: getEmptyResult, getEncodedSemanticClassifications: () => - ({ spans: [], endOfLineState: EndOfLineState.None }), + emptyEncodedSemanticClassifications, getCompletionsAtPosition: getUndefined, findReferences: getEmptyResult, getCompletionEntryDetails: getUndefined,