Merge branch 'master' into reachabilityChecks

This commit is contained in:
Vladimir Matveev
2015-10-16 13:13:51 -07:00
96 changed files with 1426 additions and 384 deletions

View File

@@ -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(<VariableDeclaration>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 = (<ForInStatement | ForOfStatement>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 &&
(<PropertyDeclaration>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 = <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 = (<ForInStatement | ForOfStatement>variableDeclaration.parent.parent).expression;
isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container);
}
}
if (isUsedBeforeDeclaration) {
if (!isBlockScopedNameDeclaredBeforeUse(<Declaration>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;

View File

@@ -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 {

View File

@@ -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<T>(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;
}
}

View File

@@ -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

View File

@@ -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));
}
}
}
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -2414,4 +2414,16 @@ namespace ts {
}
}
}
export function arrayStructurallyIsEqualTo<T>(array1: Array<T>, array2: Array<T>): boolean {
if (!array1 || !array2) {
return false;
}
if (array1.length !== array2.length) {
return false;
}
return arrayIsEqualTo(array1.sort(), array2.sort());
}
}

View File

@@ -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}`);
}
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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<ScriptInfo> = {};
roots: ScriptInfo[] = [];
private resolvedModuleNames: ts.FileMap<Map<TimestampedResolvedModule>>;
private resolvedModuleNames: ts.FileMap<Map<TimestampedResolvedModule>>;
private moduleResolutionHost: ts.ModuleResolutionHost;
constructor(public host: ServerHost, public project: Project) {
this.resolvedModuleNames = ts.createFileMap<Map<TimestampedResolvedModule>>(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<TimestampedResolvedModule> = {};
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<ts.SourceFile> = {};
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<FileWatcher> = {};
// 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<number> = {};
hostConfiguration: HostConfiguration;
timerForDetectingProjectFilelistChanges: Map<NodeJS.Timer> = {};
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;
}

View File

@@ -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<T>(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();
}
}

View File

@@ -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);
}

View File

@@ -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 = (<JsxElement>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;

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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))

View File

@@ -0,0 +1,10 @@
=== tests/cases/compiler/file1.ts ===
c;
>c : number
=== tests/cases/compiler/file2.ts ===
const c = 0;
>c : number
>0 : number

View File

@@ -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<T> {
prototype: T,
m: () => T
}
function dec<T>(c: I<T>) { }
@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() {}
}

View File

@@ -0,0 +1,31 @@
//// [decoratorCallGeneric.ts]
interface I<T> {
prototype: T,
m: () => T
}
function dec<T>(c: I<T>) { }
@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;
})();

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,17 @@
=== tests/cases/compiler/es2015modulekind.ts ===
export default class A
>A : A
{
constructor ()
{
}
public B()
>B : () => number
{
return 42;
>42 : number
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,17 @@
=== tests/cases/compiler/es2015modulekindWithES6Target.ts ===
export default class A
>A : A
{
constructor ()
{
}
public B()
>B : () => number
{
return 42;
>42 : number
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,17 @@
=== tests/cases/compiler/es6modulekindWithES2015Target.ts ===
export default class A
>A : A
{
constructor ()
{
}
public B()
>B : () => number
{
return 42;
>42 : number
}
}

View File

@@ -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;

View File

@@ -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))

View File

@@ -0,0 +1,10 @@
=== tests/cases/compiler/file1.ts ===
l;
>l : number
=== tests/cases/compiler/file2.ts ===
const l = 0;
>l : number
>0 : number

View File

@@ -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
}

View File

@@ -0,0 +1,17 @@
// @target: es2015
// @sourcemap: false
// @declaration: false
// @module: es2015
export default class A
{
constructor ()
{
}
public B()
{
return 42;
}
}

View File

@@ -0,0 +1,17 @@
// @target: es6
// @sourcemap: false
// @declaration: false
// @module: es2015
export default class A
{
constructor ()
{
}
public B()
{
return 42;
}
}

View File

@@ -0,0 +1,17 @@
// @target: es2015
// @sourcemap: false
// @declaration: false
// @module: es6
export default class A
{
constructor ()
{
}
public B()
{
return 42;
}
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var foo = async foo(): Promise<void> => {
// Legal to use 'await' in a type context.

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var foo = async (): Promise<void> => {
};

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var f = (await) => {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
function f(await = await) {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var await = () => {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var foo = async (await): Promise<void> => {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var foo = async (a = await): Promise<void> => {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var bar = async (): Promise<void> => {
// 'await' here is an identifier, and not an await expression.

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var foo = async (): Promise<void> => {
var v = { [await]: foo }

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var foo = async (a = await => await): Promise<void> => {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
class C {
method() {
function other() {}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
class C {
method() {
var fn = async () => await this;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @isolatedModules: true
// @experimentalAsyncFunctions: true
import { MyPromise } from "missing";
declare var p: Promise<number>;

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @experimentalAsyncFunctions: true
type MyPromise<T> = Promise<T>;
declare var MyPromise: typeof Promise;
declare var p: Promise<number>;

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async class C {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
class C {
async constructor() {
}

View File

@@ -1,4 +1,3 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare async function foo(): Promise<void>;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async enum E {
Value
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
class C {
async get foo() {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async interface I {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async module M {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
class C {
async set foo(value) {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
async function func(): Promise<void> {

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
async function func(): Promise<void> {

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: number;
declare var p: Promise<number>;
async function func(): Promise<void> {

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
async function func(): Promise<void> {

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
async function func(): Promise<void> {

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare var a: boolean;
declare var p: Promise<boolean>;
declare function fn(arg0: boolean, arg1: boolean, arg2: boolean): void;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare let a: number | string;
declare let b: PromiseLike<number> | PromiseLike<string>;
declare let c: PromiseLike<number | string>;

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(a = await => await): Promise<void> {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function await(): Promise<void> {
}

View File

@@ -1,4 +1,3 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var v = async function await(): Promise<void> { }

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(): Promise<void> {
// Legal to use 'await' in a type context.
var v: await;

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(): Promise<void> {
return;
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
declare class Thenable { then(): void; }
declare let a: any;
declare let obj: { then: string; };

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(): Promise<void> {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
function f(await) {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
function f(await = await) {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
function await() {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(await): Promise<void> {
}

View File

@@ -1,5 +1,4 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(a = await): Promise<void> {
}

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function bar(): Promise<void> {
// 'await' here is an identifier, and not a yield expression.
async function foo(a = await): Promise<void> {

View File

@@ -1,4 +1,3 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
var v = { [await]: foo }

View File

@@ -1,6 +1,5 @@
// @target: ES6
// @noEmitHelpers: true
// @experimentalAsyncFunctions: true
async function foo(): Promise<void> {
var v = { [await]: foo }
}

View File

@@ -0,0 +1,12 @@
// @experimentalDecorators: true
interface I<T> {
prototype: T,
m: () => T
}
function dec<T>(c: I<T>) { }
@dec
class C {
_brand: any;
static m() {}
}

View File

@@ -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() {

View File

@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// div: { ONE: string; TWO: number; }
//// }
//// }
//// var x1 = <div><//**/
goTo.marker();
verify.memberListCount(1);
verify.completionListContains('div');

View File

@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// div: { ONE: string; TWO: number; }
//// }
//// }
//// var x1 = <div> /*1*/ hello /*2*/ world /*3*/</div>;
//// var x2 = <div> /*4*/ <div></div> /*5*/ world /*6*/</div>;
//// var x3 = <div>/*7*/<div/>/*8*/world/*9*/</div>;
//// var x4 = <div>/*10*/</div>;
//// <div/>
//// /*end*/
////
for (var i = 1; i <= 10; i++) {
goTo.marker(i + '');
verify.completionListIsEmpty();
}
goTo.marker('end');
verify.not.completionListIsEmpty();

View File

@@ -45,6 +45,11 @@ module ts {
return {
close: () => { }
}
},
watchDirectory: (path, callback, recursive?) => {
return {
close: () => { }
}
}
};
}