diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 66d33f9d7d9..6b357628f33 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -399,14 +399,15 @@ namespace ts { } } - export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false): SourceFile { + export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { const start = new Date().getTime(); - const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes); + const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); parseTime += new Date().getTime() - start; return result; } + // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. // The SourceFile will be created with the compiler attempting to reuse as many nodes from @@ -416,8 +417,8 @@ namespace ts { // from this SourceFile that are being held onto may change as a result (including // becoming detached from any SourceFile). It is recommended that this SourceFile not // be used once 'update' is called on it. - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { - return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean, scriptKind?: ScriptKind): SourceFile { + return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, scriptKind); } /* @internal */ @@ -533,8 +534,10 @@ namespace ts { // attached to the EOF token. let parseErrorBeforeNextFinishedNode = false; - export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean): SourceFile { - const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0; + export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile { + + const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0 || scriptKind == ScriptKind.Js; + initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); @@ -6222,7 +6225,7 @@ namespace ts { } namespace IncrementalParser { - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean, scriptKind?: ScriptKind): SourceFile { aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); @@ -6234,7 +6237,7 @@ namespace ts { if (sourceFile.statements.length === 0) { // If we don't have any statements in the current source file, then there's no real // way to incrementally parse. So just do a full parse instead. - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, scriptKind); } // Make sure we're not trying to incrementally update a source file more than once. Once @@ -6298,7 +6301,7 @@ namespace ts { // inconsistent tree. Setting the parents on the new tree should be very fast. We // will immediately bail out of walking any subtrees when we can see that their parents // are already correct. - const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true); + const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, scriptKind); return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6b9f49cac01..d9613191c48 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,3 +1,4 @@ + namespace ts { export interface Map { [index: string]: T; @@ -2459,6 +2460,14 @@ namespace ts { character: number; } + export const enum ScriptKind { + Unknown = 0, + Js = 1, + Jsx = 2, + Ts = 3, + Tsx = 4 + } + export const enum ScriptTarget { ES3 = 0, ES5 = 1, diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 6ab9cd896c1..4bc5386252f 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -183,6 +183,7 @@ namespace Harness.LanguageService { const script = this.getScriptInfo(fileName); return script ? new ScriptSnapshot(script) : undefined; } + getScriptKind(fileName: string): ScriptKind { return ScriptKind.Unknown; } getScriptVersion(fileName: string): string { const script = this.getScriptInfo(fileName); return script ? script.version.toString() : undefined; @@ -253,6 +254,7 @@ namespace Harness.LanguageService { const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName); return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot); } + getScriptKind(fileName: string): ScriptKind { return this.nativeHost.getScriptKind(fileName); } getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); } getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index e2ec5cc159f..71907735b91 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -192,6 +192,10 @@ namespace ts.server { return this.roots.map(root => root.fileName); } + getScriptKind() { + return ScriptKind.Unknown; + } + getScriptVersion(filename: string) { return this.getScriptInfo(filename).svc.latestVersion().toString(); } diff --git a/src/services/services.ts b/src/services/services.ts index 9302e1ae366..2fa6031924c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -819,8 +819,8 @@ namespace ts { super(kind, pos, end); } - public update(newText: string, textChangeRange: TextChangeRange): SourceFile { - return updateSourceFile(this, newText, textChangeRange); + public update(newText: string, textChangeRange: TextChangeRange, scriptKind?: ScriptKind): SourceFile { + return updateSourceFile(this, newText, textChangeRange, /*aggressiveChecks*/ undefined, scriptKind); } public getLineAndCharacterOfPosition(position: number): LineAndCharacter { @@ -1019,6 +1019,7 @@ namespace ts { getNewLine?(): string; getProjectVersion?(): string; getScriptFileNames(): string[]; + getScriptKind(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot; getLocalizedDiagnosticMessages?(): any; @@ -1468,7 +1469,8 @@ namespace ts { fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, - version: string): SourceFile; + version: string, + scriptKind?: ScriptKind): SourceFile; /** * Request an updated version of an already existing SourceFile with a given fileName @@ -1486,7 +1488,8 @@ namespace ts { fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, - version: string): SourceFile; + version: string, + scriptKind?: ScriptKind): SourceFile; /** * Informs the DocumentRegistry that a file is not needed any longer. @@ -1657,6 +1660,7 @@ namespace ts { hostFileName: string; version: string; scriptSnapshot: IScriptSnapshot; + scriptKind: ScriptKind; } interface DocumentRegistryEntry { @@ -1746,12 +1750,14 @@ namespace ts { private createEntry(fileName: string, path: Path) { let entry: HostFileInformation; + const scriptKind = this.host.getScriptKind(fileName); const scriptSnapshot = this.host.getScriptSnapshot(fileName); if (scriptSnapshot) { entry = { hostFileName: fileName, version: this.host.getScriptVersion(fileName), - scriptSnapshot: scriptSnapshot + scriptSnapshot: scriptSnapshot, + scriptKind: scriptKind }; } @@ -1817,17 +1823,18 @@ namespace ts { throw new Error("Could not find file: '" + fileName + "'."); } + const scriptKind = this.host.getScriptKind(fileName); const version = this.host.getScriptVersion(fileName); let sourceFile: SourceFile; if (this.currentFileName !== fileName) { // This is a new file, just parse it - sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, ScriptTarget.Latest, version, /*setNodeParents*/ true); + sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, ScriptTarget.Latest, version, /*setNodeParents*/ true, scriptKind); } else if (this.currentFileVersion !== version) { // This is the same file, just a newer version. Incrementally parse the file. const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot); - sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange); + sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange, /*aggressiveChecks*/ undefined, scriptKind); } if (sourceFile) { @@ -1953,16 +1960,16 @@ namespace ts { return output.outputText; } - export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile { + export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile { const text = scriptSnapshot.getText(0, scriptSnapshot.getLength()); - const sourceFile = createSourceFile(fileName, text, scriptTarget, setNodeParents); + const sourceFile = createSourceFile(fileName, text, scriptTarget, setNodeParents, scriptKind); setSourceFileFields(sourceFile, scriptSnapshot, version); return sourceFile; } export let disableIncrementalParsing = false; - export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { + export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean, scriptKind?: ScriptKind): SourceFile { // If we were given a text change range, and our version or open-ness changed, then // incrementally parse this file. if (textChangeRange) { @@ -1996,7 +2003,7 @@ namespace ts { : (changedText + suffix); } - const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); + const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, scriptKind); setSourceFileFields(newSourceFile, scriptSnapshot, version); // after incremental parsing nameTable might not be up-to-date // drop it so it can be lazily recreated later @@ -2017,7 +2024,7 @@ namespace ts { } // Otherwise, just create a new source file. - return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true); + return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, scriptKind); } export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry { @@ -2059,12 +2066,12 @@ namespace ts { return JSON.stringify(bucketInfoArray, undefined, 2); } - function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true); + function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind); } - function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false); + function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind); } function acquireOrUpdateDocument( @@ -2072,7 +2079,8 @@ namespace ts { compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, - acquiring: boolean): SourceFile { + acquiring: boolean, + scriptKind?: ScriptKind): SourceFile { const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); const path = toPath(fileName, currentDirectory, getCanonicalFileName); @@ -2081,7 +2089,7 @@ namespace ts { Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?"); // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false); + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind); entry = { sourceFile: sourceFile, @@ -2095,8 +2103,8 @@ namespace ts { // the script snapshot. If so, update it appropriately. Otherwise, we can just // return it as is. if (entry.sourceFile.version !== version) { - entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, - scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot)); + entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, + scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot), /*aggressiveChecks*/ undefined, scriptKind); } } @@ -2848,14 +2856,14 @@ namespace ts { // it's source file any more, and instead defers to DocumentRegistry to get // either version 1, version 2 (or some other version) depending on what the // host says should be used. - return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } // We didn't already have the file. Fall through and acquire it from the registry. } // Could not find this file in the old program, create a new SourceFile for it. - return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } function sourceFileUpToDate(sourceFile: SourceFile): boolean { @@ -4641,7 +4649,7 @@ namespace ts { // Go to the original declaration for cases: // - // (1) when the aliased symbol was declared in the location(parent). + // (1) when the aliased symbol was declared in the location(parent). // (2) when the aliased symbol is originating from a named import. // if (node.kind === SyntaxKind.Identifier && diff --git a/src/services/shims.ts b/src/services/shims.ts index a4190d6752b..d7cf4da50fd 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -55,6 +55,7 @@ namespace ts { /** Returns a JSON-encoded value of the type: string[] */ getScriptFileNames(): string; + getScriptKind(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): ScriptSnapshotShim; getLocalizedDiagnosticMessages(): string; @@ -346,6 +347,14 @@ namespace ts { return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot); } + public getScriptKind(fileName: string): ScriptKind { + try { + return this.shimHost.getScriptKind(fileName); + } catch (e) { + return ScriptKind.Unknown; + } + } + public getScriptVersion(fileName: string): string { return this.shimHost.getScriptVersion(fileName); }