diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 102351f3e18..2a4ecb14f64 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -385,20 +385,72 @@ namespace ts { // return undefined if we can't find a symbol. } - /** Returns true if node1 is defined before node 2**/ - function isDefinedBefore(node1: Node, node2: Node): boolean { - let file1 = getSourceFileOfNode(node1); - let file2 = getSourceFileOfNode(node2); - if (file1 === file2) { - return node1.pos <= node2.pos; + function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean { + const declarationFile = getSourceFileOfNode(declaration); + const useFile = getSourceFileOfNode(usage); + if (declarationFile !== useFile) { + if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) { + // nodes are in different files and order cannot be determines + return true; + } + + const sourceFiles = host.getSourceFiles(); + return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); } - if (!compilerOptions.outFile && !compilerOptions.out) { - return true; + if (declaration.pos <= usage.pos) { + // declaration is before usage + // still might be illegal if usage is in the initializer of the variable declaration + return declaration.kind !== SyntaxKind.VariableDeclaration || + !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration, usage); } - let sourceFiles = host.getSourceFiles(); - return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2); + // declaration is after usage + // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) + return isUsedInFunctionOrNonStaticProperty(declaration, usage); + + function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { + const container = getEnclosingBlockScopeContainer(declaration); + + if (declaration.parent.parent.kind === SyntaxKind.VariableStatement || + declaration.parent.parent.kind === SyntaxKind.ForStatement) { + // variable statement/for statement case, + // use site should not be inside variable declaration (initializer of declaration or binding element) + return isSameScopeDescendentOf(usage, declaration, container); + } + else if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement || + declaration.parent.parent.kind === SyntaxKind.ForInStatement) { + // ForIn/ForOf case - use site should not be used in expression part + let expression = (declaration.parent.parent).expression; + return isSameScopeDescendentOf(usage, expression, container); + } + } + + function isUsedInFunctionOrNonStaticProperty(declaration: Declaration, usage: Node): boolean { + const container = getEnclosingBlockScopeContainer(declaration); + let current = usage; + while (current) { + if (current === container) { + return false; + } + + if (isFunctionLike(current)) { + return true; + } + + const initializerOfNonStaticProperty = current.parent && + current.parent.kind === SyntaxKind.PropertyDeclaration && + (current.parent.flags & NodeFlags.Static) === 0 && + (current.parent).initializer === current; + + if (initializerOfNonStaticProperty) { + return true; + } + + current = current.parent; + } + return false; + } } // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and @@ -629,34 +681,7 @@ namespace ts { Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined"); - // first check if usage is lexically located after the declaration - let isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation); - if (!isUsedBeforeDeclaration) { - // lexical check succeeded however code still can be illegal. - // - block scoped variables cannot be used in its initializers - // let x = x; // illegal but usage is lexically after definition - // - in ForIn/ForOf statements variable cannot be contained in expression part - // for (let x in x) - // for (let x of x) - - // climb up to the variable declaration skipping binding patterns - let variableDeclaration = getAncestor(declaration, SyntaxKind.VariableDeclaration); - let container = getEnclosingBlockScopeContainer(variableDeclaration); - - if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) { - // variable statement/for statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container); - } - else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) { - // ForIn/ForOf case - use site should not be used in expression part - let expression = (variableDeclaration.parent.parent).expression; - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container); - } - } - if (isUsedBeforeDeclaration) { + if (!isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration), errorLocation)) { error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); } } @@ -11758,10 +11783,6 @@ namespace ts { checkSignatureDeclaration(node); let isAsync = isAsyncFunctionLike(node); if (isAsync) { - if (!compilerOptions.experimentalAsyncFunctions) { - error(node, Diagnostics.Experimental_support_for_async_functions_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalAsyncFunctions_to_remove_this_warning); - } - emitAwaiter = true; } @@ -13350,7 +13371,7 @@ namespace ts { } // illegal case: forward reference - if (!isDefinedBefore(propertyDecl, member)) { + if (!isBlockScopedNameDeclaredBeforeUse(propertyDecl, member)) { reportError = false; error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums); return undefined; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 8fec8cb1df1..5b73ec9d8b9 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -77,6 +77,7 @@ namespace ts { "system": ModuleKind.System, "umd": ModuleKind.UMD, "es6": ModuleKind.ES6, + "es2015": ModuleKind.ES2015, }, description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es6, paramType: Diagnostics.KIND, @@ -205,7 +206,12 @@ namespace ts { { name: "target", shortName: "t", - type: { "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6 }, + type: { + "es3": ScriptTarget.ES3, + "es5": ScriptTarget.ES5, + "es6": ScriptTarget.ES6, + "es2015": ScriptTarget.ES2015, + }, description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental, paramType: Diagnostics.VERSION, error: Diagnostics.Argument_for_target_option_must_be_ES3_ES5_or_ES6 @@ -222,11 +228,6 @@ namespace ts { type: "boolean", description: Diagnostics.Watch_input_files, }, - { - name: "experimentalAsyncFunctions", - type: "boolean", - description: Diagnostics.Enables_experimental_support_for_ES7_async_functions - }, { name: "experimentalDecorators", type: "boolean", @@ -409,7 +410,7 @@ namespace ts { catch (e) { return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) }; } - return parseConfigFileText(fileName, text); + return parseConfigFileTextToJson(fileName, text); } /** @@ -417,7 +418,7 @@ namespace ts { * @param fileName The path to the config file * @param jsonText The text of the config file */ - export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { + export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { try { return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} }; } @@ -432,7 +433,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { let errors: Diagnostic[] = []; return { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index caa246373a2..9a4f5452eed 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -437,8 +437,12 @@ namespace ts { } export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): DiagnosticMessageChain { - Debug.assert(!headChain.next); - headChain.next = tailChain; + let lastChain = headChain; + while (lastChain.next) { + lastChain = lastChain.next; + } + + lastChain.next = tailChain; return headChain; } @@ -700,6 +704,9 @@ namespace ts { } export function getBaseFileName(path: string) { + if (!path) { + return undefined; + } let i = path.lastIndexOf(directorySeparator); return i < 0 ? path : path.substring(i + 1); } @@ -729,6 +736,17 @@ namespace ts { */ export const moduleFileExtensions = supportedExtensions; + export function isSupportedSourceFileName(fileName: string) { + if (!fileName) { return false; } + + for (let extension of supportedExtensions) { + if (fileExtensionIs(fileName, extension)) { + return true; + } + } + return false; + } + const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; export function removeFileExtension(path: string): string { for (let ext of extensionsToRemove) { @@ -823,4 +841,14 @@ namespace ts { Debug.assert(false, message); } } -} + + export function copyListRemovingItem(item: T, list: T[]) { + let copiedList: T[] = []; + for (var i = 0, len = list.length; i < len; i++) { + if (list[i] !== item) { + copiedList.push(list[i]); + } + } + return copiedList; + } +} \ No newline at end of file diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2146e277296..f33b3254e69 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -783,10 +783,6 @@ "category": "Error", "code": 1245 }, - "Experimental support for async functions is a feature that is subject to change in a future release. Specify '--experimentalAsyncFunctions' to remove this warning.": { - "category": "Error", - "code": 1246 - }, "'with' statements are not allowed in an async function block.": { "category": "Error", @@ -2282,10 +2278,6 @@ "category": "Message", "code": 6066 }, - "Option 'experimentalAsyncFunctions' cannot be specified when targeting ES5 or lower.": { - "category": "Message", - "code": 6067 - }, "Enables experimental support for ES7 async functions.": { "category": "Message", "code": 6068 diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 73920fd238a..86719ff30b4 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1071,11 +1071,6 @@ namespace ts { !options.experimentalDecorators) { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators")); } - - if (options.experimentalAsyncFunctions && - options.target !== ScriptTarget.ES6) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_experimentalAsyncFunctions_cannot_be_specified_when_targeting_ES5_or_lower)); - } } } } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 76f12666878..0872a2e5ba5 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -8,7 +8,8 @@ namespace ts { write(s: string): void; readFile(path: string, encoding?: string): string; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - watchFile?(path: string, callback: (path: string, removed: boolean) => void): FileWatcher; + watchFile?(path: string, callback: (path: string, removed?: boolean) => void): FileWatcher; + watchDirectory?(path: string, callback: (path: string) => void, recursive?: boolean): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; directoryExists(path: string): boolean; @@ -20,6 +21,12 @@ namespace ts { exit(exitCode?: number): void; } + interface WatchedFile { + fileName: string; + callback: (fileName: string, removed?: boolean) => void; + mtime: Date; + } + export interface FileWatcher { close(): void; } @@ -192,6 +199,103 @@ namespace ts { const _path = require("path"); const _os = require("os"); + // average async stat takes about 30 microseconds + // set chunk size to do 30 files in < 1 millisecond + function createWatchedFileSet(interval = 2500, chunkSize = 30) { + let watchedFiles: WatchedFile[] = []; + let nextFileToCheck = 0; + let watchTimer: any; + + function getModifiedTime(fileName: string): Date { + return _fs.statSync(fileName).mtime; + } + + function poll(checkedIndex: number) { + let watchedFile = watchedFiles[checkedIndex]; + if (!watchedFile) { + return; + } + + _fs.stat(watchedFile.fileName, (err: any, stats: any) => { + if (err) { + watchedFile.callback(watchedFile.fileName); + } + else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { + watchedFile.mtime = getModifiedTime(watchedFile.fileName); + watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0); + } + }); + } + + // this implementation uses polling and + // stat due to inconsistencies of fs.watch + // and efficiency of stat on modern filesystems + function startWatchTimer() { + watchTimer = setInterval(() => { + let count = 0; + let nextToCheck = nextFileToCheck; + let firstCheck = -1; + while ((count < chunkSize) && (nextToCheck !== firstCheck)) { + poll(nextToCheck); + if (firstCheck < 0) { + firstCheck = nextToCheck; + } + nextToCheck++; + if (nextToCheck === watchedFiles.length) { + nextToCheck = 0; + } + count++; + } + nextFileToCheck = nextToCheck; + }, interval); + } + + function addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile { + let file: WatchedFile = { + fileName, + callback, + mtime: getModifiedTime(fileName) + }; + + watchedFiles.push(file); + if (watchedFiles.length === 1) { + startWatchTimer(); + } + return file; + } + + function removeFile(file: WatchedFile) { + watchedFiles = copyListRemovingItem(file, watchedFiles); + } + + return { + getModifiedTime: getModifiedTime, + poll: poll, + startWatchTimer: startWatchTimer, + addFile: addFile, + removeFile: removeFile + }; + } + + // REVIEW: for now this implementation uses polling. + // The advantage of polling is that it works reliably + // on all os and with network mounted files. + // For 90 referenced files, the average time to detect + // changes is 2*msInterval (by default 5 seconds). + // The overhead of this is .04 percent (1/2500) with + // average pause of < 1 millisecond (and max + // pause less than 1.5 milliseconds); question is + // do we anticipate reference sets in the 100s and + // do we care about waiting 10-20 seconds to detect + // changes for large reference sets? If so, do we want + // to increase the chunk size or decrease the interval + // time dynamically to match the large reference set? + let watchedFileSet = createWatchedFileSet(); + + function isNode4OrLater(): Boolean { + return parseInt(process.version.charAt(1)) >= 4; + } + const platform: string = _os.platform(); // win32\win64 are case insensitive platforms, MacOS (darwin) by default is also case insensitive const useCaseSensitiveFileNames = platform !== "win32" && platform !== "win64" && platform !== "darwin"; @@ -284,25 +388,36 @@ namespace ts { readFile, writeFile, watchFile: (fileName, callback) => { - // watchFile polls a file every 250ms, picking up file notifications. - _fs.watchFile(fileName, { persistent: true, interval: 250 }, fileChanged); - - return { - close() { _fs.unwatchFile(fileName, fileChanged); } - }; - - function fileChanged(curr: any, prev: any) { - // mtime.getTime() equals 0 if file was removed - if (curr.mtime.getTime() === 0) { - callback(fileName, /* removed */ true); - return; - } - if (+curr.mtime <= +prev.mtime) { - return; - } - - callback(fileName, /* removed */ false); + // Node 4.0 stablized the `fs.watch` function on Windows which avoids polling + // and is more efficient than `fs.watchFile` (ref: https://github.com/nodejs/node/pull/2649 + // and https://github.com/Microsoft/TypeScript/issues/4643), therefore + // if the current node.js version is newer than 4, use `fs.watch` instead. + if (isNode4OrLater()) { + // Note: in node the callback of fs.watch is given only the relative file name as a parameter + return _fs.watch(fileName, (eventName: string, relativeFileName: string) => callback(fileName)); } + + let watchedFile = watchedFileSet.addFile(fileName, callback); + return { + close: () => watchedFileSet.removeFile(watchedFile) + }; + }, + watchDirectory: (path, callback, recursive) => { + // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows + // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) + return _fs.watch( + path, + { persisten: true, recursive: !!recursive }, + (eventName: string, relativeFileName: string) => { + // In watchDirectory we only care about adding and removing files (when event name is + // "rename"); changes made within files are handled by corresponding fileWatchers (when + // event name is "change") + if (eventName === "rename") { + // When deleting a file, the passed baseFileName is null + callback(!relativeFileName ? relativeFileName : normalizePath(ts.combinePaths(path, relativeFileName))); + }; + } + ); }, resolvePath: function (path: string): string { return _path.resolve(path); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 7b74024aec4..9d79d435a04 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -147,14 +147,17 @@ namespace ts { export function executeCommandLine(args: string[]): void { let commandLine = parseCommandLine(args); - let configFileName: string; // Configuration file name (if any) - let configFileWatcher: FileWatcher; // Configuration file watcher - let cachedProgram: Program; // Program cached from last compilation - let rootFileNames: string[]; // Root fileNames for compilation - let compilerOptions: CompilerOptions; // Compiler options for compilation - let compilerHost: CompilerHost; // Compiler host - let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host - let timerHandle: number; // Handle for 0.25s wait timer + let configFileName: string; // Configuration file name (if any) + let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any) + let configFileWatcher: FileWatcher; // Configuration file watcher + let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal + let cachedProgram: Program; // Program cached from last compilation + let rootFileNames: string[]; // Root fileNames for compilation + let compilerOptions: CompilerOptions; // Compiler options for compilation + let compilerHost: CompilerHost; // Compiler host + let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host + let timerHandleForRecompilation: number; // Handle for 0.25s wait timer to trigger recompilation + let timerHandleForDirectoryChanges: number; // Handle for 0.25s wait timer to trigger directory change handler if (commandLine.options.locale) { if (!isJSONSupported()) { @@ -218,28 +221,49 @@ namespace ts { if (configFileName) { configFileWatcher = sys.watchFile(configFileName, configFileChanged); } + if (sys.watchDirectory && configFileName) { + let directory = ts.getDirectoryPath(configFileName); + directoryWatcher = sys.watchDirectory( + // When the configFileName is just "tsconfig.json", the watched directory should be + // the current direcotry; if there is a given "project" parameter, then the configFileName + // is an absolute file name. + directory == "" ? "." : directory, + watchedDirectoryChanged, /*recursive*/ true); + } } performCompilation(); + function parseConfigFile(): ParsedCommandLine { + if (!cachedConfigFileText) { + try { + cachedConfigFileText = sys.readFile(configFileName); + } + catch (e) { + let error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); + reportWatchDiagnostic(error); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } + } + + let result = parseConfigFileTextToJson(configFileName, cachedConfigFileText); + let configObject = result.config; + let configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName)); + if (configParseResult.errors.length > 0) { + reportDiagnostics(configParseResult.errors); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return; + } + return configParseResult; + } + // Invoked to perform initial compilation or re-compilation in watch mode function performCompilation() { if (!cachedProgram) { if (configFileName) { - - let result = readConfigFile(configFileName, sys.readFile); - if (result.error) { - reportWatchDiagnostic(result.error); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - let configObject = result.config; - let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName)); - if (configParseResult.errors.length > 0) { - reportDiagnostics(configParseResult.errors); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } + let configParseResult = parseConfigFile(); rootFileNames = configParseResult.fileNames; compilerOptions = extend(commandLine.options, configParseResult.options); } @@ -275,7 +299,7 @@ namespace ts { let sourceFile = hostGetSourceFile(fileName, languageVersion, onError); if (sourceFile && compilerOptions.watch) { // Attach a file watcher - sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (fileName, removed) => sourceFileChanged(sourceFile, removed)); + sourceFile.fileWatcher = sys.watchFile(sourceFile.fileName, (fileName: string, removed?: boolean) => sourceFileChanged(sourceFile, removed)); } return sourceFile; } @@ -297,7 +321,7 @@ namespace ts { } // If a source file changes, mark it as unwatched and start the recompilation timer - function sourceFileChanged(sourceFile: SourceFile, removed: boolean) { + function sourceFileChanged(sourceFile: SourceFile, removed?: boolean) { sourceFile.fileWatcher.close(); sourceFile.fileWatcher = undefined; if (removed) { @@ -306,27 +330,54 @@ namespace ts { rootFileNames.splice(index, 1); } } - startTimer(); + startTimerForRecompilation(); } // If the configuration file changes, forget cached program and start the recompilation timer function configFileChanged() { setCachedProgram(undefined); - startTimer(); + cachedConfigFileText = undefined; + startTimerForRecompilation(); + } + + function watchedDirectoryChanged(fileName: string) { + if (fileName && !ts.isSupportedSourceFileName(fileName)) { + return; + } + + startTimerForHandlingDirectoryChanges(); + } + + function startTimerForHandlingDirectoryChanges() { + if (timerHandleForDirectoryChanges) { + clearTimeout(timerHandleForDirectoryChanges); + } + timerHandleForDirectoryChanges = setTimeout(directoryChangeHandler, 250); + } + + function directoryChangeHandler() { + let parsedCommandLine = parseConfigFile(); + let newFileNames = ts.map(parsedCommandLine.fileNames, compilerHost.getCanonicalFileName); + let canonicalRootFileNames = ts.map(rootFileNames, compilerHost.getCanonicalFileName); + + if (!arrayStructurallyIsEqualTo(newFileNames, canonicalRootFileNames)) { + setCachedProgram(undefined); + startTimerForRecompilation(); + } } // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch // operations (such as saving all modified files in an editor) a chance to complete before we kick // off a new compilation. - function startTimer() { - if (timerHandle) { - clearTimeout(timerHandle); + function startTimerForRecompilation() { + if (timerHandleForRecompilation) { + clearTimeout(timerHandleForRecompilation); } - timerHandle = setTimeout(recompile, 250); + timerHandleForRecompilation = setTimeout(recompile, 250); } function recompile() { - timerHandle = undefined; + timerHandleForRecompilation = undefined; reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation)); performCompilation(); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fef3f071125..568905dab06 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2090,7 +2090,6 @@ namespace ts { watch?: boolean; isolatedModules?: boolean; experimentalDecorators?: boolean; - experimentalAsyncFunctions?: boolean; emitDecoratorMetadata?: boolean; moduleResolution?: ModuleResolutionKind; allowUnusedLabels?: boolean; @@ -2112,6 +2111,7 @@ namespace ts { UMD = 3, System = 4, ES6 = 5, + ES2015 = ES6, } export const enum JsxEmit { @@ -2137,12 +2137,13 @@ namespace ts { ES3 = 0, ES5 = 1, ES6 = 2, + ES2015 = ES6, Latest = ES6, } export const enum LanguageVariant { Standard, - JSX + JSX, } export interface ParsedCommandLine { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index afd192e4b55..1fcbcbc842d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2414,4 +2414,16 @@ namespace ts { } } } + + export function arrayStructurallyIsEqualTo(array1: Array, array2: Array): boolean { + if (!array1 || !array2) { + return false; + } + + if (array1.length !== array2.length) { + return false; + } + + return arrayIsEqualTo(array1.sort(), array2.sort()); + } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 3a628bb62c1..457ece7e132 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -591,14 +591,21 @@ namespace FourSlash { } } - public verifyCompletionListItemsCountIsGreaterThan(count: number) { + public verifyCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) { this.taoInvalidReason = "verifyCompletionListItemsCountIsGreaterThan NYI"; let completions = this.getCompletionListAtCaret(); let itemsCount = completions.entries.length; - if (itemsCount <= count) { - this.raiseError(`Expected completion list items count to be greater than ${count}, but is actually ${itemsCount}`); + if (negative) { + if (itemsCount > count) { + this.raiseError(`Expected completion list items count to not be greater than ${count}, but is actually ${itemsCount}`); + } + } + else { + if (itemsCount <= count) { + this.raiseError(`Expected completion list items count to be greater than ${count}, but is actually ${itemsCount}`); + } } } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index dfde2bd1c08..df5efccdcb5 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -572,6 +572,10 @@ namespace Harness.LanguageService { return { close() { } }; } + watchDirectory(path: string, callback: (path: string) => void, recursive?: boolean): ts.FileWatcher { + return { close() { } }; + } + close(): void { } @@ -614,7 +618,9 @@ namespace Harness.LanguageService { // This host is just a proxy for the clientHost, it uses the client // host to answer server queries about files on disk let serverHost = new SessionServerHost(clientHost); - let server = new ts.server.Session(serverHost, Buffer.byteLength, process.hrtime, serverHost); + let server = new ts.server.Session(serverHost, + Buffer ? Buffer.byteLength : (string: string, encoding?: string) => string.length, + process.hrtime, serverHost); // Fake the connection between the client and the server serverHost.writeMessage = client.onMessage.bind(client); diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 3027afae9dc..19c989bcd6f 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -78,8 +78,8 @@ namespace RWC { let tsconfigFile = ts.forEach(ioLog.filesRead, f => isTsConfigFile(f) ? f : undefined); if (tsconfigFile) { let tsconfigFileContents = getHarnessCompilerInputUnit(tsconfigFile.path); - let parsedTsconfigFileContents = ts.parseConfigFileText(tsconfigFile.path, tsconfigFileContents.content); - let configParseResult = ts.parseConfigFile(parsedTsconfigFileContents.config, Harness.IO, ts.getDirectoryPath(tsconfigFile.path)); + let parsedTsconfigFileContents = ts.parseConfigFileTextToJson(tsconfigFile.path, tsconfigFileContents.content); + let configParseResult = ts.parseJsonConfigFileContent(parsedTsconfigFileContents.config, Harness.IO, ts.getDirectoryPath(tsconfigFile.path)); fileNames = configParseResult.fileNames; opts.options = ts.extend(opts.options, configParseResult.options); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 436b97821cc..09f8a372463 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -78,19 +78,19 @@ namespace ts.server { return this.snap().getChangeRange(oldSnapshot); } } - + interface TimestampedResolvedModule extends ResolvedModuleWithFailedLookupLocations { - lastCheckTime: number; + lastCheckTime: number; } - + export class LSHost implements ts.LanguageServiceHost { ls: ts.LanguageService = null; compilationSettings: ts.CompilerOptions; filenameToScript: ts.Map = {}; roots: ScriptInfo[] = []; - private resolvedModuleNames: ts.FileMap>; + private resolvedModuleNames: ts.FileMap>; private moduleResolutionHost: ts.ModuleResolutionHost; - + constructor(public host: ServerHost, public project: Project) { this.resolvedModuleNames = ts.createFileMap>(ts.createGetCanonicalFileName(host.useCaseSensitiveFileNames)) this.moduleResolutionHost = { @@ -98,15 +98,15 @@ namespace ts.server { readFile: fileName => this.host.readFile(fileName) } } - + resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] { let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile); - + let newResolutions: Map = {}; let resolvedModules: ResolvedModule[] = []; - + let compilerOptions = this.getCompilationSettings(); - + for (let moduleName of moduleNames) { // check if this is a duplicate entry in the list let resolution = lookUp(newResolutions, moduleName); @@ -122,21 +122,21 @@ namespace ts.server { newResolutions[moduleName] = resolution; } } - + ts.Debug.assert(resolution !== undefined); - + resolvedModules.push(resolution.resolvedModule); } // replace old results with a new one this.resolvedModuleNames.set(containingFile, newResolutions); return resolvedModules; - + function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean { if (!resolution) { return false; } - + if (resolution.resolvedModule) { // TODO: consider checking failedLookupLocations // TODO: use lastCheckTime to track expiration for module name resolution @@ -147,7 +147,7 @@ namespace ts.server { // after all there is no point to invalidate it if we have no idea where to look for the module. return resolution.failedLookupLocations.length === 0; } - } + } getDefaultLibFileName() { var nodeModuleBinDir = ts.getDirectoryPath(ts.normalizePath(this.host.getExecutingFilePath())); @@ -224,12 +224,13 @@ namespace ts.server { this.roots.push(info); } } - + removeRoot(info: ScriptInfo) { var scriptInfo = ts.lookUp(this.filenameToScript, info.fileName); if (scriptInfo) { this.filenameToScript[info.fileName] = undefined; this.roots = copyListRemovingItem(info, this.roots); + this.resolvedModuleNames.remove(info.fileName); } } @@ -354,6 +355,9 @@ namespace ts.server { compilerService: CompilerService; projectFilename: string; projectFileWatcher: FileWatcher; + directoryWatcher: FileWatcher; + // Used to keep track of what directories are watched for this project + directoriesWatchedForTsconfig: string[] = []; program: ts.Program; filenameToSourceFile: ts.Map = {}; updateGraphSeq = 0; @@ -377,6 +381,10 @@ namespace ts.server { return this.projectService.openFile(filename, false); } + getRootFiles() { + return this.compilerService.host.roots.map(info => info.fileName); + } + getFileNames() { let sourceFiles = this.program.getSourceFiles(); return sourceFiles.map(sourceFile => sourceFile.fileName); @@ -429,13 +437,11 @@ namespace ts.server { // add a root file to project addRoot(info: ScriptInfo) { - info.defaultProject = this; this.compilerService.host.addRoot(info); } // remove a root file from project removeRoot(info: ScriptInfo) { - info.defaultProject = undefined; this.compilerService.host.removeRoot(info); } @@ -491,7 +497,13 @@ namespace ts.server { openFilesReferenced: ScriptInfo[] = []; // open files that are roots of a configured project openFileRootsConfigured: ScriptInfo[] = []; + // a path to directory watcher map that detects added tsconfig files + directoryWatchersForTsconfig: ts.Map = {}; + // count of how many projects are using the directory watcher. If the + // number becomes 0 for a watcher, then we should close it. + directoryWatchersRefCount: ts.Map = {}; hostConfiguration: HostConfiguration; + timerForDetectingProjectFilelistChanges: Map = {}; constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { // ts.disableIncrementalParsing = true; @@ -532,8 +544,82 @@ namespace ts.server { } } + /** + * This is the callback function when a watched directory has added or removed source code files. + * @param project the project that associates with this directory watcher + * @param fileName the absolute file name that changed in watched directory + */ + directoryWatchedForSourceFilesChanged(project: Project, fileName: string) { + // If a change was made inside "folder/file", node will trigger the callback twice: + // one with the fileName being "folder/file", and the other one with "folder". + // We don't respond to the second one. + if (fileName && !ts.isSupportedSourceFileName(fileName)) { + return; + } + + this.log("Detected source file changes: " + fileName); + this.startTimerForDetectingProjectFilelistChanges(project); + } + + startTimerForDetectingProjectFilelistChanges(project: Project) { + if (this.timerForDetectingProjectFilelistChanges[project.projectFilename]) { + clearTimeout(this.timerForDetectingProjectFilelistChanges[project.projectFilename]); + } + this.timerForDetectingProjectFilelistChanges[project.projectFilename] = setTimeout( + () => this.handleProjectFilelistChanges(project), + 250 + ); + } + + handleProjectFilelistChanges(project: Project) { + let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); + let newRootFiles = projectOptions.files.map((f => this.getCanonicalFileName(f))); + let currentRootFiles = project.getRootFiles().map((f => this.getCanonicalFileName(f))); + + if (!arrayStructurallyIsEqualTo(currentRootFiles, newRootFiles)) { + // For configured projects, the change is made outside the tsconfig file, and + // it is not likely to affect the project for other files opened by the client. We can + // just update the current project. + this.updateConfiguredProject(project); + + // Call updateProjectStructure to clean up inferred projects we may have + // created for the new files + this.updateProjectStructure(); + } + } + + /** + * This is the callback function when a watched directory has an added tsconfig file. + */ + directoryWatchedForTsconfigChanged(fileName: string) { + if (ts.getBaseFileName(fileName) != "tsconfig.json") { + this.log(fileName + " is not tsconfig.json"); + return; + } + + this.log("Detected newly added tsconfig file: " + fileName); + + let { succeeded, projectOptions, error } = this.configFileToProjectOptions(fileName); + let rootFilesInTsconfig = projectOptions.files.map(f => this.getCanonicalFileName(f)); + let openFileRoots = this.openFileRoots.map(s => this.getCanonicalFileName(s.fileName)); + + // We should only care about the new tsconfig file if it contains any + // opened root files of existing inferred projects + for (let openFileRoot of openFileRoots) { + if (rootFilesInTsconfig.indexOf(openFileRoot) >= 0) { + this.reloadProjects(); + return; + } + } + } + + getCanonicalFileName(fileName: string) { + let name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); + return ts.normalizePath(name); + } + watchedProjectConfigFileChanged(project: Project) { - this.log("Config File Changed: " + project.projectFilename); + this.log("Config file changed: " + project.projectFilename); this.updateConfiguredProject(project); this.updateProjectStructure(); } @@ -567,11 +653,29 @@ namespace ts.server { } createInferredProject(root: ScriptInfo) { - var iproj = new Project(this); - iproj.addRoot(root); - iproj.finishGraph(); - this.inferredProjects.push(iproj); - return iproj; + var project = new Project(this); + project.addRoot(root); + + let currentPath = ts.getDirectoryPath(root.fileName); + let parentPath = ts.getDirectoryPath(currentPath); + while (currentPath != parentPath) { + if (!project.projectService.directoryWatchersForTsconfig[currentPath]) { + this.log("Add watcher for: " + currentPath); + project.projectService.directoryWatchersForTsconfig[currentPath] = + this.host.watchDirectory(currentPath, fileName => this.directoryWatchedForTsconfigChanged(fileName)); + project.projectService.directoryWatchersRefCount[currentPath] = 1; + } + else { + project.projectService.directoryWatchersRefCount[currentPath] += 1; + } + project.directoriesWatchedForTsconfig.push(currentPath); + currentPath = parentPath; + parentPath = ts.getDirectoryPath(parentPath); + } + + project.finishGraph(); + this.inferredProjects.push(project); + return project; } fileDeletedInFilesystem(info: ScriptInfo) { @@ -585,6 +689,9 @@ namespace ts.server { if (!info.isOpen) { this.filenameToScriptInfo[info.fileName] = undefined; var referencingProjects = this.findReferencingProjects(info); + if (info.defaultProject) { + info.defaultProject.removeRoot(info); + } for (var i = 0, len = referencingProjects.length; i < len; i++) { referencingProjects[i].removeReferencedFile(info); } @@ -615,9 +722,24 @@ namespace ts.server { this.configuredProjects = configuredProjects; } - removeConfiguredProject(project: Project) { - project.projectFileWatcher.close(); - this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); + removeProject(project: Project) { + this.log("remove project: " + project.getRootFiles().toString()); + if (project.isConfiguredProject()) { + project.projectFileWatcher.close(); + project.directoryWatcher.close(); + this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); + } + else { + for (let directory of project.directoriesWatchedForTsconfig) { + // if the ref count for this directory watcher drops to 0, it's time to close it + if (!(--project.projectService.directoryWatchersRefCount[directory])) { + this.log("Close directory watcher for: " + directory); + project.projectService.directoryWatchersForTsconfig[directory].close(); + delete project.projectService.directoryWatchersForTsconfig[directory]; + } + } + this.inferredProjects = copyListRemovingItem(project, this.inferredProjects); + } let fileNames = project.getFileNames(); for (let fileName of fileNames) { @@ -659,8 +781,7 @@ namespace ts.server { // if r referenced by the new project if (info.defaultProject.getSourceFile(r)) { // remove project rooted at r - this.inferredProjects = - copyListRemovingItem(r.defaultProject, this.inferredProjects); + this.removeProject(r.defaultProject); // put r in referenced open file list this.openFilesReferenced.push(r); // set default project of r to the new project @@ -683,6 +804,11 @@ namespace ts.server { * @param info The file that has been closed or newly configured */ closeOpenFile(info: ScriptInfo) { + // Closing file should trigger re-reading the file content from disk. This is + // because the user may chose to discard the buffer content before saving + // to the disk, and the server's version of the file can be out of sync. + info.svc.reloadFromFile(info.fileName); + var openFileRoots: ScriptInfo[] = []; var removedProject: Project; for (var i = 0, len = this.openFileRoots.length; i < len; i++) { @@ -713,19 +839,14 @@ namespace ts.server { this.openFileRootsConfigured = openFileRootsConfigured; } if (removedProject) { - if (removedProject.isConfiguredProject()) { - this.configuredProjects = copyListRemovingItem(removedProject, this.configuredProjects); - } - else { - this.inferredProjects = copyListRemovingItem(removedProject, this.inferredProjects); - } + this.removeProject(removedProject); var openFilesReferenced: ScriptInfo[] = []; var orphanFiles: ScriptInfo[] = []; // for all open, referenced files f for (var i = 0, len = this.openFilesReferenced.length; i < len; i++) { var f = this.openFilesReferenced[i]; // if f was referenced by the removed project, remember it - if (f.defaultProject === removedProject) { + if (f.defaultProject === removedProject || !f.defaultProject) { f.defaultProject = undefined; orphanFiles.push(f); } @@ -769,7 +890,11 @@ namespace ts.server { return referencingProjects; } + /** + * This function rebuilds the project for every file opened by the client + */ reloadProjects() { + this.log("reload projects."); // First check if there is new tsconfig file added for inferred project roots for (let info of this.openFileRoots) { this.openOrUpdateConfiguredProjectForFile(info.fileName); @@ -830,14 +955,25 @@ namespace ts.server { var rootFile = this.openFileRoots[i]; var rootedProject = rootFile.defaultProject; var referencingProjects = this.findReferencingProjects(rootFile, rootedProject); - if (referencingProjects.length === 0) { - rootFile.defaultProject = rootedProject; - openFileRoots.push(rootFile); + + if (rootFile.defaultProject && rootFile.defaultProject.isConfiguredProject()) { + // If the root file has already been added into a configured project, + // meaning the original inferred project is gone already. + if (!rootedProject.isConfiguredProject()) { + this.removeProject(rootedProject); + } + this.openFileRootsConfigured.push(rootFile); } else { - // remove project from inferred projects list because root captured - this.inferredProjects = copyListRemovingItem(rootedProject, this.inferredProjects); - this.openFilesReferenced.push(rootFile); + if (referencingProjects.length === 0) { + rootFile.defaultProject = rootedProject; + openFileRoots.push(rootFile); + } + else { + // remove project from inferred projects list because root captured + this.removeProject(rootedProject); + this.openFilesReferenced.push(rootFile); + } } } this.openFileRoots = openFileRoots; @@ -922,6 +1058,11 @@ namespace ts.server { return info; } + /** + * This function tries to search for a tsconfig.json for the given file. If we found it, + * we first detect if there is already a configured project created for it: if so, we re-read + * the tsconfig file content and update the project; otherwise we create a new one. + */ openOrUpdateConfiguredProjectForFile(fileName: string) { let searchPath = ts.normalizePath(getDirectoryPath(fileName)); this.log("Search path: " + searchPath, "Info"); @@ -1041,17 +1182,17 @@ namespace ts.server { // file references will be relative to dirPath (or absolute) var dirPath = ts.getDirectoryPath(configFilename); var contents = this.host.readFile(configFilename) - var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileText(configFilename, contents); + var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents); if (rawConfig.error) { return { succeeded: false, error: rawConfig.error }; } else { - var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath); + var parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath); if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { return { succeeded: false, error: { errorMsg: "tsconfig option errors" } }; } else if (parsedCommandLine.fileNames == null) { - return { succeeded: false, error: { errorMsg: "no files found" } } + return { succeeded: false, error: { errorMsg: "no files found" } }; } else { var projectOptions: ProjectOptions = { @@ -1070,27 +1211,32 @@ namespace ts.server { return error; } else { - let proj = this.createProject(configFilename, projectOptions); - for (let i = 0, len = projectOptions.files.length; i < len; i++) { - let rootFilename = projectOptions.files[i]; + let project = this.createProject(configFilename, projectOptions); + for (let rootFilename of projectOptions.files) { if (this.host.fileExists(rootFilename)) { let info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename); - proj.addRoot(info); + project.addRoot(info); } else { return { errorMsg: "specified file " + rootFilename + " not found" }; } } - proj.finishGraph(); - proj.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(proj)); - return { success: true, project: proj }; + project.finishGraph(); + project.projectFileWatcher = this.host.watchFile(configFilename, _ => this.watchedProjectConfigFileChanged(project)); + this.log("Add recursive watcher for: " + ts.getDirectoryPath(configFilename)); + project.directoryWatcher = this.host.watchDirectory( + ts.getDirectoryPath(configFilename), + path => this.directoryWatchedForSourceFilesChanged(project, path), + /*recursive*/ true + ); + return { success: true, project: project }; } } updateConfiguredProject(project: Project) { if (!this.host.fileExists(project.projectFilename)) { this.log("Config file deleted"); - this.removeConfiguredProject(project); + this.removeProject(project); } else { let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename); @@ -1105,7 +1251,9 @@ namespace ts.server { for (let fileName of fileNamesToRemove) { let info = this.getScriptInfo(fileName); - project.removeRoot(info); + if (info) { + project.removeRoot(info); + } } for (let fileName of fileNamesToAdd) { @@ -1217,9 +1365,9 @@ namespace ts.server { goSubtree: boolean; done: boolean; leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void; - pre? (relativeStart: number, relativeLength: number, lineCollection: LineCollection, + pre?(relativeStart: number, relativeLength: number, lineCollection: LineCollection, parent: LineNode, nodeType: CharRangeSection): LineCollection; - post? (relativeStart: number, relativeLength: number, lineCollection: LineCollection, + post?(relativeStart: number, relativeLength: number, lineCollection: LineCollection, parent: LineNode, nodeType: CharRangeSection): LineCollection; } diff --git a/src/server/server.ts b/src/server/server.ts index c3942b01a39..39864fc8477 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -11,7 +11,7 @@ namespace ts.server { input: process.stdin, output: process.stdout, terminal: false, - }); + }); class Logger implements ts.server.Logger { fd = -1; @@ -58,7 +58,7 @@ namespace ts.server { isVerbose() { return this.loggingEnabled() && (this.level == "verbose"); } - + msg(s: string, type = "Err") { if (this.fd < 0) { @@ -83,95 +83,6 @@ namespace ts.server { } } - interface WatchedFile { - fileName: string; - callback: (fileName: string, removed: boolean) => void; - mtime: Date; - } - - class WatchedFileSet { - private watchedFiles: WatchedFile[] = []; - private nextFileToCheck = 0; - private watchTimer: NodeJS.Timer; - - // average async stat takes about 30 microseconds - // set chunk size to do 30 files in < 1 millisecond - constructor(public interval = 2500, public chunkSize = 30) { - } - - private static copyListRemovingItem(item: T, list: T[]) { - var copiedList: T[] = []; - for (var i = 0, len = list.length; i < len; i++) { - if (list[i] != item) { - copiedList.push(list[i]); - } - } - return copiedList; - } - - private static getModifiedTime(fileName: string): Date { - return fs.statSync(fileName).mtime; - } - - private poll(checkedIndex: number) { - var watchedFile = this.watchedFiles[checkedIndex]; - if (!watchedFile) { - return; - } - - fs.stat(watchedFile.fileName,(err, stats) => { - if (err) { - watchedFile.callback(watchedFile.fileName, /* removed */ false); - } - else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { - watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName); - watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0); - } - }); - } - - // this implementation uses polling and - // stat due to inconsistencies of fs.watch - // and efficiency of stat on modern filesystems - private startWatchTimer() { - this.watchTimer = setInterval(() => { - var count = 0; - var nextToCheck = this.nextFileToCheck; - var firstCheck = -1; - while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) { - this.poll(nextToCheck); - if (firstCheck < 0) { - firstCheck = nextToCheck; - } - nextToCheck++; - if (nextToCheck === this.watchedFiles.length) { - nextToCheck = 0; - } - count++; - } - this.nextFileToCheck = nextToCheck; - }, this.interval); - } - - addFile(fileName: string, callback: (fileName: string, removed: boolean) => void ): WatchedFile { - var file: WatchedFile = { - fileName, - callback, - mtime: WatchedFileSet.getModifiedTime(fileName) - }; - - this.watchedFiles.push(file); - if (this.watchedFiles.length === 1) { - this.startWatchTimer(); - } - return file; - } - - removeFile(file: WatchedFile) { - this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles); - } - } - class IOSession extends Session { constructor(host: ServerHost, logger: ts.server.Logger) { super(host, Buffer.byteLength, process.hrtime, logger); @@ -244,31 +155,10 @@ namespace ts.server { var logger = createLoggerFromEnv(); - // REVIEW: for now this implementation uses polling. - // The advantage of polling is that it works reliably - // on all os and with network mounted files. - // For 90 referenced files, the average time to detect - // changes is 2*msInterval (by default 5 seconds). - // The overhead of this is .04 percent (1/2500) with - // average pause of < 1 millisecond (and max - // pause less than 1.5 milliseconds); question is - // do we anticipate reference sets in the 100s and - // do we care about waiting 10-20 seconds to detect - // changes for large reference sets? If so, do we want - // to increase the chunk size or decrease the interval - // time dynamically to match the large reference set? - var watchedFileSet = new WatchedFileSet(); - ts.sys.watchFile = function (fileName, callback) { - var watchedFile = watchedFileSet.addFile(fileName, callback); - return { - close: () => watchedFileSet.removeFile(watchedFile) - } - - }; var ioSession = new IOSession(ts.sys, logger); process.on('uncaughtException', function(err: Error) { ioSession.logError(err, "unknown"); }); // Start listening ioSession.listen(); -} +} \ No newline at end of file diff --git a/src/server/session.ts b/src/server/session.ts index f3d3826409e..550b551e51a 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -21,6 +21,21 @@ namespace ts.server { return spaceCache[n]; } + export function generateIndentString(n: number, editorOptions: EditorOptions): string { + if (editorOptions.ConvertTabsToSpaces) { + return generateSpaces(n); + } else { + var result = ""; + for (var i = 0; i < Math.floor(n / editorOptions.TabSize); i++) { + result += "\t"; + } + for (var i = 0; i < n % editorOptions.TabSize; i++) { + result += " "; + } + return result; + } + } + interface FileStart { file: string; start: ILineInfo; @@ -608,27 +623,25 @@ namespace ts.server { ConvertTabsToSpaces: formatOptions.ConvertTabsToSpaces, IndentStyle: ts.IndentStyle.Smart, }; - var indentPosition = - compilerService.languageService.getIndentationAtPosition(file, position, editorOptions); + var preferredIndent = compilerService.languageService.getIndentationAtPosition(file, position, editorOptions); + var hasIndent = 0; for (var i = 0, len = lineText.length; i < len; i++) { if (lineText.charAt(i) == " ") { - indentPosition--; + hasIndent++; } else if (lineText.charAt(i) == "\t") { - indentPosition -= editorOptions.IndentSize; + hasIndent += editorOptions.TabSize; } else { break; } } - if (indentPosition > 0) { - var spaces = generateSpaces(indentPosition); - edits.push({ span: ts.createTextSpanFromBounds(position, position), newText: spaces }); - } - else if (indentPosition < 0) { + // i points to the first non whitespace character + if (preferredIndent !== hasIndent) { + var firstNoWhiteSpacePosition = lineInfo.offset + i; edits.push({ - span: ts.createTextSpanFromBounds(position, position - indentPosition), - newText: "" + span: ts.createTextSpanFromBounds(lineInfo.offset, firstNoWhiteSpacePosition), + newText: generateIndentString(preferredIndent, editorOptions) }); } } @@ -780,6 +793,7 @@ namespace ts.server { } private closeClientFile(fileName: string) { + if (!fileName) { return; } var file = ts.normalizePath(fileName); this.projectService.closeClientFile(file); } diff --git a/src/services/services.ts b/src/services/services.ts index b5c9dee40ac..6179c8a169b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3112,6 +3112,7 @@ namespace ts { let node = currentToken; let isRightOfDot = false; let isRightOfOpenTag = false; + let isStartingCloseTag = false; let location = getTouchingPropertyName(sourceFile, position); if (contextToken) { @@ -3137,9 +3138,14 @@ namespace ts { return undefined; } } - else if (kind === SyntaxKind.LessThanToken && sourceFile.languageVariant === LanguageVariant.JSX) { - isRightOfOpenTag = true; - location = contextToken; + else if (sourceFile.languageVariant === LanguageVariant.JSX) { + if (kind === SyntaxKind.LessThanToken) { + isRightOfOpenTag = true; + location = contextToken; + } + else if (kind === SyntaxKind.SlashToken && contextToken.parent.kind === SyntaxKind.JsxClosingElement) { + isStartingCloseTag = true; + } } } @@ -3162,6 +3168,13 @@ namespace ts { isMemberCompletion = true; isNewIdentifierLocation = false; } + else if (isStartingCloseTag) { + let tagName = (contextToken.parent.parent).openingElement.tagName; + symbols = [typeChecker.getSymbolAtLocation(tagName)]; + + isMemberCompletion = true; + isNewIdentifierLocation = false; + } else { // For JavaScript or TypeScript, if we're not after a dot, then just try to get the // global symbols in scope. These results should be valid for either language as @@ -3318,11 +3331,29 @@ namespace ts { let start = new Date().getTime(); let result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) || isSolelyIdentifierDefinitionLocation(contextToken) || - isDotOfNumericLiteral(contextToken); + isDotOfNumericLiteral(contextToken) || + isInJsxText(contextToken); log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start)); return result; } + function isInJsxText(contextToken: Node): boolean { + if (contextToken.kind === SyntaxKind.JsxText) { + return true; + } + + if (contextToken.kind === SyntaxKind.GreaterThanToken && contextToken.parent) { + if (contextToken.parent.kind === SyntaxKind.JsxOpeningElement) { + return true; + } + + if (contextToken.parent.kind === SyntaxKind.JsxClosingElement || contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement) { + return contextToken.parent.parent && contextToken.parent.parent.kind === SyntaxKind.JsxElement; + } + } + return false; + } + function isNewIdentifierDefinitionLocation(previousToken: Node): boolean { if (previousToken) { let containingNodeKind = previousToken.parent.kind; diff --git a/src/services/shims.ts b/src/services/shims.ts index f1fb109ba7e..8482cb50975 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -989,7 +989,7 @@ namespace ts { () => { let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()); - let result = parseConfigFileText(fileName, text); + let result = parseConfigFileTextToJson(fileName, text); if (result.error) { return { @@ -999,7 +999,7 @@ namespace ts { }; } - var configFile = parseConfigFile(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName))); + var configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName))); return { options: configFile.options, diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt new file mode 100644 index 00000000000..94c58bccb04 --- /dev/null +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.errors.txt @@ -0,0 +1,118 @@ +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(2,13): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(58,20): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(65,20): error TS2448: Block-scoped variable 'x' used before its declaration. +tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448: Block-scoped variable 'x' used before its declaration. + + +==== tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts (4 errors) ==== + function foo0() { + let a = x; + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + let x; + } + + function foo1() { + let a = () => x; + let x; + } + + function foo2() { + let a = function () { return x; } + let x; + } + + function foo3() { + class X { + m() { return x;} + } + let x; + } + + function foo4() { + let y = class { + m() { return x; } + }; + let x; + } + + function foo5() { + let x = () => y; + let y = () => x; + } + + function foo6() { + function f() { + return x; + } + let x; + } + + function foo7() { + class A { + a = x; + } + let x; + } + + function foo8() { + let y = class { + a = x; + } + let x; + } + + function foo9() { + let y = class { + static a = x; + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + } + let x; + } + + function foo10() { + class A { + static a = x; + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + } + let x; + } + + function foo11() { + function f () { + let y = class { + static a = x; + } + } + let x; + } + + function foo12() { + function f () { + let y = class { + a; + constructor() { + this.a = x; + } + } + } + let x; + } + + function foo13() { + let a = { + get a() { return x } + } + let x + } + + function foo14() { + let a = { + a: x + ~ +!!! error TS2448: Block-scoped variable 'x' used before its declaration. + } + let x + } \ No newline at end of file diff --git a/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js new file mode 100644 index 00000000000..5ed3ac62e6a --- /dev/null +++ b/tests/baselines/reference/blockScopedVariablesUseBeforeDef.js @@ -0,0 +1,216 @@ +//// [blockScopedVariablesUseBeforeDef.ts] +function foo0() { + let a = x; + let x; +} + +function foo1() { + let a = () => x; + let x; +} + +function foo2() { + let a = function () { return x; } + let x; +} + +function foo3() { + class X { + m() { return x;} + } + let x; +} + +function foo4() { + let y = class { + m() { return x; } + }; + let x; +} + +function foo5() { + let x = () => y; + let y = () => x; +} + +function foo6() { + function f() { + return x; + } + let x; +} + +function foo7() { + class A { + a = x; + } + let x; +} + +function foo8() { + let y = class { + a = x; + } + let x; +} + +function foo9() { + let y = class { + static a = x; + } + let x; +} + +function foo10() { + class A { + static a = x; + } + let x; +} + +function foo11() { + function f () { + let y = class { + static a = x; + } + } + let x; +} + +function foo12() { + function f () { + let y = class { + a; + constructor() { + this.a = x; + } + } + } + let x; +} + +function foo13() { + let a = { + get a() { return x } + } + let x +} + +function foo14() { + let a = { + a: x + } + let x +} + +//// [blockScopedVariablesUseBeforeDef.js] +function foo0() { + var a = x; + var x; +} +function foo1() { + var a = function () { return x; }; + var x; +} +function foo2() { + var a = function () { return x; }; + var x; +} +function foo3() { + var X = (function () { + function X() { + } + X.prototype.m = function () { return x; }; + return X; + })(); + var x; +} +function foo4() { + var y = (function () { + function class_1() { + } + class_1.prototype.m = function () { return x; }; + return class_1; + })(); + var x; +} +function foo5() { + var x = function () { return y; }; + var y = function () { return x; }; +} +function foo6() { + function f() { + return x; + } + var x; +} +function foo7() { + var A = (function () { + function A() { + this.a = x; + } + return A; + })(); + var x; +} +function foo8() { + var y = (function () { + function class_2() { + this.a = x; + } + return class_2; + })(); + var x; +} +function foo9() { + var y = (function () { + function class_3() { + } + class_3.a = x; + return class_3; + })(); + var x; +} +function foo10() { + var A = (function () { + function A() { + } + A.a = x; + return A; + })(); + var x; +} +function foo11() { + function f() { + var y = (function () { + function class_4() { + } + class_4.a = x; + return class_4; + })(); + } + var x; +} +function foo12() { + function f() { + var y = (function () { + function class_5() { + this.a = x; + } + return class_5; + })(); + } + var x; +} +function foo13() { + var a = { + get a() { return x; } + }; + var x; +} +function foo14() { + var a = { + a: x + }; + var x; +} diff --git a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt deleted file mode 100644 index a4d28f51878..00000000000 --- a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.errors.txt +++ /dev/null @@ -1,11 +0,0 @@ -tests/cases/compiler/file1.ts(2,1): error TS2448: Block-scoped variable 'c' used before its declaration. - - -==== tests/cases/compiler/file1.ts (1 errors) ==== - - c; - ~ -!!! error TS2448: Block-scoped variable 'c' used before its declaration. - -==== tests/cases/compiler/file2.ts (0 errors) ==== - const c = 0; \ No newline at end of file diff --git a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols new file mode 100644 index 00000000000..281ce427733 --- /dev/null +++ b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/file1.ts === + +c; +>c : Symbol(c, Decl(file2.ts, 0, 5)) + +=== tests/cases/compiler/file2.ts === +const c = 0; +>c : Symbol(c, Decl(file2.ts, 0, 5)) + diff --git a/tests/baselines/reference/constDeclarations-useBeforeDefinition2.types b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.types new file mode 100644 index 00000000000..ae60fdfa477 --- /dev/null +++ b/tests/baselines/reference/constDeclarations-useBeforeDefinition2.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/file1.ts === + +c; +>c : number + +=== tests/cases/compiler/file2.ts === +const c = 0; +>c : number +>0 : number + diff --git a/tests/baselines/reference/decoratorCallGeneric.errors.txt b/tests/baselines/reference/decoratorCallGeneric.errors.txt new file mode 100644 index 00000000000..c8443420740 --- /dev/null +++ b/tests/baselines/reference/decoratorCallGeneric.errors.txt @@ -0,0 +1,22 @@ +tests/cases/conformance/decorators/decoratorCallGeneric.ts(7,2): error TS1238: Unable to resolve signature of class decorator when called as an expression. + The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. + Type argument candidate 'C' is not a valid type argument because it is not a supertype of candidate 'void'. + + +==== tests/cases/conformance/decorators/decoratorCallGeneric.ts (1 errors) ==== + interface I { + prototype: T, + m: () => T + } + function dec(c: I) { } + + @dec + ~~~ +!!! error TS1238: Unable to resolve signature of class decorator when called as an expression. +!!! error TS1238: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. +!!! error TS1238: Type argument candidate 'C' is not a valid type argument because it is not a supertype of candidate 'void'. + class C { + _brand: any; + static m() {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/decoratorCallGeneric.js b/tests/baselines/reference/decoratorCallGeneric.js new file mode 100644 index 00000000000..8c141d5a131 --- /dev/null +++ b/tests/baselines/reference/decoratorCallGeneric.js @@ -0,0 +1,31 @@ +//// [decoratorCallGeneric.ts] +interface I { + prototype: T, + m: () => T +} +function dec(c: I) { } + +@dec +class C { + _brand: any; + static m() {} +} + + +//// [decoratorCallGeneric.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +function dec(c) { } +var C = (function () { + function C() { + } + C.m = function () { }; + C = __decorate([ + dec + ], C); + return C; +})(); diff --git a/tests/baselines/reference/es2015modulekind.js b/tests/baselines/reference/es2015modulekind.js new file mode 100644 index 00000000000..af638557fde --- /dev/null +++ b/tests/baselines/reference/es2015modulekind.js @@ -0,0 +1,23 @@ +//// [es2015modulekind.ts] + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} + +//// [es2015modulekind.js] +export default class A { + constructor() { + } + B() { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekind.symbols b/tests/baselines/reference/es2015modulekind.symbols new file mode 100644 index 00000000000..227443ac06b --- /dev/null +++ b/tests/baselines/reference/es2015modulekind.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/es2015modulekind.ts === + +export default class A +>A : Symbol(A, Decl(es2015modulekind.ts, 0, 0)) +{ + constructor () + { + + } + + public B() +>B : Symbol(B, Decl(es2015modulekind.ts, 6, 5)) + { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekind.types b/tests/baselines/reference/es2015modulekind.types new file mode 100644 index 00000000000..e77cf095aed --- /dev/null +++ b/tests/baselines/reference/es2015modulekind.types @@ -0,0 +1,17 @@ +=== tests/cases/compiler/es2015modulekind.ts === + +export default class A +>A : A +{ + constructor () + { + + } + + public B() +>B : () => number + { + return 42; +>42 : number + } +} diff --git a/tests/baselines/reference/es2015modulekindWithES6Target.js b/tests/baselines/reference/es2015modulekindWithES6Target.js new file mode 100644 index 00000000000..b76ab2a02a2 --- /dev/null +++ b/tests/baselines/reference/es2015modulekindWithES6Target.js @@ -0,0 +1,23 @@ +//// [es2015modulekindWithES6Target.ts] + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} + +//// [es2015modulekindWithES6Target.js] +export default class A { + constructor() { + } + B() { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekindWithES6Target.symbols b/tests/baselines/reference/es2015modulekindWithES6Target.symbols new file mode 100644 index 00000000000..2ff208ac704 --- /dev/null +++ b/tests/baselines/reference/es2015modulekindWithES6Target.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/es2015modulekindWithES6Target.ts === + +export default class A +>A : Symbol(A, Decl(es2015modulekindWithES6Target.ts, 0, 0)) +{ + constructor () + { + + } + + public B() +>B : Symbol(B, Decl(es2015modulekindWithES6Target.ts, 6, 5)) + { + return 42; + } +} diff --git a/tests/baselines/reference/es2015modulekindWithES6Target.types b/tests/baselines/reference/es2015modulekindWithES6Target.types new file mode 100644 index 00000000000..50b85921b0e --- /dev/null +++ b/tests/baselines/reference/es2015modulekindWithES6Target.types @@ -0,0 +1,17 @@ +=== tests/cases/compiler/es2015modulekindWithES6Target.ts === + +export default class A +>A : A +{ + constructor () + { + + } + + public B() +>B : () => number + { + return 42; +>42 : number + } +} diff --git a/tests/baselines/reference/es6modulekindWithES2015Target.js b/tests/baselines/reference/es6modulekindWithES2015Target.js new file mode 100644 index 00000000000..98808fd1c65 --- /dev/null +++ b/tests/baselines/reference/es6modulekindWithES2015Target.js @@ -0,0 +1,23 @@ +//// [es6modulekindWithES2015Target.ts] + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} + +//// [es6modulekindWithES2015Target.js] +export default class A { + constructor() { + } + B() { + return 42; + } +} diff --git a/tests/baselines/reference/es6modulekindWithES2015Target.symbols b/tests/baselines/reference/es6modulekindWithES2015Target.symbols new file mode 100644 index 00000000000..98b91a0411b --- /dev/null +++ b/tests/baselines/reference/es6modulekindWithES2015Target.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/es6modulekindWithES2015Target.ts === + +export default class A +>A : Symbol(A, Decl(es6modulekindWithES2015Target.ts, 0, 0)) +{ + constructor () + { + + } + + public B() +>B : Symbol(B, Decl(es6modulekindWithES2015Target.ts, 6, 5)) + { + return 42; + } +} diff --git a/tests/baselines/reference/es6modulekindWithES2015Target.types b/tests/baselines/reference/es6modulekindWithES2015Target.types new file mode 100644 index 00000000000..63acdae43d1 --- /dev/null +++ b/tests/baselines/reference/es6modulekindWithES2015Target.types @@ -0,0 +1,17 @@ +=== tests/cases/compiler/es6modulekindWithES2015Target.ts === + +export default class A +>A : A +{ + constructor () + { + + } + + public B() +>B : () => number + { + return 42; +>42 : number + } +} diff --git a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt deleted file mode 100644 index 5b8633312d9..00000000000 --- a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.errors.txt +++ /dev/null @@ -1,11 +0,0 @@ -tests/cases/compiler/file1.ts(2,1): error TS2448: Block-scoped variable 'l' used before its declaration. - - -==== tests/cases/compiler/file1.ts (1 errors) ==== - - l; - ~ -!!! error TS2448: Block-scoped variable 'l' used before its declaration. - -==== tests/cases/compiler/file2.ts (0 errors) ==== - const l = 0; \ No newline at end of file diff --git a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols new file mode 100644 index 00000000000..c5a067ede4d --- /dev/null +++ b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/file1.ts === + +l; +>l : Symbol(l, Decl(file2.ts, 0, 5)) + +=== tests/cases/compiler/file2.ts === +const l = 0; +>l : Symbol(l, Decl(file2.ts, 0, 5)) + diff --git a/tests/baselines/reference/letDeclarations-useBeforeDefinition2.types b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.types new file mode 100644 index 00000000000..793a7a78ba7 --- /dev/null +++ b/tests/baselines/reference/letDeclarations-useBeforeDefinition2.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/file1.ts === + +l; +>l : number + +=== tests/cases/compiler/file2.ts === +const l = 0; +>l : number +>0 : number + diff --git a/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts new file mode 100644 index 00000000000..956705bd7d3 --- /dev/null +++ b/tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts @@ -0,0 +1,104 @@ +// @target: ES5 +function foo0() { + let a = x; + let x; +} + +function foo1() { + let a = () => x; + let x; +} + +function foo2() { + let a = function () { return x; } + let x; +} + +function foo3() { + class X { + m() { return x;} + } + let x; +} + +function foo4() { + let y = class { + m() { return x; } + }; + let x; +} + +function foo5() { + let x = () => y; + let y = () => x; +} + +function foo6() { + function f() { + return x; + } + let x; +} + +function foo7() { + class A { + a = x; + } + let x; +} + +function foo8() { + let y = class { + a = x; + } + let x; +} + +function foo9() { + let y = class { + static a = x; + } + let x; +} + +function foo10() { + class A { + static a = x; + } + let x; +} + +function foo11() { + function f () { + let y = class { + static a = x; + } + } + let x; +} + +function foo12() { + function f () { + let y = class { + a; + constructor() { + this.a = x; + } + } + } + let x; +} + +function foo13() { + let a = { + get a() { return x } + } + let x +} + +function foo14() { + let a = { + a: x + } + let x +} \ No newline at end of file diff --git a/tests/cases/compiler/es2015modulekind.ts b/tests/cases/compiler/es2015modulekind.ts new file mode 100644 index 00000000000..6e1f584844a --- /dev/null +++ b/tests/cases/compiler/es2015modulekind.ts @@ -0,0 +1,17 @@ +// @target: es2015 +// @sourcemap: false +// @declaration: false +// @module: es2015 + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/es2015modulekindWithES6Target.ts b/tests/cases/compiler/es2015modulekindWithES6Target.ts new file mode 100644 index 00000000000..78809d3835f --- /dev/null +++ b/tests/cases/compiler/es2015modulekindWithES6Target.ts @@ -0,0 +1,17 @@ +// @target: es6 +// @sourcemap: false +// @declaration: false +// @module: es2015 + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/es6modulekindWithES2015Target.ts b/tests/cases/compiler/es6modulekindWithES2015Target.ts new file mode 100644 index 00000000000..aaf79e6607d --- /dev/null +++ b/tests/cases/compiler/es6modulekindWithES2015Target.ts @@ -0,0 +1,17 @@ +// @target: es2015 +// @sourcemap: false +// @declaration: false +// @module: es6 + +export default class A +{ + constructor () + { + + } + + public B() + { + return 42; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts index bb9f8a54e44..0959ed6c561 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction10_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async foo(): Promise => { // Legal to use 'await' in a type context. diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts index 45e686fd280..83e7413c527 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction1_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (): Promise => { }; \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts index 8d792fdff9c..bf39735d460 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction2_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var f = (await) => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts index 9ca200f8d76..4ae27c596f1 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction3_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function f(await = await) { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts index 4946592c45b..51b34abeca1 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction4_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var await = () => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts index 53052368b31..174a619bdf9 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (await): Promise => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts index 69768429ecf..dace96c4933 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction6_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (a = await): Promise => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts index a034381ba57..b1fd5cc030d 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction7_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var bar = async (): Promise => { // 'await' here is an identifier, and not an await expression. diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts index 397e06f703d..5d53338b1bd 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction8_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (): Promise => { var v = { [await]: foo } diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts index 3f961c9d595..da041fe472b 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var foo = async (a = await => await): Promise => { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts index f00d0d16545..8dab2c04086 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesArguments_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { method() { function other() {} diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts index a90f08cb9f7..f2c507cbdd6 100644 --- a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunctionCapturesThis_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { method() { var fn = async () => await this; diff --git a/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts b/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts index 52694f24118..8e2cfd8c6c5 100644 --- a/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts +++ b/tests/cases/conformance/async/es6/asyncAwaitIsolatedModules_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @isolatedModules: true -// @experimentalAsyncFunctions: true import { MyPromise } from "missing"; declare var p: Promise; diff --git a/tests/cases/conformance/async/es6/asyncAwait_es6.ts b/tests/cases/conformance/async/es6/asyncAwait_es6.ts index eee2e73d023..8e72197a98d 100644 --- a/tests/cases/conformance/async/es6/asyncAwait_es6.ts +++ b/tests/cases/conformance/async/es6/asyncAwait_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 -// @experimentalAsyncFunctions: true type MyPromise = Promise; declare var MyPromise: typeof Promise; declare var p: Promise; diff --git a/tests/cases/conformance/async/es6/asyncClass_es6.ts b/tests/cases/conformance/async/es6/asyncClass_es6.ts index c4a444ca909..22ffe997b03 100644 --- a/tests/cases/conformance/async/es6/asyncClass_es6.ts +++ b/tests/cases/conformance/async/es6/asyncClass_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async class C { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncConstructor_es6.ts b/tests/cases/conformance/async/es6/asyncConstructor_es6.ts index 998156f19f4..d769fad0395 100644 --- a/tests/cases/conformance/async/es6/asyncConstructor_es6.ts +++ b/tests/cases/conformance/async/es6/asyncConstructor_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { async constructor() { } diff --git a/tests/cases/conformance/async/es6/asyncDeclare_es6.ts b/tests/cases/conformance/async/es6/asyncDeclare_es6.ts index d83c25a421a..5e0fb536b39 100644 --- a/tests/cases/conformance/async/es6/asyncDeclare_es6.ts +++ b/tests/cases/conformance/async/es6/asyncDeclare_es6.ts @@ -1,4 +1,3 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare async function foo(): Promise; \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncEnum_es6.ts b/tests/cases/conformance/async/es6/asyncEnum_es6.ts index d9569bef9d1..4fad7923a8c 100644 --- a/tests/cases/conformance/async/es6/asyncEnum_es6.ts +++ b/tests/cases/conformance/async/es6/asyncEnum_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async enum E { Value } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncGetter_es6.ts b/tests/cases/conformance/async/es6/asyncGetter_es6.ts index fe5751ece0b..79fde60fdc4 100644 --- a/tests/cases/conformance/async/es6/asyncGetter_es6.ts +++ b/tests/cases/conformance/async/es6/asyncGetter_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { async get foo() { } diff --git a/tests/cases/conformance/async/es6/asyncInterface_es6.ts b/tests/cases/conformance/async/es6/asyncInterface_es6.ts index c7bb460ea2b..5d5497d3cd4 100644 --- a/tests/cases/conformance/async/es6/asyncInterface_es6.ts +++ b/tests/cases/conformance/async/es6/asyncInterface_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async interface I { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncModule_es6.ts b/tests/cases/conformance/async/es6/asyncModule_es6.ts index cf660d25d1f..aa4c295d0e3 100644 --- a/tests/cases/conformance/async/es6/asyncModule_es6.ts +++ b/tests/cases/conformance/async/es6/asyncModule_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async module M { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/asyncSetter_es6.ts b/tests/cases/conformance/async/es6/asyncSetter_es6.ts index 2ca1c805a45..8eedcbb5288 100644 --- a/tests/cases/conformance/async/es6/asyncSetter_es6.ts +++ b/tests/cases/conformance/async/es6/asyncSetter_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true class C { async set foo(value) { } diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts index df0d0e459d4..46060f309b2 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression1_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts index 7678c4b17ab..362f1ecc446 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression2_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts index 0b441f063dd..d3b02033252 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression3_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: number; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts index a0bdf58710f..eba3be31acf 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression4_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts index 3276d24ee4b..71b173f9e73 100644 --- a/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts +++ b/tests/cases/conformance/async/es6/awaitBinaryExpression/awaitBinaryExpression5_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; async function func(): Promise { diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts index 02de646112c..d0442f0968d 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression1_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts index 5d2e4046349..6de07ef12ff 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression2_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts index 702e1f4cbcc..2cc0b7b12d0 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression3_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts index aeeb192b4ba..8e349fbc5c3 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression4_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts index 82bb1f88ad2..bbe81ac94b1 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression5_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts index d5778df71ba..09e88f5acbc 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression6_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts index db01f34c825..fe0182da4aa 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression7_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts index b0b10a02a6f..e3280eb7c10 100644 --- a/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts +++ b/tests/cases/conformance/async/es6/awaitCallExpression/awaitCallExpression8_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare var a: boolean; declare var p: Promise; declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void; diff --git a/tests/cases/conformance/async/es6/awaitUnion_es6.ts b/tests/cases/conformance/async/es6/awaitUnion_es6.ts index 59c5285556c..a132ae01dfd 100644 --- a/tests/cases/conformance/async/es6/awaitUnion_es6.ts +++ b/tests/cases/conformance/async/es6/awaitUnion_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare let a: number | string; declare let b: PromiseLike | PromiseLike; declare let c: PromiseLike; diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts index d543e424a38..aab1f0013ec 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration10_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(a = await => await): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts index 23e40a0e5ba..747d20d7ea8 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration11_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function await(): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts index a457d27073f..bbd77250365 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration12_es6.ts @@ -1,4 +1,3 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var v = async function await(): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts index 83b6a99579c..8670fa02811 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration13_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { // Legal to use 'await' in a type context. var v: await; diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts index b837e6618d1..ba70c61b492 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration14_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { return; } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts index 56bcf84911a..74018b73678 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true declare class Thenable { then(): void; } declare let a: any; declare let obj: { then: string; }; diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts index 3a792140011..289a65bb74f 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts index e75ef1d36a3..25a153b4a34 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration2_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function f(await) { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts index 9ca200f8d76..4ae27c596f1 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration3_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function f(await = await) { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts index ac4447a0e4d..5d1ec389da3 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration4_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true function await() { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts index 60a0a8f4b74..eb3cd1db556 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration5_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(await): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts index a3ab64b9d29..89b0445fd2e 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration6_es6.ts @@ -1,5 +1,4 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(a = await): Promise { } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts index cde12c7dbe3..5a964695da5 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration7_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function bar(): Promise { // 'await' here is an identifier, and not a yield expression. async function foo(a = await): Promise { diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts index 52b6dba2792..764b0f3fb8a 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration8_es6.ts @@ -1,4 +1,3 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true var v = { [await]: foo } \ No newline at end of file diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts index 662fa017606..7671764ad2a 100644 --- a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration9_es6.ts @@ -1,6 +1,5 @@ // @target: ES6 // @noEmitHelpers: true -// @experimentalAsyncFunctions: true async function foo(): Promise { var v = { [await]: foo } } \ No newline at end of file diff --git a/tests/cases/conformance/decorators/decoratorCallGeneric.ts b/tests/cases/conformance/decorators/decoratorCallGeneric.ts new file mode 100644 index 00000000000..3eeaae6837c --- /dev/null +++ b/tests/cases/conformance/decorators/decoratorCallGeneric.ts @@ -0,0 +1,12 @@ +// @experimentalDecorators: true +interface I { + prototype: T, + m: () => T +} +function dec(c: I) { } + +@dec +class C { + _brand: any; + static m() {} +} diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 54e86786d14..41e8d9005f7 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -212,7 +212,7 @@ module FourSlashInterface { // Verifies the completion list items count to be greater than the specified amount. The // completion list is brought up if necessary public completionListItemsCountIsGreaterThan(count: number) { - FourSlash.currentTestState.verifyCompletionListItemsCountIsGreaterThan(count); + FourSlash.currentTestState.verifyCompletionListItemsCountIsGreaterThan(count, this.negative); } public completionListIsEmpty() { diff --git a/tests/cases/fourslash/tsxCompletion10.ts b/tests/cases/fourslash/tsxCompletion10.ts new file mode 100644 index 00000000000..f10e014c282 --- /dev/null +++ b/tests/cases/fourslash/tsxCompletion10.ts @@ -0,0 +1,14 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { ONE: string; TWO: number; } +//// } +//// } +//// var x1 =
+ +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { ONE: string; TWO: number; } +//// } +//// } +//// var x1 =
/*1*/ hello /*2*/ world /*3*/
; +//// var x2 =
/*4*/
/*5*/ world /*6*/
; +//// var x3 =
/*7*/
/*8*/world/*9*/
; +//// var x4 =
/*10*/
; +////
+//// /*end*/ +//// + +for (var i = 1; i <= 10; i++) { + goTo.marker(i + ''); + verify.completionListIsEmpty(); +} + +goTo.marker('end'); +verify.not.completionListIsEmpty(); diff --git a/tests/cases/unittests/cachingInServerLSHost.ts b/tests/cases/unittests/cachingInServerLSHost.ts index ff2da76ba4f..88b44a693b9 100644 --- a/tests/cases/unittests/cachingInServerLSHost.ts +++ b/tests/cases/unittests/cachingInServerLSHost.ts @@ -45,6 +45,11 @@ module ts { return { close: () => { } } + }, + watchDirectory: (path, callback, recursive?) => { + return { + close: () => { } + } } }; }