From a2035a572eba0c98c79ba3dd952e1991d0123200 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 7 Apr 2016 23:01:20 -0700 Subject: [PATCH 1/2] Add API support for LS host to specify script kind of a file to open --- src/harness/fourslash.ts | 16 +++++++------- src/harness/harnessLanguageService.ts | 8 +++---- src/server/client.ts | 4 ++-- src/server/editorServices.ts | 20 +++++++++++++----- src/server/protocol.d.ts | 5 +++++ src/server/session.ts | 21 ++++++++++++++++--- src/services/utilities.ts | 11 ++++++---- tests/cases/fourslash/fourslash.ts | 4 ++-- .../server/openFileWithSyntaxKind.ts | 19 +++++++++++++++++ 9 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 tests/cases/fourslash/server/openFileWithSyntaxKind.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 44057cad327..1e58ab50996 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -358,14 +358,14 @@ namespace FourSlash { } // Opens a file given its 0-based index or fileName - public openFile(index: number, content?: string): void; - public openFile(name: string, content?: string): void; - public openFile(indexOrName: any, content?: string) { + public openFile(index: number, content?: string, scriptKindName?: string): void; + public openFile(name: string, content?: string, scriptKindName?: string): void; + public openFile(indexOrName: any, content?: string, scriptKindName?: string) { const fileToOpen: FourSlashFile = this.findFile(indexOrName); fileToOpen.fileName = ts.normalizeSlashes(fileToOpen.fileName); this.activeFile = fileToOpen; // Let the host know that this file is now open - this.languageServiceAdapterHost.openFile(fileToOpen.fileName, content); + this.languageServiceAdapterHost.openFile(fileToOpen.fileName, content, scriptKindName); } public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) { @@ -2755,10 +2755,10 @@ namespace FourSlashInterface { // Opens a file, given either its index as it // appears in the test source, or its filename // as specified in the test metadata - public file(index: number, content?: string): void; - public file(name: string, content?: string): void; - public file(indexOrName: any, content?: string): void { - this.state.openFile(indexOrName, content); + public file(index: number, content?: string, scriptKindName?: string): void; + public file(name: string, content?: string, scriptKindName?: string): void; + public file(indexOrName: any, content?: string, scriptKindName?: string): void { + this.state.openFile(indexOrName, content, scriptKindName); } } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 12bb6a470e4..d74c2fdeb8e 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -157,7 +157,7 @@ namespace Harness.LanguageService { throw new Error("No script with name '" + fileName + "'"); } - public openFile(fileName: string, content?: string): void { + public openFile(fileName: string, content?: string, scriptKindName?: string): void { } /** @@ -496,9 +496,9 @@ namespace Harness.LanguageService { this.client = client; } - openFile(fileName: string, content?: string): void { - super.openFile(fileName, content); - this.client.openFile(fileName, content); + openFile(fileName: string, content?: string, scriptKindName?: string): void { + super.openFile(fileName, content, scriptKindName); + this.client.openFile(fileName, content, scriptKindName); } editScript(fileName: string, start: number, end: number, newText: string) { diff --git a/src/server/client.ts b/src/server/client.ts index 957d36e4a3a..35fb0b11fbe 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -120,8 +120,8 @@ namespace ts.server { return response; } - openFile(fileName: string, content?: string): void { - var args: protocol.OpenRequestArgs = { file: fileName, fileContent: content }; + openFile(fileName: string, content?: string, scriptKindName?: string): void { + var args: protocol.OpenRequestArgs = { file: fileName, fileContent: content, scriptKindName }; this.processRequest(CommandNames.Open, args); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4b9f17798c8..443eb766e03 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -34,6 +34,7 @@ namespace ts.server { fileWatcher: FileWatcher; formatCodeOptions = ts.clone(CompilerService.defaultFormatCodeOptions); path: Path; + scriptKind: ScriptKind; constructor(private host: ServerHost, public fileName: string, public content: string, public isOpen = false) { this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames)); @@ -192,8 +193,16 @@ namespace ts.server { return this.roots.map(root => root.fileName); } - getScriptKind() { - return ScriptKind.Unknown; + getScriptKind(fileName: string) { + const info = this.getScriptInfo(fileName); + if (!info) { + return undefined; + } + + if (!info.scriptKind) { + info.scriptKind = getScriptKindFromFileName(fileName); + } + return info.scriptKind; } getScriptVersion(filename: string) { @@ -988,7 +997,7 @@ namespace ts.server { * @param filename is absolute pathname * @param fileContent is a known version of the file content that is more up to date than the one on disk */ - openFile(fileName: string, openedByClient: boolean, fileContent?: string) { + openFile(fileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) { fileName = ts.normalizePath(fileName); let info = ts.lookUp(this.filenameToScriptInfo, fileName); if (!info) { @@ -1003,6 +1012,7 @@ namespace ts.server { } if (content !== undefined) { info = new ScriptInfo(this.host, fileName, content, openedByClient); + info.scriptKind = scriptKind; info.setFormatOptions(this.getFormatCodeOptions()); this.filenameToScriptInfo[fileName] = info; if (!info.isOpen) { @@ -1052,9 +1062,9 @@ namespace ts.server { * @param filename is absolute pathname * @param fileContent is a known version of the file content that is more up to date than the one on disk */ - openClientFile(fileName: string, fileContent?: string) { + openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind) { this.openOrUpdateConfiguredProjectForFile(fileName); - const info = this.openFile(fileName, /*openedByClient*/ true, fileContent); + const info = this.openFile(fileName, /*openedByClient*/ true, fileContent, scriptKind); this.addOpenFile(info); this.printProjects(); return info; diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 5ed227ebaa7..d6ff438a1c0 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -518,6 +518,11 @@ declare namespace ts.server.protocol { * Then the known content will be used upon opening instead of the disk copy */ fileContent?: string; + /** + * Used to specify the script kind of the file explicitly. It could be one of the following: + * ".ts", ".js", ".tsx", ".jsx" + */ + scriptKindName?: string; } /** diff --git a/src/server/session.ts b/src/server/session.ts index f0975a3f947..6dd74e8985b 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -529,9 +529,9 @@ namespace ts.server { * @param fileName is the name of the file to be opened * @param fileContent is a version of the file content that is known to be more up to date than the one on disk */ - private openClientFile(fileName: string, fileContent?: string) { + private openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind) { const file = ts.normalizePath(fileName); - this.projectService.openClientFile(file, fileContent); + this.projectService.openClientFile(file, fileContent, scriptKind); } private getQuickInfo(line: number, offset: number, fileName: string): protocol.QuickInfoResponseBody { @@ -967,7 +967,22 @@ namespace ts.server { }, [CommandNames.Open]: (request: protocol.Request) => { const openArgs = request.arguments; - this.openClientFile(openArgs.file, openArgs.fileContent); + let scriptKind: ScriptKind; + switch (openArgs.scriptKindName) { + case ".ts": + scriptKind = ScriptKind.TS; + break; + case ".js": + scriptKind = ScriptKind.JS; + break; + case ".tsx": + scriptKind = ScriptKind.TSX; + break; + case ".jsx": + scriptKind = ScriptKind.JSX; + break; + } + this.openClientFile(openArgs.file, openArgs.fileContent, scriptKind); return {responseRequired: false}; }, [CommandNames.Quickinfo]: (request: protocol.Request) => { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4d29cd99e12..332762a55dd 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -818,12 +818,15 @@ namespace ts { } export function getScriptKind(fileName: string, host?: LanguageServiceHost): ScriptKind { - // First check to see if the script kind can be determined from the file name - var scriptKind = getScriptKindFromFileName(fileName); - if (scriptKind === ScriptKind.Unknown && host && host.getScriptKind) { - // Next check to see if the host can resolve the script kind + // First check to see if the script kind was specified by the host. Chances are the host + // may override the default script kind for the file extension. + let scriptKind: ScriptKind; + if (host && host.getScriptKind) { scriptKind = host.getScriptKind(fileName); } + if (!scriptKind || scriptKind === ScriptKind.Unknown) { + scriptKind = getScriptKindFromFileName(fileName); + } return ensureScriptKind(fileName, scriptKind); } } \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index b69a757f01e..0e379017d08 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -110,8 +110,8 @@ declare namespace FourSlashInterface { type(definitionIndex?: number): void; position(position: number, fileIndex?: number): any; position(position: number, fileName?: string): any; - file(index: number, content?: string): any; - file(name: string, content?: string): any; + file(index: number, content?: string, scriptKindName?: string): any; + file(name: string, content?: string, scriptKindName?: string): any; } class verifyNegatable { private negative; diff --git a/tests/cases/fourslash/server/openFileWithSyntaxKind.ts b/tests/cases/fourslash/server/openFileWithSyntaxKind.ts new file mode 100644 index 00000000000..006e5490b6f --- /dev/null +++ b/tests/cases/fourslash/server/openFileWithSyntaxKind.ts @@ -0,0 +1,19 @@ +/// + +// Because the fourslash runner automatically opens the first file with the default setting, +// to test the openFile function, the targeted file cannot be the first one. + +// @Filename: dumbFile.ts +//// var x; + +// @allowJs: true +// @Filename: test.ts +//// /** +//// * @type {number} +//// */ +//// var t; +//// t. + +goTo.file("test.ts", /*content*/ undefined, ".js"); +goTo.eof(); +verify.completionListContains("toExponential"); From cc58e2d7eb144f0b2ff89e6a6685fb4deaa24fde Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 19 Apr 2016 15:17:36 -0700 Subject: [PATCH 2/2] Use string literal type for script kind names --- src/harness/harnessLanguageService.ts | 2 +- src/server/client.ts | 2 +- src/server/protocol.d.ts | 4 ++-- src/server/session.ts | 8 ++++---- tests/cases/fourslash/server/openFileWithSyntaxKind.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index d74c2fdeb8e..8f1b0493a85 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -496,7 +496,7 @@ namespace Harness.LanguageService { this.client = client; } - openFile(fileName: string, content?: string, scriptKindName?: string): void { + openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void { super.openFile(fileName, content, scriptKindName); this.client.openFile(fileName, content, scriptKindName); } diff --git a/src/server/client.ts b/src/server/client.ts index 35fb0b11fbe..bd5abbcfd77 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -120,7 +120,7 @@ namespace ts.server { return response; } - openFile(fileName: string, content?: string, scriptKindName?: string): void { + openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void { var args: protocol.OpenRequestArgs = { file: fileName, fileContent: content, scriptKindName }; this.processRequest(CommandNames.Open, args); } diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index d6ff438a1c0..10cebb2ddb7 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -520,9 +520,9 @@ declare namespace ts.server.protocol { fileContent?: string; /** * Used to specify the script kind of the file explicitly. It could be one of the following: - * ".ts", ".js", ".tsx", ".jsx" + * "TS", "JS", "TSX", "JSX" */ - scriptKindName?: string; + scriptKindName?: "TS" | "JS" | "TSX" | "JSX"; } /** diff --git a/src/server/session.ts b/src/server/session.ts index 6dd74e8985b..6fe8ed7b075 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -969,16 +969,16 @@ namespace ts.server { const openArgs = request.arguments; let scriptKind: ScriptKind; switch (openArgs.scriptKindName) { - case ".ts": + case "TS": scriptKind = ScriptKind.TS; break; - case ".js": + case "JS": scriptKind = ScriptKind.JS; break; - case ".tsx": + case "TSX": scriptKind = ScriptKind.TSX; break; - case ".jsx": + case "JSX": scriptKind = ScriptKind.JSX; break; } diff --git a/tests/cases/fourslash/server/openFileWithSyntaxKind.ts b/tests/cases/fourslash/server/openFileWithSyntaxKind.ts index 006e5490b6f..dcb1e54999e 100644 --- a/tests/cases/fourslash/server/openFileWithSyntaxKind.ts +++ b/tests/cases/fourslash/server/openFileWithSyntaxKind.ts @@ -14,6 +14,6 @@ //// var t; //// t. -goTo.file("test.ts", /*content*/ undefined, ".js"); +goTo.file("test.ts", /*content*/ undefined, "JS"); goTo.eof(); verify.completionListContains("toExponential");