diff --git a/Jakefile b/Jakefile index 7a8ee59d532..a36c3f4a9d7 100644 --- a/Jakefile +++ b/Jakefile @@ -62,19 +62,17 @@ var servicesSources = [ var harnessSources = [ "harness.ts", "sourceMapRecorder.ts", -// TODO Re-enable -// "harnessLanguageService.ts", -// "fourslash.ts", - "runner.ts", + "harnessLanguageService.ts", + "fourslash.ts", "external/json2.ts", "runnerbase.ts", "compilerRunner.ts", "typeWriter.ts", -// TODO Re-enable fourslash and project tests -// "fourslashRunner.ts", + "fourslashRunner.ts", "projectsRunner.ts", "unittestrunner.ts", "rwcRunner.ts", + "runner.ts" ].map(function (f) { return path.join(harnessDirectory, f); }); @@ -233,7 +231,7 @@ task("generate-diagnostics", [diagnosticInfoMapTs]) var tcFile = path.join(builtLocalDirectory, "tc.js"); compileFile(tcFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); -var tcServicesFile = path.join(builtLocalDirectory, "services.js"); +var tcServicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); compileFile(tcServicesFile, servicesSources, [builtLocalDirectory, copyright].concat(servicesSources), [copyright], /*useBuiltCompiler:*/ true); // Local target to build the compiler and services diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92da4005ea9..6097e3337dd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -63,7 +63,32 @@ module ts { var diagnostics: Diagnostic[] = []; var diagnosticsModified: boolean = false; - var checker: TypeChecker; + var checker: TypeChecker = { + getProgram: () => program, + getDiagnostics: getDiagnostics, + getGlobalDiagnostics: getGlobalDiagnostics, + getNodeCount: () => sum(program.getSourceFiles(), "nodeCount"), + getIdentifierCount: () => sum(program.getSourceFiles(), "identifierCount"), + getSymbolCount: () => sum(program.getSourceFiles(), "symbolCount"), + getTypeCount: () => typeCount, + checkProgram: checkProgram, + emitFiles: invokeEmitter, + getSymbolOfNode: getSymbolOfNode, + getParentOfSymbol: getParentOfSymbol, + getTypeOfSymbol: getTypeOfSymbol, + getDeclaredTypeOfSymbol: getDeclaredTypeOfSymbol, + getPropertiesOfType: getPropertiesOfType, + getSignaturesOfType: getSignaturesOfType, + getIndexTypeOfType: getIndexTypeOfType, + getReturnTypeOfSignature: getReturnTypeOfSignature, + resolveEntityName: resolveEntityName, + getSymbolsInScope: getSymbolsInScope, + getSymbolOfIdentifier: getSymbolOfIdentifier, + getTypeOfExpression: getTypeOfExpression, + typeToString: typeToString, + symbolToString: symbolToString, + getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType + }; function addDiagnostic(diagnostic: Diagnostic) { diagnostics.push(diagnostic); @@ -739,10 +764,10 @@ module ts { }; } - function typeToString(type: Type, flags?: TypeFormatFlags): string { + function typeToString(type: Type, enclosingDeclaration?:Node, flags?: TypeFormatFlags): string { var stringWriter = createSingleLineTextWriter(); // TODO(shkamat): typeToString should take enclosingDeclaration as input, once we have implemented enclosingDeclaration - writeTypeToTextWriter(type, /*enclosingDeclaration*/ null, flags, stringWriter); + writeTypeToTextWriter(type, enclosingDeclaration, flags, stringWriter); return stringWriter.getText(); } @@ -1379,7 +1404,7 @@ module ts { type.baseTypes.push(baseType); } else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, TypeFormatFlags.WriteArrayAsGenericType)); + error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); } } else { @@ -1420,7 +1445,7 @@ module ts { type.baseTypes.push(baseType); } else { - error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, TypeFormatFlags.WriteArrayAsGenericType)); + error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); } } else { @@ -1987,7 +2012,7 @@ module ts { type = createTypeReference(type, map(node.typeArguments, t => getTypeFromTypeNode(t))); } else { - error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); + error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); type = undefined; } } @@ -2387,7 +2412,7 @@ module ts { var errorInfo = chainDiagnosticMessages(undefined, Diagnostics.Named_properties_0_of_types_1_and_2_are_not_identical, prop.name, typeName1, typeName2); errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon, typeToString(type), typeName1, typeName2); - addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo)); + addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo, program.getCompilerHost().getNewLine())); } } } @@ -2434,7 +2459,7 @@ module ts { error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target)); } else if (errorInfo) { - addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo)); + addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo, program.getCompilerHost().getNewLine())); } return result; @@ -4784,14 +4809,6 @@ module ts { return (node.flags & NodeFlags.Private) && isInAmbientContext(node); } - function isInAmbientContext(node: Node): boolean { - while (node) { - if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) return true; - node = node.parent; - } - return false; - } - function checkSpecializedSignatureDeclaration(signatureDeclarationNode: SignatureDeclaration): void { var signature = getSignatureFromDeclaration(signatureDeclarationNode); if (!signature.hasStringLiterals) { @@ -6181,7 +6198,7 @@ module ts { // True if the given identifier is part of a type reference function isTypeReferenceIdentifier(identifier: Identifier): boolean { var node: Node = identifier; - while (node.parent && node.parent.kind === SyntaxKind.QualifiedName) node = node.parent; + if (node.parent && node.parent.kind === SyntaxKind.QualifiedName) node = node.parent; return node.parent && node.parent.kind === SyntaxKind.TypeReference; } @@ -6245,10 +6262,20 @@ module ts { return false; } + function isRightSideOfQualifiedName(node: Node) { + return (node.parent.kind === SyntaxKind.QualifiedName || node.parent.kind === SyntaxKind.PropertyAccess) && + (node.parent).right === node; + } + function getSymbolOfIdentifier(identifier: Identifier) { if (isExpression(identifier)) { - if (isRightSideOfQualifiedName()) { - // TODO + if (isRightSideOfQualifiedName(identifier)) { + var node = identifier.parent; + var symbol = getNodeLinks(node).resolvedSymbol; + if (!symbol) { + checkPropertyAccess(node); + } + return getNodeLinks(node).resolvedSymbol; } return resolveEntityName(identifier, identifier, SymbolFlags.Value); } @@ -6256,16 +6283,58 @@ module ts { return getSymbolOfNode(identifier.parent); } if (isTypeReferenceIdentifier(identifier)) { - var entityName = isRightSideOfQualifiedName() ? identifier.parent : identifier; + var entityName = isRightSideOfQualifiedName(identifier) ? identifier.parent : identifier; var meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; return resolveEntityName(entityName, entityName, meaning); } - function isRightSideOfQualifiedName() { - return (identifier.parent.kind === SyntaxKind.QualifiedName || identifier.parent.kind === SyntaxKind.PropertyAccess) && - (identifier.parent).right === identifier; - } } + function getTypeOfExpression(node: Node) { + if (isExpression(node)) { + while (isRightSideOfQualifiedName(node)) { + node = node.parent; + } + return getApparentType(checkExpression(node)); + } + return unknownType; + } + + function getAugmentedPropertiesOfApparentType(type: Type): Symbol[]{ + var apparentType = getApparentType(type); + + if (apparentType.flags & TypeFlags.ObjectType) { + // Augment the apprent type with Function and Object memeber as applicaple + var propertiesByName: Map = {}; + var results: Symbol[] = []; + + forEach(getPropertiesOfType(apparentType), (s) => { + propertiesByName[s.name] = s; + results.push(s); + }); + + var resolved = resolveObjectTypeMembers(type); + forEachValue(resolved.members, (s) => { + if (symbolIsValue(s) && !propertiesByName[s.name]) { + propertiesByName[s.name] = s; + results.push(s); + } + }); + + if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) { + forEach(getPropertiesOfType(globalFunctionType), (s) => { + if (!propertiesByName[s.name]) { + propertiesByName[s.name] = s; + results.push(s); + } + }); + } + + return results; + } + else { + return getPropertiesOfType(apparentType); + } + } // Emitter support function isExternalModuleSymbol(symbol: Symbol): boolean { @@ -6459,28 +6528,7 @@ module ts { } initializeTypeChecker(); - checker = { - getProgram: () => program, - getDiagnostics: getDiagnostics, - getGlobalDiagnostics: getGlobalDiagnostics, - getNodeCount: () => sum(program.getSourceFiles(), "nodeCount"), - getIdentifierCount: () => sum(program.getSourceFiles(), "identifierCount"), - getSymbolCount: () => sum(program.getSourceFiles(), "symbolCount"), - getTypeCount: () => typeCount, - checkProgram: checkProgram, - emitFiles: invokeEmitter, - getSymbolOfNode: getSymbolOfNode, - getParentOfSymbol: getParentOfSymbol, - getTypeOfSymbol: getTypeOfSymbol, - getDeclaredTypeOfSymbol: getDeclaredTypeOfSymbol, - getPropertiesOfType: getPropertiesOfType, - getSignaturesOfType: getSignaturesOfType, - getIndexTypeOfType: getIndexTypeOfType, - getReturnTypeOfSignature: getReturnTypeOfSignature, - resolveEntityName: resolveEntityName, - getSymbolsInScope: getSymbolsInScope, - getSymbolOfIdentifier: getSymbolOfIdentifier - }; + return checker; } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 8e59408f151..2aa6ae6e9a7 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -134,6 +134,18 @@ module ts { return result; } + export function forEachKey(map: Map, callback: (key: string) => U): U { + var result: U; + for (var id in map) { + if (result = callback(id)) break; + } + return result; + } + + export function lookUp(map: Map, key: string): T { + return hasProperty(map, key) ? map[key] : undefined; + } + export function mapToArray(map: Map): T[] { var result: T[] = []; for (var id in map) result.push(map[id]); @@ -214,7 +226,7 @@ module ts { } } - export function flattenDiagnosticChain(file: SourceFile, start: number, length: number, diagnosticChain: DiagnosticMessageChain): Diagnostic { + export function flattenDiagnosticChain(file: SourceFile, start: number, length: number, diagnosticChain: DiagnosticMessageChain, newLine: string): Diagnostic { var code = diagnosticChain.code; var category = diagnosticChain.category; var messageText = ""; @@ -222,7 +234,7 @@ module ts { var indent = 0; while (diagnosticChain) { if (indent) { - messageText += sys.newLine; + messageText += newLine; for (var i = 0; i < indent; i++) { messageText += " "; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d1e2367babb..a80845d852b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -22,6 +22,7 @@ module ts { var compilerOptions = program.getCompilerOptions(); var sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap ? [] : undefined; var diagnostics: Diagnostic[] = []; + var newLine = program.getCompilerHost().getNewLine(); function getSourceFilePathInNewDir(newDirPath: string, sourceFile: SourceFile) { var sourceFilePath = getNormalizedPathFromPathCompoments(getNormalizedPathComponents(sourceFile.filename, compilerHost.getCurrentDirectory())); @@ -126,7 +127,7 @@ module ts { function writeLine() { if (!lineStart) { - output += sys.newLine; + output += newLine; lineCount++; linePos = output.length; lineStart = true; @@ -2252,7 +2253,7 @@ module ts { compilerHost.getCurrentDirectory(), /*isAbsolutePathAnUrl*/ false); - referencePathsOutput += "/// " + sys.newLine; + referencePathsOutput += "/// " + newLine; } if (root) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d3e40a21780..5174b9aa788 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -88,12 +88,12 @@ module ts { return createFileDiagnostic(file, start, length, message, arg0, arg1, arg2); } - export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain): Diagnostic { + export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain, newLine: string): Diagnostic { node = getErrorSpanForNode(node); var file = getSourceFileOfNode(node); var start = skipTrivia(file.text, node.pos); var length = node.end - start; - return flattenDiagnosticChain(file, start, length, messageChain); + return flattenDiagnosticChain(file, start, length, messageChain, newLine); } export function getErrorSpanForNode(node: Node): Node { @@ -327,6 +327,14 @@ module ts { return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0; } + export function isInAmbientContext(node: Node): boolean { + while (node) { + if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) return true; + node = node.parent; + } + return false; + } + enum ParsingContext { SourceElements, // Elements in source file ModuleElements, // Elements in module declaration @@ -1624,7 +1632,12 @@ module ts { parameter.name = identifier; finishNode(parameter); - var signature = { parameters: [parameter] }; + var parameters = >[]; + parameters.push(parameter); + parameters.pos = parameter.pos; + parameters.end = parameter.end; + + var signature = { parameters: parameters }; return parseArrowExpressionTail(identifier.pos, signature, /*noIn:*/ false); } diff --git a/src/compiler/tc.ts b/src/compiler/tc.ts index b363ab4d39f..eb232f98654 100644 --- a/src/compiler/tc.ts +++ b/src/compiler/tc.ts @@ -174,7 +174,8 @@ module ts { writeFile: writeFile, getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()), useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, - getCanonicalFileName: getCanonicalFileName + getCanonicalFileName: getCanonicalFileName, + getNewLine: () => sys.newLine }; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6b846768c15..6ca6035a968 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -599,6 +599,10 @@ module ts { resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolOfIdentifier(identifier: Identifier): Symbol; + getTypeOfExpression(node: Expression, contextualType?: Type, contextualMapper?: TypeMapper): Type; + typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; + symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; + getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; } export interface TextWriter { @@ -1104,5 +1108,13 @@ module ts { getCurrentDirectory(): string; getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; + getNewLine(): string; + } + + export enum ByteOrderMark { + None = 0, + Utf8 = 1, + Utf16BigEndian = 2, + Utf16LittleEndian = 3, } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 3037971471a..9a1f97395a0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -14,6 +14,7 @@ // /// +/// /// module FourSlash { @@ -108,7 +109,7 @@ module FourSlash { High } - var entityMap: TypeScript.IIndexable = { + var entityMap: ts.Map = { '&': '&', '"': '"', "'": ''', @@ -127,7 +128,7 @@ module FourSlash { export var currentTestState: TestState = null; - export class TestCancellationToken implements TypeScript.ICancellationToken { + export class TestCancellationToken implements ts.CancellationToken { // 0 - cancelled // >0 - not cancelled // <0 - not cancelled and value denotes number of isCancellationRequested after which token become cancelled @@ -162,7 +163,7 @@ module FourSlash { f(); } catch (e) { - if (e instanceof TypeScript.OperationCanceledException) { + if (e instanceof ts.OperationCanceledException) { return; } } @@ -172,9 +173,8 @@ module FourSlash { export class TestState { // Language service instance - public languageServiceShimHost: Harness.TypeScriptLS = null; - private languageService: TypeScript.Services.ILanguageService = null; - private newLanguageService: ts.LanguageService = null; + public languageServiceShimHost: Harness.LanguageService.TypeScriptLS; + private languageService: ts.LanguageService; // A reference to the language service's compiler state's compiler instance private compiler: () => { getSyntaxTree(fileName: string): TypeScript.SyntaxTree; getSourceUnit(fileName: string): TypeScript.SourceUnitSyntax; }; @@ -189,7 +189,7 @@ module FourSlash { // Whether or not we should format on keystrokes public enableFormatting = true; - public formatCodeOptions: TypeScript.Services.FormatCodeOptions = null; + public formatCodeOptions: ts.FormatCodeOptions; public cancellationToken: TestCancellationToken; @@ -202,7 +202,7 @@ module FourSlash { constructor(public testData: FourSlashData) { // Initialize the language service with all the scripts this.cancellationToken = new TestCancellationToken(); - this.languageServiceShimHost = new Harness.TypeScriptLS(this.cancellationToken); + this.languageServiceShimHost = new Harness.LanguageService.TypeScriptLS(this.cancellationToken); var harnessCompiler = Harness.Compiler.getCompiler(); var inputFiles: { unitName: string; content: string }[] = []; @@ -226,40 +226,48 @@ module FourSlash { }); } - // NEWTODO: Re-implement commented-out section - // harnessCompiler.addInputFiles(inputFiles); - try { - // var resolvedFiles = harnessCompiler.resolve(); + // NEWTODO: Re-implement commented-out section + //harnessCompiler.addInputFiles(inputFiles); + //try { + // var resolvedFiles = harnessCompiler.resolve(); - //resolvedFiles.forEach(file => { - // if (!Harness.isLibraryFile(file.path)) { - // var fixedPath = file.path.substr(file.path.indexOf('tests/')); - // var content = harnessCompiler.getContentForFile(fixedPath); - // this.languageServiceShimHost.addScript(fixedPath, content); - // } - //}); + // resolvedFiles.forEach(file => { + // if (!Harness.isLibraryFile(file.path)) { + // var fixedPath = file.path.substr(file.path.indexOf('tests/')); + // var content = harnessCompiler.getContentForFile(fixedPath); + // this.languageServiceShimHost.addScript(fixedPath, content); + // } + // }); - // NEWTODO: For now do not resolve, just use the input files - inputFiles.forEach(file => { - if (!Harness.isLibraryFile(file.unitName)) { - this.languageServiceShimHost.addScript(file.unitName, file.content); - } - }); + // this.languageServiceShimHost.addScript('lib.d.ts', Harness.Compiler.libTextMinimal); + //} + //finally { + // // harness no longer needs the results of the above work, make sure the next test operations are in a clean state + // harnessCompiler.reset(); + //} - this.languageServiceShimHost.addScript('lib.d.ts', Harness.Compiler.libTextMinimal); - } - finally { - // harness no longer needs the results of the above work, make sure the next test operations are in a clean state - //harnessCompiler.reset(); - } + /// NEWTODO: For now do not resolve, just use the input files inputFiles.forEach(file => { if (!Harness.isLibraryFile(file.unitName)) { this.languageServiceShimHost.addScript(file.unitName, file.content); } }); + this.languageServiceShimHost.addScript('lib.d.ts', Harness.Compiler.libTextMinimal); // Sneak into the language service and get its compiler so we can examine the syntax trees this.languageService = this.languageServiceShimHost.getLanguageService().languageService; - this.newLanguageService = this.languageServiceShimHost.newLS; var compilerState = (this.languageService).compiler; this.compiler = () => compilerState.compiler; - this.formatCodeOptions = new TypeScript.Services.FormatCodeOptions(); + this.formatCodeOptions = { + IndentSize: 4, + TabSize: 4, + NewLineCharacter: sys.newLine, + ConvertTabsToSpaces: true, + InsertSpaceAfterCommaDelimiter: true, + InsertSpaceAfterSemicolonInForStatements: true, + InsertSpaceBeforeAndAfterBinaryOperators: true, + InsertSpaceAfterKeywordsInControlFlowStatements: true, + InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + PlaceOpenBraceOnNewLineForFunctions: false, + PlaceOpenBraceOnNewLineForControlBlocks: false, + }; this.testData.files.forEach(file => { var filename = file.fileName.replace(Harness.IO.directoryName(file.fileName), '').substr(1); @@ -308,7 +316,7 @@ module FourSlash { public openFile(name: string): void; public openFile(indexOrName: any) { var fileToOpen: FourSlashFile = this.findFile(indexOrName); - fileToOpen.fileName = Harness.Path.switchToForwardSlashes(fileToOpen.fileName); + fileToOpen.fileName = ts.normalizeSlashes(fileToOpen.fileName); this.activeFile = fileToOpen; var filename = fileToOpen.fileName.replace(Harness.IO.directoryName(fileToOpen.fileName), '').substr(1); this.scenarioActions.push(''); @@ -318,9 +326,7 @@ module FourSlash { var startMarker = this.getMarkerByName(startMarkerName); var endMarker = this.getMarkerByName(endMarkerName); var predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { - // NEWTODO: make this more specific again - //return ((errorMinChar === startPos) && (errorLimChar === endPos)) ? true : false; - return ((errorMinChar >= startPos) && (errorLimChar <= endPos)) ? true : false; + return ((errorMinChar === startPos) && (errorLimChar === endPos)) ? true : false; }; var exists = this.anyErrorInRange(predicate, startMarker, endMarker); @@ -328,28 +334,15 @@ module FourSlash { this.taoInvalidReason = 'verifyErrorExistsBetweenMarkers NYI'; if (exists !== negative) { - this.new_printErrorLog(negative, this.new_getAllDiagnostics()); + this.printErrorLog(negative, this.getAllDiagnostics()); throw new Error("Failure between markers: " + startMarkerName + ", " + endMarkerName); } } - private getDiagnostics(fileName: string): TypeScript.Diagnostic[] { + private getDiagnostics(fileName: string): ts.Diagnostic[] { var syntacticErrors = this.languageService.getSyntacticDiagnostics(fileName); var semanticErrors = this.languageService.getSemanticDiagnostics(fileName); - var diagnostics: TypeScript.Diagnostic[] = []; - diagnostics.push.apply(diagnostics, syntacticErrors); - diagnostics.push.apply(diagnostics, semanticErrors); - - return diagnostics; - } - - - private new_getDiagnostics(fileName: string): ts.Diagnostic[] { - var syntacticErrors = this.newLanguageService.getSyntacticDiagnostics(fileName); - var semanticErrors = this.newLanguageService.getSemanticDiagnostics(fileName); - - var diagnostics: ts.Diagnostic[] = []; diagnostics.push.apply(diagnostics, syntacticErrors); diagnostics.push.apply(diagnostics, semanticErrors); @@ -357,9 +350,8 @@ module FourSlash { return diagnostics; } - - private getAllDiagnostics(): TypeScript.Diagnostic[] { - var diagnostics: TypeScript.Diagnostic[] = []; + private getAllDiagnostics(): ts.Diagnostic[] { + var diagnostics: ts.Diagnostic[] = []; var fileNames = JSON.parse(this.languageServiceShimHost.getScriptFileNames()); for (var i = 0, n = fileNames.length; i < n; i++) { @@ -369,17 +361,6 @@ module FourSlash { return diagnostics; } - private new_getAllDiagnostics(): ts.Diagnostic[] { - var diagnostics: ts.Diagnostic[] = []; - - var fileNames = JSON.parse(this.languageServiceShimHost.getScriptFileNames()); - for (var i = 0, n = fileNames.length; i < n; i++) { - diagnostics.push.apply(this.new_getDiagnostics(fileNames[i])); - } - - return diagnostics; - } - public verifyErrorExistsAfterMarker(markerName: string, negative: boolean, after: boolean) { var marker: Marker = this.getMarkerByName(markerName); var predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean; @@ -397,17 +378,17 @@ module FourSlash { this.taoInvalidReason = 'verifyErrorExistsAfterMarker NYI'; var exists = this.anyErrorInRange(predicate, marker); - var diagnostics = this.new_getAllDiagnostics(); + var diagnostics = this.getAllDiagnostics(); if (exists !== negative) { - this.new_printErrorLog(negative, diagnostics); + this.printErrorLog(negative, diagnostics); throw new Error("Failure at marker: " + markerName); } } private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker) { - var errors = this.new_getDiagnostics(startMarker.fileName); + var errors = this.getDiagnostics(startMarker.fileName); var exists = false; var startPos = startMarker.position; @@ -415,7 +396,7 @@ module FourSlash { var endPos = endMarker.position; } - errors.forEach((error)=> { + errors.forEach(function (error: ts.Diagnostic) { if (predicate(error.start, error.start + error.length, startPos, endPos)) { exists = true; } @@ -424,26 +405,14 @@ module FourSlash { return exists; } - private printErrorLog(expectErrors: boolean, errors: TypeScript.Diagnostic[]) { + private printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) { if (expectErrors) { Harness.IO.log("Expected error not found. Error list is:"); } else { Harness.IO.log("Unexpected error(s) found. Error list is:"); } - errors.forEach(function (error: TypeScript.Diagnostic) { - Harness.IO.log(" minChar: " + error.start() + ", limChar: " + (error.start() + error.length()) + ", message: " + error.message() + "\n"); - }); - } - - private new_printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) { - if (expectErrors) { - Harness.IO.log("Expected error not found. Error list is:"); - } else { - Harness.IO.log("Unexpected error(s) found. Error list is:"); - } - - errors.forEach(error => { + errors.forEach(function (error: ts.Diagnostic) { Harness.IO.log(" minChar: " + error.start + ", limChar: " + (error.start + error.length) + ", message: " + error.messageText + "\n"); }); } @@ -455,6 +424,7 @@ module FourSlash { this.scenarioActions.push(''); if (actual !== expected) { + this.printErrorLog(false, errors); var errorMsg = "Actual number of errors (" + actual + ") does not match expected number (" + expected + ")"; Harness.IO.log(errorMsg); throw new Error(errorMsg); @@ -754,22 +724,22 @@ module FourSlash { this.taoInvalidReason = 'verifyCurrentSignatureHelpIs NYI'; var help = this.getActiveSignatureHelp(); - assert.equal(help.prefix + help.parameters.map(p => p.display).join(help.separator) + help.suffix, expected); + assert.equal(help.signatureInfo, expected); } public verifyCurrentParameterIsVariable(isVariable: boolean) { this.taoInvalidReason = 'verifyCurrentParameterIsVariable NYI'; - var signature = this.getActiveSignatureHelp(); - assert.isNotNull(signature); - assert.equal(isVariable, signature.isVariadic); + var activeParameter = this.getActiveParameter(); + assert.isNotNull(activeParameter.parameter); + assert.equal(isVariable, activeParameter.parameter.isVariable); } public verifyCurrentParameterHelpName(name: string) { this.taoInvalidReason = 'verifyCurrentParameterHelpName NYI'; var activeParameter = this.getActiveParameter(); - var activeParameterName = activeParameter.name; + var activeParameterName = activeParameter.parameter ? activeParameter.parameter.name : activeParameter.typeParameter.name; assert.equal(activeParameterName, name); } @@ -778,14 +748,16 @@ module FourSlash { var activeSignature = this.getActiveSignatureHelp(); var activeParameter = this.getActiveParameter(); - assert.equal(activeParameter.display, parameter); + var activeParameterMinChar = activeParameter.parameter ? activeParameter.parameter.minChar : activeParameter.typeParameter.minChar; + var activeParameterLimChar = activeParameter.parameter ? activeParameter.parameter.limChar : activeParameter.typeParameter.limChar; + assert.equal(activeSignature.signatureInfo.substring(activeParameterMinChar, activeParameterLimChar), parameter); } public verifyCurrentParameterHelpDocComment(docComment: string) { this.taoInvalidReason = 'verifyCurrentParameterHelpDocComment NYI'; var activeParameter = this.getActiveParameter(); - var activeParameterDocComment = activeParameter.documentation; + var activeParameterDocComment = activeParameter.parameter ? activeParameter.parameter.docComment : activeParameter.typeParameter.docComment; assert.equal(activeParameterDocComment, docComment); } @@ -798,13 +770,13 @@ module FourSlash { public verifyCurrentSignatureHelpTypeParameterCount(expectedCount: number) { this.taoInvalidReason = 'verifyCurrentSignatureHelpTypeParameterCount NYI'; - // assert.equal(this.getActiveSignatureHelp().typeParameters.length, expectedCount); + assert.equal(this.getActiveSignatureHelp().typeParameters.length, expectedCount); } public verifyCurrentSignatureHelpDocComment(docComment: string) { this.taoInvalidReason = 'verifyCurrentSignatureHelpDocComment NYI'; - var actualDocComment = this.getActiveSignatureHelp().documentation; + var actualDocComment = this.getActiveSignatureHelp().docComment; assert.equal(actualDocComment, docComment); } @@ -812,15 +784,15 @@ module FourSlash { this.scenarioActions.push(''); this.scenarioActions.push(''); - var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); - var actual = help && help.items ? help.items.length : 0; + var help = this.languageService.getSignatureAtPosition(this.activeFile.fileName, this.currentCaretPosition); + var actual = help && help.formal ? help.formal.length : 0; assert.equal(actual, expected); } public verifySignatureHelpPresent(shouldBePresent = true) { this.taoInvalidReason = 'verifySignatureHelpPresent NYI'; - var actual = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); + var actual = this.languageService.getSignatureAtPosition(this.activeFile.fileName, this.currentCaretPosition); if (shouldBePresent) { if (!actual) { throw new Error("Expected signature help to be present, but it wasn't"); @@ -832,32 +804,45 @@ module FourSlash { } } - //private getFormalParameter() { - // var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); - // return help.formal; - //} + private getFormalParameter() { + var help = this.languageService.getSignatureAtPosition(this.activeFile.fileName, this.currentCaretPosition); + return help.formal; + } private getActiveSignatureHelp() { - var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); + var help = this.languageService.getSignatureAtPosition(this.activeFile.fileName, this.currentCaretPosition); + var activeFormal = help.activeFormal; // If the signature hasn't been narrowed down yet (e.g. no parameters have yet been entered), // 'activeFormal' will be -1 (even if there is only 1 signature). Signature help will show the // first signature in the signature group, so go with that - var index = help.selectedItemIndex < 0 ? 0 : help.selectedItemIndex; + if (activeFormal === -1) { + activeFormal = 0; + } - return help.items[index]; + return help.formal[activeFormal]; } - private getActiveParameter(): TypeScript.Services.SignatureHelpParameter { + private getActiveParameter(): { parameter: ts.FormalParameterInfo; typeParameter: ts.FormalTypeParameterInfo; } { var currentSig = this.getActiveSignatureHelp(); - var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); - - var item = help.items[help.selectedItemIndex]; - var state = this.languageService.getSignatureHelpCurrentArgumentState(this.activeFile.fileName, this.currentCaretPosition, help.applicableSpan.start()); + var help = this.languageService.getSignatureAtPosition(this.activeFile.fileName, this.currentCaretPosition); // Same logic as in getActiveSignatureHelp - this value might be -1 until a parameter value actually gets typed - var currentParam = state === null ? 0 : state.argumentIndex; - return item.parameters[currentParam]; + var currentParam = help.actual.currentParameter; + if (currentParam === -1) currentParam = 0; + + if (help.actual.currentParameterIsTypeParameter) { + return { + parameter: null, + typeParameter: currentSig.typeParameters[currentParam] + }; + } + else { + return { + parameter: currentSig.parameters[currentParam], + typeParameter: null + }; + } } public getBreakpointStatementLocation(pos: number) { @@ -866,7 +851,7 @@ module FourSlash { var spanInfo = this.languageService.getBreakpointStatementAtPosition(this.activeFile.fileName, pos); var resultString = "\n**Pos: " + pos + " SpanInfo: " + JSON.stringify(spanInfo) + "\n** Statement: "; if (spanInfo !== null) { - resultString = resultString + this.activeFile.content.substr(spanInfo.start(), spanInfo.length()); + resultString = resultString + this.activeFile.content.substr(spanInfo.minChar, spanInfo.limChar - spanInfo.minChar); } return resultString; } @@ -884,7 +869,8 @@ module FourSlash { resultString = resultString + this.getBreakpointStatementLocation(pos); } return resultString; - }); + }, + true /* run immediately */); } public printBreakpointLocation(pos: number) { @@ -896,7 +882,7 @@ module FourSlash { } public printCurrentParameterHelp() { - var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); + var help = this.languageService.getSignatureAtPosition(this.activeFile.fileName, this.currentCaretPosition); Harness.IO.log(JSON.stringify(help)); } @@ -906,10 +892,6 @@ module FourSlash { } public printErrorList() { - Harness.IO.log("--------------"); - Harness.IO.log("Old Errors"); - Harness.IO.log("--------------"); - var syntacticErrors = this.languageService.getSyntacticDiagnostics(this.activeFile.fileName); var semanticErrors = this.languageService.getSemanticDiagnostics(this.activeFile.fileName); var errorList = syntacticErrors.concat(semanticErrors); @@ -917,27 +899,7 @@ module FourSlash { if (errorList.length) { errorList.forEach(err => { - Harness.IO.log("start: " + err.start() + ", length: " + err.length() + ", message: " + err.message()); - }); - } - - Harness.IO.log("--------------"); - Harness.IO.log("New Errors"); - Harness.IO.log("--------------"); - this.new_printErrorList(); - } - - public new_printErrorList() { - var syntacticErrors = this.newLanguageService.getSyntacticDiagnostics(this.activeFile.fileName); - var semanticErrors = this.newLanguageService.getSemanticDiagnostics(this.activeFile.fileName); - var errorList = syntacticErrors.concat(semanticErrors); - Harness.IO.log('Error list (' + errorList.length + ' errors)'); - - - if (errorList.length) { - errorList.forEach(error => { - Harness.IO.log("start: " + error.start + ", length: " + error.length + - ", message: " + error.messageText); + Harness.IO.log("start: " + err.start + ", length: " + err.length + ", message: " + err.messageText); }); } } @@ -1100,10 +1062,10 @@ module FourSlash { offset++; if (ch === '(' || ch === ',') { - // Signature help - this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset); + /* Signature help*/ + this.languageService.getSignatureAtPosition(this.activeFile.fileName, offset); } else if (prevChar === ' ' && /A-Za-z_/.test(ch)) { - // Completions + /* Completions */ this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, false); } @@ -1141,7 +1103,7 @@ module FourSlash { // Handle formatting if (this.enableFormatting) { - var edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeOptions); + var edits = this.languageService.getFormattingEditsOnPaste(this.activeFile.fileName, start, offset, this.formatCodeOptions); offset += this.applyEdits(this.activeFile.fileName, edits, true); this.editCheckpoint(this.activeFile.fileName); } @@ -1154,46 +1116,44 @@ module FourSlash { } private checkPostEditInvariants() { - if (this.editValidation === IncrementalEditValidation.None) { - return; - } + return; - // Get syntactic errors (to force a refresh) - var incrSyntaxErrs = JSON.stringify(this.languageService.getSyntacticDiagnostics(this.activeFile.fileName)); + /// TODO: reimplement this section + //if (this.editValidation === IncrementalEditValidation.None) { + // return; + //} - // Check syntactic structure - var compilationSettings = new TypeScript.CompilationSettings(); - compilationSettings.codeGenTarget = TypeScript.LanguageVersion.EcmaScript5; - var immutableSettings = TypeScript.ImmutableCompilationSettings.fromCompilationSettings(compilationSettings); + //// Get syntactic errors (to force a refresh) + //var incrSyntaxErrs = JSON.stringify(this.languageService.getSyntacticDiagnostics(this.activeFile.fileName)); - var parseOptions = immutableSettings.codeGenTarget(); - var snapshot = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName); - var content = snapshot.getText(0, snapshot.getLength()); - var refSyntaxTree = TypeScript.Parser.parse(this.activeFile.fileName, TypeScript.SimpleText.fromString(content), parseOptions, TypeScript.isDTSFile(this.activeFile.fileName)); - var fullSyntaxErrs = JSON.stringify(refSyntaxTree.diagnostics()); + //// Check syntactic structure + //var snapshot = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName); + //var content = snapshot.getText(0, snapshot.getLength()); + //var refSyntaxTree = TypeScript.Parser.parse(this.activeFile.fileName, TypeScript.SimpleText.fromString(content), ts.ScriptTarget.ES5, TypeScript.isDTSFile(this.activeFile.fileName)); + //var fullSyntaxErrs = JSON.stringify(refSyntaxTree.diagnostics()); - if (incrSyntaxErrs !== fullSyntaxErrs) { - throw new Error('Mismatched incremental/full syntactic errors for file ' + this.activeFile.fileName + '.\n=== Incremental errors ===\n' + incrSyntaxErrs + '\n=== Full Errors ===\n' + fullSyntaxErrs); - } + //if (incrSyntaxErrs !== fullSyntaxErrs) { + // throw new Error('Mismatched incremental/full syntactic errors for file ' + this.activeFile.fileName + '.\n=== Incremental errors ===\n' + incrSyntaxErrs + '\n=== Full Errors ===\n' + fullSyntaxErrs); + //} - if (this.editValidation !== IncrementalEditValidation.SyntacticOnly) { - var compiler = new TypeScript.TypeScriptCompiler(); - for (var i = 0; i < this.testData.files.length; i++) { - snapshot = this.languageServiceShimHost.getScriptSnapshot(this.testData.files[i].fileName); - compiler.addFile(this.testData.files[i].fileName, TypeScript.ScriptSnapshot.fromString(snapshot.getText(0, snapshot.getLength())), TypeScript.ByteOrderMark.None, "0", true); - } + // if (this.editValidation !== IncrementalEditValidation.SyntacticOnly) { + // var compiler = new TypeScript.TypeScriptCompiler(); + // for (var i = 0; i < this.testData.files.length; i++) { + // snapshot = this.languageServiceShimHost.getScriptSnapshot(this.testData.files[i].fileName); + // compiler.addFile(this.testData.files[i].fileName, TypeScript.ScriptSnapshot.fromString(snapshot.getText(0, snapshot.getLength())), ts.ByteOrderMark.None, 0, true); + // } - compiler.addFile('lib.d.ts', TypeScript.ScriptSnapshot.fromString(Harness.Compiler.libTextMinimal), TypeScript.ByteOrderMark.None, "0", true); + // compiler.addFile('lib.d.ts', TypeScript.ScriptSnapshot.fromString(Harness.Compiler.libTextMinimal), ts.ByteOrderMark.None, 0, true); - for (var i = 0; i < this.testData.files.length; i++) { - var refSemanticErrs = JSON.stringify(compiler.getSemanticDiagnostics(this.testData.files[i].fileName)); - var incrSemanticErrs = JSON.stringify(this.languageService.getSemanticDiagnostics(this.testData.files[i].fileName)); + // for (var i = 0; i < this.testData.files.length; i++) { + // var refSemanticErrs = JSON.stringify(compiler.getSemanticDiagnostics(this.testData.files[i].fileName)); + // var incrSemanticErrs = JSON.stringify(this.languageService.getSemanticDiagnostics(this.testData.files[i].fileName)); - if (incrSemanticErrs !== refSemanticErrs) { - throw new Error('Mismatched incremental/full semantic errors for file ' + this.testData.files[i].fileName + '\n=== Incremental errors ===\n' + incrSemanticErrs + '\n=== Full Errors ===\n' + refSemanticErrs); - } - } - } + // if (incrSemanticErrs !== refSemanticErrs) { + // throw new Error('Mismatched incremental/full semantic errors for file ' + this.testData.files[i].fileName + '\n=== Incremental errors ===\n' + incrSemanticErrs + '\n=== Full Errors ===\n' + refSemanticErrs); + // } + // } + // } } private fixCaretPosition() { @@ -1207,18 +1167,18 @@ module FourSlash { }; } - private applyEdits(fileName: string, edits: TypeScript.Services.TextChange[], isFormattingEdit = false): number { + private applyEdits(fileName: string, edits: ts.TextEdit[], isFormattingEdit = false): number { // We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track // of the incremental offest from each edit to the next. Assumption is that these edit ranges don't overlap var runningOffset = 0; - edits = edits.sort((a, b) => a.span.start() - b.span.start()); + edits = edits.sort((a, b) => a.minChar - b.minChar); // Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters var snapshot = this.languageServiceShimHost.getScriptSnapshot(fileName); var oldContent = snapshot.getText(0, snapshot.getLength()); for (var j = 0; j < edits.length; j++) { - this.languageServiceShimHost.editScript(fileName, edits[j].span.start() + runningOffset, edits[j].span.end() + runningOffset, edits[j].newText); - this.updateMarkersForEdit(fileName, edits[j].span.start() + runningOffset, edits[j].span.end() + runningOffset, edits[j].newText); - var change = (edits[j].span.start() - edits[j].span.end()) + edits[j].newText.length; + this.languageServiceShimHost.editScript(fileName, edits[j].minChar + runningOffset, edits[j].limChar + runningOffset, edits[j].text); + this.updateMarkersForEdit(fileName, edits[j].minChar + runningOffset, edits[j].limChar + runningOffset, edits[j].text); + var change = (edits[j].minChar - edits[j].limChar) + edits[j].text.length; runningOffset += change; // TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150) // this.languageService.getScriptLexicalStructure(fileName); @@ -1238,7 +1198,7 @@ module FourSlash { public formatDocument() { this.scenarioActions.push(''); - var edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeOptions); + var edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, 0, this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getLength(), this.formatCodeOptions); this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, true); this.fixCaretPosition(); } @@ -1295,7 +1255,7 @@ module FourSlash { var definition = definitions[definitionIndex]; this.openFile(definition.fileName); - this.currentCaretPosition = definition.textSpan.start(); + this.currentCaretPosition = definition.minChar; } public verifyDefinitionLocationExists(negative: boolean) { @@ -1331,7 +1291,7 @@ module FourSlash { throw new Error('verifyCaretAtMarker failed - expected to be in file "' + pos.fileName + '", but was in file "' + this.activeFile.fileName + '"'); } if (pos.position !== this.currentCaretPosition) { - throw new Error('verifyCaretAtMarker failed - expected to be at marker "/*' + markerName + '*' + '/, but was at position ' + this.currentCaretPosition + '(' + this.getLineColStringAtCaret() + ')'); + throw new Error('verifyCaretAtMarker failed - expected to be at marker "/*' + markerName + '*/, but was at position ' + this.currentCaretPosition + '(' + this.getLineColStringAtCaret() + ')'); } } @@ -1401,7 +1361,7 @@ module FourSlash { '\t Actual: null'); } - var actual = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(span.start(), span.end()); + var actual = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(span.minChar, span.limChar); if (actual !== text) { throw new Error('verifyCurrentNameOrDottedNameSpanText\n' + '\tExpected: "' + text + '"\n' + @@ -1413,7 +1373,7 @@ module FourSlash { var spanInfo = this.languageService.getNameOrDottedNameSpan(this.activeFile.fileName, pos, pos); var resultString = "\n**Pos: " + pos + " SpanInfo: " + JSON.stringify(spanInfo) + "\n** Statement: "; if (spanInfo !== null) { - resultString = resultString + this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(spanInfo.start(), spanInfo.end()); + resultString = resultString + this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(spanInfo.minChar, spanInfo.limChar); } return resultString; } @@ -1431,7 +1391,8 @@ module FourSlash { resultString = resultString + this.getNameOrDottedNameSpan(pos); } return resultString; - }); + }, + true /* run immediately */); } public printNameOrDottedNameSpans(pos: number) { @@ -1441,7 +1402,7 @@ module FourSlash { public verifyOutliningSpans(spans: TextSpan[]) { this.taoInvalidReason = 'verifyOutliningSpans NYI'; - var actual = this.languageService.getOutliningSpans(this.activeFile.fileName); + var actual = this.languageService.getOutliningRegions(this.activeFile.fileName); if (actual.length !== spans.length) { throw new Error('verifyOutliningSpans failed - expected total spans to be ' + spans.length + ', but was ' + actual.length); @@ -1450,27 +1411,8 @@ module FourSlash { for (var i = 0; i < spans.length; i++) { var expectedSpan = spans[i]; var actualSpan = actual[i]; - if (expectedSpan.start !== actualSpan.textSpan.start() || expectedSpan.end !== actualSpan.textSpan.end()) { - throw new Error('verifyOutliningSpans failed - span ' + (i + 1) + ' expected: (' + expectedSpan.start + ',' + expectedSpan.end + '), actual: (' + actualSpan.textSpan.start() + ',' + actualSpan.textSpan.end() + ')'); - } - } - } - - public verifyTodoComments(descriptors: string[], spans: TextSpan[]) { - var actual = this.languageService.getTodoComments(this.activeFile.fileName, - descriptors.map(d => new TypeScript.Services.TodoCommentDescriptor(d, 0))); - - if (actual.length !== spans.length) { - throw new Error('verifyTodoComments failed - expected total spans to be ' + spans.length + ', but was ' + actual.length); - } - - for (var i = 0; i < spans.length; i++) { - var expectedSpan = spans[i]; - var actualComment = actual[i]; - var actualCommentSpan = new TypeScript.TextSpan(actualComment.position, actualComment.message.length); - - if (expectedSpan.start !== actualCommentSpan.start() || expectedSpan.end !== actualCommentSpan.end()) { - throw new Error('verifyOutliningSpans failed - span ' + (i + 1) + ' expected: (' + expectedSpan.start + ',' + expectedSpan.end + '), actual: (' + actualCommentSpan.start() + ',' + actualCommentSpan.end() + ')'); + if (expectedSpan.start !== actualSpan.start() || expectedSpan.end !== actualSpan.end()) { + throw new Error('verifyOutliningSpans failed - span ' + (i + 1) + ' expected: (' + expectedSpan.start + ',' + expectedSpan.end + '), actual: (' + actualSpan.start() + ',' + actualSpan.end() + ')'); } } } @@ -1485,9 +1427,9 @@ module FourSlash { } var actualMatchPosition = -1; - if (bracePosition == actual[0].start()) { + if (bracePosition >= actual[0].start() && bracePosition <= actual[0].end()) { actualMatchPosition = actual[1].start(); - } else if (bracePosition == actual[1].start()) { + } else if (bracePosition >= actual[1].start() && bracePosition <= actual[1].end()) { actualMatchPosition = actual[0].start(); } else { throw new Error('verifyMatchingBracePosition failed - could not find the brace position: ' + bracePosition + ' in the returned list: (' + actual[0].start() + ',' + actual[0].end() + ') and (' + actual[1].start() + ',' + actual[1].end() + ')'); @@ -1512,7 +1454,7 @@ module FourSlash { this.taoInvalidReason = 'verifyTypesAgainstFullCheckAtPositions impossible'; // Create a from-scratch LS to check against - var referenceLanguageServiceShimHost = new Harness.TypeScriptLS(); + var referenceLanguageServiceShimHost = new Harness.LanguageService.TypeScriptLS(); var referenceLanguageServiceShim = referenceLanguageServiceShimHost.getLanguageService(); var referenceLanguageService = referenceLanguageServiceShim.languageService; @@ -1528,7 +1470,7 @@ module FourSlash { } for (i = 0; i < positions.length; i++) { - var nameOf = (type: TypeScript.Services.TypeInfo) => type ? type.fullSymbolName : '(none)'; + var nameOf = (type: ts.TypeInfo) => type ? type.fullSymbolName : '(none)'; var pullName: string, refName: string; var anyFailed = false; @@ -1571,14 +1513,16 @@ module FourSlash { } } - /// Check number of navigationItems which match both searchValue and matchKind. - /// Report an error if expected value and actual value do not match. + /* + Check number of navigationItems which match both searchValue and matchKind. + Report an error if expected value and actual value do not match. + */ public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string) { this.taoInvalidReason = 'verifyNavigationItemsCount NYI'; var items = this.languageService.getNavigateToItems(searchValue); var actual = 0; - var item: TypeScript.Services.NavigateToItem = null; + var item: ts.NavigateToItem = null; // Count only the match that match the same MatchKind for (var i = 0; i < items.length; ++i) { @@ -1593,8 +1537,10 @@ module FourSlash { } } - /// Verify that returned navigationItems from getNavigateToItems have matched searchValue, matchKind, and kind. - /// Report an error if getNavigateToItems does not find any matched searchValue. + /* + Verify that returned navigationItems from getNavigateToItems have matched searchValue, matchKind, and kind. + Report an error if getNavigateToItems does not find any matched searchValue. + */ public verifyNavigationItemsListContains( name: string, kind: string, @@ -1630,61 +1576,70 @@ module FourSlash { public verifyGetScriptLexicalStructureListCount(expected: number) { this.taoInvalidReason = 'verifyNavigationItemsListContains impossible'; - var items = this.languageService.getNavigationBarItems(this.activeFile.fileName); - var actual = this.getNavigationBarItemsCount(items); - + var items = this.languageService.getScriptLexicalStructure(this.activeFile.fileName); + var actual = (items && items.length) || 0; if (expected != actual) { throw new Error('verifyGetScriptLexicalStructureListCount failed - found: ' + actual + ' navigation items, expected: ' + expected + '.'); } } - private getNavigationBarItemsCount(items: TypeScript.Services.NavigationBarItem[]) { - var result = 0; - if (items) { - for (var i = 0, n = items.length; i < n; i++) { - result++; - result += this.getNavigationBarItemsCount(items[i].childItems); - } - } - - return result; - } - public verifGetScriptLexicalStructureListContains( name: string, kind: string, + fileName?: string, + parentName?: string, + isAdditionalSpan?: boolean, markerPosition?: number) { this.taoInvalidReason = 'verifGetScriptLexicalStructureListContains impossible'; - var items = this.languageService.getNavigationBarItems(this.activeFile.fileName); + var items = this.languageService.getScriptLexicalStructure(this.activeFile.fileName); if (!items || items.length === 0) { throw new Error('verifyGetScriptLexicalStructureListContains failed - found 0 navigation items, expected at least one.'); } - if (this.navigationBarItemsContains(items, name, kind)) { - return; - } - - var missingItem = { name: name, kind: kind }; - throw new Error('verifyGetScriptLexicalStructureListContains failed - could not find the item: ' + JSON.stringify(missingItem) + ' in the returned list: (' + JSON.stringify(items) + ')'); - } - - private navigationBarItemsContains(items: TypeScript.Services.NavigationBarItem[], name: string, kind: string) { - if (items) { - for (var i = 0; i < items.length; i++) { - var item = items[i]; - if (item && item.text === name && item.kind === kind) { - return true; + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (item && item.name === name && item.kind === kind && + (fileName === undefined || item.fileName === fileName) && + (parentName === undefined || item.containerName === parentName)) { + if (markerPosition !== undefined || isAdditionalSpan !== undefined) { + if (isAdditionalSpan) { + if (item.additionalSpans && + item.additionalSpans.some(span => span.minChar <= markerPosition && markerPosition <= span.limChar)) { + // marker is in an additional span for this item. + return; + } + else { + throw new Error( + 'verifGetScriptLexicalStructureListContains failed - ' + + 'no additional span was found that contained the position: ' + JSON.stringify(markerPosition) + + ' in the item: ' + JSON.stringify(item)); + } + } + else if (!isAdditionalSpan) { + if (item.minChar <= markerPosition && + markerPosition <= item.minChar) { + // marker is in span normal item's span + return; + } + else { + throw new Error( + 'verifGetScriptLexicalStructureListContains failed - ' + + 'marker was positioned: ' + JSON.stringify(markerPosition) + + ' which is not in the item: ' + JSON.stringify(item)); + } + } } - - if (this.navigationBarItemsContains(item.childItems, name, kind)) { - return true; + else { + return; } } } - return false; + + var missingItem = { name: name, kind: kind, fileName: fileName, parentName: parentName }; + throw new Error('verifyGetScriptLexicalStructureListContains failed - could not find the item: ' + JSON.stringify(missingItem) + ' in the returned list: (' + JSON.stringify(items) + ')'); } public printNavigationItems(searchValue: string) { @@ -1700,14 +1655,14 @@ module FourSlash { } public printScriptLexicalStructureItems() { - var items = this.languageService.getNavigationBarItems(this.activeFile.fileName); + var items = this.languageService.getScriptLexicalStructure(this.activeFile.fileName); var length = items && items.length; Harness.IO.log('NavigationItems list (' + length + ' items)'); for (var i = 0; i < length; i++) { var item = items[i]; - Harness.IO.log('name: ' + item.text + ', kind: ' + item.kind); + Harness.IO.log('name: ' + item.name + ', kind: ' + item.kind + ', parentName: ' + item.containerName + ', fileName: ' + item.fileName); } } @@ -1726,7 +1681,7 @@ module FourSlash { for (var i = 0; i < occurances.length; i++) { var occurance = occurances[i]; - if (occurance && occurance.fileName === fileName && occurance.textSpan.start() === start && occurance.textSpan.end() === end) { + if (occurance && occurance.fileName === fileName && occurance.minChar === start && occurance.limChar === end) { if (typeof isWriteAccess !== "undefined" && occurance.isWriteAccess !== isWriteAccess) { throw new Error('verifyOccurancesAtPositionListContains failed - item isWriteAccess value doe not match, actual: ' + occurance.isWriteAccess + ', expected: ' + isWriteAccess + '.'); } @@ -1798,7 +1753,7 @@ module FourSlash { return result; } - private assertItemInCompletionList(items: TypeScript.Services.CompletionEntry[], name: string, type?: string, docComment?: string, fullSymbolName?: string, kind?: string) { + private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, type?: string, docComment?: string, fullSymbolName?: string, kind?: string) { this.scenarioActions.push(''); this.scenarioActions.push(''); @@ -1940,7 +1895,6 @@ module FourSlash { { unitName: fileName, content: Harness.IO.readFile(fileName) } ]; harnessCompiler.addInputFiles(filesToAdd); - harnessCompiler.compile(); var emitterIOHost: Harness.Compiler.IEmitterIOHost = { writeFile: (path: string, contents: string, writeByteOrderMark: boolean) => fsOutput.Write(contents), diff --git a/src/harness/fourslashRun.ts b/src/harness/fourslashRun.ts deleted file mode 100644 index 28f0b2dd637..00000000000 --- a/src/harness/fourslashRun.ts +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -/// - -var testList: string[] = []; - -if (IO.arguments.length === 0) { - IO.dir(Harness.userSpecifiedroot + 'tests/ls/fourslash', /\.ts$/).forEach(fn => { - if (!fn.match(/fourslash.ts$/i)) { - testList.push(fn); - } - }); -} else { - IO.arguments.forEach(tests => tests.split(',').forEach(test => { - testList.push(test); - })); -} - -var passCount = 0, failCount = 0; -testList.forEach(test => { - try { - IO.print('Running ' + test.substr(IO.dirName(test).length + 1) + '... '); - FourSlash.runFourSlashTest(test); - IO.printLine('passed.'); - passCount++; - } catch (e) { - IO.printLine(e); - if (e.stack) { - IO.printLine(e.stack); - } - failCount++; - } -}); - -IO.printLine(passCount + ' passed, ' + failCount + ' failed.'); \ No newline at end of file diff --git a/src/harness/harness.ts b/src/harness/harness.ts index c4032c2d600..f90d096f536 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -429,25 +429,21 @@ module Harness { module Harness { - var typescriptServiceFileName = "typescriptServices.js"; - // Services files are exported because we need to eval them at global scope in order for them to be available everywhere - export var typescriptServiceFile: string; - - var tcServicesFilename = "services.js"; + var tcServicesFilename = "typescriptServices.js"; export var libFolder: string; switch (Utils.getExecutionEnvironment()) { case Utils.ExecutionEnvironment.CScript: libFolder = Path.filePath(global['WScript'].ScriptFullName); - tcServicesFilename = "built/local/services.js"; + tcServicesFilename = "built/local/typescriptServices.js"; break; case Utils.ExecutionEnvironment.Node: libFolder = (__dirname + '/'); - tcServicesFilename = "built/local/services.js"; + tcServicesFilename = "built/local/typescriptServices.js"; break; case Utils.ExecutionEnvironment.Browser: libFolder = "bin/"; - tcServicesFilename = "built/local/services.js"; + tcServicesFilename = "built/local/typescriptServices.js"; break; default: throw new Error('Unknown context'); @@ -557,7 +553,8 @@ module Harness { getDefaultLibFilename: () => 'lib.d.ts', writeFile: writeFile, getCanonicalFileName: ts.getCanonicalFileName, - useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames + useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, + getNewLine: ()=> sys.newLine } } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index c9075949dd6..e3646b98e03 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -1,10 +1,13 @@ -module Harness.LanguageService { +/// +/// + +module Harness.LanguageService { export class ScriptInfo { public version: number = 1; public editRanges: { length: number; textChangeRange: TypeScript.TextChangeRange; }[] = []; public lineMap: TypeScript.LineMap = null; - constructor(public fileName: string, public content: string, public isOpen = true, public byteOrderMark: TypeScript.ByteOrderMark = TypeScript.ByteOrderMark.None) { + constructor(public fileName: string, public content: string, public isOpen = true, public byteOrderMark: ts.ByteOrderMark = ts.ByteOrderMark.None) { this.setContent(content); } @@ -51,7 +54,7 @@ } } - class ScriptSnapshotShim implements TypeScript.Services.IScriptSnapshotShim { + class ScriptSnapshotShim implements ts.ScriptSnapshotShim { private lineMap: TypeScript.LineMap = null; private textSnapshot: string; private version: number; @@ -77,9 +80,8 @@ return JSON.stringify(this.lineMap.lineStarts()); } - public getChangeRange(oldScript: TypeScript.Services.IScriptSnapshotShim): string { - var oldShim = oldScript; - var range = this.scriptInfo.getTextChangeRangeBetweenVersions(oldShim.version, this.version); + public getTextChangeRangeSinceVersion(scriptVersion: number): string { + var range = this.scriptInfo.getTextChangeRangeBetweenVersions(scriptVersion, this.version); if (range === null) { return null; } @@ -88,14 +90,91 @@ } } - export class TypeScriptLS implements TypeScript.Services.ILanguageServiceShimHost { - IO = TypeScript.Environment ? TypeScript.Environment : Network.getEnvironment(); - private ls: TypeScript.Services.ILanguageServiceShim = null; + class CancellationToken { + public static None: CancellationToken = new CancellationToken(null) + + constructor(private cancellationToken: ts.CancellationToken) { + } + + public isCancellationRequested() { + return this.cancellationToken && this.cancellationToken.isCancellationRequested(); + } + } + + class ScriptSnapshotShimAdapter implements TypeScript.IScriptSnapshot { + private lineStartPositions: number[] = null; + constructor(private scriptSnapshotShim: ts.ScriptSnapshotShim) {} + getText(start: number, end: number): string {return this.scriptSnapshotShim.getText(start, end);} + getLength(): number {return this.scriptSnapshotShim.getLength();} + getLineStartPositions(): number[] { return JSON.parse(this.scriptSnapshotShim.getLineStartPositions()); } + getTextChangeRangeSinceVersion(scriptVersion: number): TypeScript.TextChangeRange { + var encoded = this.scriptSnapshotShim.getTextChangeRangeSinceVersion(scriptVersion); + if (encoded == null) { + return null; + } + + var decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded); + return new TypeScript.TextChangeRange( + new TypeScript.TextSpan(decoded.span.start, decoded.span.length), decoded.newLength); + } + } + + class LanguageServiceShimHostAdapter implements ts.LanguageServiceHost { + constructor(private shimHost: ts.LanguageServiceShimHost) { } + information(): boolean { return this.shimHost.information(); } + debug(): boolean { return this.shimHost.debug(); } + warning(): boolean { return this.shimHost.warning();} + error(): boolean { return this.shimHost.error(); } + fatal(): boolean { return this.shimHost.fatal(); } + log(s: string): void { this.shimHost.log(s); } + getCompilationSettings(): ts.CompilerOptions { return JSON.parse(this.shimHost.getCompilationSettings()); } + getScriptFileNames(): string[] { return JSON.parse(this.shimHost.getScriptFileNames());} + getScriptSnapshot(fileName: string): TypeScript.IScriptSnapshot { return new ScriptSnapshotShimAdapter(this.shimHost.getScriptSnapshot(fileName));} + getScriptVersion(fileName: string): number { return this.shimHost.getScriptVersion(fileName);} + getScriptIsOpen(fileName: string): boolean { return this.shimHost.getScriptIsOpen(fileName); } + getScriptByteOrderMark(fileName: string): ts.ByteOrderMark { return this.shimHost.getScriptByteOrderMark(fileName);} + getLocalizedDiagnosticMessages(): any { JSON.parse(this.shimHost.getLocalizedDiagnosticMessages());} + getCancellationToken(): ts.CancellationToken { return this.shimHost.getCancellationToken(); } + } + + export class NonCachingDocumentRegistry implements ts.DocumentRegistry { + + public static Instance: ts.DocumentRegistry = new NonCachingDocumentRegistry(); + + public acquireDocument( + fileName: string, + compilationSettings: ts.CompilerOptions, + scriptSnapshot: TypeScript.IScriptSnapshot, + byteOrderMark: ts.ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]= []): ts.Document { + return ts.createDocument(compilationSettings, fileName, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles); + } + + public updateDocument( + document: ts.Document, + fileName: string, + compilationSettings: ts.CompilerOptions, + scriptSnapshot: TypeScript.IScriptSnapshot, + version: number, + isOpen: boolean, + textChangeRange: TypeScript.TextChangeRange + ): ts.Document { + return document.update(scriptSnapshot, version, isOpen, textChangeRange); + } + + public releaseDocument(fileName: string, compilationSettings: ts.CompilerOptions): void { + // no op since this class doesn't cache anything + } + } + export class TypeScriptLS implements ts.LanguageServiceShimHost { + private ls: ts.LanguageServiceShim = null; public newLS: ts.LanguageService; - private fileNameToScript = new TypeScript.StringHashTable(); + private fileNameToScript: ts.Map = {}; - constructor(private cancellationToken: TypeScript.ICancellationToken = TypeScript.CancellationToken.None) { + constructor(private cancellationToken: ts.CancellationToken = CancellationToken.None) { } public addDefaultLibrary() { @@ -107,17 +186,16 @@ } public addFile(fileName: string) { - var code = Harness.Environment.readFile(fileName); + var code = Harness.IO.readFile(fileName); this.addScript(fileName, code); } private getScriptInfo(fileName: string): ScriptInfo { - return this.fileNameToScript.lookup(fileName); + return this.fileNameToScript[fileName]; } public addScript(fileName: string, content: string) { - var script = new ScriptInfo(fileName, content); - this.fileNameToScript.add(fileName, script); + this.fileNameToScript[fileName] = new ScriptInfo(fileName, content); } public updateScript(fileName: string, content: string) { @@ -155,91 +233,64 @@ } ////////////////////////////////////////////////////////////////////// - // ILanguageServiceShimHost implementation + // LanguageServiceShimHost implementation // /// Returns json for Tools.CompilationSettings public getCompilationSettings(): string { - return ""; // i.e. default settings + return JSON.stringify({}); // i.e. default settings } - public getCancellationToken(): TypeScript.ICancellationToken { + public getCancellationToken(): ts.CancellationToken { return this.cancellationToken; } public getScriptFileNames(): string { - return JSON.stringify(this.fileNameToScript.getAllKeys()); + var fileNames: string[] = []; + ts.forEachKey(this.fileNameToScript, (fileName) => { fileNames.push(fileName); }); + return JSON.stringify(fileNames); } - public getScriptSnapshot(fileName: string): TypeScript.Services.IScriptSnapshotShim { + public getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim { return new ScriptSnapshotShim(this.getScriptInfo(fileName)); } - public getScriptVersion(fileName: string): string { - return this.getScriptInfo(fileName).version.toString(); + public getScriptVersion(fileName: string): number { + return this.getScriptInfo(fileName).version; } public getScriptIsOpen(fileName: string): boolean { return this.getScriptInfo(fileName).isOpen; } - public getScriptByteOrderMark(fileName: string): TypeScript.ByteOrderMark { + public getScriptByteOrderMark(fileName: string): ts.ByteOrderMark { return this.getScriptInfo(fileName).byteOrderMark; } - public getDiagnosticsObject(): TypeScript.Services.ILanguageServicesDiagnostics { - return new LanguageServicesDiagnostics(""); - } - public getLocalizedDiagnosticMessages(): string { - return ""; - } - - public fileExists(s: string) { - return this.IO.fileExists(s); - } - - public directoryExists(s: string) { - return this.IO.directoryExists(s); - } - - public resolveRelativePath(path: string, directory: string): string { - if (TypeScript.isRooted(path) || !directory) { - return this.IO.absolutePath(path); - } - else { - return this.IO.absolutePath(TypeScript.IOUtils.combine(directory, path)); - } - } - - public getParentDirectory(path: string): string { - return this.IO.directoryName(path); + return JSON.stringify({}); } /** Return a new instance of the language service shim, up-to-date wrt to typecheck. * To access the non-shim (i.e. actual) language service, use the "ls.languageService" property. */ - public getLanguageService(): TypeScript.Services.ILanguageServiceShim { + public getLanguageService(): ts.LanguageServiceShim { var ls = new TypeScript.Services.TypeScriptServicesFactory().createLanguageServiceShim(this); this.ls = ls; - var hostAdapter = new ts.LanguageServiceShimHostAdapter(this); - this.newLS = ts.createLanguageService(hostAdapter); + var hostAdapter = new LanguageServiceShimHostAdapter(this); + + this.newLS = ts.createLanguageService(hostAdapter, NonCachingDocumentRegistry.Instance); return ls; } /** Parse file given its source text */ public parseSourceText(fileName: string, sourceText: TypeScript.IScriptSnapshot): TypeScript.SourceUnitSyntax { - var compilationSettings = new TypeScript.CompilationSettings(); - compilationSettings.codeGenTarget = TypeScript.LanguageVersion.EcmaScript5; - - var settings = TypeScript.ImmutableCompilationSettings.fromCompilationSettings(compilationSettings); - var parseOptions = settings.codeGenTarget(); - return TypeScript.Parser.parse(fileName, TypeScript.SimpleText.fromScriptSnapshot(sourceText), parseOptions, TypeScript.isDTSFile(fileName)).sourceUnit(); + return TypeScript.Parser.parse(fileName, TypeScript.SimpleText.fromScriptSnapshot(sourceText), ts.ScriptTarget.ES5, TypeScript.isDTSFile(fileName)).sourceUnit(); } /** Parse a file on disk given its fileName */ public parseFile(fileName: string) { - var sourceText = TypeScript.ScriptSnapshot.fromString(this.IO.readFile(fileName, /*codepage:*/ null).contents) + var sourceText = TypeScript.ScriptSnapshot.fromString(Harness.IO.readFile(fileName)) return this.parseSourceText(fileName, sourceText); } @@ -248,7 +299,7 @@ * @param col 1 based index */ public lineColToPosition(fileName: string, line: number, col: number): number { - var script: ScriptInfo = this.fileNameToScript.lookup(fileName); + var script: ScriptInfo = this.fileNameToScript[fileName]; assert.isNotNull(script); assert.isTrue(line >= 1); assert.isTrue(col >= 1); @@ -261,7 +312,7 @@ * @param col 0 based index */ public positionToZeroBasedLineCol(fileName: string, position: number): TypeScript.ILineAndCharacter { - var script: ScriptInfo = this.fileNameToScript.lookup(fileName); + var script: ScriptInfo = this.fileNameToScript[fileName]; assert.isNotNull(script); var result = script.lineMap.getLineAndCharacterFromPosition(position); @@ -272,10 +323,10 @@ } /** Verify that applying edits to sourceFileName result in the content of the file baselineFileName */ - public checkEdits(sourceFileName: string, baselineFileName: string, edits: TypeScript.Services.TextChange[]) { - var script = Utils.readFile(sourceFileName); - var formattedScript = this.applyEdits(script.contents, edits); - var baseline = Utils.readFile(baselineFileName).contents; + public checkEdits(sourceFileName: string, baselineFileName: string, edits: ts.TextEdit[]) { + var script = Harness.IO.readFile(sourceFileName); + var formattedScript = this.applyEdits(script, edits); + var baseline = Harness.IO.readFile(baselineFileName); function noDiff(text1: string, text2: string) { text1 = text1.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n"); @@ -301,26 +352,26 @@ /** Apply an array of text edits to a string, and return the resulting string. */ - public applyEdits(content: string, edits: TypeScript.Services.TextChange[]): string { + public applyEdits(content: string, edits: ts.TextEdit[]): string { var result = content; edits = this.normalizeEdits(edits); for (var i = edits.length - 1; i >= 0; i--) { var edit = edits[i]; - var prefix = result.substring(0, edit.span.start()); - var middle = edit.newText; - var suffix = result.substring(edit.span.end()); + var prefix = result.substring(0, edit.minChar); + var middle = edit.text; + var suffix = result.substring(edit.limChar); result = prefix + middle + suffix; } return result; } /** Normalize an array of edits by removing overlapping entries and sorting entries on the minChar position. */ - private normalizeEdits(edits: TypeScript.Services.TextChange[]): TypeScript.Services.TextChange[] { - var result: TypeScript.Services.TextChange[] = []; + private normalizeEdits(edits: ts.TextEdit[]): ts.TextEdit[] { + var result: ts.TextEdit[] = []; - function mapEdits(edits: TypeScript.Services.TextChange[]): { edit: TypeScript.Services.TextChange; index: number; }[] { - var result: { edit: TypeScript.Services.TextChange; index: number; }[] = []; + function mapEdits(edits: ts.TextEdit[]): { edit: ts.TextEdit; index: number; }[] { + var result: { edit: ts.TextEdit; index: number; }[] = []; for (var i = 0; i < edits.length; i++) { result.push({ edit: edits[i], index: i }); } @@ -328,7 +379,7 @@ } var temp = mapEdits(edits).sort(function (a, b) { - var result = a.edit.span.start() - b.edit.span.start(); + var result = a.edit.minChar - b.edit.limChar; if (result === 0) result = a.index - b.index; return result; @@ -347,7 +398,7 @@ } var nextEdit = temp[next].edit; - var gap = nextEdit.span.start() - currentEdit.span.end(); + var gap = nextEdit.minChar - currentEdit.limChar; // non-overlapping edits if (gap >= 0) { @@ -359,7 +410,7 @@ // overlapping edits: for now, we only support ignoring an next edit // entirely contained in the current edit. - if (currentEdit.span.end() >= nextEdit.span.end()) { + if (currentEdit.minChar >= nextEdit.limChar) { next++; continue; } @@ -371,15 +422,5 @@ return result; } } - - export class LanguageServicesDiagnostics implements TypeScript.Services.ILanguageServicesDiagnostics { - - constructor(private destination: string) { } - - public log(content: string): void { - //Imitates the LanguageServicesDiagnostics object when not in Visual Studio - } - - } } \ No newline at end of file diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index f7d2ec22cd2..f3fc650e55c 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -223,7 +223,8 @@ class ProjectRunner extends RunnerBase { writeFile: writeFile, getCurrentDirectory: getCurrentDirectory, getCanonicalFileName: ts.getCanonicalFileName, - useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames + useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, + getNewLine:()=> sys.newLine }; } diff --git a/src/harness/runner.ts b/src/harness/runner.ts index 26b768729fc..9f1ae0e90a9 100644 --- a/src/harness/runner.ts +++ b/src/harness/runner.ts @@ -62,12 +62,10 @@ if (testConfigFile !== '') { runners.push(new ProjectRunner()); break; case 'fourslash': - // TODO: Re-enable Fourslash tests - // runners.push(new FourslashRunner()); + runners.push(new FourslashRunner()); break; case 'fourslash-generated': - // TODO: Re-enable Fourslash tests - // runners.push(new GeneratedFourslashRunner()); + runners.push(new GeneratedFourslashRunner()); break; case 'unittests': runners.push(new UnitTestRunner(UnittestTestType.Compiler)); @@ -99,9 +97,8 @@ if (runners.length === 0) { } //// language services - // TODO: Re-enable Fourslash runner - // runners.push(new FourslashRunner()); - // runners.push(new GeneratedFourslashRunner()); + runners.push(new FourslashRunner()); + //runners.push(new GeneratedFourslashRunner()); } sys.newLine = '\r\n'; diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index e4038eb76e6..576b06e3435 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -112,7 +112,8 @@ module RWC { getDefaultLibFilename: () => libPath, writeFile: (fn, contents) => emitterIOHost.writeFile(fn, contents, false), getCanonicalFileName: ts.getCanonicalFileName, - useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames + useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, + getNewLine: () => sys.newLine }; var resolvedProgram = ts.createProgram(opts.filenames, opts.options, host); diff --git a/src/services/braceMatcher.ts b/src/services/braceMatcher.ts new file mode 100644 index 00000000000..0cb336d4dc2 --- /dev/null +++ b/src/services/braceMatcher.ts @@ -0,0 +1,116 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services { + export class BraceMatcher { + + // Given a script name and position in the script, return a pair of text range if the + // position corresponds to a "brace matchin" characters (e.g. "{" or "(", etc.) + // If the position is not on any range, return an empty set. + public static getMatchSpans(syntaxTree: TypeScript.SyntaxTree, position: number): TypeScript.TextSpan[] { + var result: TypeScript.TextSpan[] = []; + + var currentToken = findToken(syntaxTree.sourceUnit(), position); + + BraceMatcher.getMatchingCloseBrace(currentToken, position, result); + BraceMatcher.getMatchingOpenBrace(currentToken, position, result); + + return result; + } + + private static getMatchingCloseBrace(currentToken: TypeScript.ISyntaxToken, position: number, result: TypeScript.TextSpan[]) { + if (start(currentToken) === position) { + var closingBraceKind = BraceMatcher.getMatchingCloseBraceTokenKind(currentToken); + if (closingBraceKind !== null) { + var parentElement = currentToken.parent + var currentPosition = fullStart(currentToken.parent); + for (var i = 0, n = childCount(parentElement); i < n; i++) { + var element = childAt(parentElement, i); + if (element !== null && fullWidth(element) > 0) { + if (element.kind() === closingBraceKind) { + var range1 = new TypeScript.TextSpan(position, width(currentToken)); + var range2 = new TypeScript.TextSpan(currentPosition + leadingTriviaWidth(element), width(element)); + result.push(range1, range2); + break; + } + + currentPosition += fullWidth(element); + } + } + } + } + } + + private static getMatchingOpenBrace(currentToken: TypeScript.ISyntaxToken, position: number, result: TypeScript.TextSpan[]) { + // Check if the current token to the left is a close brace + if (currentToken.fullStart() === position) { + currentToken = previousToken(currentToken); + } + + if (currentToken !== null && start(currentToken) === (position - 1)) { + var openBraceKind = BraceMatcher.getMatchingOpenBraceTokenKind(currentToken); + if (openBraceKind !== null) { + var parentElement = currentToken.parent; + var currentPosition = fullStart(currentToken.parent) + fullWidth(parentElement); + for (var i = childCount(parentElement) - 1 ; i >= 0; i--) { + var element = childAt(parentElement, i); + if (element !== null && fullWidth(element) > 0) { + if (element.kind() === openBraceKind) { + var range1 = new TypeScript.TextSpan(position - 1, width(currentToken)); + var range2 = new TypeScript.TextSpan(currentPosition - lastToken(element).trailingTriviaWidth() - width(element), width(element)); + result.push(range1, range2); + break; + } + + currentPosition -= fullWidth(element); + } + } + } + } + } + + private static getMatchingCloseBraceTokenKind(positionedElement: TypeScript.ISyntaxElement): TypeScript.SyntaxKind { + var element = positionedElement !== null && positionedElement; + switch (element.kind()) { + case TypeScript.SyntaxKind.OpenBraceToken: + return TypeScript.SyntaxKind.CloseBraceToken + case TypeScript.SyntaxKind.OpenParenToken: + return TypeScript.SyntaxKind.CloseParenToken; + case TypeScript.SyntaxKind.OpenBracketToken: + return TypeScript.SyntaxKind.CloseBracketToken; + case TypeScript.SyntaxKind.LessThanToken: + return TypeScript.SyntaxUtilities.isAngleBracket(positionedElement) ? TypeScript.SyntaxKind.GreaterThanToken : null; + } + return null; + } + + private static getMatchingOpenBraceTokenKind(positionedElement: TypeScript.ISyntaxElement): TypeScript.SyntaxKind { + var element = positionedElement !== null && positionedElement; + switch (element.kind()) { + case TypeScript.SyntaxKind.CloseBraceToken: + return TypeScript.SyntaxKind.OpenBraceToken + case TypeScript.SyntaxKind.CloseParenToken: + return TypeScript.SyntaxKind.OpenParenToken; + case TypeScript.SyntaxKind.CloseBracketToken: + return TypeScript.SyntaxKind.OpenBracketToken; + case TypeScript.SyntaxKind.GreaterThanToken: + return TypeScript.SyntaxUtilities.isAngleBracket(positionedElement) ? TypeScript.SyntaxKind.LessThanToken : null; + } + return null; + } + } +} diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts new file mode 100644 index 00000000000..5dd057ac2af --- /dev/null +++ b/src/services/breakpoints.ts @@ -0,0 +1,1097 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. +// See LICENSE.txt in the project root for complete license information. + +/// + +module TypeScript.Services.Breakpoints { + function createBreakpointSpanInfo(parentElement: TypeScript.ISyntaxElement, ...childElements: TypeScript.ISyntaxElement[]): ts.SpanInfo { + if (!parentElement) { + return null; + } + + if (childElements.length == 0) { + return { + minChar: TypeScript.start(parentElement), + limChar: TypeScript.end(parentElement) + }; + } + + var start: number; + var end: number; + for (var i = 0; i < childElements.length; i++) { + var element = childElements[i]; + if (element && !isShared(element)) { + if (start == undefined) { + start = TypeScript.start(element); + } + end = TypeScript.end(element); + } + } + + return { + minChar: start, + limChar: end + }; + } + + function createBreakpointSpanInfoWithLimChar(startElement: TypeScript.ISyntaxElement, limChar: number): ts.SpanInfo { + return { + minChar: start(startElement), + limChar: limChar + }; + } + + class BreakpointResolver { + constructor(private posLine: number, private lineMap: TypeScript.LineMap) { + } + + private breakpointSpanOfToken(positionedToken: TypeScript.ISyntaxToken): ts.SpanInfo { + switch (positionedToken.kind()) { + case TypeScript.SyntaxKind.OpenBraceToken: + return this.breakpointSpanOfOpenBrace(positionedToken); + + case TypeScript.SyntaxKind.CloseBraceToken: + return this.breakpointSpanOfCloseBrace(positionedToken); + + case TypeScript.SyntaxKind.CommaToken: + return this.breakpointSpanOfComma(positionedToken); + + case TypeScript.SyntaxKind.SemicolonToken: + case TypeScript.SyntaxKind.EndOfFileToken: + return this.breakpointSpanIfStartsOnSameLine(previousToken(positionedToken)); + + case TypeScript.SyntaxKind.CloseParenToken: + return this.breakpointSpanOfCloseParen(positionedToken); + + case TypeScript.SyntaxKind.DoKeyword: + var parentElement = positionedToken.parent; + if (parentElement && parentElement.kind() == TypeScript.SyntaxKind.DoStatement) { + return this.breakpointSpanIfStartsOnSameLine(nextToken(positionedToken)); + } + break; + } + + return this.breakpointSpanOfContainingNode(positionedToken); + } + + private breakpointSpanOfOpenBrace(openBraceToken: TypeScript.ISyntaxToken): ts.SpanInfo { + var container = Syntax.containingNode(openBraceToken); + if (container) { + var originalContainer = container; + if (container && container.kind() == TypeScript.SyntaxKind.Block) { + // We have to check the parent and decide what to do with the breakpoint + container = Syntax.containingNode(container); + if (!container) { + container = originalContainer; + } + } + + switch (container.kind()) { + case TypeScript.SyntaxKind.Block: + if (!this.canHaveBreakpointInBlock(container)) { + return null; + } + return this.breakpointSpanOfFirstStatementInBlock(container); + break; + + case TypeScript.SyntaxKind.ModuleDeclaration: + case TypeScript.SyntaxKind.ClassDeclaration: + case TypeScript.SyntaxKind.FunctionDeclaration: + case TypeScript.SyntaxKind.ConstructorDeclaration: + case TypeScript.SyntaxKind.MemberFunctionDeclaration: + case TypeScript.SyntaxKind.GetAccessor: + case TypeScript.SyntaxKind.SetAccessor: + case TypeScript.SyntaxKind.FunctionExpression: + case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: + case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: + if (!this.canHaveBreakpointInDeclaration(container)) { + return null; + } + if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { + return this.breakpointSpanOfFirstChildOfSyntaxList(this.getSyntaxListOfDeclarationWithElements(container)); + } + else { + return this.breakpointSpanOf(container); + } + + case TypeScript.SyntaxKind.EnumDeclaration: + if (!this.canHaveBreakpointInDeclaration(container)) { + return null; + } + if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { + return this.breakpointSpanOfFirstEnumElement(container); + } + else { + return this.breakpointSpanOf(container); + } + + case TypeScript.SyntaxKind.IfStatement: + case TypeScript.SyntaxKind.ForInStatement: + case TypeScript.SyntaxKind.WhileStatement: + case TypeScript.SyntaxKind.CatchClause: + if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { + return this.breakpointSpanOfFirstStatementInBlock(originalContainer); + } + else { + return this.breakpointSpanOf(container); + } + + case TypeScript.SyntaxKind.DoStatement: + return this.breakpointSpanOfFirstStatementInBlock(originalContainer); + + case TypeScript.SyntaxKind.ForStatement: + if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { + return this.breakpointSpanOfFirstStatementInBlock(originalContainer); + } + else { + return this.breakpointSpanOf(previousToken(openBraceToken)); + } + + case TypeScript.SyntaxKind.ElseClause: + case TypeScript.SyntaxKind.CaseSwitchClause: + case TypeScript.SyntaxKind.DefaultSwitchClause: + case TypeScript.SyntaxKind.WithStatement: + case TypeScript.SyntaxKind.TryStatement: + case TypeScript.SyntaxKind.FinallyClause: + return this.breakpointSpanOfFirstStatementInBlock(originalContainer); + + case TypeScript.SyntaxKind.SwitchStatement: + if (this.posLine != this.lineMap.getLineNumberFromPosition(start(container))) { + return this.breakpointSpanOfFirstStatementOfFirstCaseClause(container); + } + else { + return this.breakpointSpanOf(container); + } + } + } + + return null; + } + + private breakpointSpanOfCloseBrace(closeBraceToken: TypeScript.ISyntaxToken): ts.SpanInfo { + var container = Syntax.containingNode(closeBraceToken); + if (container) { + var originalContainer = container; + if (container.kind() == TypeScript.SyntaxKind.Block) { + // We have to check the parent and decide what to do with the breakpoint + container = Syntax.containingNode(container); + if (!container) { + container = originalContainer; + } + } + + switch (container.kind()) { + case TypeScript.SyntaxKind.Block: + if (!this.canHaveBreakpointInBlock(container)) { + return null; + } + return this.breakpointSpanOfLastStatementInBlock(container); + break; + + case TypeScript.SyntaxKind.ModuleDeclaration: + if (!this.canHaveBreakpointInDeclaration(container)) { + return null; + } + var moduleSyntax = container; + if (moduleSyntax.moduleElements && moduleSyntax.moduleElements.length > 0) { + return createBreakpointSpanInfo(closeBraceToken); + } + else { + return null; + } + + case TypeScript.SyntaxKind.ClassDeclaration: + case TypeScript.SyntaxKind.FunctionDeclaration: + case TypeScript.SyntaxKind.ConstructorDeclaration: + case TypeScript.SyntaxKind.MemberFunctionDeclaration: + case TypeScript.SyntaxKind.GetAccessor: + case TypeScript.SyntaxKind.SetAccessor: + case TypeScript.SyntaxKind.FunctionExpression: + if (!this.canHaveBreakpointInDeclaration(container)) { + return null; + } + return createBreakpointSpanInfo(closeBraceToken); + + case TypeScript.SyntaxKind.EnumDeclaration: + if (!this.canHaveBreakpointInDeclaration(container)) { + return null; + } + return createBreakpointSpanInfo(closeBraceToken); + + case TypeScript.SyntaxKind.IfStatement: + case TypeScript.SyntaxKind.ElseClause: + case TypeScript.SyntaxKind.ForInStatement: + case TypeScript.SyntaxKind.ForStatement: + case TypeScript.SyntaxKind.WhileStatement: + case TypeScript.SyntaxKind.DoStatement: + case TypeScript.SyntaxKind.CaseSwitchClause: + case TypeScript.SyntaxKind.DefaultSwitchClause: + case TypeScript.SyntaxKind.WithStatement: + case TypeScript.SyntaxKind.TryStatement: + case TypeScript.SyntaxKind.CatchClause: + case TypeScript.SyntaxKind.FinallyClause: + case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: + case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: + return this.breakpointSpanOfLastStatementInBlock(originalContainer); + + case TypeScript.SyntaxKind.SwitchStatement: + return this.breakpointSpanOfLastStatementOfLastCaseClause(container); + } + } + + return null; + } + + + private breakpointSpanOfComma(commaToken: TypeScript.ISyntaxToken): ts.SpanInfo { + var commaParent = commaToken.parent; + if (isSeparatedList(commaParent)) { + var grandParent = commaParent.parent; + if (grandParent) { + switch (grandParent.kind()) { + case TypeScript.SyntaxKind.VariableDeclaration: + case TypeScript.SyntaxKind.EnumDeclaration: + case TypeScript.SyntaxKind.ParameterList: + var index = Syntax.childIndex(commaParent, commaToken); + // Use the previous child + if (index > 0) { + var child = childAt(commaParent, index - 1); + return this.breakpointSpanOf(child); + } + + // If we cant set breakpoint on enum element, just dont set breakpoint + if (grandParent.kind() == TypeScript.SyntaxKind.EnumDeclaration) { + return null; + } + break; + } + } + } + + return this.breakpointSpanOfContainingNode(commaToken); + } + + private breakpointSpanOfCloseParen(closeParenToken: TypeScript.ISyntaxToken): ts.SpanInfo { + var closeParenParent = closeParenToken.parent; + if (closeParenParent) { + switch (closeParenParent.kind()) { + case TypeScript.SyntaxKind.ForStatement: + case TypeScript.SyntaxKind.ParameterList: + return this.breakpointSpanOf(previousToken(closeParenToken)); + } + } + + return this.breakpointSpanOfContainingNode(closeParenToken); + } + + private canHaveBreakpointInBlock(blockNode: TypeScript.ISyntaxNode) { + if (!blockNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(blockNode)) { + return false; + } + + var blockSyntax = blockNode; + return blockSyntax.statements && blockSyntax.statements.length != 0; + } + + private breakpointSpanOfFirstStatementInBlock(blockNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!blockNode) { + return null; + } + + var blockSyntax = blockNode; + var statementsNode = blockSyntax.statements; + if (!statementsNode || statementsNode.length == 0) { + return null; + } + + var firstStatement = childAt(statementsNode, 0); + if (firstStatement && firstStatement.kind() == TypeScript.SyntaxKind.Block) { + if (this.canHaveBreakpointInBlock(firstStatement)) { + return this.breakpointSpanOfFirstStatementInBlock(firstStatement); + } + return null; + } + else { + return this.breakpointSpanOf(firstStatement); + } + } + + private breakpointSpanOfLastStatementInBlock(blockNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!blockNode) { + return null; + } + + var blockSyntax = blockNode; + var statementsNode = blockSyntax.statements; + if (!statementsNode || statementsNode.length == 0) { + return null; + } + + var lastStatement = childAt(statementsNode, statementsNode.length - 1); + if (lastStatement && lastStatement.kind() == TypeScript.SyntaxKind.Block) { + if (this.canHaveBreakpointInBlock(lastStatement)) { + return this.breakpointSpanOfLastStatementInBlock(lastStatement); + } + return null; + } + else { + return this.breakpointSpanOf(lastStatement); + } + } + + private breakpointSpanOfFirstChildOfSyntaxList(positionedList: TypeScript.ISyntaxNodeOrToken[]): ts.SpanInfo { + if (!positionedList) { + return null; + } + + // Find the first syntax element + var listSyntax = positionedList; + if (listSyntax.length == 0) { + return null; + } + + var firstStatement = childAt(positionedList, 0); + if (firstStatement && firstStatement.kind() == TypeScript.SyntaxKind.Block) { + if (this.canHaveBreakpointInBlock(firstStatement)) { + return this.breakpointSpanOfFirstStatementInBlock(firstStatement); + } + + return null; + } + else { + return this.breakpointSpanOf(firstStatement); + } + } + + private breakpointSpanOfLastChildOfSyntaxList(positionedList: TypeScript.ISyntaxNodeOrToken[]): ts.SpanInfo { + if (!positionedList) { + return null; + } + + // Find the first syntax element + var listSyntax = positionedList; + if (listSyntax.length == 0) { + return null; + } + var lastStatement = childAt(positionedList, 0); + if (lastStatement && lastStatement.kind() == TypeScript.SyntaxKind.Block) { + if (this.canHaveBreakpointInBlock(lastStatement)) { + return this.breakpointSpanOfLastStatementInBlock(lastStatement); + } + return null; + } + else { + return this.breakpointSpanOf(lastStatement); + } + } + + private breakpointSpanOfNode(positionedNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var node = positionedNode; + switch (node.kind()) { + // Declarations with elements + case TypeScript.SyntaxKind.ModuleDeclaration: + case TypeScript.SyntaxKind.ClassDeclaration: + case TypeScript.SyntaxKind.FunctionDeclaration: + case TypeScript.SyntaxKind.ConstructorDeclaration: + case TypeScript.SyntaxKind.MemberFunctionDeclaration: + case TypeScript.SyntaxKind.GetAccessor: + case TypeScript.SyntaxKind.SetAccessor: + case TypeScript.SyntaxKind.FunctionExpression: + return this.breakpointSpanOfDeclarationWithElements(positionedNode); + + // Var, parameter and member variable declaration syntax + case TypeScript.SyntaxKind.VariableDeclarator: + return this.breakpointSpanOfVariableDeclarator(positionedNode); + + case TypeScript.SyntaxKind.VariableDeclaration: + return this.breakpointSpanOfVariableDeclaration(positionedNode); + + case TypeScript.SyntaxKind.VariableStatement: + return this.breakpointSpanOfVariableStatement(positionedNode); + + case TypeScript.SyntaxKind.Parameter: + return this.breakpointSpanOfParameter(positionedNode); + + case TypeScript.SyntaxKind.MemberVariableDeclaration: + return this.breakpointSpanOfMemberVariableDeclaration(positionedNode); + + case TypeScript.SyntaxKind.ImportDeclaration: + return this.breakpointSpanOfImportDeclaration(positionedNode); + + case TypeScript.SyntaxKind.EnumDeclaration: + return this.breakpointSpanOfEnumDeclaration(positionedNode); + + case TypeScript.SyntaxKind.EnumElement: + return this.breakpointSpanOfEnumElement(positionedNode); + + // Statements + case TypeScript.SyntaxKind.IfStatement: + return this.breakpointSpanOfIfStatement(positionedNode); + case TypeScript.SyntaxKind.ElseClause: + return this.breakpointSpanOfElseClause(positionedNode); + case TypeScript.SyntaxKind.ForInStatement: + return this.breakpointSpanOfForInStatement(positionedNode); + case TypeScript.SyntaxKind.ForStatement: + return this.breakpointSpanOfForStatement(positionedNode); + case TypeScript.SyntaxKind.WhileStatement: + return this.breakpointSpanOfWhileStatement(positionedNode); + case TypeScript.SyntaxKind.DoStatement: + return this.breakpointSpanOfDoStatement(positionedNode); + case TypeScript.SyntaxKind.SwitchStatement: + return this.breakpointSpanOfSwitchStatement(positionedNode); + case TypeScript.SyntaxKind.CaseSwitchClause: + return this.breakpointSpanOfCaseSwitchClause(positionedNode); + case TypeScript.SyntaxKind.DefaultSwitchClause: + return this.breakpointSpanOfDefaultSwitchClause(positionedNode); + case TypeScript.SyntaxKind.WithStatement: + return this.breakpointSpanOfWithStatement(positionedNode); + case TypeScript.SyntaxKind.TryStatement: + return this.breakpointSpanOfTryStatement(positionedNode); + case TypeScript.SyntaxKind.CatchClause: + return this.breakpointSpanOfCatchClause(positionedNode); + case TypeScript.SyntaxKind.FinallyClause: + return this.breakpointSpanOfFinallyClause(positionedNode); + + // Arrow expressions + case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: + return this.breakpointSpanOfParenthesizedArrowFunctionExpression(positionedNode); + + case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: + return this.breakpointSpanOfSimpleArrowFunctionExpression(positionedNode); + + // Expressions or statements + default: + if (SyntaxUtilities.isStatement(node)) { + return this.breakpointSpanOfStatement(positionedNode); + } + else { + return this.breakpointOfExpression(positionedNode); + } + } + } + + private isExpressionOfArrowExpressions(expression: ISyntaxElement): boolean { + if (!expression) { + return false; + } + + var expressionParent = expression.parent; + if (expressionParent) { + if (expressionParent.kind() == TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression) { + var parenthesizedArrowExpression = expressionParent; + var expressionOfParenthesizedArrowExpression = parenthesizedArrowExpression.expression; + return expressionOfParenthesizedArrowExpression == expression; + } + else if (expressionParent.kind() == TypeScript.SyntaxKind.SimpleArrowFunctionExpression) { + var simpleArrowExpression = expressionParent; + var expressionOfSimpleArrowExpression = simpleArrowExpression.expression; + return expressionOfSimpleArrowExpression == expression; + } + else if (expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { + return this.isExpressionOfArrowExpressions(expressionParent); + } + } + return false; + } + + private isInitializerOfForStatement(expressionNode: TypeScript.ISyntaxNode): boolean { + if (!expressionNode) { + return false; + } + + var expressionParent = expressionNode.parent; + if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.ForStatement) { + + var expression = expressionNode; + var forStatement = expressionParent; + var initializer = forStatement.initializer; + return initializer === expression; + } + else if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { + return this.isInitializerOfForStatement(expressionParent); + } + + return false; + } + + private isConditionOfForStatement(expressionNode: TypeScript.ISyntaxNode): boolean { + if (!expressionNode) { + return false; + } + + var expressionParent = expressionNode.parent; + if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.ForStatement) { + var expression = expressionNode; + var forStatement = expressionParent; + var condition = forStatement.condition; + return condition === expression; + } + else if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { + return this.isConditionOfForStatement(expressionParent); + } + + return false; + } + + private isIncrememtorOfForStatement(expressionNode: TypeScript.ISyntaxNode): boolean { + if (!expressionNode) { + return false; + } + + var expressionParent = expressionNode.parent; + if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.ForStatement) { + var expression = expressionNode; + var forStatement = expressionParent; + var incrementor = forStatement.incrementor; + return incrementor === expression; + } + else if (expressionParent && expressionParent.kind() == TypeScript.SyntaxKind.CommaExpression) { + return this.isIncrememtorOfForStatement(expressionParent); + } + + return false; + } + + private breakpointOfLeftOfCommaExpression(commaExpressionNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var commaExpression = commaExpressionNode; + return this.breakpointSpanOf(commaExpression.left); + } + + private breakpointOfExpression(expressionNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (this.isInitializerOfForStatement(expressionNode) || + this.isConditionOfForStatement(expressionNode) || + this.isIncrememtorOfForStatement(expressionNode)) { + if (expressionNode.kind() == TypeScript.SyntaxKind.CommaExpression) { + return this.breakpointOfLeftOfCommaExpression(expressionNode); + } + return createBreakpointSpanInfo(expressionNode); + } + + if (this.isExpressionOfArrowExpressions(expressionNode)) { + if (expressionNode.kind() == TypeScript.SyntaxKind.CommaExpression) { + return this.breakpointOfLeftOfCommaExpression(expressionNode); + } + return createBreakpointSpanInfo(expressionNode); + } + + if (expressionNode.kind() == TypeScript.SyntaxKind.ExportAssignment) { + var exportAssignmentSyntax = expressionNode; + return createBreakpointSpanInfo(expressionNode, exportAssignmentSyntax.exportKeyword, exportAssignmentSyntax.equalsToken, exportAssignmentSyntax.identifier); + } + + return this.breakpointSpanOfContainingNode(expressionNode); + } + + private breakpointSpanOfStatement(statementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var statement = statementNode; + if (statement.kind() == TypeScript.SyntaxKind.EmptyStatement) { + return null; + } + + var containingNode = Syntax.containingNode(statementNode); + if (SyntaxUtilities.isStatement(containingNode)) { + // Check if not the declarations and the compound statements + var useNodeForBreakpoint = false; + switch (containingNode.kind()) { + // Declarations + case TypeScript.SyntaxKind.ModuleDeclaration: + case TypeScript.SyntaxKind.ClassDeclaration: + case TypeScript.SyntaxKind.FunctionDeclaration: + case TypeScript.SyntaxKind.ConstructorDeclaration: + case TypeScript.SyntaxKind.MemberFunctionDeclaration: + case TypeScript.SyntaxKind.GetAccessor: + case TypeScript.SyntaxKind.SetAccessor: + case TypeScript.SyntaxKind.Block: + + // Compound Statements + case TypeScript.SyntaxKind.IfStatement: + case TypeScript.SyntaxKind.ElseClause: + case TypeScript.SyntaxKind.ForInStatement: + case TypeScript.SyntaxKind.ForStatement: + case TypeScript.SyntaxKind.WhileStatement: + case TypeScript.SyntaxKind.DoStatement: + case TypeScript.SyntaxKind.SwitchStatement: + case TypeScript.SyntaxKind.CaseSwitchClause: + case TypeScript.SyntaxKind.DefaultSwitchClause: + case TypeScript.SyntaxKind.WithStatement: + case TypeScript.SyntaxKind.TryStatement: + case TypeScript.SyntaxKind.CatchClause: + case TypeScript.SyntaxKind.FinallyClause: + case TypeScript.SyntaxKind.Block: + useNodeForBreakpoint = true; + } + + if (!useNodeForBreakpoint) { + return this.breakpointSpanOfContainingNode(statementNode); + } + } + + switch (statement.kind()) { + case TypeScript.SyntaxKind.ExpressionStatement: + var expressionSyntax = statement; + return createBreakpointSpanInfo(expressionSyntax.expression); + + case TypeScript.SyntaxKind.ReturnStatement: + var returnStatementSyntax = statement; + return createBreakpointSpanInfo(statementNode, returnStatementSyntax.returnKeyword, returnStatementSyntax.expression); + + case TypeScript.SyntaxKind.ThrowStatement: + var throwStatementSyntax = statement; + return createBreakpointSpanInfo(statementNode, throwStatementSyntax.throwKeyword, throwStatementSyntax.expression); + + case TypeScript.SyntaxKind.BreakStatement: + var breakStatementSyntax = statement; + return createBreakpointSpanInfo(statementNode, breakStatementSyntax.breakKeyword, breakStatementSyntax.identifier); + + case TypeScript.SyntaxKind.ContinueStatement: + var continueStatementSyntax = statement; + return createBreakpointSpanInfo(statementNode, continueStatementSyntax.continueKeyword, continueStatementSyntax.identifier); + + case TypeScript.SyntaxKind.DebuggerStatement: + var debuggerStatementSyntax = statement; + return createBreakpointSpanInfo(debuggerStatementSyntax.debuggerKeyword); + + case TypeScript.SyntaxKind.LabeledStatement: + var labeledStatementSyntax = statement; + return this.breakpointSpanOf(labeledStatementSyntax.statement); + } + + return null; + } + + private getSyntaxListOfDeclarationWithElements(positionedNode: TypeScript.ISyntaxNode) { + var node = positionedNode; + var elementsList: TypeScript.ISyntaxNodeOrToken[]; + var block: TypeScript.BlockSyntax; + + switch (node.kind()) { + case TypeScript.SyntaxKind.ModuleDeclaration: + elementsList = (node).moduleElements; + break; + + case TypeScript.SyntaxKind.ClassDeclaration: + elementsList = (node).classElements; + break; + + case TypeScript.SyntaxKind.FunctionDeclaration: + block = (node).block; + break; + + case TypeScript.SyntaxKind.ConstructorDeclaration: + block = (node).block; + break; + + case TypeScript.SyntaxKind.MemberFunctionDeclaration: + block = (node).block; + break; + + case TypeScript.SyntaxKind.GetAccessor: + block = (node).block; + break; + + case TypeScript.SyntaxKind.SetAccessor: + block = (node).block; + break; + + case TypeScript.SyntaxKind.FunctionExpression: + block = (node).block; + break; + + case TypeScript.SyntaxKind.ParenthesizedArrowFunctionExpression: + block = (node).block; + break; + + case TypeScript.SyntaxKind.SimpleArrowFunctionExpression: + block = (node).block; + break; + + default: + throw TypeScript.Errors.argument('positionNode', 'unknown node kind in getSyntaxListOfDeclarationWithElements'); + } + + var parentElement: TypeScript.ISyntaxElement = positionedNode; + if (block) { + parentElement = block; + elementsList = block.statements; + } + + return elementsList; + } + + private canHaveBreakpointInDeclaration(positionedNode: TypeScript.ISyntaxNode) { + return positionedNode && !TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(positionedNode); + } + + private breakpointSpanOfDeclarationWithElements(positionedNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!this.canHaveBreakpointInDeclaration(positionedNode)) { + return null; + } + + // If inside another module the whole declaration is debuggable + var node = positionedNode; + var moduleSyntax = positionedNode; + if ((SyntaxUtilities.isModuleElement(node) && Syntax.containingNode(positionedNode).kind() != TypeScript.SyntaxKind.SourceUnit) || + SyntaxUtilities.isClassElement(node) || + (moduleSyntax.kind() == TypeScript.SyntaxKind.ModuleDeclaration && moduleSyntax.name + && moduleSyntax.name.kind() == TypeScript.SyntaxKind.QualifiedName)) { + return createBreakpointSpanInfo(positionedNode); + } + else { + // Try to get the breakpoint in first element declaration + return this.breakpointSpanOfFirstChildOfSyntaxList(this.getSyntaxListOfDeclarationWithElements(positionedNode)); + } + } + + private canHaveBreakpointInVariableDeclarator(varDeclaratorNode: TypeScript.ISyntaxNode) { + if (!varDeclaratorNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(varDeclaratorNode)) { + return false; + } + + var varDeclaratorSyntax = varDeclaratorNode; + return !!varDeclaratorSyntax.equalsValueClause; + } + + private breakpointSpanOfVariableDeclarator(varDeclaratorNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!this.canHaveBreakpointInVariableDeclarator(varDeclaratorNode)) { + return null; + } + + var container = Syntax.containingNode(varDeclaratorNode); + if (container && container.kind() == TypeScript.SyntaxKind.VariableDeclaration) { + var parentDeclaratorsList = varDeclaratorNode.parent; + // If this is the first declarator in the list use the declaration instead + if (parentDeclaratorsList && childAt(parentDeclaratorsList, 0) == varDeclaratorNode) { + return this.breakpointSpanOfVariableDeclaration(container); + } + + // Create breakpoint on this var declarator + if (this.canHaveBreakpointInVariableDeclarator(varDeclaratorNode)) { + return createBreakpointSpanInfo(varDeclaratorNode); + } + else { + return null; + } + } + else if (container) { + // Member Variable syntax + return this.breakpointSpanOfMemberVariableDeclaration(container); + } + + return null; + } + + private canHaveBreakpointInVariableDeclaration(varDeclarationNode: TypeScript.ISyntaxNode) { + if (!varDeclarationNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(varDeclarationNode)) { + return false; + } + + var varDeclarationSyntax = varDeclarationNode; + var containerChildren = varDeclarationSyntax.variableDeclarators; + if (!containerChildren || childCount(containerChildren) == 0) { + return false; + } + + var child = childAt(containerChildren, 0); + if (isNode(child)) { + return this.canHaveBreakpointInVariableDeclarator(child); + } + + return false; + } + + private breakpointSpanOfVariableDeclaration(varDeclarationNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!this.canHaveBreakpointInDeclaration(varDeclarationNode)) { + return null; + } + + var container = Syntax.containingNode(varDeclarationNode); + var varDeclarationSyntax = varDeclarationNode; + var varDeclarators = varDeclarationSyntax.variableDeclarators; + var varDeclaratorsCount = childCount(varDeclarators); // varDeclarators has to be non null because its checked in canHaveBreakpoint + + if (container && container.kind() == TypeScript.SyntaxKind.VariableStatement) { + return this.breakpointSpanOfVariableStatement(container); + } + + if (this.canHaveBreakpointInVariableDeclaration(varDeclarationNode)) { + return createBreakpointSpanInfoWithLimChar(varDeclarationNode, end(childAt(varDeclarators, 0))); + } + else { + return null; + } + } + + private canHaveBreakpointInVariableStatement(varStatementNode: TypeScript.ISyntaxNode) { + if (!varStatementNode || TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(varStatementNode)) { + return false; + } + + var variableStatement = varStatementNode; + return this.canHaveBreakpointInVariableDeclaration(variableStatement.variableDeclaration); + } + + private breakpointSpanOfVariableStatement(varStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!this.canHaveBreakpointInVariableStatement(varStatementNode)) { + return null; + } + + var variableStatement = varStatementNode; + var variableDeclaration = variableStatement.variableDeclaration; + var varDeclarationSyntax = variableDeclaration; + var varDeclarators = varDeclarationSyntax.variableDeclarators; + return createBreakpointSpanInfoWithLimChar(varStatementNode, end(childAt(varDeclarators, 0))); + } + + private breakpointSpanOfParameter(parameterNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (parameterNode.parent.kind() === SyntaxKind.SimpleArrowFunctionExpression) { + return this.breakpointSpanOfNode(parameterNode.parent); + } + + if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(parameterNode)) { + return null; + } + + var parameterSyntax = parameterNode; + if (parameterSyntax.dotDotDotToken || parameterSyntax.equalsValueClause || parameterSyntax.modifiers.length > 0) { + return createBreakpointSpanInfo(parameterNode); + } + else { + return null; + } + } + + private breakpointSpanOfMemberVariableDeclaration(memberVarDeclarationNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(memberVarDeclarationNode)) { + return null; + } + + var memberVariableDeclaration = memberVarDeclarationNode; + if (this.canHaveBreakpointInVariableDeclarator(memberVariableDeclaration.variableDeclarator)) { + return createBreakpointSpanInfo(memberVarDeclarationNode, memberVariableDeclaration.modifiers, memberVariableDeclaration.variableDeclarator); + } + else { + return null; + } + } + + private breakpointSpanOfImportDeclaration(importDeclarationNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(importDeclarationNode)) { + return null; + } + + var importSyntax = importDeclarationNode; + return createBreakpointSpanInfo(importDeclarationNode, importSyntax.modifiers, importSyntax.importKeyword, importSyntax.identifier, importSyntax.equalsToken, importSyntax.moduleReference); + } + + private breakpointSpanOfEnumDeclaration(enumDeclarationNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (!this.canHaveBreakpointInDeclaration(enumDeclarationNode)) { + return null; + } + + return createBreakpointSpanInfo(enumDeclarationNode); + } + + private breakpointSpanOfFirstEnumElement(enumDeclarationNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var enumDeclarationSyntax = enumDeclarationNode; + var enumElements = enumDeclarationSyntax.enumElements; + if (enumElements && childCount(enumElements)) { + return this.breakpointSpanOf(childAt(enumElements, 0)); + } + + return null; + } + + private breakpointSpanOfEnumElement(enumElementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + if (TypeScript.SyntaxUtilities.isAmbientDeclarationSyntax(enumElementNode)) { + return null; + } + + return createBreakpointSpanInfo(enumElementNode); + } + + private breakpointSpanOfIfStatement(ifStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var ifStatement = ifStatementNode; + return createBreakpointSpanInfo(ifStatementNode, ifStatement.ifKeyword, ifStatement.openParenToken, ifStatement.condition, ifStatement.closeParenToken); + } + + private breakpointSpanOfElseClause(elseClauseNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var elseClause = elseClauseNode; + return this.breakpointSpanOf(elseClause.statement); + } + + private breakpointSpanOfForInStatement(forInStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var forInStatement = forInStatementNode; + return createBreakpointSpanInfo(forInStatementNode, forInStatement.forKeyword, forInStatement.openParenToken, forInStatement.variableDeclaration, + forInStatement.left, forInStatement.inKeyword, forInStatement.expression, forInStatement.closeParenToken); + } + + private breakpointSpanOfForStatement(forStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var forStatement = forStatementNode; + return this.breakpointSpanOf(forStatement.variableDeclaration + ? forStatement.variableDeclaration + : forStatement.initializer); + } + + private breakpointSpanOfWhileStatement(whileStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var whileStatement = whileStatementNode; + return createBreakpointSpanInfo(whileStatementNode, whileStatement.whileKeyword, whileStatement.openParenToken, whileStatement.condition, whileStatement.closeParenToken); + } + + private breakpointSpanOfDoStatement(doStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var doStatement = doStatementNode; + return createBreakpointSpanInfo(doStatementNode, doStatement.whileKeyword, doStatement.openParenToken, doStatement.condition, doStatement.closeParenToken); + } + + private breakpointSpanOfSwitchStatement(switchStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var switchStatement = switchStatementNode; + return createBreakpointSpanInfo(switchStatementNode, switchStatement.switchKeyword, switchStatement.openParenToken, switchStatement.expression, switchStatement.closeParenToken); + } + + private breakpointSpanOfFirstStatementOfFirstCaseClause(switchStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var switchStatement = switchStatementNode; + if (switchStatement.switchClauses && switchStatement.switchClauses.length == 0) { + return null; + } + + var switchClauses = switchStatement.switchClauses; + if (switchClauses.length == 0) { + return null; + } + + var firstCaseClause = switchClauses[0]; + var statements = firstCaseClause.statements; + + return this.breakpointSpanOfFirstChildOfSyntaxList(statements); + } + + private breakpointSpanOfLastStatementOfLastCaseClause(switchStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var switchStatement = switchStatementNode; + if (switchStatement.switchClauses && switchStatement.switchClauses.length == 0) { + return null; + } + + var switchClauses = switchStatement.switchClauses; + if (switchClauses.length == 0) { + return null; + } + + var lastClauseNode = switchClauses[switchClauses.length - 1]; + var statements = lastClauseNode.statements; + + return this.breakpointSpanOfLastChildOfSyntaxList(statements); + } + + private breakpointSpanOfCaseSwitchClause(caseClauseNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var caseSwitchClause = caseClauseNode; + return this.breakpointSpanOfFirstChildOfSyntaxList(caseSwitchClause.statements); + } + + private breakpointSpanOfDefaultSwitchClause(defaultSwithClauseNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var defaultSwitchClause = defaultSwithClauseNode; + return this.breakpointSpanOfFirstChildOfSyntaxList(defaultSwitchClause.statements); + } + + private breakpointSpanOfWithStatement(withStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var withStatement = withStatementNode; + return this.breakpointSpanOf(withStatement.statement); + } + + private breakpointSpanOfTryStatement(tryStatementNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var tryStatement = tryStatementNode; + return this.breakpointSpanOfFirstStatementInBlock(tryStatement.block); + } + + private breakpointSpanOfCatchClause(catchClauseNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var catchClause = catchClauseNode; + return createBreakpointSpanInfo(catchClauseNode, catchClause.catchKeyword, catchClause.openParenToken, catchClause.identifier, catchClause.typeAnnotation, catchClause.closeParenToken); + } + + private breakpointSpanOfFinallyClause(finallyClauseNode: TypeScript.ISyntaxNode): ts.SpanInfo { + var finallyClause = finallyClauseNode; + return this.breakpointSpanOfFirstStatementInBlock(finallyClause.block); + } + + private breakpointSpanOfParenthesizedArrowFunctionExpression(arrowFunctionExpression: ParenthesizedArrowFunctionExpressionSyntax): ts.SpanInfo { + if (arrowFunctionExpression.block) { + return this.breakpointSpanOfFirstStatementInBlock(arrowFunctionExpression.block); + } + else { + return this.breakpointSpanOf(arrowFunctionExpression.expression); + } + } + + private breakpointSpanOfSimpleArrowFunctionExpression(arrowFunctionExpression: SimpleArrowFunctionExpressionSyntax): ts.SpanInfo { + if (arrowFunctionExpression.block) { + return this.breakpointSpanOfFirstStatementInBlock(arrowFunctionExpression.block); + } + else { + return this.breakpointSpanOf(arrowFunctionExpression.expression); + } + } + + private breakpointSpanOfContainingNode(positionedElement: ISyntaxElement): ts.SpanInfo { + var current = positionedElement.parent; + while (!isNode(current)) { + current = current.parent; + } + + return this.breakpointSpanOf(current); + } + + private breakpointSpanIfStartsOnSameLine(positionedElement: TypeScript.ISyntaxElement): ts.SpanInfo { + if (positionedElement && this.posLine == this.lineMap.getLineNumberFromPosition(start(positionedElement))) { + return this.breakpointSpanOf(positionedElement); + } + + return null; + } + + public breakpointSpanOf(positionedElement: TypeScript.ISyntaxElement): ts.SpanInfo { + if (!positionedElement) { + return null; + } + + for (var containingNode = Syntax.containingNode(positionedElement); containingNode != null; containingNode = Syntax.containingNode(containingNode)) { + if (containingNode.kind() == TypeScript.SyntaxKind.TypeAnnotation) { + return this.breakpointSpanIfStartsOnSameLine(containingNode); + } + } + + var element = positionedElement; + + // Syntax node + if (isNode(element)) { + return this.breakpointSpanOfNode(positionedElement); + } + + // Token + if (isToken(element)) { + return this.breakpointSpanOfToken(positionedElement); + } + + // List + // Separated List + return this.breakpointSpanOfContainingNode(positionedElement); + } + } + + export function getBreakpointLocation(syntaxTree: TypeScript.SyntaxTree, askedPos: number): ts.SpanInfo { + // Cannot set breakpoint in dts file + if (TypeScript.isDTSFile(syntaxTree.fileName())) { + return null; + } + + var sourceUnit = syntaxTree.sourceUnit(); + var positionedToken = TypeScript.findToken(sourceUnit, askedPos); + + var lineMap = syntaxTree.lineMap(); + var posLine = lineMap.getLineNumberFromPosition(askedPos); + var tokenStartLine = lineMap.getLineNumberFromPosition(start(positionedToken)); + if (posLine < tokenStartLine) { + return null; + } + + var breakpointResolver = new BreakpointResolver(posLine, lineMap); + return breakpointResolver.breakpointSpanOf(positionedToken); + } +} \ No newline at end of file diff --git a/src/services/compiler/ast.ts b/src/services/compiler/ast.ts new file mode 100644 index 00000000000..76b4903768d --- /dev/null +++ b/src/services/compiler/ast.ts @@ -0,0 +1,53 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export class Comment { + constructor(private _trivia: ISyntaxTrivia, + public endsLine: boolean, + public _start: number, + public _end: number) { + } + + public start(): number { + return this._start; + } + + public end(): number { + return this._end; + } + + public fullText(): string { + return this._trivia.fullText(); + } + + public kind(): SyntaxKind { + return this._trivia.kind(); + } + + public structuralEquals(ast: Comment, includingPosition: boolean): boolean { + if (includingPosition) { + if (this.start() !== ast.start() || this.end() !== ast.end()) { + return false; + } + } + + return this._trivia.fullText() === ast._trivia.fullText() && + this.endsLine === ast.endsLine; + } + } +} \ No newline at end of file diff --git a/src/services/compiler/astHelpers.ts b/src/services/compiler/astHelpers.ts new file mode 100644 index 00000000000..74d072846db --- /dev/null +++ b/src/services/compiler/astHelpers.ts @@ -0,0 +1,759 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.ASTHelpers { + + + var sentinelEmptyArray: any[] = []; + + //export function scriptIsElided(sourceUnit: SourceUnitSyntax): boolean { + // return isDTSFile(sourceUnit.syntaxTree.fileName()) || moduleMembersAreElided(sourceUnit.moduleElements); + //} + + //export function moduleIsElided(declaration: ModuleDeclarationSyntax): boolean { + // return hasModifier(declaration.modifiers, PullElementFlags.Ambient) || moduleMembersAreElided(declaration.moduleElements); + //} + + //function moduleMembersAreElided(members: IModuleElementSyntax[]): boolean { + // for (var i = 0, n = members.length; i < n; i++) { + // var member = members[i]; + + // // We should emit *this* module if it contains any non-interface types. + // // Caveat: if we have contain a module, then we should be emitted *if we want to + // // emit that inner module as well. + // if (member.kind() === SyntaxKind.ModuleDeclaration) { + // if (!moduleIsElided(member)) { + // return false; + // } + // } + // else if (member.kind() !== SyntaxKind.InterfaceDeclaration) { + // return false; + // } + // } + + // return true; + //} + + //export function enumIsElided(declaration: EnumDeclarationSyntax): boolean { + // if (hasModifier(declaration.modifiers, PullElementFlags.Ambient)) { + // return true; + // } + + // return false; + //} + + export function isValidAstNode(ast: ISyntaxElement): boolean { + return ast && !isShared(ast) && start(ast) !== -1 && end(ast) !== -1; + } + + export function isValidSpan(ast: ISpan): boolean { + if (!ast) + return false; + + if (ast.start() === -1 || ast.end() === -1) + return false; + + return true; + } + + /// + /// Return the ISyntaxElement containing "position" + /// + export function getAstAtPosition(script: ISyntaxElement, pos: number, useTrailingTriviaAsLimChar: boolean = true, forceInclusive: boolean = false): ISyntaxElement { + var top: ISyntaxElement = null; + + var pre = function (cur: ISyntaxElement, walker: IAstWalker) { + if (!isShared(cur) && isValidAstNode(cur)) { + var isInvalid1 = cur.kind() === SyntaxKind.ExpressionStatement && width(cur) === 0; + + if (isInvalid1) { + walker.options.goChildren = false; + } + else { + // Add "cur" to the stack if it contains our position + // For "identifier" nodes, we need a special case: A position equal to "limChar" is + // valid, since the position corresponds to a caret position (in between characters) + // For example: + // bar + // 0123 + // If "position === 3", the caret is at the "right" of the "r" character, which should be considered valid + var inclusive = + forceInclusive || + cur.kind() === SyntaxKind.IdentifierName || + cur.kind() === SyntaxKind.MemberAccessExpression || + cur.kind() === SyntaxKind.QualifiedName || + //cur.kind() === SyntaxKind.TypeRef || + cur.kind() === SyntaxKind.VariableDeclaration || + cur.kind() === SyntaxKind.VariableDeclarator || + cur.kind() === SyntaxKind.InvocationExpression || + pos === end(script) + lastToken(script).trailingTriviaWidth(); // Special "EOF" case + + var minChar = start(cur); + var limChar = end(cur) + (useTrailingTriviaAsLimChar ? trailingTriviaWidth(cur) : 0) + (inclusive ? 1 : 0); + if (pos >= minChar && pos < limChar) { + + // Ignore empty lists + if ((cur.kind() !== SyntaxKind.List && cur.kind() !== SyntaxKind.SeparatedList) || end(cur) > start(cur)) { + // TODO: Since ISyntaxElement is sometimes not correct wrt to position, only add "cur" if it's better + // than top of the stack. + if (top === null) { + top = cur; + } + else if (start(cur) >= start(top) && + (end(cur) + (useTrailingTriviaAsLimChar ? trailingTriviaWidth(cur) : 0)) <= (end(top) + (useTrailingTriviaAsLimChar ? trailingTriviaWidth(top) : 0))) { + // this new node appears to be better than the one we're + // storing. Make this the new node. + + // However, If the current top is a missing identifier, we + // don't want to replace it with another missing identifier. + // We want to return the first missing identifier found in a + // depth first walk of the tree. + if (width(top) !== 0 || width(cur) !== 0) { + top = cur; + } + } + } + } + + // Don't go further down the tree if pos is outside of [minChar, limChar] + walker.options.goChildren = (minChar <= pos && pos <= limChar); + } + } + }; + + getAstWalkerFactory().walk(script, pre); + return top; + } + + export function getExtendsHeritageClause(clauses: HeritageClauseSyntax[]): HeritageClauseSyntax { + return getHeritageClause(clauses, SyntaxKind.ExtendsHeritageClause); + } + + export function getImplementsHeritageClause(clauses: HeritageClauseSyntax[]): HeritageClauseSyntax { + return getHeritageClause(clauses, SyntaxKind.ImplementsHeritageClause); + } + + function getHeritageClause(clauses: HeritageClauseSyntax[], kind: SyntaxKind): HeritageClauseSyntax { + if (clauses) { + for (var i = 0, n = clauses.length; i < n; i++) { + var child = clauses[i]; + + if (child.typeNames.length > 0 && child.kind() === kind) { + return child; + } + } + } + + return null; + } + + export function isCallExpression(ast: ISyntaxElement): boolean { + return (ast && ast.kind() === SyntaxKind.InvocationExpression) || + (ast && ast.kind() === SyntaxKind.ObjectCreationExpression); + } + + export function isCallExpressionTarget(ast: ISyntaxElement): boolean { + return !!getCallExpressionTarget(ast); + } + + export function getCallExpressionTarget(ast: ISyntaxElement): ISyntaxElement { + if (!ast) { + return null; + } + + var current = ast; + + while (current && current.parent) { + if (current.parent.kind() === SyntaxKind.MemberAccessExpression && + (current.parent).name === current) { + current = current.parent; + continue; + } + + break; + } + + if (current && current.parent) { + if (current.parent.kind() === SyntaxKind.InvocationExpression || current.parent.kind() === SyntaxKind.ObjectCreationExpression) { + return current === (current.parent).expression ? current : null; + } + } + return null; + } + + function isNameOfSomeDeclaration(ast: ISyntaxElement) { + if (ast === null || ast.parent === null) { + return false; + } + if (ast.kind() !== SyntaxKind.IdentifierName) { + return false; + } + + switch (ast.parent.kind()) { + case SyntaxKind.ClassDeclaration: + return (ast.parent).identifier === ast; + case SyntaxKind.InterfaceDeclaration: + return (ast.parent).identifier === ast; + case SyntaxKind.EnumDeclaration: + return (ast.parent).identifier === ast; + case SyntaxKind.ModuleDeclaration: + return (ast.parent).name === ast || (ast.parent).stringLiteral === ast; + case SyntaxKind.VariableDeclarator: + return (ast.parent).propertyName === ast; + case SyntaxKind.FunctionDeclaration: + return (ast.parent).identifier === ast; + case SyntaxKind.MemberFunctionDeclaration: + return (ast.parent).propertyName === ast; + case SyntaxKind.Parameter: + return (ast.parent).identifier === ast; + case SyntaxKind.TypeParameter: + return (ast.parent).identifier === ast; + case SyntaxKind.SimplePropertyAssignment: + return (ast.parent).propertyName === ast; + case SyntaxKind.FunctionPropertyAssignment: + return (ast.parent).propertyName === ast; + case SyntaxKind.EnumElement: + return (ast.parent).propertyName === ast; + case SyntaxKind.ImportDeclaration: + return (ast.parent).identifier === ast; + case SyntaxKind.MethodSignature: + return (ast.parent).propertyName === ast; + case SyntaxKind.PropertySignature: + return (ast.parent).propertyName === ast; + } + + return false; + } + + export function isDeclarationASTOrDeclarationNameAST(ast: ISyntaxElement) { + return isNameOfSomeDeclaration(ast) || ASTHelpers.isDeclarationAST(ast); + } + + export function getEnclosingParameterForInitializer(ast: ISyntaxElement): ParameterSyntax { + var current = ast; + while (current) { + switch (current.kind()) { + case SyntaxKind.EqualsValueClause: + if (current.parent && current.parent.kind() === SyntaxKind.Parameter) { + return current.parent; + } + break; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ModuleDeclaration: + // exit early + return null; + } + + current = current.parent; + } + return null; + } + + export function getEnclosingMemberDeclaration(ast: ISyntaxElement): ISyntaxElement { + var current = ast; + + while (current) { + switch (current.kind()) { + case SyntaxKind.MemberVariableDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return current; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ModuleDeclaration: + // exit early + return null; + } + current = current.parent; + } + + return null; + } + + export function isNameOfFunction(ast: ISyntaxElement) { + return ast + && ast.parent + && ast.kind() === SyntaxKind.IdentifierName + && ast.parent.kind() === SyntaxKind.FunctionDeclaration + && (ast.parent).identifier === ast; + } + + export function isNameOfMemberFunction(ast: ISyntaxElement) { + return ast + && ast.parent + && ast.kind() === SyntaxKind.IdentifierName + && ast.parent.kind() === SyntaxKind.MemberFunctionDeclaration + && (ast.parent).propertyName === ast; + } + + export function isNameOfMemberAccessExpression(ast: ISyntaxElement) { + if (ast && + ast.parent && + ast.parent.kind() === SyntaxKind.MemberAccessExpression && + (ast.parent).name === ast) { + + return true; + } + + return false; + } + + export function isRightSideOfQualifiedName(ast: ISyntaxElement) { + if (ast && + ast.parent && + ast.parent.kind() === SyntaxKind.QualifiedName && + (ast.parent).right === ast) { + + return true; + } + + return false; + } + + export function parentIsModuleDeclaration(ast: ISyntaxElement) { + return ast.parent && ast.parent.kind() === SyntaxKind.ModuleDeclaration; + } + + export function isDeclarationAST(ast: ISyntaxElement): boolean { + switch (ast.kind()) { + case SyntaxKind.VariableDeclarator: + return getVariableStatement(ast) !== null; + + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.SimpleArrowFunctionExpression: + case SyntaxKind.ParenthesizedArrowFunctionExpression: + case SyntaxKind.IndexSignature: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ArrayType: + case SyntaxKind.ObjectType: + case SyntaxKind.TypeParameter: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MemberVariableDeclaration: + case SyntaxKind.IndexMemberDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumElement: + case SyntaxKind.SimplePropertyAssignment: + case SyntaxKind.FunctionPropertyAssignment: + case SyntaxKind.FunctionExpression: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.PropertySignature: + return true; + default: + return false; + } + } + + export function preComments(element: ISyntaxElement, text: ISimpleText): Comment[]{ + if (element) { + switch (element.kind()) { + case SyntaxKind.VariableStatement: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.IfStatement: + case SyntaxKind.SimplePropertyAssignment: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.MemberVariableDeclaration: + case SyntaxKind.EnumElement: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.PropertySignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.FunctionPropertyAssignment: + case SyntaxKind.Parameter: + return convertNodeLeadingComments(element, text); + } + } + + return null; + } + + export function postComments(element: ISyntaxElement, text: ISimpleText): Comment[] { + if (element) { + switch (element.kind()) { + case SyntaxKind.ExpressionStatement: + return convertNodeTrailingComments(element, text, /*allowWithNewLine:*/ true); + case SyntaxKind.VariableStatement: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.IfStatement: + case SyntaxKind.SimplePropertyAssignment: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.MemberVariableDeclaration: + case SyntaxKind.EnumElement: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.PropertySignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.FunctionPropertyAssignment: + case SyntaxKind.Parameter: + return convertNodeTrailingComments(element, text); + } + } + + return null; + } + + function convertNodeTrailingComments(node: ISyntaxElement, text: ISimpleText, allowWithNewLine = false): Comment[]{ + // Bail out quickly before doing any expensive math computation. + var _lastToken = lastToken(node); + if (_lastToken === null || !_lastToken.hasTrailingTrivia()) { + return null; + } + + if (!allowWithNewLine && SyntaxUtilities.isLastTokenOnLine(_lastToken, text)) { + return null; + } + + return convertComments(_lastToken.trailingTrivia(text), fullStart(node) + fullWidth(node) - _lastToken.trailingTriviaWidth(text)); + } + + function convertNodeLeadingComments(element: ISyntaxElement, text: ISimpleText): Comment[]{ + if (element) { + return convertTokenLeadingComments(firstToken(element), text); + } + + return null; + } + + export function convertTokenLeadingComments(token: ISyntaxToken, text: ISimpleText): Comment[]{ + if (token === null) { + return null; + } + + return token.hasLeadingTrivia() + ? convertComments(token.leadingTrivia(text), token.fullStart()) + : null; + } + + export function convertTokenTrailingComments(token: ISyntaxToken, text: ISimpleText): Comment[] { + if (token === null) { + return null; + } + + return token.hasTrailingTrivia() + ? convertComments(token.trailingTrivia(text), fullEnd(token) - token.trailingTriviaWidth(text)) + : null; + } + + function convertComments(triviaList: ISyntaxTriviaList, commentStartPosition: number): Comment[]{ + var result: Comment[] = null; + + for (var i = 0, n = triviaList.count(); i < n; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + + if (trivia.isComment()) { + var hasTrailingNewLine = ((i + 1) < n) && triviaList.syntaxTriviaAt(i + 1).isNewLine(); + result = result || []; + result.push(convertComment(trivia, commentStartPosition, hasTrailingNewLine)); + } + + commentStartPosition += trivia.fullWidth(); + } + + return result; + } + + function convertComment(trivia: ISyntaxTrivia, commentStartPosition: number, hasTrailingNewLine: boolean): Comment { + var comment = new Comment(trivia, hasTrailingNewLine, commentStartPosition, commentStartPosition + trivia.fullWidth()); + + return comment; + } + + export function docComments(ast: ISyntaxElement, text: ISimpleText): Comment[] { + if (isDeclarationAST(ast)) { + var comments: Comment[] = null; + + if (ast.kind() === SyntaxKind.VariableDeclarator) { + // Get the doc comments for a variable off of the variable statement. That's what + // they'll be attached to in the tree. + comments = TypeScript.ASTHelpers.preComments(getVariableStatement(ast), text); + } + else if (ast.kind() === SyntaxKind.Parameter) { + // First check if the parameter was written like so: + // ( + // /** blah */ a, + // /** blah */ b); + comments = TypeScript.ASTHelpers.preComments(ast, text); + if (!comments) { + // Now check if it was written like so: + // (/** blah */ a, /** blah */ b); + // In this case, the comment will belong to the preceding token. + var previousToken = findToken(syntaxTree(ast).sourceUnit(), firstToken(ast).fullStart() - 1); + if (previousToken && (previousToken.kind() === SyntaxKind.OpenParenToken || previousToken.kind() === SyntaxKind.CommaToken)) { + comments = convertTokenTrailingComments(previousToken, text); + } + } + } + else { + comments = TypeScript.ASTHelpers.preComments(ast, text); + } + + if (comments && comments.length > 0) { + return comments.filter(c => isDocComment(c)); + } + } + + return sentinelEmptyArray; + } + + export function isDocComment(comment: Comment) { + if (comment.kind() === SyntaxKind.MultiLineCommentTrivia) { + var fullText = comment.fullText(); + return fullText.charAt(2) === "*" && fullText.charAt(3) !== "/"; + } + + return false; + } + + export function getParameterList(ast: ISyntaxElement): ParameterListSyntax { + if (ast) { + switch (ast.kind()) { + case SyntaxKind.ConstructorDeclaration: + return getParameterList((ast).callSignature); + case SyntaxKind.FunctionDeclaration: + return getParameterList((ast).callSignature); + case SyntaxKind.ParenthesizedArrowFunctionExpression: + return getParameterList((ast).callSignature); + case SyntaxKind.ConstructSignature: + return getParameterList((ast).callSignature); + case SyntaxKind.MemberFunctionDeclaration: + return getParameterList((ast).callSignature); + case SyntaxKind.FunctionPropertyAssignment: + return getParameterList((ast).callSignature); + case SyntaxKind.FunctionExpression: + return getParameterList((ast).callSignature); + case SyntaxKind.MethodSignature: + return getParameterList((ast).callSignature); + case SyntaxKind.ConstructorType: + return (ast).parameterList; + case SyntaxKind.FunctionType: + return (ast).parameterList; + case SyntaxKind.CallSignature: + return (ast).parameterList; + case SyntaxKind.GetAccessor: + return getParameterList((ast).callSignature); + case SyntaxKind.SetAccessor: + return getParameterList((ast).callSignature); + } + } + + return null; + } + + export function getType(ast: ISyntaxElement): ITypeSyntax { + if (ast) { + switch (ast.kind()) { + case SyntaxKind.FunctionDeclaration: + return getType((ast).callSignature); + case SyntaxKind.ParenthesizedArrowFunctionExpression: + return getType((ast).callSignature); + case SyntaxKind.ConstructSignature: + return getType((ast).callSignature); + case SyntaxKind.MemberFunctionDeclaration: + return getType((ast).callSignature); + case SyntaxKind.FunctionPropertyAssignment: + return getType((ast).callSignature); + case SyntaxKind.FunctionExpression: + return getType((ast).callSignature); + case SyntaxKind.MethodSignature: + return getType((ast).callSignature); + case SyntaxKind.CallSignature: + return getType((ast).typeAnnotation); + case SyntaxKind.IndexSignature: + return getType((ast).typeAnnotation); + case SyntaxKind.PropertySignature: + return getType((ast).typeAnnotation); + case SyntaxKind.GetAccessor: + return getType((ast).callSignature); + case SyntaxKind.Parameter: + return getType((ast).typeAnnotation); + case SyntaxKind.MemberVariableDeclaration: + return getType((ast).variableDeclarator); + case SyntaxKind.VariableDeclarator: + return getType((ast).typeAnnotation); + case SyntaxKind.CatchClause: + return getType((ast).typeAnnotation); + case SyntaxKind.ConstructorType: + return (ast).type; + case SyntaxKind.FunctionType: + return (ast).type; + case SyntaxKind.TypeAnnotation: + return (ast).type; + } + } + + return null; + } + + function getVariableStatement(variableDeclarator: VariableDeclaratorSyntax): VariableStatementSyntax { + if (variableDeclarator && variableDeclarator.parent && variableDeclarator.parent.parent && variableDeclarator.parent.parent.parent && + variableDeclarator.parent.kind() === SyntaxKind.SeparatedList && + variableDeclarator.parent.parent.kind() === SyntaxKind.VariableDeclaration && + variableDeclarator.parent.parent.parent.kind() === SyntaxKind.VariableStatement) { + + return variableDeclarator.parent.parent.parent; + } + + return null; + } + + export function getVariableDeclaratorModifiers(variableDeclarator: VariableDeclaratorSyntax): ISyntaxToken[] { + var variableStatement = getVariableStatement(variableDeclarator); + return variableStatement ? variableStatement.modifiers : Syntax.emptyList(); + } + + export function isIntegerLiteralAST(expression: ISyntaxElement): boolean { + if (expression) { + switch (expression.kind()) { + case SyntaxKind.PlusExpression: + case SyntaxKind.NegateExpression: + // Note: if there is a + or - sign, we can only allow a normal integer following + // (and not a hex integer). i.e. -0xA is a legal expression, but it is not a + // *literal*. + expression = (expression).operand; + return expression.kind() === SyntaxKind.NumericLiteral && IntegerUtilities.isInteger((expression).text()); + + case SyntaxKind.NumericLiteral: + // If it doesn't have a + or -, then either an integer literal or a hex literal + // is acceptable. + var text = (expression).text(); + return IntegerUtilities.isInteger(text) || IntegerUtilities.isHexInteger(text); + } + } + + return false; + } + + export function getEnclosingModuleDeclaration(ast: ISyntaxElement): ModuleDeclarationSyntax { + while (ast) { + if (ast.kind() === SyntaxKind.ModuleDeclaration) { + return ast; + } + + ast = ast.parent; + } + + return null; + } + + function isEntireNameOfModuleDeclaration(nameAST: ISyntaxElement) { + return parentIsModuleDeclaration(nameAST) && (nameAST.parent).name === nameAST; + } + + export function getModuleDeclarationFromNameAST(ast: ISyntaxElement): ModuleDeclarationSyntax { + if (ast) { + switch (ast.kind()) { + case SyntaxKind.StringLiteral: + if (parentIsModuleDeclaration(ast) && (ast.parent).stringLiteral === ast) { + return ast.parent; + } + return null; + + case SyntaxKind.IdentifierName: + case SyntaxKind.QualifiedName: + if (isEntireNameOfModuleDeclaration(ast)) { + return ast.parent; + } + break; + + default: + return null; + } + + // Only qualified names can be name of module declaration if they didnt satisfy above conditions + for (ast = ast.parent; ast && ast.kind() === SyntaxKind.QualifiedName; ast = ast.parent) { + if (isEntireNameOfModuleDeclaration(ast)) { + return ast.parent; + } + } + } + + return null; + } + + export function isLastNameOfModule(ast: ModuleDeclarationSyntax, astName: ISyntaxElement): boolean { + if (ast) { + if (ast.stringLiteral) { + return astName === ast.stringLiteral; + } + else if (ast.name.kind() === SyntaxKind.QualifiedName) { + return astName === (ast.name).right; + } + else { + return astName === ast.name; + } + } + + return false; + } + + export function getNameOfIdenfierOrQualifiedName(name: ISyntaxElement): string { + if (name.kind() === SyntaxKind.IdentifierName) { + return (name).text(); + } + else { + Debug.assert(name.kind() == SyntaxKind.QualifiedName); + var dotExpr = name; + return getNameOfIdenfierOrQualifiedName(dotExpr.left) + "." + getNameOfIdenfierOrQualifiedName(dotExpr.right); + } + } + + export function getModuleNames(name: ISyntaxElement, result?: ISyntaxToken[]): ISyntaxToken[] { + result = result || []; + + if (name.kind() === SyntaxKind.QualifiedName) { + getModuleNames((name).left, result); + result.push((name).right); + } + else { + result.push(name); + } + + return result; + } +} \ No newline at end of file diff --git a/src/services/compiler/astWalker.ts b/src/services/compiler/astWalker.ts new file mode 100644 index 00000000000..53d22142cbb --- /dev/null +++ b/src/services/compiler/astWalker.ts @@ -0,0 +1,716 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + function walkListChildren(preAst: ISyntaxNodeOrToken[], walker: AstWalker): void { + for (var i = 0, n = preAst.length; i < n; i++) { + walker.walk(preAst[i]); + } + } + + function walkThrowStatementChildren(preAst: ThrowStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkPrefixUnaryExpressionChildren(preAst: PrefixUnaryExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.operand); + } + + function walkPostfixUnaryExpressionChildren(preAst: PostfixUnaryExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.operand); + } + + function walkDeleteExpressionChildren(preAst: DeleteExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkTypeArgumentListChildren(preAst: TypeArgumentListSyntax, walker: AstWalker): void { + walker.walk(preAst.typeArguments); + } + + function walkTypeOfExpressionChildren(preAst: TypeOfExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkVoidExpressionChildren(preAst: VoidExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkArgumentListChildren(preAst: ArgumentListSyntax, walker: AstWalker): void { + walker.walk(preAst.typeArgumentList); + walker.walk(preAst.arguments); + } + + function walkArrayLiteralExpressionChildren(preAst: ArrayLiteralExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expressions); + } + + function walkSimplePropertyAssignmentChildren(preAst: SimplePropertyAssignmentSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.expression); + } + + function walkFunctionPropertyAssignmentChildren(preAst: FunctionPropertyAssignmentSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkGetAccessorChildren(preAst: GetAccessorSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkSeparatedListChildren(preAst: ISyntaxNodeOrToken[], walker: AstWalker): void { + for (var i = 0, n = preAst.length; i < n; i++) { + walker.walk(preAst[i]); + } + } + + function walkSetAccessorChildren(preAst: SetAccessorSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkObjectLiteralExpressionChildren(preAst: ObjectLiteralExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyAssignments); + } + + function walkCastExpressionChildren(preAst: CastExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.type); + walker.walk(preAst.expression); + } + + function walkParenthesizedExpressionChildren(preAst: ParenthesizedExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkElementAccessExpressionChildren(preAst: ElementAccessExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + walker.walk(preAst.argumentExpression); + } + + function walkMemberAccessExpressionChildren(preAst: MemberAccessExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + walker.walk(preAst.name); + } + + function walkQualifiedNameChildren(preAst: QualifiedNameSyntax, walker: AstWalker): void { + walker.walk(preAst.left); + walker.walk(preAst.right); + } + + function walkBinaryExpressionChildren(preAst: BinaryExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.left); + walker.walk(preAst.right); + } + + function walkEqualsValueClauseChildren(preAst: EqualsValueClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.value); + } + + function walkTypeParameterChildren(preAst: TypeParameterSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.constraint); + } + + function walkTypeParameterListChildren(preAst: TypeParameterListSyntax, walker: AstWalker): void { + walker.walk(preAst.typeParameters); + } + + function walkGenericTypeChildren(preAst: GenericTypeSyntax, walker: AstWalker): void { + walker.walk(preAst.name); + walker.walk(preAst.typeArgumentList); + } + + function walkTypeAnnotationChildren(preAst: TypeAnnotationSyntax, walker: AstWalker): void { + walker.walk(preAst.type); + } + + function walkTypeQueryChildren(preAst: TypeQuerySyntax, walker: AstWalker): void { + walker.walk(preAst.name); + } + + function walkInvocationExpressionChildren(preAst: InvocationExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + walker.walk(preAst.argumentList); + } + + function walkObjectCreationExpressionChildren(preAst: ObjectCreationExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + walker.walk(preAst.argumentList); + } + + function walkTrinaryExpressionChildren(preAst: ConditionalExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.condition); + walker.walk(preAst.whenTrue); + walker.walk(preAst.whenFalse); + } + + function walkFunctionExpressionChildren(preAst: FunctionExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkFunctionTypeChildren(preAst: FunctionTypeSyntax, walker: AstWalker): void { + walker.walk(preAst.typeParameterList); + walker.walk(preAst.parameterList); + walker.walk(preAst.type); + } + + function walkParenthesizedArrowFunctionExpressionChildren(preAst: ParenthesizedArrowFunctionExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + walker.walk(preAst.expression); + } + + function walkSimpleArrowFunctionExpressionChildren(preAst: SimpleArrowFunctionExpressionSyntax, walker: AstWalker): void { + walker.walk(preAst.parameter); + walker.walk(preAst.block); + walker.walk(preAst.expression); + } + + function walkMemberFunctionDeclarationChildren(preAst: MemberFunctionDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkFuncDeclChildren(preAst: FunctionDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkIndexMemberDeclarationChildren(preAst: IndexMemberDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.indexSignature); + } + + function walkIndexSignatureChildren(preAst: IndexSignatureSyntax, walker: AstWalker): void { + walker.walk(preAst.parameters); + walker.walk(preAst.typeAnnotation); + } + + function walkCallSignatureChildren(preAst: CallSignatureSyntax, walker: AstWalker): void { + walker.walk(preAst.typeParameterList); + walker.walk(preAst.parameterList); + walker.walk(preAst.typeAnnotation); + } + + function walkConstraintChildren(preAst: ConstraintSyntax, walker: AstWalker): void { + walker.walk(preAst.typeOrExpression); + } + + function walkConstructorDeclarationChildren(preAst: ConstructorDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.callSignature); + walker.walk(preAst.block); + } + + function walkConstructorTypeChildren(preAst: FunctionTypeSyntax, walker: AstWalker): void { + walker.walk(preAst.typeParameterList); + walker.walk(preAst.parameterList); + walker.walk(preAst.type); + } + + function walkConstructSignatureChildren(preAst: ConstructSignatureSyntax, walker: AstWalker): void { + walker.walk(preAst.callSignature); + } + + function walkParameterChildren(preAst: ParameterSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.typeAnnotation); + walker.walk(preAst.equalsValueClause); + } + + function walkParameterListChildren(preAst: ParameterListSyntax, walker: AstWalker): void { + walker.walk(preAst.parameters); + } + + function walkPropertySignatureChildren(preAst: PropertySignatureSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.typeAnnotation); + } + + function walkVariableDeclaratorChildren(preAst: VariableDeclaratorSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.typeAnnotation); + walker.walk(preAst.equalsValueClause); + } + + function walkMemberVariableDeclarationChildren(preAst: MemberVariableDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.variableDeclarator); + } + + function walkMethodSignatureChildren(preAst: MethodSignatureSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.callSignature); + } + + function walkReturnStatementChildren(preAst: ReturnStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkForStatementChildren(preAst: ForStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.variableDeclaration); + walker.walk(preAst.initializer); + walker.walk(preAst.condition); + walker.walk(preAst.incrementor); + walker.walk(preAst.statement); + } + + function walkForInStatementChildren(preAst: ForInStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.variableDeclaration); + walker.walk(preAst.left); + walker.walk(preAst.expression); + walker.walk(preAst.statement); + } + + function walkIfStatementChildren(preAst: IfStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.condition); + walker.walk(preAst.statement); + walker.walk(preAst.elseClause); + } + + function walkElseClauseChildren(preAst: ElseClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.statement); + } + + function walkWhileStatementChildren(preAst: WhileStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.condition); + walker.walk(preAst.statement); + } + + function walkDoStatementChildren(preAst: DoStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.condition); + walker.walk(preAst.statement); + } + + function walkBlockChildren(preAst: BlockSyntax, walker: AstWalker): void { + walker.walk(preAst.statements); + } + + function walkVariableDeclarationChildren(preAst: VariableDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.variableDeclarators); + } + + function walkCaseSwitchClauseChildren(preAst: CaseSwitchClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + walker.walk(preAst.statements); + } + + function walkDefaultSwitchClauseChildren(preAst: DefaultSwitchClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.statements); + } + + function walkSwitchStatementChildren(preAst: SwitchStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + walker.walk(preAst.switchClauses); + } + + function walkTryStatementChildren(preAst: TryStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.block); + walker.walk(preAst.catchClause); + walker.walk(preAst.finallyClause); + } + + function walkCatchClauseChildren(preAst: CatchClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.typeAnnotation); + walker.walk(preAst.block); + } + + function walkExternalModuleReferenceChildren(preAst: ExternalModuleReferenceSyntax, walker: AstWalker): void { + walker.walk(preAst.stringLiteral); + } + + function walkFinallyClauseChildren(preAst: FinallyClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.block); + } + + function walkClassDeclChildren(preAst: ClassDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.typeParameterList); + walker.walk(preAst.heritageClauses); + walker.walk(preAst.classElements); + } + + function walkScriptChildren(preAst: SourceUnitSyntax, walker: AstWalker): void { + walker.walk(preAst.moduleElements); + } + + function walkHeritageClauseChildren(preAst: HeritageClauseSyntax, walker: AstWalker): void { + walker.walk(preAst.typeNames); + } + + function walkInterfaceDeclerationChildren(preAst: InterfaceDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.typeParameterList); + walker.walk(preAst.heritageClauses); + walker.walk(preAst.body); + } + + function walkObjectTypeChildren(preAst: ObjectTypeSyntax, walker: AstWalker): void { + walker.walk(preAst.typeMembers); + } + + function walkArrayTypeChildren(preAst: ArrayTypeSyntax, walker: AstWalker): void { + walker.walk(preAst.type); + } + + function walkModuleDeclarationChildren(preAst: ModuleDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.name); + walker.walk(preAst.stringLiteral); + walker.walk(preAst.moduleElements); + } + + function walkModuleNameModuleReferenceChildren(preAst: ModuleNameModuleReferenceSyntax, walker: AstWalker): void { + walker.walk(preAst.moduleName); + } + + function walkEnumDeclarationChildren(preAst: EnumDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.enumElements); + } + + function walkEnumElementChildren(preAst: EnumElementSyntax, walker: AstWalker): void { + walker.walk(preAst.propertyName); + walker.walk(preAst.equalsValueClause); + } + + function walkImportDeclarationChildren(preAst: ImportDeclarationSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.moduleReference); + } + + function walkExportAssignmentChildren(preAst: ExportAssignmentSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + } + + function walkWithStatementChildren(preAst: WithStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.condition); + walker.walk(preAst.statement); + } + + function walkExpressionStatementChildren(preAst: ExpressionStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.expression); + } + + function walkLabeledStatementChildren(preAst: LabeledStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.identifier); + walker.walk(preAst.statement); + } + + function walkVariableStatementChildren(preAst: VariableStatementSyntax, walker: AstWalker): void { + walker.walk(preAst.variableDeclaration); + } + + var childrenWalkers: IAstWalkChildren[] = new Array(SyntaxKind.LastNode + 1); + + // Tokens/trivia can't ever be walked into. + for (var i = SyntaxKind.FirstToken, n = SyntaxKind.LastToken; i <= n; i++) { + childrenWalkers[i] = null; + } + for (var i = SyntaxKind.FirstTrivia, n = SyntaxKind.LastTrivia; i <= n; i++) { + childrenWalkers[i] = null; + } + + childrenWalkers[SyntaxKind.AddAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.AddExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.AndAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.AnyKeyword] = null; + childrenWalkers[SyntaxKind.ArgumentList] = walkArgumentListChildren; + childrenWalkers[SyntaxKind.ArrayLiteralExpression] = walkArrayLiteralExpressionChildren; + childrenWalkers[SyntaxKind.ArrayType] = walkArrayTypeChildren; + childrenWalkers[SyntaxKind.SimpleArrowFunctionExpression] = walkSimpleArrowFunctionExpressionChildren; + childrenWalkers[SyntaxKind.ParenthesizedArrowFunctionExpression] = walkParenthesizedArrowFunctionExpressionChildren; + childrenWalkers[SyntaxKind.AssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.BitwiseAndExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.BitwiseExclusiveOrExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.BitwiseNotExpression] = walkPrefixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.BitwiseOrExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.Block] = walkBlockChildren; + childrenWalkers[SyntaxKind.BooleanKeyword] = null; + childrenWalkers[SyntaxKind.BreakStatement] = null; + childrenWalkers[SyntaxKind.CallSignature] = walkCallSignatureChildren; + childrenWalkers[SyntaxKind.CaseSwitchClause] = walkCaseSwitchClauseChildren; + childrenWalkers[SyntaxKind.CastExpression] = walkCastExpressionChildren; + childrenWalkers[SyntaxKind.CatchClause] = walkCatchClauseChildren; + childrenWalkers[SyntaxKind.ClassDeclaration] = walkClassDeclChildren; + childrenWalkers[SyntaxKind.CommaExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.ConditionalExpression] = walkTrinaryExpressionChildren; + childrenWalkers[SyntaxKind.Constraint] = walkConstraintChildren; + childrenWalkers[SyntaxKind.ConstructorDeclaration] = walkConstructorDeclarationChildren; + childrenWalkers[SyntaxKind.ConstructSignature] = walkConstructSignatureChildren; + childrenWalkers[SyntaxKind.ContinueStatement] = null; + childrenWalkers[SyntaxKind.ConstructorType] = walkConstructorTypeChildren; + childrenWalkers[SyntaxKind.DebuggerStatement] = null; + childrenWalkers[SyntaxKind.DefaultSwitchClause] = walkDefaultSwitchClauseChildren; + childrenWalkers[SyntaxKind.DeleteExpression] = walkDeleteExpressionChildren; + childrenWalkers[SyntaxKind.DivideAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.DivideExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.DoStatement] = walkDoStatementChildren; + childrenWalkers[SyntaxKind.ElementAccessExpression] = walkElementAccessExpressionChildren; + childrenWalkers[SyntaxKind.ElseClause] = walkElseClauseChildren; + childrenWalkers[SyntaxKind.EmptyStatement] = null; + childrenWalkers[SyntaxKind.EnumDeclaration] = walkEnumDeclarationChildren; + childrenWalkers[SyntaxKind.EnumElement] = walkEnumElementChildren; + childrenWalkers[SyntaxKind.EqualsExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.EqualsValueClause] = walkEqualsValueClauseChildren; + childrenWalkers[SyntaxKind.EqualsWithTypeConversionExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.ExclusiveOrAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.ExportAssignment] = walkExportAssignmentChildren; + childrenWalkers[SyntaxKind.ExpressionStatement] = walkExpressionStatementChildren; + childrenWalkers[SyntaxKind.ExtendsHeritageClause] = walkHeritageClauseChildren; + childrenWalkers[SyntaxKind.ExternalModuleReference] = walkExternalModuleReferenceChildren; + childrenWalkers[SyntaxKind.FalseKeyword] = null; + childrenWalkers[SyntaxKind.FinallyClause] = walkFinallyClauseChildren; + childrenWalkers[SyntaxKind.ForInStatement] = walkForInStatementChildren; + childrenWalkers[SyntaxKind.ForStatement] = walkForStatementChildren; + childrenWalkers[SyntaxKind.FunctionDeclaration] = walkFuncDeclChildren; + childrenWalkers[SyntaxKind.FunctionExpression] = walkFunctionExpressionChildren; + childrenWalkers[SyntaxKind.FunctionPropertyAssignment] = walkFunctionPropertyAssignmentChildren; + childrenWalkers[SyntaxKind.FunctionType] = walkFunctionTypeChildren; + childrenWalkers[SyntaxKind.GenericType] = walkGenericTypeChildren; + childrenWalkers[SyntaxKind.GetAccessor] = walkGetAccessorChildren; + childrenWalkers[SyntaxKind.GreaterThanExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.GreaterThanOrEqualExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.IfStatement] = walkIfStatementChildren; + childrenWalkers[SyntaxKind.ImplementsHeritageClause] = walkHeritageClauseChildren; + childrenWalkers[SyntaxKind.ImportDeclaration] = walkImportDeclarationChildren; + childrenWalkers[SyntaxKind.IndexMemberDeclaration] = walkIndexMemberDeclarationChildren; + childrenWalkers[SyntaxKind.IndexSignature] = walkIndexSignatureChildren; + childrenWalkers[SyntaxKind.InExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.InstanceOfExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.InterfaceDeclaration] = walkInterfaceDeclerationChildren; + childrenWalkers[SyntaxKind.InvocationExpression] = walkInvocationExpressionChildren; + childrenWalkers[SyntaxKind.LabeledStatement] = walkLabeledStatementChildren; + childrenWalkers[SyntaxKind.LeftShiftAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.LeftShiftExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.LessThanExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.LessThanOrEqualExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.List] = walkListChildren; + childrenWalkers[SyntaxKind.LogicalAndExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.LogicalNotExpression] = walkPrefixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.LogicalOrExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.MemberAccessExpression] = walkMemberAccessExpressionChildren; + childrenWalkers[SyntaxKind.MemberFunctionDeclaration] = walkMemberFunctionDeclarationChildren; + childrenWalkers[SyntaxKind.MemberVariableDeclaration] = walkMemberVariableDeclarationChildren; + childrenWalkers[SyntaxKind.MethodSignature] = walkMethodSignatureChildren; + childrenWalkers[SyntaxKind.ModuleDeclaration] = walkModuleDeclarationChildren; + childrenWalkers[SyntaxKind.ModuleNameModuleReference] = walkModuleNameModuleReferenceChildren; + childrenWalkers[SyntaxKind.ModuloAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.ModuloExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.MultiplyAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.MultiplyExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.IdentifierName] = null; + childrenWalkers[SyntaxKind.NegateExpression] = walkPrefixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.None] = null; + childrenWalkers[SyntaxKind.NotEqualsExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.NotEqualsWithTypeConversionExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.NullKeyword] = null; + childrenWalkers[SyntaxKind.NumberKeyword] = null; + childrenWalkers[SyntaxKind.NumericLiteral] = null; + childrenWalkers[SyntaxKind.ObjectCreationExpression] = walkObjectCreationExpressionChildren; + childrenWalkers[SyntaxKind.ObjectLiteralExpression] = walkObjectLiteralExpressionChildren; + childrenWalkers[SyntaxKind.ObjectType] = walkObjectTypeChildren; + childrenWalkers[SyntaxKind.OmittedExpression] = null; + childrenWalkers[SyntaxKind.OrAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.Parameter] = walkParameterChildren; + childrenWalkers[SyntaxKind.ParameterList] = walkParameterListChildren; + childrenWalkers[SyntaxKind.ParenthesizedExpression] = walkParenthesizedExpressionChildren; + childrenWalkers[SyntaxKind.PlusExpression] = walkPrefixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.PostDecrementExpression] = walkPostfixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.PostIncrementExpression] = walkPostfixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.PreDecrementExpression] = walkPrefixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.PreIncrementExpression] = walkPrefixUnaryExpressionChildren; + childrenWalkers[SyntaxKind.PropertySignature] = walkPropertySignatureChildren; + childrenWalkers[SyntaxKind.QualifiedName] = walkQualifiedNameChildren; + childrenWalkers[SyntaxKind.RegularExpressionLiteral] = null; + childrenWalkers[SyntaxKind.ReturnStatement] = walkReturnStatementChildren; + childrenWalkers[SyntaxKind.SourceUnit] = walkScriptChildren; + childrenWalkers[SyntaxKind.SeparatedList] = walkSeparatedListChildren; + childrenWalkers[SyntaxKind.SetAccessor] = walkSetAccessorChildren; + childrenWalkers[SyntaxKind.SignedRightShiftAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.SignedRightShiftExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.SimplePropertyAssignment] = walkSimplePropertyAssignmentChildren; + childrenWalkers[SyntaxKind.StringLiteral] = null; + childrenWalkers[SyntaxKind.StringKeyword] = null; + childrenWalkers[SyntaxKind.SubtractAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.SubtractExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.SuperKeyword] = null; + childrenWalkers[SyntaxKind.SwitchStatement] = walkSwitchStatementChildren; + childrenWalkers[SyntaxKind.ThisKeyword] = null; + childrenWalkers[SyntaxKind.ThrowStatement] = walkThrowStatementChildren; + childrenWalkers[SyntaxKind.TriviaList] = null; + childrenWalkers[SyntaxKind.TrueKeyword] = null; + childrenWalkers[SyntaxKind.TryStatement] = walkTryStatementChildren; + childrenWalkers[SyntaxKind.TypeAnnotation] = walkTypeAnnotationChildren; + childrenWalkers[SyntaxKind.TypeArgumentList] = walkTypeArgumentListChildren; + childrenWalkers[SyntaxKind.TypeOfExpression] = walkTypeOfExpressionChildren; + childrenWalkers[SyntaxKind.TypeParameter] = walkTypeParameterChildren; + childrenWalkers[SyntaxKind.TypeParameterList] = walkTypeParameterListChildren; + childrenWalkers[SyntaxKind.TypeQuery] = walkTypeQueryChildren; + childrenWalkers[SyntaxKind.UnsignedRightShiftAssignmentExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.UnsignedRightShiftExpression] = walkBinaryExpressionChildren; + childrenWalkers[SyntaxKind.VariableDeclaration] = walkVariableDeclarationChildren; + childrenWalkers[SyntaxKind.VariableDeclarator] = walkVariableDeclaratorChildren; + childrenWalkers[SyntaxKind.VariableStatement] = walkVariableStatementChildren; + childrenWalkers[SyntaxKind.VoidExpression] = walkVoidExpressionChildren; + childrenWalkers[SyntaxKind.VoidKeyword] = null; + childrenWalkers[SyntaxKind.WhileStatement] = walkWhileStatementChildren; + childrenWalkers[SyntaxKind.WithStatement] = walkWithStatementChildren; + + // Verify the code is up to date with the enum + for (var e in SyntaxKind) { + if (SyntaxKind.hasOwnProperty(e) && StringUtilities.isString(SyntaxKind[e])) { + TypeScript.Debug.assert(childrenWalkers[e] !== undefined, "Fix initWalkers: " + SyntaxKind[e]); + } + } + + export class AstWalkOptions { + public goChildren = true; + public stopWalking = false; + } + + interface IAstWalkChildren { + (preAst: ISyntaxElement, walker: AstWalker): void; + } + + export interface IAstWalker { + options: AstWalkOptions; + state: any + } + + interface AstWalker { + walk(ast: ISyntaxElement): void; + } + + class SimplePreAstWalker implements AstWalker { + public options: AstWalkOptions = new AstWalkOptions(); + + constructor( + private pre: (ast: ISyntaxElement, state: any) => void, + public state: any) { + } + + public walk(ast: ISyntaxElement): void { + if (!ast) { + return; + } + + this.pre(ast, this.state); + + var walker = childrenWalkers[ast.kind()]; + if (walker) { + walker(ast, this); + } + } + } + + class SimplePrePostAstWalker implements AstWalker { + public options: AstWalkOptions = new AstWalkOptions(); + + constructor( + private pre: (ast: ISyntaxElement, state: any) => void, + private post: (ast: ISyntaxElement, state: any) => void, + public state: any) { + } + + public walk(ast: ISyntaxElement): void { + if (!ast) { + return; + } + + this.pre(ast, this.state); + + var walker = childrenWalkers[ast.kind()]; + if (walker) { + walker(ast, this); + } + + this.post(ast, this.state); + } + } + + class NormalAstWalker implements AstWalker { + public options: AstWalkOptions = new AstWalkOptions(); + + constructor( + private pre: (ast: ISyntaxElement, walker: IAstWalker) => void, + private post: (ast: ISyntaxElement, walker: IAstWalker) => void, + public state: any) { + } + + public walk(ast: ISyntaxElement): void { + if (!ast) { + return; + } + + // If we're stopping, then bail out immediately. + if (this.options.stopWalking) { + return; + } + + this.pre(ast, this); + + // If we were asked to stop, then stop. + if (this.options.stopWalking) { + return; + } + + if (this.options.goChildren) { + // Call the "walkChildren" function corresponding to "nodeType". + var walker = childrenWalkers[ast.kind()]; + if (walker) { + walker(ast, this); + } + } + else { + // no go only applies to children of node issuing it + this.options.goChildren = true; + } + + if (this.post) { + this.post(ast, this); + } + } + } + + export class AstWalkerFactory { + public walk(ast: ISyntaxElement, pre: (ast: ISyntaxElement, walker: IAstWalker) => void, post?: (ast: ISyntaxElement, walker: IAstWalker) => void, state?: any): void { + new NormalAstWalker(pre, post, state).walk(ast); + } + + public simpleWalk(ast: ISyntaxElement, pre: (ast: ISyntaxElement, state: any) => void, post?: (ast: ISyntaxElement, state: any) => void, state?: any): void { + if (post) { + new SimplePrePostAstWalker(pre, post, state).walk(ast); + } + else { + new SimplePreAstWalker(pre, state).walk(ast); + } + } + } + + var globalAstWalkerFactory = new AstWalkerFactory(); + + export function getAstWalkerFactory(): AstWalkerFactory { + return globalAstWalkerFactory; + } +} \ No newline at end of file diff --git a/src/services/compiler/bloomFilter.ts b/src/services/compiler/bloomFilter.ts new file mode 100644 index 00000000000..44d72bf5bfa --- /dev/null +++ b/src/services/compiler/bloomFilter.ts @@ -0,0 +1,131 @@ +/// + +module TypeScript { + + export class BloomFilter { + private bitArray: boolean[]; + private hashFunctionCount: number; + + public static falsePositiveProbability: number = 0.0001; + + /* + * From the bloom filter calculator here: http://hur.st/bloomfilter?n=4&p=1.0E-20 + * + * 1) n = Number of items in the filter + * + * 2) p = Probability of false positives, (a double between 0 and 1). + * + * 3) m = Number of bits in the filter + * + * 4) k = Number of hash functions + * + * m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0))))) + * + * k = round(log(2.0) * m / n) + * + */ + constructor(expectedCount: number) { + var m: number = Math.max(1, BloomFilter.computeM(expectedCount)); + var k: number = Math.max(1, BloomFilter.computeK(expectedCount));; + + // We must have size in even bytes, so that when we deserialize from bytes we get a bit array with the same count. + // The count is used by the hash functions. + var sizeInEvenBytes = (m + 7) & ~7; + + this.bitArray = []; + for (var i = 0, len = sizeInEvenBytes; i < len; i++) { + this.bitArray[i] = false; + } + this.hashFunctionCount = k; + } + + // m = ceil((n * log(p)) / log(1.0 / (pow(2.0, log(2.0))))) + static computeM(expectedCount: number): number { + var p: number = BloomFilter.falsePositiveProbability; + var n: number = expectedCount; + + var numerator = n * Math.log(p); + var denominator = Math.log(1.0 / Math.pow(2.0, Math.log(2.0))); + return Math.ceil(numerator / denominator); + } + + // k = round(log(2.0) * m / n) + static computeK(expectedCount: number): number { + var n: number = expectedCount; + var m: number = BloomFilter.computeM(expectedCount); + + var temp = Math.log(2.0) * m / n; + return Math.round(temp); + } + + /** Modification of the murmurhash2 algorithm. Code is simpler because it operates over + * strings instead of byte arrays. Because each string character is two bytes, it is known + * that the input will be an even number of bytes (though not necessarily a multiple of 4). + * + * This is needed over the normal 'string.GetHashCode()' because we need to be able to generate + * 'k' different well distributed hashes for any given string s. Also, we want to be able to + * generate these hashes without allocating any memory. My ideal solution would be to use an + * MD5 hash. However, there appears to be no way to do MD5 in .Net where you can: + * + * a) feed it individual values instead of a byte[] + * + * b) have the hash computed into a byte[] you provide instead of a newly allocated one + * + * Generating 'k' pieces of garbage on each insert and lookup seems very wasteful. So, + * instead, we use murmur hash since it provides well distributed values, allows for a + * seed, and allocates no memory. + * + * Murmur hash is public domain. Actual code is included below as reference. + */ + private computeHash(key: string, seed: number): number { + return Hash.computeMurmur2StringHashCode(key, seed); + } + + public addKeys(keys: ts.Map) { + for (var name in keys) { + if (ts.lookUp(keys, name)) { + this.add(name); + } + } + } + + public add(value: string) { + for (var i = 0; i < this.hashFunctionCount; i++) { + var hash = this.computeHash(value, i); + hash = hash % this.bitArray.length; + this.bitArray[Math.abs(hash)] = true; + } + } + + public probablyContains(value: string): boolean { + for (var i = 0; i < this.hashFunctionCount; i++) { + var hash = this.computeHash(value, i); + hash = hash % this.bitArray.length; + if (!this.bitArray[Math.abs(hash)]) { + return false; + } + } + + return true; + } + + public isEquivalent(filter: BloomFilter): boolean { + return BloomFilter.isEquivalent(this.bitArray, filter.bitArray) + && this.hashFunctionCount === filter.hashFunctionCount; + } + + static isEquivalent(array1: boolean[], array2: boolean[]): boolean { + if (array1.length !== array2.length) { + return false; + } + + for (var i = 0; i < array1.length; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + + return true; + } + } +} diff --git a/src/services/compiler/declarationEmitter.ts b/src/services/compiler/declarationEmitter.ts new file mode 100644 index 00000000000..d3052389cb0 --- /dev/null +++ b/src/services/compiler/declarationEmitter.ts @@ -0,0 +1,1138 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export class TextWriter { + private contents = ""; + public onNewLine = true; + constructor(private name: string, private writeByteOrderMark: boolean, private outputFileType: OutputFileType) { + } + + public Write(s: string) { + this.contents += s; + this.onNewLine = false; + } + + public WriteLine(s: string) { + this.contents += s; + this.contents += TypeScript.newLine(); + this.onNewLine = true; + } + + public Close(): void { + } + + public getOutputFile(): OutputFile { + return new OutputFile(this.name, this.writeByteOrderMark, this.contents, this.outputFileType); + } + } + + export class DeclarationEmitter { + private declFile: TextWriter = null; + private indenter = new Indenter(); + private emittedReferencePaths = false; + + constructor(private emittingFileName: string, + public document: Document, + private compiler: TypeScriptCompiler, + private emitOptions: EmitOptions, + private semanticInfoChain: SemanticInfoChain) { + this.declFile = new TextWriter(emittingFileName, this.document.byteOrderMark !== ByteOrderMark.None, OutputFileType.Declaration); + } + + public getOutputFile(): OutputFile { + return this.declFile.getOutputFile(); + } + + public emitDeclarations(sourceUnit: SourceUnitSyntax) { + this.emitDeclarationsForSourceUnit(sourceUnit); + } + + private emitDeclarationsForList(list: ISyntaxNodeOrToken[]) { + for (var i = 0, n = list.length; i < n; i++) { + this.emitDeclarationsForAST(list[i]); + } + } + + private emitSeparatedList(list: ISyntaxNodeOrToken[]) { + for (var i = 0, n = list.length; i < n; i++) { + this.emitDeclarationsForAST(list[i]); + } + } + + private emitDeclarationsForAST(ast: ISyntaxElement) { + switch (ast.kind()) { + case SyntaxKind.VariableStatement: + return this.emitDeclarationsForVariableStatement(ast); + case SyntaxKind.PropertySignature: + return this.emitPropertySignature(ast); + case SyntaxKind.VariableDeclarator: + return this.emitVariableDeclarator(ast, true, true); + case SyntaxKind.MemberVariableDeclaration: + return this.emitDeclarationsForMemberVariableDeclaration(ast); + case SyntaxKind.ConstructorDeclaration: + return this.emitDeclarationsForConstructorDeclaration(ast); + case SyntaxKind.GetAccessor: + return this.emitDeclarationsForGetAccessor(ast); + case SyntaxKind.SetAccessor: + return this.emitDeclarationsForSetAccessor(ast); + case SyntaxKind.IndexMemberDeclaration: + return this.emitIndexMemberDeclaration(ast); + case SyntaxKind.IndexSignature: + return this.emitIndexSignature(ast); + case SyntaxKind.CallSignature: + return this.emitCallSignature(ast); + case SyntaxKind.ConstructSignature: + return this.emitConstructSignature(ast); + case SyntaxKind.MethodSignature: + return this.emitMethodSignature(ast); + case SyntaxKind.FunctionDeclaration: + return this.emitDeclarationsForFunctionDeclaration(ast); + case SyntaxKind.MemberFunctionDeclaration: + return this.emitMemberFunctionDeclaration(ast); + case SyntaxKind.ClassDeclaration: + return this.emitDeclarationsForClassDeclaration(ast); + case SyntaxKind.InterfaceDeclaration: + return this.emitDeclarationsForInterfaceDeclaration(ast); + case SyntaxKind.ImportDeclaration: + return this.emitDeclarationsForImportDeclaration(ast); + case SyntaxKind.ModuleDeclaration: + return this.emitDeclarationsForModuleDeclaration(ast); + case SyntaxKind.EnumDeclaration: + return this.emitDeclarationsForEnumDeclaration(ast); + case SyntaxKind.ExportAssignment: + return this.emitDeclarationsForExportAssignment(ast); + } + } + + private getIndentString(declIndent = false) { + return this.indenter.getIndent(); + } + + private emitIndent() { + this.declFile.Write(this.getIndentString()); + } + + private canEmitDeclarations(declAST: ISyntaxElement): boolean { + var container = DeclarationEmitter.getEnclosingContainer(declAST); + if (container.kind() === SyntaxKind.ModuleDeclaration || container.kind() === SyntaxKind.SourceUnit) { + var pullDecl = this.semanticInfoChain.getDeclForAST(declAST); + if (!hasFlag(pullDecl.flags, PullElementFlags.Exported)) { + var start = new Date().getTime(); + var declSymbol = this.semanticInfoChain.getSymbolForAST(declAST); + var result = declSymbol && declSymbol.isExternallyVisible(); + TypeScript.declarationEmitIsExternallyVisibleTime += new Date().getTime() - start; + + return result; + } + } + + return true; + } + + private getDeclFlagsString(pullDecl: PullDecl, typeString: string) { + var result = this.getIndentString(); + var pullFlags = pullDecl.flags; + + // Static/public/private/global declare + if (hasFlag(pullFlags, PullElementFlags.Static)) { + if (hasFlag(pullFlags, PullElementFlags.Private)) { + result += "private "; + } + result += "static "; + } + else { + if (hasFlag(pullFlags, PullElementFlags.Private)) { + result += "private "; + } + else if (hasFlag(pullFlags, PullElementFlags.Public)) { + result += "public "; + } + else { + var emitDeclare = !hasFlag(pullFlags, PullElementFlags.Exported); + + var declAST = this.semanticInfoChain.getASTForDecl(pullDecl); + var container = DeclarationEmitter.getEnclosingContainer(declAST); + + var isExternalModule = container.kind() === SyntaxKind.SourceUnit && this.document.syntaxTree().isExternalModule(); + + // Emit export only for global export statements. + // The container for this would be dynamic module which is whole file + if (isExternalModule && hasFlag(pullFlags, PullElementFlags.Exported)) { + result += "export "; + emitDeclare = true; + } + + // Emit declare only in global context + if (isExternalModule || container.kind() === SyntaxKind.SourceUnit) { + // Emit declare if not interface declaration or import declaration && is not from module + if (emitDeclare && typeString !== "interface" && typeString !== "import") { + result += "declare "; + } + } + + result += typeString + " "; + } + } + + return result; + } + + private emitDeclFlags(declarationAST: ISyntaxElement, typeString: string) { + this.declFile.Write(this.getDeclFlagsString(this.semanticInfoChain.getDeclForAST(declarationAST), typeString)); + } + + private emitTypeNamesMember(memberName: MemberName, emitIndent: boolean = false) { + if (memberName.prefix === "{ ") { + if (emitIndent) { + this.emitIndent(); + } + + this.declFile.WriteLine("{"); + this.indenter.increaseIndent(); + emitIndent = true; + } + else if (memberName.prefix !== "") { + if (emitIndent) { + this.emitIndent(); + } + + this.declFile.Write(memberName.prefix); + emitIndent = false; + } + + if (memberName.isString()) { + if (emitIndent) { + this.emitIndent(); + } + + this.declFile.Write((memberName).text); + } + else if (memberName.isArray()) { + var ar = memberName; + for (var index = 0; index < ar.entries.length; index++) { + this.emitTypeNamesMember(ar.entries[index], emitIndent); + if (ar.delim === "; ") { + this.declFile.WriteLine(";"); + } + } + } + + if (memberName.suffix === "}") { + this.indenter.decreaseIndent(); + this.emitIndent(); + this.declFile.Write(memberName.suffix); + } + else { + this.declFile.Write(memberName.suffix); + } + } + + private emitTypeSignature(ast: ISyntaxElement, type: PullTypeSymbol) { + var declarationContainerAst = DeclarationEmitter.getEnclosingContainer(ast); + + var start = new Date().getTime(); + var declarationContainerDecl = this.semanticInfoChain.getDeclForAST(declarationContainerAst); + + var declarationPullSymbol = declarationContainerDecl.getSymbol(this.semanticInfoChain); + TypeScript.declarationEmitTypeSignatureTime += new Date().getTime() - start; + + var isNotAGenericType = ast.kind() !== SyntaxKind.GenericType; + + var typeNameMembers = type.getScopedNameEx( + declarationPullSymbol, + /*skipTypeParametersInName?*/ false, + /*useConstraintInName?*/ false, + /*getPrettyTypeName?*/ false, + /*getTypeParamMarkerInfo?*/ false, + /*skipInternalAliasName?*/ false, + /*shouldAllowArrayType:*/ isNotAGenericType); + this.emitTypeNamesMember(typeNameMembers); + } + + private emitComment(comment: Comment) { + var text = getTrimmedTextLines(comment); + if (this.declFile.onNewLine) { + this.emitIndent(); + } + else if (comment.kind() !== SyntaxKind.MultiLineCommentTrivia) { + this.declFile.WriteLine(""); + this.emitIndent(); + } + + this.declFile.Write(text[0]); + + for (var i = 1; i < text.length; i++) { + this.declFile.WriteLine(""); + this.emitIndent(); + this.declFile.Write(text[i]); + } + + if (comment.endsLine || comment.kind() !== SyntaxKind.MultiLineCommentTrivia) { + this.declFile.WriteLine(""); + } + else { + this.declFile.Write(" "); + } + } + + private text() { + return this.document.syntaxTree().text; + } + + private emitDeclarationComments(ast: ISyntaxElement, endLine?: boolean): void; + private emitDeclarationComments(astOrSymbol: any, endLine = true) { + if (this.emitOptions.compilationSettings().removeComments()) { + return; + } + + var declComments: Comment[] = astOrSymbol.docComments ? astOrSymbol.docComments() : ASTHelpers.docComments(astOrSymbol, this.text()); + this.writeDeclarationComments(declComments, endLine); + } + + private writeDeclarationComments(declComments: Comment[], endLine = true) { + if (declComments) { + var wroteComment = false; + for (var i = 0; i < declComments.length; i++) { + if (ASTHelpers.isDocComment(declComments[i])) { + this.emitComment(declComments[i]); + wroteComment = true; + } + } + + if (wroteComment) { + if (endLine) { + if (!this.declFile.onNewLine) { + this.declFile.WriteLine(""); + } + } + else { + if (this.declFile.onNewLine) { + this.emitIndent(); + } + } + } + } + } + + private emitTypeOfVariableDeclaratorOrParameter(boundDecl: ISyntaxElement) { + var start = new Date().getTime(); + var decl = this.semanticInfoChain.getDeclForAST(boundDecl); + var pullSymbol = decl.getSymbol(this.semanticInfoChain); + TypeScript.declarationEmitGetBoundDeclTypeTime += new Date().getTime() - start; + + var type = pullSymbol.type; + Debug.assert(type); + + this.declFile.Write(": "); + this.emitTypeSignature(boundDecl, type); + } + + private emitPropertySignature(varDecl: PropertySignatureSyntax): void { + this.emitDeclarationComments(varDecl); + this.emitIndent(); + this.declFile.Write(varDecl.propertyName.text()); + if (varDecl.questionToken) { + this.declFile.Write("?"); + } + + this.emitTypeOfVariableDeclaratorOrParameter(varDecl); + + this.declFile.WriteLine(";"); + } + + private emitVariableDeclarator(varDecl: VariableDeclaratorSyntax, isFirstVarInList: boolean, isLastVarInList: boolean) { + if (this.canEmitDeclarations(varDecl)) { + this.emitDeclarationComments(varDecl); + // If it is var list of form var a, b, c = emit it only if count > 0 - which will be when emitting first var + // If it is var list of form var a = varList count will be 0 + if (isFirstVarInList) { + this.emitDeclFlags(varDecl, "var"); + } + + this.declFile.Write(varDecl.propertyName.text()); + + if (!hasModifier(ASTHelpers.getVariableDeclaratorModifiers(varDecl), PullElementFlags.Private)) { + this.emitTypeOfVariableDeclaratorOrParameter(varDecl); + } + + // Write ; or , + if (isLastVarInList) { + this.declFile.WriteLine(";"); + } + else { + this.declFile.Write(", "); + } + } + } + + private emitClassElementModifiers(modifiers: ISyntaxToken[]): void { + if (hasModifier(modifiers, PullElementFlags.Static)) { + if (hasModifier(modifiers, PullElementFlags.Private)) { + this.declFile.Write("private "); + } + this.declFile.Write("static "); + } + else { + if (hasModifier(modifiers, PullElementFlags.Private)) { + this.declFile.Write("private "); + } + else { + this.declFile.Write("public "); + } + } + } + + private emitDeclarationsForMemberVariableDeclaration(varDecl: MemberVariableDeclarationSyntax) { + if (this.canEmitDeclarations(varDecl)) { + this.emitDeclarationComments(varDecl); + + this.declFile.Write(this.getIndentString()); + this.emitClassElementModifiers(varDecl.modifiers);; + + this.declFile.Write(varDecl.variableDeclarator.propertyName.text()); + + if (!hasModifier(varDecl.modifiers, PullElementFlags.Private)) { + this.emitTypeOfVariableDeclaratorOrParameter(varDecl); + } + + this.declFile.WriteLine(";"); + } + } + + private emitDeclarationsForVariableStatement(variableStatement: VariableStatementSyntax) { + this.emitDeclarationsForVariableDeclaration(variableStatement.variableDeclaration); + } + + private emitDeclarationsForVariableDeclaration(variableDeclaration: VariableDeclarationSyntax) { + var varListCount = variableDeclaration.variableDeclarators.length; + for (var i = 0; i < varListCount; i++) { + this.emitVariableDeclarator(variableDeclaration.variableDeclarators[i], i === 0, i === varListCount - 1); + } + } + + private parameterIsOptional(parameter: ParameterSyntax) { + return parameter.questionToken !== null || parameter.equalsValueClause !== null; + } + + private emitParameter(argDecl: ParameterSyntax, isPrivate: boolean) { + this.indenter.increaseIndent(); + + this.emitDeclarationComments(argDecl, false); + this.declFile.Write(argDecl.identifier.text()); + if (this.parameterIsOptional(argDecl)) { + this.declFile.Write("?"); + } + + this.indenter.decreaseIndent(); + + if (!isPrivate) { + this.emitTypeOfVariableDeclaratorOrParameter(argDecl); + } + } + + private isOverloadedCallSignature(funcDecl: ISyntaxElement) { + var start = new Date().getTime(); + var functionDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + var funcSymbol = functionDecl.getSymbol(this.semanticInfoChain); + TypeScript.declarationEmitIsOverloadedCallSignatureTime += new Date().getTime() - start; + + var funcTypeSymbol = funcSymbol.type; + var signatures = funcTypeSymbol.getCallSignatures(); + var result = signatures && signatures.length > 1; + + return result; + } + + private emitDeclarationsForConstructorDeclaration(funcDecl: ConstructorDeclarationSyntax) { + var start = new Date().getTime(); + var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl); + + TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start; + + var funcTypeSymbol = funcSymbol.type; + if (funcDecl.block) { + var constructSignatures = funcTypeSymbol.getConstructSignatures(); + if (constructSignatures && constructSignatures.length > 1) { + return; + } + //else if (this.isOverloadedCallSignature(funcDecl)) { + // // This means its implementation of overload signature. do not emit + // return; + //} + } + + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + this.emitDeclarationComments(funcDecl); + + this.emitIndent(); + this.declFile.Write("constructor"); + + this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList); + + this.declFile.WriteLine(";"); + } + + private emitParameterList(isPrivate: boolean, parameterList: ParameterListSyntax): void { + this.declFile.Write("("); + this.emitParameters(isPrivate, parameterList.parameters); + this.declFile.Write(")"); + } + + private emitParameters(isPrivate: boolean, parameterList: ParameterSyntax[]): void { + var hasLastParameterRestParameter = lastParameterIsRest(parameterList); + var argsLen = parameterList.length; + if (hasLastParameterRestParameter) { + argsLen--; + } + + for (var i = 0; i < argsLen; i++) { + this.emitParameter(parameterList[i], isPrivate); + if (i < (argsLen - 1)) { + this.declFile.Write(", "); + } + } + + if (hasLastParameterRestParameter) { + if (parameterList.length > 1) { + this.declFile.Write(", ..."); + } + else { + this.declFile.Write("..."); + } + + var index = parameterList.length - 1; + this.emitParameter(parameterList[index], isPrivate); + } + } + + private emitMemberFunctionDeclaration(funcDecl: MemberFunctionDeclarationSyntax) { + var start = new Date().getTime(); + var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl); + + TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start; + + var funcTypeSymbol = funcSymbol.type; + if (funcDecl.block) { + var constructSignatures = funcTypeSymbol.getConstructSignatures(); + if (constructSignatures && constructSignatures.length > 1) { + return; + } + else if (this.isOverloadedCallSignature(funcDecl)) { + // This means its implementation of overload signature. do not emit + return; + } + } + else if (hasModifier(funcDecl.modifiers, PullElementFlags.Private) && this.isOverloadedCallSignature(funcDecl)) { + // Print only first overload of private function + var callSignatures = funcTypeSymbol.getCallSignatures(); + Debug.assert(callSignatures && callSignatures.length > 1); + var firstSignature = callSignatures[0].isDefinition() ? callSignatures[1] : callSignatures[0]; + var firstSignatureDecl = firstSignature.getDeclarations()[0]; + var firstFuncDecl = this.semanticInfoChain.getASTForDecl(firstSignatureDecl); + if (firstFuncDecl !== funcDecl) { + return; + } + } + + if (!this.canEmitDeclarations(funcDecl)) { + return; + } + + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + this.emitDeclarationComments(funcDecl); + + this.emitDeclFlags(funcDecl, "function"); + var id = funcDecl.propertyName.text(); + this.declFile.Write(id); + this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature); + + var isPrivate = hasModifier(funcDecl.modifiers, PullElementFlags.Private); + + this.emitParameterList(isPrivate, funcDecl.callSignature.parameterList); + + if (!isPrivate) { + var returnType = funcSignature.returnType; + this.declFile.Write(": "); + this.emitTypeSignature(funcDecl, returnType); + } + + this.declFile.WriteLine(";"); + } + + private emitCallSignature(funcDecl: CallSignatureSyntax): void { + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + + this.emitDeclarationComments(funcDecl); + + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + this.emitTypeParameters(funcDecl.typeParameterList, funcSignature); + + this.emitIndent(); + + this.emitParameterList(/*isPrivate:*/ false, funcDecl.parameterList); + + var returnType = funcSignature.returnType; + this.declFile.Write(": "); + if (returnType) { + this.emitTypeSignature(funcDecl, returnType); + } + else { + this.declFile.Write("any"); + } + + this.declFile.WriteLine(";"); + } + + private emitConstructSignature(funcDecl: ConstructSignatureSyntax) { + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + + var start = new Date().getTime(); + var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl); + + TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start; + + this.emitDeclarationComments(funcDecl); + + this.emitIndent(); + this.declFile.Write("new"); + + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature); + + this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList); + + var returnType = funcSignature.returnType; + this.declFile.Write(": "); + if (returnType) { + this.emitTypeSignature(funcDecl, returnType); + } + else { + this.declFile.Write("any"); + } + + this.declFile.WriteLine(";"); + } + + private emitMethodSignature(funcDecl: MethodSignatureSyntax) { + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + + var start = new Date().getTime(); + var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl); + + TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start; + + this.emitDeclarationComments(funcDecl); + + this.emitIndent(); + this.declFile.Write(funcDecl.propertyName.text()); + if (funcDecl.questionToken) { + this.declFile.Write("? "); + } + + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature); + + this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList); + + var returnType = funcSignature.returnType; + this.declFile.Write(": "); + if (returnType) { + this.emitTypeSignature(funcDecl, returnType); + } + else { + this.declFile.Write("any"); + } + + this.declFile.WriteLine(";"); + } + + private emitDeclarationsForFunctionDeclaration(funcDecl: FunctionDeclarationSyntax) { + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + + var start = new Date().getTime(); + var funcSymbol = this.semanticInfoChain.getSymbolForAST(funcDecl); + + TypeScript.declarationEmitFunctionDeclarationGetSymbolTime += new Date().getTime() - start; + + if (funcDecl.block) { + var funcTypeSymbol = funcSymbol.type; + var constructSignatures = funcTypeSymbol.getConstructSignatures(); + if (constructSignatures && constructSignatures.length > 1) { + return; + } + else if (this.isOverloadedCallSignature(funcDecl)) { + // This means its implementation of overload signature. do not emit + return; + } + } + + if (!this.canEmitDeclarations(funcDecl)) { + return; + } + + this.emitDeclarationComments(funcDecl); + + var id = funcDecl.identifier.text(); + this.emitDeclFlags(funcDecl, "function"); + if (id !== "" || !funcDecl.identifier || funcDecl.identifier.text().length > 0) { + this.declFile.Write(id); + } + else if (funcPullDecl.kind === PullElementKind.ConstructSignature) { + this.declFile.Write("new"); + } + + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + this.emitTypeParameters(funcDecl.callSignature.typeParameterList, funcSignature); + + this.emitParameterList(/*isPrivate:*/ false, funcDecl.callSignature.parameterList); + + var returnType = funcSignature.returnType; + this.declFile.Write(": "); + if (returnType) { + this.emitTypeSignature(funcDecl, returnType); + } + else { + this.declFile.Write("any"); + } + + this.declFile.WriteLine(";"); + } + + private emitIndexMemberDeclaration(funcDecl: IndexMemberDeclarationSyntax) { + this.emitDeclarationsForAST(funcDecl.indexSignature); + } + + private emitIndexSignature(funcDecl: IndexSignatureSyntax) { + if (!this.canEmitDeclarations(funcDecl)) { + return; + } + + this.emitDeclarationComments(funcDecl); + + this.emitIndent(); + this.declFile.Write("["); + this.emitParameters(/*isPrivate:*/ false, funcDecl.parameters); + this.declFile.Write("]"); + + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + var funcSignature = funcPullDecl.getSignatureSymbol(this.semanticInfoChain); + var returnType = funcSignature.returnType; + this.declFile.Write(": "); + this.emitTypeSignature(funcDecl, returnType); + + this.declFile.WriteLine(";"); + } + + private emitBaseList(bases: INameSyntax[], useExtendsList: boolean) { + if (bases && (bases.length > 0)) { + var qual = useExtendsList ? "extends" : "implements"; + this.declFile.Write(" " + qual + " "); + var basesLen = bases.length; + for (var i = 0; i < basesLen; i++) { + if (i > 0) { + this.declFile.Write(", "); + } + var base = bases[i]; + var baseType = this.semanticInfoChain.getSymbolForAST(base); + this.emitTypeSignature(base, baseType); + } + } + } + + private emitAccessorDeclarationComments(funcDecl: ISyntaxElement) { + if (this.emitOptions.compilationSettings().removeComments()) { + return; + } + + var start = new Date().getTime(); + var accessors = PullHelpers.getGetterAndSetterFunction(funcDecl, this.semanticInfoChain); + TypeScript.declarationEmitGetAccessorFunctionTime += new Date().getTime(); + + var comments: Comment[] = []; + if (accessors.getter) { + comments = comments.concat(ASTHelpers.docComments(accessors.getter, this.text())); + } + if (accessors.setter) { + comments = comments.concat(ASTHelpers.docComments(accessors.setter, this.text())); + } + + this.writeDeclarationComments(comments); + } + + private emitDeclarationsForGetAccessor(funcDecl: GetAccessorSyntax): void { + this.emitMemberAccessorDeclaration(funcDecl, funcDecl.modifiers, funcDecl.propertyName); + } + + private emitDeclarationsForSetAccessor(funcDecl: SetAccessorSyntax): void { + this.emitMemberAccessorDeclaration(funcDecl, funcDecl.modifiers, funcDecl.propertyName); + } + + private emitMemberAccessorDeclaration(funcDecl: ISyntaxElement, modifiers: ISyntaxToken[], name: ISyntaxToken) { + var start = new Date().getTime(); + var accessorSymbol = PullHelpers.getAccessorSymbol(funcDecl, this.semanticInfoChain); + TypeScript.declarationEmitGetAccessorFunctionTime += new Date().getTime(); + + if (funcDecl.kind() === SyntaxKind.SetAccessor && accessorSymbol.getGetter()) { + // Setter is being used to emit the type info. + return; + } + + var isPrivate = hasModifier(modifiers, PullElementFlags.Private); + this.emitAccessorDeclarationComments(funcDecl); + this.declFile.Write(this.getIndentString()); + this.emitClassElementModifiers(modifiers); + this.declFile.Write(name.text()); + if (!isPrivate) { + this.declFile.Write(" : "); + var type = accessorSymbol.type; + this.emitTypeSignature(funcDecl, type); + } + this.declFile.WriteLine(";"); + } + + private emitClassMembersFromConstructorDefinition(funcDecl: ConstructorDeclarationSyntax) { + var argsLen = funcDecl.callSignature.parameterList.parameters.length; + if (lastParameterIsRest(funcDecl.callSignature.parameterList.parameters)) { + argsLen--; + } + + for (var i = 0; i < argsLen; i++) { + var parameter = funcDecl.callSignature.parameterList.parameters[i]; + var parameterDecl = this.semanticInfoChain.getDeclForAST(parameter); + if (hasFlag(parameterDecl.flags, PullElementFlags.PropertyParameter)) { + var funcPullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + this.emitDeclarationComments(parameter); + this.declFile.Write(this.getIndentString()); + this.emitClassElementModifiers(parameter.modifiers); + this.declFile.Write(parameter.identifier.text()); + + if (!hasModifier(parameter.modifiers, PullElementFlags.Private)) { + this.emitTypeOfVariableDeclaratorOrParameter(parameter); + } + this.declFile.WriteLine(";"); + } + } + } + + private emitDeclarationsForClassDeclaration(classDecl: ClassDeclarationSyntax) { + if (!this.canEmitDeclarations(classDecl)) { + return; + } + + var className = classDecl.identifier.text(); + this.emitDeclarationComments(classDecl); + var classPullDecl = this.semanticInfoChain.getDeclForAST(classDecl); + this.emitDeclFlags(classDecl, "class"); + this.declFile.Write(className); + + this.emitTypeParameters(classDecl.typeParameterList); + this.emitHeritageClauses(classDecl.heritageClauses); + this.declFile.WriteLine(" {"); + + this.indenter.increaseIndent(); + var constructorDecl = getLastConstructor(classDecl); + if (constructorDecl) { + this.emitClassMembersFromConstructorDefinition(constructorDecl); + } + + this.emitDeclarationsForList(classDecl.classElements); + + this.indenter.decreaseIndent(); + + this.emitIndent(); + this.declFile.WriteLine("}"); + } + + private emitHeritageClauses(clauses: HeritageClauseSyntax[]): void { + if (clauses) { + for (var i = 0, n = clauses.length; i < n; i++) { + this.emitHeritageClause(clauses[i]); + } + } + } + + private emitHeritageClause(clause: HeritageClauseSyntax) { + this.emitBaseList(clause.typeNames, clause.kind() === SyntaxKind.ExtendsHeritageClause); + } + + static getEnclosingContainer(ast: ISyntaxElement): ISyntaxElement { + // If the passed in as is the 'name' portion of an module declaration. + // If so, we want the actual container of *that* module declaration. + var enclosingModule = ASTHelpers.getModuleDeclarationFromNameAST(ast); + ast = enclosingModule || ast; + + ast = ast.parent; + while (ast) { + if (ast.kind() === SyntaxKind.ClassDeclaration || + ast.kind() === SyntaxKind.InterfaceDeclaration || + ast.kind() === SyntaxKind.ModuleDeclaration || + ast.kind() === SyntaxKind.SourceUnit) { + + return ast; + } + + ast = ast.parent; + } + + return null; + } + + private emitTypeParameters(typeParams: TypeParameterListSyntax, funcSignature?: PullSignatureSymbol) { + if (!typeParams || !typeParams.typeParameters.length) { + return; + } + + this.declFile.Write("<"); + var containerAst = DeclarationEmitter.getEnclosingContainer(typeParams); + + var start = new Date().getTime(); + var containerDecl = this.semanticInfoChain.getDeclForAST(containerAst); + var containerSymbol = containerDecl.getSymbol(this.semanticInfoChain); + TypeScript.declarationEmitGetTypeParameterSymbolTime += new Date().getTime() - start; + + var typars: PullTypeSymbol[]; + if (funcSignature) { + typars = funcSignature.getTypeParameters(); + } + else { + typars = containerSymbol.getTypeArgumentsOrTypeParameters(); + } + + for (var i = 0; i < typars.length; i++) { + if (i) { + this.declFile.Write(", "); + } + + var memberName = typars[i].getScopedNameEx(containerSymbol, /*skipTypeParametersInName*/ false, /*useConstraintInName:*/ true); + this.emitTypeNamesMember(memberName); + } + + this.declFile.Write(">"); + } + + private emitDeclarationsForInterfaceDeclaration(interfaceDecl: InterfaceDeclarationSyntax) { + if (!this.canEmitDeclarations(interfaceDecl)) { + return; + } + + var interfaceName = interfaceDecl.identifier.text(); + this.emitDeclarationComments(interfaceDecl); + var interfacePullDecl = this.semanticInfoChain.getDeclForAST(interfaceDecl); + this.emitDeclFlags(interfaceDecl, "interface"); + this.declFile.Write(interfaceName); + + this.emitTypeParameters(interfaceDecl.typeParameterList); + this.emitHeritageClauses(interfaceDecl.heritageClauses); + this.declFile.WriteLine(" {"); + + this.indenter.increaseIndent(); + + this.emitSeparatedList(interfaceDecl.body.typeMembers); + + this.indenter.decreaseIndent(); + + this.emitIndent(); + this.declFile.WriteLine("}"); + } + + private emitDeclarationsForImportDeclaration(importDeclAST: ImportDeclarationSyntax) { + var importDecl = this.semanticInfoChain.getDeclForAST(importDeclAST); + var importSymbol = importDecl.getSymbol(this.semanticInfoChain); + var isExportedImportDecl = hasModifier(importDeclAST.modifiers, PullElementFlags.Exported); + + if (isExportedImportDecl || importSymbol.typeUsedExternally() || PullContainerSymbol.usedAsSymbol(importSymbol.getContainer(), importSymbol)) { + this.emitDeclarationComments(importDeclAST); + this.emitIndent(); + if (isExportedImportDecl) { + this.declFile.Write("export "); + } + this.declFile.Write("import "); + this.declFile.Write(importDeclAST.identifier.text() + " = "); + if (importDeclAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference) { + this.declFile.WriteLine("require(" + (importDeclAST.moduleReference).stringLiteral.text() + ");"); + } + else { + this.declFile.WriteLine(ASTHelpers.getNameOfIdenfierOrQualifiedName((importDeclAST.moduleReference).moduleName) + ";"); + } + } + } + + private emitDeclarationsForEnumDeclaration(moduleDecl: EnumDeclarationSyntax): void { + if (!this.canEmitDeclarations(moduleDecl)) { + return; + } + + this.emitDeclarationComments(moduleDecl); + var modulePullDecl = this.semanticInfoChain.getDeclForAST(moduleDecl); + this.emitDeclFlags(moduleDecl, "enum"); + this.declFile.WriteLine(moduleDecl.identifier.text() + " {"); + + this.indenter.increaseIndent(); + var membersLen = moduleDecl.enumElements.length; + for (var j = 0; j < membersLen; j++) { + var enumElement = moduleDecl.enumElements[j]; + var enumElementDecl = this.semanticInfoChain.getDeclForAST(enumElement); + this.emitDeclarationComments(enumElement); + this.emitIndent(); + this.declFile.Write(enumElement.propertyName.text()); + if (enumElementDecl.constantValue !== null) { + this.declFile.Write(" = " + enumElementDecl.constantValue); + } + this.declFile.WriteLine(","); + } + this.indenter.decreaseIndent(); + + this.emitIndent(); + this.declFile.WriteLine("}"); + } + + private emitDeclarationsForModuleDeclaration(moduleDecl: ModuleDeclarationSyntax) { + // If module is defined as A.B.C + // The whole moduleDecl will have the pullDecl corresponding to innermost + // Which would always be exported and hence would need to be emitted + // But we really want to check if module A needs to be emitted and hence use + // the leftmost name to determine if this needs to be emitted + // Since the module name will have the correct decl, it is always used to determine + // if this ast needs to be emitted or not + var name: ISyntaxElement = moduleDecl.stringLiteral || ArrayUtilities.first(ASTHelpers.getModuleNames(moduleDecl.name)); + if (!this.canEmitDeclarations(name)) { + return; + } + + this.emitDeclarationComments(moduleDecl); + this.emitDeclFlags(name, "module"); + + if (moduleDecl.stringLiteral) { + this.declFile.Write(moduleDecl.stringLiteral.text()); + } + else { + this.declFile.Write(ASTHelpers.getNameOfIdenfierOrQualifiedName(moduleDecl.name)); + } + + this.declFile.WriteLine(" {"); + this.indenter.increaseIndent(); + + this.emitDeclarationsForList(moduleDecl.moduleElements); + + this.indenter.decreaseIndent(); + this.emitIndent(); + this.declFile.WriteLine("}"); + } + + private emitDeclarationsForExportAssignment(ast: ExportAssignmentSyntax) { + this.emitIndent(); + this.declFile.Write("export = "); + this.declFile.Write(ast.identifier.text()); + this.declFile.WriteLine(";"); + } + + private resolveScriptReference(document: Document, reference: string) { + if (!this.emitOptions.compilationSettings().noResolve() || isRooted(reference)) { + return reference; + } + + var documentDir = convertToDirectoryPath(switchToForwardSlashes(getRootFilePath(document.fileName))); + var resolvedReferencePath = this.emitOptions.resolvePath(documentDir + reference); + return resolvedReferencePath; + } + + private emitReferencePaths(sourceUnit: SourceUnitSyntax) { + // In case of shared handler we collect all the references and emit them + if (this.emittedReferencePaths) { + return; + } + + // Collect all the documents that need to be emitted as reference + var documents: Document[] = []; + if (this.document.emitToOwnOutputFile()) { + // Emit only from this file + var scriptReferences = this.document.referencedFiles; + var addedGlobalDocument = false; + for (var j = 0; j < scriptReferences.length; j++) { + var currentReference = this.resolveScriptReference(this.document, scriptReferences[j]); + var document = this.compiler.getDocument(currentReference); + // All the references that are not going to be part of same file + + if (document && + (document.emitToOwnOutputFile() || document.isDeclareFile() || !addedGlobalDocument)) { + + documents = documents.concat(document); + + if (!document.isDeclareFile() && document.syntaxTree().isExternalModule()) { + addedGlobalDocument = true; + } + } + } + } + else { + // Collect from all the references and emit + var fileNames = this.compiler.fileNames(); + for (var i = 0; i < fileNames.length; i++) { + var doc = this.compiler.getDocument(fileNames[i]); + if (!doc.isDeclareFile() && !doc.syntaxTree().isExternalModule()) { + // Check what references need to be added + var scriptReferences = doc.referencedFiles; + for (var j = 0; j < scriptReferences.length; j++) { + var currentReference = this.resolveScriptReference(doc, scriptReferences[j]); + var document = this.compiler.getDocument(currentReference); + // All the references that are not going to be part of same file + if (document && + (document.isDeclareFile() || document.syntaxTree().isExternalModule())) { + for (var k = 0; k < documents.length; k++) { + if (documents[k] === document) { + break; + } + } + + if (k === documents.length) { + documents = documents.concat(document); + } + } + } + } + } + } + + // Emit the references + var emittingFilePath = documents.length ? getRootFilePath(this.emittingFileName) : null; + for (var i = 0; i < documents.length; i++) { + var document = documents[i]; + var declFileName: string; + if (document.isDeclareFile()) { + declFileName = document.fileName; + } + else { + declFileName = this.compiler.mapOutputFileName(document, this.emitOptions, TypeScriptCompiler.mapToDTSFileName); + } + + // Get the relative path + declFileName = getRelativePathToFixedPath(emittingFilePath, declFileName, false); + this.declFile.WriteLine('/// '); + } + + this.emittedReferencePaths = true; + } + + private emitDeclarationsForSourceUnit(sourceUnit: SourceUnitSyntax) { + this.emitReferencePaths(sourceUnit); + this.emitDeclarationsForList(sourceUnit.moduleElements); + } + } +} \ No newline at end of file diff --git a/src/services/compiler/diagnostics.ts b/src/services/compiler/diagnostics.ts new file mode 100644 index 00000000000..d759c8bf014 --- /dev/null +++ b/src/services/compiler/diagnostics.ts @@ -0,0 +1,47 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export interface Logger { + information(): boolean; + debug(): boolean; + warning(): boolean; + error(): boolean; + fatal(): boolean; + log(s: string): void; + } + + export class NullLogger implements Logger { + public information(): boolean { return false; } + public debug(): boolean { return false; } + public warning(): boolean { return false; } + public error(): boolean { return false; } + public fatal(): boolean { return false; } + public log(s: string): void { + } + } + + export function timeFunction(logger: Logger, funcDescription: string, func: () => any): any { + var start = (new Date()).getTime(); + var result = func(); + var end = (new Date()).getTime(); + if (logger.information()) { + logger.log(funcDescription + " completed in " + (end - start) + " msec"); + } + return result; + } +} \ No newline at end of file diff --git a/src/services/compiler/emitter.ts b/src/services/compiler/emitter.ts new file mode 100644 index 00000000000..8de5e64dbba --- /dev/null +++ b/src/services/compiler/emitter.ts @@ -0,0 +1,3797 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export enum EmitContainer { + Prog, + Module, + DynamicModule, + Class, + Constructor, + Function, + Args, + Interface, + } + + export class EmitState { + public column: number; + public line: number; + public container: EmitContainer; + + constructor() { + this.column = 0; + this.line = 0; + this.container = EmitContainer.Prog; + } + } + + export class EmitOptions { + private _diagnostic: Diagnostic = null; + + private _settings: ImmutableCompilationSettings = null; + private _commonDirectoryPath = ""; + private _sharedOutputFile = ""; + private _sourceRootDirectory = ""; + private _sourceMapRootDirectory = ""; + private _outputDirectory = ""; + + public diagnostic(): Diagnostic { return this._diagnostic; } + + public commonDirectoryPath() { return this._commonDirectoryPath; } + public sharedOutputFile() { return this._sharedOutputFile; } + public sourceRootDirectory() { return this._sourceRootDirectory; } + public sourceMapRootDirectory() { return this._sourceMapRootDirectory; } + public outputDirectory() { return this._outputDirectory; } + + public compilationSettings() { return this._settings; } + + constructor(compiler: TypeScriptCompiler, public resolvePath: (path: string) => string) { + var settings = compiler.compilationSettings(); + this._settings = settings; + + // If the document is an external module, then report if the the user has not + // provided the right command line option. + if (settings.moduleGenTarget() === ModuleGenTarget.Unspecified) { + var fileNames = compiler.fileNames(); + for (var i = 0, n = fileNames.length; i < n; i++) { + var document = compiler.getDocument(fileNames[i]); + if (!document.isDeclareFile() && document.syntaxTree().isExternalModule()) { + var errorSpan = externalModuleIndicatorSpan(document.syntaxTree()); + this._diagnostic = new Diagnostic(document.fileName, document.lineMap(), errorSpan.start(), errorSpan.length(), + DiagnosticCode.Cannot_compile_external_modules_unless_the_module_flag_is_provided); + + return; + } + } + } + + if (!settings.mapSourceFiles()) { + // Error to specify --mapRoot or --sourceRoot without mapSourceFiles + if (settings.mapRoot()) { + if (settings.sourceRoot()) { + this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Options_mapRoot_and_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option, null); + return; + } + else { + this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option, null); + return; + } + } + else if (settings.sourceRoot()) { + this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option, null); + return; + } + } + + this._sourceMapRootDirectory = convertToDirectoryPath(switchToForwardSlashes(settings.mapRoot())); + this._sourceRootDirectory = convertToDirectoryPath(switchToForwardSlashes(settings.sourceRoot())); + + if (settings.outFileOption() || + settings.outDirOption() || + settings.mapRoot() || + settings.sourceRoot()) { + + if (settings.outFileOption()) { + this._sharedOutputFile = switchToForwardSlashes(resolvePath(settings.outFileOption())); + } + + if (settings.outDirOption()) { + this._outputDirectory = convertToDirectoryPath(switchToForwardSlashes(resolvePath(settings.outDirOption()))); + } + + // Parse the directory structure + if (this._outputDirectory || this._sourceMapRootDirectory || this.sourceRootDirectory) { + this.determineCommonDirectoryPath(compiler); + } + } + } + + private determineCommonDirectoryPath(compiler: TypeScriptCompiler): void { + var commonComponents: string[] = []; + var commonComponentsLength = -1; + + var fileNames = compiler.fileNames(); + for (var i = 0, len = fileNames.length; i < len; i++) { + var fileName = fileNames[i]; + var document = compiler.getDocument(fileNames[i]); + var sourceUnit = document.sourceUnit(); + + if (!document.isDeclareFile()) { + var fileComponents = filePathComponents(fileName); + if (commonComponentsLength === -1) { + // First time at finding common path + // So common path = directory of file + commonComponents = fileComponents; + commonComponentsLength = commonComponents.length; + } + else { + var updatedPath = false; + for (var j = 0; j < commonComponentsLength && j < fileComponents.length; j++) { + if (commonComponents[j] !== fileComponents[j]) { + // The new components = 0 ... j -1 + commonComponentsLength = j; + updatedPath = true; + + if (j === 0) { + var isDynamicModuleCompilation = ArrayUtilities.any(fileNames, fileName => { + document = compiler.getDocument(fileName); + return !document.isDeclareFile() && document.syntaxTree().isExternalModule(); + }); + + if (this._outputDirectory || // there is --outDir specified + this._sourceRootDirectory || // there is --sourceRoot specified + (this._sourceMapRootDirectory && // there is --map Specified and there would be multiple js files generated + (!this._sharedOutputFile || isDynamicModuleCompilation))) { + // Its error to not have common path + this._diagnostic = new Diagnostic(null, null, 0, 0, DiagnosticCode.Cannot_find_the_common_subdirectory_path_for_the_input_files, null); + return; + } + + return; + } + + break; + } + } + + // If the fileComponent path completely matched and less than already found update the length + if (!updatedPath && fileComponents.length < commonComponentsLength) { + commonComponentsLength = fileComponents.length; + } + } + } + } + + this._commonDirectoryPath = commonComponents.slice(0, commonComponentsLength).join("/") + "/"; + } + } + + export class Indenter { + static indentStep: number = 4; + static indentStepString: string = " "; + static indentStrings: string[] = []; + public indentAmt: number = 0; + + public increaseIndent() { + this.indentAmt += Indenter.indentStep; + } + + public decreaseIndent() { + this.indentAmt -= Indenter.indentStep; + } + + public getIndent() { + var indentString = Indenter.indentStrings[this.indentAmt]; + if (indentString === undefined) { + indentString = ""; + for (var i = 0; i < this.indentAmt; i = i + Indenter.indentStep) { + indentString += Indenter.indentStepString; + } + Indenter.indentStrings[this.indentAmt] = indentString; + } + return indentString; + } + } + + export function lastParameterIsRest(parameters: ParameterSyntax[]): boolean { + return parameters.length > 0 && (parameters[parameters.length - 1]).dotDotDotToken !== null; + } + + export class Emitter { + public globalThisCapturePrologueEmitted = false; + public extendsPrologueEmitted = false; + public thisClassNode: ClassDeclarationSyntax = null; + public inArrowFunction: boolean = false; + public moduleName = ""; + public emitState = new EmitState(); + public indenter = new Indenter(); + public sourceMapper: SourceMapper = null; + public captureThisStmtString = "var _this = this;"; + private currentVariableDeclaration: VariableDeclarationSyntax; + private declStack: PullDecl[] = []; + private exportAssignment: ExportAssignmentSyntax = null; + private inWithBlock = false; + + public document: Document = null; + + // If we choose to detach comments from an element (for example, the Copyright comments), + // then keep track of that element so that we don't emit all on the comments on it when + // we visit it. + private detachedCommentsElement: ISyntaxElement = null; + + constructor(public emittingFileName: string, + public outfile: TextWriter, + public emitOptions: EmitOptions, + private semanticInfoChain: SemanticInfoChain) { + } + + private pushDecl(decl: PullDecl) { + if (decl) { + this.declStack[this.declStack.length] = decl; + } + } + + private popDecl(decl: PullDecl) { + if (decl) { + this.declStack.length--; + } + } + + private getEnclosingDecl() { + var declStackLen = this.declStack.length; + var enclosingDecl = declStackLen > 0 ? this.declStack[declStackLen - 1] : null; + return enclosingDecl; + } + + public setExportAssignment(exportAssignment: ExportAssignmentSyntax) { + this.exportAssignment = exportAssignment; + } + + public getExportAssignment() { + return this.exportAssignment; + } + + public setDocument(document: Document) { + this.document = document; + } + + public shouldEmitImportDeclaration(importDeclAST: ImportDeclarationSyntax) { + var isExternalModuleReference = importDeclAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference; + var importDecl = this.semanticInfoChain.getDeclForAST(importDeclAST); + var isExported = hasFlag(importDecl.flags, PullElementFlags.Exported); + var isAmdCodeGen = this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous; + + // 1) Any internal reference needs to check if the emit can happen + // 2) External module reference with export modifier always needs to be emitted + // 3) commonjs needs the var declaration for the import declaration + if (isExternalModuleReference && !isExported && isAmdCodeGen) { + return false; + } + + var importSymbol = importDecl.getSymbol(this.semanticInfoChain); + if (importSymbol.isUsedAsValue()) { + return true; + } + + if (importDeclAST.moduleReference.kind() !== SyntaxKind.ExternalModuleReference) { + var canBeUsedExternally = isExported || importSymbol.typeUsedExternally() || importSymbol.isUsedInExportedAlias(); + if (!canBeUsedExternally && !this.document.syntaxTree().isExternalModule()) { + // top level import in non-external module are visible across the whole global module + canBeUsedExternally = hasFlag(importDecl.getParentDecl().kind, PullElementKind.Script | PullElementKind.DynamicModule); + } + + if (canBeUsedExternally) { + if (importSymbol.getExportAssignedValueSymbol()) { + return true; + } + + var containerSymbol = importSymbol.getExportAssignedContainerSymbol(); + if (containerSymbol && containerSymbol.getInstanceSymbol()) { + return true; + } + } + } + + return false; + } + + public emitImportDeclaration(importDeclAST: ImportDeclarationSyntax) { + var isExternalModuleReference = importDeclAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference; + var importDecl = this.semanticInfoChain.getDeclForAST(importDeclAST); + var isExported = hasFlag(importDecl.flags, PullElementFlags.Exported); + var isAmdCodeGen = this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous; + + this.emitComments(importDeclAST, true); + + var importSymbol = importDecl.getSymbol(this.semanticInfoChain); + + var parentSymbol = importSymbol.getContainer(); + var parentKind = parentSymbol ? parentSymbol.kind : PullElementKind.None; + var associatedParentSymbol = parentSymbol ? parentSymbol.getAssociatedContainerType() : null; + var associatedParentSymbolKind = associatedParentSymbol ? associatedParentSymbol.kind : PullElementKind.None; + + var needsPropertyAssignment = false; + var usePropertyAssignmentInsteadOfVarDecl = false; + var moduleNamePrefix: string; + + if (isExported && + (parentKind === PullElementKind.Container || + parentKind === PullElementKind.DynamicModule || + associatedParentSymbolKind === PullElementKind.Container || + associatedParentSymbolKind === PullElementKind.DynamicModule)) { + if (importSymbol.getExportAssignedTypeSymbol() || importSymbol.getExportAssignedContainerSymbol()) { + // Type or container assignment that is exported + needsPropertyAssignment = true; + } + else { + var valueSymbol = importSymbol.getExportAssignedValueSymbol(); + if (valueSymbol && + (valueSymbol.kind === PullElementKind.Method || valueSymbol.kind === PullElementKind.Function)) { + needsPropertyAssignment = true; + } + else { + usePropertyAssignmentInsteadOfVarDecl = true; + } + } + + // Calculate what name prefix to use + if (this.emitState.container === EmitContainer.DynamicModule) { + moduleNamePrefix = "exports." + } + else { + moduleNamePrefix = this.moduleName + "."; + } + } + + if (isAmdCodeGen && isExternalModuleReference) { + // For amdCode gen of exported external module reference, do not emit var declaration + // Emit the property assignment since it is exported + needsPropertyAssignment = true; + } + else { + this.recordSourceMappingStart(importDeclAST); + if (usePropertyAssignmentInsteadOfVarDecl) { + this.writeToOutput(moduleNamePrefix); + } + else { + this.writeToOutput("var "); + } + this.writeToOutput(importDeclAST.identifier.text() + " = "); + var aliasAST = importDeclAST.moduleReference; + + if (isExternalModuleReference) { + this.writeToOutput("require(" + (aliasAST).stringLiteral.text() + ")"); + } + else { + this.emitJavascript((aliasAST).moduleName, false); + } + + this.recordSourceMappingEnd(importDeclAST); + this.writeToOutput(";"); + + if (needsPropertyAssignment) { + this.writeLineToOutput(""); + this.emitIndent(); + } + } + + if (needsPropertyAssignment) { + this.writeToOutputWithSourceMapRecord(moduleNamePrefix + importDeclAST.identifier.text() + " = " + importDeclAST.identifier.text(), importDeclAST); + this.writeToOutput(";"); + } + this.emitComments(importDeclAST, false); + } + + public createSourceMapper(document: Document, jsFileName: string, jsFile: TextWriter, sourceMapOut: TextWriter, resolvePath: (path: string) => string) { + this.sourceMapper = new SourceMapper(jsFile, sourceMapOut, document, jsFileName, this.emitOptions, resolvePath); + } + + public setSourceMapperNewSourceFile(document: Document) { + this.sourceMapper.setNewSourceFile(document, this.emitOptions); + } + + private updateLineAndColumn(s: string) { + var lineNumbers = TextUtilities.parseLineStarts(s); + if (lineNumbers.length > 1) { + // There are new lines in the string, update the line and column number accordingly + this.emitState.line += lineNumbers.length - 1; + this.emitState.column = s.length - lineNumbers[lineNumbers.length - 1]; + } + else { + // No new lines in the string + this.emitState.column += s.length; + } + } + + public writeToOutputWithSourceMapRecord(s: string, astSpan: ISyntaxElement) { + if (astSpan) { + this.recordSourceMappingStart(astSpan); + } + + this.writeToOutput(s); + + if (astSpan) { + this.recordSourceMappingEnd(astSpan); + } + } + + public writeToOutput(s: string) { + this.outfile.Write(s); + this.updateLineAndColumn(s); + } + + public writeLineToOutput(s: string, force = false) { + // No need to print a newline if we're already at the start of the line. + if (!force && s === "" && this.emitState.column === 0) { + return; + } + + this.outfile.WriteLine(s); + this.updateLineAndColumn(s); + this.emitState.column = 0; + this.emitState.line++; + } + + public writeCaptureThisStatement(ast: ISyntaxElement) { + this.emitIndent(); + this.writeToOutputWithSourceMapRecord(this.captureThisStmtString, ast); + this.writeLineToOutput(""); + } + + public setContainer(c: number): number { + var temp = this.emitState.container; + this.emitState.container = c; + return temp; + } + + private getIndentString() { + return this.indenter.getIndent(); + } + + public emitIndent() { + this.writeToOutput(this.getIndentString()); + } + + public emitComment(comment: Comment, trailing: boolean, first: boolean, noLeadingSpace = false) { + if (this.emitOptions.compilationSettings().removeComments()) { + return; + } + + var text = getTrimmedTextLines(comment); + var emitColumn = this.emitState.column; + + if (emitColumn === 0) { + this.emitIndent(); + } + else if (trailing && first && !noLeadingSpace) { + this.writeToOutput(" "); + } + + if (comment.kind() === SyntaxKind.MultiLineCommentTrivia) { + this.recordSourceMappingCommentStart(comment); + this.writeToOutput(text[0]); + + if (text.length > 1 || comment.endsLine) { + for (var i = 1; i < text.length; i++) { + this.writeLineToOutput(""); + this.emitIndent(); + this.writeToOutput(text[i]); + } + this.recordSourceMappingCommentEnd(comment); + this.writeLineToOutput(""); + // Fall through + } + else { + this.recordSourceMappingCommentEnd(comment); + this.writeToOutput(" "); + return; + } + } + else { + this.recordSourceMappingCommentStart(comment); + this.writeToOutput(text[0]); + this.recordSourceMappingCommentEnd(comment); + this.writeLineToOutput(""); + // Fall through + } + + if (!trailing && emitColumn !== 0) { + // If we were indented before, stay indented after. + this.emitIndent(); + } + } + + private text(): ISimpleText { + return this.document.syntaxTree().text; + } + + public emitComments(ast: ISyntaxElement, pre: boolean, onlyPinnedOrTripleSlashComments: boolean = false) { + // Emitting the comments for the exprssion inside an arrow function is handled specially + // in emitFunctionBodyStatements. We don't want to emit those comments a second time. + if (ast && !isShared(ast) && ast.kind() !== SyntaxKind.Block) { + if (ast.parent.kind() === SyntaxKind.SimpleArrowFunctionExpression || ast.parent.kind() === SyntaxKind.ParenthesizedArrowFunctionExpression) { + return; + } + } + + if (pre) { + var preComments = TypeScript.ASTHelpers.preComments(ast, this.text()); + + if (preComments && ast === this.detachedCommentsElement) { + // We're emitting the comments for the first script element. Skip any + // copyright comments, as we'll already have emitted those. + var detachedComments = this.getDetachedComments(ast); + preComments = preComments.slice(detachedComments.length); + this.detachedCommentsElement = null; + } + + // We're emitting comments on an elided element. Only keep the comment if it is + // a triple slash or pinned comment. + if (preComments && onlyPinnedOrTripleSlashComments) { + preComments = ArrayUtilities.where(preComments, c => this.isPinnedOrTripleSlash(c)); + } + + this.emitCommentsArray(preComments, /*trailing:*/ false); + } + else { + this.emitCommentsArray(ASTHelpers.postComments(ast, this.text()), /*trailing:*/ true); + } + } + + private isPinnedOrTripleSlash(comment: Comment): boolean { + var fullText = comment.fullText(); + if (fullText.match(tripleSlashReferenceRegExp)) { + return true; + } + else { + return fullText.indexOf("/*!") === 0; + } + } + + private emitCommentsArray(comments: Comment[], trailing: boolean, noLeadingSpace = false): void { + if (!this.emitOptions.compilationSettings().removeComments() && comments) { + for (var i = 0, n = comments.length; i < n; i++) { + this.emitComment(comments[i], trailing, /*first:*/ i === 0, noLeadingSpace); + } + } + } + + public emitObjectLiteralExpression(objectLiteral: ObjectLiteralExpressionSyntax) { + this.recordSourceMappingStart(objectLiteral); + + // Try to preserve the newlines between elements that the user had. + this.writeToken(objectLiteral.openBraceToken); + this.emitCommaSeparatedList(objectLiteral, objectLiteral.propertyAssignments, /*buffer:*/ " ", /*preserveNewLines:*/ true); + this.writeToken(objectLiteral.closeBraceToken); + + this.recordSourceMappingEnd(objectLiteral); + } + + public emitArrayLiteralExpression(arrayLiteral: ArrayLiteralExpressionSyntax) { + this.recordSourceMappingStart(arrayLiteral); + + // Try to preserve the newlines between elements that the user had. + this.writeToken(arrayLiteral.openBracketToken); + this.emitCommaSeparatedList(arrayLiteral, arrayLiteral.expressions, /*buffer:*/ "", /*preserveNewLines:*/ true); + this.writeToken(arrayLiteral.closeBracketToken); + + this.recordSourceMappingEnd(arrayLiteral); + } + + public emitObjectCreationExpression(objectCreationExpression: ObjectCreationExpressionSyntax) { + this.recordSourceMappingStart(objectCreationExpression); + this.writeToken(objectCreationExpression.newKeyword); + this.writeToOutput(" "); + var target = objectCreationExpression.expression; + + this.emit(target); + if (objectCreationExpression.argumentList) { + this.recordSourceMappingStart(objectCreationExpression.argumentList); + this.writeToken(objectCreationExpression.argumentList.openParenToken); + this.emitCommaSeparatedList(objectCreationExpression.argumentList, objectCreationExpression.argumentList.arguments, /*buffer:*/ "", /*preserveNewLines:*/ false); + this.writeToken(objectCreationExpression.argumentList.closeParenToken); + this.recordSourceMappingEnd(objectCreationExpression.argumentList); + } + + this.recordSourceMappingEnd(objectCreationExpression); + } + + public getConstantDecl(dotExpr: MemberAccessExpressionSyntax): PullEnumElementDecl { + var pullSymbol = this.semanticInfoChain.getSymbolForAST(dotExpr); + if (pullSymbol && pullSymbol.kind === PullElementKind.EnumMember) { + var pullDecls = pullSymbol.getDeclarations(); + if (pullDecls.length === 1) { + var pullDecl = pullDecls[0]; + if (pullDecl.kind === PullElementKind.EnumMember) { + return pullDecl; + } + } + } + + return null; + } + + public tryEmitConstant(dotExpr: MemberAccessExpressionSyntax) { + var propertyName = dotExpr.name; + var boundDecl = this.getConstantDecl(dotExpr); + if (boundDecl) { + var value = boundDecl.constantValue; + if (value !== null) { + this.recordSourceMappingStart(dotExpr); + this.writeToOutput(value.toString()); + var comment = " /* "; + comment += propertyName.text(); + comment += " */"; + this.writeToOutput(comment); + this.recordSourceMappingEnd(dotExpr); + return true; + } + } + + return false; + } + + public emitInvocationExpression(callNode: InvocationExpressionSyntax) { + this.recordSourceMappingStart(callNode); + var target = callNode.expression; + var args = callNode.argumentList.arguments; + + if (target.kind() === SyntaxKind.MemberAccessExpression && (target).expression.kind() === SyntaxKind.SuperKeyword) { + this.emit(target); + this.writeToOutput(".call"); + this.recordSourceMappingStart(args); + this.writeToken(callNode.argumentList.openParenToken); + this.emitThis(); + if (args && args.length > 0) { + this.writeToOutput(", "); + this.emitCommaSeparatedList(callNode.argumentList, args, /*buffer:*/ "", /*preserveNewLines:*/ false); + } + } + else { + if (callNode.expression.kind() === SyntaxKind.SuperKeyword && this.emitState.container === EmitContainer.Constructor) { + this.writeToOutput("_super.call"); + } + else { + this.emitJavascript(target, false); + } + this.recordSourceMappingStart(args); + this.writeToken(callNode.argumentList.openParenToken); + if (callNode.expression.kind() === SyntaxKind.SuperKeyword && this.emitState.container === EmitContainer.Constructor) { + this.writeToOutput("this"); + if (args && args.length > 0) { + this.writeToOutput(", "); + } + } + this.emitCommaSeparatedList(callNode.argumentList, args, /*buffer:*/ "", /*preserveNewLines:*/ false); + } + + this.writeToken(callNode.argumentList.closeParenToken); + this.recordSourceMappingEnd(args); + this.recordSourceMappingEnd(callNode); + } + + private emitParameterList(list: ParameterListSyntax): void { + this.writeToken(list.openParenToken); + this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(list.openParenToken, this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true); + this.emitFunctionParameters(list.parameters, list.parameters); + this.writeToken(list.closeParenToken); + } + + private emitFunctionParameters(ast: ISyntaxElement, parameters: ParameterSyntax[]): void { + var argsLen = 0; + + if (parameters) { + this.emitComments(ast, true); + + var tempContainer = this.setContainer(EmitContainer.Args); + argsLen = parameters.length; + var printLen = argsLen; + if (lastParameterIsRest(parameters)) { + printLen--; + } + for (var i = 0; i < printLen; i++) { + var arg = parameters[i]; + this.emit(arg); + + if (i < (printLen - 1)) { + this.writeToOutput(", "); + if (parameters) { + this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(parameters.separatorAt(i), this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true); + } + } + } + this.setContainer(tempContainer); + + this.emitComments(ast, false); + } + } + + private emitFunctionBodyStatements(name: string, funcDecl: ISyntaxElement, parameters: ParameterSyntax[], block: BlockSyntax, bodyExpression: ISyntaxElement): void { + this.writeLineToOutput(" {"); + if (name) { + this.recordSourceMappingNameStart(name); + } + + this.indenter.increaseIndent(); + + if (block) { + // We want any detached statements at the start of hte block to stay at the start. + // This is important for features like VSDoc which place their comments inside a + // block, but can't have them preceded by things like "var _this = this" when we + // emit. + + this.emitDetachedComments(block.statements); + } + + // Parameter list parameters with defaults could capture this + if (this.shouldCaptureThis(funcDecl)) { + this.writeCaptureThisStatement(funcDecl); + } + + if (parameters) { + this.emitDefaultValueAssignments(parameters); + this.emitRestParameterInitializer(parameters); + } + + if (block) { + this.emitList(block.statements); + this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(block.closeBraceToken, this.text()), /*trailing:*/ false); + } + else { + // Copy any comments before the body of the arrow function to the return statement. + // This is necessary for emitting correctness so we don't emit something like this: + // + // return + // // foo + // this.foo(); + // + // Because of ASI, this gets parsed as "return;" which is *not* what we want for + // proper semantics. + //var preComments = bodyExpression.preComments(); + //var postComments = bodyExpression.postComments(); + + //bodyExpression.setPreComments(null); + //bodyExpression.setPostComments(null); + + this.emitIndent(); + this.emitCommentsArray(ASTHelpers.preComments(bodyExpression, this.text()), /*trailing:*/ false); + this.writeToOutput("return "); + this.emit(bodyExpression); + this.writeLineToOutput(";"); + this.emitCommentsArray(ASTHelpers.preComments(bodyExpression, this.text()), /*trailing:*/ true); + + //bodyExpression.setPreComments(preComments); + //bodyExpression.setPostComments(postComments); + } + + this.indenter.decreaseIndent(); + this.emitIndent(); + + if (block) { + this.writeToken(block.closeBraceToken); + } + else { + this.writeToOutputWithSourceMapRecord("}", bodyExpression); + } + + if (name) { + this.recordSourceMappingNameEnd(); + } + } + + private emitDefaultValueAssignments(parameters: ParameterSyntax[]): void { + var n = parameters.length; + if (lastParameterIsRest(parameters)) { + n--; + } + + for (var i = 0; i < n; i++) { + var arg = parameters[i]; + var id = arg.identifier; + var equalsValueClause = arg.equalsValueClause; + if (equalsValueClause) { + this.emitIndent(); + this.recordSourceMappingStart(arg); + this.writeToOutput("if (typeof " + id.text() + " === \"undefined\") { ");// + this.writeToken(id); + this.emitJavascript(equalsValueClause, false); + this.writeLineToOutput("; }"); + this.recordSourceMappingEnd(arg); + } + } + } + + private emitRestParameterInitializer(parameters: ParameterSyntax[]): void { + if (lastParameterIsRest(parameters)) { + var n = parameters.length; + var lastArg = parameters[n - 1]; + var id = lastArg.identifier; + this.emitIndent(); + this.recordSourceMappingStart(lastArg); + this.writeToOutput("var "); + this.writeToken(id); + this.writeLineToOutput(" = [];"); + this.recordSourceMappingEnd(lastArg); + this.emitIndent(); + this.writeToOutput("for ("); + this.writeToOutputWithSourceMapRecord("var _i = 0;", lastArg); + this.writeToOutput(" "); + this.writeToOutputWithSourceMapRecord("_i < (arguments.length - " + (n - 1) + ")", lastArg); + this.writeToOutput("; "); + this.writeToOutputWithSourceMapRecord("_i++", lastArg); + this.writeLineToOutput(") {"); + this.indenter.increaseIndent(); + this.emitIndent(); + + this.writeToOutputWithSourceMapRecord(id.text() + "[_i] = arguments[_i + " + (n - 1) + "];", lastArg); + this.writeLineToOutput(""); + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeLineToOutput("}"); + } + } + + private getImportDecls(fileName: string): PullDecl[] { + var topLevelDecl = this.semanticInfoChain.topLevelDecl(this.document.fileName); + var result: PullDecl[] = []; + + var dynamicModuleDecl = topLevelDecl.getChildDecls()[0]; // Dynamic module declaration has to be present + var queue: PullDecl[] = dynamicModuleDecl.getChildDecls(); + + for (var i = 0, n = queue.length; i < n; i++) { + var decl = queue[i]; + + if (decl.kind & PullElementKind.TypeAlias) { + var importStatementAST = this.semanticInfoChain.getASTForDecl(decl); + if (importStatementAST.moduleReference.kind() === SyntaxKind.ExternalModuleReference) { // external module + var symbol = decl.getSymbol(this.semanticInfoChain); + var typeSymbol = symbol && symbol.type; + if (typeSymbol && typeSymbol !== this.semanticInfoChain.anyTypeSymbol && !typeSymbol.isError()) { + result.push(decl); + } + } + } + } + + return result; + } + + public getModuleImportAndDependencyList(sourceUnit: SourceUnitSyntax) { + var importList = ""; + var dependencyList = ""; + + var importDecls = this.getImportDecls(this.document.fileName); + + // all dependencies are quoted + if (importDecls.length) { + for (var i = 0; i < importDecls.length; i++) { + var importStatementDecl = importDecls[i]; + var importStatementSymbol = importStatementDecl.getSymbol(this.semanticInfoChain); + var importStatementAST = this.semanticInfoChain.getASTForDecl(importStatementDecl); + + if (importStatementSymbol.isUsedAsValue()) { + if (i <= importDecls.length - 1) { + dependencyList += ", "; + importList += ", "; + } + + importList += importStatementDecl.name; + dependencyList += (importStatementAST.moduleReference).stringLiteral.text(); + } + } + } + + // emit any potential amd dependencies + var amdDependencies = this.document.syntaxTree().amdDependencies(); + for (var i = 0; i < amdDependencies.length; i++) { + dependencyList += ", \"" + amdDependencies[i] + "\""; + } + + return { + importList: importList, + dependencyList: dependencyList + }; + } + + public shouldCaptureThis(ast: ISyntaxElement) { + if (ast.kind() === SyntaxKind.SourceUnit) { + var scriptDecl = this.semanticInfoChain.topLevelDecl(this.document.fileName); + return hasFlag(scriptDecl.flags, PullElementFlags.MustCaptureThis); + } + + var decl = this.semanticInfoChain.getDeclForAST(ast); + if (decl) { + return hasFlag(decl.flags, PullElementFlags.MustCaptureThis); + } + + return false; + } + + public emitEnum(moduleDecl: EnumDeclarationSyntax) { + var pullDecl = this.semanticInfoChain.getDeclForAST(moduleDecl); + this.pushDecl(pullDecl); + + var svModuleName = this.moduleName; + this.moduleName = moduleDecl.identifier.text(); + + var temp = this.setContainer(EmitContainer.Module); + var isExported = hasFlag(pullDecl.flags, PullElementFlags.Exported); + + if (!isExported) { + this.recordSourceMappingStart(moduleDecl); + this.writeToOutput("var "); + this.writeToOutputWithSourceMapRecord(this.moduleName, moduleDecl.identifier); + this.writeLineToOutput(";"); + this.recordSourceMappingEnd(moduleDecl); + this.emitIndent(); + } + + this.writeToOutput("("); + this.recordSourceMappingStart(moduleDecl); + this.writeToOutput("function ("); + this.writeToOutputWithSourceMapRecord(this.moduleName, moduleDecl.identifier); + this.writeLineToOutput(") {"); + + this.recordSourceMappingNameStart(this.moduleName); + + this.indenter.increaseIndent(); + + if (this.shouldCaptureThis(moduleDecl)) { + this.writeCaptureThisStatement(moduleDecl); + } + + this.emitSeparatedList(moduleDecl.enumElements); + this.indenter.decreaseIndent(); + this.emitIndent(); + + var parentIsDynamic = temp === EmitContainer.DynamicModule; + if (temp === EmitContainer.Prog && isExported) { + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.writeToOutput(")(this." + this.moduleName + " || (this." + this.moduleName + " = {}));"); + } + else if (isExported || temp === EmitContainer.Prog) { + var dotMod = svModuleName !== "" ? (parentIsDynamic ? "exports" : svModuleName) + "." : svModuleName; + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.writeToOutput(")(" + dotMod + this.moduleName + " || (" + dotMod + this.moduleName + " = {}));"); + } + else if (!isExported && temp !== EmitContainer.Prog) { + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.writeToOutput(")(" + this.moduleName + " || (" + this.moduleName + " = {}));"); + } + else { + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.writeToOutput(")();"); + } + + this.recordSourceMappingEnd(moduleDecl); + if (temp !== EmitContainer.Prog && isExported) { + this.recordSourceMappingStart(moduleDecl); + if (parentIsDynamic) { + this.writeLineToOutput(""); + this.emitIndent(); + this.writeToOutput("var " + this.moduleName + " = exports." + this.moduleName + ";"); + } + else { + this.writeLineToOutput(""); + this.emitIndent(); + this.writeToOutput("var " + this.moduleName + " = " + svModuleName + "." + this.moduleName + ";"); + } + this.recordSourceMappingEnd(moduleDecl); + } + + this.setContainer(temp); + this.moduleName = svModuleName; + + this.popDecl(pullDecl); + } + + private getModuleDeclToVerifyChildNameCollision(moduleDecl: PullDecl, changeNameIfAnyDeclarationInContext: boolean) { + if (ArrayUtilities.contains(this.declStack, moduleDecl)) { + // Given decl is in the scope, we would need to check for child name collision + return moduleDecl; + } + else if (changeNameIfAnyDeclarationInContext) { + // Check if any other declaration of the given symbol is in scope + // (eg. when emitting expression of type defined from different declaration in reopened module) + var symbol = moduleDecl.getSymbol(this.semanticInfoChain); + if (symbol) { + var otherDecls = symbol.getDeclarations(); + for (var i = 0; i < otherDecls.length; i++) { + // If the other decl is in the scope, use this decl to determine which name to display + if (ArrayUtilities.contains(this.declStack, otherDecls[i])) { + return otherDecls[i]; + } + } + } + } + + return null; + } + + private hasChildNameCollision(moduleName: string, parentDecl: PullDecl) { + var childDecls = parentDecl.getChildDecls(); + return ArrayUtilities.any(childDecls, (childDecl: PullDecl) => { + var childAST = this.semanticInfoChain.getASTForDecl(childDecl); + // Enum member it can never conflict with module name as it is property of the enum + // Only if this child would be emitted we need to look further in + if (childDecl.kind != PullElementKind.EnumMember && this.shouldEmit(childAST)) { + // same name child + if (childDecl.name === moduleName) { + // collision if the parent was not class + if (parentDecl.kind != PullElementKind.Class) { + return true; + } + + // If the parent was class, we would find name collision if this was not a property/method/accessor + if (!(childDecl.kind == PullElementKind.Method || + childDecl.kind == PullElementKind.Property || + childDecl.kind == PullElementKind.SetAccessor || + childDecl.kind == PullElementKind.GetAccessor)) { + return true; + } + } + + // Check if the name collision exists in any of the children + if (this.hasChildNameCollision(moduleName, childDecl)) { + return true; + } + } + return false; + }); + } + + // Get the moduleName to write in js file + // If changeNameIfAnyDeclarationInContext is true, verify if any of the declarations for the symbol would need rename. + private getModuleName(moduleDecl: PullDecl, changeNameIfAnyDeclarationInContext?: boolean) { + var moduleName = moduleDecl.name; + var moduleDisplayName = moduleDecl.getDisplayName(); + + // If the decl is in stack it may need name change in the js file + moduleDecl = this.getModuleDeclToVerifyChildNameCollision(moduleDecl, changeNameIfAnyDeclarationInContext); + if (moduleDecl && moduleDecl.kind != PullElementKind.Enum) { + // If there is any child that would be emitted with same name as module, js files would need to use rename for the module + while (this.hasChildNameCollision(moduleName, moduleDecl)) { + // there was name collision with member which could result in faulty codegen, try rename with prepend of '_' + moduleName = "_" + moduleName; + moduleDisplayName = "_" + moduleDisplayName; + } + } + + return moduleDisplayName; + } + + private emitModuleDeclarationWorker(moduleDecl: ModuleDeclarationSyntax) { + if (moduleDecl.stringLiteral) { + this.emitSingleModuleDeclaration(moduleDecl, moduleDecl.stringLiteral); + } + else { + var moduleNames = ASTHelpers.getModuleNames(moduleDecl.name); + this.emitSingleModuleDeclaration(moduleDecl, moduleNames[0]); + } + } + + private writeToken(token: ISyntaxToken) { + if (token) { + this.writeToOutputWithSourceMapRecord(token.text(), token); + } + } + + public emitSingleModuleDeclaration(moduleDecl: ModuleDeclarationSyntax, moduleName: ISyntaxToken) { + var isLastName = ASTHelpers.isLastNameOfModule(moduleDecl, moduleName); + + if (isLastName) { + // Doc Comments on the ast belong to the innermost module being emitted. + this.emitComments(moduleDecl, true); + } + + var pullDecl = this.semanticInfoChain.getDeclForAST(moduleName); + this.pushDecl(pullDecl); + + var svModuleName = this.moduleName; + + if (moduleDecl.stringLiteral) { + this.moduleName = tokenValueText(moduleDecl.stringLiteral); + if (isTSFile(this.moduleName)) { + this.moduleName = this.moduleName.substring(0, this.moduleName.length - ".ts".length); + } + } + else { + this.moduleName = moduleName.text(); + } + + var temp = this.setContainer(EmitContainer.Module); + var isExported = hasFlag(pullDecl.flags, PullElementFlags.Exported); + + // prologue + + if (!isExported) { + this.recordSourceMappingStart(moduleDecl); + this.writeToOutput("var "); + this.writeToOutputWithSourceMapRecord(this.moduleName, moduleName); + this.writeLineToOutput(";"); + this.recordSourceMappingEnd(moduleDecl); + this.emitIndent(); + } + + this.writeToOutput("("); + this.recordSourceMappingStart(moduleDecl); + this.writeToOutput("function ("); + // Use the name that doesnt conflict with its members, + // this.moduleName needs to be updated to make sure that export member declaration is emitted correctly + this.moduleName = this.getModuleName(pullDecl); + this.writeToOutputWithSourceMapRecord(this.moduleName, moduleName); + this.writeLineToOutput(") {"); + + this.recordSourceMappingNameStart(moduleName.text()); + + this.indenter.increaseIndent(); + + if (this.shouldCaptureThis(moduleDecl)) { + this.writeCaptureThisStatement(moduleDecl); + } + + if (moduleName === moduleDecl.stringLiteral) { + this.emitList(moduleDecl.moduleElements); + } + else { + var moduleNames = ASTHelpers.getModuleNames(moduleDecl.name); + var nameIndex = moduleNames.indexOf(moduleName); + + Debug.assert(nameIndex >= 0); + + if (isLastName) { + // If we're on the innermost module, we can emit the module elements. + this.emitList(moduleDecl.moduleElements); + } + else { + // otherwise, just recurse and emit the next module in the A.B.C module name. + this.emitIndent(); + this.emitSingleModuleDeclaration(moduleDecl, moduleNames[nameIndex + 1]); + this.writeLineToOutput(""); + } + } + + this.moduleName = moduleName.text(); + this.indenter.decreaseIndent(); + this.emitIndent(); + + // epilogue + var parentIsDynamic = temp === EmitContainer.DynamicModule; + this.recordSourceMappingStart(moduleDecl.closeBraceToken); + if (temp === EmitContainer.Prog && isExported) { + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.recordSourceMappingEnd(moduleDecl.closeBraceToken); + this.writeToOutput(")(this." + this.moduleName + " || (this." + this.moduleName + " = {}));"); + } + else if (isExported || temp === EmitContainer.Prog) { + var dotMod = svModuleName !== "" ? (parentIsDynamic ? "exports" : svModuleName) + "." : svModuleName; + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.recordSourceMappingEnd(moduleDecl.closeBraceToken); + this.writeToOutput(")(" + dotMod + this.moduleName + " || (" + dotMod + this.moduleName + " = {}));"); + } + else if (!isExported && temp !== EmitContainer.Prog) { + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.recordSourceMappingEnd(moduleDecl.closeBraceToken); + this.writeToOutput(")(" + this.moduleName + " || (" + this.moduleName + " = {}));"); + } + else { + this.writeToOutput("}"); + this.recordSourceMappingNameEnd(); + this.recordSourceMappingEnd(moduleDecl.closeBraceToken); + this.writeToOutput(")();"); + } + + this.recordSourceMappingEnd(moduleDecl); + if (temp !== EmitContainer.Prog && isExported) { + this.recordSourceMappingStart(moduleDecl); + if (parentIsDynamic) { + this.writeLineToOutput(""); + this.emitIndent(); + this.writeToOutput("var " + this.moduleName + " = exports." + this.moduleName + ";"); + } + else { + this.writeLineToOutput(""); + this.emitIndent(); + this.writeToOutput("var " + this.moduleName + " = " + svModuleName + "." + this.moduleName + ";"); + } + this.recordSourceMappingEnd(moduleDecl); + } + + this.setContainer(temp); + this.moduleName = svModuleName; + + this.popDecl(pullDecl); + + if (isLastName) { + // Comments on the module ast belong to the innermost module being emitted. + this.emitComments(moduleDecl, false); + } + } + + public emitEnumElement(varDecl: EnumElementSyntax): void { + // [[""] = ] = ""; + var pullDecl = this.semanticInfoChain.getDeclForAST(varDecl); + Debug.assert(pullDecl && pullDecl.kind === PullElementKind.EnumMember); + + this.emitComments(varDecl, true); + this.recordSourceMappingStart(varDecl); + + var representation = (varDecl.propertyName.kind() === SyntaxKind.StringLiteral) + ? varDecl.propertyName.text() + : ('"' + tokenValueText(varDecl.propertyName) + '"'); + + this.writeToOutput(this.moduleName); + this.writeToOutput('['); + this.writeToOutput(this.moduleName); + this.writeToOutput('['); + this.writeToOutput(representation); + this.writeToOutput(']'); + + if (varDecl.equalsValueClause) { + this.emit(varDecl.equalsValueClause); + } + else if (pullDecl.constantValue !== null) { + this.writeToOutput(' = '); + this.writeToOutput(pullDecl.constantValue.toString()); + } + else { + this.writeToOutput(' = null'); + } + + this.writeToOutput('] = '); + this.writeToOutput(representation); + this.recordSourceMappingEnd(varDecl); + this.emitComments(varDecl, false); + this.writeToOutput(';'); + } + + public emitElementAccessExpression(expression: ElementAccessExpressionSyntax) { + this.recordSourceMappingStart(expression); + this.emit(expression.expression); + this.writeToken(expression.openBracketToken); + this.emit(expression.argumentExpression); + this.writeToken(expression.closeBracketToken); + this.recordSourceMappingEnd(expression); + } + + public emitSimpleArrowFunctionExpression(arrowFunction: SimpleArrowFunctionExpressionSyntax): void { + this.emitAnyArrowFunctionExpression(arrowFunction, arrowFunction.block, arrowFunction.expression); + } + + public emitParenthesizedArrowFunctionExpression(arrowFunction: ParenthesizedArrowFunctionExpressionSyntax): void { + this.emitAnyArrowFunctionExpression(arrowFunction, arrowFunction.block, arrowFunction.expression); + } + + private emitAnyArrowFunctionExpression(arrowFunction: ISyntaxElement, block: BlockSyntax, expression: ISyntaxElement): void { + var savedInArrowFunction = this.inArrowFunction; + this.inArrowFunction = true; + + var temp = this.setContainer(EmitContainer.Function); + + this.recordSourceMappingStart(arrowFunction); + + // Start + var pullDecl = this.semanticInfoChain.getDeclForAST(arrowFunction); + this.pushDecl(pullDecl); + + this.emitComments(arrowFunction, true); + + this.recordSourceMappingStart(arrowFunction); + this.writeToOutput("function "); + + var parameters: ParameterSyntax[] = null; + if (arrowFunction.kind() === SyntaxKind.ParenthesizedArrowFunctionExpression) { + var parenthesizedArrowFunction = arrowFunction; + + parameters = parenthesizedArrowFunction.callSignature.parameterList.parameters; + this.emitParameterList(parenthesizedArrowFunction.callSignature.parameterList); + } + else { + var parameter = (arrowFunction).parameter; + parameters = [parameter]; + this.writeToOutput("("); + this.emitFunctionParameters(parameter, parameters); + this.writeToOutput(")"); + } + + this.emitFunctionBodyStatements(/*funcName:*/ null, arrowFunction, parameters, block, expression); + + this.recordSourceMappingEnd(arrowFunction); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(arrowFunction); + + this.emitComments(arrowFunction, false); + + this.popDecl(pullDecl); + this.setContainer(temp); + this.inArrowFunction = savedInArrowFunction; + } + + public emitConstructor(funcDecl: ConstructorDeclarationSyntax) { + if (!funcDecl.block) { + return; + } + var temp = this.setContainer(EmitContainer.Constructor); + + this.recordSourceMappingStart(funcDecl); + + var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + this.pushDecl(pullDecl); + + this.emitComments(funcDecl, true); + + this.recordSourceMappingStart(funcDecl); + this.writeToOutput("function "); + this.writeToOutput(this.thisClassNode.identifier.text()); + + this.emitParameterList(funcDecl.callSignature.parameterList); + this.writeLineToOutput(" {"); + + this.recordSourceMappingNameStart("constructor"); + this.indenter.increaseIndent(); + + var parameters = funcDecl.callSignature.parameterList.parameters; + this.emitDefaultValueAssignments(parameters); + this.emitRestParameterInitializer(parameters); + + if (this.shouldCaptureThis(funcDecl)) { + this.writeCaptureThisStatement(funcDecl); + } + + this.emitConstructorStatements(funcDecl); + this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(funcDecl.block.closeBraceToken, this.text()), /*trailing:*/ false); + + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeToken(funcDecl.block.closeBraceToken); + + this.recordSourceMappingNameEnd(); + this.recordSourceMappingEnd(funcDecl); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(funcDecl); + + this.emitComments(funcDecl, false); + + this.popDecl(pullDecl); + this.setContainer(temp); + } + + public emitGetAccessor(accessor: GetAccessorSyntax): void { + this.recordSourceMappingStart(accessor); + this.writeToOutput("get "); + + var temp = this.setContainer(EmitContainer.Function); + + this.recordSourceMappingStart(accessor); + + var pullDecl = this.semanticInfoChain.getDeclForAST(accessor); + this.pushDecl(pullDecl); + + this.recordSourceMappingStart(accessor); + + var accessorSymbol = PullHelpers.getAccessorSymbol(accessor, this.semanticInfoChain); + var container = accessorSymbol.getContainer(); + var containerKind = container.kind; + + this.recordSourceMappingNameStart(accessor.propertyName.text()); + this.writeToOutput(accessor.propertyName.text()); + this.emitParameterList(accessor.callSignature.parameterList); + + this.emitFunctionBodyStatements(null, accessor, accessor.callSignature.parameterList.parameters, accessor.block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(accessor); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(accessor); + + this.popDecl(pullDecl); + this.setContainer(temp); + this.recordSourceMappingEnd(accessor); + } + + public emitSetAccessor(accessor: SetAccessorSyntax): void { + this.recordSourceMappingStart(accessor); + this.writeToOutput("set "); + + var temp = this.setContainer(EmitContainer.Function); + + this.recordSourceMappingStart(accessor); + + var pullDecl = this.semanticInfoChain.getDeclForAST(accessor); + this.pushDecl(pullDecl); + + this.recordSourceMappingStart(accessor); + + var accessorSymbol = PullHelpers.getAccessorSymbol(accessor, this.semanticInfoChain); + var container = accessorSymbol.getContainer(); + var containerKind = container.kind; + + this.recordSourceMappingNameStart(accessor.propertyName.text()); + this.writeToOutput(accessor.propertyName.text()); + + this.emitParameterList(accessor.callSignature.parameterList); + + this.emitFunctionBodyStatements(null, accessor, accessor.callSignature.parameterList.parameters, accessor.block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(accessor); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(accessor); + + this.popDecl(pullDecl); + this.setContainer(temp); + this.recordSourceMappingEnd(accessor); + } + + public emitFunctionExpression(funcDecl: FunctionExpressionSyntax): void { + var savedInArrowFunction = this.inArrowFunction; + this.inArrowFunction = false; + + var temp = this.setContainer(EmitContainer.Function); + + var funcName = funcDecl.identifier ? funcDecl.identifier.text() : null;//.getNameText(); + + this.recordSourceMappingStart(funcDecl); + + var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + this.pushDecl(pullDecl); + + this.recordSourceMappingStart(funcDecl); + this.writeToken(funcDecl.functionKeyword); + this.writeToOutput(" "); + + //var id = funcDecl.getNameText(); + if (funcDecl.identifier) { + this.writeToOutputWithSourceMapRecord(funcDecl.identifier.text(), funcDecl.identifier); + } + + this.emitParameterList(funcDecl.callSignature.parameterList); + this.emitFunctionBodyStatements(funcName, funcDecl, funcDecl.callSignature.parameterList.parameters, funcDecl.block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(funcDecl); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(funcDecl); + + this.emitComments(funcDecl, false); + + this.popDecl(pullDecl); + + this.setContainer(temp); + this.inArrowFunction = savedInArrowFunction; + } + + public emitFunction(funcDecl: FunctionDeclarationSyntax) { + if (funcDecl.block === null) { + return; + } + var savedInArrowFunction = this.inArrowFunction; + this.inArrowFunction = false; + + var temp = this.setContainer(EmitContainer.Function); + + var funcName = funcDecl.identifier.text(); + + this.recordSourceMappingStart(funcDecl); + + var printName = funcDecl.identifier !== null + var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + this.pushDecl(pullDecl); + + this.emitComments(funcDecl, true); + + this.recordSourceMappingStart(funcDecl); + this.writeToken(funcDecl.functionKeyword); + this.writeToOutput(" "); + + if (printName) { + var id = funcDecl.identifier.text(); + if (id) { + if (funcDecl.identifier) { + this.recordSourceMappingStart(funcDecl.identifier); + } + this.writeToOutput(id); + if (funcDecl.identifier) { + this.recordSourceMappingEnd(funcDecl.identifier); + } + } + } + + this.emitParameterList(funcDecl.callSignature.parameterList); + + var parameters = funcDecl.callSignature.parameterList.parameters; + this.emitFunctionBodyStatements(funcDecl.identifier.text(), funcDecl, parameters, funcDecl.block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(funcDecl); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(funcDecl); + + this.emitComments(funcDecl, false); + + this.popDecl(pullDecl); + + this.setContainer(temp); + this.inArrowFunction = savedInArrowFunction; + + if (funcDecl.block) { + var pullFunctionDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + if ((this.emitState.container === EmitContainer.Module || this.emitState.container === EmitContainer.DynamicModule) && pullFunctionDecl && hasFlag(pullFunctionDecl.flags, PullElementFlags.Exported)) { + this.writeLineToOutput(""); + this.emitIndent(); + var modName = this.emitState.container === EmitContainer.Module ? this.moduleName : "exports"; + this.recordSourceMappingStart(funcDecl); + this.writeToOutput(modName + "." + funcName + " = " + funcName + ";"); + this.recordSourceMappingEnd(funcDecl); + } + } + } + + public emitAmbientVarDecl(varDecl: VariableDeclaratorSyntax) { + this.recordSourceMappingStart(this.currentVariableDeclaration); + if (varDecl.equalsValueClause) { + this.emitComments(varDecl, true); + this.recordSourceMappingStart(varDecl); + this.writeToOutputWithSourceMapRecord(varDecl.propertyName.text(), varDecl.propertyName); + this.emitJavascript(varDecl.equalsValueClause, false); + this.recordSourceMappingEnd(varDecl); + this.emitComments(varDecl, false); + } + } + + // Emits "var " if it is allowed + public emitVarDeclVar() { + if (this.currentVariableDeclaration) { + this.writeToOutput("var "); + } + } + + public emitVariableDeclaration(declaration: VariableDeclarationSyntax) { + var varDecl = declaration.variableDeclarators[0]; + + var symbol = this.semanticInfoChain.getSymbolForAST(varDecl); + + var parentSymbol = symbol ? symbol.getContainer() : null; + var parentKind = parentSymbol ? parentSymbol.kind : PullElementKind.None; + + this.emitComments(declaration, true); + + var pullVarDecl = this.semanticInfoChain.getDeclForAST(varDecl); + var isAmbientWithoutInit = pullVarDecl && hasFlag(pullVarDecl.flags, PullElementFlags.Ambient) && varDecl.equalsValueClause === null; + if (!isAmbientWithoutInit) { + var prevVariableDeclaration = this.currentVariableDeclaration; + this.currentVariableDeclaration = declaration; + + for (var i = 0, n = declaration.variableDeclarators.length; i < n; i++) { + var declarator = declaration.variableDeclarators[i]; + + if (i > 0) { + this.writeToOutput(", "); + } + + this.emit(declarator); + } + this.currentVariableDeclaration = prevVariableDeclaration; + + // Declarator emit would take care of emitting start of the variable declaration start + this.recordSourceMappingEnd(declaration); + } + + this.emitComments(declaration, false); + } + + private emitMemberVariableDeclaration(varDecl: MemberVariableDeclarationSyntax) { + Debug.assert(!hasModifier(varDecl.modifiers, PullElementFlags.Static) && varDecl.variableDeclarator.equalsValueClause); + + var pullDecl = this.semanticInfoChain.getDeclForAST(varDecl); + this.pushDecl(pullDecl); + + this.emitComments(varDecl, true); + this.recordSourceMappingStart(varDecl); + + var varDeclName = varDecl.variableDeclarator.propertyName.text(); + var quotedOrNumber = isQuoted(varDeclName) || varDecl.variableDeclarator.propertyName.kind() !== SyntaxKind.IdentifierName; + + var symbol = this.semanticInfoChain.getSymbolForAST(varDecl); + var parentSymbol = symbol ? symbol.getContainer() : null; + var parentDecl = pullDecl && pullDecl.getParentDecl(); + + if (quotedOrNumber) { + this.writeToOutput("this["); + } + else { + this.writeToOutput("this."); + } + + this.writeToOutputWithSourceMapRecord(varDecl.variableDeclarator.propertyName.text(), varDecl.variableDeclarator.propertyName); + + if (quotedOrNumber) { + this.writeToOutput("]"); + } + + if (varDecl.variableDeclarator.equalsValueClause) { + // Ensure we have a fresh var list count when recursing into the variable + // initializer. We don't want our current list of variables to affect how we + // emit nested variable lists. + var prevVariableDeclaration = this.currentVariableDeclaration; + this.emit(varDecl.variableDeclarator.equalsValueClause); + this.currentVariableDeclaration = prevVariableDeclaration; + } + + // class + if (this.emitState.container !== EmitContainer.Args) { + this.writeToOutput(";"); + } + + this.recordSourceMappingEnd(varDecl); + this.emitComments(varDecl, false); + + this.popDecl(pullDecl); + } + + public emitVariableDeclarator(varDecl: VariableDeclaratorSyntax) { + var pullDecl = this.semanticInfoChain.getDeclForAST(varDecl); + this.pushDecl(pullDecl); + if (pullDecl && (pullDecl.flags & PullElementFlags.Ambient) === PullElementFlags.Ambient) { + this.emitAmbientVarDecl(varDecl); + } + else { + this.emitComments(varDecl, true); + this.recordSourceMappingStart(this.currentVariableDeclaration); + this.recordSourceMappingStart(varDecl); + + var varDeclName = varDecl.propertyName.text(); + + var symbol = this.semanticInfoChain.getSymbolForAST(varDecl); + var parentSymbol = symbol ? symbol.getContainer() : null; + var parentDecl = pullDecl && pullDecl.getParentDecl(); + var parentIsModule = parentDecl && (parentDecl.flags & PullElementFlags.SomeInitializedModule); + + if (parentIsModule) { + // module + if (!hasFlag(pullDecl.flags, PullElementFlags.Exported)/* && !varDecl.isProperty() */) { + this.emitVarDeclVar(); + } + else { + if (this.emitState.container === EmitContainer.DynamicModule) { + this.writeToOutput("exports."); + } + else { + this.writeToOutput(this.moduleName + "."); + } + } + } + else { + this.emitVarDeclVar(); + } + + this.writeToOutputWithSourceMapRecord(varDecl.propertyName.text(), varDecl.propertyName); + + if (varDecl.equalsValueClause) { + // Ensure we have a fresh var list count when recursing into the variable + // initializer. We don't want our current list of variables to affect how we + // emit nested variable lists. + var prevVariableDeclaration = this.currentVariableDeclaration; + this.emit(varDecl.equalsValueClause); + this.currentVariableDeclaration = prevVariableDeclaration; + } + + this.recordSourceMappingEnd(varDecl); + this.emitComments(varDecl, false); + } + this.currentVariableDeclaration = undefined; + this.popDecl(pullDecl); + } + + private symbolIsUsedInItsEnclosingContainer(symbol: PullSymbol, dynamic = false) { + var symDecls = symbol.getDeclarations(); + + if (symDecls.length) { + var enclosingDecl = this.getEnclosingDecl(); + if (enclosingDecl) { + var parentDecl = symDecls[0].getParentDecl(); + if (parentDecl) { + var symbolDeclarationEnclosingContainer = parentDecl; + var enclosingContainer = enclosingDecl; + + // compute the closing container of the symbol's declaration + while (symbolDeclarationEnclosingContainer) { + if (symbolDeclarationEnclosingContainer.kind === (dynamic ? PullElementKind.DynamicModule : PullElementKind.Container)) { + break; + } + symbolDeclarationEnclosingContainer = symbolDeclarationEnclosingContainer.getParentDecl(); + } + + // if the symbol in question is not a global, compute the nearest + // enclosing declaration from the point of usage + if (symbolDeclarationEnclosingContainer) { + while (enclosingContainer) { + if (enclosingContainer.kind === (dynamic ? PullElementKind.DynamicModule : PullElementKind.Container)) { + break; + } + + enclosingContainer = enclosingContainer.getParentDecl(); + } + } + + if (symbolDeclarationEnclosingContainer && enclosingContainer) { + var same = symbolDeclarationEnclosingContainer === enclosingContainer; + + // initialized module object variables are bound to their parent's decls + if (!same && symbol.anyDeclHasFlag(PullElementFlags.InitializedModule)) { + same = symbolDeclarationEnclosingContainer === enclosingContainer.getParentDecl(); + } + + return same; + } + } + } + } + + return false; + } + + // In some cases, when emitting a name, the emitter needs to qualify a symbol + // name by first emitting its parent's name. This generally happens when the + // name referenced is in scope in TypeScript, but not in Javascript. This is true + // in any of the following 3 cases: + // - It is an enum member, even if accessed from within the enum declaration in which it is defined + // - It is an exported var, even if accessed from within the module declaration in which it is defined + // - It is an exported member of the current module, but is never defined in this particular module + // declaration (i.e. it is only defined in other components of the same merged module) + private shouldQualifySymbolNameWithParentName(symbol: PullSymbol): boolean { + var enclosingContextDeclPath = this.declStack; + var symbolDeclarations = symbol.getDeclarations(); + for (var i = 0; i < symbolDeclarations.length; i++) { + var currentDecl = symbolDeclarations[i]; + var declParent = currentDecl.getParentDecl(); + + if (currentDecl.kind === PullElementKind.EnumMember) { + return true; + } + + // All decls of the same symbol must agree on whether they are exported + // If one decl is not exported, none of them are, and it is safe to + // assume it is just a local + if (!hasFlag(currentDecl.flags, PullElementFlags.Exported)) { + return false; + } + + // Section 10.6: + // When a variable is exported, all references to the variable in the body of the + // module are replaced with + // .< VariableName> + if (currentDecl.kind === PullElementKind.Variable && !hasFlag(currentDecl.flags, PullElementFlags.ImplicitVariable)) { + return true; + } + + if (ArrayUtilities.contains(this.declStack, declParent)) { + return false; + } + } + + return true; + } + + // Get the symbol information to be used for emitting the ast + private getSymbolForEmit(ast: ISyntaxElement) { + var pullSymbol = this.semanticInfoChain.getSymbolForAST(ast); + var pullSymbolAlias = this.semanticInfoChain.getAliasSymbolForAST(ast); + if (pullSymbol && pullSymbolAlias) { + var symbolToCompare = isTypesOnlyLocation(ast) ? + pullSymbolAlias.getExportAssignedTypeSymbol() : + pullSymbolAlias.getExportAssignedValueSymbol(); + + if (pullSymbol === symbolToCompare) { + pullSymbol = pullSymbolAlias; + pullSymbolAlias = null; + } + } + return { symbol: pullSymbol, aliasSymbol: pullSymbolAlias }; + } + + public emitName(name: ISyntaxToken, addThis: boolean) { + this.emitComments(name, true); + this.recordSourceMappingStart(name); + if (name.text().length > 0) { + var symbolForEmit = this.getSymbolForEmit(name); + var pullSymbol = symbolForEmit.symbol; + if (!pullSymbol) { + pullSymbol = this.semanticInfoChain.anyTypeSymbol; + } + var pullSymbolAlias = symbolForEmit.aliasSymbol; + var pullSymbolKind = pullSymbol.kind; + var isLocalAlias = pullSymbolAlias && (pullSymbolAlias.getDeclarations()[0].getParentDecl() === this.getEnclosingDecl()); + if (addThis && (this.emitState.container !== EmitContainer.Args) && pullSymbol) { + var pullSymbolContainer = pullSymbol.getContainer(); + + if (pullSymbolContainer) { + var pullSymbolContainerKind = pullSymbolContainer.kind; + + if (PullHelpers.symbolIsModule(pullSymbolContainer) || pullSymbolContainerKind === PullElementKind.Enum || + pullSymbolContainer.anyDeclHasFlag(PullElementFlags.InitializedModule | PullElementFlags.Enum)) { + var needToEmitParentName = this.shouldQualifySymbolNameWithParentName(pullSymbol); + if (needToEmitParentName) { + var parentDecl = pullSymbol.getDeclarations()[0].getParentDecl(); + Debug.assert(parentDecl && !parentDecl.isRootDecl()); + this.writeToOutput(this.getModuleName(parentDecl, /* changeNameIfAnyDeclarationInContext */ true) + "."); + } + } + else if (pullSymbolContainerKind === PullElementKind.DynamicModule || + pullSymbolContainer.anyDeclHasFlag(PullElementFlags.InitializedDynamicModule)) { + if (pullSymbolKind === PullElementKind.Property) { + // If dynamic module + this.writeToOutput("exports."); + } + else if (pullSymbol.anyDeclHasFlag(PullElementFlags.Exported) && + !isLocalAlias && + !pullSymbol.anyDeclHasFlag(PullElementFlags.ImplicitVariable) && + pullSymbol.kind !== PullElementKind.ConstructorMethod && + pullSymbol.kind !== PullElementKind.Class && + pullSymbol.kind !== PullElementKind.Enum) { + this.writeToOutput("exports."); + } + } + } + } + + this.writeToOutput(name.text()); + } + + this.recordSourceMappingEnd(name); + this.emitComments(name, false); + } + + public recordSourceMappingNameStart(name: string) { + if (this.sourceMapper) { + var nameIndex = -1; + if (name) { + if (this.sourceMapper.currentNameIndex.length > 0) { + var parentNameIndex = this.sourceMapper.currentNameIndex[this.sourceMapper.currentNameIndex.length - 1]; + if (parentNameIndex !== -1) { + name = this.sourceMapper.names[parentNameIndex] + "." + name; + } + } + + // Look if there already exists name + var nameIndex = this.sourceMapper.names.length - 1; + for (nameIndex; nameIndex >= 0; nameIndex--) { + if (this.sourceMapper.names[nameIndex] === name) { + break; + } + } + + if (nameIndex === -1) { + nameIndex = this.sourceMapper.names.length; + this.sourceMapper.names.push(name); + } + } + this.sourceMapper.currentNameIndex.push(nameIndex); + } + } + + public recordSourceMappingNameEnd() { + if (this.sourceMapper) { + this.sourceMapper.currentNameIndex.pop(); + } + } + + private recordSourceMappingStart(ast: ISyntaxElement) { + if (this.sourceMapper && ASTHelpers.isValidAstNode(ast)) { + var text = this.text(); + this.recordSourceMappingSpanStart(ast, start(ast, text), end(ast, text)); + } + } + + private recordSourceMappingCommentStart(comment: Comment) { + this.recordSourceMappingSpanStart(comment, comment.start(), comment.end()); + } + + private recordSourceMappingSpanStart(ast: any, start: number, end: number) { + if (this.sourceMapper && ast && start !== -1 && end !== -1) { + var lineCol = { line: -1, character: -1 }; + var sourceMapping = new SourceMapping(); + sourceMapping.start.emittedColumn = this.emitState.column; + sourceMapping.start.emittedLine = this.emitState.line; + // REVIEW: check time consumed by this binary search (about two per leaf statement) + var lineMap = this.document.lineMap(); + lineMap.fillLineAndCharacterFromPosition(start, lineCol); + sourceMapping.start.sourceColumn = lineCol.character; + sourceMapping.start.sourceLine = lineCol.line + 1; + lineMap.fillLineAndCharacterFromPosition(end, lineCol); + sourceMapping.end.sourceColumn = lineCol.character; + sourceMapping.end.sourceLine = lineCol.line + 1; + + Debug.assert(!isNaN(sourceMapping.start.emittedColumn)); + Debug.assert(!isNaN(sourceMapping.start.emittedLine)); + Debug.assert(!isNaN(sourceMapping.start.sourceColumn)); + Debug.assert(!isNaN(sourceMapping.start.sourceLine)); + Debug.assert(!isNaN(sourceMapping.end.sourceColumn)); + Debug.assert(!isNaN(sourceMapping.end.sourceLine)); + + if (this.sourceMapper.currentNameIndex.length > 0) { + sourceMapping.nameIndex = this.sourceMapper.currentNameIndex[this.sourceMapper.currentNameIndex.length - 1]; + } + // Set parent and child relationship + var siblings = this.sourceMapper.currentMappings[this.sourceMapper.currentMappings.length - 1]; + siblings.push(sourceMapping); + this.sourceMapper.currentMappings.push(sourceMapping.childMappings); + this.sourceMapper.increaseMappingLevel(ast); + } + } + + private recordSourceMappingEnd(ast: ISyntaxElement) { + if (this.sourceMapper && ASTHelpers.isValidAstNode(ast)) { + var text = this.text(); + this.recordSourceMappingSpanEnd(ast, start(ast, text), end(ast, text)); + } + } + + private recordSourceMappingCommentEnd(ast: Comment) { + if (this.sourceMapper && ASTHelpers.isValidSpan(ast)) { + this.recordSourceMappingSpanEnd(ast, ast.start(), ast.end()); + } + } + + private recordSourceMappingSpanEnd(ast: any, start: number, end: number) { + if (this.sourceMapper && ast && start !== -1 && end !== -1) { + // Pop source mapping childs + this.sourceMapper.currentMappings.pop(); + + // Get the last source mapping from sibling list = which is the one we are recording end for + var siblings = this.sourceMapper.currentMappings[this.sourceMapper.currentMappings.length - 1]; + var sourceMapping = siblings[siblings.length - 1]; + + sourceMapping.end.emittedColumn = this.emitState.column; + sourceMapping.end.emittedLine = this.emitState.line; + + Debug.assert(!isNaN(sourceMapping.end.emittedColumn)); + Debug.assert(!isNaN(sourceMapping.end.emittedLine)); + + this.sourceMapper.decreaseMappingLevel(ast); + } + } + + // Note: may throw exception. + public getOutputFiles(): OutputFile[] { + // Output a source mapping. As long as we haven't gotten any errors yet. + var result: OutputFile[] = []; + if (this.sourceMapper !== null) { + this.sourceMapper.emitSourceMapping(); + result.push(this.sourceMapper.getOutputFile()); + } + + result.push(this.outfile.getOutputFile()); + return result; + } + + private emitParameterPropertyAndMemberVariableAssignments(): void { + // emit any parameter properties first + var constructorDecl = getLastConstructor(this.thisClassNode); + + if (constructorDecl) { + for (var i = 0, n = constructorDecl.callSignature.parameterList.parameters.length; i < n; i++) { + var parameter = constructorDecl.callSignature.parameterList.parameters[i]; + + var parameterDecl = this.semanticInfoChain.getDeclForAST(parameter); + if (hasFlag(parameterDecl.flags, PullElementFlags.PropertyParameter)) { + this.emitIndent(); + this.recordSourceMappingStart(parameter); + this.writeToOutputWithSourceMapRecord("this." + parameter.identifier.text(), parameter.identifier); + this.writeToOutput(" = "); + this.writeToOutputWithSourceMapRecord(parameter.identifier.text(), parameter.identifier); + this.writeLineToOutput(";"); + this.recordSourceMappingEnd(parameter); + } + } + } + + for (var i = 0, n = this.thisClassNode.classElements.length; i < n; i++) { + if (this.thisClassNode.classElements[i].kind() === SyntaxKind.MemberVariableDeclaration) { + var varDecl = this.thisClassNode.classElements[i]; + if (!hasModifier(varDecl.modifiers, PullElementFlags.Static) && varDecl.variableDeclarator.equalsValueClause) { + this.emitIndent(); + this.emitMemberVariableDeclaration(varDecl); + this.writeLineToOutput(""); + } + } + } + } + + private isOnSameLine(pos1: number, pos2: number): boolean { + if (pos1 < 0 || pos2 < 0) { + // Missing element. Assume it's on the same line as the other element. + return true; + } + + var lineMap = this.document.lineMap(); + return lineMap.getLineNumberFromPosition(pos1) === lineMap.getLineNumberFromPosition(pos2); + } + + private emitCommaSeparatedList(parent: ISyntaxElement, list: T[], buffer: string, preserveNewLines: boolean): void { + if (list === null || list.length === 0) { + return; + } + + // If the first element isn't on hte same line as the parent node, then we need to + // start with a newline. + var text = this.text(); + var startLine = preserveNewLines && !this.isOnSameLine(end(parent, text), end(list[0], text)); + + if (preserveNewLines) { + // Any elements on a new line will have to be indented. + this.indenter.increaseIndent(); + } + + // If we're starting on a newline, then emit an actual newline. Otherwise write out + // the buffer character before hte first element. + if (startLine) { + this.writeLineToOutput(""); + } + else { + this.writeToOutput(buffer); + } + + for (var i = 0, n = list.length; i < n; i++) { + var emitNode = list[i]; + + // Write out the element, emitting an indent if we're on a new line. + this.emitJavascript(emitNode, startLine); + + if (i < (n - 1)) { + // If the next element start on a different line than this element ended on, + // then we want to start on a newline. Emit the comma with a newline. + // Otherwise, emit the comma with the space. + startLine = preserveNewLines && !this.isOnSameLine(end(emitNode, text), start(list[i + 1], text)); + if (startLine) { + this.writeLineToOutput(","); + } + else { + this.writeToOutput(", "); + } + } + } + + if (preserveNewLines) { + // We're done with all the elements. Return the indent back to where it was. + this.indenter.decreaseIndent(); + } + + // If the last element isn't on the same line as the parent, then emit a newline + // after the last element and emit our indent so the list's terminator will be + // on the right line. Otherwise, emit the buffer string between the last value + // and the terminator. + if (preserveNewLines && !this.isOnSameLine(end(parent, text), end(list[list.length - 1], text))) { + this.writeLineToOutput(""); + this.emitIndent(); + } + else { + this.writeToOutput(buffer); + } + } + + public emitList(list: T[], useNewLineSeparator = true, startInclusive = 0, endExclusive = list.length) { + if (list === null) { + return; + } + + this.emitComments(list, true); + var lastEmittedNode: ISyntaxElement = null; + + for (var i = startInclusive; i < endExclusive; i++) { + var node = list[i]; + + if (this.shouldEmit(node)) { + this.emitSpaceBetweenConstructs(lastEmittedNode, node); + + this.emitJavascript(node, true); + if (useNewLineSeparator) { + this.writeLineToOutput(""); + } + + lastEmittedNode = node; + } + } + + this.emitComments(list, false); + } + + public emitSeparatedList(list: T[], useNewLineSeparator = true, startInclusive = 0, endExclusive = list.length) { + if (list === null) { + return; + } + + this.emitComments(list, true); + var lastEmittedNode: ISyntaxElement = null; + + for (var i = startInclusive; i < endExclusive; i++) { + var node = list[i]; + + if (this.shouldEmit(node)) { + this.emitSpaceBetweenConstructs(lastEmittedNode, node); + + this.emitJavascript(node, true); + if (useNewLineSeparator) { + this.writeLineToOutput(""); + } + + lastEmittedNode = node; + } + } + + this.emitComments(list, false); + } + + private isDirectivePrologueElement(node: ISyntaxElement) { + if (node.kind() === SyntaxKind.ExpressionStatement) { + var exprStatement = node; + return exprStatement.expression.kind() === SyntaxKind.StringLiteral; + } + + return false; + } + + // If these two constructs had more than one line between them originally, then emit at + // least one blank line between them. + public emitSpaceBetweenConstructs(node1: ISyntaxElement, node2: ISyntaxElement): void { + if (node1 === null || node2 === null) { + return; + } + + var text = this.text(); + if (start(node1, text) === -1 || end(node1, text) === -1 || start(node2, text) === -1 || end(node2, text) === -1) { + return; + } + + var lineMap = this.document.lineMap(); + var node1EndLine = lineMap.getLineNumberFromPosition(end(node1, text)); + var node2StartLine = lineMap.getLineNumberFromPosition(start(node2, text)); + + if ((node2StartLine - node1EndLine) > 1) { + this.writeLineToOutput("", /*force:*/ true); + } + } + + // We consider a sequence of comments to be a detached from an ast if there are no blank lines + // between them, and there is a blank line after the last one and the node they're attached + // to. + private getDetachedComments(element: ISyntaxElement): Comment[] { + var text = this.text(); + var preComments = TypeScript.ASTHelpers.preComments(element, text); + if (preComments) { + var lineMap = this.document.lineMap(); + + var detachedComments: Comment[] = []; + var lastComment: Comment = null; + + for (var i = 0, n = preComments.length; i < n; i++) { + var comment = preComments[i]; + + if (lastComment) { + var lastCommentLine = lineMap.getLineNumberFromPosition(lastComment.end()); + var commentLine = lineMap.getLineNumberFromPosition(comment.start()); + + if (commentLine >= lastCommentLine + 2) { + // There was a blank line between the last comment and this comment. This + // comment is not part of the copyright comments. Return what we have so + // far. + return detachedComments; + } + } + + detachedComments.push(comment); + lastComment = comment; + } + + // All comments look like they could have been part of the copyright header. Make + // sure there is at least one blank line between it and the node. If not, it's not + // a copyright header. + var lastCommentLine = lineMap.getLineNumberFromPosition(ArrayUtilities.last(detachedComments).end()); + var astLine = lineMap.getLineNumberFromPosition(start(element, text)); + if (astLine >= lastCommentLine + 2) { + return detachedComments; + } + } + + // No usable copyright comments found. + return []; + } + + private emitPossibleCopyrightHeaders(script: SourceUnitSyntax): void { + this.emitDetachedComments(script.moduleElements); + } + + private emitDetachedComments(list: ISyntaxNodeOrToken[]): void { + if (list.length > 0) { + var firstElement = childAt(list, 0); + + this.detachedCommentsElement = firstElement; + this.emitCommentsArray(this.getDetachedComments(this.detachedCommentsElement), /*trailing:*/ false); + } + } + + public emitScriptElements(sourceUnit: SourceUnitSyntax) { + var list = sourceUnit.moduleElements; + + this.emitPossibleCopyrightHeaders(sourceUnit); + + // First, emit all the prologue elements. + for (var i = 0, n = list.length; i < n; i++) { + var node = list[i]; + + if (!this.isDirectivePrologueElement(node)) { + break; + } + + this.emitJavascript(node, true); + this.writeLineToOutput(""); + } + + // Now emit __extends or a _this capture if necessary. + this.emitPrologue(sourceUnit); + + var isExternalModule = this.document.syntaxTree().isExternalModule(); + var isNonElidedExternalModule = isExternalModule && !ASTHelpers.scriptIsElided(sourceUnit); + if (isNonElidedExternalModule) { + this.recordSourceMappingStart(sourceUnit); + + if (this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) { // AMD + var dependencyList = "[\"require\", \"exports\""; + var importList = "require, exports"; + + var importAndDependencyList = this.getModuleImportAndDependencyList(sourceUnit); + importList += importAndDependencyList.importList; + dependencyList += importAndDependencyList.dependencyList + "]"; + + this.writeLineToOutput("define(" + dependencyList + "," + " function(" + importList + ") {"); + } + } + + if (isExternalModule) { + var temp = this.setContainer(EmitContainer.DynamicModule); + + var svModuleName = this.moduleName; + this.moduleName = syntaxTree(sourceUnit).fileName(); + if (TypeScript.isTSFile(this.moduleName)) { + this.moduleName = this.moduleName.substring(0, this.moduleName.length - ".ts".length); + } + + // if the external module has an "export =" identifier, we'll + // set it in the ExportAssignment emit method + this.setExportAssignment(null); + + if(this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) { + this.indenter.increaseIndent(); + } + + var externalModule = this.semanticInfoChain.getDeclForAST(this.document.sourceUnit()); + + if (hasFlag(externalModule.flags, PullElementFlags.MustCaptureThis)) { + this.writeCaptureThisStatement(sourceUnit); + } + + this.pushDecl(externalModule); + } + + this.emitList(list, /*useNewLineSeparator:*/ true, /*startInclusive:*/ i, /*endExclusive:*/ n); + + if (isExternalModule) { + if (this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) { + this.indenter.decreaseIndent(); + } + + if (isNonElidedExternalModule) { + var exportAssignment = this.getExportAssignment(); + var exportAssignmentIdentifierText = exportAssignment ? exportAssignment.identifier.text() : null; + var exportAssignmentValueSymbol = (externalModule.getSymbol(this.semanticInfoChain)).getExportAssignedValueSymbol(); + + if (this.emitOptions.compilationSettings().moduleGenTarget() === ModuleGenTarget.Asynchronous) { // AMD + if (exportAssignmentIdentifierText && exportAssignmentValueSymbol && !(exportAssignmentValueSymbol.kind & PullElementKind.SomeTypeReference)) { + // indent was decreased for AMD above + this.indenter.increaseIndent(); + this.emitIndent(); + this.writeToOutputWithSourceMapRecord("return " + exportAssignmentIdentifierText, exportAssignment); + this.writeLineToOutput(";"); + this.indenter.decreaseIndent(); + } + this.writeToOutput("});"); + } + else if (exportAssignmentIdentifierText && exportAssignmentValueSymbol && !(exportAssignmentValueSymbol.kind & PullElementKind.SomeTypeReference)) { + this.emitIndent(); + this.writeToOutputWithSourceMapRecord("module.exports = " + exportAssignmentIdentifierText, exportAssignment); + this.writeToOutput(";"); + } + + this.recordSourceMappingEnd(sourceUnit); + this.writeLineToOutput(""); + } + + this.setContainer(temp); + this.moduleName = svModuleName; + this.popDecl(externalModule); + } + } + + public emitConstructorStatements(funcDecl: ConstructorDeclarationSyntax) { + var list = funcDecl.block.statements; + + if (list === null) { + return; + } + + this.emitComments(list, true); + + var emitPropertyAssignmentsAfterSuperCall = ASTHelpers.getExtendsHeritageClause(this.thisClassNode.heritageClauses) !== null; + var propertyAssignmentIndex = emitPropertyAssignmentsAfterSuperCall ? 1 : 0; + var lastEmittedNode: ISyntaxElement = null; + + for (var i = 0, n = list.length; i < n; i++) { + // In some circumstances, class property initializers must be emitted immediately after the 'super' constructor + // call which, in these cases, must be the first statement in the constructor body + if (i === propertyAssignmentIndex) { + this.emitParameterPropertyAndMemberVariableAssignments(); + } + + var node = list[i]; + + if (this.shouldEmit(node)) { + this.emitSpaceBetweenConstructs(lastEmittedNode, node); + + this.emitJavascript(node, true); + this.writeLineToOutput(""); + + lastEmittedNode = node; + } + } + + if (i === propertyAssignmentIndex) { + this.emitParameterPropertyAndMemberVariableAssignments(); + } + + this.emitComments(list, false); + } + + // tokenId is the id the preceding token + public emitJavascript(ast: ISyntaxElement, startLine: boolean) { + if (ast === null) { + return; + } + + if (startLine && + this.indenter.indentAmt > 0) { + + this.emitIndent(); + } + + this.emit(ast); + } + + public emitAccessorMemberDeclaration(funcDecl: ISyntaxElement, name: ISyntaxToken, className: string, isProto: boolean) { + if (funcDecl.kind() !== SyntaxKind.GetAccessor) { + var accessorSymbol = PullHelpers.getAccessorSymbol(funcDecl, this.semanticInfoChain); + if (accessorSymbol.getGetter()) { + return; + } + } + + this.emitIndent(); + this.recordSourceMappingStart(funcDecl); + + this.writeToOutput("Object.defineProperty(" + className); + if (isProto) { + this.writeToOutput(".prototype, "); + } + else { + this.writeToOutput(", "); + } + + var functionName = name.text(); + if (isQuoted(functionName)) { + this.writeToOutput(functionName); + } + else { + this.writeToOutput('"' + functionName + '"'); + } + + this.writeLineToOutput(", {"); + + this.indenter.increaseIndent(); + + var accessors = PullHelpers.getGetterAndSetterFunction(funcDecl, this.semanticInfoChain); + if (accessors.getter) { + this.emitIndent(); + this.recordSourceMappingStart(accessors.getter); + this.emitComments(accessors.getter, true); + this.writeToOutput("get: "); + this.emitAccessorBody(accessors.getter, accessors.getter.callSignature.parameterList, accessors.getter.block); + this.writeLineToOutput(","); + } + + if (accessors.setter) { + this.emitIndent(); + this.recordSourceMappingStart(accessors.setter); + this.emitComments(accessors.setter, true); + this.writeToOutput("set: "); + this.emitAccessorBody(accessors.setter, accessors.setter.callSignature.parameterList, accessors.setter.block); + this.writeLineToOutput(","); + } + + this.emitIndent(); + this.writeLineToOutput("enumerable: true,"); + this.emitIndent(); + this.writeLineToOutput("configurable: true"); + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeLineToOutput("});"); + this.recordSourceMappingEnd(funcDecl); + } + + private emitAccessorBody(funcDecl: ISyntaxElement, parameterList: ParameterListSyntax, block: BlockSyntax): void { + var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + this.pushDecl(pullDecl); + + this.recordSourceMappingStart(funcDecl); + this.writeToOutput("function "); + this.emitParameterList(parameterList); + + var parameters = parameterList.parameters; + this.emitFunctionBodyStatements(null, funcDecl, parameters, block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(funcDecl); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(funcDecl); + this.popDecl(pullDecl); + } + + public emitClass(classDecl: ClassDeclarationSyntax) { + var pullDecl = this.semanticInfoChain.getDeclForAST(classDecl); + this.pushDecl(pullDecl); + + var svClassNode = this.thisClassNode; + this.thisClassNode = classDecl; + var className = classDecl.identifier.text(); + this.emitComments(classDecl, true); + var temp = this.setContainer(EmitContainer.Class); + + this.recordSourceMappingStart(classDecl); + this.writeToOutput("var " + className); + + var hasBaseClass = ASTHelpers.getExtendsHeritageClause(classDecl.heritageClauses) !== null; + var baseTypeReference: ISyntaxElement = null; + var varDecl: VariableDeclaratorSyntax = null; + + if (hasBaseClass) { + this.writeLineToOutput(" = (function (_super) {"); + } + else { + this.writeLineToOutput(" = (function () {"); + } + + this.recordSourceMappingNameStart(className); + this.indenter.increaseIndent(); + + if (hasBaseClass) { + baseTypeReference = ASTHelpers.getExtendsHeritageClause(classDecl.heritageClauses).typeNames[0]; + this.emitIndent(); + this.writeToOutputWithSourceMapRecord("__extends(" + className + ", _super)", baseTypeReference); + this.writeLineToOutput(";"); + } + + this.emitIndent(); + + var constrDecl = getLastConstructor(classDecl); + + // output constructor + if (constrDecl) { + // declared constructor + this.emit(constrDecl); + this.writeLineToOutput(""); + } + else { + this.recordSourceMappingStart(classDecl); + // default constructor + this.indenter.increaseIndent(); + this.writeLineToOutput("function " + classDecl.identifier.text() + "() {"); + this.recordSourceMappingNameStart("constructor"); + if (hasBaseClass) { + this.emitIndent(); + this.writeToOutputWithSourceMapRecord("_super.apply(this, arguments)", baseTypeReference); + this.writeLineToOutput(";"); + } + + if (this.shouldCaptureThis(classDecl)) { + this.writeCaptureThisStatement(classDecl); + } + + this.emitParameterPropertyAndMemberVariableAssignments(); + + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeToken(classDecl.closeBraceToken); + this.writeLineToOutput(""); + + this.recordSourceMappingNameEnd(); + this.recordSourceMappingEnd(classDecl); + } + + this.emitClassMembers(classDecl); + + this.emitIndent(); + this.writeToOutputWithSourceMapRecord("return " + className + ";", classDecl.closeBraceToken); + this.writeLineToOutput(""); + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeToken(classDecl.closeBraceToken); + this.recordSourceMappingNameEnd(); + this.recordSourceMappingStart(classDecl); + this.writeToOutput(")("); + if (hasBaseClass) { + this.emitJavascript(baseTypeReference, /*startLine:*/ false); + } + this.writeToOutput(");"); + this.recordSourceMappingEnd(classDecl); + + if ((temp === EmitContainer.Module || temp === EmitContainer.DynamicModule) && hasFlag(pullDecl.flags, PullElementFlags.Exported)) { + this.writeLineToOutput(""); + this.emitIndent(); + var modName = temp === EmitContainer.Module ? this.moduleName : "exports"; + this.writeToOutputWithSourceMapRecord(modName + "." + className + " = " + className + ";", classDecl); + } + + this.recordSourceMappingEnd(classDecl); + this.emitComments(classDecl, false); + this.setContainer(temp); + this.thisClassNode = svClassNode; + + this.popDecl(pullDecl); + } + + private emitClassMembers(classDecl: ClassDeclarationSyntax): void { + // First, emit all the functions. + var lastEmittedMember: ISyntaxElement = null; + + for (var i = 0, n = classDecl.classElements.length; i < n; i++) { + var memberDecl = classDecl.classElements[i]; + + if (memberDecl.kind() === SyntaxKind.GetAccessor) { + this.emitSpaceBetweenConstructs(lastEmittedMember, memberDecl); + var getter = memberDecl; + this.emitAccessorMemberDeclaration(getter, getter.propertyName, classDecl.identifier.text(), + !hasModifier(getter.modifiers, PullElementFlags.Static)); + lastEmittedMember = memberDecl; + } + else if (memberDecl.kind() === SyntaxKind.SetAccessor) { + this.emitSpaceBetweenConstructs(lastEmittedMember, memberDecl); + var setter = memberDecl; + this.emitAccessorMemberDeclaration(setter, setter.propertyName, classDecl.identifier.text(), + !hasModifier(setter.modifiers, PullElementFlags.Static)); + lastEmittedMember = memberDecl; + } + else if (memberDecl.kind() === SyntaxKind.MemberFunctionDeclaration) { + + var memberFunction = memberDecl; + + if (memberFunction.block) { + this.emitSpaceBetweenConstructs(lastEmittedMember, memberDecl); + + this.emitClassMemberFunctionDeclaration(classDecl, memberFunction); + lastEmittedMember = memberDecl; + } + } + } + + // Now emit all the statics. + for (var i = 0, n = classDecl.classElements.length; i < n; i++) { + var memberDecl = classDecl.classElements[i]; + + if (memberDecl.kind() === SyntaxKind.MemberVariableDeclaration) { + var varDecl = memberDecl; + + if (hasModifier(varDecl.modifiers, PullElementFlags.Static) && varDecl.variableDeclarator.equalsValueClause) { + this.emitSpaceBetweenConstructs(lastEmittedMember, varDecl); + + this.emitIndent(); + this.recordSourceMappingStart(varDecl); + + this.emitComments(varDecl, true); + var varDeclName = varDecl.variableDeclarator.propertyName.text(); + if (isQuoted(varDeclName) || varDecl.variableDeclarator.propertyName.kind() !== SyntaxKind.IdentifierName) { + this.writeToOutput(classDecl.identifier.text() + "[" + varDeclName + "]"); + } + else { + this.writeToOutput(classDecl.identifier.text() + "." + varDeclName); + } + + this.emit(varDecl.variableDeclarator.equalsValueClause); + + this.recordSourceMappingEnd(varDecl); + this.writeLineToOutput(";"); + + lastEmittedMember = varDecl; + } + } + } + } + + private emitClassMemberFunctionDeclaration(classDecl: ClassDeclarationSyntax, funcDecl: MemberFunctionDeclarationSyntax): void { + this.emitIndent(); + this.recordSourceMappingStart(funcDecl); + this.emitComments(funcDecl, true); + var functionName = funcDecl.propertyName.text(); + + this.writeToOutput(classDecl.identifier.text()); + + if (!hasModifier(funcDecl.modifiers, PullElementFlags.Static)) { + this.writeToOutput(".prototype"); + } + + if (isQuoted(functionName) || funcDecl.propertyName.kind() !== SyntaxKind.IdentifierName) { + this.writeToOutput("[" + functionName + "] = "); + } + else { + this.writeToOutput("." + functionName + " = "); + } + + var pullDecl = this.semanticInfoChain.getDeclForAST(funcDecl); + this.pushDecl(pullDecl); + + this.recordSourceMappingStart(funcDecl); + this.writeToOutput("function "); + + this.emitParameterList(funcDecl.callSignature.parameterList); + + var parameters = funcDecl.callSignature.parameterList.parameters; + this.emitFunctionBodyStatements(funcDecl.propertyName.text(), funcDecl, parameters, funcDecl.block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(funcDecl); + + this.emitComments(funcDecl, false); + + this.recordSourceMappingEnd(funcDecl); + this.popDecl(pullDecl); + + this.writeLineToOutput(";"); + } + + private requiresExtendsBlock(moduleElements: IModuleElementSyntax[]): boolean { + for (var i = 0, n = moduleElements.length; i < n; i++) { + var moduleElement = moduleElements[i]; + + if (moduleElement.kind() === SyntaxKind.ModuleDeclaration) { + var moduleAST = moduleElement; + + if (!hasModifier(moduleAST.modifiers, PullElementFlags.Ambient) && this.requiresExtendsBlock(moduleAST.moduleElements)) { + return true; + } + } + else if (moduleElement.kind() === SyntaxKind.ClassDeclaration) { + var classDeclaration = moduleElement; + + if (!hasModifier(classDeclaration.modifiers, PullElementFlags.Ambient) && ASTHelpers.getExtendsHeritageClause(classDeclaration.heritageClauses) !== null) { + return true; + } + } + } + + return false; + } + + public emitPrologue(sourceUnit: SourceUnitSyntax) { + if (!this.extendsPrologueEmitted) { + if (this.requiresExtendsBlock(sourceUnit.moduleElements)) { + this.extendsPrologueEmitted = true; + this.writeLineToOutput("var __extends = this.__extends || function (d, b) {"); + this.writeLineToOutput(" for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];"); + this.writeLineToOutput(" function __() { this.constructor = d; }"); + this.writeLineToOutput(" __.prototype = b.prototype;"); + this.writeLineToOutput(" d.prototype = new __();"); + this.writeLineToOutput("};"); + } + } + + if (!this.globalThisCapturePrologueEmitted) { + if (this.shouldCaptureThis(sourceUnit)) { + this.globalThisCapturePrologueEmitted = true; + this.writeLineToOutput(this.captureThisStmtString); + } + } + } + + public emitThis() { + if (!this.inWithBlock && this.inArrowFunction) { + this.writeToOutput("_this"); + } + else { + this.writeToOutput("this"); + } + } + + public emitBlockOrStatement(node: ISyntaxElement): void { + if (node.kind() === SyntaxKind.Block) { + this.emit(node); + } + else { + this.writeLineToOutput(""); + this.indenter.increaseIndent(); + this.emitJavascript(node, true); + this.indenter.decreaseIndent(); + } + } + + public emitLiteralExpression(expression: ISyntaxToken): void { + switch (expression.kind()) { + case SyntaxKind.NullKeyword: + this.writeToken(expression); + break; + case SyntaxKind.FalseKeyword: + this.writeToken(expression); + break; + case SyntaxKind.TrueKeyword: + this.writeToken(expression); + break; + default: + throw Errors.abstract(); + } + } + + public emitThisExpression(expression: ISyntaxToken): void { + if (!this.inWithBlock && this.inArrowFunction) { + this.writeToOutputWithSourceMapRecord("_this", expression); + } + else { + this.writeToken(expression); + } + } + + public emitSuperExpression(expression: ISyntaxToken): void { + if (PullHelpers.isInStaticMemberContext(expression, this.semanticInfoChain)) { + this.writeToOutputWithSourceMapRecord("_super", expression); + } + else { + this.writeToOutputWithSourceMapRecord("_super.prototype", expression); + } + } + + private hasTrailingComment(token: ISyntaxToken) { + return token.hasTrailingTrivia() && token.trailingTrivia().hasComment(); + } + + public emitParenthesizedExpression(parenthesizedExpression: ParenthesizedExpressionSyntax): void { + var omitParentheses = false; + + if (parenthesizedExpression.expression.kind() === SyntaxKind.CastExpression && !this.hasTrailingComment(parenthesizedExpression.openParenToken)) { + var castedExpression = (parenthesizedExpression.expression).expression; + + // Make sure we consider all nested cast expressions, e.g.: + // (-A).x; + while (castedExpression.kind() == SyntaxKind.CastExpression) { + castedExpression = (castedExpression).expression; + } + + // We have an expression of the form: (SubExpr) + // Emitting this as (SubExpr) is really not desirable. Just emit the subexpr as is. + // We cannot generalize this rule however, as omitting the parentheses could cause change in the semantics of the generated + // code if the casted expression has a lower precedence than the rest of the expression, e.g.: + // (new A).foo should be emitted as (new A).foo and not new A.foo + // (typeof A).toString() should be emitted as (typeof A).toString() and not typeof A.toString() + // (function foo() { })() should be emitted as an IIF (function foo(){})() and not declaration function foo(){} () + // Parenthesis can be safelly removed from: + // Literals + // MemberAccessExpressions + // ElementAccessExpressions + // InvocationExpression, only if they are not part of an object creation (new) expression; e.g.: + // new (A()) removing the parentheses would result in calling A as a constructor, instead of calling the + // result of the function invocation A() as a constructor + switch (castedExpression.kind()) { + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.IdentifierName: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.MemberAccessExpression: + case SyntaxKind.ElementAccessExpression: + omitParentheses = true; + break; + + case SyntaxKind.InvocationExpression: + if (parenthesizedExpression.parent.kind() !== SyntaxKind.ObjectCreationExpression) { + omitParentheses = true; + } + + break; + } + } + + if (omitParentheses) { + this.emit(parenthesizedExpression.expression); + } + else { + this.recordSourceMappingStart(parenthesizedExpression); + this.writeToken(parenthesizedExpression.openParenToken); + this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(parenthesizedExpression.openParenToken, this.text()), /*trailing:*/ false); + this.emit(parenthesizedExpression.expression); + this.writeToken(parenthesizedExpression.closeParenToken); + this.recordSourceMappingEnd(parenthesizedExpression); + } + } + + public emitCastExpression(expression: CastExpressionSyntax): void { + this.emit(expression.expression); + } + + public emitPrefixUnaryExpression(expression: PrefixUnaryExpressionSyntax): void { + var nodeType = expression.kind(); + + this.recordSourceMappingStart(expression); + switch (nodeType) { + case SyntaxKind.LogicalNotExpression: + this.writeToken(expression.operatorToken); + this.emit(expression.operand); + break; + case SyntaxKind.BitwiseNotExpression: + this.writeToken(expression.operatorToken); + this.emit(expression.operand); + break; + case SyntaxKind.NegateExpression: + this.writeToken(expression.operatorToken); + if (expression.operand.kind() === SyntaxKind.NegateExpression || expression.operand.kind() === SyntaxKind.PreDecrementExpression) { + this.writeToOutput(" "); + } + this.emit(expression.operand); + break; + case SyntaxKind.PlusExpression: + this.writeToken(expression.operatorToken); + if (expression.operand.kind() === SyntaxKind.PlusExpression || expression.operand.kind() === SyntaxKind.PreIncrementExpression) { + this.writeToOutput(" "); + } + this.emit(expression.operand); + break; + case SyntaxKind.PreIncrementExpression: + this.writeToOutputWithSourceMapRecord("++", expression.operatorToken); + this.emit(expression.operand); + break; + case SyntaxKind.PreDecrementExpression: + this.writeToOutputWithSourceMapRecord("--", expression.operatorToken); + this.emit(expression.operand); + break; + default: + throw Errors.abstract(); + } + + this.recordSourceMappingEnd(expression); + } + + public emitPostfixUnaryExpression(expression: PostfixUnaryExpressionSyntax): void { + var nodeType = expression.kind(); + + this.recordSourceMappingStart(expression); + switch (nodeType) { + case SyntaxKind.PostIncrementExpression: + this.emit(expression.operand); + this.writeToOutputWithSourceMapRecord("++", expression.operatorToken); + break; + case SyntaxKind.PostDecrementExpression: + this.emit(expression.operand); + this.writeToOutputWithSourceMapRecord("--", expression.operatorToken); + break; + default: + throw Errors.abstract(); + } + + this.recordSourceMappingEnd(expression); + } + + public emitTypeOfExpression(expression: TypeOfExpressionSyntax): void { + this.recordSourceMappingStart(expression); + this.writeToken(expression.typeOfKeyword); + this.writeToOutput(" "); + this.emit(expression.expression); + this.recordSourceMappingEnd(expression); + } + + public emitDeleteExpression(expression: DeleteExpressionSyntax): void { + this.recordSourceMappingStart(expression); + this.writeToken(expression.deleteKeyword); + this.writeToOutput(" "); + this.emit(expression.expression); + this.recordSourceMappingEnd(expression); + } + + public emitVoidExpression(expression: VoidExpressionSyntax): void { + this.recordSourceMappingStart(expression); + this.writeToken(expression.voidKeyword); + this.writeToOutput(" "); + this.emit(expression.expression); + this.recordSourceMappingEnd(expression); + } + + private canEmitDottedNameMemberAccessExpression(expression: MemberAccessExpressionSyntax) { + var memberExpressionNodeType = expression.expression.kind(); + + // If the memberAccess is of Name or another member access, we could potentially emit the symbol using the this memberAccessSymol + if (memberExpressionNodeType === SyntaxKind.IdentifierName || memberExpressionNodeType == SyntaxKind.MemberAccessExpression) { + var memberAccessSymbol = this.getSymbolForEmit(expression).symbol; + var memberAccessExpressionSymbol = this.getSymbolForEmit(expression.expression).symbol; + if (memberAccessSymbol && memberAccessExpressionSymbol // We have symbols resolved for this expression and access + && !this.semanticInfoChain.getAliasSymbolForAST(expression.expression) // The access is not off alias + && (PullHelpers.symbolIsModule(memberAccessExpressionSymbol) || memberAccessExpressionSymbol.kind === PullElementKind.Enum || + memberAccessExpressionSymbol.anyDeclHasFlag(PullElementFlags.InitializedModule | PullElementFlags.Enum))) { // container is module + + // If the memberAccess is in the context of the container, we could use the symbol to emit this expression + var memberAccessSymbolKind = memberAccessSymbol.kind; + if (memberAccessSymbolKind === PullElementKind.Property + || memberAccessSymbolKind === PullElementKind.EnumMember + || (memberAccessSymbol.anyDeclHasFlag(PullElementFlags.Exported) && memberAccessSymbolKind === PullElementKind.Variable && !memberAccessSymbol.anyDeclHasFlag(PullElementFlags.InitializedModule | PullElementFlags.Enum)) + || ((memberAccessSymbol.anyDeclHasFlag(PullElementFlags.Exported) && !this.symbolIsUsedInItsEnclosingContainer(memberAccessSymbol)))) { + + // If the expression is member access, we need to verify it as well + if (memberExpressionNodeType === SyntaxKind.MemberAccessExpression) { + return this.canEmitDottedNameMemberAccessExpression(expression.expression); + } + + return true; + } + } + } + + return false; + } + + // Emit the member access expression using the declPath + private emitDottedNameMemberAccessExpression(expression: MemberAccessExpressionSyntax) { + this.recordSourceMappingStart(expression); + if (expression.expression.kind() === SyntaxKind.MemberAccessExpression) { + // Emit the dotted name access expression + this.emitDottedNameMemberAccessExpressionRecurse(expression.expression); + } + else { // Name + this.emitName(expression.expression, /*addThis*/ true); + } + + this.writeToken(expression.dotToken); + this.emitName(expression.name, /*addThis*/ false); + + this.recordSourceMappingEnd(expression); + } + + // Set the right indices for the recursive member access expression before emitting it using the decl path + private emitDottedNameMemberAccessExpressionRecurse(expression: MemberAccessExpressionSyntax) { + this.emitComments(expression, true); + this.emitDottedNameMemberAccessExpression(expression); + this.emitComments(expression, false); + } + + public emitMemberAccessExpression(expression: MemberAccessExpressionSyntax): void { + if (!this.tryEmitConstant(expression)) { + // If the expression is dotted name of the modules, emit it using decl path so the name could be resolved correctly. + if (this.canEmitDottedNameMemberAccessExpression(expression)) { + this.emitDottedNameMemberAccessExpression(expression); + } + else { + this.recordSourceMappingStart(expression); + this.emit(expression.expression); + this.writeToken(expression.dotToken); + this.emitName(expression.name, false); + this.recordSourceMappingEnd(expression); + } + } + } + + public emitQualifiedName(name: QualifiedNameSyntax): void { + this.recordSourceMappingStart(name); + + this.emit(name.left); + this.writeToken(name.dotToken); + this.emitName(name.right, false); + + this.recordSourceMappingEnd(name); + } + + public emitBinaryExpression(expression: BinaryExpressionSyntax): void { + this.recordSourceMappingStart(expression); + switch (expression.kind()) { + case SyntaxKind.CommaExpression: + this.emit(expression.left); + this.writeToken(expression.operatorToken); + this.writeToOutput(" "); + this.emit(expression.right); + break; + default: + { + this.emit(expression.left); + var binOp = SyntaxFacts.getText(SyntaxFacts.getOperatorTokenFromBinaryExpression(expression.kind())); + this.writeToOutput(" "); + this.writeToOutputWithSourceMapRecord(binOp, expression.operatorToken); + this.writeToOutput(" "); + this.emit(expression.right); + } + } + this.recordSourceMappingEnd(expression); + } + + public emitSimplePropertyAssignment(property: SimplePropertyAssignmentSyntax): void { + this.recordSourceMappingStart(property); + this.emit(property.propertyName); + + this.writeToken(property.colonToken); + this.writeToOutput(" "); + this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(property.colonToken, this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true); + + this.emit(property.expression); + this.recordSourceMappingEnd(property); + } + + public emitFunctionPropertyAssignment(funcProp: FunctionPropertyAssignmentSyntax): void { + this.recordSourceMappingStart(funcProp); + + this.emit(funcProp.propertyName); + this.writeToOutput(": "); + + var pullFunctionDecl = this.semanticInfoChain.getDeclForAST(funcProp); + + var savedInArrowFunction = this.inArrowFunction; + this.inArrowFunction = false; + + var temp = this.setContainer(EmitContainer.Function); + var funcName = funcProp.propertyName; + + var pullDecl = this.semanticInfoChain.getDeclForAST(funcProp); + this.pushDecl(pullDecl); + + this.recordSourceMappingStart(funcProp); + this.writeToOutput("function "); + + //this.recordSourceMappingStart(funcProp.propertyName); + //this.writeToOutput(funcProp.propertyName.actualText); + //this.recordSourceMappingEnd(funcProp.propertyName); + + this.emitParameterList(funcProp.callSignature.parameterList); + + this.emitFunctionBodyStatements(funcProp.propertyName.text(), funcProp, + funcProp.callSignature.parameterList.parameters, funcProp.block, /*bodyExpression:*/ null); + + this.recordSourceMappingEnd(funcProp); + + // The extra call is to make sure the caller's funcDecl end is recorded, since caller wont be able to record it + this.recordSourceMappingEnd(funcProp); + + this.emitComments(funcProp, false); + + this.popDecl(pullDecl); + + this.setContainer(temp); + this.inArrowFunction = savedInArrowFunction; + } + + public emitConditionalExpression(expression: ConditionalExpressionSyntax): void { + this.emit(expression.condition); + this.writeToOutput(" "); + this.writeToken(expression.questionToken); + this.writeToOutput(" "); + this.emit(expression.whenTrue); + this.writeToOutput(" "); + this.writeToken(expression.colonToken); + this.writeToOutput(" "); + this.emit(expression.whenFalse); + } + + public emitThrowStatement(statement: ThrowStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.throwKeyword); + this.writeToOutput(" "); + this.emit(statement.expression); + this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken); + this.recordSourceMappingEnd(statement); + } + + public emitExpressionStatement(statement: ExpressionStatementSyntax): void { + var isArrowExpression = statement.expression.kind() === SyntaxKind.SimpleArrowFunctionExpression || statement.expression.kind() === SyntaxKind.ParenthesizedArrowFunctionExpression; + + this.recordSourceMappingStart(statement); + if (isArrowExpression) { + this.writeToOutput("("); + } + + this.emit(statement.expression); + + if (isArrowExpression) { + this.writeToOutput(")"); + } + + this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken); + this.recordSourceMappingEnd(statement); + } + + public emitLabeledStatement(statement: LabeledStatementSyntax): void { + this.writeToOutputWithSourceMapRecord(statement.identifier.text(), statement.identifier); + this.writeToken(statement.colonToken); + this.writeLineToOutput(""); + this.emitJavascript(statement.statement, /*startLine:*/ true); + } + + public emitBlock(block: BlockSyntax): void { + this.recordSourceMappingStart(block); + this.writeLineToOutput(" {"); + this.indenter.increaseIndent(); + if (block.statements) { + this.emitList(block.statements); + } + this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(block.closeBraceToken, this.text()), /*trailing:*/ false); + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeToken(block.closeBraceToken); + this.recordSourceMappingEnd(block); + } + + public emitBreakStatement(jump: BreakStatementSyntax): void { + this.recordSourceMappingStart(jump); + this.writeToken(jump.breakKeyword); + + if (jump.identifier) { + this.writeToOutput(" "); + this.writeToOutputWithSourceMapRecord(jump.identifier.text(), jump.identifier); + } + + this.writeToOutputWithSourceMapRecord(";", jump.semicolonToken); + this.recordSourceMappingEnd(jump); + } + + public emitContinueStatement(jump: ContinueStatementSyntax): void { + this.recordSourceMappingStart(jump); + this.writeToken(jump.continueKeyword); + + if (jump.identifier) { + this.writeToOutput(" "); + this.writeToOutputWithSourceMapRecord(jump.identifier.text(), jump.identifier); + } + + this.writeToOutputWithSourceMapRecord(";", jump.semicolonToken); + this.recordSourceMappingEnd(jump); + } + + public emitWhileStatement(statement: WhileStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.whileKeyword); + this.writeToOutput(" "); + this.writeToken(statement.openParenToken); + this.emit(statement.condition); + this.writeToken(statement.closeParenToken); + this.emitBlockOrStatement(statement.statement); + this.recordSourceMappingEnd(statement); + } + + public emitDoStatement(statement: DoStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.doKeyword); + this.emitBlockOrStatement(statement.statement); + this.writeToOutput(" "); + this.writeToken(statement.whileKeyword); + this.writeToken(statement.openParenToken); + this.emit(statement.condition); + this.writeToken(statement.closeParenToken); + this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken); + this.recordSourceMappingEnd(statement); + } + + public emitIfStatement(statement: IfStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.ifKeyword); + this.writeToOutput(" "); + this.writeToken(statement.openParenToken); + this.emit(statement.condition); + this.writeToken(statement.closeParenToken); + + this.emitBlockOrStatement(statement.statement); + + if (statement.elseClause) { + if (statement.statement.kind() !== SyntaxKind.Block) { + this.writeLineToOutput(""); + this.emitIndent(); + } + else { + this.writeToOutput(" "); + } + + this.emit(statement.elseClause); + } + this.recordSourceMappingEnd(statement); + } + + public emitElseClause(elseClause: ElseClauseSyntax): void { + this.writeToken(elseClause.elseKeyword); + if (elseClause.statement.kind() === SyntaxKind.IfStatement) { + this.writeToOutput(" "); + this.emit(elseClause.statement); + } + else { + this.emitBlockOrStatement(elseClause.statement); + } + } + + public emitReturnStatement(statement: ReturnStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.returnKeyword); + if (statement.expression) { + this.writeToOutput(" "); + this.emit(statement.expression); + } + + this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken); + this.recordSourceMappingEnd(statement); + } + + public emitForInStatement(statement: ForInStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.forKeyword); + this.writeToOutput(" "); + this.writeToken(statement.openParenToken); + + if (statement.left) { + this.emit(statement.left); + } + else { + this.emit(statement.variableDeclaration); + } + this.writeToOutput(" "); + this.writeToken(statement.inKeyword); + this.writeToOutput(" "); + this.emit(statement.expression); + this.writeToken(statement.closeParenToken); + this.emitBlockOrStatement(statement.statement); + this.recordSourceMappingEnd(statement); + } + + public emitForStatement(statement: ForStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.forKeyword); + this.writeToOutput(" "); + this.writeToken(statement.openParenToken); + + if (statement.variableDeclaration) { + this.emit(statement.variableDeclaration); + } + else if (statement.initializer) { + this.emit(statement.initializer); + } + + this.writeToken(statement.firstSemicolonToken); + this.writeToOutput(" "); + this.emitJavascript(statement.condition, false); + this.writeToken(statement.secondSemicolonToken); + if (statement.incrementor) { + this.writeToOutput(" "); + this.emitJavascript(statement.incrementor, false); + } + this.writeToken(statement.closeParenToken); + this.emitBlockOrStatement(statement.statement); + this.recordSourceMappingEnd(statement); + } + + public emitWithStatement(statement: WithStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.withKeyword); + this.writeToOutput(" "); + this.writeToken(statement.openParenToken); + if (statement.condition) { + this.emit(statement.condition); + } + + this.writeToken(statement.closeParenToken); + var prevInWithBlock = this.inWithBlock; + this.inWithBlock = true; + this.emitBlockOrStatement(statement.statement); + this.inWithBlock = prevInWithBlock; + this.recordSourceMappingEnd(statement); + } + + public emitSwitchStatement(statement: SwitchStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.switchKeyword); + this.writeToOutput(" "); + this.writeToken(statement.openParenToken); + this.emit(statement.expression); + this.writeToken(statement.closeParenToken); + this.writeLineToOutput(" {"); + this.indenter.increaseIndent(); + this.emitList(statement.switchClauses, /*useNewLineSeparator:*/ false); + this.indenter.decreaseIndent(); + this.emitIndent(); + this.writeToken(statement.closeBraceToken); + this.recordSourceMappingEnd(statement); + } + + public emitCaseSwitchClause(clause: CaseSwitchClauseSyntax): void { + this.recordSourceMappingStart(clause); + this.writeToken(clause.caseKeyword); + this.writeToOutput(" "); + this.emit(clause.expression); + this.writeToken(clause.colonToken); + + this.emitSwitchClauseBody(clause.colonToken, clause.statements); + this.recordSourceMappingEnd(clause); + } + + private emitSwitchClauseBody(colonToken: ISyntaxToken, body: IStatementSyntax[]): void { + var text = this.text(); + if (body.length === 1 && childAt(body, 0).kind() === SyntaxKind.Block) { + // The case statement was written with curly braces, so emit it with the appropriate formatting + this.emit(childAt(body, 0)); + this.writeLineToOutput(""); + } + else if (body.length === 1 && this.isOnSameLine(end(colonToken, text), start(body[0], text))) { + this.writeToOutput(" "); + this.emit(childAt(body, 0)); + this.writeLineToOutput(""); + } + else { + // No curly braces. Format in the expected way + this.writeLineToOutput(""); + this.indenter.increaseIndent(); + this.emit(body); + this.indenter.decreaseIndent(); + } + } + + public emitDefaultSwitchClause(clause: DefaultSwitchClauseSyntax): void { + this.recordSourceMappingStart(clause); + this.writeToken(clause.defaultKeyword); + this.writeToken(clause.colonToken); + + this.emitSwitchClauseBody(clause.colonToken, clause.statements); + this.recordSourceMappingEnd(clause); + } + + public emitTryStatement(statement: TryStatementSyntax): void { + this.recordSourceMappingStart(statement); + this.writeToken(statement.tryKeyword); + this.writeToOutput(" "); + this.emit(statement.block); + this.emitJavascript(statement.catchClause, false); + + if (statement.finallyClause) { + this.emit(statement.finallyClause); + } + this.recordSourceMappingEnd(statement); + } + + public emitCatchClause(clause: CatchClauseSyntax): void { + this.writeToOutput(" "); + this.recordSourceMappingStart(clause); + this.writeToken(clause.catchKeyword); + this.writeToOutput(" "); + this.writeToken(clause.openParenToken); + this.emit(clause.identifier); + this.writeToken(clause.closeParenToken); + this.emit(clause.block); + this.recordSourceMappingEnd(clause); + } + + public emitFinallyClause(clause: FinallyClauseSyntax): void { + this.writeToOutput(" "); + this.writeToken(clause.finallyKeyword); + this.emit(clause.block); + } + + public emitDebuggerStatement(statement: DebuggerStatementSyntax): void { + this.writeToken(statement.debuggerKeyword); + this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken); + } + + public emitNumericLiteral(literal: ISyntaxToken): void { + this.writeToOutputWithSourceMapRecord(literal.text(), literal); + } + + public emitRegularExpressionLiteral(literal: ISyntaxToken): void { + this.writeToOutputWithSourceMapRecord(literal.text(), literal); + } + + public emitStringLiteral(literal: ISyntaxToken): void { + this.writeToOutputWithSourceMapRecord(literal.text(), literal); + } + + public emitEqualsValueClause(clause: EqualsValueClauseSyntax): void { + this.writeToOutput(" "); + this.writeToken(clause.equalsToken); + this.writeToOutput(" "); + this.emitCommentsArray(ASTHelpers.convertTokenTrailingComments(clause.equalsToken, this.text()), /*trailing:*/ true, /*noLeadingSpace:*/ true); + + this.emit(clause.value); + } + + private emitParameter(parameter: ParameterSyntax): void { + this.writeToOutputWithSourceMapRecord(parameter.identifier.text(), parameter); + } + + public emitConstructorDeclaration(declaration: ConstructorDeclarationSyntax): void { + if (declaration.block) { + this.emitConstructor(declaration); + } + else { + this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + } + + public shouldEmitFunctionDeclaration(declaration: FunctionDeclarationSyntax): boolean { + return ASTHelpers.preComments(declaration, this.text()) !== null || (!hasModifier(declaration.modifiers, PullElementFlags.Ambient) && declaration.block !== null); + } + + public emitFunctionDeclaration(declaration: FunctionDeclarationSyntax): void { + if (!hasModifier(declaration.modifiers, PullElementFlags.Ambient) && declaration.block !== null) { + this.emitFunction(declaration); + } + else { + this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + } + + private emitSourceUnit(sourceUnit: SourceUnitSyntax): void { + if (!this.document.isDeclareFile()) { + var pullDecl = this.semanticInfoChain.getDeclForAST(sourceUnit); + this.pushDecl(pullDecl); + this.emitScriptElements(sourceUnit); + this.popDecl(pullDecl); + + this.emitCommentsArray(ASTHelpers.convertTokenLeadingComments(sourceUnit.endOfFileToken, this.text()), /*trailing:*/ false); + } + } + + public shouldEmitEnumDeclaration(declaration: EnumDeclarationSyntax): boolean { + return ASTHelpers.preComments(declaration, this.text()) !== null || !ASTHelpers.enumIsElided(declaration); + } + + public emitEnumDeclaration(declaration: EnumDeclarationSyntax): void { + if (!ASTHelpers.enumIsElided(declaration)) { + this.emitComments(declaration, true); + this.emitEnum(declaration); + this.emitComments(declaration, false); + } + else { + this.emitComments(declaration, true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + } + + public shouldEmitModuleDeclaration(declaration: ModuleDeclarationSyntax): boolean { + return ASTHelpers.preComments(declaration, this.text()) !== null || !ASTHelpers.moduleIsElided(declaration); + } + + private emitModuleDeclaration(declaration: ModuleDeclarationSyntax): void { + if (!ASTHelpers.moduleIsElided(declaration)) { + this.emitModuleDeclarationWorker(declaration); + } + else { + this.emitComments(declaration, true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + } + + public shouldEmitClassDeclaration(declaration: ClassDeclarationSyntax): boolean { + return ASTHelpers.preComments(declaration, this.text()) !== null || !hasModifier(declaration.modifiers, PullElementFlags.Ambient); + } + + public emitClassDeclaration(declaration: ClassDeclarationSyntax): void { + if (!hasModifier(declaration.modifiers, PullElementFlags.Ambient)) { + this.emitClass(declaration); + } + else { + this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + } + + public shouldEmitInterfaceDeclaration(declaration: InterfaceDeclarationSyntax): boolean { + return ASTHelpers.preComments(declaration, this.text()) !== null; + } + + public emitInterfaceDeclaration(declaration: InterfaceDeclarationSyntax): void { + this.emitComments(declaration, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + + private firstVariableDeclarator(statement: VariableStatementSyntax): VariableDeclaratorSyntax { + return statement.variableDeclaration.variableDeclarators[0]; + } + + private isNotAmbientOrHasInitializer(variableStatement: VariableStatementSyntax): boolean { + return !hasModifier(variableStatement.modifiers, PullElementFlags.Ambient) || this.firstVariableDeclarator(variableStatement).equalsValueClause !== null; + } + + public shouldEmitVariableStatement(statement: VariableStatementSyntax): boolean { + return ASTHelpers.preComments(statement, this.text()) !== null || this.isNotAmbientOrHasInitializer(statement); + } + + public emitVariableStatement(statement: VariableStatementSyntax): void { + if (this.isNotAmbientOrHasInitializer(statement)) { + this.emitComments(statement, true); + this.emit(statement.variableDeclaration); + this.writeToOutputWithSourceMapRecord(";", statement.semicolonToken); + this.emitComments(statement, false); + } + else { + this.emitComments(statement, /*pre:*/ true, /*onlyPinnedOrTripleSlashComments:*/ true); + } + } + + public emitGenericType(type: GenericTypeSyntax): void { + this.emit(type.name); + } + + private shouldEmit(ast: ISyntaxElement): boolean { + if (!ast) { + return false; + } + + switch (ast.kind()) { + case SyntaxKind.ImportDeclaration: + return this.shouldEmitImportDeclaration(ast); + case SyntaxKind.ClassDeclaration: + return this.shouldEmitClassDeclaration(ast); + case SyntaxKind.InterfaceDeclaration: + return this.shouldEmitInterfaceDeclaration(ast); + case SyntaxKind.FunctionDeclaration: + return this.shouldEmitFunctionDeclaration(ast); + case SyntaxKind.ModuleDeclaration: + return this.shouldEmitModuleDeclaration(ast); + case SyntaxKind.VariableStatement: + return this.shouldEmitVariableStatement(ast); + case SyntaxKind.OmittedExpression: + return false; + case SyntaxKind.EnumDeclaration: + return this.shouldEmitEnumDeclaration(ast); + } + + return true; + } + + private emit(ast: ISyntaxElement): void { + if (!ast) { + return; + } + + switch (ast.kind()) { + case SyntaxKind.SeparatedList: + return this.emitSeparatedList(ast); + case SyntaxKind.List: + return this.emitList(ast); + case SyntaxKind.SourceUnit: + return this.emitSourceUnit(ast); + case SyntaxKind.ImportDeclaration: + return this.emitImportDeclaration(ast); + case SyntaxKind.ExportAssignment: + return this.setExportAssignment(ast); + case SyntaxKind.ClassDeclaration: + return this.emitClassDeclaration(ast); + case SyntaxKind.InterfaceDeclaration: + return this.emitInterfaceDeclaration(ast); + case SyntaxKind.IdentifierName: + return this.emitName(ast, true); + case SyntaxKind.VariableDeclarator: + return this.emitVariableDeclarator(ast); + case SyntaxKind.SimpleArrowFunctionExpression: + return this.emitSimpleArrowFunctionExpression(ast); + case SyntaxKind.ParenthesizedArrowFunctionExpression: + return this.emitParenthesizedArrowFunctionExpression(ast); + case SyntaxKind.FunctionDeclaration: + return this.emitFunctionDeclaration(ast); + case SyntaxKind.ModuleDeclaration: + return this.emitModuleDeclaration(ast); + case SyntaxKind.VariableDeclaration: + return this.emitVariableDeclaration(ast); + case SyntaxKind.GenericType: + return this.emitGenericType(ast); + case SyntaxKind.ConstructorDeclaration: + return this.emitConstructorDeclaration(ast); + case SyntaxKind.EnumDeclaration: + return this.emitEnumDeclaration(ast); + case SyntaxKind.EnumElement: + return this.emitEnumElement(ast); + case SyntaxKind.FunctionExpression: + return this.emitFunctionExpression(ast); + case SyntaxKind.VariableStatement: + return this.emitVariableStatement(ast); + } + + this.emitComments(ast, true); + this.emitWorker(ast); + this.emitComments(ast, false); + } + + private emitWorker(ast: ISyntaxElement): void { + if (!ast) { + return; + } + + switch (ast.kind()) { + case SyntaxKind.NumericLiteral: + return this.emitNumericLiteral(ast); + case SyntaxKind.RegularExpressionLiteral: + return this.emitRegularExpressionLiteral(ast); + case SyntaxKind.StringLiteral: + return this.emitStringLiteral(ast); + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.TrueKeyword: + return this.emitLiteralExpression(ast); + case SyntaxKind.ThisKeyword: + return this.emitThisExpression(ast); + case SyntaxKind.SuperKeyword: + return this.emitSuperExpression(ast); + case SyntaxKind.ParenthesizedExpression: + return this.emitParenthesizedExpression(ast); + case SyntaxKind.ArrayLiteralExpression: + return this.emitArrayLiteralExpression(ast); + case SyntaxKind.PostDecrementExpression: + case SyntaxKind.PostIncrementExpression: + return this.emitPostfixUnaryExpression(ast); + case SyntaxKind.LogicalNotExpression: + case SyntaxKind.BitwiseNotExpression: + case SyntaxKind.NegateExpression: + case SyntaxKind.PlusExpression: + case SyntaxKind.PreIncrementExpression: + case SyntaxKind.PreDecrementExpression: + return this.emitPrefixUnaryExpression(ast); + case SyntaxKind.InvocationExpression: + return this.emitInvocationExpression(ast); + case SyntaxKind.ElementAccessExpression: + return this.emitElementAccessExpression(ast); + case SyntaxKind.MemberAccessExpression: + return this.emitMemberAccessExpression(ast); + case SyntaxKind.QualifiedName: + return this.emitQualifiedName(ast); + case SyntaxKind.CommaExpression: + case SyntaxKind.AssignmentExpression: + case SyntaxKind.AddAssignmentExpression: + case SyntaxKind.SubtractAssignmentExpression: + case SyntaxKind.MultiplyAssignmentExpression: + case SyntaxKind.DivideAssignmentExpression: + case SyntaxKind.ModuloAssignmentExpression: + case SyntaxKind.AndAssignmentExpression: + case SyntaxKind.ExclusiveOrAssignmentExpression: + case SyntaxKind.OrAssignmentExpression: + case SyntaxKind.LeftShiftAssignmentExpression: + case SyntaxKind.SignedRightShiftAssignmentExpression: + case SyntaxKind.UnsignedRightShiftAssignmentExpression: + case SyntaxKind.LogicalOrExpression: + case SyntaxKind.LogicalAndExpression: + case SyntaxKind.BitwiseOrExpression: + case SyntaxKind.BitwiseExclusiveOrExpression: + case SyntaxKind.BitwiseAndExpression: + case SyntaxKind.EqualsWithTypeConversionExpression: + case SyntaxKind.NotEqualsWithTypeConversionExpression: + case SyntaxKind.EqualsExpression: + case SyntaxKind.NotEqualsExpression: + case SyntaxKind.LessThanExpression: + case SyntaxKind.GreaterThanExpression: + case SyntaxKind.LessThanOrEqualExpression: + case SyntaxKind.GreaterThanOrEqualExpression: + case SyntaxKind.InstanceOfExpression: + case SyntaxKind.InExpression: + case SyntaxKind.LeftShiftExpression: + case SyntaxKind.SignedRightShiftExpression: + case SyntaxKind.UnsignedRightShiftExpression: + case SyntaxKind.MultiplyExpression: + case SyntaxKind.DivideExpression: + case SyntaxKind.ModuloExpression: + case SyntaxKind.AddExpression: + case SyntaxKind.SubtractExpression: + return this.emitBinaryExpression(ast); + case SyntaxKind.ConditionalExpression: + return this.emitConditionalExpression(ast); + case SyntaxKind.EqualsValueClause: + return this.emitEqualsValueClause(ast); + case SyntaxKind.Parameter: + return this.emitParameter(ast); + case SyntaxKind.Block: + return this.emitBlock(ast); + case SyntaxKind.ElseClause: + return this.emitElseClause(ast); + case SyntaxKind.IfStatement: + return this.emitIfStatement(ast); + case SyntaxKind.ExpressionStatement: + return this.emitExpressionStatement(ast); + case SyntaxKind.GetAccessor: + return this.emitGetAccessor(ast); + case SyntaxKind.SetAccessor: + return this.emitSetAccessor(ast); + case SyntaxKind.ThrowStatement: + return this.emitThrowStatement(ast); + case SyntaxKind.ReturnStatement: + return this.emitReturnStatement(ast); + case SyntaxKind.ObjectCreationExpression: + return this.emitObjectCreationExpression(ast); + case SyntaxKind.SwitchStatement: + return this.emitSwitchStatement(ast); + case SyntaxKind.CaseSwitchClause: + return this.emitCaseSwitchClause(ast); + case SyntaxKind.DefaultSwitchClause: + return this.emitDefaultSwitchClause(ast); + case SyntaxKind.BreakStatement: + return this.emitBreakStatement(ast); + case SyntaxKind.ContinueStatement: + return this.emitContinueStatement(ast); + case SyntaxKind.ForStatement: + return this.emitForStatement(ast); + case SyntaxKind.ForInStatement: + return this.emitForInStatement(ast); + case SyntaxKind.WhileStatement: + return this.emitWhileStatement(ast); + case SyntaxKind.WithStatement: + return this.emitWithStatement(ast); + case SyntaxKind.CastExpression: + return this.emitCastExpression(ast); + case SyntaxKind.ObjectLiteralExpression: + return this.emitObjectLiteralExpression(ast); + case SyntaxKind.SimplePropertyAssignment: + return this.emitSimplePropertyAssignment(ast); + case SyntaxKind.FunctionPropertyAssignment: + return this.emitFunctionPropertyAssignment(ast); + case SyntaxKind.EmptyStatement: + return this.writeToken((ast).semicolonToken); + case SyntaxKind.TryStatement: + return this.emitTryStatement(ast); + case SyntaxKind.CatchClause: + return this.emitCatchClause(ast); + case SyntaxKind.FinallyClause: + return this.emitFinallyClause(ast); + case SyntaxKind.LabeledStatement: + return this.emitLabeledStatement(ast); + case SyntaxKind.DoStatement: + return this.emitDoStatement(ast); + case SyntaxKind.TypeOfExpression: + return this.emitTypeOfExpression(ast); + case SyntaxKind.DeleteExpression: + return this.emitDeleteExpression(ast); + case SyntaxKind.VoidExpression: + return this.emitVoidExpression(ast); + case SyntaxKind.DebuggerStatement: + return this.emitDebuggerStatement(ast); + } + } + } + + export function getLastConstructor(classDecl: ClassDeclarationSyntax): ConstructorDeclarationSyntax { + for (var i = classDecl.classElements.length - 1; i >= 0; i--) { + var child = classDecl.classElements[i]; + + if (child.kind() === SyntaxKind.ConstructorDeclaration) { + return child; + } + } + + return null; + } + + export function getTrimmedTextLines(comment: Comment): string[] { + if (comment.kind() === SyntaxKind.MultiLineCommentTrivia) { + return comment.fullText().split("\n").map(s => s.trim()); + } + else { + return [comment.fullText().trim()]; + } + } +} \ No newline at end of file diff --git a/src/services/compiler/enumerator.ts b/src/services/compiler/enumerator.ts new file mode 100644 index 00000000000..eb9559b02fa --- /dev/null +++ b/src/services/compiler/enumerator.ts @@ -0,0 +1,6 @@ +declare class Enumerator { + public atEnd(): boolean; + public moveNext(): boolean; + public item(): any; + constructor (o: any); +} \ No newline at end of file diff --git a/src/services/compiler/hashTable.ts b/src/services/compiler/hashTable.ts new file mode 100644 index 00000000000..89c7551a571 --- /dev/null +++ b/src/services/compiler/hashTable.ts @@ -0,0 +1,187 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + var proto = "__proto__" + + class BlockIntrinsics { + public prototype: T = undefined; + public toString: T = undefined; + public toLocaleString: T = undefined; + public valueOf: T = undefined; + public hasOwnProperty: T = undefined; + public propertyIsEnumerable: T = undefined; + public isPrototypeOf: T = undefined; + [s: string]: T; + + constructor() { + // initialize the 'constructor' field + this["constructor"] = undefined; + + // First we set it to null, because that's the only way to erase the value in node. Then we set it to undefined in case we are not in node, since + // in StringHashTable below, we check for undefined explicitly. + this[proto] = null; + this[proto] = undefined; + } + } + + export function createIntrinsicsObject(): ts.Map { + return new BlockIntrinsics(); + } + + //export interface IHashTable { + // getAllKeys(): string[]; + // add(key: string, data: T): boolean; + // addOrUpdate(key: string, data: T): boolean; + // map(fn: (k: string, value: T, context: any) => void , context: any): void; + // every(fn: (k: string, value: T, context: any) => void , context: any): boolean; + // some(fn: (k: string, value: T, context: any) => void , context: any): boolean; + // count(): number; + // lookup(key: string): T; + //} + + //export class StringHashTable implements IHashTable { + // private itemCount = 0; + // private table: IIndexable = createIntrinsicsObject(); + + // public getAllKeys(): string[] { + // var result: string[] = []; + + // for (var k in this.table) { + // if (this.table[k] !== undefined) { + // result.push(k); + // } + // } + + // return result; + // } + + // public add(key: string, data: T): boolean { + // if (this.table[key] !== undefined) { + // return false; + // } + + // this.table[key] = data; + // this.itemCount++; + // return true; + // } + + // public addOrUpdate(key: string, data: T): boolean { + // if (this.table[key] !== undefined) { + // this.table[key] = data; + // return false; + // } + + // this.table[key] = data; + // this.itemCount++; + // return true; + // } + + // public map(fn: (k: string, value: T, context: any) => void , context: any) { + // for (var k in this.table) { + // var data = this.table[k]; + + // if (data !== undefined) { + // fn(k, this.table[k], context); + // } + // } + // } + + // public every(fn: (k: string, value: T, context: any) => void , context: any) { + // for (var k in this.table) { + // var data = this.table[k]; + + // if (data !== undefined) { + // if (!fn(k, this.table[k], context)) { + // return false; + // } + // } + // } + + // return true; + // } + + // public some(fn: (k: string, value: T, context: any) => void , context: any) { + // for (var k in this.table) { + // var data = this.table[k]; + + // if (data !== undefined) { + // if (fn(k, this.table[k], context)) { + // return true; + // } + // } + // } + + // return false; + // } + + // public count(): number { + // return this.itemCount; + // } + + // public lookup(key: string) : T { + // var data = this.table[key]; + // return data === undefined ? null : data; + // } + + // public remove(key: string): void { + // if (this.table[key] !== undefined) { + // this.table[key] = undefined; + // this.itemCount--; + // } + // } + //} + + + //export class IdentiferNameHashTable extends StringHashTable { + // public getAllKeys(): string[]{ + // var result: string[] = []; + + // super.map((k, v, c) => { + // if (v !== undefined) { + // result.push(k.substring(1)); + // } + // }, null); + + // return result; + // } + + // public add(key: string, data: T): boolean { + // return super.add("#" + key, data); + // } + + // public addOrUpdate(key: string, data: T): boolean { + // return super.addOrUpdate("#" + key, data); + // } + + // public map(fn: (k: string, value: T, context: any) => void , context: any) { + // return super.map((k, v, c) => fn(k.substring(1), v, c), context); + // } + + // public every(fn: (k: string, value: T, context: any) => void , context: any) { + // return super.every((k, v, c) => fn(k.substring(1), v, c), context); + // } + + // public some(fn: (k: string, value: any, context: any) => void , context: any) { + // return super.some((k, v, c) => fn(k.substring(1), v, c), context); + // } + + // public lookup(key: string): T { + // return super.lookup("#" + key); + // } + //} +} \ No newline at end of file diff --git a/src/services/compiler/identifierWalker.ts b/src/services/compiler/identifierWalker.ts new file mode 100644 index 00000000000..4af0893db6c --- /dev/null +++ b/src/services/compiler/identifierWalker.ts @@ -0,0 +1,11 @@ +module TypeScript { + export class IdentifierWalker extends SyntaxWalker { + constructor(public list: IIndexable) { + super(); + } + + public visitToken(token: ISyntaxToken): void { + this.list[token.text()] = true; + } + } +} \ No newline at end of file diff --git a/src/services/compiler/io.ts b/src/services/compiler/io.ts new file mode 100644 index 00000000000..952187809b0 --- /dev/null +++ b/src/services/compiler/io.ts @@ -0,0 +1,64 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// +/// +/// + +module TypeScript { + + export interface IFindFileResult { + fileInformation: FileInformation; + path: string; + } + + export module IOUtils { + // Creates the directory including its parent if not already present + function createDirectoryStructure(ioHost: IEnvironment, dirName: string) { + if (ioHost.directoryExists(dirName)) { + return; + } + + var parentDirectory = ioHost.directoryName(dirName); + if (parentDirectory != "") { + createDirectoryStructure(ioHost, parentDirectory); + } + ioHost.createDirectory(dirName); + } + + // Creates a file including its directory structure if not already present + export function writeFileAndFolderStructure(ioHost: IEnvironment, fileName: string, contents: string, writeByteOrderMark: boolean): void { + var start = new Date().getTime(); + var path = ioHost.absolutePath(fileName); + TypeScript.ioHostResolvePathTime += new Date().getTime() - start; + + var start = new Date().getTime(); + var dirName = ioHost.directoryName(path); + TypeScript.ioHostDirectoryNameTime += new Date().getTime() - start; + + var start = new Date().getTime(); + createDirectoryStructure(ioHost, dirName); + TypeScript.ioHostCreateDirectoryStructureTime += new Date().getTime() - start; + + var start = new Date().getTime(); + ioHost.writeFile(path, contents, writeByteOrderMark); + TypeScript.ioHostWriteFileTime += new Date().getTime() - start; + } + + export function combine(prefix: string, suffix: string): string { + return prefix + "/" + suffix; + } + } +} \ No newline at end of file diff --git a/src/services/compiler/optionsParser.ts b/src/services/compiler/optionsParser.ts new file mode 100644 index 00000000000..10d9ddac84e --- /dev/null +++ b/src/services/compiler/optionsParser.ts @@ -0,0 +1,266 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export interface IOptions { + name?: string; + flag?: boolean; + short?: string; + usage?: { + locCode: string; // DiagnosticCode + args: string[] + }; + set?: (s: string) => void; + type?: string; // DiagnosticCode + experimental?: boolean; + } + + export class OptionsParser { + private DEFAULT_SHORT_FLAG = "-"; + private DEFAULT_LONG_FLAG = "--"; + + private printedVersion: boolean = false; + + // Find the option record for the given string. Returns null if not found. + private findOption(arg: string) { + var upperCaseArg = arg && arg.toUpperCase(); + + for (var i = 0; i < this.options.length; i++) { + var current = this.options[i]; + + if (upperCaseArg === (current.short && current.short.toUpperCase()) || + upperCaseArg === (current.name && current.name.toUpperCase())) { + return current; + } + } + + return null; + } + + public unnamed: string[] = []; + + public options: IOptions[] = []; + + constructor(public host: IEnvironment, public version: string) { + } + + public printUsage() { + this.printVersion(); + + var optionsWord = getLocalizedText(DiagnosticCode.options, null); + var fileWord = getLocalizedText(DiagnosticCode.file1, null); + var tscSyntax = "tsc [" + optionsWord + "] [" + fileWord + " ..]"; + var syntaxHelp = getLocalizedText(DiagnosticCode.Syntax_0, [tscSyntax]); + this.host.standardOut.WriteLine(syntaxHelp); + this.host.standardOut.WriteLine(""); + this.host.standardOut.WriteLine(getLocalizedText(DiagnosticCode.Examples, null) + " tsc hello.ts"); + this.host.standardOut.WriteLine(" tsc --out foo.js foo.ts"); + this.host.standardOut.WriteLine(" tsc @args.txt"); + this.host.standardOut.WriteLine(""); + this.host.standardOut.WriteLine(getLocalizedText(DiagnosticCode.Options, null)); + + var output: string[][] = []; + var maxLength = 0; + var i = 0; + + this.options = this.options.sort(function (a, b) { + var aName = a.name.toLowerCase(); + var bName = b.name.toLowerCase(); + + if (aName > bName) { + return 1; + } else if (aName < bName) { + return -1; + } else { + return 0; + } + }); + + // Build up output array + for (i = 0; i < this.options.length; i++) { + var option = this.options[i]; + + if (option.experimental) { + continue; + } + + if (!option.usage) { + break; + } + + var usageString = " "; + var type = option.type ? (" " + TypeScript.getLocalizedText(option.type, null)) : ""; + + if (option.short) { + usageString += this.DEFAULT_SHORT_FLAG + option.short + type + ", "; + } + + usageString += this.DEFAULT_LONG_FLAG + option.name + type; + + output.push([usageString, TypeScript.getLocalizedText(option.usage.locCode, option.usage.args)]); + + if (usageString.length > maxLength) { + maxLength = usageString.length; + } + } + + var fileDescription = getLocalizedText(DiagnosticCode.Insert_command_line_options_and_files_from_a_file, null); + output.push([" @<" + fileWord + ">", fileDescription]); + + // Print padded output + for (i = 0; i < output.length; i++) { + this.host.standardOut.WriteLine(output[i][0] + (new Array(maxLength - output[i][0].length + 3)).join(" ") + output[i][1]); + } + } + + public printVersion() { + if (!this.printedVersion) { + this.host.standardOut.WriteLine(getLocalizedText(DiagnosticCode.Version_0, [this.version])); + this.printedVersion = true; + } + } + + public option(name: string, config: IOptions, short?: string) { + if (!config) { + config = short; + short = null; + } + + config.name = name; + config.short = short; + config.flag = false; + + this.options.push(config); + } + + public flag(name: string, config: IOptions, short?: string) { + if (!config) { + config = short; + short = null; + } + + config.name = name; + config.short = short; + config.flag = true + + this.options.push(config); + } + + // Parse an arguments string + public parseString(argString: string) { + var position = 0; + var tokens = argString.match(/\s+|"|[^\s"]+/g); + + function peek() { + return tokens[position]; + } + + function consume() { + return tokens[position++]; + } + + function consumeQuotedString() { + var value = ''; + consume(); // skip opening quote. + + var token = peek(); + + while (token && token !== '"') { + consume(); + + value += token; + + token = peek(); + } + + consume(); // skip ending quote; + + return value; + } + + var args: string[] = []; + var currentArg = ''; + + while (position < tokens.length) { + var token = peek(); + + if (token === '"') { + currentArg += consumeQuotedString(); + } else if (token.match(/\s/)) { + if (currentArg.length > 0) { + args.push(currentArg); + currentArg = ''; + } + + consume(); + } else { + consume(); + currentArg += token; + } + } + + if (currentArg.length > 0) { + args.push(currentArg); + } + + this.parse(args); + } + + // Parse arguments as they come from the platform: split into arguments. + public parse(args: string[]) { + var position = 0; + + function consume() { + return args[position++]; + } + + while (position < args.length) { + var current = consume(); + var match = current.match(/^(--?|@)(.*)/); + var value: any = null; + + if (match) { + if (match[1] === '@') { + this.parseString(this.host.readFile(match[2], null).contents); + } else { + var arg = match[2]; + var option = this.findOption(arg); + + if (option === null) { + this.host.standardOut.WriteLine(getDiagnosticMessage(DiagnosticCode.Unknown_compiler_option_0, [arg])); + this.host.standardOut.WriteLine(getLocalizedText(DiagnosticCode.Use_the_0_flag_to_see_options, ["--help"])); + } else { + if (!option.flag) { + value = consume(); + if (value === undefined) { + // No value provided + this.host.standardOut.WriteLine(getDiagnosticMessage(DiagnosticCode.Option_0_specified_without_1, [arg, getLocalizedText(option.type, null)])); + this.host.standardOut.WriteLine(getLocalizedText(DiagnosticCode.Use_the_0_flag_to_see_options, ["--help"])); + continue; + } + } + + option.set(value); + } + } + } else { + this.unnamed.push(current); + } + } + } + } +} \ No newline at end of file diff --git a/src/services/compiler/pathUtils.ts b/src/services/compiler/pathUtils.ts new file mode 100644 index 00000000000..3868881fc75 --- /dev/null +++ b/src/services/compiler/pathUtils.ts @@ -0,0 +1,193 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export function stripStartAndEndQuotes(str: string) { + var firstCharCode = str && str.charCodeAt(0); + if (str && str.length >= 2 && firstCharCode === str.charCodeAt(str.length - 1) && (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) { + return str.substring(1, str.length - 1); + } + + return str; + } + + export function isSingleQuoted(str: string) { + return str && str.length >= 2 && str.charCodeAt(0) === str.charCodeAt(str.length - 1) && str.charCodeAt(0) === CharacterCodes.singleQuote; + } + + export function isDoubleQuoted(str: string) { + return str && str.length >= 2 && str.charCodeAt(0) === str.charCodeAt(str.length - 1) && str.charCodeAt(0) === CharacterCodes.doubleQuote; + } + + export function isQuoted(str: string) { + return isDoubleQuoted(str) || isSingleQuoted(str); + } + + export function quoteStr(str: string) { + return "\"" + str + "\""; + } + + var switchToForwardSlashesRegEx = /\\/g; + export function switchToForwardSlashes(path: string) { + return path.replace(switchToForwardSlashesRegEx, "/"); + } + + export function trimModName(modName: string) { + // in case's it's a declare file... + if (modName.length > 5 && modName.substring(modName.length - 5, modName.length) === ".d.ts") { + return modName.substring(0, modName.length - 5); + } + if (modName.length > 3 && modName.substring(modName.length - 3, modName.length) === ".ts") { + return modName.substring(0, modName.length - 3); + } + // in case's it's a .js file + if (modName.length > 3 && modName.substring(modName.length - 3, modName.length) === ".js") { + return modName.substring(0, modName.length - 3); + } + + return modName; + } + + export function getDeclareFilePath(fname: string) { + return isTSFile(fname) ? changePathToDTS(fname) : changePathToDTS(fname); + } + + function isFileOfExtension(fname: string, ext: string) { + var invariantFname = fname.toLocaleUpperCase(); + var invariantExt = ext.toLocaleUpperCase(); + var extLength = invariantExt.length; + return invariantFname.length > extLength && invariantFname.substring(invariantFname.length - extLength, invariantFname.length) === invariantExt; + } + + export function isTSFile(fname: string) { + return isFileOfExtension(fname, ".ts"); + } + + export function isDTSFile(fname: string) { + return isFileOfExtension(fname, ".d.ts"); + } + + export function getPrettyName(modPath: string, quote=true, treatAsFileName=false): any { + var modName = treatAsFileName ? switchToForwardSlashes(modPath) : trimModName(stripStartAndEndQuotes(modPath)); + var components = this.getPathComponents(modName); + return components.length ? (quote ? quoteStr(components[components.length - 1]) : components[components.length - 1]) : modPath; + } + + export function getPathComponents(path: string) { + return path.split("/"); + } + + export function getRelativePathToFixedPath(fixedModFilePath: string, absoluteModPath: string, isAbsoultePathURL = true) { + absoluteModPath = switchToForwardSlashes(absoluteModPath); + + var modComponents = this.getPathComponents(absoluteModPath); + var fixedModComponents = this.getPathComponents(fixedModFilePath); + + // Find the component that differs + var joinStartIndex = 0; + for (; joinStartIndex < modComponents.length && joinStartIndex < fixedModComponents.length ; joinStartIndex++) { + if (fixedModComponents[joinStartIndex] !== modComponents[joinStartIndex]) { + break; + } + } + + // Get the relative path + if (joinStartIndex !== 0) { + var relativePath = ""; + var relativePathComponents = modComponents.slice(joinStartIndex, modComponents.length); + for (; joinStartIndex < fixedModComponents.length; joinStartIndex++) { + if (fixedModComponents[joinStartIndex] !== "") { + relativePath = relativePath + "../"; + } + } + + return relativePath + relativePathComponents.join("/"); + } + + if (isAbsoultePathURL && absoluteModPath.indexOf("://") === -1) { + absoluteModPath = "file:///" + absoluteModPath; + } + + return absoluteModPath; + } + + export function changePathToDTS(modPath: string) { + return trimModName(stripStartAndEndQuotes(modPath)) + ".d.ts"; + } + + export function isRelative(path: string) { + return path.length > 0 && path.charAt(0) === "."; + } + export function isRooted(path: string) { + return path.length > 0 && (path.charAt(0) === "\\" || path.charAt(0) === "/" || (path.indexOf(":\\") !== -1) || (path.indexOf(":/") !== -1)); + } + + export function getRootFilePath(outFname: string) { + if (outFname === "") { + return outFname; + } + else { + var isPath = outFname.indexOf("/") !== -1; + return isPath ? filePath(outFname) : ""; + } + } + + export function filePathComponents(fullPath: string) { + fullPath = switchToForwardSlashes(fullPath); + var components = getPathComponents(fullPath); + return components.slice(0, components.length - 1); + } + + export function filePath(fullPath: string) { + var path = filePathComponents(fullPath); + return path.join("/") + "/"; + } + + export function convertToDirectoryPath(dirPath: string) { + if (dirPath && dirPath.charAt(dirPath.length - 1) !== "/") { + dirPath += "/"; + } + + return dirPath; + } + + var normalizePathRegEx = /^\\\\[^\\]/; + export function normalizePath(path: string): string { + // If it's a UNC style path (i.e. \\server\share), convert to a URI style (i.e. file://server/share) + if (normalizePathRegEx.test(path)) { + path = "file:" + path; + } + var parts = this.getPathComponents(switchToForwardSlashes(path)); + var normalizedParts: string[] = []; + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + if (part === ".") { + continue; + } + + if (normalizedParts.length > 0 && ArrayUtilities.last(normalizedParts) !== ".." && part === "..") { + normalizedParts.pop(); + continue; + } + + normalizedParts.push(part); + } + + return normalizedParts.join("/"); + } +} \ No newline at end of file diff --git a/src/services/compiler/precompile.ts b/src/services/compiler/precompile.ts new file mode 100644 index 00000000000..a45ea5134a1 --- /dev/null +++ b/src/services/compiler/precompile.ts @@ -0,0 +1,208 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +module TypeScript { + export interface ILineAndCharacter { + line: number; + character: number; + } + + // Note: This is being using by the host (VS) and is marshaled back and forth. When changing this make sure the changes + // are reflected in the managed side as well. + export interface IFileReference extends ILineAndCharacter { + path: string; + isResident: boolean; + position: number; + length: number; + } + + /// + /// Preprocessing + /// + export interface IPreProcessedFileInfo { + referencedFiles: IFileReference[]; + importedFiles: IFileReference[]; + diagnostics: Diagnostic[]; + isLibFile: boolean; + } + + interface ITripleSlashDirectiveProperties { + noDefaultLib: boolean; + diagnostics: Diagnostic[]; + referencedFiles: IFileReference[]; + } + + function isNoDefaultLibMatch(comment: string): RegExpExecArray { + var isNoDefaultLibRegex = /^(\/\/\/\s*/gim; + return isNoDefaultLibRegex.exec(comment); + } + + export var tripleSlashReferenceRegExp = /^(\/\/\/\s*/; + + function getFileReferenceFromReferencePath(fileName: string, text: ISimpleText, position: number, comment: string, diagnostics: Diagnostic[]): IFileReference { + // First, just see if they've written: /// = 7 && fullReference[6] === "true"; + return { + line: 0, + character: 0, + position: 0, + length: 0, + path: switchToForwardSlashes(adjustedPath), + isResident: isResident + }; + } + } + } + + return null; + } + + var reportDiagnostic = () => { }; + + function processImports(text: ISimpleText, scanner: Scanner.IScanner, token: ISyntaxToken, importedFiles: IFileReference[]): void { + var lineChar = { line: -1, character: -1 }; + + var lineMap = text.lineMap(); + var start = new Date().getTime(); + // Look for: + // import foo = module("foo") + while (token.kind() !== SyntaxKind.EndOfFileToken) { + if (token.kind() === SyntaxKind.ImportKeyword) { + var importToken = token; + token = scanner.scan(/*allowRegularExpression:*/ false); + + if (SyntaxFacts.isIdentifierNameOrAnyKeyword(token)) { + token = scanner.scan(/*allowRegularExpression:*/ false); + + if (token.kind() === SyntaxKind.EqualsToken) { + token = scanner.scan(/*allowRegularExpression:*/ false); + + if (token.kind() === SyntaxKind.ModuleKeyword || token.kind() === SyntaxKind.RequireKeyword) { + token = scanner.scan(/*allowRegularExpression:*/ false); + + if (token.kind() === SyntaxKind.OpenParenToken) { + token = scanner.scan(/*allowRegularExpression:*/ false); + + lineMap.fillLineAndCharacterFromPosition(TypeScript.start(importToken, text), lineChar); + + if (token.kind() === SyntaxKind.StringLiteral) { + var ref = { + line: lineChar.line, + character: lineChar.character, + position: TypeScript.start(token, text), + length: width(token), + path: stripStartAndEndQuotes(switchToForwardSlashes(token.text())), + isResident: false + }; + importedFiles.push(ref); + } + } + } + } + } + } + + token = scanner.scan(/*allowRegularExpression:*/ false); + } + + var totalTime = new Date().getTime() - start; + //TypeScript.fileResolutionScanImportsTime += totalTime; + } + + function processTripleSlashDirectives(fileName: string, text: ISimpleText, firstToken: ISyntaxToken): ITripleSlashDirectiveProperties { + var leadingTrivia = firstToken.leadingTrivia(text); + + var position = 0; + var lineChar = { line: -1, character: -1 }; + var noDefaultLib = false; + var diagnostics: Diagnostic[] = []; + var referencedFiles: IFileReference[] = []; + var lineMap = text.lineMap() + + for (var i = 0, n = leadingTrivia.count(); i < n; i++) { + var trivia = leadingTrivia.syntaxTriviaAt(i); + + if (trivia.kind() === SyntaxKind.SingleLineCommentTrivia) { + var triviaText = trivia.fullText(); + var referencedCode = getFileReferenceFromReferencePath(fileName, text, position, triviaText, diagnostics); + + if (referencedCode) { + lineMap.fillLineAndCharacterFromPosition(position, lineChar); + referencedCode.position = position; + referencedCode.length = trivia.fullWidth(); + referencedCode.line = lineChar.line; + referencedCode.character = lineChar.character; + + referencedFiles.push(referencedCode); + } + + // is it a lib file? + var isNoDefaultLib = isNoDefaultLibMatch(triviaText); + if (isNoDefaultLib) { + noDefaultLib = isNoDefaultLib[3] === "true"; + } + } + + position += trivia.fullWidth(); + } + + return { noDefaultLib: noDefaultLib, diagnostics: diagnostics, referencedFiles: referencedFiles }; + } + + export function preProcessFile(fileName: string, sourceText: IScriptSnapshot, readImportFiles = true): IPreProcessedFileInfo { + var text = SimpleText.fromScriptSnapshot(sourceText); + var scanner = Scanner.createScanner(ts.ScriptTarget.ES5, text, reportDiagnostic); + + var firstToken = scanner.scan(/*allowRegularExpression:*/ false); + + // only search out dynamic mods + // if you find a dynamic mod, ignore every other mod inside, until you balance rcurlies + // var position + + var importedFiles: IFileReference[] = []; + if (readImportFiles) { + processImports(text, scanner, firstToken, importedFiles); + } + + var properties = processTripleSlashDirectives(fileName, text, firstToken); + + return { referencedFiles: properties.referencedFiles, importedFiles: importedFiles, isLibFile: properties.noDefaultLib, diagnostics: properties.diagnostics }; + } + + export function getReferencedFiles(fileName: string, sourceText: IScriptSnapshot): IFileReference[] { + return preProcessFile(fileName, sourceText, false).referencedFiles; + } +} // Tools \ No newline at end of file diff --git a/src/services/compiler/process.ts b/src/services/compiler/process.ts new file mode 100644 index 00000000000..cd4cfde5f67 --- /dev/null +++ b/src/services/compiler/process.ts @@ -0,0 +1,17 @@ +//declare module process { +// export var argv: string[]; +// export var platform: string; +// export function on(event: string, handler: (arg: any) => void ): void; +// export module stdout { +// export function write(str: string): any; +// export function on(event: string, action: () => void ): void; +// } +// export module stderr { +// export function write(str: string): any; +// export function on(event: string, action: () => void): void; +// } +// export module mainModule { +// export var filename: string; +// } +// export function exit(exitCode?: number): any; +//} \ No newline at end of file diff --git a/src/services/compiler/references.ts b/src/services/compiler/references.ts new file mode 100644 index 00000000000..464af87ec50 --- /dev/null +++ b/src/services/compiler/references.ts @@ -0,0 +1,37 @@ +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// + +///// +///// diff --git a/src/services/compiler/sourceMapping.ts b/src/services/compiler/sourceMapping.ts new file mode 100644 index 00000000000..11fae5b4353 --- /dev/null +++ b/src/services/compiler/sourceMapping.ts @@ -0,0 +1,270 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export class SourceMapPosition { + public sourceLine: number; + public sourceColumn: number; + public emittedLine: number; + public emittedColumn: number; + } + + export class SourceMapping { + public start = new SourceMapPosition(); + public end = new SourceMapPosition(); + public nameIndex: number = -1; + public childMappings: SourceMapping[] = []; + } + + export class SourceMapEntry { + constructor( + public emittedFile: string, + public emittedLine: number, + public emittedColumn: number, + public sourceFile: string, + public sourceLine: number, + public sourceColumn: number, + public sourceName: string) { + + Debug.assert(isFinite(emittedLine)); + Debug.assert(isFinite(emittedColumn)); + Debug.assert(isFinite(sourceColumn)); + Debug.assert(isFinite(sourceLine)); + } + } + + export class SourceMapper { + static MapFileExtension = ".map"; + + private jsFileName: string; + private sourceMapPath: string; + private sourceMapDirectory: string; + private sourceRoot: string; + + public names: string[] = []; + + private mappingLevel: ISpan[] = []; + + // Below two arrays represent the information about sourceFile at that index. + private tsFilePaths: string[] = []; + private allSourceMappings: SourceMapping[][] = []; + + public currentMappings: SourceMapping[][]; + public currentNameIndex: number[]; + + private sourceMapEntries: SourceMapEntry[] = []; + + constructor(private jsFile: TextWriter, + private sourceMapOut: TextWriter, + document: Document, + jsFilePath: string, + emitOptions: EmitOptions, + resolvePath: (path: string) => string) { + this.setSourceMapOptions(document, jsFilePath, emitOptions, resolvePath); + this.setNewSourceFile(document, emitOptions); + } + + public getOutputFile(): OutputFile { + var result = this.sourceMapOut.getOutputFile(); + result.sourceMapEntries = this.sourceMapEntries; + + return result; + } + + public increaseMappingLevel(ast: ISpan) { + this.mappingLevel.push(ast); + } + + public decreaseMappingLevel(ast: any) { + Debug.assert(this.mappingLevel.length > 0, "Mapping level should never be less than 0. This suggests a missing start call."); + var expectedAst = this.mappingLevel.pop(); + if (ast !== expectedAst) { + var expectedAstInfo: any = (expectedAst).kind ? SyntaxKind[(expectedAst).kind] : [expectedAst.start(), expectedAst.end()]; + var astInfo: any = (ast).kind ? SyntaxKind[(ast).kind] : [ast.start(), ast.end()] + Debug.fail( + "Provided ast is not the expected ISyntaxElement, Expected: " + expectedAstInfo + " Given: " + astInfo) + + } + } + + public setNewSourceFile(document: Document, emitOptions: EmitOptions) { + // Set new mappings + var sourceMappings: SourceMapping[] = []; + this.allSourceMappings.push(sourceMappings); + this.currentMappings = [sourceMappings]; + this.currentNameIndex = []; + + // Set new source file path + this.setNewSourceFilePath(document, emitOptions); + } + + private setSourceMapOptions(document: Document, jsFilePath: string, emitOptions: EmitOptions, resolvePath: (path: string) => string) { + // Decode mapRoot and sourceRoot + + // Js File Name = pretty name of js file + var prettyJsFileName = TypeScript.getPrettyName(jsFilePath, false, true); + var prettyMapFileName = prettyJsFileName + SourceMapper.MapFileExtension; + this.jsFileName = prettyJsFileName; + + // Figure out sourceMapPath and sourceMapDirectory + if (emitOptions.sourceMapRootDirectory()) { + // Get the sourceMap Directory + this.sourceMapDirectory = emitOptions.sourceMapRootDirectory(); + if (document.emitToOwnOutputFile()) { + // For modules or multiple emit files the mapRoot will have directory structure like the sources + // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map + this.sourceMapDirectory = this.sourceMapDirectory + switchToForwardSlashes(getRootFilePath((document.fileName)).replace(emitOptions.commonDirectoryPath(), "")); + } + + if (isRelative(this.sourceMapDirectory)) { + // The relative paths are relative to the common directory + this.sourceMapDirectory = emitOptions.commonDirectoryPath() + this.sourceMapDirectory; + this.sourceMapDirectory = convertToDirectoryPath(switchToForwardSlashes(resolvePath(this.sourceMapDirectory))); + this.sourceMapPath = getRelativePathToFixedPath(getRootFilePath(jsFilePath), this.sourceMapDirectory + prettyMapFileName); + } + else { + this.sourceMapPath = this.sourceMapDirectory + prettyMapFileName; + } + } + else { + this.sourceMapPath = prettyMapFileName; + this.sourceMapDirectory = getRootFilePath(jsFilePath); + } + this.sourceRoot = emitOptions.sourceRootDirectory(); + } + + private setNewSourceFilePath(document: Document, emitOptions: EmitOptions) { + var tsFilePath = switchToForwardSlashes(document.fileName); + if (emitOptions.sourceRootDirectory()) { + // Use the relative path corresponding to the common directory path + tsFilePath = getRelativePathToFixedPath(emitOptions.commonDirectoryPath(), tsFilePath); + } + else { + // Source locations relative to map file location + tsFilePath = getRelativePathToFixedPath(this.sourceMapDirectory, tsFilePath); + } + this.tsFilePaths.push(tsFilePath); + } + + // Generate source mapping. + // Creating files can cause exceptions, they will be caught higher up in TypeScriptCompiler.emit + public emitSourceMapping(): void { + Debug.assert( + this.mappingLevel.length === 0, + "Mapping level is not 0. This suggest a missing end call. Value: " + + this.mappingLevel.map(item => ['Node of type', SyntaxKind[(item).kind], 'at', item.start(), 'to', item.end()].join(' ')).join(', ')); + // Output map file name into the js file + this.jsFile.WriteLine("//# sourceMappingURL=" + this.sourceMapPath); + + // Now output map file + var mappingsString = ""; + + var prevEmittedColumn = 0; + var prevEmittedLine = 0; + var prevSourceColumn = 0; + var prevSourceLine = 0; + var prevSourceIndex = 0; + var prevNameIndex = 0; + var emitComma = false; + + var recordedPosition: SourceMapPosition = null; + for (var sourceIndex = 0; sourceIndex < this.tsFilePaths.length; sourceIndex++) { + var recordSourceMapping = (mappedPosition: SourceMapPosition, nameIndex: number) => { + + if (recordedPosition !== null && + recordedPosition.emittedColumn === mappedPosition.emittedColumn && + recordedPosition.emittedLine === mappedPosition.emittedLine) { + // This position is already recorded + return; + } + + // Record this position + if (prevEmittedLine !== mappedPosition.emittedLine) { + while (prevEmittedLine < mappedPosition.emittedLine) { + prevEmittedColumn = 0; + mappingsString = mappingsString + ";"; + prevEmittedLine++; + } + emitComma = false; + } + else if (emitComma) { + mappingsString = mappingsString + ","; + } + + this.sourceMapEntries.push(new SourceMapEntry( + this.jsFileName, + mappedPosition.emittedLine + 1, + mappedPosition.emittedColumn + 1, + this.tsFilePaths[sourceIndex], + mappedPosition.sourceLine, + mappedPosition.sourceColumn + 1, + nameIndex >= 0 ? this.names[nameIndex] : undefined)); + + // 1. Relative Column + mappingsString = mappingsString + Base64VLQFormat.encode(mappedPosition.emittedColumn - prevEmittedColumn); + prevEmittedColumn = mappedPosition.emittedColumn; + + // 2. Relative sourceIndex + mappingsString = mappingsString + Base64VLQFormat.encode(sourceIndex - prevSourceIndex); + prevSourceIndex = sourceIndex; + + // 3. Relative sourceLine 0 based + mappingsString = mappingsString + Base64VLQFormat.encode(mappedPosition.sourceLine - 1 - prevSourceLine); + prevSourceLine = mappedPosition.sourceLine - 1; + + // 4. Relative sourceColumn 0 based + mappingsString = mappingsString + Base64VLQFormat.encode(mappedPosition.sourceColumn - prevSourceColumn); + prevSourceColumn = mappedPosition.sourceColumn; + + // 5. Relative namePosition 0 based + if (nameIndex >= 0) { + mappingsString = mappingsString + Base64VLQFormat.encode(nameIndex - prevNameIndex); + prevNameIndex = nameIndex; + } + + emitComma = true; + recordedPosition = mappedPosition; + }; + + // Record starting spans + var recordSourceMappingSiblings = (sourceMappings: SourceMapping[]) => { + for (var i = 0; i < sourceMappings.length; i++) { + var sourceMapping = sourceMappings[i]; + recordSourceMapping(sourceMapping.start, sourceMapping.nameIndex); + recordSourceMappingSiblings(sourceMapping.childMappings); + recordSourceMapping(sourceMapping.end, sourceMapping.nameIndex); + } + }; + + recordSourceMappingSiblings(this.allSourceMappings[sourceIndex]); + } + + // Write the actual map file + this.sourceMapOut.Write(JSON.stringify({ + version: 3, + file: this.jsFileName, + sourceRoot: this.sourceRoot, + sources: this.tsFilePaths, + names: this.names, + mappings: mappingsString + })); + + // Closing files could result in exceptions, report them if they occur + this.sourceMapOut.Close(); + } + } +} \ No newline at end of file diff --git a/src/services/compiler/tsc.ts b/src/services/compiler/tsc.ts new file mode 100644 index 00000000000..e77ea9467ca --- /dev/null +++ b/src/services/compiler/tsc.ts @@ -0,0 +1,736 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// +/// +/// + +module TypeScript { + class SourceFile { + constructor(public scriptSnapshot: IScriptSnapshot, public byteOrderMark: ByteOrderMark) { + } + } + + class DiagnosticsLogger implements ILogger { + constructor(public ioHost: IEnvironment) { + } + public information(): boolean { return false; } + public debug(): boolean { return false; } + public warning(): boolean { return false; } + public error(): boolean { return false; } + public fatal(): boolean { return false; } + public log(s: string): void { + this.ioHost.standardOut.WriteLine(s); + } + } + + export class BatchCompiler implements IReferenceResolverHost { + public compilerVersion = "1.0.1.0"; + private inputFiles: string[] = []; + private compilationSettings: ImmutableCompilationSettings; + private resolvedFiles: IResolvedFile[] = []; + private fileNameToSourceFile = new StringHashTable(); + private hasErrors: boolean = false; + private logger: ILogger = null; + + constructor(private ioHost: IEnvironment) { + } + + // Begin batch compilation + public batchCompile() { + // Parse command line options + if (this.parseOptions()) { + var start = new Date().getTime(); + + if (this.compilationSettings.gatherDiagnostics()) { + this.logger = new DiagnosticsLogger(this.ioHost); + } else { + this.logger = new NullLogger(); + } + + if (this.compilationSettings.watch()) { + // Watch will cause the program to stick around as long as the files exist + this.watchFiles(); + return; + } + + // Resolve the compilation environemnt + this.resolve(); + + this.compile(); + + if (this.compilationSettings.gatherDiagnostics()) { + this.logger.log(""); + this.logger.log("File resolution time: " + TypeScript.fileResolutionTime); + this.logger.log(" file read: " + TypeScript.fileResolutionIOTime); + this.logger.log(" scan imports: " + TypeScript.fileResolutionScanImportsTime); + this.logger.log(" import search: " + TypeScript.fileResolutionImportFileSearchTime); + this.logger.log(" get lib.d.ts: " + TypeScript.fileResolutionGetDefaultLibraryTime); + + this.logger.log("SyntaxTree parse time: " + TypeScript.syntaxTreeParseTime); + this.logger.log("Syntax Diagnostics time: " + TypeScript.syntaxDiagnosticsTime); + this.logger.log("Create declarations time: " + TypeScript.createDeclarationsTime); + this.logger.log(""); + this.logger.log("Type check time: " + TypeScript.typeCheckTime); + this.logger.log(""); + this.logger.log("Emit time: " + TypeScript.emitTime); + this.logger.log("Declaration emit time: " + TypeScript.declarationEmitTime); + + this.logger.log("Total number of symbols created: " + TypeScript.pullSymbolID); + this.logger.log("Specialized types created: " + TypeScript.nSpecializationsCreated); + this.logger.log("Specialized signatures created: " + TypeScript.nSpecializedSignaturesCreated); + + this.logger.log(" IsExternallyVisibleTime: " + TypeScript.declarationEmitIsExternallyVisibleTime); + this.logger.log(" TypeSignatureTime: " + TypeScript.declarationEmitTypeSignatureTime); + this.logger.log(" GetBoundDeclTypeTime: " + TypeScript.declarationEmitGetBoundDeclTypeTime); + this.logger.log(" IsOverloadedCallSignatureTime: " + TypeScript.declarationEmitIsOverloadedCallSignatureTime); + this.logger.log(" FunctionDeclarationGetSymbolTime: " + TypeScript.declarationEmitFunctionDeclarationGetSymbolTime); + this.logger.log(" GetBaseTypeTime: " + TypeScript.declarationEmitGetBaseTypeTime); + this.logger.log(" GetAccessorFunctionTime: " + TypeScript.declarationEmitGetAccessorFunctionTime); + this.logger.log(" GetTypeParameterSymbolTime: " + TypeScript.declarationEmitGetTypeParameterSymbolTime); + this.logger.log(" GetImportDeclarationSymbolTime: " + TypeScript.declarationEmitGetImportDeclarationSymbolTime); + + this.logger.log("Emit write file time: " + TypeScript.emitWriteFileTime); + + this.logger.log("Compiler resolve path time: " + TypeScript.compilerResolvePathTime); + this.logger.log("Compiler directory name time: " + TypeScript.compilerDirectoryNameTime); + this.logger.log("Compiler directory exists time: " + TypeScript.compilerDirectoryExistsTime); + this.logger.log("Compiler file exists time: " + TypeScript.compilerFileExistsTime); + + this.logger.log("IO host resolve path time: " + TypeScript.ioHostResolvePathTime); + this.logger.log("IO host directory name time: " + TypeScript.ioHostDirectoryNameTime); + this.logger.log("IO host create directory structure time: " + TypeScript.ioHostCreateDirectoryStructureTime); + this.logger.log("IO host write file time: " + TypeScript.ioHostWriteFileTime); + + this.logger.log("Node make directory time: " + TypeScript.nodeMakeDirectoryTime); + this.logger.log("Node writeFileSync time: " + TypeScript.nodeWriteFileSyncTime); + this.logger.log("Node createBuffer time: " + TypeScript.nodeCreateBufferTime); + + this.logger.log("Total time: " + (new Date().getTime() - start)); + } + } + + // Exit with the appropriate error code + this.ioHost.quit(this.hasErrors ? 1 : 0); + } + + private resolve() { + // Resolve file dependencies, if requested + var includeDefaultLibrary = !this.compilationSettings.noLib(); + var resolvedFiles: IResolvedFile[] = []; + + var start = new Date().getTime(); + + if (!this.compilationSettings.noResolve()) { + // Resolve references + var resolutionResults = ReferenceResolver.resolve(this.inputFiles, this, this.compilationSettings.useCaseSensitiveFileResolution()); + resolvedFiles = resolutionResults.resolvedFiles; + + // Only include the library if useDefaultLib is set to true and did not see any 'no-default-lib' comments + includeDefaultLibrary = !this.compilationSettings.noLib() && !resolutionResults.seenNoDefaultLibTag; + + // Populate any diagnostic messages generated during resolution + resolutionResults.diagnostics.forEach(d => this.addDiagnostic(d)); + } + else { + for (var i = 0, n = this.inputFiles.length; i < n; i++) { + var inputFile = this.inputFiles[i]; + var referencedFiles: string[] = []; + var importedFiles: string[] = []; + + // If declaration files are going to be emitted, preprocess the file contents and add in referenced files as well + if (this.compilationSettings.generateDeclarationFiles()) { + var references = getReferencedFiles(inputFile, this.getScriptSnapshot(inputFile)); + for (var j = 0; j < references.length; j++) { + referencedFiles.push(references[j].path); + } + + inputFile = this.resolvePath(inputFile); + } + + resolvedFiles.push({ + path: inputFile, + referencedFiles: referencedFiles, + importedFiles: importedFiles + }); + } + } + + var defaultLibStart = new Date().getTime(); + if (includeDefaultLibrary) { + var libraryResolvedFile: IResolvedFile = { + path: this.getDefaultLibraryFilePath(), + referencedFiles: [], + importedFiles: [] + }; + + // Prepend the library to the resolved list + resolvedFiles = [libraryResolvedFile].concat(resolvedFiles); + } + TypeScript.fileResolutionGetDefaultLibraryTime += new Date().getTime() - defaultLibStart; + + this.resolvedFiles = resolvedFiles; + + TypeScript.fileResolutionTime = new Date().getTime() - start; + } + + // Returns true if compilation failed from some reason. + private compile(): void { + var compiler = new TypeScriptCompiler(this.logger, this.compilationSettings); + + this.resolvedFiles.forEach(resolvedFile => { + var sourceFile = this.getSourceFile(resolvedFile.path); + compiler.addFile(resolvedFile.path, sourceFile.scriptSnapshot, sourceFile.byteOrderMark, /*version:*/ 0, /*isOpen:*/ false, resolvedFile.referencedFiles); + }); + + for (var it = compiler.compile((path: string) => this.resolvePath(path)); it.moveNext();) { + var result = it.current(); + + result.diagnostics.forEach(d => this.addDiagnostic(d)); + if (!this.tryWriteOutputFiles(result.outputFiles)) { + return; + } + } + } + + // Parse command line options + private parseOptions() { + var opts = new OptionsParser(this.ioHost, this.compilerVersion); + + var mutableSettings = new CompilationSettings(); + opts.option('out', { + usage: { + locCode: DiagnosticCode.Concatenate_and_emit_output_to_single_file, + args: null + }, + type: DiagnosticCode.file2, + set: (str) => { + mutableSettings.outFileOption = str; + } + }); + + opts.option('outDir', { + usage: { + locCode: DiagnosticCode.Redirect_output_structure_to_the_directory, + args: null + }, + type: DiagnosticCode.DIRECTORY, + set: (str) => { + mutableSettings.outDirOption = str; + } + }); + + opts.flag('sourcemap', { + usage: { + locCode: DiagnosticCode.Generates_corresponding_0_file, + args: ['.map'] + }, + set: () => { + mutableSettings.mapSourceFiles = true; + } + }); + + opts.option('mapRoot', { + usage: { + locCode: DiagnosticCode.Specifies_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations, + args: null + }, + type: DiagnosticCode.LOCATION, + set: (str) => { + mutableSettings.mapRoot = str; + } + }); + + opts.option('sourceRoot', { + usage: { + locCode: DiagnosticCode.Specifies_the_location_where_debugger_should_locate_TypeScript_files_instead_of_source_locations, + args: null + }, + type: DiagnosticCode.LOCATION, + set: (str) => { + mutableSettings.sourceRoot = str; + } + }); + + opts.flag('declaration', { + usage: { + locCode: DiagnosticCode.Generates_corresponding_0_file, + args: ['.d.ts'] + }, + set: () => { + mutableSettings.generateDeclarationFiles = true; + } + }, 'd'); + + if (this.ioHost.watchFile) { + opts.flag('watch', { + usage: { + locCode: DiagnosticCode.Watch_input_files, + args: null + }, + set: () => { + mutableSettings.watch = true; + } + }, 'w'); + } + + opts.flag('propagateEnumConstants', { + experimental: true, + set: () => { mutableSettings.propagateEnumConstants = true; } + }); + + opts.flag('removeComments', { + usage: { + locCode: DiagnosticCode.Do_not_emit_comments_to_output, + args: null + }, + set: () => { + mutableSettings.removeComments = true; + } + }); + + opts.flag('noResolve', { + experimental: true, + usage: { + locCode: DiagnosticCode.Skip_resolution_and_preprocessing, + args: null + }, + set: () => { + mutableSettings.noResolve = true; + } + }); + + opts.flag('noLib', { + experimental: true, + set: () => { + mutableSettings.noLib = true; + } + }); + + opts.flag('diagnostics', { + experimental: true, + set: () => { + mutableSettings.gatherDiagnostics = true; + } + }); + + opts.option('target', { + usage: { + locCode: DiagnosticCode.Specify_ECMAScript_target_version_0_default_or_1, + args: ['ES3', 'ES5'] + }, + type: DiagnosticCode.VERSION, + set: (type) => { + type = type.toLowerCase(); + + if (type === 'es3') { + mutableSettings.codeGenTarget = LanguageVersion.EcmaScript3; + } + else if (type === 'es5') { + mutableSettings.codeGenTarget = LanguageVersion.EcmaScript5; + } + else { + this.addDiagnostic( + new Diagnostic(null, null, 0, 0, DiagnosticCode.Argument_for_0_option_must_be_1_or_2, ["target", "ES3", "ES5"])); + } + } + }, 't'); + + opts.option('module', { + usage: { + locCode: DiagnosticCode.Specify_module_code_generation_0_or_1, + args: ['commonjs', 'amd'] + }, + type: DiagnosticCode.KIND, + set: (type) => { + type = type.toLowerCase(); + + if (type === 'commonjs') { + mutableSettings.moduleGenTarget = ModuleGenTarget.Synchronous; + } + else if (type === 'amd') { + mutableSettings.moduleGenTarget = ModuleGenTarget.Asynchronous; + } + else { + this.addDiagnostic( + new Diagnostic(null, null, 0, 0, DiagnosticCode.Argument_for_0_option_must_be_1_or_2, ["module", "commonjs", "amd"])); + } + } + }, 'm'); + + var needsHelp = false; + opts.flag('help', { + usage: { + locCode: DiagnosticCode.Print_this_message, + args: null + }, + set: () => { + needsHelp = true; + } + }, 'h'); + + opts.flag('useCaseSensitiveFileResolution', { + experimental: true, + set: () => { + mutableSettings.useCaseSensitiveFileResolution = true; + } + }); + var shouldPrintVersionOnly = false; + opts.flag('version', { + usage: { + locCode: DiagnosticCode.Print_the_compiler_s_version_0, + args: [this.compilerVersion] + }, + set: () => { + shouldPrintVersionOnly = true; + } + }, 'v'); + + var locale: string = null; + opts.option('locale', { + experimental: true, + usage: { + locCode: DiagnosticCode.Specify_locale_for_errors_and_messages_For_example_0_or_1, + args: ['en', 'ja-jp'] + }, + type: DiagnosticCode.STRING, + set: (value) => { + locale = value; + } + }); + + opts.flag('noImplicitAny', { + usage: { + locCode: DiagnosticCode.Warn_on_expressions_and_declarations_with_an_implied_any_type, + args: null + }, + set: () => { + mutableSettings.noImplicitAny = true; + } + }); + + if (Environment.supportsCodePage()) { + opts.option('codepage', { + usage: { + locCode: DiagnosticCode.Specify_the_codepage_to_use_when_opening_source_files, + args: null + }, + type: DiagnosticCode.NUMBER, + set: (arg) => { + mutableSettings.codepage = parseInt(arg, 10); + } + }); + } + + opts.parse(this.ioHost.arguments); + + this.compilationSettings = ImmutableCompilationSettings.fromCompilationSettings(mutableSettings); + + if (locale) { + if (!this.setLocale(locale)) { + return false; + } + } + + this.inputFiles.push.apply(this.inputFiles, opts.unnamed); + + if (shouldPrintVersionOnly) { + opts.printVersion(); + return false; + } + // If no source files provided to compiler - print usage information + else if (this.inputFiles.length === 0 || needsHelp) { + opts.printUsage(); + return false; + } + + return !this.hasErrors; + } + + private setLocale(locale: string): boolean { + var matchResult = /^([a-z]+)([_\-]([a-z]+))?$/.exec(locale.toLowerCase()); + if (!matchResult) { + this.addDiagnostic(new Diagnostic(null, null, 0, 0, DiagnosticCode.Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1, ['en', 'ja-jp'])); + return false; + } + + var language = matchResult[1]; + var territory = matchResult[3]; + + // First try the entire locale, then fall back to just language if that's all we have. + if (!this.setLanguageAndTerritory(language, territory) && + !this.setLanguageAndTerritory(language, null)) { + + this.addDiagnostic(new Diagnostic(null, null, 0, 0, DiagnosticCode.Unsupported_locale_0, [locale])); + return false; + } + + return true; + } + + private setLanguageAndTerritory(language: string, territory: string): boolean { + + var compilerFilePath = this.ioHost.executingFilePath(); + var containingDirectoryPath = this.ioHost.directoryName(compilerFilePath); + + var filePath = IOUtils.combine(containingDirectoryPath, language); + if (territory) { + filePath = filePath + "-" + territory; + } + + filePath = this.resolvePath(IOUtils.combine(filePath, "diagnosticMessages.generated.json")); + + if (!this.fileExists(filePath)) { + return false; + } + + var fileContents = this.ioHost.readFile(filePath, this.compilationSettings.codepage()); + TypeScript.LocalizedDiagnosticMessages = JSON.parse(fileContents.contents); + return true; + } + + // Handle -watch switch + private watchFiles() { + if (!this.ioHost.watchFile) { + this.addDiagnostic( + new Diagnostic(null, null, 0, 0, DiagnosticCode.Current_host_does_not_support_0_option, ['-w[atch]'])); + return; + } + + var lastResolvedFileSet: string[] = [] + var watchers: { [x: string]: IFileWatcher; } = {}; + var firstTime = true; + + var addWatcher = (fileName: string) => { + if (!watchers[fileName]) { + var watcher = this.ioHost.watchFile(fileName, onWatchedFileChange); + watchers[fileName] = watcher; + } + }; + + var removeWatcher = (fileName: string) => { + if (watchers[fileName]) { + watchers[fileName].close(); + delete watchers[fileName]; + } + }; + + var onWatchedFileChange = () => { + // Clean errors for previous compilation + this.hasErrors = false; + + // Clear out any source file data we've cached. + this.fileNameToSourceFile = new StringHashTable(); + + // Resolve file dependencies, if requested + this.resolve(); + + // Check if any new files were added to the environment as a result of the file change + var oldFiles = lastResolvedFileSet; + var newFiles = this.resolvedFiles.map(resolvedFile => resolvedFile.path).sort(); + + var i = 0, j = 0; + while (i < oldFiles.length && j < newFiles.length) { + + var compareResult = oldFiles[i].localeCompare(newFiles[j]); + if (compareResult === 0) { + // No change here + i++; + j++; + } + else if (compareResult < 0) { + // Entry in old list does not exist in the new one, it was removed + removeWatcher(oldFiles[i]); + i++; + } + else { + // Entry in new list does exist in the new one, it was added + addWatcher(newFiles[j]); + j++; + } + } + + // All remaining unmatched items in the old list have been removed + for (var k = i; k < oldFiles.length; k++) { + removeWatcher(oldFiles[k]); + } + + // All remaing unmatched items in the new list have been added + for (k = j; k < newFiles.length; k++) { + addWatcher(newFiles[k]); + } + + // Update the state + lastResolvedFileSet = newFiles; + + // Print header + if (!firstTime) { + var fileNames = ""; + for (var k = 0; k < lastResolvedFileSet.length; k++) { + fileNames += Environment.newLine + " " + lastResolvedFileSet[k]; + } + this.ioHost.standardError.WriteLine(getLocalizedText(DiagnosticCode.NL_Recompiling_0, [fileNames])); + } + else { + firstTime = false; + } + + // Trigger a new compilation + this.compile(); + }; + + // Switch to using stdout for all error messages + this.ioHost.standardOut = this.ioHost.standardOut; + + onWatchedFileChange(); + } + + private getSourceFile(fileName: string): SourceFile { + var sourceFile: SourceFile = this.fileNameToSourceFile.lookup(fileName); + if (!sourceFile) { + // Attempt to read the file + var fileInformation: FileInformation; + + try { + fileInformation = this.ioHost.readFile(fileName, this.compilationSettings.codepage()); + } + catch (e) { + this.addDiagnostic(new Diagnostic(null, null, 0, 0, DiagnosticCode.Cannot_read_file_0_1, [fileName, e.message])); + fileInformation = new FileInformation("", ByteOrderMark.None); + } + + var snapshot = ScriptSnapshot.fromString(fileInformation.contents); + var sourceFile = new SourceFile(snapshot, fileInformation.byteOrderMark); + this.fileNameToSourceFile.add(fileName, sourceFile); + } + + return sourceFile; + } + + private getDefaultLibraryFilePath(): string { + var compilerFilePath = this.ioHost.executingFilePath(); + var containingDirectoryPath = this.ioHost.directoryName(compilerFilePath); + var libraryFilePath = this.resolvePath(IOUtils.combine(containingDirectoryPath, "lib.d.ts")); + + return libraryFilePath; + } + + /// IReferenceResolverHost methods + getScriptSnapshot(fileName: string): IScriptSnapshot { + return this.getSourceFile(fileName).scriptSnapshot; + } + + resolveRelativePath(path: string, directory: string): string { + var unQuotedPath = stripStartAndEndQuotes(path); + var normalizedPath: string; + + if (isRooted(unQuotedPath) || !directory) { + normalizedPath = unQuotedPath; + } else { + normalizedPath = IOUtils.combine(directory, unQuotedPath); + } + + // get the absolute path + normalizedPath = this.resolvePath(normalizedPath); + + // Switch to forward slashes + normalizedPath = switchToForwardSlashes(normalizedPath); + + return normalizedPath; + } + + private fileExistsCache = createIntrinsicsObject(); + + fileExists(path: string): boolean { + var exists = this.fileExistsCache[path]; + if (exists === undefined) { + var start = new Date().getTime(); + exists = this.ioHost.fileExists(path); + this.fileExistsCache[path] = exists; + TypeScript.compilerFileExistsTime += new Date().getTime() - start; + } + + return exists; + } + + getParentDirectory(path: string): string { + var start = new Date().getTime(); + var result = this.ioHost.directoryName(path); + TypeScript.compilerDirectoryNameTime += new Date().getTime() - start; + + return result; + } + + + private addDiagnostic(diagnostic: Diagnostic): void { + var diagnosticInfo = diagnostic.info(); + if (diagnosticInfo.category === DiagnosticCategory.Error) { + this.hasErrors = true; + } + + this.ioHost.standardError.Write(TypeScriptCompiler.getFullDiagnosticText(diagnostic, path => this.resolvePath(path))); + } + + private tryWriteOutputFiles(outputFiles: OutputFile[]): boolean { + for (var i = 0, n = outputFiles.length; i < n; i++) { + var outputFile = outputFiles[i]; + + try { + this.writeFile(outputFile.name, outputFile.text, outputFile.writeByteOrderMark); + } + catch (e) { + this.addDiagnostic( + new Diagnostic(outputFile.name, null, 0, 0, DiagnosticCode.Emit_Error_0, [e.message])); + return false; + } + } + + return true; + } + + writeFile(fileName: string, contents: string, writeByteOrderMark: boolean): void { + var start = new Date().getTime(); + IOUtils.writeFileAndFolderStructure(this.ioHost, fileName, contents, writeByteOrderMark); + TypeScript.emitWriteFileTime += new Date().getTime() - start; + } + + directoryExists(path: string): boolean { + var start = new Date().getTime(); + var result = this.ioHost.directoryExists(path); + TypeScript.compilerDirectoryExistsTime += new Date().getTime() - start; + return result; + } + + // For performance reasons we cache the results of resolvePath. This avoids costly lookup + // on the disk once we've already resolved a path once. + private resolvePathCache = createIntrinsicsObject(); + + resolvePath(path: string): string { + var cachedValue = this.resolvePathCache[path]; + if (!cachedValue) { + var start = new Date().getTime(); + cachedValue = this.ioHost.absolutePath(path); + this.resolvePathCache[path] = cachedValue; + TypeScript.compilerResolvePathTime += new Date().getTime() - start; + } + + return cachedValue; + } + } + + // Start the batch compilation using the current hosts IO + var batch = new TypeScript.BatchCompiler(Environment); + batch.batchCompile(); +} diff --git a/src/services/compiler/types.ts b/src/services/compiler/types.ts new file mode 100644 index 00000000000..28bef81629b --- /dev/null +++ b/src/services/compiler/types.ts @@ -0,0 +1,102 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript { + export class MemberName { + public prefix: string = ""; + public suffix: string = ""; + + public isString() { return false; } + public isArray() { return false; } + public isMarker() { return !this.isString() && !this.isArray(); } + + public toString(): string { + return MemberName.memberNameToString(this); + } + + static memberNameToString(memberName: MemberName, markerInfo?: number[], markerBaseLength: number = 0): string { + var result = memberName.prefix; + + if (memberName.isString()) { + result += (memberName).text; + } + else if (memberName.isArray()) { + var ar = memberName; + for (var index = 0; index < ar.entries.length; index++) { + if (ar.entries[index].isMarker()) { + if (markerInfo) { + markerInfo.push(markerBaseLength + result.length); + } + continue; + } + + result += MemberName.memberNameToString(ar.entries[index], markerInfo, markerBaseLength + result.length); + result += ar.delim; + } + } + + result += memberName.suffix; + return result; + } + + static create(text: string): MemberName; + static create(entry: MemberName, prefix: string, suffix: string): MemberName; + static create(arg1: any, arg2?: any, arg3?: any): MemberName { + if (typeof arg1 === "string") { + return new MemberNameString(arg1); + } + else { + var result = new MemberNameArray(); + if (arg2) + result.prefix = arg2; + if (arg3) + result.suffix = arg3; + result.entries.push(arg1); + return result; + } + } + } + + export class MemberNameString extends MemberName { + constructor(public text: string) { + super(); + } + + public isString() { return true; } + } + + export class MemberNameArray extends MemberName { + public delim: string = ""; + public entries: MemberName[] = []; + + public isArray() { return true; } + + public add(entry: MemberName) { + this.entries.push(entry); + } + + public addAll(entries: MemberName[]) { + for (var i = 0 ; i < entries.length; i++) { + this.entries.push(entries[i]); + } + } + + constructor() { + super(); + } + } +} \ No newline at end of file diff --git a/src/services/compiler/typescript.ts b/src/services/compiler/typescript.ts new file mode 100644 index 00000000000..03b59816b1d --- /dev/null +++ b/src/services/compiler/typescript.ts @@ -0,0 +1,1528 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +if (Error) (Error).stackTraceLimit = 1000; + +module TypeScript { + export var fileResolutionTime = 0; + export var fileResolutionIOTime = 0; + export var fileResolutionScanImportsTime = 0; + export var fileResolutionImportFileSearchTime = 0; + export var fileResolutionGetDefaultLibraryTime = 0; + export var sourceCharactersCompiled = 0; + export var syntaxTreeParseTime = 0; + export var typeCheckTime = 0; + export var createDeclarationsTime = 0; + + export var compilerResolvePathTime = 0; + export var compilerDirectoryNameTime = 0; + export var compilerDirectoryExistsTime = 0; + export var compilerFileExistsTime = 0; + + export var emitTime = 0; + export var emitWriteFileTime = 0; + + export var declarationEmitTime = 0; + export var declarationEmitIsExternallyVisibleTime = 0; + export var declarationEmitTypeSignatureTime = 0; + export var declarationEmitGetBoundDeclTypeTime = 0; + export var declarationEmitIsOverloadedCallSignatureTime = 0; + export var declarationEmitFunctionDeclarationGetSymbolTime = 0; + export var declarationEmitGetBaseTypeTime = 0; + export var declarationEmitGetAccessorFunctionTime = 0; + export var declarationEmitGetTypeParameterSymbolTime = 0; + export var declarationEmitGetImportDeclarationSymbolTime = 0; + + export var ioHostResolvePathTime = 0; + export var ioHostDirectoryNameTime = 0; + export var ioHostCreateDirectoryStructureTime = 0; + export var ioHostWriteFileTime = 0; + + export interface PullSymbolInfo { + symbol: PullSymbol; + aliasSymbol: PullTypeAliasSymbol; + ast: ISyntaxElement; + enclosingScopeSymbol: PullSymbol; + } + + export interface PullCallSymbolInfo { + targetSymbol: PullSymbol; + resolvedSignatures: TypeScript.PullSignatureSymbol[]; + candidateSignature: TypeScript.PullSignatureSymbol; + isConstructorCall: boolean; + ast: ISyntaxElement; + enclosingScopeSymbol: PullSymbol; + } + + export interface PullVisibleSymbolsInfo { + symbols: PullSymbol[]; + enclosingScopeSymbol: PullSymbol; + } + + export enum EmitOutputResult { + Succeeded, + FailedBecauseOfSyntaxErrors, + FailedBecauseOfCompilerOptionsErrors, + FailedToGenerateDeclarationsBecauseOfSemanticErrors + } + + export class EmitOutput { + public outputFiles: OutputFile[] = []; + public emitOutputResult: EmitOutputResult; + constructor(emitOutputResult = EmitOutputResult.Succeeded) { + this.emitOutputResult = emitOutputResult; + } + } + + export enum OutputFileType { + JavaScript, + SourceMap, + Declaration + } + + export class OutputFile { + constructor(public name: string, + public writeByteOrderMark: boolean, + public text: string, + public fileType: OutputFileType, + public sourceMapEntries: SourceMapEntry[] = []) { + } + } + + // Represents the results of the last "pull" on the compiler when using the streaming + // 'compile' method. The compile result for a single pull can have diagnostics (if + // something went wrong), and/or OutputFiles that need to get written. + export class CompileResult { + public diagnostics: Diagnostic[] = []; + public outputFiles: OutputFile[] = []; + + public static fromDiagnostics(diagnostics: Diagnostic[]): CompileResult { + var result = new CompileResult(); + result.diagnostics = diagnostics; + return result; + } + + public static fromOutputFiles(outputFiles: OutputFile[]): CompileResult { + var result = new CompileResult(); + result.outputFiles = outputFiles; + return result; + } + } + + export interface ICancellationToken { + isCancellationRequested(): boolean; + } + + export class OperationCanceledException { } + + export class CancellationToken { + + public static None: CancellationToken = new CancellationToken(null) + + constructor(private cancellationToken: ICancellationToken) { + } + + public isCancellationRequested() { + return this.cancellationToken && this.cancellationToken.isCancellationRequested(); + } + + public throwIfCancellationRequested(): void { + if (this.isCancellationRequested()) { + throw new OperationCanceledException(); + } + } + } + + class DocumentRegistryEntry { + public refCount: number = 0; + public owners: string[] = []; + constructor(public document: Document) { + } + } + + export interface IDocumentRegistry { + acquireDocument( + fileName: string, + compilationSettings: ImmutableCompilationSettings, + scriptSnapshot: IScriptSnapshot, + byteOrderMark: ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]): TypeScript.Document; + + updateDocument( + document: Document, + fileName: string, + compilationSettings: ImmutableCompilationSettings, + scriptSnapshot: IScriptSnapshot, + version: number, + isOpen: boolean, + textChangeRange: TextChangeRange + ): TypeScript.Document; + + releaseDocument(fileName: string, compilationSettings: ImmutableCompilationSettings): void + } + + export class NonCachingDocumentRegistry implements IDocumentRegistry { + + public static Instance: IDocumentRegistry = new NonCachingDocumentRegistry(); + + public acquireDocument( + fileName: string, + compilationSettings: ImmutableCompilationSettings, + scriptSnapshot: IScriptSnapshot, + byteOrderMark: ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]= []): TypeScript.Document { + return Document.create(compilationSettings, fileName, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles); + } + + public updateDocument( + document: Document, + fileName: string, + compilationSettings: ImmutableCompilationSettings, + scriptSnapshot: IScriptSnapshot, + version: number, + isOpen: boolean, + textChangeRange: TextChangeRange + ): TypeScript.Document { + return document.update(scriptSnapshot, version, isOpen, textChangeRange); + } + + public releaseDocument(fileName: string, compilationSettings: ImmutableCompilationSettings): void { + // no op since this class doesn't cache anything + } + } + + export class DocumentRegistry implements IDocumentRegistry { + private buckets: IIndexable> = {}; + + private getKeyFromCompilationSettings(settings: ImmutableCompilationSettings): string { + return "_" + settings.propagateEnumConstants().toString() + "|" + settings.allowAutomaticSemicolonInsertion().toString() + "|" + LanguageVersion[settings.codeGenTarget()]; + } + + private getBucketForCompilationSettings(settings: ImmutableCompilationSettings, createIfMissing: boolean): StringHashTable { + var key = this.getKeyFromCompilationSettings(settings); + var bucket = this.buckets[key]; + if (!bucket && createIfMissing) { + this.buckets[key] = bucket = new StringHashTable(); + } + return bucket; + } + + public reportStats() { + var bucketInfoArray = Object.keys(this.buckets).filter(name => name && name.charAt(0) === '_').map(name => { + var entries = this.buckets[name]; + var documents = entries.getAllKeys().map((name) => { + var entry = entries.lookup(name); + return { + name: name, + refCount: entry.refCount, + references: entry.owners.slice(0) + }; + }); + documents.sort((x, y) => y.refCount - x.refCount); + return { bucket: name, documents: documents } + }); + return JSON.stringify(bucketInfoArray, null, 2); + } + + public acquireDocument( + fileName: string, + compilationSettings: ImmutableCompilationSettings, + scriptSnapshot: IScriptSnapshot, + byteOrderMark: ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]= []): TypeScript.Document { + + var bucket = this.getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); + var entry = bucket.lookup(fileName); + if (!entry) { + var document = Document.create(compilationSettings, fileName, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles); + + entry = new DocumentRegistryEntry(document); + bucket.add(fileName, entry); + } + entry.refCount++; + + return entry.document; + } + + public updateDocument( + document: Document, + fileName: string, + compilationSettings: ImmutableCompilationSettings, + scriptSnapshot: IScriptSnapshot, + version: number, + isOpen: boolean, + textChangeRange: TextChangeRange + ): TypeScript.Document { + + var bucket = this.getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ false); + Debug.assert(bucket); + var entry = bucket.lookup(fileName); + Debug.assert(entry); + + if (entry.document.isOpen === isOpen && entry.document.version === version) { + return entry.document; + } + + entry.document = entry.document.update(scriptSnapshot, version, isOpen, textChangeRange); + return entry.document; + } + + public releaseDocument(fileName: string, compilationSettings: ImmutableCompilationSettings): void { + var bucket = this.getBucketForCompilationSettings(compilationSettings, false); + Debug.assert(bucket); + + var entry = bucket.lookup(fileName); + entry.refCount--; + + Debug.assert(entry.refCount >= 0); + if (entry.refCount === 0) { + bucket.remove(fileName); + } + } + } + + interface IExpressionWithArgumentListSyntax extends IExpressionSyntax { + expression: IExpressionSyntax; + argumentList: ArgumentListSyntax; + } + + export class TypeScriptCompiler { + private semanticInfoChain: SemanticInfoChain = null; + + constructor(public logger: ILogger = new NullLogger(), + private _settings: ImmutableCompilationSettings = ImmutableCompilationSettings.defaultSettings()) { + this.semanticInfoChain = new SemanticInfoChain(this, logger); + } + + public getSemanticInfoChain() { + return this.semanticInfoChain; + } + + public compilationSettings(): ImmutableCompilationSettings { + return this._settings; + } + + public setCompilationSettings(newSettings: ImmutableCompilationSettings) { + var oldSettings = this._settings; + this._settings = newSettings; + + if (!compareDataObjects(oldSettings, newSettings)) { + // If our options have changed at all, we have to consider any cached semantic + // data we have invalid. + this.semanticInfoChain.invalidate(oldSettings, newSettings); + } + } + + public getDocument(fileName: string): Document { + fileName = TypeScript.switchToForwardSlashes(fileName); + return this.semanticInfoChain.getDocument(fileName); + } + + public cleanupSemanticCache(): void { + this.semanticInfoChain.invalidate(); + } + + public addOrUpdateFile(document: Document): void { + // TODO: TypeScript.sourceCharactersCompiled += document. scriptSnapshot.getLength(); + // Note: the semantic info chain will recognize that this is a replacement of an + // existing script, and will handle it appropriately. + this.semanticInfoChain.addDocument(document); + } + + public addFile( + fileName: string, + scriptSnapshot: IScriptSnapshot, + byteOrderMark: ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]= []): void { + + fileName = TypeScript.switchToForwardSlashes(fileName); + var document = Document.create(this.compilationSettings(), fileName, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles); + this.addOrUpdateFile(document); + } + + public updateFile(fileName: string, scriptSnapshot: IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TextChangeRange): void { + fileName = TypeScript.switchToForwardSlashes(fileName); + + var document = this.getDocument(fileName); + var updatedDocument = document.update(scriptSnapshot, version, isOpen, textChangeRange); + + // Note: the semantic info chain will recognize that this is a replacement of an + // existing script, and will handle it appropriately. + this.addOrUpdateFile(updatedDocument); + } + + public removeFile(fileName: string): void { + fileName = TypeScript.switchToForwardSlashes(fileName); + this.semanticInfoChain.removeDocument(fileName); + } + + public mapOutputFileName(document: Document, emitOptions: EmitOptions, extensionChanger: (fname: string, wholeFileNameReplaced: boolean) => string) { + if (document.emitToOwnOutputFile()) { + var updatedFileName = document.fileName; + if (emitOptions.outputDirectory() !== "") { + // Replace the common directory path with the option specified + updatedFileName = document.fileName.replace(emitOptions.commonDirectoryPath(), ""); + updatedFileName = emitOptions.outputDirectory() + updatedFileName; + } + return extensionChanger(updatedFileName, false); + } + else { + return extensionChanger(emitOptions.sharedOutputFile(), true); + } + } + + private writeByteOrderMarkForDocument(document: Document) { + // Set this to 'true' if you want to know why the compiler emitted a document with a + // byte order mark. + var printReason = false; + + // If module its always emitted in its own file + if (document.emitToOwnOutputFile()) { + var result = document.byteOrderMark !== ByteOrderMark.None; + if (printReason) { + Environment.standardOut.WriteLine("Emitting byte order mark because of: " + document.fileName); + } + return result; + } + else { + var fileNames = this.fileNames(); + + var result = false; + for (var i = 0, n = fileNames.length; i < n; i++) { + var document = this.getDocument(fileNames[i]); + + if (document.syntaxTree().isExternalModule()) { + // Dynamic module never contributes to the single file + continue; + } + + if (document.byteOrderMark !== ByteOrderMark.None) { + if (printReason) { + Environment.standardOut.WriteLine("Emitting byte order mark because of: " + document.fileName); + result = true; + } + else { + return true; + } + } + } + + return result; + } + } + + static mapToDTSFileName(fileName: string, wholeFileNameReplaced: boolean) { + return getDeclareFilePath(fileName); + } + + public _shouldEmit(document: Document) { + // If its already a declare file or is resident or does not contain body + return !document.isDeclareFile(); + } + + public _shouldEmitDeclarations(document: Document) { + if (!this.compilationSettings().generateDeclarationFiles()) { + return false; + } + + return this._shouldEmit(document); + } + + // Does the actual work of emittin the declarations from the provided document into the + // provided emitter. If no emitter is provided a new one is created. + private emitDocumentDeclarationsWorker( + document: Document, + emitOptions: EmitOptions, + declarationEmitter?: DeclarationEmitter): DeclarationEmitter { + + var sourceUnit = document.sourceUnit(); + Debug.assert(this._shouldEmitDeclarations(document)); + + if (declarationEmitter) { + declarationEmitter.document = document; + } + else { + var declareFileName = this.mapOutputFileName(document, emitOptions, TypeScriptCompiler.mapToDTSFileName); + declarationEmitter = new DeclarationEmitter(declareFileName, document, this, emitOptions, this.semanticInfoChain); + } + + declarationEmitter.emitDeclarations(sourceUnit); + return declarationEmitter; + } + + public _emitDocumentDeclarations( + document: Document, + emitOptions: EmitOptions, + onSingleFileEmitComplete: (files: OutputFile) => void, + sharedEmitter: DeclarationEmitter): DeclarationEmitter { + + var start = new Date().getTime(); + if (this._shouldEmitDeclarations(document)) { + if (document.emitToOwnOutputFile()) { + var singleEmitter = this.emitDocumentDeclarationsWorker(document, emitOptions); + if (singleEmitter) { + onSingleFileEmitComplete(singleEmitter.getOutputFile()); + } + } + else { + // Create or reuse file + sharedEmitter = this.emitDocumentDeclarationsWorker(document, emitOptions, sharedEmitter); + } + } + + declarationEmitTime += new Date().getTime() - start; + return sharedEmitter; + } + + // Will not throw exceptions. + public emitAllDeclarations(resolvePath: (path: string) => string): EmitOutput { + var emitOutput = new EmitOutput(); + + var emitOptions = new EmitOptions(this, resolvePath); + if (emitOptions.diagnostic()) { + emitOutput.emitOutputResult = EmitOutputResult.FailedBecauseOfCompilerOptionsErrors; + return emitOutput; + } + + var sharedEmitter: DeclarationEmitter = null; + var fileNames = this.fileNames(); + + for (var i = 0, n = fileNames.length; i < n; i++) { + var fileName = fileNames[i]; + + var document = this.getDocument(fileNames[i]); + + sharedEmitter = this._emitDocumentDeclarations(document, emitOptions, + file => emitOutput.outputFiles.push(file), sharedEmitter); + } + + if (sharedEmitter) { + emitOutput.outputFiles.push(sharedEmitter.getOutputFile()); + } + + return emitOutput; + } + + // Will not throw exceptions. + public emitDeclarations(fileName: string, resolvePath: (path: string) => string): EmitOutput { + fileName = TypeScript.switchToForwardSlashes(fileName); + var emitOutput = new EmitOutput(); + + var emitOptions = new EmitOptions(this, resolvePath); + if (emitOptions.diagnostic()) { + emitOutput.emitOutputResult = EmitOutputResult.FailedBecauseOfCompilerOptionsErrors; + return emitOutput; + } + + var document = this.getDocument(fileName); + + // Emitting module or multiple files, always goes to single file + if (document.emitToOwnOutputFile()) { + this._emitDocumentDeclarations(document, emitOptions, + file => emitOutput.outputFiles.push(file), /*sharedEmitter:*/ null); + return emitOutput; + } + else { + return this.emitAllDeclarations(resolvePath); + } + } + + public canEmitDeclarations(fileName: string) { + fileName = TypeScript.switchToForwardSlashes(fileName); + var document = this.getDocument(fileName); + return this._shouldEmitDeclarations(document); + } + + static mapToFileNameExtension(extension: string, fileName: string, wholeFileNameReplaced: boolean) { + if (wholeFileNameReplaced) { + // The complete output is redirected in this file so do not change extension + return fileName; + } + else { + // Change the extension of the file + var splitFname = fileName.split("."); + splitFname.pop(); + return splitFname.join(".") + extension; + } + } + + static mapToJSFileName(fileName: string, wholeFileNameReplaced: boolean) { + return TypeScriptCompiler.mapToFileNameExtension(".js", fileName, wholeFileNameReplaced); + } + + // Caller is responsible for closing the returned emitter. + // May throw exceptions. + private emitDocumentWorker(document: Document, emitOptions: EmitOptions, emitter?: Emitter): Emitter { + var sourceUnit = document.sourceUnit(); + Debug.assert(this._shouldEmit(document)); + + var typeScriptFileName = document.fileName; + if (!emitter) { + var javaScriptFileName = this.mapOutputFileName(document, emitOptions, TypeScriptCompiler.mapToJSFileName); + var outFile = new TextWriter(javaScriptFileName, this.writeByteOrderMarkForDocument(document), OutputFileType.JavaScript); + + emitter = new Emitter(javaScriptFileName, outFile, emitOptions, this.semanticInfoChain); + + if (this.compilationSettings().mapSourceFiles()) { + // We always create map files next to the jsFiles + var sourceMapFile = new TextWriter(javaScriptFileName + SourceMapper.MapFileExtension, /*writeByteOrderMark:*/ false, OutputFileType.SourceMap); + emitter.createSourceMapper(document, javaScriptFileName, outFile, sourceMapFile, emitOptions.resolvePath); + } + } + else if (this.compilationSettings().mapSourceFiles()) { + // Already emitting into js file, update the mapper for new source info + emitter.setSourceMapperNewSourceFile(document); + } + + // Set location info + emitter.setDocument(document); + emitter.emitJavascript(sourceUnit, /*startLine:*/false); + + return emitter; + } + + // Private. only for use by compiler or CompilerIterator + public _emitDocument( + document: Document, + emitOptions: EmitOptions, + onSingleFileEmitComplete: (files: OutputFile[]) => void, + sharedEmitter: Emitter): Emitter { + + var start = new Date().getTime(); + + // Emitting module or multiple files, always goes to single file + if (this._shouldEmit(document)) { + if (document.emitToOwnOutputFile()) { + // We're outputting to mulitple files. We don't want to reuse an emitter in that case. + var singleEmitter = this.emitDocumentWorker(document, emitOptions); + if (singleEmitter) { + onSingleFileEmitComplete(singleEmitter.getOutputFiles()); + } + } + else { + // We're not outputting to multiple files. Keep using the same emitter and don't + // close until below. + sharedEmitter = this.emitDocumentWorker(document, emitOptions, sharedEmitter); + } + } + + emitTime += new Date().getTime() - start; + return sharedEmitter; + } + + // Will not throw exceptions. + public emitAll(resolvePath: (path: string) => string): EmitOutput { + var emitOutput = new EmitOutput(); + + var emitOptions = new EmitOptions(this, resolvePath); + if (emitOptions.diagnostic()) { + emitOutput.emitOutputResult = EmitOutputResult.FailedBecauseOfCompilerOptionsErrors; + return emitOutput; + } + + var fileNames = this.fileNames(); + var sharedEmitter: Emitter = null; + + // Iterate through the files, as long as we don't get an error. + for (var i = 0, n = fileNames.length; i < n; i++) { + var fileName = fileNames[i]; + + var document = this.getDocument(fileName); + + sharedEmitter = this._emitDocument(document, emitOptions, + files => emitOutput.outputFiles.push.apply(emitOutput.outputFiles, files), + sharedEmitter); + } + + if (sharedEmitter) { + emitOutput.outputFiles.push.apply(emitOutput.outputFiles, sharedEmitter.getOutputFiles()); + } + + return emitOutput; + } + + // Emit single file if outputMany is specified, else emit all + // Will not throw exceptions. + public emit(fileName: string, resolvePath: (path: string) => string): EmitOutput { + fileName = TypeScript.switchToForwardSlashes(fileName); + var emitOutput = new EmitOutput(); + + var emitOptions = new EmitOptions(this, resolvePath); + if (emitOptions.diagnostic()) { + emitOutput.emitOutputResult = EmitOutputResult.FailedBecauseOfCompilerOptionsErrors; + return emitOutput; + } + + var document = this.getDocument(fileName); + // Emitting module or multiple files, always goes to single file + if (document.emitToOwnOutputFile()) { + this._emitDocument(document, emitOptions, + files => emitOutput.outputFiles.push.apply(emitOutput.outputFiles, files), /*sharedEmitter:*/ null); + return emitOutput; + } + else { + // In output Single file mode, emit everything + return this.emitAll(resolvePath); + } + } + + // Returns an iterator that will stream compilation results from this compiler. Syntactic + // diagnostics will be returned first, then semantic diagnostics, then emit results, then + // declaration emit results. + // + // The continueOnDiagnostics flag governs whether or not iteration follows the batch compiler + // logic and doesn't perform further analysis once diagnostics are produced. For example, + // in batch compilation nothing is done if there are any syntactic diagnostics. Clients + // can override this if they still want to procede in those cases. + public compile(resolvePath: (path: string) => string, continueOnDiagnostics = false): Iterator { + return new CompilerIterator(this, resolvePath, continueOnDiagnostics); + } + + // + // Pull typecheck infrastructure + // + + public getSyntacticDiagnostics(fileName: string): Diagnostic[] { + fileName = TypeScript.switchToForwardSlashes(fileName) + return this.getDocument(fileName).diagnostics(); + } + + /** Used for diagnostics in tests */ + private getSyntaxTree(fileName: string): SyntaxTree { + return this.getDocument(fileName).syntaxTree(); + } + + private getSourceUnit(fileName: string): SourceUnitSyntax { + return this.getDocument(fileName).sourceUnit(); + } + + public getSemanticDiagnostics(fileName: string): Diagnostic[] { + fileName = TypeScript.switchToForwardSlashes(fileName); + + var document = this.getDocument(fileName); + + var startTime = (new Date()).getTime(); + PullTypeResolver.typeCheck(this.compilationSettings(), this.semanticInfoChain, document) + var endTime = (new Date()).getTime(); + + typeCheckTime += endTime - startTime; + + var errors = this.semanticInfoChain.getDiagnostics(fileName); + + errors = ArrayUtilities.distinct(errors, Diagnostic.equals); + errors.sort((d1, d2) => { + if (d1.fileName() < d2.fileName()) { + return -1; + } + else if (d1.fileName() > d2.fileName()) { + return 1; + } + + if (d1.start() < d2.start()) { + return -1; + } + else if (d1.start() > d2.start()) { + return 1; + } + + // For multiple errors reported on the same file at the same position. + var code1 = diagnosticInformationMap[d1.diagnosticKey()].code; + var code2 = diagnosticInformationMap[d2.diagnosticKey()].code; + if (code1 < code2) { + return -1; + } + else if (code1 > code2) { + return 1; + } + + return 0; + }); + + return errors; + } + + public getCompilerOptionsDiagnostics(resolvePath: (path: string) => string): Diagnostic[] { + var emitOptions = new EmitOptions(this, resolvePath); + var emitDiagnostic = emitOptions.diagnostic(); + if (emitDiagnostic) { + return [emitDiagnostic]; + } + return sentinelEmptyArray; + } + + public resolveAllFiles() { + var fileNames = this.fileNames(); + for (var i = 0, n = fileNames.length; i < n; i++) { + this.getSemanticDiagnostics(fileNames[i]); + } + } + + public getSymbolOfDeclaration(decl: PullDecl): PullSymbol { + if (!decl) { + return null; + } + + var resolver = this.semanticInfoChain.getResolver(); + var ast = this.semanticInfoChain.getASTForDecl(decl); + if (!ast) { + return null; + } + + var enclosingDecl = resolver.getEnclosingDecl(decl); + if (ast.kind() === SyntaxKind.GetAccessor || ast.kind() === SyntaxKind.SetAccessor) { + return this.getSymbolOfDeclaration(enclosingDecl); + } + + return resolver.resolveAST(ast, /*inContextuallyTypedAssignment:*/false, new PullTypeResolutionContext(resolver)); + } + + private extractResolutionContextFromAST(resolver: PullTypeResolver, ast: ISyntaxElement, document: Document, propagateContextualTypes: boolean): { ast: ISyntaxElement; enclosingDecl: PullDecl; resolutionContext: PullTypeResolutionContext; inContextuallyTypedAssignment: boolean; inWithBlock: boolean; } { + var scriptName = document.fileName; + + var enclosingDecl: PullDecl = null; + var enclosingDeclAST: ISyntaxElement = null; + var inContextuallyTypedAssignment = false; + var inWithBlock = false; + + var resolutionContext = new PullTypeResolutionContext(resolver); + + if (!ast) { + return null; + } + + var path = this.getASTPath(ast); + + // Extract infromation from path + for (var i = 0 , n = path.length; i < n; i++) { + var current = path[i]; + + switch (current.kind()) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.SimpleArrowFunctionExpression: + case SyntaxKind.ParenthesizedArrowFunctionExpression: + if (propagateContextualTypes) { + resolver.resolveAST(current, /*inContextuallyTypedAssignment*/ true, resolutionContext); + } + break; + + //case SyntaxKind.Parameter: + // var parameter = current; + // inContextuallyTypedAssignment = parameter.typeExpr !== null; + + // this.extractResolutionContextForVariable(inContextuallyTypedAssignment, propagateContextualTypes, resolver, resolutionContext, enclosingDecl, parameter, parameter.init); + // break; + + case SyntaxKind.MemberVariableDeclaration: + var memberVariable = current; + inContextuallyTypedAssignment = memberVariable.variableDeclarator.typeAnnotation !== null; + + this.extractResolutionContextForVariable(inContextuallyTypedAssignment, propagateContextualTypes, resolver, resolutionContext, enclosingDecl, memberVariable, memberVariable.variableDeclarator.equalsValueClause); + break; + + case SyntaxKind.VariableDeclarator: + var variableDeclarator = current; + inContextuallyTypedAssignment = variableDeclarator.typeAnnotation !== null; + + this.extractResolutionContextForVariable(inContextuallyTypedAssignment, propagateContextualTypes, resolver, resolutionContext, enclosingDecl, variableDeclarator, variableDeclarator.equalsValueClause); + break; + + case SyntaxKind.InvocationExpression: + case SyntaxKind.ObjectCreationExpression: + if (propagateContextualTypes) { + var isNew = current.kind() === SyntaxKind.ObjectCreationExpression; + var callExpression = current; + var contextualType: PullTypeSymbol = null; + + // Check if we are in an argumnt for a call, propagate the contextual typing + if ((i + 2 < n) && callExpression.argumentList === path[i + 1] && callExpression.argumentList.arguments === path[i + 2]) { + var callResolutionResults = new PullAdditionalCallResolutionData(); + if (isNew) { + resolver.resolveObjectCreationExpression(callExpression, resolutionContext, callResolutionResults); + } + else { + resolver.resolveInvocationExpression(callExpression, resolutionContext, callResolutionResults); + } + + // Find the index in the arguments list + if (callResolutionResults.actualParametersContextTypeSymbols) { + var argExpression = path[i + 3]; + if (argExpression) { + for (var j = 0, m = callExpression.argumentList.arguments.length; j < m; j++) { + if (callExpression.argumentList.arguments[j] === argExpression) { + var callContextualType = callResolutionResults.actualParametersContextTypeSymbols[j]; + if (callContextualType) { + contextualType = callContextualType; + break; + } + } + } + } + } + } + else { + // Just resolve the call expression + if (isNew) { + resolver.resolveObjectCreationExpression(callExpression, resolutionContext); + } + else { + resolver.resolveInvocationExpression(callExpression, resolutionContext); + } + } + + resolutionContext.pushNewContextualType(contextualType); + } + + break; + + case SyntaxKind.ArrayLiteralExpression: + if (propagateContextualTypes) { + // Propagate the child element type + var contextualType: PullTypeSymbol = null; + var currentContextualType = resolutionContext.getContextualType(); + if (currentContextualType && currentContextualType.isArrayNamedTypeReference()) { + contextualType = currentContextualType.getElementType(); + } + + resolutionContext.pushNewContextualType(contextualType); + } + + break; + + case SyntaxKind.ObjectLiteralExpression: + if (propagateContextualTypes) { + var objectLiteralExpression = current; + var objectLiteralResolutionContext = new PullAdditionalObjectLiteralResolutionData(); + resolver.resolveObjectLiteralExpression(objectLiteralExpression, inContextuallyTypedAssignment, resolutionContext, objectLiteralResolutionContext); + + // find the member in the path + var memeberAST = (path[i + 1] && path[i + 1].kind() === SyntaxKind.SeparatedList) ? path[i + 2] : path[i + 1]; + if (memeberAST) { + // Propagate the member contextual type + var contextualType: PullTypeSymbol = null; + var memberDecls = objectLiteralExpression.propertyAssignments; + if (memberDecls && objectLiteralResolutionContext.membersContextTypeSymbols) { + for (var j = 0, m = memberDecls.length; j < m; j++) { + if (memberDecls[j] === memeberAST) { + var memberContextualType = objectLiteralResolutionContext.membersContextTypeSymbols[j]; + if (memberContextualType) { + contextualType = memberContextualType; + break; + } + } + } + } + + resolutionContext.pushNewContextualType(contextualType); + } + } + + break; + + case SyntaxKind.AssignmentExpression: + if (propagateContextualTypes) { + var assignmentExpression = current; + var contextualType: PullTypeSymbol = null; + + if (path[i + 1] && path[i + 1] === assignmentExpression.right) { + // propagate the left hand side type as a contextual type + var leftType = resolver.resolveAST(assignmentExpression.left, inContextuallyTypedAssignment, resolutionContext).type; + if (leftType) { + inContextuallyTypedAssignment = true; + contextualType = leftType; + } + } + + resolutionContext.pushNewContextualType(contextualType); + } + + break; + + case SyntaxKind.CastExpression: + var castExpression = current; + if (!(i + 1 < n && path[i + 1] === castExpression.type)) { + // We are outside the cast term + if (propagateContextualTypes) { + var contextualType: PullTypeSymbol = null; + var typeSymbol = resolver.resolveAST(castExpression, inContextuallyTypedAssignment, resolutionContext).type; + + // Set the context type + if (typeSymbol) { + inContextuallyTypedAssignment = true; + contextualType = typeSymbol; + } + + resolutionContext.pushNewContextualType(contextualType); + } + } + + break; + + case SyntaxKind.ReturnStatement: + if (propagateContextualTypes) { + var returnStatement = current; + var contextualType: PullTypeSymbol = null; + + if (enclosingDecl && (enclosingDecl.kind & PullElementKind.SomeFunction)) { + var typeAnnotation = ASTHelpers.getType(enclosingDeclAST); + if (typeAnnotation) { + // The containing function has a type annotation, propagate it as the contextual type + var returnTypeSymbol = resolver.resolveTypeReference(typeAnnotation, resolutionContext); + if (returnTypeSymbol) { + inContextuallyTypedAssignment = true; + contextualType = returnTypeSymbol; + } + } + else { + // No type annotation, check if there is a contextual type enforced on the function, and propagate that + var currentContextualType = resolutionContext.getContextualType(); + if (currentContextualType && currentContextualType.isFunction()) { + var contextualSignatures = currentContextualType.kind == PullElementKind.ConstructorType + ? currentContextualType.getConstructSignatures() + : currentContextualType.getCallSignatures(); + var currentContextualTypeSignatureSymbol = contextualSignatures[0]; + var currentContextualTypeReturnTypeSymbol = currentContextualTypeSignatureSymbol.returnType; + if (currentContextualTypeReturnTypeSymbol) { + inContextuallyTypedAssignment = true; + contextualType = currentContextualTypeReturnTypeSymbol; + } + } + } + } + + resolutionContext.pushNewContextualType(contextualType); + } + + break; + + case SyntaxKind.ObjectType: + // ObjectType are just like Object Literals are bound when needed, ensure we have a decl, by forcing it to be + // resolved before descending into it. + if (propagateContextualTypes && TypeScript.isTypesOnlyLocation(current)) { + resolver.resolveAST(current, /*inContextuallyTypedAssignment*/ false, resolutionContext); + } + + break; + + case SyntaxKind.WithStatement: + inWithBlock = true; + break; + + case SyntaxKind.Block: + inContextuallyTypedAssignment = false; + break; + } + + // Record enclosing Decl + var decl = this.semanticInfoChain.getDeclForAST(current); + if (decl) { + enclosingDecl = decl; + enclosingDeclAST = current; + } + } + + // if the found ISyntaxElement is a named, we want to check for previous dotted expressions, + // since those will give us the right typing + if (ast && ast.parent && ast.kind() === SyntaxKind.IdentifierName) { + if (ast.parent.kind() === SyntaxKind.MemberAccessExpression) { + if ((ast.parent).name === ast) { + ast = ast.parent; + } + } + else if (ast.parent.kind() === SyntaxKind.QualifiedName) { + if ((ast.parent).right === ast) { + ast = ast.parent; + } + } + } + + return { + ast: ast, + enclosingDecl: enclosingDecl, + resolutionContext: resolutionContext, + inContextuallyTypedAssignment: inContextuallyTypedAssignment, + inWithBlock: inWithBlock + }; + } + + private extractResolutionContextForVariable( + inContextuallyTypedAssignment: boolean, + propagateContextualTypes: boolean, + resolver: PullTypeResolver, + resolutionContext: PullTypeResolutionContext, + enclosingDecl: PullDecl, + assigningAST: ISyntaxElement, + init: ISyntaxElement): void { + if (inContextuallyTypedAssignment) { + if (propagateContextualTypes) { + resolver.resolveAST(assigningAST, /*inContextuallyTypedAssignment*/false, resolutionContext); + var varSymbol = this.semanticInfoChain.getSymbolForAST(assigningAST); + + var contextualType: PullTypeSymbol = null; + if (varSymbol && inContextuallyTypedAssignment) { + contextualType = varSymbol.type; + } + + resolutionContext.pushNewContextualType(contextualType); + + if (init) { + resolver.resolveAST(init, inContextuallyTypedAssignment, resolutionContext); + } + } + } + } + + private getASTPath(ast: ISyntaxElement): ISyntaxElement[] { + var result: ISyntaxElement[] = []; + + while (ast) { + result.unshift(ast); + ast = ast.parent; + } + + return result; + } + + public pullGetSymbolInformationFromAST(ast: ISyntaxElement, document: Document): PullSymbolInfo { + var resolver = this.semanticInfoChain.getResolver(); + var context = this.extractResolutionContextFromAST(resolver, ast, document, /*propagateContextualTypes*/ true); + if (!context || context.inWithBlock) { + return null; + } + + ast = context.ast; + var symbol = resolver.resolveAST(ast, context.inContextuallyTypedAssignment, context.resolutionContext); + + if (!symbol) { + Debug.assert( + ast.kind() === SyntaxKind.SourceUnit, + "No symbol was found for ast and ast was not source unit. Ast Kind: " + SyntaxKind[ast.kind()] ); + return null; + } + + if (symbol.isTypeReference()) { + symbol = (symbol).getReferencedTypeSymbol(); + } + + var aliasSymbol = this.semanticInfoChain.getAliasSymbolForAST(ast); + + return { + symbol: symbol, + aliasSymbol: aliasSymbol, + ast: ast, + enclosingScopeSymbol: this.getSymbolOfDeclaration(context.enclosingDecl) + }; + } + + public pullGetCallInformationFromAST(ast: ISyntaxElement, document: Document): PullCallSymbolInfo { + // ISyntaxElement has to be a call expression + if (ast.kind() !== SyntaxKind.InvocationExpression && ast.kind() !== SyntaxKind.ObjectCreationExpression) { + return null; + } + + var isNew = ast.kind() === SyntaxKind.ObjectCreationExpression; + + var resolver = this.semanticInfoChain.getResolver(); + var context = this.extractResolutionContextFromAST(resolver, ast, document, /*propagateContextualTypes*/ true); + if (!context || context.inWithBlock) { + return null; + } + + var callResolutionResults = new PullAdditionalCallResolutionData(); + + if (isNew) { + resolver.resolveObjectCreationExpression(ast, context.resolutionContext, callResolutionResults); + } + else { + resolver.resolveInvocationExpression(ast, context.resolutionContext, callResolutionResults); + } + + return { + targetSymbol: callResolutionResults.targetSymbol, + resolvedSignatures: callResolutionResults.resolvedSignatures, + candidateSignature: callResolutionResults.candidateSignature, + ast: ast, + enclosingScopeSymbol: this.getSymbolOfDeclaration(context.enclosingDecl), + isConstructorCall: isNew + }; + } + + public pullGetVisibleMemberSymbolsFromAST(ast: ISyntaxElement, document: Document): PullVisibleSymbolsInfo { + var resolver = this.semanticInfoChain.getResolver(); + var context = this.extractResolutionContextFromAST(resolver, ast, document, /*propagateContextualTypes*/ true); + if (!context || context.inWithBlock) { + return null; + } + + var symbols = resolver.getVisibleMembersFromExpression(ast, context.enclosingDecl, context.resolutionContext); + if (!symbols) { + return null; + } + + return { + symbols: symbols, + enclosingScopeSymbol: this.getSymbolOfDeclaration(context.enclosingDecl) + }; + } + + public pullGetVisibleDeclsFromAST(ast: ISyntaxElement, document: Document): PullDecl[] { + var resolver = this.semanticInfoChain.getResolver(); + var context = this.extractResolutionContextFromAST(resolver, ast, document, /*propagateContextualTypes*/ false); + if (!context || context.inWithBlock) { + return null; + } + + return resolver.getVisibleDecls(context.enclosingDecl); + } + + public pullGetContextualMembersFromAST(ast: ISyntaxElement, document: Document): PullVisibleSymbolsInfo { + // Input has to be an object literal + if (ast.kind() !== SyntaxKind.ObjectLiteralExpression) { + return null; + } + + var resolver = this.semanticInfoChain.getResolver(); + var context = this.extractResolutionContextFromAST(resolver, ast, document, /*propagateContextualTypes*/ true); + if (!context || context.inWithBlock) { + return null; + } + + var members = resolver.getVisibleContextSymbols(context.enclosingDecl, context.resolutionContext); + + return { + symbols: members, + enclosingScopeSymbol: this.getSymbolOfDeclaration(context.enclosingDecl) + }; + } + + public pullGetDeclInformation(decl: PullDecl, ast: ISyntaxElement, document: Document): PullSymbolInfo { + var resolver = this.semanticInfoChain.getResolver(); + + // Note: we not only need to resolve down to the path the ast is at, but we also need to + // resolve the path to where the decl is at. This is because, currently, some decls + // can't fin their symbols unless they are first resolved. For example, a property of + // an object literal must be resolved before its symbol can be retrieved. + var context = this.extractResolutionContextFromAST(resolver, ast, document, /*propagateContextualTypes*/ true); + if (!context || context.inWithBlock) { + return null; + } + + var astForDecl = decl.ast(); + if (!astForDecl) { + return null; + } + + var astForDeclContext = this.extractResolutionContextFromAST( + resolver, astForDecl, this.getDocument(syntaxTree(astForDecl).fileName()), /*propagateContextualTypes*/ true); + if (!astForDeclContext) { + return null; + } + + var symbol = decl.getSymbol(this.semanticInfoChain); + resolver.resolveDeclaredSymbol(symbol, context.resolutionContext); + symbol.setUnresolved(); + + return { + symbol: symbol, + aliasSymbol: null, + ast: ast, + enclosingScopeSymbol: this.getSymbolOfDeclaration(context.enclosingDecl) + }; + } + + public topLevelDeclaration(fileName: string) : PullDecl { + return this.semanticInfoChain.topLevelDecl(fileName); + } + + public getDeclForAST(ast: ISyntaxElement): PullDecl { + return this.semanticInfoChain.getDeclForAST(ast); + } + + public fileNames(): string[] { + return this.semanticInfoChain.fileNames(); + } + + public topLevelDecl(fileName: string): PullDecl { + return this.semanticInfoChain.topLevelDecl(fileName); + } + + private static getLocationText(location: Location, resolvePath: (path: string) => string): string { + return resolvePath(location.fileName()) + "(" + (location.line() + 1) + "," + (location.character() + 1) + ")"; + } + + public static getFullDiagnosticText(diagnostic: Diagnostic, resolvePath: (path: string) => string): string { + var result = ""; + if (diagnostic.fileName()) { + result += this.getLocationText(diagnostic, resolvePath) + ": "; + } + + result += diagnostic.message(); + + var additionalLocations = diagnostic.additionalLocations(); + if (additionalLocations.length > 0) { + result += " " + getLocalizedText(DiagnosticCode.Additional_locations, null) + Environment.newLine; + + for (var i = 0, n = additionalLocations.length; i < n; i++) { + result += "\t" + this.getLocationText(additionalLocations[i], resolvePath) + Environment.newLine; + } + } + else { + result += Environment.newLine; + } + + return result; + } + } + + enum CompilerPhase { + Syntax, + Semantics, + EmitOptionsValidation, + Emit, + DeclarationEmit, + } + + class CompilerIterator implements Iterator { + private compilerPhase: CompilerPhase; + private index: number = -1; + private fileNames: string[] = null; + private _current: CompileResult = null; + private _emitOptions: EmitOptions = null; + private _sharedEmitter: Emitter = null; + private _sharedDeclarationEmitter: DeclarationEmitter = null; + private hadSyntacticDiagnostics: boolean = false; + private hadSemanticDiagnostics: boolean = false; + private hadEmitDiagnostics: boolean = false; + + constructor(private compiler: TypeScriptCompiler, + private resolvePath: (path: string) => string, + private continueOnDiagnostics: boolean, + startingPhase = CompilerPhase.Syntax) { + this.fileNames = compiler.fileNames(); + this.compilerPhase = startingPhase; + } + + public current(): CompileResult { + return this._current; + } + + public moveNext(): boolean { + this._current = null; + + // Attempt to move the iterator 'one step' forward. Note: this may produce no result + // (for example, if we're emitting everything to a single file). So only return once + // we actually have a result, or we're done enumerating. + while (this.moveNextInternal()) { + if (this._current) { + return true; + } + } + + return false; + } + + private moveNextInternal(): boolean { + this.index++; + + // If we're at the end of hte set of files the compiler knows about, then move to the + // next phase of compilation. + while (this.shouldMoveToNextPhase()) { + this.index = 0; + this.compilerPhase++; + } + + if (this.compilerPhase > CompilerPhase.DeclarationEmit) { + // We're totally done. + return false; + } + + switch (this.compilerPhase) { + case CompilerPhase.Syntax: + return this.moveNextSyntaxPhase(); + case CompilerPhase.Semantics: + return this.moveNextSemanticsPhase(); + case CompilerPhase.EmitOptionsValidation: + return this.moveNextEmitOptionsValidationPhase(); + case CompilerPhase.Emit: + return this.moveNextEmitPhase(); + case CompilerPhase.DeclarationEmit: + return this.moveNextDeclarationEmitPhase(); + } + } + + private shouldMoveToNextPhase(): boolean { + switch (this.compilerPhase) { + case CompilerPhase.EmitOptionsValidation: + // Only one step in emit validation. We're done once we do that step. + return this.index === 1; + + case CompilerPhase.Syntax: + case CompilerPhase.Semantics: + // Each of these phases are done when we've processed the last file. + return this.index === this.fileNames.length; + + case CompilerPhase.Emit: + case CompilerPhase.DeclarationEmit: + // Emitting is done when we get 'one' past the end of hte file list. This is + // because we use that step to collect the results from the shared emitter. + return this.index === (this.fileNames.length + 1); + } + + return false; + } + + private moveNextSyntaxPhase(): boolean { + Debug.assert(this.index >= 0 && this.index < this.fileNames.length); + var fileName = this.fileNames[this.index]; + + var diagnostics = this.compiler.getSyntacticDiagnostics(fileName); + if (diagnostics.length) { + if (!this.continueOnDiagnostics) { + this.hadSyntacticDiagnostics = true; + } + + this._current = CompileResult.fromDiagnostics(diagnostics); + } + + return true; + } + + private moveNextSemanticsPhase(): boolean { + // Don't move forward if there were syntax diagnostics. + if (this.hadSyntacticDiagnostics) { + return false; + } + + Debug.assert(this.index >= 0 && this.index < this.fileNames.length); + var fileName = this.fileNames[this.index]; + var diagnostics = this.compiler.getSemanticDiagnostics(fileName); + if (diagnostics.length) { + if (!this.continueOnDiagnostics) { + this.hadSemanticDiagnostics = true; + } + + this._current = CompileResult.fromDiagnostics(diagnostics); + } + + return true; + } + + private moveNextEmitOptionsValidationPhase(): boolean { + Debug.assert(!this.hadSyntacticDiagnostics); + + if (!this._emitOptions) { + this._emitOptions = new EmitOptions(this.compiler, this.resolvePath); + } + + if (this._emitOptions.diagnostic()) { + if (!this.continueOnDiagnostics) { + this.hadEmitDiagnostics = true; + } + + this._current = CompileResult.fromDiagnostics([this._emitOptions.diagnostic()]); + } + + return true; + } + + private moveNextEmitPhase(): boolean { + Debug.assert(!this.hadSyntacticDiagnostics); + Debug.assert(this._emitOptions); + + if (this.hadEmitDiagnostics) { + return false; + } + + Debug.assert(this.index >= 0 && this.index <= this.fileNames.length); + if (this.index < this.fileNames.length) { + var fileName = this.fileNames[this.index]; + var document = this.compiler.getDocument(fileName); + + // Try to emit this single document. It will either get emitted to its own file + // (in which case we'll have our call back triggered), or it will get added to the + // shared emitter (and we'll take care of it after all the files are done. + this._sharedEmitter = this.compiler._emitDocument( + document, this._emitOptions, + outputFiles => { this._current = CompileResult.fromOutputFiles(outputFiles) }, + this._sharedEmitter); + return true; + } + + // If we've moved past all the files, and we have a multi-input->single-output + // emitter set up. Then add the outputs of that emitter to the results. + if (this.index === this.fileNames.length && this._sharedEmitter) { + // Collect shared emit result. + this._current = CompileResult.fromOutputFiles(this._sharedEmitter.getOutputFiles()); + } + + return true; + } + + private moveNextDeclarationEmitPhase(): boolean { + Debug.assert(!this.hadSyntacticDiagnostics); + Debug.assert(!this.hadEmitDiagnostics); + if (this.hadSemanticDiagnostics) { + return false; + } + + if (!this.compiler.compilationSettings().generateDeclarationFiles()) { + return false; + } + + Debug.assert(this.index >= 0 && this.index <= this.fileNames.length); + if (this.index < this.fileNames.length) { + var fileName = this.fileNames[this.index]; + var document = this.compiler.getDocument(fileName); + + this._sharedDeclarationEmitter = this.compiler._emitDocumentDeclarations( + document, this._emitOptions, + file => { this._current = CompileResult.fromOutputFiles([file]); }, + this._sharedDeclarationEmitter); + return true; + } + + // If we've moved past all the files, and we have a multi-input->single-output + // emitter set up. Then add the outputs of that emitter to the results. + if (this.index === this.fileNames.length && this._sharedDeclarationEmitter) { + this._current = CompileResult.fromOutputFiles([this._sharedDeclarationEmitter.getOutputFile()]); + } + + return true; + } + } + + export function compareDataObjects(dst: any, src: any): boolean { + for (var e in dst) { + if (typeof dst[e] === "object") { + if (!compareDataObjects(dst[e], src[e])) + return false; + } + else if (typeof dst[e] !== "function") { + if (dst[e] !== src[e]) + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/services/compiler/walkContext.ts b/src/services/compiler/walkContext.ts new file mode 100644 index 00000000000..0d08f9af8b2 --- /dev/null +++ b/src/services/compiler/walkContext.ts @@ -0,0 +1,31 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module Tools { + export interface IWalkContext { + goChildren: boolean; + goNextSibling: boolean; + // visit siblings in reverse execution order + reverseSiblings: boolean; + } + + export class BaseWalkContext implements IWalkContext { + public goChildren = true; + public goNextSibling = true; + public reverseSiblings = false; + } +} \ No newline at end of file diff --git a/src/services/core/arrayUtilities.ts b/src/services/core/arrayUtilities.ts new file mode 100644 index 00000000000..4bfec137664 --- /dev/null +++ b/src/services/core/arrayUtilities.ts @@ -0,0 +1,205 @@ +/// + +module TypeScript { + export class ArrayUtilities { + public static sequenceEquals(array1: T[], array2: T[], equals: (v1: T, v2: T) => boolean) { + if (array1 === array2) { + return true; + } + + if (array1 === null || array2 === null) { + return false; + } + + if (array1.length !== array2.length) { + return false; + } + + for (var i = 0, n = array1.length; i < n; i++) { + if (!equals(array1[i], array2[i])) { + return false; + } + } + + return true; + } + + public static contains(array: T[], value: T): boolean { + for (var i = 0; i < array.length; i++) { + if (array[i] === value) { + return true; + } + } + + return false; + } + + // Gets unique element array + public static distinct(array: T[], equalsFn?: (a: T, b: T) => boolean): T[] { + var result: T[] = []; + + // TODO: use map when available + for (var i = 0, n = array.length; i < n; i++) { + var current = array[i]; + for (var j = 0; j < result.length; j++) { + if (equalsFn(result[j], current)) { + break; + } + } + + if (j === result.length) { + result.push(current); + } + } + + return result; + } + + public static last(array: T[]): T { + if (array.length === 0) { + throw Errors.argumentOutOfRange('array'); + } + + return array[array.length - 1]; + } + + public static lastOrDefault(array: T[], predicate: (v: T, index: number) => boolean): T { + for (var i = array.length - 1; i >= 0; i--) { + var v = array[i]; + if (predicate(v, i)) { + return v; + } + } + + return null; + } + + public static firstOrDefault(array: T[], func: (v: T, index: number) => boolean): T { + for (var i = 0, n = array.length; i < n; i++) { + var value = array[i]; + if (func(value, i)) { + return value; + } + } + + return null; + } + + public static first(array: T[], func?: (v: T, index: number) => boolean): T { + for (var i = 0, n = array.length; i < n; i++) { + var value = array[i]; + if (!func || func(value, i)) { + return value; + } + } + + throw Errors.invalidOperation(); + } + + public static sum(array: T[], func: (v: T) => number): number { + var result = 0; + + for (var i = 0, n = array.length; i < n; i++) { + result += func(array[i]); + } + + return result; + } + + public static select(values: T[], func: (v: T) => S): S[] { + var result: S[] = new Array(values.length); + + for (var i = 0; i < values.length; i++) { + result[i] = func(values[i]); + } + + return result; + } + + public static where(values: T[], func: (v: T) => boolean): T[] { + var result = new Array(); + + for (var i = 0; i < values.length; i++) { + if (func(values[i])) { + result.push(values[i]); + } + } + + return result; + } + + public static any(array: T[], func: (v: T) => boolean): boolean { + for (var i = 0, n = array.length; i < n; i++) { + if (func(array[i])) { + return true; + } + } + + return false; + } + + public static all(array: T[], func: (v: T) => boolean): boolean { + for (var i = 0, n = array.length; i < n; i++) { + if (!func(array[i])) { + return false; + } + } + + return true; + } + + public static binarySearch(array: number[], value: number): number { + var low = 0; + var high = array.length - 1; + + while (low <= high) { + var middle = low + ((high - low) >> 1); + var midValue = array[middle]; + + if (midValue === value) { + return middle; + } + else if (midValue > value) { + high = middle - 1; + } + else { + low = middle + 1; + } + } + + return ~low; + } + + public static createArray(length: number, defaultValue: any): T[] { + var result = new Array(length); + for (var i = 0; i < length; i++) { + result[i] = defaultValue; + } + + return result; + } + + public static grow(array: T[], length: number, defaultValue: T): void { + var count = length - array.length; + for (var i = 0; i < count; i++) { + array.push(defaultValue); + } + } + + public static copy(sourceArray: T[], sourceIndex: number, destinationArray: T[], destinationIndex: number, length: number): void { + for (var i = 0; i < length; i++) { + destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i]; + } + } + + public static indexOf(array: T[], predicate: (v: T) => boolean): number { + for (var i = 0, n = array.length; i < n; i++) { + if (predicate(array[i])) { + return i; + } + } + + return -1; + } + } +} \ No newline at end of file diff --git a/src/services/core/debug.ts b/src/services/core/debug.ts new file mode 100644 index 00000000000..e57f9745353 --- /dev/null +++ b/src/services/core/debug.ts @@ -0,0 +1,32 @@ +/// + +module TypeScript { + export enum AssertionLevel { + None = 0, + Normal = 1, + Aggressive = 2, + VeryAggressive = 3, + } + + export class Debug { + private static currentAssertionLevel = AssertionLevel.None; + public static shouldAssert(level: AssertionLevel): boolean { + return this.currentAssertionLevel >= level; + } + + public static assert(expression: any, message: string = "", verboseDebugInfo: () => string = null): void { + if (!expression) { + var verboseDebugString = ""; + if (verboseDebugInfo) { + verboseDebugString = "\r\nVerbose Debug Information:" + verboseDebugInfo(); + } + + throw new Error("Debug Failure. False expression: " + message + verboseDebugString); + } + } + + public static fail(message?: string): void { + Debug.assert(false, message); + } + } +} \ No newline at end of file diff --git a/src/services/core/diagnosticCategory.ts b/src/services/core/diagnosticCategory.ts new file mode 100644 index 00000000000..30c6513ee38 --- /dev/null +++ b/src/services/core/diagnosticCategory.ts @@ -0,0 +1,10 @@ +/// + +module TypeScript { + export enum DiagnosticCategory { + Warning, + Error, + Message, + NoPrefix, + } +} \ No newline at end of file diff --git a/src/services/core/diagnosticCore.ts b/src/services/core/diagnosticCore.ts new file mode 100644 index 00000000000..0c12a354933 --- /dev/null +++ b/src/services/core/diagnosticCore.ts @@ -0,0 +1,197 @@ +/// + +module TypeScript { + export var LocalizedDiagnosticMessages: ts.Map = null; + + export class Location { + private _fileName: string; + private _lineMap: LineMap; + private _start: number; + private _length: number; + + constructor(fileName: string, lineMap: LineMap, start: number, length: number) { + this._fileName = fileName; + this._lineMap = lineMap; + this._start = start; + this._length = length; + } + + public fileName(): string { + return this._fileName; + } + + public lineMap(): LineMap { + return this._lineMap; + } + + public line(): number { + return this._lineMap ? this._lineMap.getLineNumberFromPosition(this.start()) : 0; + } + + public character(): number { + return this._lineMap ? this._lineMap.getLineAndCharacterFromPosition(this.start()).character() : 0; + } + + public start(): number { + return this._start; + } + + public length(): number { + return this._length; + } + + public static equals(location1: Location, location2: Location): boolean { + return location1._fileName === location2._fileName && + location1._start === location2._start && + location1._length === location2._length; + } + } + + export class Diagnostic extends Location { + private _diagnosticKey: string; + private _arguments: any[]; + private _additionalLocations: Location[]; + + constructor(fileName: string, lineMap: LineMap, start: number, length: number, diagnosticKey: string, _arguments: any[]= null, additionalLocations: Location[] = null) { + super(fileName, lineMap, start, length); + this._diagnosticKey = diagnosticKey; + this._arguments = (_arguments && _arguments.length > 0) ? _arguments : null; + this._additionalLocations = (additionalLocations && additionalLocations.length > 0) ? additionalLocations : null; + } + + public toJSON(key: any): any { + var result: any = {}; + result.start = this.start(); + result.length = this.length(); + + result.diagnosticCode = this._diagnosticKey; + + var _arguments: any[] = (this).arguments(); + if (_arguments && _arguments.length > 0) { + result.arguments = _arguments; + } + + return result; + } + + public diagnosticKey(): string { + return this._diagnosticKey; + } + + public arguments(): any[] { + return this._arguments; + } + + /** + * Get the text of the message in the given language. + */ + public text(): string { + return TypeScript.getLocalizedText(this._diagnosticKey, this._arguments); + } + + /** + * Get the text of the message including the error code in the given language. + */ + public message(): string { + return TypeScript.getDiagnosticMessage(this._diagnosticKey, this._arguments); + } + + /** + * If a derived class has additional information about other referenced symbols, it can + * expose the locations of those symbols in a general way, so they can be reported along + * with the error. + */ + public additionalLocations(): Location[] { + return this._additionalLocations || []; + } + + public static equals(diagnostic1: Diagnostic, diagnostic2: Diagnostic): boolean { + return Location.equals(diagnostic1, diagnostic2) && + diagnostic1._diagnosticKey === diagnostic2._diagnosticKey && + ArrayUtilities.sequenceEquals(diagnostic1._arguments, diagnostic2._arguments, (v1, v2) => v1 === v2); + } + + public info(): DiagnosticInfo { + return getDiagnosticInfoFromKey(this.diagnosticKey()); + } + } + + export function newLine(): string { + // TODO: We need to expose an extensibility point on our hosts to have them tell us what + // they want the newline string to be. That way we can get the correct result regardless + // of which host we use + return sys.newLine ? sys.newLine : "\r\n"; + } + + function getLargestIndex(diagnostic: string): number { + var largest = -1; + var regex = /\{(\d+)\}/g; + + var match: RegExpExecArray; + while (match = regex.exec(diagnostic)) { + var val = parseInt(match[1]); + if (!isNaN(val) && val > largest) { + largest = val; + } + } + + return largest; + } + + function getDiagnosticInfoFromKey(diagnosticKey: string): DiagnosticInfo { + var result: DiagnosticInfo = diagnosticInformationMap[diagnosticKey]; + Debug.assert(result); + return result; + } + + export function getLocalizedText(diagnosticKey: string, args: any[]): string { + + var diagnosticMessageText: string = diagnosticKey; + Debug.assert(diagnosticMessageText !== undefined && diagnosticMessageText !== null); + + var actualCount = args ? args.length : 0; + // We have a string like "foo_0_bar_1". We want to find the largest integer there. + // (i.e.'1'). We then need one more arg than that to be correct. + var expectedCount = 1 + getLargestIndex(diagnosticKey); + + if (expectedCount !== actualCount) { + throw new Error(getLocalizedText(DiagnosticCode.Expected_0_arguments_to_message_got_1_instead, [expectedCount, actualCount])); + } + + // This should also be the same number of arguments as the message text + var valueCount = 1 + getLargestIndex(diagnosticMessageText); + if (valueCount !== expectedCount) { + throw new Error(getLocalizedText(DiagnosticCode.Expected_the_message_0_to_have_1_arguments_but_it_had_2, [diagnosticMessageText, expectedCount, valueCount])); + } + + diagnosticMessageText = diagnosticMessageText.replace(/{(\d+)}/g, function (match, num?) { + return typeof args[num] !== 'undefined' + ? args[num] + : match; + }); + + diagnosticMessageText = diagnosticMessageText.replace(/{(NL)}/g, function (match) { + return TypeScript.newLine(); + }); + + return diagnosticMessageText; + } + + export function getDiagnosticMessage(diagnosticKey: string, args: any[]): string { + var diagnostic = getDiagnosticInfoFromKey(diagnosticKey); + var diagnosticMessageText = getLocalizedText(diagnosticKey, args); + + var message: string; + if (diagnostic.category === DiagnosticCategory.Error) { + message = getLocalizedText(DiagnosticCode.error_TS_0_1, [diagnostic.code, diagnosticMessageText]); + } + else if (diagnostic.category === DiagnosticCategory.Warning) { + message = getLocalizedText(DiagnosticCode.warning_TS_0_1, [diagnostic.code, diagnosticMessageText]); + } + else { + message = diagnosticMessageText; + } + + return message; + } +} \ No newline at end of file diff --git a/src/services/core/diagnosticInfo.ts b/src/services/core/diagnosticInfo.ts new file mode 100644 index 00000000000..515f3cff309 --- /dev/null +++ b/src/services/core/diagnosticInfo.ts @@ -0,0 +1,9 @@ +/// + +module TypeScript { + export interface DiagnosticInfo { + category: DiagnosticCategory; + message: string; + code: number; + } +} \ No newline at end of file diff --git a/src/services/core/errors.ts b/src/services/core/errors.ts new file mode 100644 index 00000000000..77f0643f333 --- /dev/null +++ b/src/services/core/errors.ts @@ -0,0 +1,29 @@ +/// + +module TypeScript { + export class Errors { + public static argument(argument: string, message?: string): Error { + return new Error("Invalid argument: " + argument + ". " + message); + } + + public static argumentOutOfRange(argument: string): Error { + return new Error("Argument out of range: " + argument); + } + + public static argumentNull(argument: string): Error { + return new Error("Argument null: " + argument); + } + + public static abstract(): Error { + return new Error("Operation not implemented properly by subclass."); + } + + public static notYetImplemented(): Error { + return new Error("Not yet implemented."); + } + + public static invalidOperation(message?: string): Error { + return new Error("Invalid operation: " + message); + } + } +} \ No newline at end of file diff --git a/src/services/core/hash.ts b/src/services/core/hash.ts new file mode 100644 index 00000000000..ef117df8025 --- /dev/null +++ b/src/services/core/hash.ts @@ -0,0 +1,112 @@ +/// + +module TypeScript { + export class Hash { + // This table uses FNV1a as a string hash + private static FNV_BASE = 2166136261; + private static FNV_PRIME = 16777619; + + private static computeFnv1aCharArrayHashCode(text: number[], start: number, len: number): number { + var hashCode = Hash.FNV_BASE; + var end = start + len; + + for (var i = start; i < end; i++) { + hashCode = IntegerUtilities.integerMultiplyLow32Bits(hashCode ^ text[i], Hash.FNV_PRIME); + } + + return hashCode; + } + + public static computeSimple31BitCharArrayHashCode(key: number[], start: number, len: number): number { + // Start with an int. + var hash = 0; + + for (var i = 0; i < len; i++) { + var ch = key[start + i]; + + // Left shift keeps things as a 32bit int. And we're only doing two adds. Chakra and + // V8 recognize this as not needing to go past the 53 bits needed for the float + // mantissa. Or'ing with 0 keeps this 32 bits. + hash = ((((hash << 5) - hash) | 0) + ch) | 0; + } + + // Ensure we fit in 31 bits. That way if/when this gets stored, it won't require any heap + // allocation. + return hash & 0x7FFFFFFF; + } + + public static computeSimple31BitStringHashCode(key: string): number { + // Start with an int. + var hash = 0; + + var start = 0; + var len = key.length; + + for (var i = 0; i < len; i++) { + var ch = key.charCodeAt(start + i); + + // Left shift keeps things as a 32bit int. And we're only doing two adds. Chakra and + // V8 recognize this as not needing to go past the 53 bits needed for the float + // mantissa. Or'ing with 0 keeps this 32 bits. + hash = ((((hash << 5) - hash) | 0) + ch) | 0; + } + + // Ensure we fit in 31 bits. That way if/when this gets stored, it won't require any heap + // allocation. + return hash & 0x7FFFFFFF; + } + + public static computeMurmur2StringHashCode(key: string, seed: number): number { + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + var m: number = 0x5bd1e995; + var r: number = 24; + + // Initialize the hash to a 'random' value + + var numberOfCharsLeft = key.length; + var h = Math.abs(seed ^ numberOfCharsLeft); + + // Mix 4 bytes at a time into the hash. NOTE: 4 bytes is two chars, so we iterate + // through the string two chars at a time. + var index = 0; + while (numberOfCharsLeft >= 2) { + var c1 = key.charCodeAt(index); + var c2 = key.charCodeAt(index + 1); + + var k = Math.abs(c1 | (c2 << 16)); + + k = IntegerUtilities.integerMultiplyLow32Bits(k, m); + k ^= k >> r; + k = IntegerUtilities.integerMultiplyLow32Bits(k, m); + + h = IntegerUtilities.integerMultiplyLow32Bits(h, m); + h ^= k; + + index += 2; + numberOfCharsLeft -= 2; + } + + // Handle the last char (or 2 bytes) if they exist. This happens if the original string had + // odd length. + if (numberOfCharsLeft === 1) { + h ^= key.charCodeAt(index); + h = IntegerUtilities.integerMultiplyLow32Bits(h, m); + } + + // Do a few final mixes of the hash to ensure the last few bytes are well-incorporated. + + h ^= h >> 13; + h = IntegerUtilities.integerMultiplyLow32Bits(h, m); + h ^= h >> 15; + + return h; + } + + public static combine(value: number, currentHash: number): number { + // Ensure we stay within 31 bits. + return (((currentHash << 5) + currentHash) + value) & 0x7FFFFFFF; + } + } +} \ No newline at end of file diff --git a/src/services/core/integerUtilities.ts b/src/services/core/integerUtilities.ts new file mode 100644 index 00000000000..0af38471dfd --- /dev/null +++ b/src/services/core/integerUtilities.ts @@ -0,0 +1,28 @@ +/// + +module TypeScript { + export module IntegerUtilities { + export function integerDivide(numerator: number, denominator: number): number { + return (numerator / denominator) >> 0; + } + + export function integerMultiplyLow32Bits(n1: number, n2: number): number { + var n1Low16 = n1 & 0x0000ffff; + var n1High16 = n1 >>> 16; + + var n2Low16 = n2 & 0x0000ffff; + var n2High16 = n2 >>> 16; + + var resultLow32 = (((n1 & 0xffff0000) * n2) >>> 0) + (((n1 & 0x0000ffff) * n2) >>> 0) >>> 0; + return resultLow32; + } + + export function isInteger(text: string): boolean { + return /^[0-9]+$/.test(text); + } + + export function isHexInteger(text: string): boolean { + return /^0(x|X)[0-9a-fA-F]+$/.test(text); + } + } +} \ No newline at end of file diff --git a/src/services/core/lineMap.ts b/src/services/core/lineMap.ts new file mode 100644 index 00000000000..8820f55b920 --- /dev/null +++ b/src/services/core/lineMap.ts @@ -0,0 +1,82 @@ +/// + +module TypeScript { + export class LineMap { + public static empty = new LineMap(() => [0], 0); + private _lineStarts: number[] = null; + + constructor(private _computeLineStarts: () => number[], private length: number) { + } + + public toJSON(key: any) { + return { lineStarts: this.lineStarts(), length: this.length }; + } + + public equals(other: LineMap): boolean { + return this.length === other.length && + ArrayUtilities.sequenceEquals(this.lineStarts(), other.lineStarts(), (v1, v2) => v1 === v2); + } + + public lineStarts(): number[] { + if (this._lineStarts === null) { + this._lineStarts = this._computeLineStarts(); + } + + return this._lineStarts; + } + + public lineCount(): number { + return this.lineStarts().length; + } + + public getPosition(line: number, character: number): number { + return this.lineStarts()[line] + character; + } + + public getLineNumberFromPosition(position: number): number { + if (position < 0 || position > this.length) { + throw Errors.argumentOutOfRange("position"); + } + + if (position === this.length) { + // this can happen when the user tried to get the line of items + // that are at the absolute end of this text (i.e. the EndOfLine + // token, or missing tokens that are at the end of the text). + // In this case, we want the last line in the text. + return this.lineCount() - 1; + } + + // Binary search to find the right line + var lineNumber = ArrayUtilities.binarySearch(this.lineStarts(), position); + if (lineNumber < 0) { + lineNumber = (~lineNumber) - 1; + } + + return lineNumber; + } + + public getLineStartPosition(lineNumber: number): number { + return this.lineStarts()[lineNumber]; + } + + public fillLineAndCharacterFromPosition(position: number, lineAndCharacter: ILineAndCharacter): void { + if (position < 0 || position > this.length) { + throw Errors.argumentOutOfRange("position"); + } + + var lineNumber = this.getLineNumberFromPosition(position); + lineAndCharacter.line = lineNumber; + lineAndCharacter.character = position - this.lineStarts()[lineNumber]; + } + + public getLineAndCharacterFromPosition(position: number): LineAndCharacter { + if (position < 0 || position > this.length) { + throw Errors.argumentOutOfRange("position"); + } + + var lineNumber = this.getLineNumberFromPosition(position); + + return new LineAndCharacter(lineNumber, position - this.lineStarts()[lineNumber]); + } + } +} \ No newline at end of file diff --git a/src/services/core/linePosition.ts b/src/services/core/linePosition.ts new file mode 100644 index 00000000000..119c367ee3e --- /dev/null +++ b/src/services/core/linePosition.ts @@ -0,0 +1,35 @@ +/// + +module TypeScript { + export class LineAndCharacter { + private _line: number = 0; + private _character: number = 0; + + /** + * Initializes a new instance of a LinePosition with the given line and character. ArgumentOutOfRangeException if "line" or "character" is less than zero. + * @param line The line of the line position. The first line in a file is defined as line 0 (zero based line numbering). + * @param character The character position in the line. + */ + + constructor(line: number, character: number) { + if (line < 0) { + throw Errors.argumentOutOfRange("line"); + } + + if (character < 0) { + throw Errors.argumentOutOfRange("character"); + } + + this._line = line; + this._character = character; + } + + public line(): number { + return this._line; + } + + public character(): number { + return this._character; + } + } +} \ No newline at end of file diff --git a/src/services/core/mathPrototype.ts b/src/services/core/mathPrototype.ts new file mode 100644 index 00000000000..fe4c23f9d91 --- /dev/null +++ b/src/services/core/mathPrototype.ts @@ -0,0 +1,13 @@ +/// + +module TypeScript { + export class MathPrototype { + public static max(a: number, b: number): number { + return a >= b ? a : b; + } + + public static min(a: number, b: number): number { + return a <= b ? a : b; + } + } +} \ No newline at end of file diff --git a/src/services/core/references.ts b/src/services/core/references.ts new file mode 100644 index 00000000000..fcfd10126ec --- /dev/null +++ b/src/services/core/references.ts @@ -0,0 +1,13 @@ +/// + +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// \ No newline at end of file diff --git a/src/services/core/stringTable.ts b/src/services/core/stringTable.ts new file mode 100644 index 00000000000..18af8be8b5c --- /dev/null +++ b/src/services/core/stringTable.ts @@ -0,0 +1,159 @@ +/// + +module TypeScript.Collections { + export var DefaultStringTableCapacity = 256; + + class StringTableEntry { + constructor(public Text: string, + public HashCode: number, + public Next: StringTableEntry) { + } + } + + // A table of interned strings. Faster and better than an arbitrary hashtable for the needs of the + // scanner. Specifically, the scanner operates over a sliding window of characters, with a start + // and end pointer for the current lexeme. The scanner then wants to get the *interned* string + // represented by that subsection. + // + // Importantly, if the string is already interned, then it wants ask "is the string represented by + // this section of a char array contained within the table" in a non-allocating fashion. i.e. if + // you have "[' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ']" and you ask to get the string represented by + // range [1, 7), then this table will return "public" without any allocations if that value was + // already in the table. + // + // Of course, if the value is not in the table then there will be an initial cost to allocate the + // string and the bucket for the table. However, that is only incurred the first time each unique + // string is added. + export class StringTable { + // TODO: uncomment this once typecheck bug is fixed. + private entries: StringTableEntry[]; + private count: number = 0; + + constructor(capacity: number) { + var size = Hash.getPrime(capacity); + this.entries = ArrayUtilities.createArray(size, null); + } + + public addCharArray(key: number[], start: number, len: number): string { + // Compute the hash for this key. Also ensure that it fits within 31 bits (so that it + // stays a non-heap integer, and so we can index into the array safely). + var hashCode = Hash.computeSimple31BitCharArrayHashCode(key, start, len) & 0x7FFFFFFF; + // Debug.assert(hashCode > 0); + + // First see if we already have the string represented by "key[start, start + len)" already + // present in this table. If we do, just return that string. Do this without any + // allocations + var entry = this.findCharArrayEntry(key, start, len, hashCode); + if (entry !== null) { + return entry.Text; + } + + // We don't have an entry for that string in our table. Convert that + var slice: number[] = key.slice(start, start + len); + return this.addEntry(StringUtilities.fromCharCodeArray(slice), hashCode); + } + + private findCharArrayEntry(key: number[], start: number, len: number, hashCode: number) { + for (var e = this.entries[hashCode % this.entries.length]; e !== null; e = e.Next) { + if (e.HashCode === hashCode && StringTable.textCharArrayEquals(e.Text, key, start, len)) { + return e; + } + } + + return null; + } + + private addEntry(text: string, hashCode: number): string { + var index = hashCode % this.entries.length; + + var e = new StringTableEntry(text, hashCode, this.entries[index]); + + this.entries[index] = e; + + // We grow when our load factor equals 1. I tried different load factors (like .75 and + // .5), however they seemed to have no effect on running time. With a load factor of 1 + // we seem to get about 80% slot fill rate with an average of around 1.25 table entries + // per slot. + if (this.count === this.entries.length) { + this.grow(); + } + + this.count++; + return e.Text; + } + + //private dumpStats() { + // var standardOut = Environment.standardOut; + + // standardOut.WriteLine("----------------------") + // standardOut.WriteLine("String table stats"); + // standardOut.WriteLine("Count : " + this.count); + // standardOut.WriteLine("Entries Length : " + this.entries.length); + + // var longestSlot = 0; + // var occupiedSlots = 0; + // for (var i = 0; i < this.entries.length; i++) { + // if (this.entries[i] !== null) { + // occupiedSlots++; + + // var current = this.entries[i]; + // var slotCount = 0; + // while (current !== null) { + // slotCount++; + // current = current.Next; + // } + + // longestSlot = MathPrototype.max(longestSlot, slotCount); + // } + // } + + // standardOut.WriteLine("Occupied slots : " + occupiedSlots); + // standardOut.WriteLine("Longest slot : " + longestSlot); + // standardOut.WriteLine("Avg Length/Slot : " + (this.count / occupiedSlots)); + // standardOut.WriteLine("----------------------"); + //} + + private grow(): void { + // this.dumpStats(); + + var newSize = Hash.expandPrime(this.entries.length); + + var oldEntries = this.entries; + var newEntries: StringTableEntry[] = ArrayUtilities.createArray(newSize, null); + + this.entries = newEntries; + + for (var i = 0; i < oldEntries.length; i++) { + var e = oldEntries[i]; + while (e !== null) { + var newIndex = e.HashCode % newSize; + var tmp = e.Next; + e.Next = newEntries[newIndex]; + newEntries[newIndex] = e; + e = tmp; + } + } + + // this.dumpStats(); + } + + private static textCharArrayEquals(text: string, array: number[], start: number, length: number): boolean { + if (text.length !== length) { + return false; + } + + var s = start; + for (var i = 0; i < length; i++) { + if (text.charCodeAt(i) !== array[s]) { + return false; + } + + s++; + } + + return true; + } + } + + export var DefaultStringTable = new StringTable(DefaultStringTableCapacity); +} \ No newline at end of file diff --git a/src/services/core/stringUtilities.ts b/src/services/core/stringUtilities.ts new file mode 100644 index 00000000000..0ed2b4a7e8f --- /dev/null +++ b/src/services/core/stringUtilities.ts @@ -0,0 +1,21 @@ +/// + +module TypeScript { + export class StringUtilities { + public static isString(value: any): boolean { + return Object.prototype.toString.apply(value, []) === '[object String]'; + } + + public static endsWith(string: string, value: string): boolean { + return string.substring(string.length - value.length, string.length) === value; + } + + public static startsWith(string: string, value: string): boolean { + return string.substr(0, value.length) === value; + } + + public static repeat(value: string, count: number) { + return Array(count + 1).join(value); + } + } +} \ No newline at end of file diff --git a/src/services/core/timer.ts b/src/services/core/timer.ts new file mode 100644 index 00000000000..7d6e3b0784e --- /dev/null +++ b/src/services/core/timer.ts @@ -0,0 +1,52 @@ +/// + +var global: any = Function("return this").call(null); + +module TypeScript { + module Clock { + export var now: () => number; + export var resolution: number; + + declare module WScript { + export function InitializeProjection(): void; + } + + declare module TestUtilities { + export function QueryPerformanceCounter(): number; + export function QueryPerformanceFrequency(): number; + } + + if (typeof WScript !== "undefined" && typeof global['WScript'].InitializeProjection !== "undefined") { + // Running in JSHost. + global['WScript'].InitializeProjection(); + + now = function () { + return TestUtilities.QueryPerformanceCounter(); + }; + + resolution = TestUtilities.QueryPerformanceFrequency(); + } + else { + now = function () { + return Date.now(); + }; + + resolution = 1000; + } + } + + export class Timer { + public startTime: number; + public time = 0; + + public start() { + this.time = 0; + this.startTime = Clock.now(); + } + + public end() { + // Set time to MS. + this.time = (Clock.now() - this.startTime); + } + } +} \ No newline at end of file diff --git a/src/services/findReferenceHelpers.ts b/src/services/findReferenceHelpers.ts new file mode 100644 index 00000000000..2e47a90ba12 --- /dev/null +++ b/src/services/findReferenceHelpers.ts @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. +// See LICENSE.txt in the project root for complete license information. + +/// + +module TypeScript.Services { + export class FindReferenceHelpers { + public static compareSymbolsForLexicalIdentity(firstSymbol: TypeScript.PullSymbol, secondSymbol: TypeScript.PullSymbol): boolean { + // Unwrap modules so that we're always referring to the variable. + if (!firstSymbol.isAlias() && firstSymbol.isContainer()) { + var containerForFirstSymbol = (firstSymbol); + if (containerForFirstSymbol.getInstanceSymbol()) { + firstSymbol = containerForFirstSymbol.getInstanceSymbol(); + } + } + + if (!secondSymbol.isAlias() && secondSymbol.isContainer()) { + var containerForSecondSymbol = (secondSymbol); + if (containerForSecondSymbol.getInstanceSymbol()) { + secondSymbol = containerForSecondSymbol.getInstanceSymbol(); + } + } + + if (firstSymbol.kind === secondSymbol.kind) { + if (firstSymbol === secondSymbol) { + return true; + } + + // If we have two variables and they have the same name and the same parent, then + // they are the same symbol. + if (firstSymbol.kind === TypeScript.PullElementKind.Variable && + firstSymbol.name === secondSymbol.name && + firstSymbol.getDeclarations() && firstSymbol.getDeclarations().length >= 1 && + secondSymbol.getDeclarations() && secondSymbol.getDeclarations().length >= 1) { + + var firstSymbolDecl = firstSymbol.getDeclarations()[0]; + var secondSymbolDecl = secondSymbol.getDeclarations()[0]; + + return firstSymbolDecl.getParentDecl() === secondSymbolDecl.getParentDecl(); + } + + // If we have two properties that belong to an object literal, then we need ot see + // if they came from teh same object literal ast. + if (firstSymbol.kind === TypeScript.PullElementKind.Property && + firstSymbol.name === secondSymbol.name && + firstSymbol.getDeclarations() && firstSymbol.getDeclarations().length >= 1 && + secondSymbol.getDeclarations() && secondSymbol.getDeclarations().length >= 1) { + + var firstSymbolDecl = firstSymbol.getDeclarations()[0]; + var secondSymbolDecl = secondSymbol.getDeclarations()[0]; + + var firstParentDecl = firstSymbolDecl.getParentDecl(); + var secondParentDecl = secondSymbolDecl.getParentDecl() + + if (firstParentDecl.kind === TypeScript.PullElementKind.ObjectLiteral && + secondParentDecl.kind === TypeScript.PullElementKind.ObjectLiteral) { + + return firstParentDecl.ast() === secondParentDecl.ast(); + } + } + + // check if we are dealing with the implementation of interface method or a method override + if (firstSymbol.name === secondSymbol.name) { + // at this point firstSymbol.kind === secondSymbol.kind so we can pick any of those + switch (firstSymbol.kind) { + case PullElementKind.Property: + case PullElementKind.Method: + case PullElementKind.GetAccessor: + case PullElementKind.SetAccessor: + // these kinds can only be defined in types + var t1 = firstSymbol.getContainer(); + var t2 = secondSymbol.getContainer(); + t1._resolveDeclaredSymbol(); + t2._resolveDeclaredSymbol(); + + return t1.hasBase(t2) || t2.hasBase(t1); + break; + } + } + + return false; + } + else { + switch (firstSymbol.kind) { + case TypeScript.PullElementKind.Class: { + return this.checkSymbolsForDeclarationEquality(firstSymbol, secondSymbol); + } + case TypeScript.PullElementKind.Property: { + if (firstSymbol.isAccessor()) { + var getterSymbol = (firstSymbol).getGetter(); + var setterSymbol = (firstSymbol).getSetter(); + + if (getterSymbol && getterSymbol === secondSymbol) { + return true; + } + + if (setterSymbol && setterSymbol === secondSymbol) { + return true; + } + } + return false; + } + case TypeScript.PullElementKind.Function: { + if (secondSymbol.isAccessor()) { + var getterSymbol = (secondSymbol).getGetter(); + var setterSymbol = (secondSymbol).getSetter(); + + if (getterSymbol && getterSymbol === firstSymbol) { + return true; + } + + if (setterSymbol && setterSymbol === firstSymbol) { + return true; + } + } + return false; + } + case TypeScript.PullElementKind.ConstructorMethod: { + return this.checkSymbolsForDeclarationEquality(firstSymbol, secondSymbol); + } + } + } + + return firstSymbol === secondSymbol; + } + + private static checkSymbolsForDeclarationEquality(firstSymbol: TypeScript.PullSymbol, secondSymbol: TypeScript.PullSymbol): boolean { + var firstSymbolDeclarations: TypeScript.PullDecl[] = firstSymbol.getDeclarations(); + var secondSymbolDeclarations: TypeScript.PullDecl[] = secondSymbol.getDeclarations(); + for (var i = 0, iLen = firstSymbolDeclarations.length; i < iLen; i++) { + for (var j = 0, jLen = secondSymbolDeclarations.length; j < jLen; j++) { + if (this.declarationsAreSameOrParents(firstSymbolDeclarations[i], secondSymbolDeclarations[j])) { + return true; + } + } + } + return false; + } + + private static declarationsAreSameOrParents(firstDecl: TypeScript.PullDecl, secondDecl: TypeScript.PullDecl): boolean { + var firstParent: TypeScript.PullDecl = firstDecl.getParentDecl(); + var secondParent: TypeScript.PullDecl = secondDecl.getParentDecl(); + if (firstDecl === secondDecl || + firstDecl === secondParent || + firstParent === secondDecl || + firstParent === secondParent) { + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/formatter.ts b/src/services/formatting/formatter.ts new file mode 100644 index 00000000000..e4a1229abef --- /dev/null +++ b/src/services/formatting/formatter.ts @@ -0,0 +1,320 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class Formatter extends MultipleTokenIndenter { + private previousTokenSpan: TokenSpan = null; + private previousTokenParent: IndentationNodeContext = null; + + // TODO: implement it with skipped tokens in Fidelity + private scriptHasErrors: boolean = false; + + private rulesProvider: RulesProvider; + private formattingRequestKind: FormattingRequestKind; + private formattingContext: FormattingContext; + + constructor(textSpan: TextSpan, + sourceUnit: SourceUnitSyntax, + indentFirstToken: boolean, + options: FormattingOptions, + snapshot: ITextSnapshot, + rulesProvider: RulesProvider, + formattingRequestKind: FormattingRequestKind) { + + super(textSpan, sourceUnit, snapshot, indentFirstToken, options); + + this.previousTokenParent = this.parent().clone(this.indentationNodeContextPool()); + + this.rulesProvider = rulesProvider; + this.formattingRequestKind = formattingRequestKind; + this.formattingContext = new FormattingContext(this.snapshot(), this.formattingRequestKind); + } + + public static getEdits(textSpan: TextSpan, + sourceUnit: SourceUnitSyntax, + options: FormattingOptions, + indentFirstToken: boolean, + snapshot: ITextSnapshot, + rulesProvider: RulesProvider, + formattingRequestKind: FormattingRequestKind): TextEditInfo[] { + var walker = new Formatter(textSpan, sourceUnit, indentFirstToken, options, snapshot, rulesProvider, formattingRequestKind); + visitNodeOrToken(walker, sourceUnit); + return walker.edits(); + } + + public visitTokenInSpan(token: ISyntaxToken): void { + if (token.fullWidth() !== 0) { + var tokenSpan = new TextSpan(this.position() + token.leadingTriviaWidth(), width(token)); + if (this.textSpan().containsTextSpan(tokenSpan)) { + this.processToken(token); + } + } + + // Call the base class to process the token and indent it if needed + super.visitTokenInSpan(token); + } + + private processToken(token: ISyntaxToken): void { + var position = this.position(); + + // Extract any leading comments + if (token.leadingTriviaWidth() !== 0) { + this.processTrivia(token.leadingTrivia(), position); + position += token.leadingTriviaWidth(); + } + + // Push the token + var currentTokenSpan = new TokenSpan(token.kind(), position, width(token)); + if (!this.parent().hasSkippedOrMissingTokenChild()) { + if (this.previousTokenSpan) { + // Note that formatPair calls TrimWhitespaceInLineRange in between the 2 tokens + this.formatPair(this.previousTokenSpan, this.previousTokenParent, currentTokenSpan, this.parent()); + } + else { + // We still want to trim whitespace even if it is the first trivia of the first token. Trim from the beginning of the span to the trivia + this.trimWhitespaceInLineRange(this.getLineNumber(this.textSpan()), this.getLineNumber(currentTokenSpan)); + } + } + this.previousTokenSpan = currentTokenSpan; + if (this.previousTokenParent) { + // Make sure to clear the previous parent before assigning a new value to it + this.indentationNodeContextPool().releaseNode(this.previousTokenParent, /* recursive */true); + } + this.previousTokenParent = this.parent().clone(this.indentationNodeContextPool()); + position += width(token); + + // Extract any trailing comments + if (token.trailingTriviaWidth() !== 0) { + this.processTrivia(token.trailingTrivia(), position); + } + } + + private processTrivia(triviaList: ISyntaxTriviaList, fullStart: number) { + var position = fullStart; + + for (var i = 0, n = triviaList.count(); i < n ; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + // For a comment, format it like it is a token. For skipped text, eat it up as a token, but skip the formatting + if (trivia.isComment() || trivia.isSkippedToken()) { + var currentTokenSpan = new TokenSpan(trivia.kind(), position, trivia.fullWidth()); + if (this.textSpan().containsTextSpan(currentTokenSpan)) { + if (trivia.isComment() && this.previousTokenSpan) { + // Note that formatPair calls TrimWhitespaceInLineRange in between the 2 tokens + this.formatPair(this.previousTokenSpan, this.previousTokenParent, currentTokenSpan, this.parent()); + } + else { + // We still want to trim whitespace even if it is the first trivia of the first token. Trim from the beginning of the span to the trivia + var startLine = this.getLineNumber(this.previousTokenSpan || this.textSpan()); + this.trimWhitespaceInLineRange(startLine, this.getLineNumber(currentTokenSpan)); + } + this.previousTokenSpan = currentTokenSpan; + if (this.previousTokenParent) { + // Make sure to clear the previous parent before assigning a new value to it + this.indentationNodeContextPool().releaseNode(this.previousTokenParent, /* recursive */true); + } + this.previousTokenParent = this.parent().clone(this.indentationNodeContextPool()); + } + } + + position += trivia.fullWidth(); + } + } + + private findCommonParents(parent1: IndentationNodeContext, parent2: IndentationNodeContext): IndentationNodeContext { + // TODO: disable debug assert message + + var shallowParent: IndentationNodeContext; + var shallowParentDepth: number; + var deepParent: IndentationNodeContext; + var deepParentDepth: number; + + if (parent1.depth() < parent2.depth()) { + shallowParent = parent1; + shallowParentDepth = parent1.depth(); + deepParent = parent2; + deepParentDepth = parent2.depth(); + } + else { + shallowParent = parent2; + shallowParentDepth = parent2.depth(); + deepParent = parent1; + deepParentDepth = parent1.depth(); + } + + Debug.assert(shallowParentDepth >= 0, "Expected shallowParentDepth >= 0"); + Debug.assert(deepParentDepth >= 0, "Expected deepParentDepth >= 0"); + Debug.assert(deepParentDepth >= shallowParentDepth, "Expected deepParentDepth >= shallowParentDepth"); + + while (deepParentDepth > shallowParentDepth) { + deepParent = deepParent.parent(); + deepParentDepth--; + } + + Debug.assert(deepParentDepth === shallowParentDepth, "Expected deepParentDepth === shallowParentDepth"); + + while (deepParent.node() && shallowParent.node()) { + if (deepParent.node() === shallowParent.node()) { + return deepParent; + } + deepParent = deepParent.parent(); + shallowParent = shallowParent.parent(); + } + + // The root should be the first element in the parent chain, we can not be here unless something wrong + // happened along the way + throw Errors.invalidOperation(); + } + + private formatPair(t1: TokenSpan, t1Parent: IndentationNodeContext, t2: TokenSpan, t2Parent: IndentationNodeContext): void { + var token1Line = this.getLineNumber(t1); + var token2Line = this.getLineNumber(t2); + + // Find common parent + var commonParent= this.findCommonParents(t1Parent, t2Parent); + + // Update the context + this.formattingContext.updateContext(t1, t1Parent, t2, t2Parent, commonParent); + + // Find rules matching the current context + var rule = this.rulesProvider.getRulesMap().GetRule(this.formattingContext); + + if (rule != null) { + // Record edits from the rule + this.RecordRuleEdits(rule, t1, t2); + + // Handle the case where the next line is moved to be the end of this line. + // In this case we don't indent the next line in the next pass. + if ((rule.Operation.Action == RuleAction.Space || rule.Operation.Action == RuleAction.Delete) && + token1Line != token2Line) { + this.forceSkipIndentingNextToken(t2.start()); + } + + // Handle the case where token2 is moved to the new line. + // In this case we indent token2 in the next pass but we set + // sameLineIndent flag to notify the indenter that the indentation is within the line. + if (rule.Operation.Action == RuleAction.NewLine && token1Line == token2Line) { + this.forceIndentNextToken(t2.start()); + } + } + + // We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line + if (token1Line != token2Line && (!rule || (rule.Operation.Action != RuleAction.Delete && rule.Flag != RuleFlags.CanDeleteNewLines))) { + this.trimWhitespaceInLineRange(token1Line, token2Line, t1); + } + } + + private getLineNumber(span: TextSpan): number { + return this.snapshot().getLineNumberFromPosition(span.start()); + } + + private trimWhitespaceInLineRange(startLine: number, endLine: number, token?: TokenSpan): void { + for (var lineNumber = startLine; lineNumber < endLine; ++lineNumber) { + var line = this.snapshot().getLineFromLineNumber(lineNumber); + + this.trimWhitespace(line, token); + } + } + + private trimWhitespace(line: ITextSnapshotLine, token?: TokenSpan): void { + // Don't remove the trailing spaces inside comments (this includes line comments and block comments) + if (token && (token.kind == SyntaxKind.MultiLineCommentTrivia || token.kind == SyntaxKind.SingleLineCommentTrivia) && token.start() <= line.endPosition() && token.end() >= line.endPosition()) + return; + + var text = line.getText(); + var index = 0; + + for (index = text.length - 1; index >= 0; --index) { + if (!CharacterInfo.isWhitespace(text.charCodeAt(index))) { + break; + } + } + + ++index; + + if (index < text.length) { + this.recordEdit(line.startPosition() + index, line.length() - index, ""); + } + } + + private RecordRuleEdits(rule: Rule, t1: TokenSpan, t2: TokenSpan): void { + if (rule.Operation.Action == RuleAction.Ignore) { + return; + } + + var betweenSpan: TextSpan; + + switch (rule.Operation.Action) { + case RuleAction.Delete: + { + betweenSpan = new TextSpan(t1.end(), t2.start() - t1.end()); + + if (betweenSpan.length() > 0) { + this.recordEdit(betweenSpan.start(), betweenSpan.length(), ""); + return; + } + } + break; + + case RuleAction.NewLine: + { + if (!(rule.Flag == RuleFlags.CanDeleteNewLines || this.getLineNumber(t1) == this.getLineNumber(t2))) { + return; + } + + betweenSpan = new TextSpan(t1.end(), t2.start() - t1.end()); + + var doEdit = false; + var betweenText = this.snapshot().getText(betweenSpan); + + var lineFeedLoc = betweenText.indexOf(this.options.newLineCharacter); + if (lineFeedLoc < 0) { + // no linefeeds, do the edit + doEdit = true; + } + else { + // We only require one line feed. If there is another one, do the edit + lineFeedLoc = betweenText.indexOf(this.options.newLineCharacter, lineFeedLoc + 1); + if (lineFeedLoc >= 0) { + doEdit = true; + } + } + + if (doEdit) { + this.recordEdit(betweenSpan.start(), betweenSpan.length(), this.options.newLineCharacter); + return; + } + } + break; + + case RuleAction.Space: + { + if (!(rule.Flag == RuleFlags.CanDeleteNewLines || this.getLineNumber(t1) == this.getLineNumber(t2))) { + return; + } + + betweenSpan = new TextSpan(t1.end(), t2.start() - t1.end()); + + if (betweenSpan.length() > 1 || this.snapshot().getText(betweenSpan) != " ") { + this.recordEdit(betweenSpan.start(), betweenSpan.length(), " "); + return; + } + } + break; + } + } + } +} \ No newline at end of file diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts new file mode 100644 index 00000000000..c334e1864ab --- /dev/null +++ b/src/services/formatting/formatting.ts @@ -0,0 +1,40 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// \ No newline at end of file diff --git a/src/services/formatting/formattingContext.ts b/src/services/formatting/formattingContext.ts new file mode 100644 index 00000000000..9ced31ecef2 --- /dev/null +++ b/src/services/formatting/formattingContext.ts @@ -0,0 +1,116 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class FormattingContext { + public currentTokenSpan: TokenSpan = null; + public nextTokenSpan: TokenSpan = null; + public contextNode: IndentationNodeContext = null; + public currentTokenParent: IndentationNodeContext = null; + public nextTokenParent: IndentationNodeContext = null; + + private contextNodeAllOnSameLine: boolean = null; + private nextNodeAllOnSameLine: boolean = null; + private tokensAreOnSameLine: boolean = null; + private contextNodeBlockIsOnOneLine: boolean = null; + private nextNodeBlockIsOnOneLine: boolean = null; + + constructor(private snapshot: ITextSnapshot, public formattingRequestKind: FormattingRequestKind) { + Debug.assert(this.snapshot != null, "snapshot is null"); + } + + public updateContext(currentTokenSpan: TokenSpan, currentTokenParent: IndentationNodeContext, nextTokenSpan: TokenSpan, nextTokenParent: IndentationNodeContext, commonParent: IndentationNodeContext) { + Debug.assert(currentTokenSpan != null, "currentTokenSpan is null"); + Debug.assert(currentTokenParent != null, "currentTokenParent is null"); + Debug.assert(nextTokenSpan != null, "nextTokenSpan is null"); + Debug.assert(nextTokenParent != null, "nextTokenParent is null"); + Debug.assert(commonParent != null, "commonParent is null"); + + this.currentTokenSpan = currentTokenSpan; + this.currentTokenParent = currentTokenParent; + this.nextTokenSpan = nextTokenSpan; + this.nextTokenParent = nextTokenParent; + this.contextNode = commonParent; + + this.contextNodeAllOnSameLine = null; + this.nextNodeAllOnSameLine = null; + this.tokensAreOnSameLine = null; + this.contextNodeBlockIsOnOneLine = null; + this.nextNodeBlockIsOnOneLine = null; + } + + public ContextNodeAllOnSameLine(): boolean { + if (this.contextNodeAllOnSameLine === null) { + this.contextNodeAllOnSameLine = this.NodeIsOnOneLine(this.contextNode); + } + + return this.contextNodeAllOnSameLine; + } + + public NextNodeAllOnSameLine(): boolean { + if (this.nextNodeAllOnSameLine === null) { + this.nextNodeAllOnSameLine = this.NodeIsOnOneLine(this.nextTokenParent); + } + + return this.nextNodeAllOnSameLine; + } + + public TokensAreOnSameLine(): boolean { + if (this.tokensAreOnSameLine === null) { + var startLine = this.snapshot.getLineNumberFromPosition(this.currentTokenSpan.start()); + var endLine = this.snapshot.getLineNumberFromPosition(this.nextTokenSpan.start()); + + this.tokensAreOnSameLine = (startLine == endLine); + } + + return this.tokensAreOnSameLine; + } + + public ContextNodeBlockIsOnOneLine() { + if (this.contextNodeBlockIsOnOneLine === null) { + this.contextNodeBlockIsOnOneLine = this.BlockIsOnOneLine(this.contextNode); + } + + return this.contextNodeBlockIsOnOneLine; + } + + public NextNodeBlockIsOnOneLine() { + if (this.nextNodeBlockIsOnOneLine === null) { + this.nextNodeBlockIsOnOneLine = this.BlockIsOnOneLine(this.nextTokenParent); + } + + return this.nextNodeBlockIsOnOneLine; + } + + public NodeIsOnOneLine(node: IndentationNodeContext): boolean { + var startLine = this.snapshot.getLineNumberFromPosition(node.start()); + var endLine = this.snapshot.getLineNumberFromPosition(node.end()); + + return startLine == endLine; + } + + // Now we know we have a block (or a fake block represented by some other kind of node with an open and close brace as children). + // IMPORTANT!!! This relies on the invariant that IsBlockContext must return true ONLY for nodes with open and close braces as immediate children + public BlockIsOnOneLine(node: IndentationNodeContext): boolean { + var block = node.node(); + + // Now check if they are on the same line + return this.snapshot.getLineNumberFromPosition(end(block.openBraceToken)) === + this.snapshot.getLineNumberFromPosition(start(block.closeBraceToken)); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/formattingManager.ts b/src/services/formatting/formattingManager.ts new file mode 100644 index 00000000000..4756b6eb240 --- /dev/null +++ b/src/services/formatting/formattingManager.ts @@ -0,0 +1,128 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class FormattingManager { + private options: FormattingOptions; + + constructor(private syntaxTree: SyntaxTree, private snapshot: ITextSnapshot, private rulesProvider: RulesProvider, editorOptions: ts.EditorOptions) { + // + // TODO: convert to use FormattingOptions instead of EditorOptions + this.options = new FormattingOptions(!editorOptions.ConvertTabsToSpaces, editorOptions.TabSize, editorOptions.IndentSize, editorOptions.NewLineCharacter) + } + + public formatSelection(minChar: number, limChar: number): ts.TextEdit[] { + var span = TextSpan.fromBounds(minChar, limChar); + return this.formatSpan(span, FormattingRequestKind.FormatSelection); + } + + public formatDocument(minChar: number, limChar: number): ts.TextEdit[] { + var span = TextSpan.fromBounds(minChar, limChar); + return this.formatSpan(span, FormattingRequestKind.FormatDocument); + } + + public formatOnPaste(minChar: number, limChar: number): ts.TextEdit[] { + var span = TextSpan.fromBounds(minChar, limChar); + return this.formatSpan(span, FormattingRequestKind.FormatOnPaste); + } + + public formatOnSemicolon(caretPosition: number): ts.TextEdit[] { + var sourceUnit = this.syntaxTree.sourceUnit(); + var semicolonPositionedToken = findToken(sourceUnit, caretPosition - 1); + + if (semicolonPositionedToken.kind() === SyntaxKind.SemicolonToken) { + // Find the outer most parent that this semicolon terminates + var current: ISyntaxElement = semicolonPositionedToken; + while (current.parent !== null && + end(current.parent) === end(semicolonPositionedToken) && + current.parent.kind() !== SyntaxKind.List) { + current = current.parent; + } + + // Compute the span + var span = new TextSpan(fullStart(current), fullWidth(current)); + + // Format the span + return this.formatSpan(span, FormattingRequestKind.FormatOnSemicolon); + } + + return []; + } + + public formatOnClosingCurlyBrace(caretPosition: number): ts.TextEdit[] { + var sourceUnit = this.syntaxTree.sourceUnit(); + var closeBracePositionedToken = findToken(sourceUnit, caretPosition - 1); + + if (closeBracePositionedToken.kind() === SyntaxKind.CloseBraceToken) { + // Find the outer most parent that this closing brace terminates + var current: ISyntaxElement = closeBracePositionedToken; + while (current.parent !== null && + end(current.parent) === end(closeBracePositionedToken) && + current.parent.kind() !== SyntaxKind.List) { + current = current.parent; + } + + // Compute the span + var span = new TextSpan(fullStart(current), fullWidth(current)); + + // Format the span + return this.formatSpan(span, FormattingRequestKind.FormatOnClosingCurlyBrace); + } + + return []; + } + + public formatOnEnter(caretPosition: number): ts.TextEdit[] { + var lineNumber = this.snapshot.getLineNumberFromPosition(caretPosition); + + if (lineNumber > 0) { + // Format both lines + var prevLine = this.snapshot.getLineFromLineNumber(lineNumber - 1); + var currentLine = this.snapshot.getLineFromLineNumber(lineNumber); + var span = TextSpan.fromBounds(prevLine.startPosition(), currentLine.endPosition()); + + // Format the span + return this.formatSpan(span, FormattingRequestKind.FormatOnEnter); + + } + + return []; + } + + private formatSpan(span: TextSpan, formattingRequestKind: FormattingRequestKind): ts.TextEdit[] { + // Always format from the beginning of the line + var startLine = this.snapshot.getLineFromPosition(span.start()); + span = TextSpan.fromBounds(startLine.startPosition(), span.end()); + + var result: ts.TextEdit[] = []; + + var formattingEdits = Formatter.getEdits(span, this.syntaxTree.sourceUnit(), this.options, true, this.snapshot, this.rulesProvider, formattingRequestKind); + + // + // TODO: Change the ILanguageService interface to return TextEditInfo (with start, and length) instead of TextEdit (with minChar and limChar) + formattingEdits.forEach((item) => { + result.push({ + minChar: item.position, + limChar: item.position + item.length, + text: item.replaceWith + }); + }); + + return result; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/formattingRequestKind.ts b/src/services/formatting/formattingRequestKind.ts new file mode 100644 index 00000000000..b766058e1ca --- /dev/null +++ b/src/services/formatting/formattingRequestKind.ts @@ -0,0 +1,27 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export enum FormattingRequestKind { + FormatDocument, + FormatSelection, + FormatOnEnter, + FormatOnSemicolon, + FormatOnClosingCurlyBrace, + FormatOnPaste + } +} \ No newline at end of file diff --git a/src/services/formatting/indentationNodeContext.ts b/src/services/formatting/indentationNodeContext.ts new file mode 100644 index 00000000000..398d4998eed --- /dev/null +++ b/src/services/formatting/indentationNodeContext.ts @@ -0,0 +1,103 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class IndentationNodeContext { + private _node: ISyntaxNode; + private _parent: IndentationNodeContext; + private _fullStart: number; + private _indentationAmount: number; + private _childIndentationAmountDelta: number; + private _depth: number; + private _hasSkippedOrMissingTokenChild: boolean; + + constructor(parent: IndentationNodeContext, node: ISyntaxNode, fullStart: number, indentationAmount: number, childIndentationAmountDelta: number) { + this.update(parent, node, fullStart, indentationAmount, childIndentationAmountDelta); + } + + public parent(): IndentationNodeContext { + return this._parent; + } + + public node(): ISyntaxNode { + return this._node; + } + + public fullStart(): number { + return this._fullStart; + } + + public fullWidth(): number { + return fullWidth(this._node); + } + + public start(): number { + return this._fullStart + leadingTriviaWidth(this._node); + } + + public end(): number { + return this._fullStart + leadingTriviaWidth(this._node) + width(this._node); + } + + public indentationAmount(): number { + return this._indentationAmount; + } + + public childIndentationAmountDelta(): number { + return this._childIndentationAmountDelta; + } + + public depth(): number { + return this._depth; + } + + public kind(): SyntaxKind { + return this._node.kind(); + } + + public hasSkippedOrMissingTokenChild(): boolean { + if (this._hasSkippedOrMissingTokenChild === null) { + this._hasSkippedOrMissingTokenChild = Syntax.nodeHasSkippedOrMissingTokens(this._node); + } + return this._hasSkippedOrMissingTokenChild; + } + + public clone(pool: IndentationNodeContextPool): IndentationNodeContext { + var parent: IndentationNodeContext = null; + if (this._parent) { + parent = this._parent.clone(pool); + } + return pool.getNode(parent, this._node, this._fullStart, this._indentationAmount, this._childIndentationAmountDelta); + } + + public update(parent: IndentationNodeContext, node: ISyntaxNode, fullStart: number, indentationAmount: number, childIndentationAmountDelta: number) { + this._parent = parent; + this._node = node; + this._fullStart = fullStart; + this._indentationAmount = indentationAmount; + this._childIndentationAmountDelta = childIndentationAmountDelta; + this._hasSkippedOrMissingTokenChild = null; + + if (parent) { + this._depth = parent.depth() + 1; + } + else { + this._depth = 0; + } + } + } +} \ No newline at end of file diff --git a/src/services/formatting/indentationNodeContextPool.ts b/src/services/formatting/indentationNodeContextPool.ts new file mode 100644 index 00000000000..ea5b26277b5 --- /dev/null +++ b/src/services/formatting/indentationNodeContextPool.ts @@ -0,0 +1,43 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class IndentationNodeContextPool { + private nodes: IndentationNodeContext[] = []; + + public getNode(parent: IndentationNodeContext, node: ISyntaxNode, fullStart: number, indentationLevel: number, childIndentationLevelDelta: number): IndentationNodeContext { + if (this.nodes.length > 0) { + var cachedNode = this.nodes.pop(); + cachedNode.update(parent, node, fullStart, indentationLevel, childIndentationLevelDelta); + return cachedNode; + } + + return new IndentationNodeContext(parent, node, fullStart, indentationLevel, childIndentationLevelDelta); + } + + public releaseNode(node: IndentationNodeContext, recursive: boolean = false): void { + this.nodes.push(node); + + if (recursive) { + var parent = node.parent(); + if (parent) { + this.releaseNode(parent, recursive); + } + } + } + } +} \ No newline at end of file diff --git a/src/services/formatting/indentationTrackingWalker.ts b/src/services/formatting/indentationTrackingWalker.ts new file mode 100644 index 00000000000..b07b477623e --- /dev/null +++ b/src/services/formatting/indentationTrackingWalker.ts @@ -0,0 +1,349 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class IndentationTrackingWalker extends SyntaxWalker { + private _position: number = 0; + private _parent: IndentationNodeContext = null; + private _textSpan: TextSpan; + private _snapshot: ITextSnapshot; + private _lastTriviaWasNewLine: boolean; + private _indentationNodeContextPool: IndentationNodeContextPool; + private _text: ISimpleText; + + constructor(textSpan: TextSpan, sourceUnit: SourceUnitSyntax, snapshot: ITextSnapshot, indentFirstToken: boolean, public options: FormattingOptions) { + super(); + + // Create a pool object to manage context nodes while walking the tree + this._indentationNodeContextPool = new IndentationNodeContextPool(); + + this._textSpan = textSpan; + this._text = sourceUnit.syntaxTree.text; + this._snapshot = snapshot; + this._parent = this._indentationNodeContextPool.getNode(null, sourceUnit, 0, 0, 0); + + // Is the first token in the span at the start of a new line. + this._lastTriviaWasNewLine = indentFirstToken; + } + + public position(): number { + return this._position; + } + + public parent(): IndentationNodeContext { + return this._parent; + } + + public textSpan(): TextSpan { + return this._textSpan; + } + + public snapshot(): ITextSnapshot { + return this._snapshot; + } + + public indentationNodeContextPool(): IndentationNodeContextPool { + return this._indentationNodeContextPool; + } + + public forceIndentNextToken(tokenStart: number): void { + this._lastTriviaWasNewLine = true; + this.forceRecomputeIndentationOfParent(tokenStart, true); + } + + public forceSkipIndentingNextToken(tokenStart: number): void { + this._lastTriviaWasNewLine = false; + this.forceRecomputeIndentationOfParent(tokenStart, false); + } + + public indentToken(token: ISyntaxToken, indentationAmount: number, commentIndentationAmount: number): void { + throw Errors.abstract(); + } + + public visitTokenInSpan(token: ISyntaxToken): void { + if (this._lastTriviaWasNewLine) { + // Compute the indentation level at the current token + var indentationAmount = this.getTokenIndentationAmount(token); + var commentIndentationAmount = this.getCommentIndentationAmount(token); + + // Process the token + this.indentToken(token, indentationAmount, commentIndentationAmount); + } + } + + public visitToken(token: ISyntaxToken): void { + var tokenSpan = new TextSpan(this._position, token.fullWidth()); + + if (tokenSpan.intersectsWithTextSpan(this._textSpan)) { + this.visitTokenInSpan(token); + + // Only track new lines on tokens within the range. Make sure to check that the last trivia is a newline, and not just one of the trivia + var trivia = token.trailingTrivia(); + this._lastTriviaWasNewLine = trivia.hasNewLine() && trivia.syntaxTriviaAt(trivia.count() - 1).kind() == SyntaxKind.NewLineTrivia; + } + + // Update the position + this._position += token.fullWidth(); + } + + public visitNode(node: ISyntaxNode): void { + var nodeSpan = new TextSpan(this._position, fullWidth(node)); + + if (nodeSpan.intersectsWithTextSpan(this._textSpan)) { + // Update indentation level + var indentation = this.getNodeIndentation(node); + + // Update the parent + var currentParent = this._parent; + this._parent = this._indentationNodeContextPool.getNode(currentParent, node, this._position, indentation.indentationAmount, indentation.indentationAmountDelta); + + // Visit node + visitNodeOrToken(this, node); + + // Reset state + this._indentationNodeContextPool.releaseNode(this._parent); + this._parent = currentParent; + } + else { + // We're skipping the node, so update our position accordingly. + this._position += fullWidth(node); + } + } + + private getTokenIndentationAmount(token: ISyntaxToken): number { + // If this is the first token of a node, it should follow the node indentation and not the child indentation; + // (e.g.class in a class declaration or module in module declariotion). + // Open and close braces should follow the indentation of thier parent as well(e.g. + // class { + // } + // Also in a do-while statement, the while should be indented like the parent. + if (firstToken(this._parent.node()) === token || + token.kind() === SyntaxKind.OpenBraceToken || token.kind() === SyntaxKind.CloseBraceToken || + token.kind() === SyntaxKind.OpenBracketToken || token.kind() === SyntaxKind.CloseBracketToken || + (token.kind() === SyntaxKind.WhileKeyword && this._parent.node().kind() == SyntaxKind.DoStatement)) { + return this._parent.indentationAmount(); + } + + return (this._parent.indentationAmount() + this._parent.childIndentationAmountDelta()); + } + + private getCommentIndentationAmount(token: ISyntaxToken): number { + // If this is token terminating an indentation scope, leading comments should be indented to follow the children + // indentation level and not the node + + if (token.kind() === SyntaxKind.CloseBraceToken || token.kind() === SyntaxKind.CloseBracketToken) { + return (this._parent.indentationAmount() + this._parent.childIndentationAmountDelta()); + } + return this._parent.indentationAmount(); + } + + private getNodeIndentation(node: ISyntaxNode, newLineInsertedByFormatting?: boolean): { indentationAmount: number; indentationAmountDelta: number; } { + var parent = this._parent; + + // We need to get the parent's indentation, which could be one of 2 things. If first token of the parent is in the span, use the parent's computed indentation. + // If the parent was outside the span, use the actual indentation of the parent. + var parentIndentationAmount: number; + if (this._textSpan.containsPosition(parent.start())) { + parentIndentationAmount = parent.indentationAmount(); + } + else { + if (parent.kind() === SyntaxKind.Block && !this.shouldIndentBlockInParent(this._parent.parent())) { + // Blocks preserve the indentation of their containing node (unless they're a + // standalone block in a list). i.e. if you have: + // + // function foo( + // a: number) { + // + // Then we expect the indentation of the block to be tied to the function, not to + // the line that the block is defined on. If we were to do the latter, then the + // indentation would be here: + // + // function foo( + // a: number) { + // | + // + // Instead of: + // + // function foo( + // a: number) { + // | + parent = this._parent.parent(); + } + + var line = this._snapshot.getLineFromPosition(parent.start()).getText(); + var firstNonWhiteSpacePosition = Indentation.firstNonWhitespacePosition(line); + parentIndentationAmount = Indentation.columnForPositionInString(line, firstNonWhiteSpacePosition, this.options); + } + var parentIndentationAmountDelta = parent.childIndentationAmountDelta(); + + // The indentation level of the node + var indentationAmount: number; + + // The delta it adds to its children. + var indentationAmountDelta: number; + var parentNode = parent.node(); + + switch (node.kind()) { + default: + // General case + // This node should follow the child indentation set by its parent + // This node does not introduce any new indentation scope, indent any decendants of this node (tokens or child nodes) + // using the same indentation level + indentationAmount = (parentIndentationAmount + parentIndentationAmountDelta); + indentationAmountDelta = 0; + break; + + // Statements introducing {} + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ObjectType: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.SwitchStatement: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.IndexMemberDeclaration: + case SyntaxKind.CatchClause: + // Statements introducing [] + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ArrayType: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.IndexSignature: + // Other statements + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.CaseSwitchClause: + case SyntaxKind.DefaultSwitchClause: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.SimpleArrowFunctionExpression: + case SyntaxKind.ParenthesizedArrowFunctionExpression: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.ExportAssignment: + + // Expressions which have argument lists or parameter lists + case SyntaxKind.InvocationExpression: + case SyntaxKind.ObjectCreationExpression: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + + // These nodes should follow the child indentation set by its parent; + // they introduce a new indenation scope; children should be indented at one level deeper + indentationAmount = (parentIndentationAmount + parentIndentationAmountDelta); + indentationAmountDelta = this.options.indentSpaces; + break; + + case SyntaxKind.IfStatement: + if (parent.kind() === SyntaxKind.ElseClause && + !SyntaxUtilities.isLastTokenOnLine((parentNode).elseKeyword, this._text)) { + // This is an else if statement with the if on the same line as the else, do not indent the if statmement. + // Note: Children indentation has already been set by the parent if statement, so no need to increment + indentationAmount = parentIndentationAmount; + } + else { + // Otherwise introduce a new indenation scope; children should be indented at one level deeper + indentationAmount = (parentIndentationAmount + parentIndentationAmountDelta); + } + indentationAmountDelta = this.options.indentSpaces; + break; + + case SyntaxKind.ElseClause: + // Else should always follow its parent if statement indentation. + // Note: Children indentation has already been set by the parent if statement, so no need to increment + indentationAmount = parentIndentationAmount; + indentationAmountDelta = this.options.indentSpaces; + break; + + + case SyntaxKind.Block: + // Check if the block is a member in a list of statements (if the parent is a source unit, module, or block, or switch clause) + if (this.shouldIndentBlockInParent(parent)) { + indentationAmount = parentIndentationAmount + parentIndentationAmountDelta; + } + else { + indentationAmount = parentIndentationAmount; + } + + indentationAmountDelta = this.options.indentSpaces; + break; + } + + // If the parent happens to start on the same line as this node, then override the current node indenation with that + // of the parent. This avoid having to add an extra level of indentation for the children. e.g.: + // return { + // a:1 + // }; + // instead of: + // return { + // a:1 + // }; + // We also need to pass the delta (if it is nonzero) to the children, so that subsequent lines get indented. Essentially, if any node starting on the given line + // has a nonzero delta , the resulting delta should be inherited from this node. This is to indent cases like the following: + // return a + // || b; + // Lastly, it is possible the node indentation needs to be recomputed because the formatter inserted a newline before its first token. + // If this is the case, we know the node no longer starts on the same line as its parent (or at least we shouldn't treat it as such). + if (parentNode) { + if (!newLineInsertedByFormatting /*This could be false or undefined here*/) { + var parentStartLine = this._snapshot.getLineNumberFromPosition(parent.start()); + var currentNodeStartLine = this._snapshot.getLineNumberFromPosition(this._position + leadingTriviaWidth(node)); + if (parentStartLine === currentNodeStartLine || newLineInsertedByFormatting === false /*meaning a new line was removed and we are force recomputing*/) { + indentationAmount = parentIndentationAmount; + indentationAmountDelta = Math.min(this.options.indentSpaces, parentIndentationAmountDelta + indentationAmountDelta); + } + } + } + + return { + indentationAmount: indentationAmount, + indentationAmountDelta: indentationAmountDelta + }; + } + + private shouldIndentBlockInParent(parent: IndentationNodeContext): boolean { + switch (parent.kind()) { + case SyntaxKind.SourceUnit: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.Block: + case SyntaxKind.CaseSwitchClause: + case SyntaxKind.DefaultSwitchClause: + return true; + + default: + return false; + } + } + + private forceRecomputeIndentationOfParent(tokenStart: number, newLineAdded: boolean /*as opposed to removed*/): void { + var parent = this._parent; + if (parent.fullStart() === tokenStart) { + // Temporarily pop the parent before recomputing + this._parent = parent.parent(); + var indentation = this.getNodeIndentation(parent.node(), /* newLineInsertedByFormatting */ newLineAdded); + parent.update(parent.parent(), parent.node(), parent.fullStart(), indentation.indentationAmount, indentation.indentationAmountDelta); + this._parent = parent; + } + } + } +} \ No newline at end of file diff --git a/src/services/formatting/multipleTokenIndenter.ts b/src/services/formatting/multipleTokenIndenter.ts new file mode 100644 index 00000000000..23181571427 --- /dev/null +++ b/src/services/formatting/multipleTokenIndenter.ts @@ -0,0 +1,206 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class MultipleTokenIndenter extends IndentationTrackingWalker { + private _edits: TextEditInfo[] = []; + + constructor(textSpan: TextSpan, sourceUnit: SourceUnitSyntax, snapshot: ITextSnapshot, indentFirstToken: boolean, options: FormattingOptions) { + super(textSpan, sourceUnit, snapshot, indentFirstToken, options); + } + + public indentToken(token: ISyntaxToken, indentationAmount: number, commentIndentationAmount: number): void { + // Ignore generated tokens + if (token.fullWidth() === 0) { + return; + } + + // If we have any skipped tokens as children, do not process this node for indentation or formatting + if (this.parent().hasSkippedOrMissingTokenChild()) { + return; + } + + // Be strict, and only consider nodes that fall inside the span. This avoids indenting a multiline string + // on enter at the end of, as the whole token was not included in the span + var tokenSpan = new TextSpan(this.position() + token.leadingTriviaWidth(), width(token)); + if (!this.textSpan().containsTextSpan(tokenSpan)) { + return; + } + + // Compute an indentation string for this token + var indentationString = Indentation.indentationString(indentationAmount, this.options); + + var commentIndentationString = Indentation.indentationString(commentIndentationAmount, this.options); + + // Record any needed indentation edits + this.recordIndentationEditsForToken(token, indentationString, commentIndentationString); + } + + public edits(): TextEditInfo[]{ + return this._edits; + } + + public recordEdit(position: number, length: number, replaceWith: string): void { + this._edits.push(new TextEditInfo(position, length, replaceWith)); + } + + private recordIndentationEditsForToken(token: ISyntaxToken, indentationString: string, commentIndentationString: string) { + var position = this.position(); + var indentNextTokenOrTrivia = true; + var leadingWhiteSpace = ""; // We need to track the whitespace before a multiline comment + + // Process any leading trivia if any + var triviaList = token.leadingTrivia(); + if (triviaList) { + for (var i = 0, length = triviaList.count(); i < length; i++, position += trivia.fullWidth()) { + var trivia = triviaList.syntaxTriviaAt(i); + // Skip this trivia if it is not in the span + if (!this.textSpan().containsTextSpan(new TextSpan(position, trivia.fullWidth()))) { + continue; + } + + switch (trivia.kind()) { + case SyntaxKind.MultiLineCommentTrivia: + // We will only indent the first line of the multiline comment if we were planning to indent the next trivia. However, + // subsequent lines will always be indented + this.recordIndentationEditsForMultiLineComment(trivia, position, commentIndentationString, leadingWhiteSpace, !indentNextTokenOrTrivia /* already indented first line */); + indentNextTokenOrTrivia = false; + leadingWhiteSpace = ""; + break; + + case SyntaxKind.SingleLineCommentTrivia: + case SyntaxKind.SkippedTokenTrivia: + if (indentNextTokenOrTrivia) { + this.recordIndentationEditsForSingleLineOrSkippedText(trivia, position, commentIndentationString); + indentNextTokenOrTrivia = false; + } + break; + + case SyntaxKind.WhitespaceTrivia: + // If the next trivia is a comment, use the comment indentation level instead of the regular indentation level + // If the next trivia is a newline, this whole line is just whitespace, so don't do anything (trimming will take care of it) + var nextTrivia = length > i + 1 && triviaList.syntaxTriviaAt(i + 1); + var whiteSpaceIndentationString = nextTrivia && nextTrivia.isComment() ? commentIndentationString : indentationString; + if (indentNextTokenOrTrivia) { + if (!(nextTrivia && nextTrivia.isNewLine())) { + this.recordIndentationEditsForWhitespace(trivia, position, whiteSpaceIndentationString); + } + indentNextTokenOrTrivia = false; + } + leadingWhiteSpace += trivia.fullText(); + break; + + case SyntaxKind.NewLineTrivia: + // We hit a newline processing the trivia. We need to add the indentation to the + // next line as well. Note: don't bother indenting the newline itself. This will + // just insert ugly whitespace that most users probably will not want. + indentNextTokenOrTrivia = true; + leadingWhiteSpace = ""; + break; + + default: + throw Errors.invalidOperation(); + } + } + + } + + if (token.kind() !== SyntaxKind.EndOfFileToken && indentNextTokenOrTrivia) { + // If the last trivia item was a new line, or no trivia items were encounterd record the + // indentation edit at the token position + if (indentationString.length > 0) { + this.recordEdit(position, 0, indentationString); + } + } + } + + private recordIndentationEditsForSingleLineOrSkippedText(trivia: ISyntaxTrivia, fullStart: number, indentationString: string): void { + // Record the edit + if (indentationString.length > 0) { + this.recordEdit(fullStart, 0, indentationString); + } + } + + private recordIndentationEditsForWhitespace(trivia: ISyntaxTrivia, fullStart: number, indentationString: string): void { + var text = trivia.fullText(); + + // Check if the current indentation matches the desired indentation or not + if (indentationString === text) { + return; + } + + // Record the edit + this.recordEdit(fullStart, text.length, indentationString); + } + + private recordIndentationEditsForMultiLineComment(trivia: ISyntaxTrivia, fullStart: number, indentationString: string, leadingWhiteSpace: string, firstLineAlreadyIndented: boolean): void { + // If the multiline comment spans multiple lines, we need to add the right indent amount to + // each successive line segment as well. + var position = fullStart; + var segments = Syntax.splitMultiLineCommentTriviaIntoMultipleLines(trivia); + + if (segments.length <= 1) { + if (!firstLineAlreadyIndented) { + // Process the one-line multiline comment just like a single line comment + this.recordIndentationEditsForSingleLineOrSkippedText(trivia, fullStart, indentationString); + } + return; + } + + // Find number of columns in first segment + var whiteSpaceColumnsInFirstSegment = Indentation.columnForPositionInString(leadingWhiteSpace, leadingWhiteSpace.length, this.options); + + var indentationColumns = Indentation.columnForPositionInString(indentationString, indentationString.length, this.options); + var startIndex = 0; + if (firstLineAlreadyIndented) { + startIndex = 1; + position += segments[0].length; + } + for (var i = startIndex; i < segments.length; i++) { + var segment = segments[i]; + this.recordIndentationEditsForSegment(segment, position, indentationColumns, whiteSpaceColumnsInFirstSegment); + position += segment.length; + } + } + + private recordIndentationEditsForSegment(segment: string, fullStart: number, indentationColumns: number, whiteSpaceColumnsInFirstSegment: number): void { + // Indent subsequent lines using a column delta of the actual indentation relative to the first line + var firstNonWhitespacePosition = Indentation.firstNonWhitespacePosition(segment); + var leadingWhiteSpaceColumns = Indentation.columnForPositionInString(segment, firstNonWhitespacePosition, this.options); + var deltaFromFirstSegment = leadingWhiteSpaceColumns - whiteSpaceColumnsInFirstSegment; + var finalColumns = indentationColumns + deltaFromFirstSegment; + if (finalColumns < 0) { + finalColumns = 0; + } + var indentationString = Indentation.indentationString(finalColumns, this.options); + + if (firstNonWhitespacePosition < segment.length && + CharacterInfo.isLineTerminator(segment.charCodeAt(firstNonWhitespacePosition))) { + // If this segment was just a newline, then don't bother indenting it. That will just + // leave the user with an ugly indent in their output that they probably do not want. + return; + } + + if (indentationString === segment.substring(0, firstNonWhitespacePosition)) { + return; + } + + // Record the edit + this.recordEdit(fullStart, firstNonWhitespacePosition, indentationString); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/rule.ts b/src/services/formatting/rule.ts new file mode 100644 index 00000000000..273f0590ce7 --- /dev/null +++ b/src/services/formatting/rule.ts @@ -0,0 +1,32 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class Rule { + constructor( + public Descriptor: RuleDescriptor, + public Operation: RuleOperation, + public Flag: RuleFlags = RuleFlags.None) { + } + + public toString() { + return "[desc=" + this.Descriptor + "," + + "operation=" + this.Operation + "," + + "flag=" + this.Flag + "]"; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/ruleAction.ts b/src/services/formatting/ruleAction.ts new file mode 100644 index 00000000000..32c67c950ca --- /dev/null +++ b/src/services/formatting/ruleAction.ts @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export enum RuleAction { + Ignore, + Space, + NewLine, + Delete + } +} \ No newline at end of file diff --git a/src/services/formatting/ruleDescriptor.ts b/src/services/formatting/ruleDescriptor.ts new file mode 100644 index 00000000000..52ee970130e --- /dev/null +++ b/src/services/formatting/ruleDescriptor.ts @@ -0,0 +1,46 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class RuleDescriptor { + constructor(public LeftTokenRange: Shared.TokenRange, public RightTokenRange: Shared.TokenRange) { + } + + public toString(): string { + return "[leftRange=" + this.LeftTokenRange + "," + + "rightRange=" + this.RightTokenRange + "]"; + } + + static create1(left: SyntaxKind, right: SyntaxKind): RuleDescriptor { + return RuleDescriptor.create4(Shared.TokenRange.FromToken(left), Shared.TokenRange.FromToken(right)) + } + + static create2(left: Shared.TokenRange, right: SyntaxKind): RuleDescriptor { + return RuleDescriptor.create4(left, Shared.TokenRange.FromToken(right)); + } + + static create3(left: SyntaxKind, right: Shared.TokenRange): RuleDescriptor + //: this(TokenRange.FromToken(left), right) + { + return RuleDescriptor.create4(Shared.TokenRange.FromToken(left), right); + } + + static create4(left: Shared.TokenRange, right: Shared.TokenRange): RuleDescriptor { + return new RuleDescriptor(left, right); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/ruleFlag.ts b/src/services/formatting/ruleFlag.ts new file mode 100644 index 00000000000..d815537dfd9 --- /dev/null +++ b/src/services/formatting/ruleFlag.ts @@ -0,0 +1,23 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export enum RuleFlags { + None, + CanDeleteNewLines + } +} \ No newline at end of file diff --git a/src/services/formatting/ruleOperation.ts b/src/services/formatting/ruleOperation.ts new file mode 100644 index 00000000000..1f74aea0784 --- /dev/null +++ b/src/services/formatting/ruleOperation.ts @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class RuleOperation { + public Context: RuleOperationContext; + public Action: RuleAction; + + constructor() { + this.Context = null; + this.Action = null; + } + + public toString(): string { + return "[context=" + this.Context + "," + + "action=" + this.Action + "]"; + } + + static create1(action: RuleAction) { + return RuleOperation.create2(RuleOperationContext.Any, action) + } + + static create2(context: RuleOperationContext, action: RuleAction) { + var result = new RuleOperation(); + result.Context = context; + result.Action = action; + return result; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/ruleOperationContext.ts b/src/services/formatting/ruleOperationContext.ts new file mode 100644 index 00000000000..a1e349210e4 --- /dev/null +++ b/src/services/formatting/ruleOperationContext.ts @@ -0,0 +1,47 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + + export class RuleOperationContext { + private customContextChecks: { (context: FormattingContext): boolean; }[]; + + constructor(...funcs: { (context: FormattingContext): boolean; }[]) { + this.customContextChecks = funcs; + } + + static Any: RuleOperationContext = new RuleOperationContext(); + + + public IsAny(): boolean { + return this == RuleOperationContext.Any; + } + + public InContext(context: FormattingContext): boolean { + if (this.IsAny()) { + return true; + } + + for (var i = 0, len = this.customContextChecks.length; i < len; i++) { + if (!this.customContextChecks[i](context)) { + return false; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts new file mode 100644 index 00000000000..ade1dc6b217 --- /dev/null +++ b/src/services/formatting/rules.ts @@ -0,0 +1,678 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class Rules { + public getRuleName(rule: Rule) { + var o: ts.Map = this; + for (var name in o) { + if (o[name] === rule) { + return name; + } + } + throw new Error(TypeScript.getDiagnosticMessage(TypeScript.DiagnosticCode.Unknown_rule, null)); + } + + [name: string]: any; + + public IgnoreBeforeComment: Rule; + public IgnoreAfterLineComment: Rule; + + // Space after keyword but not before ; or : or ? + public NoSpaceBeforeSemicolon: Rule; + public NoSpaceBeforeColon: Rule; + public NoSpaceBeforeQMark: Rule; + public SpaceAfterColon: Rule; + public SpaceAfterQMark: Rule; + public SpaceAfterSemicolon: Rule; + + // Space/new line after }. + public SpaceAfterCloseBrace: Rule; + + // Special case for (}, else) and (}, while) since else & while tokens are not part of the tree which makes SpaceAfterCloseBrace rule not applied + // Also should not apply to }) + public SpaceBetweenCloseBraceAndElse: Rule; + public SpaceBetweenCloseBraceAndWhile: Rule; + public NoSpaceAfterCloseBrace: Rule; + + // No space for indexer and dot + public NoSpaceBeforeDot: Rule; + public NoSpaceAfterDot: Rule; + public NoSpaceBeforeOpenBracket: Rule; + public NoSpaceAfterOpenBracket: Rule; + public NoSpaceBeforeCloseBracket: Rule; + public NoSpaceAfterCloseBracket: Rule; + + // Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}. + public SpaceAfterOpenBrace: Rule; + public SpaceBeforeCloseBrace: Rule; + public NoSpaceBetweenEmptyBraceBrackets: Rule; + + // Insert new line after { and before } in multi-line contexts. + public NewLineAfterOpenBraceInBlockContext: Rule; + + // For functions and control block place } on a new line [multi-line rule] + public NewLineBeforeCloseBraceInBlockContext: Rule; + + // Special handling of unary operators. + // Prefix operators generally shouldn't have a space between + // them and their target unary expression. + public NoSpaceAfterUnaryPrefixOperator: Rule; + public NoSpaceAfterUnaryPreincrementOperator: Rule; + public NoSpaceAfterUnaryPredecrementOperator: Rule; + public NoSpaceBeforeUnaryPostincrementOperator: Rule; + public NoSpaceBeforeUnaryPostdecrementOperator: Rule; + + // More unary operator special-casing. + // DevDiv 181814: Be careful when removing leading whitespace + // around unary operators. Examples: + // 1 - -2 --X--> 1--2 + // a + ++b --X--> a+++b + public SpaceAfterPostincrementWhenFollowedByAdd: Rule; + public SpaceAfterAddWhenFollowedByUnaryPlus: Rule; + public SpaceAfterAddWhenFollowedByPreincrement: Rule; + public SpaceAfterPostdecrementWhenFollowedBySubtract: Rule; + public SpaceAfterSubtractWhenFollowedByUnaryMinus: Rule; + public SpaceAfterSubtractWhenFollowedByPredecrement: Rule; + + public NoSpaceBeforeComma: Rule; + + public SpaceAfterCertainKeywords: Rule; + public NoSpaceBeforeOpenParenInFuncCall: Rule; + public SpaceAfterFunctionInFuncDecl: Rule; + public NoSpaceBeforeOpenParenInFuncDecl: Rule; + public SpaceAfterVoidOperator: Rule; + + public NoSpaceBetweenReturnAndSemicolon: Rule; + + // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. + // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] + public SpaceBetweenStatements: Rule; + + // This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter. + public SpaceAfterTryFinally: Rule; + + // For get/set members, we check for (identifier,identifier) since get/set don't have tokens and they are represented as just an identifier token. + // Though, we do extra check on the context to make sure we are dealing with get/set node. Example: + // get x() {} + // set x(val) {} + public SpaceAfterGetSetInMember: Rule; + + // Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options. + public SpaceBeforeBinaryKeywordOperator: Rule; + public SpaceAfterBinaryKeywordOperator: Rule; + + // TypeScript-specific rules + + // Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses + public NoSpaceAfterConstructor: Rule; + + // Use of module as a function call. e.g.: import m2 = module("m2"); + public NoSpaceAfterModuleImport: Rule; + + // Add a space around certain TypeScript keywords + public SpaceAfterCertainTypeScriptKeywords: Rule; + public SpaceBeforeCertainTypeScriptKeywords: Rule; + + // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { + public SpaceAfterModuleName: Rule; + + // Lambda expressions + public SpaceAfterArrow: Rule; + + // Optional parameters and var args + public NoSpaceAfterEllipsis: Rule; + public NoSpaceAfterOptionalParameters: Rule; + + // generics + public NoSpaceBeforeOpenAngularBracket: Rule; + public NoSpaceBetweenCloseParenAndAngularBracket: Rule; + public NoSpaceAfterOpenAngularBracket: Rule; + public NoSpaceBeforeCloseAngularBracket: Rule; + public NoSpaceAfterCloseAngularBracket: Rule; + + // Remove spaces in empty interface literals. e.g.: x: {} + public NoSpaceBetweenEmptyInterfaceBraceBrackets: Rule; + + // These rules are higher in priority than user-configurable rules. + public HighPriorityCommonRules: Rule[]; + + // These rules are lower in priority than user-configurable rules. + public LowPriorityCommonRules: Rule[]; + + /// + /// Rules controlled by user options + /// + + // Insert space after comma delimiter + public SpaceAfterComma: Rule; + public NoSpaceAfterComma: Rule; + + // Insert space before and after binary operators + public SpaceBeforeBinaryOperator: Rule; + public SpaceAfterBinaryOperator: Rule; + public NoSpaceBeforeBinaryOperator: Rule; + public NoSpaceAfterBinaryOperator: Rule; + + // Insert space after keywords in control flow statements + public SpaceAfterKeywordInControl: Rule; + public NoSpaceAfterKeywordInControl: Rule; + + // Open Brace braces after function + //TypeScript: Function can have return types, which can be made of tons of different token kinds + public FunctionOpenBraceLeftTokenRange: Shared.TokenRange; + public SpaceBeforeOpenBraceInFunction: Rule; + public NewLineBeforeOpenBraceInFunction: Rule; + + // Open Brace braces after TypeScript module/class/interface + public TypeScriptOpenBraceLeftTokenRange: Shared.TokenRange; + public SpaceBeforeOpenBraceInTypeScriptDeclWithBlock: Rule; + public NewLineBeforeOpenBraceInTypeScriptDeclWithBlock: Rule; + + // Open Brace braces after control block + public ControlOpenBraceLeftTokenRange: Shared.TokenRange; + public SpaceBeforeOpenBraceInControl: Rule; + public NewLineBeforeOpenBraceInControl: Rule; + + // Insert space after semicolon in for statement + public SpaceAfterSemicolonInFor: Rule; + public NoSpaceAfterSemicolonInFor: Rule; + + // Insert space after opening and before closing nonempty parenthesis + public SpaceAfterOpenParen: Rule; + public SpaceBeforeCloseParen: Rule; + public NoSpaceBetweenParens: Rule; + public NoSpaceAfterOpenParen: Rule; + public NoSpaceBeforeCloseParen: Rule; + + // Insert space after function keyword for anonymous functions + public SpaceAfterAnonymousFunctionKeyword: Rule; + public NoSpaceAfterAnonymousFunctionKeyword: Rule; + + constructor() { + /// + /// Common Rules + /// + + // Leave comments alone + this.IgnoreBeforeComment = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.Comments), RuleOperation.create1(RuleAction.Ignore)); + this.IgnoreAfterLineComment = new Rule(RuleDescriptor.create3(SyntaxKind.SingleLineCommentTrivia, Shared.TokenRange.Any), RuleOperation.create1(RuleAction.Ignore)); + + // Space after keyword but not before ; or : or ? + this.NoSpaceBeforeSemicolon = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeColon = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.ColonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete)); + this.NoSpaceBeforeQMark = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.QuestionToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete)); + this.SpaceAfterColon = new Rule(RuleDescriptor.create3(SyntaxKind.ColonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Space)); + this.SpaceAfterQMark = new Rule(RuleDescriptor.create3(SyntaxKind.QuestionToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Space)); + this.SpaceAfterSemicolon = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + + // Space after }. + this.SpaceAfterCloseBrace = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsAfterCodeBlockContext), RuleAction.Space)); + + // Special case for (}, else) and (}, while) since else & while tokens are not part of the tree which makes SpaceAfterCloseBrace rule not applied + this.SpaceBetweenCloseBraceAndElse = new Rule(RuleDescriptor.create1(SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceBetweenCloseBraceAndWhile = new Rule(RuleDescriptor.create1(SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceAfterCloseBrace = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBraceToken, Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken, SyntaxKind.SemicolonToken])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // No space for indexer and dot + this.NoSpaceBeforeDot = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.DotToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterDot = new Rule(RuleDescriptor.create3(SyntaxKind.DotToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeOpenBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterCloseBracket = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Place a space before open brace in a function declaration + this.FunctionOpenBraceLeftTokenRange = Shared.TokenRange.AnyIncludingMultilineComments; + this.SpaceBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines); + + // Place a space before open brace in a TypeScript declaration that has braces as children (class, module, enum, etc) + this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.MultiLineCommentTrivia]); + this.SpaceBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines); + + // Place a space before open brace in a control flow construct + this.ControlOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.MultiLineCommentTrivia, SyntaxKind.DoKeyword, SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword, SyntaxKind.ElseKeyword]); + this.SpaceBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines); + + // Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}. + this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSingleLineBlockContext), RuleAction.Space)); + this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSingleLineBlockContext), RuleAction.Space)); + this.NoSpaceBetweenEmptyBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsObjectContext), RuleAction.Delete)); + + // Insert new line after { and before } in multi-line contexts. + this.NewLineAfterOpenBraceInBlockContext = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsMultilineBlockContext), RuleAction.NewLine)); + + // For functions and control block place } on a new line [multi-line rule] + this.NewLineBeforeCloseBraceInBlockContext = new Rule(RuleDescriptor.create2(Shared.TokenRange.AnyIncludingMultilineComments, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsMultilineBlockContext), RuleAction.NewLine)); + + // Special handling of unary operators. + // Prefix operators generally shouldn't have a space between + // them and their target unary expression. + this.NoSpaceAfterUnaryPrefixOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.UnaryPrefixOperators, Shared.TokenRange.UnaryPrefixExpressions), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete)); + this.NoSpaceAfterUnaryPreincrementOperator = new Rule(RuleDescriptor.create3(SyntaxKind.PlusPlusToken, Shared.TokenRange.UnaryPreincrementExpressions), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterUnaryPredecrementOperator = new Rule(RuleDescriptor.create3(SyntaxKind.MinusMinusToken, Shared.TokenRange.UnaryPredecrementExpressions), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeUnaryPostincrementOperator = new Rule(RuleDescriptor.create2(Shared.TokenRange.UnaryPostincrementExpressions, SyntaxKind.PlusPlusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeUnaryPostdecrementOperator = new Rule(RuleDescriptor.create2(Shared.TokenRange.UnaryPostdecrementExpressions, SyntaxKind.MinusMinusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // More unary operator special-casing. + // DevDiv 181814: Be careful when removing leading whitespace + // around unary operators. Examples: + // 1 - -2 --X--> 1--2 + // a + ++b --X--> a+++b + this.SpaceAfterPostincrementWhenFollowedByAdd = new Rule(RuleDescriptor.create1(SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterAddWhenFollowedByUnaryPlus = new Rule(RuleDescriptor.create1(SyntaxKind.PlusToken, SyntaxKind.PlusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterAddWhenFollowedByPreincrement = new Rule(RuleDescriptor.create1(SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterPostdecrementWhenFollowedBySubtract = new Rule(RuleDescriptor.create1(SyntaxKind.MinusMinusToken, SyntaxKind.MinusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterSubtractWhenFollowedByUnaryMinus = new Rule(RuleDescriptor.create1(SyntaxKind.MinusToken, SyntaxKind.MinusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterSubtractWhenFollowedByPredecrement = new Rule(RuleDescriptor.create1(SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + + this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsFunctionCallOrNewContext), RuleAction.Delete)); + this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); + this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete)); + this.SpaceAfterVoidOperator = new Rule(RuleDescriptor.create3(SyntaxKind.VoidKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsVoidOpContext), RuleAction.Space)); + + this.NoSpaceBetweenReturnAndSemicolon = new Rule(RuleDescriptor.create1(SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. + // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] + this.SpaceBetweenStatements = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotForContext), RuleAction.Space)); + + // This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter. + this.SpaceAfterTryFinally = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword]), SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + + // get x() {} + // set x(val) {} + this.SpaceAfterGetSetInMember = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]), SyntaxKind.IdentifierName), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); + + // Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options. + this.SpaceBeforeBinaryKeywordOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryKeywordOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterBinaryKeywordOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryKeywordOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + + // TypeScript-specific higher priority rules + + // Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses + this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Use of module as a function call. e.g.: import m2 = module("m2"); + this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Add a space around certain TypeScript keywords + this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + + // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { + this.SpaceAfterModuleName = new Rule(RuleDescriptor.create1(SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsModuleDeclContext), RuleAction.Space)); + + // Lambda expressions + this.SpaceAfterArrow = new Rule(RuleDescriptor.create3(SyntaxKind.EqualsGreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + + // Optional parameters and var args + this.NoSpaceAfterEllipsis = new Rule(RuleDescriptor.create1(SyntaxKind.DotDotDotToken, SyntaxKind.IdentifierName), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterOptionalParameters = new Rule(RuleDescriptor.create3(SyntaxKind.QuestionToken, Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.CommaToken])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsNotBinaryOpContext), RuleAction.Delete)); + + // generics + this.NoSpaceBeforeOpenAngularBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.TypeNames, SyntaxKind.LessThanToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsTypeArgumentOrParameterContext), RuleAction.Delete)); + this.NoSpaceBetweenCloseParenAndAngularBracket = new Rule(RuleDescriptor.create1(SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsTypeArgumentOrParameterContext), RuleAction.Delete)); + this.NoSpaceAfterOpenAngularBracket = new Rule(RuleDescriptor.create3(SyntaxKind.LessThanToken, Shared.TokenRange.TypeNames), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsTypeArgumentOrParameterContext), RuleAction.Delete)); + this.NoSpaceBeforeCloseAngularBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.GreaterThanToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsTypeArgumentOrParameterContext), RuleAction.Delete)); + this.NoSpaceAfterCloseAngularBracket = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.FromTokens([SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.GreaterThanToken, SyntaxKind.CommaToken])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsTypeArgumentOrParameterContext), RuleAction.Delete)); + + // Remove spaces in empty interface literals. e.g.: x: {} + this.NoSpaceBetweenEmptyInterfaceBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsObjectTypeContext), RuleAction.Delete)); + + // These rules are higher in priority than user-configurable rules. + this.HighPriorityCommonRules = + [ + this.IgnoreBeforeComment, this.IgnoreAfterLineComment, + this.NoSpaceBeforeColon, this.SpaceAfterColon, this.NoSpaceBeforeQMark, this.SpaceAfterQMark, + this.NoSpaceBeforeDot, this.NoSpaceAfterDot, + this.NoSpaceAfterUnaryPrefixOperator, + this.NoSpaceAfterUnaryPreincrementOperator, this.NoSpaceAfterUnaryPredecrementOperator, + this.NoSpaceBeforeUnaryPostincrementOperator, this.NoSpaceBeforeUnaryPostdecrementOperator, + this.SpaceAfterPostincrementWhenFollowedByAdd, + this.SpaceAfterAddWhenFollowedByUnaryPlus, this.SpaceAfterAddWhenFollowedByPreincrement, + this.SpaceAfterPostdecrementWhenFollowedBySubtract, + this.SpaceAfterSubtractWhenFollowedByUnaryMinus, this.SpaceAfterSubtractWhenFollowedByPredecrement, + this.NoSpaceAfterCloseBrace, + this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NewLineBeforeCloseBraceInBlockContext, + this.SpaceAfterCloseBrace, this.SpaceBetweenCloseBraceAndElse, this.SpaceBetweenCloseBraceAndWhile, this.NoSpaceBetweenEmptyBraceBrackets, + this.SpaceAfterFunctionInFuncDecl, this.NewLineAfterOpenBraceInBlockContext, this.SpaceAfterGetSetInMember, + this.NoSpaceBetweenReturnAndSemicolon, + this.SpaceAfterCertainKeywords, + this.NoSpaceBeforeOpenParenInFuncCall, + this.SpaceBeforeBinaryKeywordOperator, this.SpaceAfterBinaryKeywordOperator, + this.SpaceAfterVoidOperator, + + // TypeScript-specific rules + this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport, + this.SpaceAfterCertainTypeScriptKeywords, this.SpaceBeforeCertainTypeScriptKeywords, + this.SpaceAfterModuleName, + this.SpaceAfterArrow, + this.NoSpaceAfterEllipsis, + this.NoSpaceAfterOptionalParameters, + this.NoSpaceBetweenEmptyInterfaceBraceBrackets, + this.NoSpaceBeforeOpenAngularBracket, + this.NoSpaceBetweenCloseParenAndAngularBracket, + this.NoSpaceAfterOpenAngularBracket, + this.NoSpaceBeforeCloseAngularBracket, + this.NoSpaceAfterCloseAngularBracket + ]; + + // These rules are lower in priority than user-configurable rules. + this.LowPriorityCommonRules = + [ + this.NoSpaceBeforeSemicolon, + this.SpaceBeforeOpenBraceInControl, this.SpaceBeforeOpenBraceInFunction, this.SpaceBeforeOpenBraceInTypeScriptDeclWithBlock, + this.NoSpaceBeforeComma, + this.NoSpaceBeforeOpenBracket, this.NoSpaceAfterOpenBracket, + this.NoSpaceBeforeCloseBracket, this.NoSpaceAfterCloseBracket, + this.SpaceAfterSemicolon, + this.NoSpaceBeforeOpenParenInFuncDecl, + this.SpaceBetweenStatements, this.SpaceAfterTryFinally + ]; + + /// + /// Rules controlled by user options + /// + + // Insert space after comma delimiter + this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Insert space before and after binary operators + this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space)); + this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete)); + this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete)); + + // Insert space after keywords in control flow statements + this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Space)); + this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Delete)); + + // Open Brace braces after function + //TypeScript: Function can have return types, which can be made of tons of different token kinds + this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines); + + // Open Brace braces after TypeScript module/class/interface + this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines); + + // Open Brace braces after control block + this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines); + + // Insert space after semicolon in for statement + this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsForContext), RuleAction.Space)); + this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsForContext), RuleAction.Delete)); + + // Insert space after opening and before closing nonempty parenthesis + this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Insert space after function keyword for anonymous functions + this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); + this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete)); + } + + /// + /// Contexts + /// + + static IsForContext(context: FormattingContext): boolean { + return context.contextNode.kind() === SyntaxKind.ForStatement; + } + + static IsNotForContext(context: FormattingContext): boolean { + return !Rules.IsForContext(context); + } + + static IsBinaryOpContext(context: FormattingContext): boolean { + + switch (context.contextNode.kind()) { + // binary expressions + case SyntaxKind.AssignmentExpression: + case SyntaxKind.AddAssignmentExpression: + case SyntaxKind.SubtractAssignmentExpression: + case SyntaxKind.MultiplyAssignmentExpression: + case SyntaxKind.DivideAssignmentExpression: + case SyntaxKind.ModuloAssignmentExpression: + case SyntaxKind.AndAssignmentExpression: + case SyntaxKind.ExclusiveOrAssignmentExpression: + case SyntaxKind.OrAssignmentExpression: + case SyntaxKind.LeftShiftAssignmentExpression: + case SyntaxKind.SignedRightShiftAssignmentExpression: + case SyntaxKind.UnsignedRightShiftAssignmentExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.LogicalOrExpression: + case SyntaxKind.LogicalAndExpression: + case SyntaxKind.BitwiseOrExpression: + case SyntaxKind.BitwiseExclusiveOrExpression: + case SyntaxKind.BitwiseAndExpression: + case SyntaxKind.EqualsWithTypeConversionExpression: + case SyntaxKind.NotEqualsWithTypeConversionExpression: + case SyntaxKind.EqualsExpression: + case SyntaxKind.NotEqualsExpression: + case SyntaxKind.LessThanExpression: + case SyntaxKind.GreaterThanExpression: + case SyntaxKind.LessThanOrEqualExpression: + case SyntaxKind.GreaterThanOrEqualExpression: + case SyntaxKind.InstanceOfExpression: + case SyntaxKind.InExpression: + case SyntaxKind.LeftShiftExpression: + case SyntaxKind.SignedRightShiftExpression: + case SyntaxKind.UnsignedRightShiftExpression: + case SyntaxKind.MultiplyExpression: + case SyntaxKind.DivideExpression: + case SyntaxKind.ModuloExpression: + case SyntaxKind.AddExpression: + case SyntaxKind.SubtractExpression: + return true; + + // equal in import a = module('a'); + case SyntaxKind.ImportDeclaration: + // equal in var a = 0; + case SyntaxKind.VariableDeclarator: + case SyntaxKind.EqualsValueClause: + return context.currentTokenSpan.kind === SyntaxKind.EqualsToken || context.nextTokenSpan.kind === SyntaxKind.EqualsToken; + // "in" keyword in for (var x in []) { } + case SyntaxKind.ForInStatement: + return context.currentTokenSpan.kind === SyntaxKind.InKeyword || context.nextTokenSpan.kind === SyntaxKind.InKeyword; + } + return false; + } + + static IsNotBinaryOpContext(context: FormattingContext): boolean { + return !Rules.IsBinaryOpContext(context); + } + + static IsSameLineTokenOrBeforeMultilineBlockContext(context: FormattingContext): boolean { + //// This check is mainly used inside SpaceBeforeOpenBraceInControl and SpaceBeforeOpenBraceInFunction. + //// + //// Ex: + //// if (1) { .... + //// * ) and { are on the same line so apply the rule. Here we don't care whether it's same or multi block context + //// + //// Ex: + //// if (1) + //// { ... } + //// * ) and { are on differnet lines. We only need to format if the block is multiline context. So in this case we don't format. + //// + //// Ex: + //// if (1) + //// { ... + //// } + //// * ) and { are on differnet lines. We only need to format if the block is multiline context. So in this case we format. + + return context.TokensAreOnSameLine() || Rules.IsBeforeMultilineBlockContext(context); + } + + // This check is done before an open brace in a control construct, a function, or a typescript block declaration + static IsBeforeMultilineBlockContext(context: FormattingContext): boolean { + return Rules.IsBeforeBlockContext(context) && !(context.NextNodeAllOnSameLine() || context.NextNodeBlockIsOnOneLine()); + } + + static IsMultilineBlockContext(context: FormattingContext): boolean { + return Rules.IsBlockContext(context) && !(context.ContextNodeAllOnSameLine() || context.ContextNodeBlockIsOnOneLine()); + } + + static IsSingleLineBlockContext(context: FormattingContext): boolean { + return Rules.IsBlockContext(context) && (context.ContextNodeAllOnSameLine() || context.ContextNodeBlockIsOnOneLine()); + } + + static IsBlockContext(context: FormattingContext): boolean { + return Rules.NodeIsBlockContext(context.contextNode); + } + + static IsBeforeBlockContext(context: FormattingContext): boolean { + return Rules.NodeIsBlockContext(context.nextTokenParent); + } + + // IMPORTANT!!! This method must return true ONLY for nodes with open and close braces as immediate children + static NodeIsBlockContext(node: IndentationNodeContext): boolean { + if (Rules.NodeIsTypeScriptDeclWithBlockContext(node)) { + // This means we are in a context that looks like a block to the user, but in the grammar is actually not a node (it's a class, module, enum, object type literal, etc). + return true; + } + + switch (node.kind()) { + case SyntaxKind.Block: + case SyntaxKind.SwitchStatement: + case SyntaxKind.ObjectLiteralExpression: + return true; + } + + return false; + } + + static IsFunctionDeclContext(context: FormattingContext): boolean { + switch (context.contextNode.kind()) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MethodSignature: + case SyntaxKind.CallSignature: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.SimpleArrowFunctionExpression: + case SyntaxKind.ParenthesizedArrowFunctionExpression: + case SyntaxKind.InterfaceDeclaration: // This one is not truly a function, but for formatting purposes, it acts just like one + return true; + } + + return false; + } + + static IsTypeScriptDeclWithBlockContext(context: FormattingContext): boolean { + return Rules.NodeIsTypeScriptDeclWithBlockContext(context.contextNode); + } + + static NodeIsTypeScriptDeclWithBlockContext(node: IndentationNodeContext): boolean { + switch (node.kind()) { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ObjectType: + case SyntaxKind.ModuleDeclaration: + return true; + } + + return false; + } + + static IsAfterCodeBlockContext(context: FormattingContext): boolean { + switch (context.currentTokenParent.kind()) { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.Block: + case SyntaxKind.SwitchStatement: + return true; + } + return false; + } + + static IsControlDeclContext(context: FormattingContext): boolean { + switch (context.contextNode.kind()) { + case SyntaxKind.IfStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.TryStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.ElseClause: + case SyntaxKind.CatchClause: + case SyntaxKind.FinallyClause: + return true; + + default: + return false; + } + } + + static IsObjectContext(context: FormattingContext): boolean { + return context.contextNode.kind() === SyntaxKind.ObjectLiteralExpression; + } + + static IsFunctionCallContext(context: FormattingContext): boolean { + return context.contextNode.kind() === SyntaxKind.InvocationExpression; + } + + static IsNewContext(context: FormattingContext): boolean { + return context.contextNode.kind() === SyntaxKind.ObjectCreationExpression; + } + + static IsFunctionCallOrNewContext(context: FormattingContext): boolean { + return Rules.IsFunctionCallContext(context) || Rules.IsNewContext(context); + } + + static IsSameLineTokenContext(context: FormattingContext): boolean { + return context.TokensAreOnSameLine(); + } + + static IsNotFormatOnEnter(context: FormattingContext): boolean { + return context.formattingRequestKind != FormattingRequestKind.FormatOnEnter; + } + + static IsModuleDeclContext(context: FormattingContext): boolean { + return context.contextNode.kind() === SyntaxKind.ModuleDeclaration; + } + + static IsObjectTypeContext(context: FormattingContext): boolean { + return context.contextNode.kind() === SyntaxKind.ObjectType && context.contextNode.parent().kind() !== SyntaxKind.InterfaceDeclaration; + } + + static IsTypeArgumentOrParameter(tokenKind: SyntaxKind, parentKind: SyntaxKind): boolean { + return ((tokenKind === SyntaxKind.LessThanToken || tokenKind === SyntaxKind.GreaterThanToken) && + (parentKind === SyntaxKind.TypeParameterList || parentKind === SyntaxKind.TypeArgumentList)); + } + + static IsTypeArgumentOrParameterContext(context: FormattingContext): boolean { + return Rules.IsTypeArgumentOrParameter(context.currentTokenSpan.kind, context.currentTokenParent.kind()) || + Rules.IsTypeArgumentOrParameter(context.nextTokenSpan.kind, context.nextTokenParent.kind()); + } + + static IsVoidOpContext(context: FormattingContext): boolean { + return context.currentTokenSpan.kind === SyntaxKind.VoidKeyword && context.currentTokenParent.kind() === SyntaxKind.VoidExpression; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts new file mode 100644 index 00000000000..0fbe81d16c9 --- /dev/null +++ b/src/services/formatting/rulesMap.ts @@ -0,0 +1,189 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class RulesMap { + public map: RulesBucket[]; + public mapRowLength: number; + + constructor() { + this.map = []; + this.mapRowLength = 0; + } + + static create(rules: Rule[]): RulesMap { + var result = new RulesMap(); + result.Initialize(rules); + return result; + } + + public Initialize(rules: Rule[]) { + this.mapRowLength = SyntaxKind.LastToken + 1; + this.map = new Array(this.mapRowLength * this.mapRowLength);//new Array(this.mapRowLength * this.mapRowLength); + + // This array is used only during construction of the rulesbucket in the map + var rulesBucketConstructionStateList: RulesBucketConstructionState[] = new Array(this.map.length);//new Array(this.map.length); + + this.FillRules(rules, rulesBucketConstructionStateList); + return this.map; + } + + public FillRules(rules: Rule[], rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { + rules.forEach((rule) => { + this.FillRule(rule, rulesBucketConstructionStateList); + }); + } + + private GetRuleBucketIndex(row: number, column: number): number { + var rulesBucketIndex = (row * this.mapRowLength) + column; + //Debug.Assert(rulesBucketIndex < this.map.Length, "Trying to access an index outside the array."); + return rulesBucketIndex; + } + + private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { + var specificRule = rule.Descriptor.LeftTokenRange != Shared.TokenRange.Any && + rule.Descriptor.RightTokenRange != Shared.TokenRange.Any; + + rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => { + rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => { + var rulesBucketIndex = this.GetRuleBucketIndex(left, right); + + var rulesBucket = this.map[rulesBucketIndex]; + if (rulesBucket == undefined) { + rulesBucket = this.map[rulesBucketIndex] = new RulesBucket(); + } + + rulesBucket.AddRule(rule, specificRule, rulesBucketConstructionStateList, rulesBucketIndex); + }) + }) + } + + public GetRule(context: FormattingContext): Rule { + var bucketIndex = this.GetRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind); + var bucket = this.map[bucketIndex]; + if (bucket != null) { + for (var i = 0, len = bucket.Rules().length; i < len; i++) { + var rule = bucket.Rules()[i]; + if (rule.Operation.Context.InContext(context)) + return rule; + } + } + return null; + } + } + + var MaskBitSize = 5; + var Mask = 0x1f; + + export enum RulesPosition { + IgnoreRulesSpecific = 0, + IgnoreRulesAny = MaskBitSize * 1, + ContextRulesSpecific = MaskBitSize * 2, + ContextRulesAny = MaskBitSize * 3, + NoContextRulesSpecific = MaskBitSize * 4, + NoContextRulesAny = MaskBitSize * 5 + } + + export class RulesBucketConstructionState { + private rulesInsertionIndexBitmap: number; + + constructor() { + //// The Rules list contains all the inserted rules into a rulebucket in the following order: + //// 1- Ignore rules with specific token combination + //// 2- Ignore rules with any token combination + //// 3- Context rules with specific token combination + //// 4- Context rules with any token combination + //// 5- Non-context rules with specific token combination + //// 6- Non-context rules with any token combination + //// + //// The member rulesInsertionIndexBitmap is used to describe the number of rules + //// in each sub-bucket (above) hence can be used to know the index of where to insert + //// the next rule. It's a bitmap which contains 6 different sections each is given 5 bits. + //// + //// Example: + //// In order to insert a rule to the end of sub-bucket (3), we get the index by adding + //// the values in the bitmap segments 3rd, 2nd, and 1st. + this.rulesInsertionIndexBitmap = 0; + } + + public GetInsertionIndex(maskPosition: RulesPosition): number { + var index = 0; + + var pos = 0; + var indexBitmap = this.rulesInsertionIndexBitmap; + + while (pos <= maskPosition) { + index += (indexBitmap & Mask); + indexBitmap >>= MaskBitSize; + pos += MaskBitSize; + } + + return index; + } + + public IncreaseInsertionIndex(maskPosition: RulesPosition): void { + var value = (this.rulesInsertionIndexBitmap >> maskPosition) & Mask; + value++; + Debug.assert((value & Mask) == value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules."); + + var temp = this.rulesInsertionIndexBitmap & ~(Mask << maskPosition); + temp |= value << maskPosition; + + this.rulesInsertionIndexBitmap = temp; + } + } + + export class RulesBucket { + private rules: Rule[]; + + constructor() { + this.rules = []; + } + + public Rules(): Rule[] { + return this.rules; + } + + public AddRule(rule: Rule, specificTokens: boolean, constructionState: RulesBucketConstructionState[], rulesBucketIndex: number): void { + var position: RulesPosition; + + if (rule.Operation.Action == RuleAction.Ignore) { + position = specificTokens ? + RulesPosition.IgnoreRulesSpecific : + RulesPosition.IgnoreRulesAny; + } + else if (!rule.Operation.Context.IsAny()) { + position = specificTokens ? + RulesPosition.ContextRulesSpecific : + RulesPosition.ContextRulesAny; + } + else { + position = specificTokens ? + RulesPosition.NoContextRulesSpecific : + RulesPosition.NoContextRulesAny; + } + + var state = constructionState[rulesBucketIndex]; + if (state === undefined) { + state = constructionState[rulesBucketIndex] = new RulesBucketConstructionState(); + } + var index = state.GetInsertionIndex(position); + this.rules.splice(index, 0, rule); + state.IncreaseInsertionIndex(position); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/rulesProvider.ts b/src/services/formatting/rulesProvider.ts new file mode 100644 index 00000000000..608ee97254a --- /dev/null +++ b/src/services/formatting/rulesProvider.ts @@ -0,0 +1,117 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class RulesProvider { + private globalRules: Rules; + private options: ts.FormatCodeOptions; + private activeRules: Rule[]; + private rulesMap: RulesMap; + + constructor(private logger: TypeScript.Logger) { + this.globalRules = new Rules(); + } + + public getRuleName(rule: Rule): string { + return this.globalRules.getRuleName(rule); + } + + public getRuleByName(name: string): Rule { + return this.globalRules[name]; + } + + public getRulesMap() { + return this.rulesMap; + } + + public ensureUpToDate(options: ts.FormatCodeOptions) { + if (this.options == null || !ts.compareDataObjects(this.options, options)) { + var activeRules: Rule[] = TypeScript.timeFunction(this.logger, "RulesProvider: createActiveRules()", () => { return this.createActiveRules(options); }); + var rulesMap: RulesMap = TypeScript.timeFunction(this.logger, "RulesProvider: RulesMap.create()", () => { return RulesMap.create(activeRules); }); + + this.activeRules = activeRules; + this.rulesMap = rulesMap; + this.options = ts.clone(options); + } + } + + private createActiveRules(options: ts.FormatCodeOptions): Rule[] { + var rules = this.globalRules.HighPriorityCommonRules.slice(0); + + if (options.InsertSpaceAfterCommaDelimiter) { + rules.push(this.globalRules.SpaceAfterComma); + } + else { + rules.push(this.globalRules.NoSpaceAfterComma); + } + + if (options.InsertSpaceAfterFunctionKeywordForAnonymousFunctions) { + rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword); + } + else { + rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword); + } + + if (options.InsertSpaceAfterKeywordsInControlFlowStatements) { + rules.push(this.globalRules.SpaceAfterKeywordInControl); + } + else { + rules.push(this.globalRules.NoSpaceAfterKeywordInControl); + } + + if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) { + rules.push(this.globalRules.SpaceAfterOpenParen); + rules.push(this.globalRules.SpaceBeforeCloseParen); + rules.push(this.globalRules.NoSpaceBetweenParens); + } + else { + rules.push(this.globalRules.NoSpaceAfterOpenParen); + rules.push(this.globalRules.NoSpaceBeforeCloseParen); + rules.push(this.globalRules.NoSpaceBetweenParens); + } + + if (options.InsertSpaceAfterSemicolonInForStatements) { + rules.push(this.globalRules.SpaceAfterSemicolonInFor); + } + else { + rules.push(this.globalRules.NoSpaceAfterSemicolonInFor); + } + + if (options.InsertSpaceBeforeAndAfterBinaryOperators) { + rules.push(this.globalRules.SpaceBeforeBinaryOperator); + rules.push(this.globalRules.SpaceAfterBinaryOperator); + } + else { + rules.push(this.globalRules.NoSpaceBeforeBinaryOperator); + rules.push(this.globalRules.NoSpaceAfterBinaryOperator); + } + + if (options.PlaceOpenBraceOnNewLineForControlBlocks) { + rules.push(this.globalRules.NewLineBeforeOpenBraceInControl); + } + + if (options.PlaceOpenBraceOnNewLineForFunctions) { + rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction); + rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock); + } + + rules = rules.concat(this.globalRules.LowPriorityCommonRules); + + return rules; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/singleTokenIndenter.ts b/src/services/formatting/singleTokenIndenter.ts new file mode 100644 index 00000000000..896c2e8ac01 --- /dev/null +++ b/src/services/formatting/singleTokenIndenter.ts @@ -0,0 +1,46 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class SingleTokenIndenter extends IndentationTrackingWalker { + private indentationAmount: number = null; + private indentationPosition: number; + + constructor(indentationPosition: number, sourceUnit: SourceUnitSyntax, snapshot: ITextSnapshot, indentFirstToken: boolean, options: FormattingOptions) { + super(new TextSpan(indentationPosition, 1), sourceUnit, snapshot, indentFirstToken, options); + + this.indentationPosition = indentationPosition; + } + + public static getIndentationAmount(position: number, sourceUnit: SourceUnitSyntax, snapshot: ITextSnapshot, options: FormattingOptions): number { + var walker = new SingleTokenIndenter(position, sourceUnit, snapshot, true, options); + visitNodeOrToken(walker, sourceUnit); + return walker.indentationAmount; + } + + public indentToken(token: ISyntaxToken, indentationAmount: number, commentIndentationAmount: number): void { + // Compute an indentation string for this token + if (token.fullWidth() === 0 || (this.indentationPosition - this.position() < token.leadingTriviaWidth())) { + // The position is in the leading trivia, use comment indentation + this.indentationAmount = commentIndentationAmount; + } + else { + this.indentationAmount = indentationAmount; + } + } + } +} \ No newline at end of file diff --git a/src/services/formatting/snapshotPoint.ts b/src/services/formatting/snapshotPoint.ts new file mode 100644 index 00000000000..762748dbc70 --- /dev/null +++ b/src/services/formatting/snapshotPoint.ts @@ -0,0 +1,30 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + + export class SnapshotPoint { + constructor(public snapshot: ITextSnapshot, public position: number) { + } + public getContainingLine(): ITextSnapshotLine { + return this.snapshot.getLineFromPosition(this.position); + } + public add(offset: number): SnapshotPoint { + return new SnapshotPoint(this.snapshot, this.position + offset); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/textEditInfo.ts b/src/services/formatting/textEditInfo.ts new file mode 100644 index 00000000000..bdcbdd0ddc8 --- /dev/null +++ b/src/services/formatting/textEditInfo.ts @@ -0,0 +1,28 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export class TextEditInfo { + + constructor(public position: number, public length: number, public replaceWith: string) { + } + + public toString() { + return "[ position: " + this.position + ", length: " + this.length + ", replaceWith: '" + this.replaceWith + "' ]"; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/textSnapshot.ts b/src/services/formatting/textSnapshot.ts new file mode 100644 index 00000000000..c91ea7ae492 --- /dev/null +++ b/src/services/formatting/textSnapshot.ts @@ -0,0 +1,84 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export interface ITextSnapshot { + getText(span: TextSpan): string; + getLineNumberFromPosition(position: number): number; + getLineFromPosition(position: number): ITextSnapshotLine; + getLineFromLineNumber(lineNumber: number): ITextSnapshotLine; + } + + export class TextSnapshot implements ITextSnapshot { + private lines: TextSnapshotLine[]; + + constructor(private snapshot: ISimpleText) { + this.lines = []; + } + + public getText(span: TextSpan): string { + return this.snapshot.substr(span.start(), span.length()); + } + + public getLineNumberFromPosition(position: number): number { + return this.snapshot.lineMap().getLineNumberFromPosition(position); + } + + public getLineFromPosition(position: number): ITextSnapshotLine { + var lineNumber = this.getLineNumberFromPosition(position); + return this.getLineFromLineNumber(lineNumber); + } + + public getLineFromLineNumber(lineNumber: number): ITextSnapshotLine { + var line = this.lines[lineNumber]; + if (line === undefined) { + line = this.getLineFromLineNumberWorker(lineNumber); + this.lines[lineNumber] = line; + } + return line; + } + + private getLineFromLineNumberWorker(lineNumber: number): ITextSnapshotLine { + var lineMap = this.snapshot.lineMap().lineStarts(); + var lineMapIndex = lineNumber; //Note: lineMap is 0-based + if (lineMapIndex < 0 || lineMapIndex >= lineMap.length) + throw new Error(TypeScript.getDiagnosticMessage(TypeScript.DiagnosticCode.Invalid_line_number_0, [lineMapIndex])); + var start = lineMap[lineMapIndex]; + + var end: number; + var endIncludingLineBreak: number; + var lineBreak = ""; + if (lineMapIndex == lineMap.length) { + end = endIncludingLineBreak = this.snapshot.length(); + } + else { + endIncludingLineBreak = (lineMapIndex >= lineMap.length - 1 ? this.snapshot.length() : lineMap[lineMapIndex + 1]); + for (var p = endIncludingLineBreak - 1; p >= start; p--) { + var c = this.snapshot.substr(p, 1); + //TODO: Other ones? + if (c != "\r" && c != "\n") { + break; + } + } + end = p + 1; + lineBreak = this.snapshot.substr(end, endIncludingLineBreak - end); + } + var result = new TextSnapshotLine(this, lineNumber, start, end, lineBreak); + return result; + } + } +} \ No newline at end of file diff --git a/src/services/formatting/textSnapshotLine.ts b/src/services/formatting/textSnapshotLine.ts new file mode 100644 index 00000000000..df2d9eff237 --- /dev/null +++ b/src/services/formatting/textSnapshotLine.ts @@ -0,0 +1,80 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export interface ITextSnapshotLine { + snapshot(): ITextSnapshot; + + start(): SnapshotPoint; + startPosition(): number; + + end(): SnapshotPoint; + endPosition(): number; + + endIncludingLineBreak(): SnapshotPoint; + endIncludingLineBreakPosition(): number; + + length(): number; + lineNumber(): number; + getText(): string; + } + + export class TextSnapshotLine implements ITextSnapshotLine { + constructor(private _snapshot: ITextSnapshot, private _lineNumber: number, private _start: number, private _end: number, private _lineBreak: string) { + } + + public snapshot() { + return this._snapshot; + } + + public start() { + return new SnapshotPoint(this._snapshot, this._start); + } + + public startPosition() { + return this._start; + } + + public end() { + return new SnapshotPoint(this._snapshot, this._end); + } + + public endPosition() { + return this._end; + } + + public endIncludingLineBreak() { + return new SnapshotPoint(this._snapshot, this._end + this._lineBreak.length); + } + + public endIncludingLineBreakPosition() { + return this._end + this._lineBreak.length; + } + + public length() { + return this._end - this._start; + } + + public lineNumber() { + return this._lineNumber; + } + + public getText(): string { + return this._snapshot.getText(TextSpan.fromBounds(this._start, this._end)); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/tokenRange.ts b/src/services/formatting/tokenRange.ts new file mode 100644 index 00000000000..0c06f797025 --- /dev/null +++ b/src/services/formatting/tokenRange.ts @@ -0,0 +1,152 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services.Formatting { + export module Shared { + export interface ITokenAccess { + GetTokens(): SyntaxKind[]; + Contains(token: SyntaxKind): boolean; + } + + export class TokenRangeAccess implements ITokenAccess { + private tokens: SyntaxKind[]; + + constructor(from: SyntaxKind, to: SyntaxKind, except: SyntaxKind[]) { + this.tokens = []; + for (var token = from; token <= to; token++) { + if (except.indexOf(token) < 0) { + this.tokens.push(token); + } + } + } + + public GetTokens(): SyntaxKind[] { + return this.tokens; + } + + public Contains(token: SyntaxKind): boolean { + return this.tokens.indexOf(token) >= 0; + } + + + public toString(): string { + return "[tokenRangeStart=" + SyntaxKind[this.tokens[0]] + "," + + "tokenRangeEnd=" + SyntaxKind[this.tokens[this.tokens.length - 1]] + "]"; + } + } + + export class TokenValuesAccess implements ITokenAccess { + private tokens: SyntaxKind[]; + + constructor(tks: SyntaxKind[]) { + this.tokens = tks && tks.length ? tks : []; + } + + public GetTokens(): SyntaxKind[] { + return this.tokens; + } + + public Contains(token: SyntaxKind): boolean { + return this.tokens.indexOf(token) >= 0; + } + } + + export class TokenSingleValueAccess implements ITokenAccess { + constructor(public token: SyntaxKind) { + } + + public GetTokens(): SyntaxKind[] { + return [this.token]; + } + + public Contains(tokenValue: SyntaxKind): boolean { + return tokenValue == this.token; + } + + public toString(): string { + return "[singleTokenKind=" + SyntaxKind[this.token] + "]"; + } + } + + export class TokenAllAccess implements ITokenAccess { + public GetTokens(): SyntaxKind[] { + var result: SyntaxKind[] = []; + for (var token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) { + result.push(token); + } + return result; + } + + public Contains(tokenValue: SyntaxKind): boolean { + return true; + } + + public toString(): string { + return "[allTokens]"; + } + } + + export class TokenRange { + constructor(public tokenAccess: ITokenAccess) { + } + + static FromToken(token: SyntaxKind): TokenRange { + return new TokenRange(new TokenSingleValueAccess(token)); + } + + static FromTokens(tokens: SyntaxKind[]): TokenRange { + return new TokenRange(new TokenValuesAccess(tokens)); + } + + static FromRange(f: SyntaxKind, to: SyntaxKind, except: SyntaxKind[] = []): TokenRange { + return new TokenRange(new TokenRangeAccess(f, to, except)); + } + + static AllTokens(): TokenRange { + return new TokenRange(new TokenAllAccess()); + } + + public GetTokens(): SyntaxKind[] { + return this.tokenAccess.GetTokens(); + } + + public Contains(token: SyntaxKind): boolean { + return this.tokenAccess.Contains(token); + } + + public toString(): string { + return this.tokenAccess.toString(); + } + + static Any: TokenRange = TokenRange.AllTokens(); + static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia])); + static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword); + static Operators = TokenRange.FromRange(SyntaxKind.SemicolonToken, SyntaxKind.SlashEqualsToken); + static BinaryOperators = TokenRange.FromRange(SyntaxKind.LessThanToken, SyntaxKind.SlashEqualsToken); + static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword]); + static ReservedKeywords = TokenRange.FromRange(SyntaxKind.FirstFutureReservedStrictKeyword, SyntaxKind.LastFutureReservedStrictKeyword); + static UnaryPrefixOperators = TokenRange.FromTokens([SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]); + static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.IdentifierName, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]); + static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]); + static UnaryPostincrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]); + static UnaryPredecrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]); + static UnaryPostdecrementExpressions = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]); + static Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]); + static TypeNames = TokenRange.FromTokens([SyntaxKind.IdentifierName, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]); + } + } +} \ No newline at end of file diff --git a/src/services/formatting/tokenSpan.ts b/src/services/formatting/tokenSpan.ts new file mode 100644 index 00000000000..aba9c372f35 --- /dev/null +++ b/src/services/formatting/tokenSpan.ts @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + + +module TypeScript.Services.Formatting { + export class TokenSpan extends TextSpan { + constructor(public kind: SyntaxKind, start: number, length: number) { + super(start, length); + } + } +} \ No newline at end of file diff --git a/src/services/getScriptLexicalStructureWalker.ts b/src/services/getScriptLexicalStructureWalker.ts new file mode 100644 index 00000000000..b42f34fa927 --- /dev/null +++ b/src/services/getScriptLexicalStructureWalker.ts @@ -0,0 +1,355 @@ + +/// +module TypeScript.Services { + interface LexicalScope { + items: ts.Map; + itemNames: string[]; + childScopes: ts.Map; + childScopeNames: string[]; + } + + export class GetScriptLexicalStructureWalker extends TypeScript.SyntaxWalker { + private nameStack: string[] = []; + private kindStack: string[] = []; + + private parentScopes: LexicalScope[] = []; + private currentScope: LexicalScope; + + private createScope(): LexicalScope { + return { + items: TypeScript.createIntrinsicsObject(), + childScopes: TypeScript.createIntrinsicsObject(), + childScopeNames: [], + itemNames: [] + }; + } + + private pushNewContainerScope(containerName: string, kind: string): LexicalScope { + Debug.assert(containerName, "No scope name provided"); + + var key = kind + "+" + containerName; + this.nameStack.push(containerName); + this.kindStack.push(kind); + + var parentScope = this.currentScope; + this.parentScopes.push(parentScope); + + var scope = ts.lookUp(parentScope.childScopes, key); + if (!scope) { + scope = this.createScope() + parentScope.childScopes[key] = scope; + parentScope.childScopeNames.push(key); + } + + this.currentScope = scope; + return parentScope; + } + + private popScope() { + Debug.assert(this.parentScopes.length > 0, "No parent scopes to return to") + this.currentScope = this.parentScopes.pop(); + this.kindStack.pop(); + this.nameStack.pop(); + } + + constructor(private fileName: string) { + super(); + this.currentScope = this.createScope(); + } + + private collectItems(items: ts.NavigateToItem[], scope = this.currentScope) { + scope.itemNames.forEach(item => { + items.push(scope.items[item]); + }); + + scope.childScopeNames.forEach(childScope => { + this.collectItems(items, scope.childScopes[childScope]); + }); + } + + static getListsOfAllScriptLexicalStructure(items: ts.NavigateToItem[], fileName: string, unit: TypeScript.SourceUnitSyntax) { + var visitor = new GetScriptLexicalStructureWalker(fileName); + visitNodeOrToken(visitor, unit); + visitor.collectItems(items); + } + + private createItem(node: TypeScript.ISyntaxNode, modifiers: ISyntaxToken[], kind: string, name: string): void { + var key = kind + "+" + name; + + if (ts.lookUp(this.currentScope.items, key) !== undefined) { + this.addAdditionalSpan(node, key); + return; + } + + var item: ts.NavigateToItem = { + name: name, + kind: kind, + matchKind: ts.MatchKind.exact, + fileName: this.fileName, + kindModifiers: this.getKindModifiers(modifiers), + minChar: start(node), + limChar: end(node), + containerName: this.nameStack.join("."), + containerKind: this.kindStack.length === 0 ? "" : TypeScript.ArrayUtilities.last(this.kindStack), + }; + this.currentScope.items[key] = item; + this.currentScope.itemNames.push(key); + } + + private addAdditionalSpan( + node: TypeScript.ISyntaxNode, + key: string) { + + var item = ts.lookUp(this.currentScope.items, key); + Debug.assert(item !== undefined); + + var start = TypeScript.start(node); + var span: ts.SpanInfo = { + minChar: start, + limChar: start + width(node) + }; + + + if (item.additionalSpans) { + item.additionalSpans.push(span); + } + else { + item.additionalSpans = [span]; + } + } + + private getKindModifiers(modifiers: TypeScript.ISyntaxToken[]): string { + var result: string[] = []; + + for (var i = 0, n = modifiers.length; i < n; i++) { + result.push(modifiers[i].text()); + } + + return result.length > 0 ? result.join(',') : ts.ScriptElementKindModifier.none; + } + + public visitModuleDeclaration(node: TypeScript.ModuleDeclarationSyntax): void { + var names = this.getModuleNames(node); + this.visitModuleDeclarationWorker(node, names, 0); + } + + private visitModuleDeclarationWorker(node: TypeScript.ModuleDeclarationSyntax, names: string[], nameIndex: number): void { + if (nameIndex === names.length) { + // We're after all the module names, descend and process all children. + super.visitModuleDeclaration(node); + } + else { + var name = names[nameIndex]; + var kind = ts.ScriptElementKind.moduleElement; + + this.createItem(node, node.modifiers, kind, name); + + this.pushNewContainerScope(name, kind); + + this.visitModuleDeclarationWorker(node, names, nameIndex + 1); + + this.popScope(); + } + } + + private getModuleNames(node: TypeScript.ModuleDeclarationSyntax): string[] { + var result: string[] = []; + + if (node.stringLiteral) { + result.push(node.stringLiteral.text()); + } + else { + this.getModuleNamesHelper(node.name, result); + } + + return result; + } + + private getModuleNamesHelper(name: TypeScript.INameSyntax, result: string[]): void { + if (name.kind() === TypeScript.SyntaxKind.QualifiedName) { + var qualifiedName = name; + this.getModuleNamesHelper(qualifiedName.left, result); + result.push(qualifiedName.right.text()); + } + else { + result.push((name).text()); + } + } + + public visitClassDeclaration(node: TypeScript.ClassDeclarationSyntax): void { + var name = node.identifier.text(); + var kind = ts.ScriptElementKind.classElement; + + this.createItem(node, node.modifiers, kind, name); + + this.pushNewContainerScope(name, kind); + + super.visitClassDeclaration(node); + + this.popScope(); + } + + public visitInterfaceDeclaration(node: TypeScript.InterfaceDeclarationSyntax): void { + var name = node.identifier.text(); + var kind = ts.ScriptElementKind.interfaceElement; + + this.createItem(node, node.modifiers, kind, name); + + this.pushNewContainerScope(name, kind); + + super.visitInterfaceDeclaration(node); + + this.popScope(); + } + + public visitObjectType(node: TypeScript.ObjectTypeSyntax): void { + // Ignore an object type if we aren't inside an interface declaration. We don't want + // to add some random object type's members to the nav bar. + if (node.parent.kind() === SyntaxKind.InterfaceDeclaration) { + super.visitObjectType(node); + } + } + + public visitEnumDeclaration(node: TypeScript.EnumDeclarationSyntax): void { + var name = node.identifier.text(); + var kind = ts.ScriptElementKind.enumElement; + + this.createItem(node, node.modifiers, kind, name); + + this.pushNewContainerScope(name, kind); + + super.visitEnumDeclaration(node); + + this.popScope(); + } + + public visitConstructorDeclaration(node: TypeScript.ConstructorDeclarationSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.constructorImplementationElement, "constructor"); + + // Search the parameter list of class properties + var parameters = node.callSignature.parameterList.parameters; + if (parameters) { + for (var i = 0, n = parameters.length; i < n; i++) { + var parameter = parameters[i]; + + Debug.assert(parameter.kind() === SyntaxKind.Parameter); + + if (SyntaxUtilities.containsToken(parameter.modifiers, SyntaxKind.PublicKeyword) || + SyntaxUtilities.containsToken(parameter.modifiers, SyntaxKind.PrivateKeyword)) { + this.createItem(node, parameter.modifiers, ts.ScriptElementKind.memberVariableElement, parameter.identifier.text()); + } + } + } + + // No need to descend into a constructor; + } + + public visitMemberFunctionDeclaration(node: TypeScript.MemberFunctionDeclarationSyntax): void { + this.createItem(node, node.modifiers, ts.ScriptElementKind.memberFunctionElement, node.propertyName.text()); + + // No need to descend into a member function; + } + + public visitGetAccessor(node: TypeScript.GetAccessorSyntax): void { + this.createItem(node, node.modifiers, ts.ScriptElementKind.memberGetAccessorElement, node.propertyName.text()); + + // No need to descend into a member accessor; + } + + public visitSetAccessor(node: TypeScript.SetAccessorSyntax): void { + this.createItem(node, node.modifiers, ts.ScriptElementKind.memberSetAccessorElement, node.propertyName.text()); + + // No need to descend into a member accessor; + } + + public visitVariableDeclarator(node: TypeScript.VariableDeclaratorSyntax): void { + var modifiers = node.parent.kind() === SyntaxKind.MemberVariableDeclaration + ? (node.parent).modifiers + : TypeScript.Syntax.emptyList(); + var kind = node.parent.kind() === SyntaxKind.MemberVariableDeclaration + ? ts.ScriptElementKind.memberVariableElement + : ts.ScriptElementKind.variableElement; + this.createItem(node, modifiers, kind, node.propertyName.text()); + + // No need to descend into a variable declarator; + } + + public visitIndexSignature(node: TypeScript.IndexSignatureSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.indexSignatureElement, "[]"); + + // No need to descend into an index signature; + } + + public visitEnumElement(node: TypeScript.EnumElementSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.memberVariableElement, node.propertyName.text()); + + // No need to descend into an enum element; + } + + public visitCallSignature(node: TypeScript.CallSignatureSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.callSignatureElement, "()"); + + // No need to descend into a call signature; + } + + public visitConstructSignature(node: TypeScript.ConstructSignatureSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.constructSignatureElement, "new()"); + + // No need to descend into a construct signature; + } + + public visitMethodSignature(node: TypeScript.MethodSignatureSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.memberFunctionElement, node.propertyName.text()); + + // No need to descend into a method signature; + } + + public visitPropertySignature(node: TypeScript.PropertySignatureSyntax): void { + this.createItem(node, TypeScript.Syntax.emptyList(), ts.ScriptElementKind.memberVariableElement, node.propertyName.text()); + + // No need to descend into a property signature; + } + + public visitFunctionDeclaration(node: TypeScript.FunctionDeclarationSyntax): void { + // in the case of: + // declare function + // the parser will synthesize an identifier. + // we shouldn't add an unnamed function declaration + if (width(node.identifier) > 0) { + this.createItem(node, node.modifiers, ts.ScriptElementKind.functionElement, node.identifier.text()); + } + + // No need to descend into a function declaration; + } + + // Common statement types. Don't even bother walking into them as we'll never find anything + // inside that we'd put in the navbar. + + public visitBlock(node: TypeScript.BlockSyntax): void { + } + + public visitIfStatement(node: TypeScript.IfStatementSyntax): void { + } + + public visitExpressionStatement(node: TypeScript.ExpressionStatementSyntax): void { + } + + public visitThrowStatement(node: TypeScript.ThrowStatementSyntax): void { + } + + public visitReturnStatement(node: TypeScript.ReturnStatementSyntax): void { + } + + public visitSwitchStatement(node: TypeScript.SwitchStatementSyntax): void { + } + + public visitWithStatement(node: TypeScript.WithStatementSyntax): void { + } + + public visitTryStatement(node: TypeScript.TryStatementSyntax): void { + } + + public visitLabeledStatement(node: TypeScript.LabeledStatementSyntax): void { + } + } +} \ No newline at end of file diff --git a/src/services/indentation.ts b/src/services/indentation.ts new file mode 100644 index 00000000000..b067e817d99 --- /dev/null +++ b/src/services/indentation.ts @@ -0,0 +1,155 @@ +/// + +module TypeScript.Indentation { + export function columnForEndOfTokenAtPosition(syntaxTree: SyntaxTree, position: number, options: FormattingOptions): number { + var token = findToken(syntaxTree.sourceUnit(), position); + return columnForStartOfTokenAtPosition(syntaxTree, position, options) + width(token); + } + + export function columnForStartOfTokenAtPosition(syntaxTree: SyntaxTree, position: number, options: FormattingOptions): number { + var token = findToken(syntaxTree.sourceUnit(), position); + + // Walk backward from this token until we find the first token in the line. For each token + // we see (that is not the first tokem in line), push the entirety of the text into the text + // array. Then, for the first token, add its text (without its leading trivia) to the text + // array. i.e. if we have: + // + // var foo = a => bar(); + // + // And we want the column for the start of 'bar', then we'll add the underlinded portions to + // the text array: + // + // var foo = a => bar(); + // _ + // __ + // __ + // ____ + // ____ + var firstTokenInLine = Syntax.firstTokenInLineContainingPosition(syntaxTree, token.fullStart()); + var leadingTextInReverse: string[] = []; + + var current = token; + while (current !== firstTokenInLine) { + current = previousToken(current); + + if (current === firstTokenInLine) { + // We're at the first token in teh line. + // We don't want the leading trivia for this token. That will be taken care of in + // columnForFirstNonWhitespaceCharacterInLine. So just push the trailing trivia + // and then the token text. + leadingTextInReverse.push(current.trailingTrivia().fullText()); + leadingTextInReverse.push(current.text()); + } + else { + // We're at an intermediate token on the line. Just push all its text into the array. + leadingTextInReverse.push(current.fullText()); + } + } + + // Now, add all trivia to the start of the line on the first token in the list. + collectLeadingTriviaTextToStartOfLine(firstTokenInLine, leadingTextInReverse); + + return columnForLeadingTextInReverse(leadingTextInReverse, options); + } + + export function columnForStartOfFirstTokenInLineContainingPosition(syntaxTree: SyntaxTree, position: number, options: FormattingOptions): number { + // Walk backward through the tokens until we find the first one on the line. + var firstTokenInLine = Syntax.firstTokenInLineContainingPosition(syntaxTree, position); + var leadingTextInReverse: string[] = []; + + // Now, add all trivia to the start of the line on the first token in the list. + collectLeadingTriviaTextToStartOfLine(firstTokenInLine, leadingTextInReverse); + + return columnForLeadingTextInReverse(leadingTextInReverse, options); + } + + // Collect all the trivia that precedes this token. Stopping when we hit a newline trivia + // or a multiline comment that spans multiple lines. This is meant to be called on the first + // token in a line. + function collectLeadingTriviaTextToStartOfLine(firstTokenInLine: ISyntaxToken, + leadingTextInReverse: string[]) { + var leadingTrivia = firstTokenInLine.leadingTrivia(); + + for (var i = leadingTrivia.count() - 1; i >= 0; i--) { + var trivia = leadingTrivia.syntaxTriviaAt(i); + if (trivia.kind() === SyntaxKind.NewLineTrivia) { + break; + } + + if (trivia.kind() === SyntaxKind.MultiLineCommentTrivia) { + var lineSegments = Syntax.splitMultiLineCommentTriviaIntoMultipleLines(trivia); + leadingTextInReverse.push(ArrayUtilities.last(lineSegments)); + + if (lineSegments.length > 0) { + // This multiline comment actually spanned multiple lines. So we're done. + break; + } + + // It was only on a single line, so keep on going. + } + + leadingTextInReverse.push(trivia.fullText()); + } + } + + function columnForLeadingTextInReverse(leadingTextInReverse: string[], + options: FormattingOptions): number { + var column = 0; + + // walk backwards. This means we're actually walking forward from column 0 to the start of + // the token. + for (var i = leadingTextInReverse.length - 1; i >= 0; i--) { + var text = leadingTextInReverse[i]; + column = columnForPositionInStringWorker(text, text.length, column, options); + } + + return column; + } + + // Returns the column that this input string ends at (assuming it starts at column 0). + export function columnForPositionInString(input: string, position: number, options: FormattingOptions): number { + return columnForPositionInStringWorker(input, position, 0, options); + } + + function columnForPositionInStringWorker(input: string, position: number, startColumn: number, options: FormattingOptions): number { + var column = startColumn; + var spacesPerTab = options.spacesPerTab; + + for (var j = 0; j < position; j++) { + var ch = input.charCodeAt(j); + + if (ch === CharacterCodes.tab) { + column += spacesPerTab - column % spacesPerTab; + } + else { + column++; + } + } + + return column; + } + + export function indentationString(column: number, options: FormattingOptions): string { + var numberOfTabs = 0; + var numberOfSpaces = Math.max(0, column); + + if (options.useTabs) { + numberOfTabs = Math.floor(column / options.spacesPerTab); + numberOfSpaces -= numberOfTabs * options.spacesPerTab; + } + + return StringUtilities.repeat('\t', numberOfTabs) + + StringUtilities.repeat(' ', numberOfSpaces); + } + + export function firstNonWhitespacePosition(value: string): number { + for (var i = 0; i < value.length; i++) { + var ch = value.charCodeAt(i); + if (!CharacterInfo.isWhitespace(ch)) { + return i; + } + } + + return value.length; + } +} \ No newline at end of file diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts new file mode 100644 index 00000000000..8a873a8774d --- /dev/null +++ b/src/services/outliningElementsCollector.ts @@ -0,0 +1,109 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +module TypeScript.Services { + export class OutliningElementsCollector extends TypeScript.DepthLimitedWalker { + // The maximum depth for collecting spans; this will cause us to miss deeply nested function/modules spans, + // but will guarantee performance will not be closely tied to tree depth. + private static MaximumDepth: number = 10; + private inObjectLiteralExpression: boolean = false; + + private elements: TypeScript.TextSpan[] = []; + + constructor() { + super(OutliningElementsCollector.MaximumDepth); + } + + public visitClassDeclaration(node: TypeScript.ClassDeclarationSyntax): void { + this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken); + super.visitClassDeclaration(node); + } + + public visitInterfaceDeclaration(node: TypeScript.InterfaceDeclarationSyntax): void { + this.addOutlineRange(node, node.body.openBraceToken, node.body.closeBraceToken); + super.visitInterfaceDeclaration(node); + } + + public visitModuleDeclaration(node: TypeScript.ModuleDeclarationSyntax): void { + this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken); + super.visitModuleDeclaration(node); + } + + public visitEnumDeclaration(node: TypeScript.EnumDeclarationSyntax): void { + this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken); + super.visitEnumDeclaration(node); + } + + public visitFunctionDeclaration(node: TypeScript.FunctionDeclarationSyntax): void { + this.addOutlineRange(node, node.block, node.block); + super.visitFunctionDeclaration(node); + } + + public visitFunctionExpression(node: TypeScript.FunctionExpressionSyntax): void { + this.addOutlineRange(node, node.block, node.block); + super.visitFunctionExpression(node); + } + + public visitConstructorDeclaration(node: TypeScript.ConstructorDeclarationSyntax): void { + this.addOutlineRange(node, node.block, node.block); + super.visitConstructorDeclaration(node); + } + + public visitMemberFunctionDeclaration(node: TypeScript.MemberFunctionDeclarationSyntax): void { + this.addOutlineRange(node, node.block, node.block); + super.visitMemberFunctionDeclaration(node); + } + + public visitGetAccessor(node: TypeScript.GetAccessorSyntax): void { + if (!this.inObjectLiteralExpression) { + this.addOutlineRange(node, node.block, node.block); + } + super.visitGetAccessor(node); + } + + public visitSetAccessor(node: TypeScript.SetAccessorSyntax): void { + if (!this.inObjectLiteralExpression) { + this.addOutlineRange(node, node.block, node.block); + } + super.visitSetAccessor(node); + } + + public visitObjectLiteralExpression(node: TypeScript.ObjectLiteralExpressionSyntax): void { + var savedInObjectLiteralExpression = this.inObjectLiteralExpression; + this.inObjectLiteralExpression = true; + super.visitObjectLiteralExpression(node); + this.inObjectLiteralExpression = savedInObjectLiteralExpression; + } + + private addOutlineRange(node: TypeScript.ISyntaxNode, startElement: TypeScript.ISyntaxNodeOrToken, endElement: TypeScript.ISyntaxNodeOrToken) { + if (startElement && endElement && !isShared(startElement) && !isShared(endElement)) { + // Compute the position + var start = TypeScript.start(startElement); + var end = TypeScript.end(endElement); + + // Push the new range + this.elements.push(TypeScript.TextSpan.fromBounds(start, end)); + } + } + + public static collectElements(node: TypeScript.SourceUnitSyntax): TypeScript.TextSpan[] { + var collector = new OutliningElementsCollector(); + visitNodeOrToken(collector, node); + return collector.elements; + } + } +} diff --git a/src/services/references.ts b/src/services/references.ts new file mode 100644 index 00000000000..f1f4245c122 --- /dev/null +++ b/src/services/references.ts @@ -0,0 +1,26 @@ +///// +///// + +//// document.ts depends on incrementalParser.ts being run first. +///// +///// + +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// +///// \ No newline at end of file diff --git a/src/services/resources/diagnosticCode.generated.ts b/src/services/resources/diagnosticCode.generated.ts new file mode 100644 index 00000000000..26df20b428d --- /dev/null +++ b/src/services/resources/diagnosticCode.generated.ts @@ -0,0 +1,445 @@ +// +module TypeScript { + export var DiagnosticCode = { + error_TS_0_1: "error TS{0}: {1}", + warning_TS_0_1: "warning TS{0}: {1}", + Unrecognized_escape_sequence: "Unrecognized escape sequence.", + Unexpected_character_0: "Unexpected character {0}.", + Missing_close_quote_character: "Missing close quote character.", + Identifier_expected: "Identifier expected.", + _0_keyword_expected: "'{0}' keyword expected.", + _0_expected: "'{0}' expected.", + Identifier_expected_0_is_a_keyword: "Identifier expected; '{0}' is a keyword.", + Automatic_semicolon_insertion_not_allowed: "Automatic semicolon insertion not allowed.", + Unexpected_token_0_expected: "Unexpected token; '{0}' expected.", + Trailing_comma_not_allowed: "Trailing comma not allowed.", + AsteriskSlash_expected: "'*/' expected.", + public_or_private_modifier_must_precede_static: "'public' or 'private' modifier must precede 'static'.", + Unexpected_token: "Unexpected token.", + Catch_clause_parameter_cannot_have_a_type_annotation: "Catch clause parameter cannot have a type annotation.", + A_rest_parameter_must_be_last_in_a_parameter_list: "A rest parameter must be last in a parameter list.", + Parameter_cannot_have_question_mark_and_initializer: "Parameter cannot have question mark and initializer.", + A_required_parameter_cannot_follow_an_optional_parameter: "A required parameter cannot follow an optional parameter.", + Index_signatures_cannot_have_rest_parameters: "Index signatures cannot have rest parameters.", + Index_signature_parameter_cannot_have_accessibility_modifiers: "Index signature parameter cannot have accessibility modifiers.", + Index_signature_parameter_cannot_have_a_question_mark: "Index signature parameter cannot have a question mark.", + Index_signature_parameter_cannot_have_an_initializer: "Index signature parameter cannot have an initializer.", + Index_signature_must_have_a_type_annotation: "Index signature must have a type annotation.", + Index_signature_parameter_must_have_a_type_annotation: "Index signature parameter must have a type annotation.", + Index_signature_parameter_type_must_be_string_or_number: "Index signature parameter type must be 'string' or 'number'.", + extends_clause_already_seen: "'extends' clause already seen.", + extends_clause_must_precede_implements_clause: "'extends' clause must precede 'implements' clause.", + Classes_can_only_extend_a_single_class: "Classes can only extend a single class.", + implements_clause_already_seen: "'implements' clause already seen.", + Accessibility_modifier_already_seen: "Accessibility modifier already seen.", + _0_modifier_must_precede_1_modifier: "'{0}' modifier must precede '{1}' modifier.", + _0_modifier_already_seen: "'{0}' modifier already seen.", + _0_modifier_cannot_appear_on_a_class_element: "'{0}' modifier cannot appear on a class element.", + Interface_declaration_cannot_have_implements_clause: "Interface declaration cannot have 'implements' clause.", + super_invocation_cannot_have_type_arguments: "'super' invocation cannot have type arguments.", + Only_ambient_modules_can_use_quoted_names: "Only ambient modules can use quoted names.", + Statements_are_not_allowed_in_ambient_contexts: "Statements are not allowed in ambient contexts.", + A_function_implementation_cannot_be_declared_in_an_ambient_context: "A function implementation cannot be declared in an ambient context.", + A_declare_modifier_cannot_be_used_in_an_already_ambient_context: "A 'declare' modifier cannot be used in an already ambient context.", + Initializers_are_not_allowed_in_ambient_contexts: "Initializers are not allowed in ambient contexts.", + _0_modifier_cannot_appear_on_a_module_element: "'{0}' modifier cannot appear on a module element.", + A_declare_modifier_cannot_be_used_with_an_interface_declaration: "A 'declare' modifier cannot be used with an interface declaration.", + A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file: "A 'declare' modifier is required for a top level declaration in a .d.ts file.", + A_rest_parameter_cannot_be_optional: "A rest parameter cannot be optional.", + A_rest_parameter_cannot_have_an_initializer: "A rest parameter cannot have an initializer.", + set_accessor_must_have_exactly_one_parameter: "'set' accessor must have exactly one parameter.", + set_accessor_parameter_cannot_be_optional: "'set' accessor parameter cannot be optional.", + set_accessor_parameter_cannot_have_an_initializer: "'set' accessor parameter cannot have an initializer.", + set_accessor_cannot_have_rest_parameter: "'set' accessor cannot have rest parameter.", + get_accessor_cannot_have_parameters: "'get' accessor cannot have parameters.", + Modifiers_cannot_appear_here: "Modifiers cannot appear here.", + Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher: "Accessors are only available when targeting ECMAScript 5 and higher.", + Enum_member_must_have_initializer: "Enum member must have initializer.", + Export_assignment_cannot_be_used_in_internal_modules: "Export assignment cannot be used in internal modules.", + Ambient_enum_elements_can_only_have_integer_literal_initializers: "Ambient enum elements can only have integer literal initializers.", + module_class_interface_enum_import_or_statement: "module, class, interface, enum, import or statement", + constructor_function_accessor_or_variable: "constructor, function, accessor or variable", + statement: "statement", + case_or_default_clause: "case or default clause", + identifier: "identifier", + call_construct_index_property_or_function_signature: "call, construct, index, property or function signature", + expression: "expression", + type_name: "type name", + property_or_accessor: "property or accessor", + parameter: "parameter", + type: "type", + type_parameter: "type parameter", + A_declare_modifier_cannot_be_used_with_an_import_declaration: "A 'declare' modifier cannot be used with an import declaration.", + Invalid_reference_directive_syntax: "Invalid 'reference' directive syntax.", + Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher: "Octal literals are not available when targeting ECMAScript 5 and higher.", + Accessors_are_not_allowed_in_ambient_contexts: "Accessors are not allowed in ambient contexts.", + _0_modifier_cannot_appear_on_a_constructor_declaration: "'{0}' modifier cannot appear on a constructor declaration.", + _0_modifier_cannot_appear_on_a_parameter: "'{0}' modifier cannot appear on a parameter.", + Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement: "Only a single variable declaration is allowed in a 'for...in' statement.", + Type_parameters_cannot_appear_on_a_constructor_declaration: "Type parameters cannot appear on a constructor declaration.", + Type_annotation_cannot_appear_on_a_constructor_declaration: "Type annotation cannot appear on a constructor declaration.", + Type_parameters_cannot_appear_on_an_accessor: "Type parameters cannot appear on an accessor.", + Type_annotation_cannot_appear_on_a_set_accessor: "Type annotation cannot appear on a 'set' accessor.", + Index_signature_must_have_exactly_one_parameter: "Index signature must have exactly one parameter.", + _0_list_cannot_be_empty: "'{0}' list cannot be empty.", + variable_declaration: "variable declaration", + type_argument: "type argument", + Invalid_use_of_0_in_strict_mode: "Invalid use of '{0}' in strict mode.", + with_statements_are_not_allowed_in_strict_mode: "'with' statements are not allowed in strict mode.", + delete_cannot_be_called_on_an_identifier_in_strict_mode: "'delete' cannot be called on an identifier in strict mode.", + Invalid_left_hand_side_in_for_in_statement: "Invalid left-hand side in 'for...in' statement.", + continue_statement_can_only_be_used_within_an_enclosing_iteration_statement: "'continue' statement can only be used within an enclosing iteration statement.", + break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement: "'break' statement can only be used within an enclosing iteration or switch statement.", + Jump_target_not_found: "Jump target not found.", + Jump_target_cannot_cross_function_boundary: "Jump target cannot cross function boundary.", + return_statement_must_be_contained_within_a_function_body: "'return' statement must be contained within a function body.", + Expression_expected: "Expression expected.", + Type_expected: "Type expected.", + Duplicate_identifier_0: "Duplicate identifier '{0}'.", + The_name_0_does_not_exist_in_the_current_scope: "The name '{0}' does not exist in the current scope.", + The_name_0_does_not_refer_to_a_value: "The name '{0}' does not refer to a value.", + super_can_only_be_used_inside_a_class_instance_method: "'super' can only be used inside a class instance method.", + The_left_hand_side_of_an_assignment_expression_must_be_a_variable_property_or_indexer: "The left-hand side of an assignment expression must be a variable, property or indexer.", + Value_of_type_0_is_not_callable_Did_you_mean_to_include_new: "Value of type '{0}' is not callable. Did you mean to include 'new'?", + Value_of_type_0_is_not_callable: "Value of type '{0}' is not callable.", + Value_of_type_0_is_not_newable: "Value of type '{0}' is not newable.", + An_index_expression_argument_must_be_string_number_or_any: "An index expression argument must be 'string', 'number', or 'any'.", + Operator_0_cannot_be_applied_to_types_1_and_2: "Operator '{0}' cannot be applied to types '{1}' and '{2}'.", + Type_0_is_not_assignable_to_type_1: "Type '{0}' is not assignable to type '{1}'.", + Type_0_is_not_assignable_to_type_1_NL_2: "Type '{0}' is not assignable to type '{1}':{NL}{2}", + Expected_var_class_interface_or_module: "Expected var, class, interface, or module.", + Getter_0_already_declared: "Getter '{0}' already declared.", + Setter_0_already_declared: "Setter '{0}' already declared.", + Exported_class_0_extends_private_class_1: "Exported class '{0}' extends private class '{1}'.", + Exported_class_0_implements_private_interface_1: "Exported class '{0}' implements private interface '{1}'.", + Exported_interface_0_extends_private_interface_1: "Exported interface '{0}' extends private interface '{1}'.", + Exported_class_0_extends_class_from_inaccessible_module_1: "Exported class '{0}' extends class from inaccessible module {1}.", + Exported_class_0_implements_interface_from_inaccessible_module_1: "Exported class '{0}' implements interface from inaccessible module {1}.", + Exported_interface_0_extends_interface_from_inaccessible_module_1: "Exported interface '{0}' extends interface from inaccessible module {1}.", + Public_static_property_0_of_exported_class_has_or_is_using_private_type_1: "Public static property '{0}' of exported class has or is using private type '{1}'.", + Public_property_0_of_exported_class_has_or_is_using_private_type_1: "Public property '{0}' of exported class has or is using private type '{1}'.", + Property_0_of_exported_interface_has_or_is_using_private_type_1: "Property '{0}' of exported interface has or is using private type '{1}'.", + Exported_variable_0_has_or_is_using_private_type_1: "Exported variable '{0}' has or is using private type '{1}'.", + Public_static_property_0_of_exported_class_is_using_inaccessible_module_1: "Public static property '{0}' of exported class is using inaccessible module {1}.", + Public_property_0_of_exported_class_is_using_inaccessible_module_1: "Public property '{0}' of exported class is using inaccessible module {1}.", + Property_0_of_exported_interface_is_using_inaccessible_module_1: "Property '{0}' of exported interface is using inaccessible module {1}.", + Exported_variable_0_is_using_inaccessible_module_1: "Exported variable '{0}' is using inaccessible module {1}.", + Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_type_1: "Parameter '{0}' of constructor from exported class has or is using private type '{1}'.", + Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_type_1: "Parameter '{0}' of public static property setter from exported class has or is using private type '{1}'.", + Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_private_type_1: "Parameter '{0}' of public property setter from exported class has or is using private type '{1}'.", + Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_type_1: "Parameter '{0}' of constructor signature from exported interface has or is using private type '{1}'.", + Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_type_1: "Parameter '{0}' of call signature from exported interface has or is using private type '{1}'.", + Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_type_1: "Parameter '{0}' of public static method from exported class has or is using private type '{1}'.", + Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_type_1: "Parameter '{0}' of public method from exported class has or is using private type '{1}'.", + Parameter_0_of_method_from_exported_interface_has_or_is_using_private_type_1: "Parameter '{0}' of method from exported interface has or is using private type '{1}'.", + Parameter_0_of_exported_function_has_or_is_using_private_type_1: "Parameter '{0}' of exported function has or is using private type '{1}'.", + Parameter_0_of_constructor_from_exported_class_is_using_inaccessible_module_1: "Parameter '{0}' of constructor from exported class is using inaccessible module {1}.", + Parameter_0_of_public_static_property_setter_from_exported_class_is_using_inaccessible_module_1: "Parameter '{0}' of public static property setter from exported class is using inaccessible module {1}.", + Parameter_0_of_public_property_setter_from_exported_class_is_using_inaccessible_module_1: "Parameter '{0}' of public property setter from exported class is using inaccessible module {1}.", + Parameter_0_of_constructor_signature_from_exported_interface_is_using_inaccessible_module_1: "Parameter '{0}' of constructor signature from exported interface is using inaccessible module {1}.", + Parameter_0_of_call_signature_from_exported_interface_is_using_inaccessible_module_1: "Parameter '{0}' of call signature from exported interface is using inaccessible module {1}", + Parameter_0_of_public_static_method_from_exported_class_is_using_inaccessible_module_1: "Parameter '{0}' of public static method from exported class is using inaccessible module {1}.", + Parameter_0_of_public_method_from_exported_class_is_using_inaccessible_module_1: "Parameter '{0}' of public method from exported class is using inaccessible module {1}.", + Parameter_0_of_method_from_exported_interface_is_using_inaccessible_module_1: "Parameter '{0}' of method from exported interface is using inaccessible module {1}.", + Parameter_0_of_exported_function_is_using_inaccessible_module_1: "Parameter '{0}' of exported function is using inaccessible module {1}.", + Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_private_type_0: "Return type of public static property getter from exported class has or is using private type '{0}'.", + Return_type_of_public_property_getter_from_exported_class_has_or_is_using_private_type_0: "Return type of public property getter from exported class has or is using private type '{0}'.", + Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_type_0: "Return type of constructor signature from exported interface has or is using private type '{0}'.", + Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_type_0: "Return type of call signature from exported interface has or is using private type '{0}'.", + Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_type_0: "Return type of index signature from exported interface has or is using private type '{0}'.", + Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_type_0: "Return type of public static method from exported class has or is using private type '{0}'.", + Return_type_of_public_method_from_exported_class_has_or_is_using_private_type_0: "Return type of public method from exported class has or is using private type '{0}'.", + Return_type_of_method_from_exported_interface_has_or_is_using_private_type_0: "Return type of method from exported interface has or is using private type '{0}'.", + Return_type_of_exported_function_has_or_is_using_private_type_0: "Return type of exported function has or is using private type '{0}'.", + Return_type_of_public_static_property_getter_from_exported_class_is_using_inaccessible_module_0: "Return type of public static property getter from exported class is using inaccessible module {0}.", + Return_type_of_public_property_getter_from_exported_class_is_using_inaccessible_module_0: "Return type of public property getter from exported class is using inaccessible module {0}.", + Return_type_of_constructor_signature_from_exported_interface_is_using_inaccessible_module_0: "Return type of constructor signature from exported interface is using inaccessible module {0}.", + Return_type_of_call_signature_from_exported_interface_is_using_inaccessible_module_0: "Return type of call signature from exported interface is using inaccessible module {0}.", + Return_type_of_index_signature_from_exported_interface_is_using_inaccessible_module_0: "Return type of index signature from exported interface is using inaccessible module {0}.", + Return_type_of_public_static_method_from_exported_class_is_using_inaccessible_module_0: "Return type of public static method from exported class is using inaccessible module {0}.", + Return_type_of_public_method_from_exported_class_is_using_inaccessible_module_0: "Return type of public method from exported class is using inaccessible module {0}.", + Return_type_of_method_from_exported_interface_is_using_inaccessible_module_0: "Return type of method from exported interface is using inaccessible module {0}.", + Return_type_of_exported_function_is_using_inaccessible_module_0: "Return type of exported function is using inaccessible module {0}.", + new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead: "'new T[]' cannot be used to create an array. Use 'new Array()' instead.", + A_parameter_list_must_follow_a_generic_type_argument_list_expected: "A parameter list must follow a generic type argument list. '(' expected.", + Multiple_constructor_implementations_are_not_allowed: "Multiple constructor implementations are not allowed.", + Cannot_find_external_module_0: "Cannot find external module '{0}'.", + Module_cannot_be_aliased_to_a_non_module_type: "Module cannot be aliased to a non-module type.", + A_class_may_only_extend_another_class: "A class may only extend another class.", + A_class_may_only_implement_another_class_or_interface: "A class may only implement another class or interface.", + An_interface_may_only_extend_a_class_or_another_interface: "An interface may only extend a class or another interface.", + Unable_to_resolve_type: "Unable to resolve type.", + Unable_to_resolve_type_of_0: "Unable to resolve type of '{0}'.", + Unable_to_resolve_type_parameter_constraint: "Unable to resolve type parameter constraint.", + Type_parameter_constraint_cannot_be_a_primitive_type: "Type parameter constraint cannot be a primitive type.", + Supplied_parameters_do_not_match_any_signature_of_call_target: "Supplied parameters do not match any signature of call target.", + Supplied_parameters_do_not_match_any_signature_of_call_target_NL_0: "Supplied parameters do not match any signature of call target:{NL}{0}", + Cannot_use_new_with_an_expression_whose_type_lacks_a_signature: "Cannot use 'new' with an expression whose type lacks a signature.", + Only_a_void_function_can_be_called_with_the_new_keyword: "Only a void function can be called with the 'new' keyword.", + Could_not_select_overload_for_new_expression: "Could not select overload for 'new' expression.", + Type_0_does_not_satisfy_the_constraint_1: "Type '{0}' does not satisfy the constraint '{1}'.", + Could_not_select_overload_for_call_expression: "Could not select overload for 'call' expression.", + Cannot_invoke_an_expression_whose_type_lacks_a_call_signature: "Cannot invoke an expression whose type lacks a call signature.", + Calls_to_super_are_only_valid_inside_a_class: "Calls to 'super' are only valid inside a class.", + Generic_type_0_requires_1_type_argument_s: "Generic type '{0}' requires {1} type argument(s).", + Type_of_array_literal_cannot_be_determined_Best_common_type_could_not_be_found_for_array_elements: "Type of array literal cannot be determined. Best common type could not be found for array elements.", + Could_not_find_enclosing_symbol_for_dotted_name_0: "Could not find enclosing symbol for dotted name '{0}'.", + Property_0_does_not_exist_on_value_of_type_1: "Property '{0}' does not exist on value of type '{1}'.", + Cannot_find_name_0: "Cannot find name '{0}'.", + get_and_set_accessor_must_have_the_same_type: "'get' and 'set' accessor must have the same type.", + this_cannot_be_referenced_in_current_location: "'this' cannot be referenced in current location.", + Static_members_cannot_reference_class_type_parameters: "Static members cannot reference class type parameters.", + Type_0_recursively_references_itself_as_a_base_type: "Type '{0}' recursively references itself as a base type.", + super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class: "'super' property access is permitted only in a constructor, member function, or member accessor of a derived class.", + super_can_only_be_referenced_in_a_derived_class: "'super' can only be referenced in a derived class.", + A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties: "A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties.", + Constructors_for_derived_classes_must_contain_a_super_call: "Constructors for derived classes must contain a 'super' call.", + Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors: "Super calls are not permitted outside constructors or in nested functions inside constructors.", + _0_1_is_inaccessible: "'{0}.{1}' is inaccessible.", + this_cannot_be_referenced_in_a_module_body: "'this' cannot be referenced in a module body.", + Invalid_expression_types_not_known_to_support_the_addition_operator: "Invalid '+' expression - types not known to support the addition operator.", + The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.", + The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: "The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.", + An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type: "An arithmetic operand must be of type 'any', 'number' or an enum type.", + Variable_declarations_of_a_for_statement_cannot_use_a_type_annotation: "Variable declarations of a 'for' statement cannot use a type annotation.", + Variable_declarations_of_a_for_statement_must_be_of_types_string_or_any: "Variable declarations of a 'for' statement must be of types 'string' or 'any'.", + The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter: "The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter.", + The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number: "The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.", + The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: "The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter.", + The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.", + The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type: "The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type.", + Setters_cannot_return_a_value: "Setters cannot return a value.", + Tried_to_query_type_of_uninitialized_module_0: "Tried to query type of uninitialized module '{0}'.", + Tried_to_set_variable_type_to_uninitialized_module_type_0: "Tried to set variable type to uninitialized module type '{0}'.", + Type_0_is_not_generic: "Type '{0}' is not generic.", + Getters_must_return_a_value: "Getters must return a value.", + Getter_and_setter_accessors_do_not_agree_in_visibility: "Getter and setter accessors do not agree in visibility.", + Invalid_left_hand_side_of_assignment_expression: "Invalid left-hand side of assignment expression.", + Function_declared_a_non_void_return_type_but_has_no_return_expression: "Function declared a non-void return type, but has no return expression.", + Cannot_resolve_return_type_reference: "Cannot resolve return type reference.", + Constructors_cannot_have_a_return_type_of_void: "Constructors cannot have a return type of 'void'.", + Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2: "Subsequent variable declarations must have the same type. Variable '{0}' must be of type '{1}', but here has type '{2}'.", + All_symbols_within_a_with_block_will_be_resolved_to_any: "All symbols within a with block will be resolved to 'any'.", + Import_declarations_in_an_internal_module_cannot_reference_an_external_module: "Import declarations in an internal module cannot reference an external module.", + Class_0_declares_interface_1_but_does_not_implement_it_NL_2: "Class {0} declares interface {1} but does not implement it:{NL}{2}", + Class_0_declares_class_1_as_an_interface_but_does_not_implement_it_NL_2: "Class {0} declares class {1} as an interface but does not implement it:{NL}{2}", + The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer: "The operand of an increment or decrement operator must be a variable, property or indexer.", + this_cannot_be_referenced_in_a_static_property_initializer: "'this' cannot be referenced in a static property initializer.", + Class_0_cannot_extend_class_1_NL_2: "Class '{0}' cannot extend class '{1}':{NL}{2}", + Interface_0_cannot_extend_class_1_NL_2: "Interface '{0}' cannot extend class '{1}':{NL}{2}", + Interface_0_cannot_extend_interface_1_NL_2: "Interface '{0}' cannot extend interface '{1}':{NL}{2}", + Overload_signature_is_not_compatible_with_function_definition: "Overload signature is not compatible with function definition.", + Overload_signature_is_not_compatible_with_function_definition_NL_0: "Overload signature is not compatible with function definition:{NL}{0}", + Overload_signatures_must_all_be_public_or_private: "Overload signatures must all be public or private.", + Overload_signatures_must_all_be_exported_or_not_exported: "Overload signatures must all be exported or not exported.", + Overload_signatures_must_all_be_ambient_or_non_ambient: "Overload signatures must all be ambient or non-ambient.", + Overload_signatures_must_all_be_optional_or_required: "Overload signatures must all be optional or required.", + Specialized_overload_signature_is_not_assignable_to_any_non_specialized_signature: "Specialized overload signature is not assignable to any non-specialized signature.", + this_cannot_be_referenced_in_constructor_arguments: "'this' cannot be referenced in constructor arguments.", + Instance_member_cannot_be_accessed_off_a_class: "Instance member cannot be accessed off a class.", + Untyped_function_calls_may_not_accept_type_arguments: "Untyped function calls may not accept type arguments.", + Non_generic_functions_may_not_accept_type_arguments: "Non-generic functions may not accept type arguments.", + A_generic_type_may_not_reference_itself_with_a_wrapped_form_of_its_own_type_parameters: "A generic type may not reference itself with a wrapped form of its own type parameters.", + A_rest_parameter_must_be_of_an_array_type: "A rest parameter must be of an array type.", + Overload_signature_implementation_cannot_use_specialized_type: "Overload signature implementation cannot use specialized type.", + Export_assignments_may_only_be_used_at_the_top_level_of_external_modules: "Export assignments may only be used at the top-level of external modules.", + Export_assignments_may_only_be_made_with_variables_functions_classes_interfaces_enums_and_internal_modules: "Export assignments may only be made with variables, functions, classes, interfaces, enums and internal modules.", + Only_public_methods_of_the_base_class_are_accessible_via_the_super_keyword: "Only public methods of the base class are accessible via the 'super' keyword.", + Numeric_indexer_type_0_must_be_assignable_to_string_indexer_type_1: "Numeric indexer type '{0}' must be assignable to string indexer type '{1}'.", + Numeric_indexer_type_0_must_be_assignable_to_string_indexer_type_1_NL_2: "Numeric indexer type '{0}' must be assignable to string indexer type '{1}':{NL}{2}", + All_numerically_named_properties_must_be_assignable_to_numeric_indexer_type_0: "All numerically named properties must be assignable to numeric indexer type '{0}'.", + All_numerically_named_properties_must_be_assignable_to_numeric_indexer_type_0_NL_1: "All numerically named properties must be assignable to numeric indexer type '{0}':{NL}{1}", + All_named_properties_must_be_assignable_to_string_indexer_type_0: "All named properties must be assignable to string indexer type '{0}'.", + All_named_properties_must_be_assignable_to_string_indexer_type_0_NL_1: "All named properties must be assignable to string indexer type '{0}':{NL}{1}", + A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation: "A parameter initializer is only allowed in a function or constructor implementation.", + Function_expression_declared_a_non_void_return_type_but_has_no_return_expression: "Function expression declared a non-void return type, but has no return expression.", + Import_declaration_referencing_identifier_from_internal_module_can_only_be_made_with_variables_functions_classes_interfaces_enums_and_internal_modules: "Import declaration referencing identifier from internal module can only be made with variables, functions, classes, interfaces, enums and internal modules.", + Module_0_has_no_exported_member_1: "Module '{0}' has no exported member '{1}'.", + Unable_to_resolve_module_reference_0: "Unable to resolve module reference '{0}'.", + Could_not_find_module_0_in_module_1: "Could not find module '{0}' in module '{1}'.", + Exported_import_declaration_0_is_assigned_value_with_type_that_has_or_is_using_private_type_1: "Exported import declaration '{0}' is assigned value with type that has or is using private type '{1}'.", + Exported_import_declaration_0_is_assigned_value_with_type_that_is_using_inaccessible_module_1: "Exported import declaration '{0}' is assigned value with type that is using inaccessible module '{1}'.", + Exported_import_declaration_0_is_assigned_type_that_has_or_is_using_private_type_1: "Exported import declaration '{0}' is assigned type that has or is using private type '{1}'.", + Exported_import_declaration_0_is_assigned_type_that_is_using_inaccessible_module_1: "Exported import declaration '{0}' is assigned type that is using inaccessible module '{1}'.", + Exported_import_declaration_0_is_assigned_container_that_is_or_is_using_inaccessible_module_1: "Exported import declaration '{0}' is assigned container that is or is using inaccessible module '{1}'.", + Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_1: "Type name '{0}' in extends clause does not reference constructor function for '{1}'.", + Internal_module_reference_0_in_import_declaration_does_not_reference_module_instance_for_1: "Internal module reference '{0}' in import declaration does not reference module instance for '{1}'.", + Module_0_cannot_merge_with_previous_declaration_of_1_in_a_different_file_2: "Module '{0}' cannot merge with previous declaration of '{1}' in a different file '{2}'.", + Interface_0_cannot_simultaneously_extend_types_1_and_2_NL_3: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':{NL}{3}", + Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it.", + Ambient_external_module_declaration_cannot_be_reopened: "Ambient external module declaration cannot be reopened.", + All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported: "All declarations of merged declaration '{0}' must be exported or not exported.", + super_cannot_be_referenced_in_constructor_arguments: "'super' cannot be referenced in constructor arguments.", + Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: "Return type of constructor signature must be assignable to the instance type of the class.", + Ambient_external_module_declaration_must_be_defined_in_global_context: "Ambient external module declaration must be defined in global context.", + Ambient_external_module_declaration_cannot_specify_relative_module_name: "Ambient external module declaration cannot specify relative module name.", + Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name: "Import declaration in an ambient external module declaration cannot reference external module through relative external module name.", + No_best_common_type_exists_among_return_expressions: "No best common type exists among return expressions.", + Import_declaration_cannot_refer_to_external_module_reference_when_noResolve_option_is_set: "Import declaration cannot refer to external module reference when --noResolve option is set.", + Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference: "Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.", + Duplicate_identifier_super_Compiler_uses_super_to_capture_base_class_reference: "Duplicate identifier '_super'. Compiler uses '_super' to capture base class reference.", + Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference: "Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.", + Expression_resolves_to_super_that_compiler_uses_to_capture_base_class_reference: "Expression resolves to '_super' that compiler uses to capture base class reference.", + TypeParameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_type_1: "TypeParameter '{0}' of constructor signature from exported interface has or is using private type '{1}'.", + TypeParameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_type_1: "TypeParameter '{0}' of call signature from exported interface has or is using private type '{1}'.", + TypeParameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_type_1: "TypeParameter '{0}' of public static method from exported class has or is using private type '{1}'.", + TypeParameter_0_of_public_method_from_exported_class_has_or_is_using_private_type_1: "TypeParameter '{0}' of public method from exported class has or is using private type '{1}'.", + TypeParameter_0_of_method_from_exported_interface_has_or_is_using_private_type_1: "TypeParameter '{0}' of method from exported interface has or is using private type '{1}'.", + TypeParameter_0_of_exported_function_has_or_is_using_private_type_1: "TypeParameter '{0}' of exported function has or is using private type '{1}'.", + TypeParameter_0_of_constructor_signature_from_exported_interface_is_using_inaccessible_module_1: "TypeParameter '{0}' of constructor signature from exported interface is using inaccessible module {1}.", + TypeParameter_0_of_call_signature_from_exported_interface_is_using_inaccessible_module_1: "TypeParameter '{0}' of call signature from exported interface is using inaccessible module {1}", + TypeParameter_0_of_public_static_method_from_exported_class_is_using_inaccessible_module_1: "TypeParameter '{0}' of public static method from exported class is using inaccessible module {1}.", + TypeParameter_0_of_public_method_from_exported_class_is_using_inaccessible_module_1: "TypeParameter '{0}' of public method from exported class is using inaccessible module {1}.", + TypeParameter_0_of_method_from_exported_interface_is_using_inaccessible_module_1: "TypeParameter '{0}' of method from exported interface is using inaccessible module {1}.", + TypeParameter_0_of_exported_function_is_using_inaccessible_module_1: "TypeParameter '{0}' of exported function is using inaccessible module {1}.", + TypeParameter_0_of_exported_class_has_or_is_using_private_type_1: "TypeParameter '{0}' of exported class has or is using private type '{1}'.", + TypeParameter_0_of_exported_interface_has_or_is_using_private_type_1: "TypeParameter '{0}' of exported interface has or is using private type '{1}'.", + TypeParameter_0_of_exported_class_is_using_inaccessible_module_1: "TypeParameter '{0}' of exported class is using inaccessible module {1}.", + TypeParameter_0_of_exported_interface_is_using_inaccessible_module_1: "TypeParameter '{0}' of exported interface is using inaccessible module {1}.", + Duplicate_identifier_i_Compiler_uses_i_to_initialize_rest_parameter: "Duplicate identifier '_i'. Compiler uses '_i' to initialize rest parameter.", + Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters: "Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters.", + No_best_common_type_exists_between_0_and_1: "No best common type exists between '{0}' and '{1}'.", + No_best_common_type_exists_between_0_1_and_2: "No best common type exists between '{0}', '{1}', and '{2}'.", + Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_an_external_module: "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of an external module.", + Constraint_of_a_type_parameter_cannot_reference_any_type_parameter_from_the_same_type_parameter_list: "Constraint of a type parameter cannot reference any type parameter from the same type parameter list.", + Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.", + Parameter_0_cannot_be_referenced_in_its_initializer: "Parameter '{0}' cannot be referenced in its initializer.", + Duplicate_string_index_signature: "Duplicate string index signature.", + Duplicate_number_index_signature: "Duplicate number index signature.", + All_declarations_of_an_interface_must_have_identical_type_parameters: "All declarations of an interface must have identical type parameters.", + Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter: "Expression resolves to variable declaration '_i' that compiler uses to initialize rest parameter.", + Neither_type_0_nor_type_1_is_assignable_to_the_other: "Neither type '{0}' nor type '{1}' is assignable to the other.", + Neither_type_0_nor_type_1_is_assignable_to_the_other_NL_2: "Neither type '{0}' nor type '{1}' is assignable to the other:{NL}{2}", + Duplicate_function_implementation: "Duplicate function implementation.", + Function_implementation_expected: "Function implementation expected.", + Function_overload_name_must_be_0: "Function overload name must be '{0}'.", + Constructor_implementation_expected: "Constructor implementation expected.", + Class_name_cannot_be_0: "Class name cannot be '{0}'.", + Interface_name_cannot_be_0: "Interface name cannot be '{0}'.", + Enum_name_cannot_be_0: "Enum name cannot be '{0}'.", + A_module_cannot_have_multiple_export_assignments: "A module cannot have multiple export assignments.", + Export_assignment_not_allowed_in_module_with_exported_element: "Export assignment not allowed in module with exported element.", + A_parameter_property_is_only_allowed_in_a_constructor_implementation: "A parameter property is only allowed in a constructor implementation.", + Function_overload_must_be_static: "Function overload must be static.", + Function_overload_must_not_be_static: "Function overload must not be static.", + Type_0_is_missing_property_1_from_type_2: "Type '{0}' is missing property '{1}' from type '{2}'.", + Types_of_property_0_of_types_1_and_2_are_incompatible: "Types of property '{0}' of types '{1}' and '{2}' are incompatible.", + Types_of_property_0_of_types_1_and_2_are_incompatible_NL_3: "Types of property '{0}' of types '{1}' and '{2}' are incompatible:{NL}{3}", + Property_0_defined_as_private_in_type_1_is_defined_as_public_in_type_2: "Property '{0}' defined as private in type '{1}' is defined as public in type '{2}'.", + Property_0_defined_as_public_in_type_1_is_defined_as_private_in_type_2: "Property '{0}' defined as public in type '{1}' is defined as private in type '{2}'.", + Types_0_and_1_define_property_2_as_private: "Types '{0}' and '{1}' define property '{2}' as private.", + Call_signatures_of_types_0_and_1_are_incompatible: "Call signatures of types '{0}' and '{1}' are incompatible.", + Call_signatures_of_types_0_and_1_are_incompatible_NL_2: "Call signatures of types '{0}' and '{1}' are incompatible:{NL}{2}", + Type_0_requires_a_call_signature_but_type_1_lacks_one: "Type '{0}' requires a call signature, but type '{1}' lacks one.", + Construct_signatures_of_types_0_and_1_are_incompatible: "Construct signatures of types '{0}' and '{1}' are incompatible.", + Construct_signatures_of_types_0_and_1_are_incompatible_NL_2: "Construct signatures of types '{0}' and '{1}' are incompatible:{NL}{2}", + Type_0_requires_a_construct_signature_but_type_1_lacks_one: "Type '{0}' requires a construct signature, but type '{1}' lacks one.", + Index_signatures_of_types_0_and_1_are_incompatible: "Index signatures of types '{0}' and '{1}' are incompatible.", + Index_signatures_of_types_0_and_1_are_incompatible_NL_2: "Index signatures of types '{0}' and '{1}' are incompatible:{NL}{2}", + Call_signature_expects_0_or_fewer_parameters: "Call signature expects {0} or fewer parameters.", + Could_not_apply_type_0_to_argument_1_which_is_of_type_2: "Could not apply type '{0}' to argument {1} which is of type '{2}'.", + Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function: "Class '{0}' defines instance member accessor '{1}', but extended class '{2}' defines it as instance member function.", + Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function: "Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function.", + Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor: "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member accessor.", + Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property: "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member property.", + Types_of_static_property_0_of_class_1_and_class_2_are_incompatible: "Types of static property '{0}' of class '{1}' and class '{2}' are incompatible.", + Types_of_static_property_0_of_class_1_and_class_2_are_incompatible_NL_3: "Types of static property '{0}' of class '{1}' and class '{2}' are incompatible:{NL}{3}", + Type_reference_cannot_refer_to_container_0: "Type reference cannot refer to container '{0}'.", + Type_reference_must_refer_to_type: "Type reference must refer to type.", + In_enums_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_the_first_enum_element: "In enums with multiple declarations only one declaration can omit an initializer for the first enum element.", + _0_overload_s: " (+ {0} overload(s))", + Variable_declaration_cannot_have_the_same_name_as_an_import_declaration: "Variable declaration cannot have the same name as an import declaration.", + Signature_expected_0_type_arguments_got_1_instead: "Signature expected {0} type arguments, got {1} instead.", + Property_0_defined_as_optional_in_type_1_but_is_required_in_type_2: "Property '{0}' defined as optional in type '{1}', but is required in type '{2}'.", + Types_0_and_1_originating_in_infinitely_expanding_type_reference_do_not_refer_to_same_named_type: "Types '{0}' and '{1}' originating in infinitely expanding type reference do not refer to same named type.", + Types_0_and_1_originating_in_infinitely_expanding_type_reference_have_incompatible_type_arguments: "Types '{0}' and '{1}' originating in infinitely expanding type reference have incompatible type arguments.", + Types_0_and_1_originating_in_infinitely_expanding_type_reference_have_incompatible_type_arguments_NL_2: "Types '{0}' and '{1}' originating in infinitely expanding type reference have incompatible type arguments:{NL}{2}", + Named_properties_0_of_types_1_and_2_are_not_identical: "Named properties '{0}' of types '{1}' and '{2}' are not identical.", + Types_of_string_indexer_of_types_0_and_1_are_not_identical: "Types of string indexer of types '{0}' and '{1}' are not identical.", + Types_of_number_indexer_of_types_0_and_1_are_not_identical: "Types of number indexer of types '{0}' and '{1}' are not identical.", + Type_of_number_indexer_in_type_0_is_not_assignable_to_string_indexer_type_in_type_1_NL_2: "Type of number indexer in type '{0}' is not assignable to string indexer type in type '{1}'.{NL}{2}", + Type_of_property_0_in_type_1_is_not_assignable_to_string_indexer_type_in_type_2_NL_3: "Type of property '{0}' in type '{1}' is not assignable to string indexer type in type '{2}'.{NL}{3}", + Type_of_property_0_in_type_1_is_not_assignable_to_number_indexer_type_in_type_2_NL_3: "Type of property '{0}' in type '{1}' is not assignable to number indexer type in type '{2}'.{NL}{3}", + Static_property_0_defined_as_private_in_type_1_is_defined_as_public_in_type_2: "Static property '{0}' defined as private in type '{1}' is defined as public in type '{2}'.", + Static_property_0_defined_as_public_in_type_1_is_defined_as_private_in_type_2: "Static property '{0}' defined as public in type '{1}' is defined as private in type '{2}'.", + Types_0_and_1_define_static_property_2_as_private: "Types '{0}' and '{1}' define static property '{2}' as private.", + Current_host_does_not_support_0_option: "Current host does not support '{0}' option.", + ECMAScript_target_version_0_not_supported_Specify_a_valid_target_version_1_default_or_2: "ECMAScript target version '{0}' not supported. Specify a valid target version: '{1}' (default), or '{2}'", + Argument_for_0_option_must_be_1_or_2: "Argument for '{0}' option must be '{1}' or '{2}'", + Could_not_find_file_0: "Could not find file: '{0}'.", + A_file_cannot_have_a_reference_to_itself: "A file cannot have a reference to itself.", + Cannot_resolve_referenced_file_0: "Cannot resolve referenced file: '{0}'.", + Cannot_find_the_common_subdirectory_path_for_the_input_files: "Cannot find the common subdirectory path for the input files.", + Emit_Error_0: "Emit Error: {0}.", + Cannot_read_file_0_1: "Cannot read file '{0}': {1}", + Unsupported_file_encoding: "Unsupported file encoding.", + Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1: "Locale must be of the form or -. For example '{0}' or '{1}'.", + Unsupported_locale_0: "Unsupported locale: '{0}'.", + Execution_Failed_NL: "Execution Failed.{NL}", + Invalid_call_to_up: "Invalid call to 'up'", + Invalid_call_to_down: "Invalid call to 'down'", + Base64_value_0_finished_with_a_continuation_bit: "Base64 value '{0}' finished with a continuation bit.", + Unknown_compiler_option_0: "Unknown compiler option '{0}'", + Expected_0_arguments_to_message_got_1_instead: "Expected {0} arguments to message, got {1} instead.", + Expected_the_message_0_to_have_1_arguments_but_it_had_2: "Expected the message '{0}' to have {1} arguments, but it had {2}", + Could_not_delete_file_0: "Could not delete file '{0}'", + Could_not_create_directory_0: "Could not create directory '{0}'", + Error_while_executing_file_0: "Error while executing file '{0}': ", + Cannot_compile_external_modules_unless_the_module_flag_is_provided: "Cannot compile external modules unless the '--module' flag is provided.", + Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option: "Option mapRoot cannot be specified without specifying sourcemap option.", + Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option: "Option sourceRoot cannot be specified without specifying sourcemap option.", + Options_mapRoot_and_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option: "Options mapRoot and sourceRoot cannot be specified without specifying sourcemap option.", + Option_0_specified_without_1: "Option '{0}' specified without '{1}'", + codepage_option_not_supported_on_current_platform: "'codepage' option not supported on current platform.", + Concatenate_and_emit_output_to_single_file: "Concatenate and emit output to single file.", + Generates_corresponding_0_file: "Generates corresponding {0} file.", + Specifies_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations: "Specifies the location where debugger should locate map files instead of generated locations.", + Specifies_the_location_where_debugger_should_locate_TypeScript_files_instead_of_source_locations: "Specifies the location where debugger should locate TypeScript files instead of source locations.", + Watch_input_files: "Watch input files.", + Redirect_output_structure_to_the_directory: "Redirect output structure to the directory.", + Do_not_emit_comments_to_output: "Do not emit comments to output.", + Skip_resolution_and_preprocessing: "Skip resolution and preprocessing.", + Specify_ECMAScript_target_version_0_default_or_1: "Specify ECMAScript target version: '{0}' (default), or '{1}'", + Specify_module_code_generation_0_or_1: "Specify module code generation: '{0}' or '{1}'", + Print_this_message: "Print this message.", + Print_the_compiler_s_version_0: "Print the compiler's version: {0}", + Allow_use_of_deprecated_0_keyword_when_referencing_an_external_module: "Allow use of deprecated '{0}' keyword when referencing an external module.", + Specify_locale_for_errors_and_messages_For_example_0_or_1: "Specify locale for errors and messages. For example '{0}' or '{1}'", + Syntax_0: "Syntax: {0}", + options: "options", + file1: "file", + Examples: "Examples:", + Options: "Options:", + Insert_command_line_options_and_files_from_a_file: "Insert command line options and files from a file.", + Version_0: "Version {0}", + Use_the_0_flag_to_see_options: "Use the '{0}' flag to see options.", + NL_Recompiling_0: "{NL}Recompiling ({0}):", + STRING: "STRING", + KIND: "KIND", + file2: "FILE", + VERSION: "VERSION", + LOCATION: "LOCATION", + DIRECTORY: "DIRECTORY", + NUMBER: "NUMBER", + Specify_the_codepage_to_use_when_opening_source_files: "Specify the codepage to use when opening source files.", + Additional_locations: "Additional locations:", + This_version_of_the_Javascript_runtime_does_not_support_the_0_function: "This version of the Javascript runtime does not support the '{0}' function.", + Unknown_rule: "Unknown rule.", + Invalid_line_number_0: "Invalid line number ({0})", + Warn_on_expressions_and_declarations_with_an_implied_any_type: "Warn on expressions and declarations with an implied 'any' type.", + Variable_0_implicitly_has_an_any_type: "Variable '{0}' implicitly has an 'any' type.", + Parameter_0_of_1_implicitly_has_an_any_type: "Parameter '{0}' of '{1}' implicitly has an 'any' type.", + Parameter_0_of_function_type_implicitly_has_an_any_type: "Parameter '{0}' of function type implicitly has an 'any' type.", + Member_0_of_object_type_implicitly_has_an_any_type: "Member '{0}' of object type implicitly has an 'any' type.", + new_expression_which_lacks_a_constructor_signature_implicitly_has_an_any_type: "'new' expression, which lacks a constructor signature, implicitly has an 'any' type.", + _0_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: "'{0}', which lacks return-type annotation, implicitly has an 'any' return type.", + Function_expression_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: "Function expression, which lacks return-type annotation, implicitly has an 'any' return type.", + Parameter_0_of_lambda_function_implicitly_has_an_any_type: "Parameter '{0}' of lambda function implicitly has an 'any' type.", + Constructor_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: "Constructor signature, which lacks return-type annotation, implicitly has an 'any' return type.", + Lambda_Function_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: "Lambda Function, which lacks return-type annotation, implicitly has an 'any' return type.", + Array_Literal_implicitly_has_an_any_type_from_widening: "Array Literal implicitly has an 'any' type from widening.", + _0_which_lacks_get_accessor_and_parameter_type_annotation_on_set_accessor_implicitly_has_an_any_type: "'{0}', which lacks 'get' accessor and parameter type annotation on 'set' accessor, implicitly has an 'any' type.", + Index_signature_of_object_type_implicitly_has_an_any_type: "Index signature of object type implicitly has an 'any' type.", + Object_literal_s_property_0_implicitly_has_an_any_type_from_widening: "Object literal's property '{0}' implicitly has an 'any' type from widening.", + }; +} \ No newline at end of file diff --git a/src/services/resources/diagnosticInformationMap.generated.ts b/src/services/resources/diagnosticInformationMap.generated.ts new file mode 100644 index 00000000000..8d3bc73486e --- /dev/null +++ b/src/services/resources/diagnosticInformationMap.generated.ts @@ -0,0 +1,446 @@ +// +/// +module TypeScript { + export var diagnosticInformationMap: ts.Map = { + "error TS{0}: {1}": { "code": 0, "category": DiagnosticCategory.NoPrefix }, + "warning TS{0}: {1}": { "code": 1, "category": DiagnosticCategory.NoPrefix }, + "Unrecognized escape sequence.": { "code": 1000, "category": DiagnosticCategory.Error }, + "Unexpected character {0}.": { "code": 1001, "category": DiagnosticCategory.Error }, + "Missing close quote character.": { "code": 1002, "category": DiagnosticCategory.Error }, + "Identifier expected.": { "code": 1003, "category": DiagnosticCategory.Error }, + "'{0}' keyword expected.": { "code": 1004, "category": DiagnosticCategory.Error }, + "'{0}' expected.": { "code": 1005, "category": DiagnosticCategory.Error }, + "Identifier expected; '{0}' is a keyword.": { "code": 1006, "category": DiagnosticCategory.Error }, + "Automatic semicolon insertion not allowed.": { "code": 1007, "category": DiagnosticCategory.Error }, + "Unexpected token; '{0}' expected.": { "code": 1008, "category": DiagnosticCategory.Error }, + "Trailing comma not allowed.": { "code": 1009, "category": DiagnosticCategory.Error }, + "'*/' expected.": { "code": 1010, "category": DiagnosticCategory.Error }, + "'public' or 'private' modifier must precede 'static'.": { "code": 1011, "category": DiagnosticCategory.Error }, + "Unexpected token.": { "code": 1012, "category": DiagnosticCategory.Error }, + "Catch clause parameter cannot have a type annotation.": { "code": 1013, "category": DiagnosticCategory.Error }, + "A rest parameter must be last in a parameter list.": { "code": 1014, "category": DiagnosticCategory.Error }, + "Parameter cannot have question mark and initializer.": { "code": 1015, "category": DiagnosticCategory.Error }, + "A required parameter cannot follow an optional parameter.": { "code": 1016, "category": DiagnosticCategory.Error }, + "Index signatures cannot have rest parameters.": { "code": 1017, "category": DiagnosticCategory.Error }, + "Index signature parameter cannot have accessibility modifiers.": { "code": 1018, "category": DiagnosticCategory.Error }, + "Index signature parameter cannot have a question mark.": { "code": 1019, "category": DiagnosticCategory.Error }, + "Index signature parameter cannot have an initializer.": { "code": 1020, "category": DiagnosticCategory.Error }, + "Index signature must have a type annotation.": { "code": 1021, "category": DiagnosticCategory.Error }, + "Index signature parameter must have a type annotation.": { "code": 1022, "category": DiagnosticCategory.Error }, + "Index signature parameter type must be 'string' or 'number'.": { "code": 1023, "category": DiagnosticCategory.Error }, + "'extends' clause already seen.": { "code": 1024, "category": DiagnosticCategory.Error }, + "'extends' clause must precede 'implements' clause.": { "code": 1025, "category": DiagnosticCategory.Error }, + "Classes can only extend a single class.": { "code": 1026, "category": DiagnosticCategory.Error }, + "'implements' clause already seen.": { "code": 1027, "category": DiagnosticCategory.Error }, + "Accessibility modifier already seen.": { "code": 1028, "category": DiagnosticCategory.Error }, + "'{0}' modifier must precede '{1}' modifier.": { "code": 1029, "category": DiagnosticCategory.Error }, + "'{0}' modifier already seen.": { "code": 1030, "category": DiagnosticCategory.Error }, + "'{0}' modifier cannot appear on a class element.": { "code": 1031, "category": DiagnosticCategory.Error }, + "Interface declaration cannot have 'implements' clause.": { "code": 1032, "category": DiagnosticCategory.Error }, + "'super' invocation cannot have type arguments.": { "code": 1034, "category": DiagnosticCategory.Error }, + "Only ambient modules can use quoted names.": { "code": 1035, "category": DiagnosticCategory.Error }, + "Statements are not allowed in ambient contexts.": { "code": 1036, "category": DiagnosticCategory.Error }, + "A function implementation cannot be declared in an ambient context.": { "code": 1037, "category": DiagnosticCategory.Error }, + "A 'declare' modifier cannot be used in an already ambient context.": { "code": 1038, "category": DiagnosticCategory.Error }, + "Initializers are not allowed in ambient contexts.": { "code": 1039, "category": DiagnosticCategory.Error }, + "'{0}' modifier cannot appear on a module element.": { "code": 1044, "category": DiagnosticCategory.Error }, + "A 'declare' modifier cannot be used with an interface declaration.": { "code": 1045, "category": DiagnosticCategory.Error }, + "A 'declare' modifier is required for a top level declaration in a .d.ts file.": { "code": 1046, "category": DiagnosticCategory.Error }, + "A rest parameter cannot be optional.": { "code": 1047, "category": DiagnosticCategory.Error }, + "A rest parameter cannot have an initializer.": { "code": 1048, "category": DiagnosticCategory.Error }, + "'set' accessor must have exactly one parameter.": { "code": 1049, "category": DiagnosticCategory.Error }, + "'set' accessor parameter cannot be optional.": { "code": 1051, "category": DiagnosticCategory.Error }, + "'set' accessor parameter cannot have an initializer.": { "code": 1052, "category": DiagnosticCategory.Error }, + "'set' accessor cannot have rest parameter.": { "code": 1053, "category": DiagnosticCategory.Error }, + "'get' accessor cannot have parameters.": { "code": 1054, "category": DiagnosticCategory.Error }, + "Modifiers cannot appear here.": { "code": 1055, "category": DiagnosticCategory.Error }, + "Accessors are only available when targeting ECMAScript 5 and higher.": { "code": 1056, "category": DiagnosticCategory.Error }, + "Enum member must have initializer.": { "code": 1061, "category": DiagnosticCategory.Error }, + "Export assignment cannot be used in internal modules.": { "code": 1063, "category": DiagnosticCategory.Error }, + "Ambient enum elements can only have integer literal initializers.": { "code": 1066, "category": DiagnosticCategory.Error }, + "module, class, interface, enum, import or statement": { "code": 1067, "category": DiagnosticCategory.NoPrefix }, + "constructor, function, accessor or variable": { "code": 1068, "category": DiagnosticCategory.NoPrefix }, + "statement": { "code": 1069, "category": DiagnosticCategory.NoPrefix }, + "case or default clause": { "code": 1070, "category": DiagnosticCategory.NoPrefix }, + "identifier": { "code": 1071, "category": DiagnosticCategory.NoPrefix }, + "call, construct, index, property or function signature": { "code": 1072, "category": DiagnosticCategory.NoPrefix }, + "expression": { "code": 1073, "category": DiagnosticCategory.NoPrefix }, + "type name": { "code": 1074, "category": DiagnosticCategory.NoPrefix }, + "property or accessor": { "code": 1075, "category": DiagnosticCategory.NoPrefix }, + "parameter": { "code": 1076, "category": DiagnosticCategory.NoPrefix }, + "type": { "code": 1077, "category": DiagnosticCategory.NoPrefix }, + "type parameter": { "code": 1078, "category": DiagnosticCategory.NoPrefix }, + "A 'declare' modifier cannot be used with an import declaration.": { "code": 1079, "category": DiagnosticCategory.Error }, + "Invalid 'reference' directive syntax.": { "code": 1084, "category": DiagnosticCategory.Error }, + "Octal literals are not available when targeting ECMAScript 5 and higher.": { "code": 1085, "category": DiagnosticCategory.Error }, + "Accessors are not allowed in ambient contexts.": { "code": 1086, "category": DiagnosticCategory.Error }, + "'{0}' modifier cannot appear on a constructor declaration.": { "code": 1089, "category": DiagnosticCategory.Error }, + "'{0}' modifier cannot appear on a parameter.": { "code": 1090, "category": DiagnosticCategory.Error }, + "Only a single variable declaration is allowed in a 'for...in' statement.": { "code": 1091, "category": DiagnosticCategory.Error }, + "Type parameters cannot appear on a constructor declaration.": { "code": 1092, "category": DiagnosticCategory.Error }, + "Type annotation cannot appear on a constructor declaration.": { "code": 1093, "category": DiagnosticCategory.Error }, + "Type parameters cannot appear on an accessor.": { "code": 1094, "category": DiagnosticCategory.Error }, + "Type annotation cannot appear on a 'set' accessor.": { "code": 1095, "category": DiagnosticCategory.Error }, + "Index signature must have exactly one parameter.": { "code": 1096, "category": DiagnosticCategory.Error }, + "'{0}' list cannot be empty.": { "code": 1097, "category": DiagnosticCategory.Error }, + "variable declaration": { "code": 1098, "category": DiagnosticCategory.NoPrefix }, + "type argument": { "code": 1099, "category": DiagnosticCategory.NoPrefix }, + "Invalid use of '{0}' in strict mode.": { "code": 1100, "category": DiagnosticCategory.Error }, + "'with' statements are not allowed in strict mode.": { "code": 1101, "category": DiagnosticCategory.Error }, + "'delete' cannot be called on an identifier in strict mode.": { "code": 1102, "category": DiagnosticCategory.Error }, + "Invalid left-hand side in 'for...in' statement.": { "code": 1103, "category": DiagnosticCategory.Error }, + "'continue' statement can only be used within an enclosing iteration statement.": { "code": 1104, "category": DiagnosticCategory.Error }, + "'break' statement can only be used within an enclosing iteration or switch statement.": { "code": 1105, "category": DiagnosticCategory.Error }, + "Jump target not found.": { "code": 1106, "category": DiagnosticCategory.Error }, + "Jump target cannot cross function boundary.": { "code": 1107, "category": DiagnosticCategory.Error }, + "'return' statement must be contained within a function body.": { "code": 1108, "category": DiagnosticCategory.Error }, + "Expression expected.": { "code": 1109, "category": DiagnosticCategory.Error }, + "Type expected.": { "code": 1110, "category": DiagnosticCategory.Error }, + "Duplicate identifier '{0}'.": { "code": 2000, "category": DiagnosticCategory.Error }, + "The name '{0}' does not exist in the current scope.": { "code": 2001, "category": DiagnosticCategory.Error }, + "The name '{0}' does not refer to a value.": { "code": 2002, "category": DiagnosticCategory.Error }, + "'super' can only be used inside a class instance method.": { "code": 2003, "category": DiagnosticCategory.Error }, + "The left-hand side of an assignment expression must be a variable, property or indexer.": { "code": 2004, "category": DiagnosticCategory.Error }, + "Value of type '{0}' is not callable. Did you mean to include 'new'?": { "code": 2161, "category": DiagnosticCategory.Error }, + "Value of type '{0}' is not callable.": { "code": 2006, "category": DiagnosticCategory.Error }, + "Value of type '{0}' is not newable.": { "code": 2007, "category": DiagnosticCategory.Error }, + "An index expression argument must be 'string', 'number', or 'any'.": { "code": 2008, "category": DiagnosticCategory.Error }, + "Operator '{0}' cannot be applied to types '{1}' and '{2}'.": { "code": 2009, "category": DiagnosticCategory.Error }, + "Type '{0}' is not assignable to type '{1}'.": { "code": 2011, "category": DiagnosticCategory.Error }, + "Type '{0}' is not assignable to type '{1}':{NL}{2}": { "code": 2012, "category": DiagnosticCategory.Error }, + "Expected var, class, interface, or module.": { "code": 2013, "category": DiagnosticCategory.Error }, + "Getter '{0}' already declared.": { "code": 2015, "category": DiagnosticCategory.Error }, + "Setter '{0}' already declared.": { "code": 2016, "category": DiagnosticCategory.Error }, + "Exported class '{0}' extends private class '{1}'.": { "code": 2018, "category": DiagnosticCategory.Error }, + "Exported class '{0}' implements private interface '{1}'.": { "code": 2019, "category": DiagnosticCategory.Error }, + "Exported interface '{0}' extends private interface '{1}'.": { "code": 2020, "category": DiagnosticCategory.Error }, + "Exported class '{0}' extends class from inaccessible module {1}.": { "code": 2021, "category": DiagnosticCategory.Error }, + "Exported class '{0}' implements interface from inaccessible module {1}.": { "code": 2022, "category": DiagnosticCategory.Error }, + "Exported interface '{0}' extends interface from inaccessible module {1}.": { "code": 2023, "category": DiagnosticCategory.Error }, + "Public static property '{0}' of exported class has or is using private type '{1}'.": { "code": 2024, "category": DiagnosticCategory.Error }, + "Public property '{0}' of exported class has or is using private type '{1}'.": { "code": 2025, "category": DiagnosticCategory.Error }, + "Property '{0}' of exported interface has or is using private type '{1}'.": { "code": 2026, "category": DiagnosticCategory.Error }, + "Exported variable '{0}' has or is using private type '{1}'.": { "code": 2027, "category": DiagnosticCategory.Error }, + "Public static property '{0}' of exported class is using inaccessible module {1}.": { "code": 2028, "category": DiagnosticCategory.Error }, + "Public property '{0}' of exported class is using inaccessible module {1}.": { "code": 2029, "category": DiagnosticCategory.Error }, + "Property '{0}' of exported interface is using inaccessible module {1}.": { "code": 2030, "category": DiagnosticCategory.Error }, + "Exported variable '{0}' is using inaccessible module {1}.": { "code": 2031, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of constructor from exported class has or is using private type '{1}'.": { "code": 2032, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public static property setter from exported class has or is using private type '{1}'.": { "code": 2033, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public property setter from exported class has or is using private type '{1}'.": { "code": 2034, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of constructor signature from exported interface has or is using private type '{1}'.": { "code": 2035, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of call signature from exported interface has or is using private type '{1}'.": { "code": 2036, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public static method from exported class has or is using private type '{1}'.": { "code": 2037, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public method from exported class has or is using private type '{1}'.": { "code": 2038, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of method from exported interface has or is using private type '{1}'.": { "code": 2039, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of exported function has or is using private type '{1}'.": { "code": 2040, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of constructor from exported class is using inaccessible module {1}.": { "code": 2041, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public static property setter from exported class is using inaccessible module {1}.": { "code": 2042, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public property setter from exported class is using inaccessible module {1}.": { "code": 2043, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of constructor signature from exported interface is using inaccessible module {1}.": { "code": 2044, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of call signature from exported interface is using inaccessible module {1}": { "code": 2045, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public static method from exported class is using inaccessible module {1}.": { "code": 2046, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of public method from exported class is using inaccessible module {1}.": { "code": 2047, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of method from exported interface is using inaccessible module {1}.": { "code": 2048, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of exported function is using inaccessible module {1}.": { "code": 2049, "category": DiagnosticCategory.Error }, + "Return type of public static property getter from exported class has or is using private type '{0}'.": { "code": 2050, "category": DiagnosticCategory.Error }, + "Return type of public property getter from exported class has or is using private type '{0}'.": { "code": 2051, "category": DiagnosticCategory.Error }, + "Return type of constructor signature from exported interface has or is using private type '{0}'.": { "code": 2052, "category": DiagnosticCategory.Error }, + "Return type of call signature from exported interface has or is using private type '{0}'.": { "code": 2053, "category": DiagnosticCategory.Error }, + "Return type of index signature from exported interface has or is using private type '{0}'.": { "code": 2054, "category": DiagnosticCategory.Error }, + "Return type of public static method from exported class has or is using private type '{0}'.": { "code": 2055, "category": DiagnosticCategory.Error }, + "Return type of public method from exported class has or is using private type '{0}'.": { "code": 2056, "category": DiagnosticCategory.Error }, + "Return type of method from exported interface has or is using private type '{0}'.": { "code": 2057, "category": DiagnosticCategory.Error }, + "Return type of exported function has or is using private type '{0}'.": { "code": 2058, "category": DiagnosticCategory.Error }, + "Return type of public static property getter from exported class is using inaccessible module {0}.": { "code": 2059, "category": DiagnosticCategory.Error }, + "Return type of public property getter from exported class is using inaccessible module {0}.": { "code": 2060, "category": DiagnosticCategory.Error }, + "Return type of constructor signature from exported interface is using inaccessible module {0}.": { "code": 2061, "category": DiagnosticCategory.Error }, + "Return type of call signature from exported interface is using inaccessible module {0}.": { "code": 2062, "category": DiagnosticCategory.Error }, + "Return type of index signature from exported interface is using inaccessible module {0}.": { "code": 2063, "category": DiagnosticCategory.Error }, + "Return type of public static method from exported class is using inaccessible module {0}.": { "code": 2064, "category": DiagnosticCategory.Error }, + "Return type of public method from exported class is using inaccessible module {0}.": { "code": 2065, "category": DiagnosticCategory.Error }, + "Return type of method from exported interface is using inaccessible module {0}.": { "code": 2066, "category": DiagnosticCategory.Error }, + "Return type of exported function is using inaccessible module {0}.": { "code": 2067, "category": DiagnosticCategory.Error }, + "'new T[]' cannot be used to create an array. Use 'new Array()' instead.": { "code": 2068, "category": DiagnosticCategory.Error }, + "A parameter list must follow a generic type argument list. '(' expected.": { "code": 2069, "category": DiagnosticCategory.Error }, + "Multiple constructor implementations are not allowed.": { "code": 2070, "category": DiagnosticCategory.Error }, + "Cannot find external module '{0}'.": { "code": 2071, "category": DiagnosticCategory.Error }, + "Module cannot be aliased to a non-module type.": { "code": 2072, "category": DiagnosticCategory.Error }, + "A class may only extend another class.": { "code": 2073, "category": DiagnosticCategory.Error }, + "A class may only implement another class or interface.": { "code": 2074, "category": DiagnosticCategory.Error }, + "An interface may only extend a class or another interface.": { "code": 2075, "category": DiagnosticCategory.Error }, + "Unable to resolve type.": { "code": 2077, "category": DiagnosticCategory.Error }, + "Unable to resolve type of '{0}'.": { "code": 2078, "category": DiagnosticCategory.Error }, + "Unable to resolve type parameter constraint.": { "code": 2079, "category": DiagnosticCategory.Error }, + "Type parameter constraint cannot be a primitive type.": { "code": 2080, "category": DiagnosticCategory.Error }, + "Supplied parameters do not match any signature of call target.": { "code": 2081, "category": DiagnosticCategory.Error }, + "Supplied parameters do not match any signature of call target:{NL}{0}": { "code": 2082, "category": DiagnosticCategory.Error }, + "Cannot use 'new' with an expression whose type lacks a signature.": { "code": 2083, "category": DiagnosticCategory.Error }, + "Only a void function can be called with the 'new' keyword.": { "code": 2084, "category": DiagnosticCategory.Error }, + "Could not select overload for 'new' expression.": { "code": 2085, "category": DiagnosticCategory.Error }, + "Type '{0}' does not satisfy the constraint '{1}'.": { "code": 2086, "category": DiagnosticCategory.Error }, + "Could not select overload for 'call' expression.": { "code": 2087, "category": DiagnosticCategory.Error }, + "Cannot invoke an expression whose type lacks a call signature.": { "code": 2088, "category": DiagnosticCategory.Error }, + "Calls to 'super' are only valid inside a class.": { "code": 2089, "category": DiagnosticCategory.Error }, + "Generic type '{0}' requires {1} type argument(s).": { "code": 2090, "category": DiagnosticCategory.Error }, + "Type of array literal cannot be determined. Best common type could not be found for array elements.": { "code": 2092, "category": DiagnosticCategory.Error }, + "Could not find enclosing symbol for dotted name '{0}'.": { "code": 2093, "category": DiagnosticCategory.Error }, + "Property '{0}' does not exist on value of type '{1}'.": { "code": 2094, "category": DiagnosticCategory.Error }, + "Cannot find name '{0}'.": { "code": 2095, "category": DiagnosticCategory.Error }, + "'get' and 'set' accessor must have the same type.": { "code": 2096, "category": DiagnosticCategory.Error }, + "'this' cannot be referenced in current location.": { "code": 2097, "category": DiagnosticCategory.Error }, + "Static members cannot reference class type parameters.": { "code": 2099, "category": DiagnosticCategory.Error }, + "Type '{0}' recursively references itself as a base type.": { "code": 2100, "category": DiagnosticCategory.Error }, + "'super' property access is permitted only in a constructor, member function, or member accessor of a derived class.": { "code": 2102, "category": DiagnosticCategory.Error }, + "'super' can only be referenced in a derived class.": { "code": 2103, "category": DiagnosticCategory.Error }, + "A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties.": { "code": 2104, "category": DiagnosticCategory.Error }, + "Constructors for derived classes must contain a 'super' call.": { "code": 2105, "category": DiagnosticCategory.Error }, + "Super calls are not permitted outside constructors or in nested functions inside constructors.": { "code": 2106, "category": DiagnosticCategory.Error }, + "'{0}.{1}' is inaccessible.": { "code": 2107, "category": DiagnosticCategory.Error }, + "'this' cannot be referenced in a module body.": { "code": 2108, "category": DiagnosticCategory.Error }, + "Invalid '+' expression - types not known to support the addition operator.": { "code": 2111, "category": DiagnosticCategory.Error }, + "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.": { "code": 2112, "category": DiagnosticCategory.Error }, + "The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.": { "code": 2113, "category": DiagnosticCategory.Error }, + "An arithmetic operand must be of type 'any', 'number' or an enum type.": { "code": 2114, "category": DiagnosticCategory.Error }, + "Variable declarations of a 'for' statement cannot use a type annotation.": { "code": 2115, "category": DiagnosticCategory.Error }, + "Variable declarations of a 'for' statement must be of types 'string' or 'any'.": { "code": 2116, "category": DiagnosticCategory.Error }, + "The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter.": { "code": 2117, "category": DiagnosticCategory.Error }, + "The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": { "code": 2118, "category": DiagnosticCategory.Error }, + "The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter.": { "code": 2119, "category": DiagnosticCategory.Error }, + "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.": { "code": 2120, "category": DiagnosticCategory.Error }, + "The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type.": { "code": 2121, "category": DiagnosticCategory.Error }, + "Setters cannot return a value.": { "code": 2122, "category": DiagnosticCategory.Error }, + "Tried to query type of uninitialized module '{0}'.": { "code": 2123, "category": DiagnosticCategory.Error }, + "Tried to set variable type to uninitialized module type '{0}'.": { "code": 2124, "category": DiagnosticCategory.Error }, + "Type '{0}' is not generic.": { "code": 2125, "category": DiagnosticCategory.Error }, + "Getters must return a value.": { "code": 2126, "category": DiagnosticCategory.Error }, + "Getter and setter accessors do not agree in visibility.": { "code": 2127, "category": DiagnosticCategory.Error }, + "Invalid left-hand side of assignment expression.": { "code": 2130, "category": DiagnosticCategory.Error }, + "Function declared a non-void return type, but has no return expression.": { "code": 2131, "category": DiagnosticCategory.Error }, + "Cannot resolve return type reference.": { "code": 2132, "category": DiagnosticCategory.Error }, + "Constructors cannot have a return type of 'void'.": { "code": 2133, "category": DiagnosticCategory.Error }, + "Subsequent variable declarations must have the same type. Variable '{0}' must be of type '{1}', but here has type '{2}'.": { "code": 2134, "category": DiagnosticCategory.Error }, + "All symbols within a with block will be resolved to 'any'.": { "code": 2135, "category": DiagnosticCategory.Error }, + "Import declarations in an internal module cannot reference an external module.": { "code": 2136, "category": DiagnosticCategory.Error }, + "Class {0} declares interface {1} but does not implement it:{NL}{2}": { "code": 2137, "category": DiagnosticCategory.Error }, + "Class {0} declares class {1} as an interface but does not implement it:{NL}{2}": { "code": 2138, "category": DiagnosticCategory.Error }, + "The operand of an increment or decrement operator must be a variable, property or indexer.": { "code": 2139, "category": DiagnosticCategory.Error }, + "'this' cannot be referenced in a static property initializer.": { "code": 2140, "category": DiagnosticCategory.Error }, + "Class '{0}' cannot extend class '{1}':{NL}{2}": { "code": 2141, "category": DiagnosticCategory.Error }, + "Interface '{0}' cannot extend class '{1}':{NL}{2}": { "code": 2142, "category": DiagnosticCategory.Error }, + "Interface '{0}' cannot extend interface '{1}':{NL}{2}": { "code": 2143, "category": DiagnosticCategory.Error }, + "Overload signature is not compatible with function definition.": { "code": 2148, "category": DiagnosticCategory.Error }, + "Overload signature is not compatible with function definition:{NL}{0}": { "code": 2149, "category": DiagnosticCategory.Error }, + "Overload signatures must all be public or private.": { "code": 2150, "category": DiagnosticCategory.Error }, + "Overload signatures must all be exported or not exported.": { "code": 2151, "category": DiagnosticCategory.Error }, + "Overload signatures must all be ambient or non-ambient.": { "code": 2152, "category": DiagnosticCategory.Error }, + "Overload signatures must all be optional or required.": { "code": 2153, "category": DiagnosticCategory.Error }, + "Specialized overload signature is not assignable to any non-specialized signature.": { "code": 2154, "category": DiagnosticCategory.Error }, + "'this' cannot be referenced in constructor arguments.": { "code": 2155, "category": DiagnosticCategory.Error }, + "Instance member cannot be accessed off a class.": { "code": 2157, "category": DiagnosticCategory.Error }, + "Untyped function calls may not accept type arguments.": { "code": 2158, "category": DiagnosticCategory.Error }, + "Non-generic functions may not accept type arguments.": { "code": 2159, "category": DiagnosticCategory.Error }, + "A generic type may not reference itself with a wrapped form of its own type parameters.": { "code": 2160, "category": DiagnosticCategory.Error }, + "A rest parameter must be of an array type.": { "code": 2162, "category": DiagnosticCategory.Error }, + "Overload signature implementation cannot use specialized type.": { "code": 2163, "category": DiagnosticCategory.Error }, + "Export assignments may only be used at the top-level of external modules.": { "code": 2164, "category": DiagnosticCategory.Error }, + "Export assignments may only be made with variables, functions, classes, interfaces, enums and internal modules.": { "code": 2165, "category": DiagnosticCategory.Error }, + "Only public methods of the base class are accessible via the 'super' keyword.": { "code": 2166, "category": DiagnosticCategory.Error }, + "Numeric indexer type '{0}' must be assignable to string indexer type '{1}'.": { "code": 2167, "category": DiagnosticCategory.Error }, + "Numeric indexer type '{0}' must be assignable to string indexer type '{1}':{NL}{2}": { "code": 2168, "category": DiagnosticCategory.Error }, + "All numerically named properties must be assignable to numeric indexer type '{0}'.": { "code": 2169, "category": DiagnosticCategory.Error }, + "All numerically named properties must be assignable to numeric indexer type '{0}':{NL}{1}": { "code": 2170, "category": DiagnosticCategory.Error }, + "All named properties must be assignable to string indexer type '{0}'.": { "code": 2171, "category": DiagnosticCategory.Error }, + "All named properties must be assignable to string indexer type '{0}':{NL}{1}": { "code": 2172, "category": DiagnosticCategory.Error }, + "A parameter initializer is only allowed in a function or constructor implementation.": { "code": 2174, "category": DiagnosticCategory.Error }, + "Function expression declared a non-void return type, but has no return expression.": { "code": 2176, "category": DiagnosticCategory.Error }, + "Import declaration referencing identifier from internal module can only be made with variables, functions, classes, interfaces, enums and internal modules.": { "code": 2177, "category": DiagnosticCategory.Error }, + "Module '{0}' has no exported member '{1}'.": { "code": 2178, "category": DiagnosticCategory.Error }, + "Unable to resolve module reference '{0}'.": { "code": 2179, "category": DiagnosticCategory.Error }, + "Could not find module '{0}' in module '{1}'.": { "code": 2180, "category": DiagnosticCategory.Error }, + "Exported import declaration '{0}' is assigned value with type that has or is using private type '{1}'.": { "code": 2181, "category": DiagnosticCategory.Error }, + "Exported import declaration '{0}' is assigned value with type that is using inaccessible module '{1}'.": { "code": 2182, "category": DiagnosticCategory.Error }, + "Exported import declaration '{0}' is assigned type that has or is using private type '{1}'.": { "code": 2183, "category": DiagnosticCategory.Error }, + "Exported import declaration '{0}' is assigned type that is using inaccessible module '{1}'.": { "code": 2184, "category": DiagnosticCategory.Error }, + "Exported import declaration '{0}' is assigned container that is or is using inaccessible module '{1}'.": { "code": 2185, "category": DiagnosticCategory.Error }, + "Type name '{0}' in extends clause does not reference constructor function for '{1}'.": { "code": 2186, "category": DiagnosticCategory.Error }, + "Internal module reference '{0}' in import declaration does not reference module instance for '{1}'.": { "code": 2187, "category": DiagnosticCategory.Error }, + "Module '{0}' cannot merge with previous declaration of '{1}' in a different file '{2}'.": { "code": 2188, "category": DiagnosticCategory.Error }, + "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':{NL}{3}": { "code": 2189, "category": DiagnosticCategory.Error }, + "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it.": { "code": 2190, "category": DiagnosticCategory.Error }, + "Ambient external module declaration cannot be reopened.": { "code": 2191, "category": DiagnosticCategory.Error }, + "All declarations of merged declaration '{0}' must be exported or not exported.": { "code": 2192, "category": DiagnosticCategory.Error }, + "'super' cannot be referenced in constructor arguments.": { "code": 2193, "category": DiagnosticCategory.Error }, + "Return type of constructor signature must be assignable to the instance type of the class.": { "code": 2194, "category": DiagnosticCategory.Error }, + "Ambient external module declaration must be defined in global context.": { "code": 2195, "category": DiagnosticCategory.Error }, + "Ambient external module declaration cannot specify relative module name.": { "code": 2196, "category": DiagnosticCategory.Error }, + "Import declaration in an ambient external module declaration cannot reference external module through relative external module name.": { "code": 2197, "category": DiagnosticCategory.Error }, + "No best common type exists among return expressions.": { "code": 2198, "category": DiagnosticCategory.Error }, + "Import declaration cannot refer to external module reference when --noResolve option is set.": { "code": 2199, "category": DiagnosticCategory.Error }, + "Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.": { "code": 2200, "category": DiagnosticCategory.Error }, + "Duplicate identifier '_super'. Compiler uses '_super' to capture base class reference.": { "code": 2205, "category": DiagnosticCategory.Error }, + "Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.": { "code": 2206, "category": DiagnosticCategory.Error }, + "Expression resolves to '_super' that compiler uses to capture base class reference.": { "code": 2207, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of constructor signature from exported interface has or is using private type '{1}'.": { "code": 2208, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of call signature from exported interface has or is using private type '{1}'.": { "code": 2209, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of public static method from exported class has or is using private type '{1}'.": { "code": 2210, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of public method from exported class has or is using private type '{1}'.": { "code": 2211, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of method from exported interface has or is using private type '{1}'.": { "code": 2212, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of exported function has or is using private type '{1}'.": { "code": 2213, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of constructor signature from exported interface is using inaccessible module {1}.": { "code": 2214, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of call signature from exported interface is using inaccessible module {1}": { "code": 2215, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of public static method from exported class is using inaccessible module {1}.": { "code": 2216, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of public method from exported class is using inaccessible module {1}.": { "code": 2217, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of method from exported interface is using inaccessible module {1}.": { "code": 2218, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of exported function is using inaccessible module {1}.": { "code": 2219, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of exported class has or is using private type '{1}'.": { "code": 2220, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of exported interface has or is using private type '{1}'.": { "code": 2221, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of exported class is using inaccessible module {1}.": { "code": 2222, "category": DiagnosticCategory.Error }, + "TypeParameter '{0}' of exported interface is using inaccessible module {1}.": { "code": 2223, "category": DiagnosticCategory.Error }, + "Duplicate identifier '_i'. Compiler uses '_i' to initialize rest parameter.": { "code": 2224, "category": DiagnosticCategory.Error }, + "Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters.": { "code": 2225, "category": DiagnosticCategory.Error }, + "No best common type exists between '{0}' and '{1}'.": { "code": 2226, "category": DiagnosticCategory.Error }, + "No best common type exists between '{0}', '{1}', and '{2}'.": { "code": 2227, "category": DiagnosticCategory.Error }, + "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of an external module.": { "code": 2228, "category": DiagnosticCategory.Error }, + "Constraint of a type parameter cannot reference any type parameter from the same type parameter list.": { "code": 2229, "category": DiagnosticCategory.Error }, + "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.": { "code": 2230, "category": DiagnosticCategory.Error }, + "Parameter '{0}' cannot be referenced in its initializer.": { "code": 2231, "category": DiagnosticCategory.Error }, + "Duplicate string index signature.": { "code": 2232, "category": DiagnosticCategory.Error }, + "Duplicate number index signature.": { "code": 2233, "category": DiagnosticCategory.Error }, + "All declarations of an interface must have identical type parameters.": { "code": 2234, "category": DiagnosticCategory.Error }, + "Expression resolves to variable declaration '_i' that compiler uses to initialize rest parameter.": { "code": 2235, "category": DiagnosticCategory.Error }, + "Neither type '{0}' nor type '{1}' is assignable to the other.": { "code": 2236, "category": DiagnosticCategory.Error }, + "Neither type '{0}' nor type '{1}' is assignable to the other:{NL}{2}": { "code": 2237, "category": DiagnosticCategory.Error }, + "Duplicate function implementation.": { "code": 2237, "category": DiagnosticCategory.Error }, + "Function implementation expected.": { "code": 2238, "category": DiagnosticCategory.Error }, + "Function overload name must be '{0}'.": { "code": 2239, "category": DiagnosticCategory.Error }, + "Constructor implementation expected.": { "code": 2240, "category": DiagnosticCategory.Error }, + "Class name cannot be '{0}'.": { "code": 2241, "category": DiagnosticCategory.Error }, + "Interface name cannot be '{0}'.": { "code": 2242, "category": DiagnosticCategory.Error }, + "Enum name cannot be '{0}'.": { "code": 2243, "category": DiagnosticCategory.Error }, + "A module cannot have multiple export assignments.": { "code": 2244, "category": DiagnosticCategory.Error }, + "Export assignment not allowed in module with exported element.": { "code": 2245, "category": DiagnosticCategory.Error }, + "A parameter property is only allowed in a constructor implementation.": { "code": 2246, "category": DiagnosticCategory.Error }, + "Function overload must be static.": { "code": 2247, "category": DiagnosticCategory.Error }, + "Function overload must not be static.": { "code": 2248, "category": DiagnosticCategory.Error }, + "Type '{0}' is missing property '{1}' from type '{2}'.": { "code": 4000, "category": DiagnosticCategory.NoPrefix }, + "Types of property '{0}' of types '{1}' and '{2}' are incompatible.": { "code": 4001, "category": DiagnosticCategory.NoPrefix }, + "Types of property '{0}' of types '{1}' and '{2}' are incompatible:{NL}{3}": { "code": 4002, "category": DiagnosticCategory.NoPrefix }, + "Property '{0}' defined as private in type '{1}' is defined as public in type '{2}'.": { "code": 4003, "category": DiagnosticCategory.NoPrefix }, + "Property '{0}' defined as public in type '{1}' is defined as private in type '{2}'.": { "code": 4004, "category": DiagnosticCategory.NoPrefix }, + "Types '{0}' and '{1}' define property '{2}' as private.": { "code": 4005, "category": DiagnosticCategory.NoPrefix }, + "Call signatures of types '{0}' and '{1}' are incompatible.": { "code": 4006, "category": DiagnosticCategory.NoPrefix }, + "Call signatures of types '{0}' and '{1}' are incompatible:{NL}{2}": { "code": 4007, "category": DiagnosticCategory.NoPrefix }, + "Type '{0}' requires a call signature, but type '{1}' lacks one.": { "code": 4008, "category": DiagnosticCategory.NoPrefix }, + "Construct signatures of types '{0}' and '{1}' are incompatible.": { "code": 4009, "category": DiagnosticCategory.NoPrefix }, + "Construct signatures of types '{0}' and '{1}' are incompatible:{NL}{2}": { "code": 4010, "category": DiagnosticCategory.NoPrefix }, + "Type '{0}' requires a construct signature, but type '{1}' lacks one.": { "code": 4011, "category": DiagnosticCategory.NoPrefix }, + "Index signatures of types '{0}' and '{1}' are incompatible.": { "code": 4012, "category": DiagnosticCategory.NoPrefix }, + "Index signatures of types '{0}' and '{1}' are incompatible:{NL}{2}": { "code": 4013, "category": DiagnosticCategory.NoPrefix }, + "Call signature expects {0} or fewer parameters.": { "code": 4014, "category": DiagnosticCategory.NoPrefix }, + "Could not apply type '{0}' to argument {1} which is of type '{2}'.": { "code": 4015, "category": DiagnosticCategory.NoPrefix }, + "Class '{0}' defines instance member accessor '{1}', but extended class '{2}' defines it as instance member function.": { "code": 4016, "category": DiagnosticCategory.NoPrefix }, + "Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function.": { "code": 4017, "category": DiagnosticCategory.NoPrefix }, + "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member accessor.": { "code": 4018, "category": DiagnosticCategory.NoPrefix }, + "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member property.": { "code": 4019, "category": DiagnosticCategory.NoPrefix }, + "Types of static property '{0}' of class '{1}' and class '{2}' are incompatible.": { "code": 4020, "category": DiagnosticCategory.NoPrefix }, + "Types of static property '{0}' of class '{1}' and class '{2}' are incompatible:{NL}{3}": { "code": 4021, "category": DiagnosticCategory.NoPrefix }, + "Type reference cannot refer to container '{0}'.": { "code": 4022, "category": DiagnosticCategory.Error }, + "Type reference must refer to type.": { "code": 4023, "category": DiagnosticCategory.Error }, + "In enums with multiple declarations only one declaration can omit an initializer for the first enum element.": { "code": 4024, "category": DiagnosticCategory.Error }, + " (+ {0} overload(s))": { "code": 4025, "category": DiagnosticCategory.Message }, + "Variable declaration cannot have the same name as an import declaration.": { "code": 4026, "category": DiagnosticCategory.Error }, + "Signature expected {0} type arguments, got {1} instead.": { "code": 4027, "category": DiagnosticCategory.Error }, + "Property '{0}' defined as optional in type '{1}', but is required in type '{2}'.": { "code": 4028, "category": DiagnosticCategory.NoPrefix }, + "Types '{0}' and '{1}' originating in infinitely expanding type reference do not refer to same named type.": { "code": 4029, "category": DiagnosticCategory.NoPrefix }, + "Types '{0}' and '{1}' originating in infinitely expanding type reference have incompatible type arguments.": { "code": 4030, "category": DiagnosticCategory.NoPrefix }, + "Types '{0}' and '{1}' originating in infinitely expanding type reference have incompatible type arguments:{NL}{2}": { "code": 4031, "category": DiagnosticCategory.NoPrefix }, + "Named properties '{0}' of types '{1}' and '{2}' are not identical.": { "code": 4032, "category": DiagnosticCategory.NoPrefix }, + "Types of string indexer of types '{0}' and '{1}' are not identical.": { "code": 4033, "category": DiagnosticCategory.NoPrefix }, + "Types of number indexer of types '{0}' and '{1}' are not identical.": { "code": 4034, "category": DiagnosticCategory.NoPrefix }, + "Type of number indexer in type '{0}' is not assignable to string indexer type in type '{1}'.{NL}{2}": { "code": 4035, "category": DiagnosticCategory.NoPrefix }, + "Type of property '{0}' in type '{1}' is not assignable to string indexer type in type '{2}'.{NL}{3}": { "code": 4036, "category": DiagnosticCategory.NoPrefix }, + "Type of property '{0}' in type '{1}' is not assignable to number indexer type in type '{2}'.{NL}{3}": { "code": 4037, "category": DiagnosticCategory.NoPrefix }, + "Static property '{0}' defined as private in type '{1}' is defined as public in type '{2}'.": { "code": 4038, "category": DiagnosticCategory.NoPrefix }, + "Static property '{0}' defined as public in type '{1}' is defined as private in type '{2}'.": { "code": 4039, "category": DiagnosticCategory.NoPrefix }, + "Types '{0}' and '{1}' define static property '{2}' as private.": { "code": 4040, "category": DiagnosticCategory.NoPrefix }, + "Current host does not support '{0}' option.": { "code": 5001, "category": DiagnosticCategory.Error }, + "ECMAScript target version '{0}' not supported. Specify a valid target version: '{1}' (default), or '{2}'": { "code": 5002, "category": DiagnosticCategory.Error }, + "Argument for '{0}' option must be '{1}' or '{2}'": { "code": 5003, "category": DiagnosticCategory.Error }, + "Could not find file: '{0}'.": { "code": 5004, "category": DiagnosticCategory.Error }, + "A file cannot have a reference to itself.": { "code": 5006, "category": DiagnosticCategory.Error }, + "Cannot resolve referenced file: '{0}'.": { "code": 5007, "category": DiagnosticCategory.Error }, + "Cannot find the common subdirectory path for the input files.": { "code": 5009, "category": DiagnosticCategory.Error }, + "Emit Error: {0}.": { "code": 5011, "category": DiagnosticCategory.Error }, + "Cannot read file '{0}': {1}": { "code": 5012, "category": DiagnosticCategory.Error }, + "Unsupported file encoding.": { "code": 5013, "category": DiagnosticCategory.NoPrefix }, + "Locale must be of the form or -. For example '{0}' or '{1}'.": { "code": 5014, "category": DiagnosticCategory.Error }, + "Unsupported locale: '{0}'.": { "code": 5015, "category": DiagnosticCategory.Error }, + "Execution Failed.{NL}": { "code": 5016, "category": DiagnosticCategory.Error }, + "Invalid call to 'up'": { "code": 5019, "category": DiagnosticCategory.Error }, + "Invalid call to 'down'": { "code": 5020, "category": DiagnosticCategory.Error }, + "Base64 value '{0}' finished with a continuation bit.": { "code": 5021, "category": DiagnosticCategory.Error }, + "Unknown compiler option '{0}'": { "code": 5023, "category": DiagnosticCategory.Error }, + "Expected {0} arguments to message, got {1} instead.": { "code": 5024, "category": DiagnosticCategory.Error }, + "Expected the message '{0}' to have {1} arguments, but it had {2}": { "code": 5025, "category": DiagnosticCategory.Error }, + "Could not delete file '{0}'": { "code": 5034, "category": DiagnosticCategory.Error }, + "Could not create directory '{0}'": { "code": 5035, "category": DiagnosticCategory.Error }, + "Error while executing file '{0}': ": { "code": 5036, "category": DiagnosticCategory.Error }, + "Cannot compile external modules unless the '--module' flag is provided.": { "code": 5037, "category": DiagnosticCategory.Error }, + "Option mapRoot cannot be specified without specifying sourcemap option.": { "code": 5038, "category": DiagnosticCategory.Error }, + "Option sourceRoot cannot be specified without specifying sourcemap option.": { "code": 5039, "category": DiagnosticCategory.Error }, + "Options mapRoot and sourceRoot cannot be specified without specifying sourcemap option.": { "code": 5040, "category": DiagnosticCategory.Error }, + "Option '{0}' specified without '{1}'": { "code": 5041, "category": DiagnosticCategory.Error }, + "'codepage' option not supported on current platform.": { "code": 5042, "category": DiagnosticCategory.Error }, + "Concatenate and emit output to single file.": { "code": 6001, "category": DiagnosticCategory.Message }, + "Generates corresponding {0} file.": { "code": 6002, "category": DiagnosticCategory.Message }, + "Specifies the location where debugger should locate map files instead of generated locations.": { "code": 6003, "category": DiagnosticCategory.Message }, + "Specifies the location where debugger should locate TypeScript files instead of source locations.": { "code": 6004, "category": DiagnosticCategory.Message }, + "Watch input files.": { "code": 6005, "category": DiagnosticCategory.Message }, + "Redirect output structure to the directory.": { "code": 6006, "category": DiagnosticCategory.Message }, + "Do not emit comments to output.": { "code": 6009, "category": DiagnosticCategory.Message }, + "Skip resolution and preprocessing.": { "code": 6010, "category": DiagnosticCategory.Message }, + "Specify ECMAScript target version: '{0}' (default), or '{1}'": { "code": 6015, "category": DiagnosticCategory.Message }, + "Specify module code generation: '{0}' or '{1}'": { "code": 6016, "category": DiagnosticCategory.Message }, + "Print this message.": { "code": 6017, "category": DiagnosticCategory.Message }, + "Print the compiler's version: {0}": { "code": 6019, "category": DiagnosticCategory.Message }, + "Allow use of deprecated '{0}' keyword when referencing an external module.": { "code": 6021, "category": DiagnosticCategory.Message }, + "Specify locale for errors and messages. For example '{0}' or '{1}'": { "code": 6022, "category": DiagnosticCategory.Message }, + "Syntax: {0}": { "code": 6023, "category": DiagnosticCategory.Message }, + "options": { "code": 6024, "category": DiagnosticCategory.Message }, + "file1": { "code": 6025, "category": DiagnosticCategory.Message }, + "Examples:": { "code": 6026, "category": DiagnosticCategory.Message }, + "Options:": { "code": 6027, "category": DiagnosticCategory.Message }, + "Insert command line options and files from a file.": { "code": 6030, "category": DiagnosticCategory.Message }, + "Version {0}": { "code": 6029, "category": DiagnosticCategory.Message }, + "Use the '{0}' flag to see options.": { "code": 6031, "category": DiagnosticCategory.Message }, + "{NL}Recompiling ({0}):": { "code": 6032, "category": DiagnosticCategory.Message }, + "STRING": { "code": 6033, "category": DiagnosticCategory.Message }, + "KIND": { "code": 6034, "category": DiagnosticCategory.Message }, + "file2": { "code": 6035, "category": DiagnosticCategory.Message }, + "VERSION": { "code": 6036, "category": DiagnosticCategory.Message }, + "LOCATION": { "code": 6037, "category": DiagnosticCategory.Message }, + "DIRECTORY": { "code": 6038, "category": DiagnosticCategory.Message }, + "NUMBER": { "code": 6039, "category": DiagnosticCategory.Message }, + "Specify the codepage to use when opening source files.": { "code": 6040, "category": DiagnosticCategory.Message }, + "Additional locations:": { "code": 6041, "category": DiagnosticCategory.Message }, + "This version of the Javascript runtime does not support the '{0}' function.": { "code": 7000, "category": DiagnosticCategory.Error }, + "Unknown rule.": { "code": 7002, "category": DiagnosticCategory.Error }, + "Invalid line number ({0})": { "code": 7003, "category": DiagnosticCategory.Error }, + "Warn on expressions and declarations with an implied 'any' type.": { "code": 7004, "category": DiagnosticCategory.Message }, + "Variable '{0}' implicitly has an 'any' type.": { "code": 7005, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of '{1}' implicitly has an 'any' type.": { "code": 7006, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of function type implicitly has an 'any' type.": { "code": 7007, "category": DiagnosticCategory.Error }, + "Member '{0}' of object type implicitly has an 'any' type.": { "code": 7008, "category": DiagnosticCategory.Error }, + "'new' expression, which lacks a constructor signature, implicitly has an 'any' type.": { "code": 7009, "category": DiagnosticCategory.Error }, + "'{0}', which lacks return-type annotation, implicitly has an 'any' return type.": { "code": 7010, "category": DiagnosticCategory.Error }, + "Function expression, which lacks return-type annotation, implicitly has an 'any' return type.": { "code": 7011, "category": DiagnosticCategory.Error }, + "Parameter '{0}' of lambda function implicitly has an 'any' type.": { "code": 7012, "category": DiagnosticCategory.Error }, + "Constructor signature, which lacks return-type annotation, implicitly has an 'any' return type.": { "code": 7013, "category": DiagnosticCategory.Error }, + "Lambda Function, which lacks return-type annotation, implicitly has an 'any' return type.": { "code": 7014, "category": DiagnosticCategory.Error }, + "Array Literal implicitly has an 'any' type from widening.": { "code": 7015, "category": DiagnosticCategory.Error }, + "'{0}', which lacks 'get' accessor and parameter type annotation on 'set' accessor, implicitly has an 'any' type.": { "code": 7016, "category": DiagnosticCategory.Error }, + "Index signature of object type implicitly has an 'any' type.": { "code": 7017, "category": DiagnosticCategory.Error }, + "Object literal's property '{0}' implicitly has an 'any' type from widening.": { "code": 7018, "category": DiagnosticCategory.Error }, + }; +} \ No newline at end of file diff --git a/src/services/resources/diagnosticMessages.json b/src/services/resources/diagnosticMessages.json new file mode 100644 index 00000000000..bbca8b3f9ce --- /dev/null +++ b/src/services/resources/diagnosticMessages.json @@ -0,0 +1,1766 @@ +{ + "error TS{0}: {1}": { + "category": "NoPrefix", + "code": 0 + }, + "warning TS{0}: {1}": { + "category": "NoPrefix", + "code": 1 + }, + "Unrecognized escape sequence.": { + "category": "Error", + "code": 1000 + }, + "Unexpected character {0}.": { + "category": "Error", + "code": 1001 + }, + "Missing close quote character.": { + "category": "Error", + "code": 1002 + }, + "Identifier expected.": { + "category": "Error", + "code": 1003 + }, + "'{0}' keyword expected.": { + "category": "Error", + "code": 1004 + }, + "'{0}' expected.": { + "category": "Error", + "code": 1005 + }, + "Identifier expected; '{0}' is a keyword.": { + "category": "Error", + "code": 1006 + }, + "Automatic semicolon insertion not allowed.": { + "category": "Error", + "code": 1007 + }, + "Unexpected token; '{0}' expected.": { + "category": "Error", + "code": 1008 + }, + "Trailing comma not allowed.": { + "category": "Error", + "code": 1009 + }, + "'*/' expected.": { + "category": "Error", + "code": 1010 + }, + "'public' or 'private' modifier must precede 'static'.": { + "category": "Error", + "code": 1011 + }, + "Unexpected token.": { + "category": "Error", + "code": 1012 + }, + "Catch clause parameter cannot have a type annotation.": { + "category": "Error", + "code": 1013 + }, + "A rest parameter must be last in a parameter list.": { + "category": "Error", + "code": 1014 + }, + "Parameter cannot have question mark and initializer.": { + "category": "Error", + "code": 1015 + }, + "A required parameter cannot follow an optional parameter.": { + "category": "Error", + "code": 1016 + }, + "Index signatures cannot have rest parameters.": { + "category": "Error", + "code": 1017 + }, + "Index signature parameter cannot have accessibility modifiers.": { + "category": "Error", + "code": 1018 + }, + "Index signature parameter cannot have a question mark.": { + "category": "Error", + "code": 1019 + }, + "Index signature parameter cannot have an initializer.": { + "category": "Error", + "code": 1020 + }, + "Index signature must have a type annotation.": { + "category": "Error", + "code": 1021 + }, + "Index signature parameter must have a type annotation.": { + "category": "Error", + "code": 1022 + }, + "Index signature parameter type must be 'string' or 'number'.": { + "category": "Error", + "code": 1023 + }, + "'extends' clause already seen.": { + "category": "Error", + "code": 1024 + }, + "'extends' clause must precede 'implements' clause.": { + "category": "Error", + "code": 1025 + }, + "Classes can only extend a single class.": { + "category": "Error", + "code": 1026 + }, + "'implements' clause already seen.": { + "category": "Error", + "code": 1027 + }, + "Accessibility modifier already seen.": { + "category": "Error", + "code": 1028 + }, + "'{0}' modifier must precede '{1}' modifier.": { + "category": "Error", + "code": 1029 + }, + "'{0}' modifier already seen.": { + "category": "Error", + "code": 1030 + }, + "'{0}' modifier cannot appear on a class element.": { + "category": "Error", + "code": 1031 + }, + "Interface declaration cannot have 'implements' clause.": { + "category": "Error", + "code": 1032 + }, + "'super' invocation cannot have type arguments.": { + "category": "Error", + "code": 1034 + }, + "Only ambient modules can use quoted names.": { + "category": "Error", + "code": 1035 + }, + "Statements are not allowed in ambient contexts.": { + "category": "Error", + "code": 1036 + }, + "A function implementation cannot be declared in an ambient context.": { + "category": "Error", + "code": 1037 + }, + "A 'declare' modifier cannot be used in an already ambient context.": { + "category": "Error", + "code": 1038 + }, + "Initializers are not allowed in ambient contexts.": { + "category": "Error", + "code": 1039 + }, + "'{0}' modifier cannot appear on a module element.": { + "category": "Error", + "code": 1044 + }, + "A 'declare' modifier cannot be used with an interface declaration.": { + "category": "Error", + "code": 1045 + }, + "A 'declare' modifier is required for a top level declaration in a .d.ts file.": { + "category": "Error", + "code": 1046 + }, + "A rest parameter cannot be optional.": { + "category": "Error", + "code": 1047 + }, + "A rest parameter cannot have an initializer.": { + "category": "Error", + "code": 1048 + }, + "'set' accessor must have exactly one parameter.": { + "category": "Error", + "code": 1049 + }, + "'set' accessor parameter cannot be optional.": { + "category": "Error", + "code": 1051 + }, + "'set' accessor parameter cannot have an initializer.": { + "category": "Error", + "code": 1052 + }, + "'set' accessor cannot have rest parameter.": { + "category": "Error", + "code": 1053 + }, + "'get' accessor cannot have parameters.": { + "category": "Error", + "code": 1054 + }, + "Modifiers cannot appear here.": { + "category": "Error", + "code": 1055 + }, + "Accessors are only available when targeting ECMAScript 5 and higher.": { + "category": "Error", + "code": 1056 + }, + "Enum member must have initializer.": { + "category": "Error", + "code": 1061 + }, + "Export assignment cannot be used in internal modules.": { + "category": "Error", + "code": 1063 + }, + "Ambient enum elements can only have integer literal initializers.": { + "category": "Error", + "code": 1066 + }, + "module, class, interface, enum, import or statement": { + "category": "NoPrefix", + "code": 1067 + }, + "constructor, function, accessor or variable": { + "category": "NoPrefix", + "code": 1068 + }, + "statement": { + "category": "NoPrefix", + "code": 1069 + }, + "case or default clause": { + "category": "NoPrefix", + "code": 1070 + }, + "identifier": { + "category": "NoPrefix", + "code": 1071 + }, + "call, construct, index, property or function signature": { + "category": "NoPrefix", + "code": 1072 + }, + "expression": { + "category": "NoPrefix", + "code": 1073 + }, + "type name": { + "category": "NoPrefix", + "code": 1074 + }, + "property or accessor": { + "category": "NoPrefix", + "code": 1075 + }, + "parameter": { + "category": "NoPrefix", + "code": 1076 + }, + "type": { + "category": "NoPrefix", + "code": 1077 + }, + "type parameter": { + "category": "NoPrefix", + "code": 1078 + }, + "A 'declare' modifier cannot be used with an import declaration.": { + "category": "Error", + "code": 1079 + }, + "Invalid 'reference' directive syntax.": { + "category": "Error", + "code": 1084 + }, + "Octal literals are not available when targeting ECMAScript 5 and higher.": { + "category": "Error", + "code": 1085 + }, + "Accessors are not allowed in ambient contexts.": { + "category": "Error", + "code": 1086 + }, + "'{0}' modifier cannot appear on a constructor declaration.": { + "category": "Error", + "code": 1089 + }, + "'{0}' modifier cannot appear on a parameter.": { + "category": "Error", + "code": 1090 + }, + "Only a single variable declaration is allowed in a 'for...in' statement.": { + "category": "Error", + "code": 1091 + }, + "Type parameters cannot appear on a constructor declaration.": { + "category": "Error", + "code": 1092 + }, + "Type annotation cannot appear on a constructor declaration.": { + "category": "Error", + "code": 1093 + }, + "Type parameters cannot appear on an accessor.": { + "category": "Error", + "code": 1094 + }, + "Type annotation cannot appear on a 'set' accessor.": { + "category": "Error", + "code": 1095 + }, + "Index signature must have exactly one parameter.": { + "category": "Error", + "code": 1096 + }, + "'{0}' list cannot be empty.": { + "category": "Error", + "code": 1097 + }, + "variable declaration": { + "category": "NoPrefix", + "code": 1098 + }, + "type argument": { + "category": "NoPrefix", + "code": 1099 + }, + "Invalid use of '{0}' in strict mode.": { + "category": "Error", + "code": 1100 + }, + "'with' statements are not allowed in strict mode.": { + "category": "Error", + "code": 1101 + }, + "'delete' cannot be called on an identifier in strict mode.": { + "category": "Error", + "code": 1102 + }, + "Invalid left-hand side in 'for...in' statement.": { + "category": "Error", + "code": 1103 + }, + "'continue' statement can only be used within an enclosing iteration statement.": { + "category": "Error", + "code": 1104 + }, + "'break' statement can only be used within an enclosing iteration or switch statement.": { + "category": "Error", + "code": 1105 + }, + "Jump target not found.": { + "category": "Error", + "code": 1106 + }, + "Jump target cannot cross function boundary.": { + "category": "Error", + "code": 1107 + }, + "'return' statement must be contained within a function body.": { + "category": "Error", + "code": 1108 + }, + "Expression expected.": { + "category": "Error", + "code": 1109 + }, + "Type expected.": { + "category": "Error", + "code": 1110 + }, + "Duplicate identifier '{0}'.": { + "category": "Error", + "code": 2000 + }, + "The name '{0}' does not exist in the current scope.": { + "category": "Error", + "code": 2001 + }, + "The name '{0}' does not refer to a value.": { + "category": "Error", + "code": 2002 + }, + "'super' can only be used inside a class instance method.": { + "category": "Error", + "code": 2003 + }, + "The left-hand side of an assignment expression must be a variable, property or indexer.": { + "category": "Error", + "code": 2004 + }, + "Value of type '{0}' is not callable. Did you mean to include 'new'?": { + "category": "Error", + "code": 2161 + }, + "Value of type '{0}' is not callable.": { + "category": "Error", + "code": 2006 + }, + "Value of type '{0}' is not newable.": { + "category": "Error", + "code": 2007 + }, + "An index expression argument must be 'string', 'number', or 'any'.": { + "category": "Error", + "code": 2008 + }, + "Operator '{0}' cannot be applied to types '{1}' and '{2}'.": { + "category": "Error", + "code": 2009 + }, + "Type '{0}' is not assignable to type '{1}'.": { + "category": "Error", + "code": 2011 + }, + "Type '{0}' is not assignable to type '{1}':{NL}{2}": { + "category": "Error", + "code": 2012 + }, + "Expected var, class, interface, or module.": { + "category": "Error", + "code": 2013 + }, + "Getter '{0}' already declared.": { + "category": "Error", + "code": 2015 + }, + "Setter '{0}' already declared.": { + "category": "Error", + "code": 2016 + }, + "Exported class '{0}' extends private class '{1}'.": { + "category": "Error", + "code": 2018 + }, + "Exported class '{0}' implements private interface '{1}'.": { + "category": "Error", + "code": 2019 + }, + "Exported interface '{0}' extends private interface '{1}'.": { + "category": "Error", + "code": 2020 + }, + "Exported class '{0}' extends class from inaccessible module {1}.": { + "category": "Error", + "code": 2021 + }, + "Exported class '{0}' implements interface from inaccessible module {1}.": { + "category": "Error", + "code": 2022 + }, + "Exported interface '{0}' extends interface from inaccessible module {1}.": { + "category": "Error", + "code": 2023 + }, + "Public static property '{0}' of exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2024 + }, + "Public property '{0}' of exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2025 + }, + "Property '{0}' of exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2026 + }, + "Exported variable '{0}' has or is using private type '{1}'.": { + "category": "Error", + "code": 2027 + }, + "Public static property '{0}' of exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2028 + }, + "Public property '{0}' of exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2029 + }, + "Property '{0}' of exported interface is using inaccessible module {1}.": { + "category": "Error", + "code": 2030 + }, + "Exported variable '{0}' is using inaccessible module {1}.": { + "category": "Error", + "code": 2031 + }, + "Parameter '{0}' of constructor from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2032 + }, + "Parameter '{0}' of public static property setter from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2033 + }, + "Parameter '{0}' of public property setter from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2034 + }, + "Parameter '{0}' of constructor signature from exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2035 + }, + "Parameter '{0}' of call signature from exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2036 + }, + "Parameter '{0}' of public static method from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2037 + }, + "Parameter '{0}' of public method from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2038 + }, + "Parameter '{0}' of method from exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2039 + }, + "Parameter '{0}' of exported function has or is using private type '{1}'.": { + "category": "Error", + "code": 2040 + }, + "Parameter '{0}' of constructor from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2041 + }, + "Parameter '{0}' of public static property setter from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2042 + }, + "Parameter '{0}' of public property setter from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2043 + }, + "Parameter '{0}' of constructor signature from exported interface is using inaccessible module {1}.": { + "category": "Error", + "code": 2044 + }, + "Parameter '{0}' of call signature from exported interface is using inaccessible module {1}": { + "category": "Error", + "code": 2045 + }, + "Parameter '{0}' of public static method from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2046 + }, + "Parameter '{0}' of public method from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2047 + }, + "Parameter '{0}' of method from exported interface is using inaccessible module {1}.": { + "category": "Error", + "code": 2048 + }, + "Parameter '{0}' of exported function is using inaccessible module {1}.": { + "category": "Error", + "code": 2049 + }, + "Return type of public static property getter from exported class has or is using private type '{0}'.": { + "category": "Error", + "code": 2050 + }, + "Return type of public property getter from exported class has or is using private type '{0}'.": { + "category": "Error", + "code": 2051 + }, + "Return type of constructor signature from exported interface has or is using private type '{0}'.": { + "category": "Error", + "code": 2052 + }, + "Return type of call signature from exported interface has or is using private type '{0}'.": { + "category": "Error", + "code": 2053 + }, + "Return type of index signature from exported interface has or is using private type '{0}'.": { + "category": "Error", + "code": 2054 + }, + "Return type of public static method from exported class has or is using private type '{0}'.": { + "category": "Error", + "code": 2055 + }, + "Return type of public method from exported class has or is using private type '{0}'.": { + "category": "Error", + "code": 2056 + }, + "Return type of method from exported interface has or is using private type '{0}'.": { + "category": "Error", + "code": 2057 + }, + "Return type of exported function has or is using private type '{0}'.": { + "category": "Error", + "code": 2058 + }, + "Return type of public static property getter from exported class is using inaccessible module {0}.": { + "category": "Error", + "code": 2059 + }, + "Return type of public property getter from exported class is using inaccessible module {0}.": { + "category": "Error", + "code": 2060 + }, + "Return type of constructor signature from exported interface is using inaccessible module {0}.": { + "category": "Error", + "code": 2061 + }, + "Return type of call signature from exported interface is using inaccessible module {0}.": { + "category": "Error", + "code": 2062 + }, + "Return type of index signature from exported interface is using inaccessible module {0}.": { + "category": "Error", + "code": 2063 + }, + "Return type of public static method from exported class is using inaccessible module {0}.": { + "category": "Error", + "code": 2064 + }, + "Return type of public method from exported class is using inaccessible module {0}.": { + "category": "Error", + "code": 2065 + }, + "Return type of method from exported interface is using inaccessible module {0}.": { + "category": "Error", + "code": 2066 + }, + "Return type of exported function is using inaccessible module {0}.": { + "category": "Error", + "code": 2067 + }, + "'new T[]' cannot be used to create an array. Use 'new Array()' instead.": { + "category": "Error", + "code": 2068 + }, + "A parameter list must follow a generic type argument list. '(' expected.": { + "category": "Error", + "code": 2069 + }, + "Multiple constructor implementations are not allowed.": { + "category": "Error", + "code": 2070 + }, + "Cannot find external module '{0}'.": { + "category": "Error", + "code": 2071 + }, + "Module cannot be aliased to a non-module type.": { + "category": "Error", + "code": 2072 + }, + "A class may only extend another class.": { + "category": "Error", + "code": 2073 + }, + "A class may only implement another class or interface.": { + "category": "Error", + "code": 2074 + }, + "An interface may only extend a class or another interface.": { + "category": "Error", + "code": 2075 + }, + "Unable to resolve type.": { + "category": "Error", + "code": 2077 + }, + "Unable to resolve type of '{0}'.": { + "category": "Error", + "code": 2078 + }, + "Unable to resolve type parameter constraint.": { + "category": "Error", + "code": 2079 + }, + "Type parameter constraint cannot be a primitive type.": { + "category": "Error", + "code": 2080 + }, + "Supplied parameters do not match any signature of call target.": { + "category": "Error", + "code": 2081 + }, + "Supplied parameters do not match any signature of call target:{NL}{0}": { + "category": "Error", + "code": 2082 + }, + "Cannot use 'new' with an expression whose type lacks a signature.": { + "category": "Error", + "code": 2083 + }, + "Only a void function can be called with the 'new' keyword.": { + "category": "Error", + "code": 2084 + }, + "Could not select overload for 'new' expression.": { + "category": "Error", + "code": 2085 + }, + "Type '{0}' does not satisfy the constraint '{1}'.": { + "category": "Error", + "code": 2086 + }, + "Could not select overload for 'call' expression.": { + "category": "Error", + "code": 2087 + }, + "Cannot invoke an expression whose type lacks a call signature.": { + "category": "Error", + "code": 2088 + }, + "Calls to 'super' are only valid inside a class.": { + "category": "Error", + "code": 2089 + }, + "Generic type '{0}' requires {1} type argument(s).": { + "category": "Error", + "code": 2090 + }, + "Type of array literal cannot be determined. Best common type could not be found for array elements.": { + "category": "Error", + "code": 2092 + }, + "Could not find enclosing symbol for dotted name '{0}'.": { + "category": "Error", + "code": 2093 + }, + "Property '{0}' does not exist on value of type '{1}'.": { + "category": "Error", + "code": 2094 + }, + "Cannot find name '{0}'.": { + "category": "Error", + "code": 2095 + }, + "'get' and 'set' accessor must have the same type.": { + "category": "Error", + "code": 2096 + }, + "'this' cannot be referenced in current location.": { + "category": "Error", + "code": 2097 + }, + "Static members cannot reference class type parameters.": { + "category": "Error", + "code": 2099 + }, + "Type '{0}' recursively references itself as a base type.": { + "category": "Error", + "code": 2100 + }, + "'super' property access is permitted only in a constructor, member function, or member accessor of a derived class.": { + "category": "Error", + "code": 2102 + }, + "'super' can only be referenced in a derived class.": { + "category": "Error", + "code": 2103 + }, + "A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties.": { + "category": "Error", + "code": 2104 + }, + "Constructors for derived classes must contain a 'super' call.": { + "category": "Error", + "code": 2105 + }, + "Super calls are not permitted outside constructors or in nested functions inside constructors.": { + "category": "Error", + "code": 2106 + }, + "'{0}.{1}' is inaccessible.": { + "category": "Error", + "code": 2107 + }, + "'this' cannot be referenced in a module body.": { + "category": "Error", + "code": 2108 + }, + "Invalid '+' expression - types not known to support the addition operator.": { + "category": "Error", + "code": 2111 + }, + "The right-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.": { + "category": "Error", + "code": 2112 + }, + "The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.": { + "category": "Error", + "code": 2113 + }, + "An arithmetic operand must be of type 'any', 'number' or an enum type.": { + "category": "Error", + "code": 2114 + }, + "Variable declarations of a 'for' statement cannot use a type annotation.": { + "category": "Error", + "code": 2115 + }, + "Variable declarations of a 'for' statement must be of types 'string' or 'any'.": { + "category": "Error", + "code": 2116 + }, + "The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter.": { + "category": "Error", + "code": 2117 + }, + "The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": { + "category": "Error", + "code": 2118 + }, + "The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter.": { + "category": "Error", + "code": 2119 + }, + "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.": { + "category": "Error", + "code": 2120 + }, + "The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type.": { + "category": "Error", + "code": 2121 + }, + "Setters cannot return a value.": { + "category": "Error", + "code": 2122 + }, + "Tried to query type of uninitialized module '{0}'.": { + "category": "Error", + "code": 2123 + }, + "Tried to set variable type to uninitialized module type '{0}'.": { + "category": "Error", + "code": 2124 + }, + "Type '{0}' is not generic.": { + "category": "Error", + "code": 2125 + }, + "Getters must return a value.": { + "category": "Error", + "code": 2126 + }, + "Getter and setter accessors do not agree in visibility.": { + "category": "Error", + "code": 2127 + }, + "Invalid left-hand side of assignment expression.": { + "category": "Error", + "code": 2130 + }, + "Function declared a non-void return type, but has no return expression.": { + "category": "Error", + "code": 2131 + }, + "Cannot resolve return type reference.": { + "category": "Error", + "code": 2132 + }, + "Constructors cannot have a return type of 'void'.": { + "category": "Error", + "code": 2133 + }, + "Subsequent variable declarations must have the same type. Variable '{0}' must be of type '{1}', but here has type '{2}'.": { + "category": "Error", + "code": 2134 + }, + "All symbols within a with block will be resolved to 'any'.": { + "category": "Error", + "code": 2135 + }, + "Import declarations in an internal module cannot reference an external module.": { + "category": "Error", + "code": 2136 + }, + "Class {0} declares interface {1} but does not implement it:{NL}{2}": { + "category": "Error", + "code": 2137 + }, + "Class {0} declares class {1} as an interface but does not implement it:{NL}{2}": { + "category": "Error", + "code": 2138 + }, + "The operand of an increment or decrement operator must be a variable, property or indexer.": { + "category": "Error", + "code": 2139 + }, + "'this' cannot be referenced in a static property initializer.": { + "category": "Error", + "code": 2140 + }, + "Class '{0}' cannot extend class '{1}':{NL}{2}": { + "category": "Error", + "code": 2141 + }, + "Interface '{0}' cannot extend class '{1}':{NL}{2}": { + "category": "Error", + "code": 2142 + }, + "Interface '{0}' cannot extend interface '{1}':{NL}{2}": { + "category": "Error", + "code": 2143 + }, + "Overload signature is not compatible with function definition.": { + "category": "Error", + "code": 2148 + }, + "Overload signature is not compatible with function definition:{NL}{0}": { + "category": "Error", + "code": 2149 + }, + "Overload signatures must all be public or private.": { + "category": "Error", + "code": 2150 + }, + "Overload signatures must all be exported or not exported.": { + "category": "Error", + "code": 2151 + }, + "Overload signatures must all be ambient or non-ambient.": { + "category": "Error", + "code": 2152 + }, + "Overload signatures must all be optional or required.": { + "category": "Error", + "code": 2153 + }, + "Specialized overload signature is not assignable to any non-specialized signature.": { + "category": "Error", + "code": 2154 + }, + "'this' cannot be referenced in constructor arguments.": { + "category": "Error", + "code": 2155 + }, + "Instance member cannot be accessed off a class.": { + "category": "Error", + "code": 2157 + }, + "Untyped function calls may not accept type arguments.": { + "category": "Error", + "code": 2158 + }, + "Non-generic functions may not accept type arguments.": { + "category": "Error", + "code": 2159 + }, + "A generic type may not reference itself with a wrapped form of its own type parameters.": { + "category": "Error", + "code": 2160 + }, + "A rest parameter must be of an array type.": { + "category": "Error", + "code": 2162 + }, + "Overload signature implementation cannot use specialized type.": { + "category": "Error", + "code": 2163 + }, + "Export assignments may only be used at the top-level of external modules.": { + "category": "Error", + "code": 2164 + }, + "Export assignments may only be made with variables, functions, classes, interfaces, enums and internal modules.": { + "category": "Error", + "code": 2165 + }, + "Only public methods of the base class are accessible via the 'super' keyword.": { + "category": "Error", + "code": 2166 + }, + "Numeric indexer type '{0}' must be assignable to string indexer type '{1}'.": { + "category": "Error", + "code": 2167 + }, + "Numeric indexer type '{0}' must be assignable to string indexer type '{1}':{NL}{2}": { + "category": "Error", + "code": 2168 + }, + "All numerically named properties must be assignable to numeric indexer type '{0}'.": { + "category": "Error", + "code": 2169 + }, + "All numerically named properties must be assignable to numeric indexer type '{0}':{NL}{1}": { + "category": "Error", + "code": 2170 + }, + "All named properties must be assignable to string indexer type '{0}'.": { + "category": "Error", + "code": 2171 + }, + "All named properties must be assignable to string indexer type '{0}':{NL}{1}": { + "category": "Error", + "code": 2172 + }, + "A parameter initializer is only allowed in a function or constructor implementation.": { + "category": "Error", + "code": 2174 + }, + "Function expression declared a non-void return type, but has no return expression.": { + "category": "Error", + "code": 2176 + }, + "Import declaration referencing identifier from internal module can only be made with variables, functions, classes, interfaces, enums and internal modules.": { + "category": "Error", + "code": 2177 + }, + "Module '{0}' has no exported member '{1}'.": { + "category": "Error", + "code": 2178 + }, + "Unable to resolve module reference '{0}'.": { + "category": "Error", + "code": 2179 + }, + "Could not find module '{0}' in module '{1}'.": { + "category": "Error", + "code": 2180 + }, + "Exported import declaration '{0}' is assigned value with type that has or is using private type '{1}'.": { + "category": "Error", + "code": 2181 + }, + "Exported import declaration '{0}' is assigned value with type that is using inaccessible module '{1}'.": { + "category": "Error", + "code": 2182 + }, + "Exported import declaration '{0}' is assigned type that has or is using private type '{1}'.": { + "category": "Error", + "code": 2183 + }, + "Exported import declaration '{0}' is assigned type that is using inaccessible module '{1}'.": { + "category": "Error", + "code": 2184 + }, + "Exported import declaration '{0}' is assigned container that is or is using inaccessible module '{1}'.": { + "category": "Error", + "code": 2185 + }, + "Type name '{0}' in extends clause does not reference constructor function for '{1}'.": { + "category": "Error", + "code": 2186 + }, + "Internal module reference '{0}' in import declaration does not reference module instance for '{1}'.": { + "category": "Error", + "code": 2187 + }, + "Module '{0}' cannot merge with previous declaration of '{1}' in a different file '{2}'.": { + "category": "Error", + "code": 2188 + }, + "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':{NL}{3}": { + "category": "Error", + "code": 2189 + }, + "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it.": { + "category": "Error", + "code": 2190 + }, + "Ambient external module declaration cannot be reopened.": { + "category": "Error", + "code": 2191 + }, + "All declarations of merged declaration '{0}' must be exported or not exported.": { + "category": "Error", + "code": 2192 + }, + "'super' cannot be referenced in constructor arguments.": { + "category": "Error", + "code": 2193 + }, + "Return type of constructor signature must be assignable to the instance type of the class.":{ + "category": "Error", + "code": 2194 + }, + "Ambient external module declaration must be defined in global context.": { + "category": "Error", + "code": 2195 + }, + "Ambient external module declaration cannot specify relative module name.": { + "category": "Error", + "code": 2196 + }, + "Import declaration in an ambient external module declaration cannot reference external module through relative external module name.": { + "category": "Error", + "code": 2197 + }, + "No best common type exists among return expressions.": { + "category": "Error", + "code": 2198 + }, + "Import declaration cannot refer to external module reference when --noResolve option is set.": { + "category": "Error", + "code": 2199 + }, + "Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.": { + "category": "Error", + "code": 2200 + }, + "Duplicate identifier '_super'. Compiler uses '_super' to capture base class reference.": { + "category": "Error", + "code": 2205 + }, + "Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.": { + "category": "Error", + "code": 2206 + }, + "Expression resolves to '_super' that compiler uses to capture base class reference.": { + "category": "Error", + "code": 2207 + }, + "TypeParameter '{0}' of constructor signature from exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2208 + }, + "TypeParameter '{0}' of call signature from exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2209 + }, + "TypeParameter '{0}' of public static method from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2210 + }, + "TypeParameter '{0}' of public method from exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2211 + }, + "TypeParameter '{0}' of method from exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2212 + }, + "TypeParameter '{0}' of exported function has or is using private type '{1}'.": { + "category": "Error", + "code": 2213 + }, + "TypeParameter '{0}' of constructor signature from exported interface is using inaccessible module {1}.": { + "category": "Error", + "code": 2214 + }, + "TypeParameter '{0}' of call signature from exported interface is using inaccessible module {1}": { + "category": "Error", + "code": 2215 + }, + "TypeParameter '{0}' of public static method from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2216 + }, + "TypeParameter '{0}' of public method from exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2217 + }, + "TypeParameter '{0}' of method from exported interface is using inaccessible module {1}.": { + "category": "Error", + "code": 2218 + }, + "TypeParameter '{0}' of exported function is using inaccessible module {1}.": { + "category": "Error", + "code": 2219 + }, + "TypeParameter '{0}' of exported class has or is using private type '{1}'.": { + "category": "Error", + "code": 2220 + }, + "TypeParameter '{0}' of exported interface has or is using private type '{1}'.": { + "category": "Error", + "code": 2221 + }, + "TypeParameter '{0}' of exported class is using inaccessible module {1}.": { + "category": "Error", + "code": 2222 + }, + "TypeParameter '{0}' of exported interface is using inaccessible module {1}.": { + "category": "Error", + "code": 2223 + }, + "Duplicate identifier '_i'. Compiler uses '_i' to initialize rest parameter.": { + "category": "Error", + "code": 2224 + }, + "Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters.": { + "category": "Error", + "code": 2225 + }, + "No best common type exists between '{0}' and '{1}'.": { + "category": "Error", + "code": 2226 + }, + "No best common type exists between '{0}', '{1}', and '{2}'.": { + "category": "Error", + "code": 2227 + }, + "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of an external module.": { + "category": "Error", + "code": 2228 + }, + "Constraint of a type parameter cannot reference any type parameter from the same type parameter list.": { + "category": "Error", + "code": 2229 + }, + "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.": { + "category": "Error", + "code": 2230 + }, + "Parameter '{0}' cannot be referenced in its initializer.": { + "category": "Error", + "code": 2231 + }, + "Duplicate string index signature.": { + "category": "Error", + "code": 2232 + }, + "Duplicate number index signature.": { + "category": "Error", + "code": 2233 + }, + "All declarations of an interface must have identical type parameters.": { + "category": "Error", + "code": 2234 + }, + "Expression resolves to variable declaration '_i' that compiler uses to initialize rest parameter.": { + "category": "Error", + "code": 2235 + }, + "Neither type '{0}' nor type '{1}' is assignable to the other.": { + "category": "Error", + "code": 2236 + }, + "Neither type '{0}' nor type '{1}' is assignable to the other:{NL}{2}": { + "category": "Error", + "code": 2237 + }, + "Duplicate function implementation.": { + "category": "Error", + "code": 2237 + }, + "Function implementation expected.": { + "category": "Error", + "code": 2238 + }, + "Function overload name must be '{0}'.": { + "category": "Error", + "code": 2239 + }, + "Constructor implementation expected.": { + "category": "Error", + "code": 2240 + }, + "Class name cannot be '{0}'.": { + "category": "Error", + "code": 2241 + }, + "Interface name cannot be '{0}'.": { + "category": "Error", + "code": 2242 + }, + "Enum name cannot be '{0}'.": { + "category": "Error", + "code": 2243 + }, + "A module cannot have multiple export assignments.": { + "category": "Error", + "code": 2244 + }, + "Export assignment not allowed in module with exported element.": { + "category": "Error", + "code": 2245 + }, + "A parameter property is only allowed in a constructor implementation.": { + "category": "Error", + "code": 2246 + }, + "Function overload must be static.": { + "category": "Error", + "code": 2247 + }, + "Function overload must not be static.": { + "category": "Error", + "code": 2248 + }, + "Type '{0}' is missing property '{1}' from type '{2}'.": { + "category": "NoPrefix", + "code": 4000 + }, + "Types of property '{0}' of types '{1}' and '{2}' are incompatible.": { + "category": "NoPrefix", + "code": 4001 + }, + "Types of property '{0}' of types '{1}' and '{2}' are incompatible:{NL}{3}": { + "category": "NoPrefix", + "code": 4002 + }, + "Property '{0}' defined as private in type '{1}' is defined as public in type '{2}'.": { + "category": "NoPrefix", + "code": 4003 + }, + "Property '{0}' defined as public in type '{1}' is defined as private in type '{2}'.": { + "category": "NoPrefix", + "code": 4004 + }, + "Types '{0}' and '{1}' define property '{2}' as private.": { + "category": "NoPrefix", + "code": 4005 + }, + "Call signatures of types '{0}' and '{1}' are incompatible.": { + "category": "NoPrefix", + "code": 4006 + }, + "Call signatures of types '{0}' and '{1}' are incompatible:{NL}{2}": { + "category": "NoPrefix", + "code": 4007 + }, + "Type '{0}' requires a call signature, but type '{1}' lacks one.": { + "category": "NoPrefix", + "code": 4008 + }, + "Construct signatures of types '{0}' and '{1}' are incompatible.": { + "category": "NoPrefix", + "code": 4009 + }, + "Construct signatures of types '{0}' and '{1}' are incompatible:{NL}{2}": { + "category": "NoPrefix", + "code": 4010 + }, + "Type '{0}' requires a construct signature, but type '{1}' lacks one.": { + "category": "NoPrefix", + "code": 4011 + }, + "Index signatures of types '{0}' and '{1}' are incompatible.": { + "category": "NoPrefix", + "code": 4012 + }, + "Index signatures of types '{0}' and '{1}' are incompatible:{NL}{2}": { + "category": "NoPrefix", + "code": 4013 + }, + "Call signature expects {0} or fewer parameters.": { + "category": "NoPrefix", + "code": 4014 + }, + "Could not apply type '{0}' to argument {1} which is of type '{2}'.": { + "category": "NoPrefix", + "code": 4015 + }, + "Class '{0}' defines instance member accessor '{1}', but extended class '{2}' defines it as instance member function.": { + "category": "NoPrefix", + "code": 4016 + }, + "Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function.": { + "category": "NoPrefix", + "code": 4017 + }, + "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member accessor.": { + "category": "NoPrefix", + "code": 4018 + }, + "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member property.": { + "category": "NoPrefix", + "code": 4019 + }, + "Types of static property '{0}' of class '{1}' and class '{2}' are incompatible.": { + "category": "NoPrefix", + "code": 4020 + }, + "Types of static property '{0}' of class '{1}' and class '{2}' are incompatible:{NL}{3}": { + "category": "NoPrefix", + "code": 4021 + }, + "Type reference cannot refer to container '{0}'.": { + "category": "Error", + "code": 4022 + }, + "Type reference must refer to type.": { + "category": "Error", + "code": 4023 + }, + "In enums with multiple declarations only one declaration can omit an initializer for the first enum element.": { + "category": "Error", + "code": 4024 + }, + " (+ {0} overload(s))": { + "category": "Message", + "code": 4025 + }, + "Variable declaration cannot have the same name as an import declaration.": { + "category": "Error", + "code": 4026 + }, + "Signature expected {0} type arguments, got {1} instead.": { + "category": "Error", + "code": 4027 + }, + "Property '{0}' defined as optional in type '{1}', but is required in type '{2}'.": { + "category": "NoPrefix", + "code": 4028 + }, + "Types '{0}' and '{1}' originating in infinitely expanding type reference do not refer to same named type.": { + "category": "NoPrefix", + "code": 4029 + }, + "Types '{0}' and '{1}' originating in infinitely expanding type reference have incompatible type arguments.": { + "category": "NoPrefix", + "code": 4030 + }, + "Types '{0}' and '{1}' originating in infinitely expanding type reference have incompatible type arguments:{NL}{2}": { + "category": "NoPrefix", + "code": 4031 + }, + "Named properties '{0}' of types '{1}' and '{2}' are not identical.": { + "category": "NoPrefix", + "code": 4032 + }, + "Types of string indexer of types '{0}' and '{1}' are not identical.": { + "category": "NoPrefix", + "code": 4033 + }, + "Types of number indexer of types '{0}' and '{1}' are not identical.": { + "category": "NoPrefix", + "code": 4034 + }, + "Type of number indexer in type '{0}' is not assignable to string indexer type in type '{1}'.{NL}{2}": { + "category": "NoPrefix", + "code": 4035 + }, + "Type of property '{0}' in type '{1}' is not assignable to string indexer type in type '{2}'.{NL}{3}": { + "category": "NoPrefix", + "code": 4036 + }, + "Type of property '{0}' in type '{1}' is not assignable to number indexer type in type '{2}'.{NL}{3}": { + "category": "NoPrefix", + "code": 4037 + }, + "Static property '{0}' defined as private in type '{1}' is defined as public in type '{2}'.": { + "category": "NoPrefix", + "code": 4038 + }, + "Static property '{0}' defined as public in type '{1}' is defined as private in type '{2}'.": { + "category": "NoPrefix", + "code": 4039 + }, + "Types '{0}' and '{1}' define static property '{2}' as private.": { + "category": "NoPrefix", + "code": 4040 + }, + "Current host does not support '{0}' option.": { + "category": "Error", + "code": 5001 + }, + "ECMAScript target version '{0}' not supported. Specify a valid target version: '{1}' (default), or '{2}'": { + "category": "Error", + "code": 5002 + }, + "Argument for '{0}' option must be '{1}' or '{2}'": { + "category": "Error", + "code": 5003 + }, + "Could not find file: '{0}'.": { + "category": "Error", + "code": 5004 + }, + "A file cannot have a reference to itself.": { + "category": "Error", + "code": 5006 + }, + "Cannot resolve referenced file: '{0}'.": { + "category": "Error", + "code": 5007 + }, + "Cannot find the common subdirectory path for the input files.": { + "category": "Error", + "code": 5009 + }, + "Emit Error: {0}.": { + "category": "Error", + "code": 5011 + }, + "Cannot read file '{0}': {1}": { + "category": "Error", + "code": 5012 + }, + "Unsupported file encoding.": { + "category": "NoPrefix", + "code": 5013 + }, + "Locale must be of the form or -. For example '{0}' or '{1}'.": { + "category": "Error", + "code": 5014 + }, + "Unsupported locale: '{0}'.": { + "category": "Error", + "code": 5015 + }, + "Execution Failed.{NL}": { + "category": "Error", + "code": 5016 + }, + "Invalid call to 'up'": { + "category": "Error", + "code": 5019 + }, + "Invalid call to 'down'": { + "category": "Error", + "code": 5020 + }, + "Base64 value '{0}' finished with a continuation bit.": { + "category": "Error", + "code": 5021 + }, + "Unknown compiler option '{0}'": { + "category": "Error", + "code": 5023 + }, + "Expected {0} arguments to message, got {1} instead.": { + "category": "Error", + "code": 5024 + }, + "Expected the message '{0}' to have {1} arguments, but it had {2}": { + "category": "Error", + "code": 5025 + }, + "Could not delete file '{0}'": { + "category": "Error", + "code": 5034 + }, + "Could not create directory '{0}'": { + "category": "Error", + "code": 5035 + }, + "Error while executing file '{0}': ": { + "category": "Error", + "code": 5036 + }, + "Cannot compile external modules unless the '--module' flag is provided.": { + "category": "Error", + "code": 5037 + }, + "Option mapRoot cannot be specified without specifying sourcemap option.": { + "category": "Error", + "code": 5038 + }, + "Option sourceRoot cannot be specified without specifying sourcemap option.": { + "category": "Error", + "code": 5039 + }, + "Options mapRoot and sourceRoot cannot be specified without specifying sourcemap option.": { + "category": "Error", + "code": 5040 + }, + "Option '{0}' specified without '{1}'": { + "category": "Error", + "code": 5041 + }, + "'codepage' option not supported on current platform.": { + "category": "Error", + "code": 5042 + }, + "Concatenate and emit output to single file.": { + "category": "Message", + "code": 6001 + }, + "Generates corresponding {0} file.": { + "category": "Message", + "code": 6002 + }, + "Specifies the location where debugger should locate map files instead of generated locations.": { + "category": "Message", + "code": 6003 + }, + "Specifies the location where debugger should locate TypeScript files instead of source locations.": { + "category": "Message", + "code": 6004 + }, + "Watch input files.": { + "category": "Message", + "code": 6005 + }, + "Redirect output structure to the directory.": { + "category": "Message", + "code": 6006 + }, + "Do not emit comments to output.": { + "category": "Message", + "code": 6009 + }, + "Skip resolution and preprocessing.": { + "category": "Message", + "code": 6010 + }, + "Specify ECMAScript target version: '{0}' (default), or '{1}'": { + "category": "Message", + "code": 6015 + }, + "Specify module code generation: '{0}' or '{1}'": { + "category": "Message", + "code": 6016 + }, + "Print this message.": { + "category": "Message", + "code": 6017 + }, + "Print the compiler's version: {0}": { + "category": "Message", + "code": 6019 + }, + "Allow use of deprecated '{0}' keyword when referencing an external module.": { + "category": "Message", + "code": 6021 + }, + "Specify locale for errors and messages. For example '{0}' or '{1}'": { + "category": "Message", + "code": 6022 + }, + "Syntax: {0}": { + "category": "Message", + "code": 6023 + }, + "options": { + "category": "Message", + "code": 6024 + }, + "file": { + "category": "Message", + "code": 6025 + }, + "Examples:": { + "category": "Message", + "code": 6026 + }, + "Options:": { + "category": "Message", + "code": 6027 + }, + "Insert command line options and files from a file.": { + "category": "Message", + "code": 6028 + }, + "Version {0}": { + "category": "Message", + "code": 6029 + }, + "Insert command line options and files from a file.": { + "category": "Message", + "code": 6030 + }, + "Use the '{0}' flag to see options.": { + "category": "Message", + "code": 6031 + }, + "{NL}Recompiling ({0}):": { + "category": "Message", + "code": 6032 + }, + "STRING": { + "category": "Message", + "code": 6033 + }, + "KIND": { + "category": "Message", + "code": 6034 + }, + "FILE": { + "category": "Message", + "code": 6035 + }, + "VERSION": { + "category": "Message", + "code": 6036 + }, + "LOCATION": { + "category": "Message", + "code": 6037 + }, + "DIRECTORY": { + "category": "Message", + "code": 6038 + }, + "NUMBER": { + "category": "Message", + "code": 6039 + }, + "Specify the codepage to use when opening source files.": { + "category": "Message", + "code": 6040 + }, + "Additional locations:": { + "category": "Message", + "code": 6041 + }, + "This version of the Javascript runtime does not support the '{0}' function.": { + "category": "Error", + "code": 7000 + }, + "Unknown rule.": { + "category": "Error", + "code": 7002 + }, + "Invalid line number ({0})": { + "category": "Error", + "code": 7003 + }, + "Warn on expressions and declarations with an implied 'any' type.": { + "category": "Message", + "code": 7004 + }, + "Variable '{0}' implicitly has an 'any' type.": { + "category": "Error", + "code": 7005 + }, + "Parameter '{0}' of '{1}' implicitly has an 'any' type.": { + "category": "Error", + "code": 7006 + }, + "Parameter '{0}' of function type implicitly has an 'any' type.": { + "category": "Error", + "code": 7007 + }, + "Member '{0}' of object type implicitly has an 'any' type.": { + "category": "Error", + "code": 7008 + }, + "'new' expression, which lacks a constructor signature, implicitly has an 'any' type.": { + "category": "Error", + "code": 7009 + }, + "'{0}', which lacks return-type annotation, implicitly has an 'any' return type.":{ + "category": "Error", + "code": 7010 + }, + "Function expression, which lacks return-type annotation, implicitly has an 'any' return type.":{ + "category": "Error", + "code": 7011 + }, + "Parameter '{0}' of lambda function implicitly has an 'any' type.":{ + "category": "Error", + "code": 7012 + }, + "Constructor signature, which lacks return-type annotation, implicitly has an 'any' return type.":{ + "category": "Error", + "code": 7013 + }, + "Lambda Function, which lacks return-type annotation, implicitly has an 'any' return type.":{ + "category": "Error", + "code": 7014 + }, + "Array Literal implicitly has an 'any' type from widening.":{ + "category": "Error", + "code": 7015 + }, + "'{0}', which lacks 'get' accessor and parameter type annotation on 'set' accessor, implicitly has an 'any' type.":{ + "category": "Error", + "code": 7016 + }, + "Index signature of object type implicitly has an 'any' type.": { + "category": "Error", + "code": 7017 + }, + "Object literal's property '{0}' implicitly has an 'any' type from widening.":{ + "category": "Error", + "code": 7018 + }, +} \ No newline at end of file diff --git a/src/services/resources/references.ts b/src/services/resources/references.ts new file mode 100644 index 00000000000..0624c396fb3 --- /dev/null +++ b/src/services/resources/references.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/src/services/services.ts b/src/services/services.ts index 69685d79fc0..1c60f2c6ba0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4,6 +4,27 @@ /// /// +/// +/// +/// +/// +/// +/// +/// +/// + +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + module ts { export interface Node { @@ -11,8 +32,12 @@ module ts { getChildCount(): number; getChildAt(index: number): Node; getChildren(): Node[]; + getStart(): number; + getFullStart(): number; + getEnd(): number; + getWidth(): number; getFullWidth(): number; - getTriviaWidth(): number; + getLeadingTriviaWidth(): number; getFullText(): string; getFirstToken(): Node; getLastToken(): Node; @@ -28,6 +53,7 @@ module ts { getFlags(): TypeFlags; getSymbol(): Symbol; getProperties(): Symbol[]; + getApparentProperties(): Symbol[]; getCallSignatures(): Signature[]; getConstructSignatures(): Signature[]; getStringIndexType(): Type; @@ -41,88 +67,6 @@ module ts { getReturnType(): Type; } - interface HostFileInformation { - version: string; - isOpen: boolean; - byteOrderMark: ByteOrderMark; - sourceText?: IScriptSnapshot; - } - - // - // Public services of a language service instance associated - // with a language service host instance - // - export interface LanguageService { - getSyntacticDiagnostics(filename: string): Diagnostic[]; - getSemanticDiagnostics(filename: string): Diagnostic[]; - } - - // - // Public interface of the host of a language service instance. - // - export interface LanguageServiceHost { - log(s: string): void; - - getCompilationSettings(): CompilerOptions; - - getScriptFileNames(): string[]; - getScriptVersion(filename: string): string; - getScriptIsOpen(filename: string): boolean; - getScriptByteOrderMark(filename: string): ByteOrderMark; - getScriptSnapshot(filename: string): IScriptSnapshot; - getLocalizedDiagnosticMessages(): any; - //getCancellationToken(): CancellationToken; - } - - // Represents an immutable snapshot of a script at a specified time. Once acquired, the - // snapshot is observably immutable. i.e. the same calls with the same parameters will return - // the same values. - export interface IScriptSnapshot { - // Get's a portion of the script snapshot specified by [start, end). - getText(start: number, end: number): string; - - // Get's the length of this script snapshot. - getLength(): number; - - // This call returns the array containing the start position of every line. - // i.e."[0, 10, 55]". TODO: consider making this optional. The language service could - // always determine this (albeit in a more expensive manner). - getLineStartPositions(): number[]; - - // Gets the TextChangeRange that describe how the text changed between this text and - // an older version. This informatoin is used by the incremental parser to determine - // what sections of the script need to be reparsed. 'null' can be returned if the - // change range cannot be determined. However, in that case, incremental parsing will - // not happen and the entire document will be reparsed. - getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange; - } - - export interface Span { - start(): number; - end(): number; - } - - export interface TextChange { - span: Span; - newText: string; - } - - export interface TextChangeRange { - span(): Span; - newLength(): number; - } - - export enum ByteOrderMark { - None = 0, - Utf8 = 1, - Utf16BigEndian = 2, - Utf16LittleEndian = 3, - } - - export interface CancellationToken { - isCancellationRequested(): boolean; - } - var scanner: Scanner = createScanner(ScriptTarget.ES5); var emptyArray: any [] = []; @@ -151,16 +95,28 @@ module ts { return node; } - public getTextPos(): number { + public getStart(): number { return getTokenPosOfNode(this); } - - public getFullWidth(): number { - return this.end - this.pos; + + public getFullStart(): number { + return this.pos; } - public getTriviaWidth(): number { - return getTokenPosOfNode(this) - this.pos; + public getEnd(): number { + return this.end; + } + + public getWidth(): number { + return this.getEnd() - this.getStart(); + } + + public getFullWidth(): number { + return this.end - this.getFullStart(); + } + + public getLeadingTriviaWidth(): number { + return this.getStart() - this.pos; } public getFullText(): string { @@ -295,6 +251,9 @@ module ts { getProperties(): Symbol[] { return this.checker.getPropertiesOfType(this); } + getApparentProperties(): Symbol[]{ + return this.checker.getAugmentedPropertiesOfApparentType(this); + } getCallSignatures(): Signature[] { return this.checker.getSignaturesOfType(this, SignatureKind.Call); } @@ -335,83 +294,1921 @@ module ts { } } - export function createLanguageService(host: LanguageServiceHost): LanguageService { + export interface Logger { + information(): boolean; + debug(): boolean; + warning(): boolean; + error(): boolean; + fatal(): boolean; + log(s: string): void; + } + // + // Public interface of the host of a language service instance. + // + export interface LanguageServiceHost extends Logger { + getCompilationSettings(): CompilerOptions; + getScriptFileNames(): string[]; + getScriptVersion(fileName: string): number; + getScriptIsOpen(fileName: string): boolean; + getScriptByteOrderMark(fileName: string): ByteOrderMark; + getScriptSnapshot(fileName: string): TypeScript.IScriptSnapshot; + getLocalizedDiagnosticMessages(): any; + getCancellationToken(): CancellationToken; + } + + // + // Public services of a language service instance associated + // with a language service host instance + // + export interface LanguageService { + // Note: refresh is a no-op now. It is only around for back compat purposes. + refresh(): void; + + cleanupSemanticCache(): void; + + getSyntacticDiagnostics(fileName: string): Diagnostic[]; + getSemanticDiagnostics(fileName: string): Diagnostic[]; + getCompilerOptionsDiagnostics(): Diagnostic[]; + + getCompletionsAtPosition(fileName: string, position: number, isMemberCompletion: boolean): CompletionInfo; + getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails; + + getTypeAtPosition(fileName: string, position: number): TypeInfo; + + getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): SpanInfo; + + getBreakpointStatementAtPosition(fileName: string, position: number): SpanInfo; + + getSignatureAtPosition(fileName: string, position: number): SignatureInfo; + + getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; + getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + getImplementorsAtPosition(fileName: string, position: number): ReferenceEntry[]; + + getNavigateToItems(searchValue: string): NavigateToItem[]; + getScriptLexicalStructure(fileName: string): NavigateToItem[]; + + getOutliningRegions(fileName: string): TypeScript.TextSpan[]; + getBraceMatchingAtPosition(fileName: string, position: number): TypeScript.TextSpan[]; + getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number; + + getFormattingEditsForRange(fileName: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[]; + getFormattingEditsForDocument(fileName: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[]; + getFormattingEditsOnPaste(fileName: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[]; + getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextEdit[]; + + getEmitOutput(fileName: string): EmitOutput; + + //getSyntaxTree(fileName: string): TypeScript.SyntaxTree; + + dispose(): void; + } + + export interface ReferenceEntry { + fileName: string; + minChar: number; + limChar: number; + isWriteAccess: boolean; + } + + export interface NavigateToItem { + name: string; + kind: string; // see ScriptElementKind + kindModifiers: string; // see ScriptElementKindModifier, comma separated + matchKind: string; + fileName: string; + minChar: number; + limChar: number; + additionalSpans?: SpanInfo[]; + containerName: string; + containerKind: string; // see ScriptElementKind + } + + export interface TextEdit { + minChar: number; + limChar: number; + text: string; + } + + export interface EditorOptions { + IndentSize: number; + TabSize: number; + NewLineCharacter: string; + ConvertTabsToSpaces: boolean; + } + + export interface FormatCodeOptions extends EditorOptions { + InsertSpaceAfterCommaDelimiter: boolean; + InsertSpaceAfterSemicolonInForStatements: boolean; + InsertSpaceBeforeAndAfterBinaryOperators: boolean; + InsertSpaceAfterKeywordsInControlFlowStatements: boolean; + InsertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean; + InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean; + PlaceOpenBraceOnNewLineForFunctions: boolean; + PlaceOpenBraceOnNewLineForControlBlocks: boolean; + } + + export interface DefinitionInfo { + fileName: string; + minChar: number; + limChar: number; + kind: string; + name: string; + containerKind: string; + containerName: string; + } + + export interface MemberName { + prefix: string; + suffix: string; + text: string; + } + + export interface TypeInfo { + memberName: MemberName; + docComment: string; + fullSymbolName: string; + kind: string; + minChar: number; + limChar: number; + } + + export interface SpanInfo { + minChar: number; + limChar: number; + // text?: string; + } + + export interface SignatureInfo { + actual: ActualSignatureInfo; + formal: FormalSignatureItemInfo[]; // Formal signatures + activeFormal: number; // Index of the "best match" formal signature + } + + export interface FormalSignatureItemInfo { + signatureInfo: string; + typeParameters: FormalTypeParameterInfo[]; + parameters: FormalParameterInfo[]; // Array of parameters + docComment: string; // Help for the signature + } + + export interface FormalTypeParameterInfo { + name: string; // Type parameter name + docComment: string; // Comments that contain help for the parameter + minChar: number; // minChar for parameter info in the formal signature info string + limChar: number; // lim char for parameter info in the formal signature info string + } + + export interface FormalParameterInfo { + name: string; // Parameter name + isVariable: boolean; // true if parameter is var args + docComment: string; // Comments that contain help for the parameter + minChar: number; // minChar for parameter info in the formal signature info string + limChar: number; // lim char for parameter info in the formal signature info string + } + + export interface ActualSignatureInfo { + parameterMinChar: number; + parameterLimChar: number; + currentParameterIsTypeParameter: boolean; // current parameter is a type argument or a normal argument + currentParameter: number; // Index of active parameter in "parameters" or "typeParamters" array + } + + export interface CompletionInfo { + isMemberCompletion: boolean; + entries: CompletionEntry[]; + } + + export interface CompletionEntry { + name: string; + kind: string; // see ScriptElementKind + kindModifiers: string; // see ScriptElementKindModifier, comma separated + } + + export interface CompletionEntryDetails { + name: string; + kind: string; // see ScriptElementKind + kindModifiers: string; // see ScriptElementKindModifier, comma separated + type: string; + fullSymbolName: string; + docComment: string; + } + + export enum EmitOutputResult { + Succeeded, + FailedBecauseOfSyntaxErrors, + FailedBecauseOfCompilerOptionsErrors, + FailedToGenerateDeclarationsBecauseOfSemanticErrors + } + + export interface EmitOutput { + outputFiles: OutputFile[]; + emitOutputResult: EmitOutputResult; + } + + export enum OutputFileType { + JavaScript, + SourceMap, + Declaration + } + + export interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + fileType: OutputFileType; + sourceMapOutput: any; + } + + export enum EndOfLineState { + Start, + InMultiLineCommentTrivia, + InSingleQuoteStringLiteral, + InDoubleQuoteStringLiteral, + } + + export enum TokenClass { + Punctuation, + Keyword, + Operator, + Comment, + Whitespace, + Identifier, + NumberLiteral, + StringLiteral, + RegExpLiteral, + } + + export interface ClassificationResult { + finalLexState: EndOfLineState; + entries: ClassificationInfo[]; + } + + export interface ClassificationInfo { + length: number; + classification: TokenClass; + } + + export interface Classifier { + getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult; + } + + export interface DocumentRegistry { + acquireDocument( + filename: string, + compilationSettings: CompilerOptions, + scriptSnapshot: TypeScript.IScriptSnapshot, + byteOrderMark: ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]): Document; + + updateDocument( + document: Document, + filename: string, + compilationSettings: CompilerOptions, + scriptSnapshot: TypeScript.IScriptSnapshot, + version: number, + isOpen: boolean, + textChangeRange: TypeScript.TextChangeRange + ): Document; + + releaseDocument(filename: string, compilationSettings: CompilerOptions): void + } + + // TODO: move these to enums + export class ScriptElementKind { + static unknown = ""; + + // predefined type (void) or keyword (class) + static keyword = "keyword"; + + // top level script node + static scriptElement = "script"; + + // module foo {} + static moduleElement = "module"; + + // class X {} + static classElement = "class"; + + // interface Y {} + static interfaceElement = "interface"; + + // enum E + static enumElement = "enum"; + + // Inside module and script only + // var v = .. + static variableElement = "var"; + + // Inside function + static localVariableElement = "local var"; + + // Inside module and script only + // function f() { } + static functionElement = "function"; + + // Inside function + static localFunctionElement = "local function"; + + // class X { [public|private]* foo() {} } + static memberFunctionElement = "method"; + + // class X { [public|private]* [get|set] foo:number; } + static memberGetAccessorElement = "getter"; + static memberSetAccessorElement = "setter"; + + // class X { [public|private]* foo:number; } + // interface Y { foo:number; } + static memberVariableElement = "property"; + + // class X { constructor() { } } + static constructorImplementationElement = "constructor"; + + // interface Y { ():number; } + static callSignatureElement = "call"; + + // interface Y { []:number; } + static indexSignatureElement = "index"; + + // interface Y { new():Y; } + static constructSignatureElement = "construct"; + + // function foo(*Y*: string) + static parameterElement = "parameter"; + + static typeParameterElement = "type parameter"; + + static primitiveType = "primitive type"; + } + + export class ScriptElementKindModifier { + static none = ""; + static publicMemberModifier = "public"; + static privateMemberModifier = "private"; + static exportedModifier = "export"; + static ambientModifier = "declare"; + static staticModifier = "static"; + } + + export class MatchKind { + static none: string = null; + static exact = "exact"; + static subString = "substring"; + static prefix = "prefix"; + } + + interface IncrementalParse { + (oldSyntaxTree: TypeScript.SyntaxTree, textChangeRange: TypeScript.TextChangeRange, newText: TypeScript.ISimpleText): TypeScript.SyntaxTree + } + + export interface Document { + getFilename(): string; + getByteOrderMark(): ByteOrderMark; + getVersion(): number; + isOpen(): boolean; + getSourceUnit(): TypeScript.SourceUnitSyntax; + getSyntaxTree(): TypeScript.SyntaxTree; + getSourceFile(): SourceFile; + getBloomFilter(): TypeScript.BloomFilter; + update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): Document; + } + + class DocumentObject implements Document { + private bloomFilter: TypeScript.BloomFilter = null; + + // By default, our Document class doesn't support incremental update of its conten + // However, we enable other layers (like teh services layer) to inject the capability + // into us by setting this function. + public static incrementalParse: IncrementalParse = TypeScript.IncrementalParser.parse; + + constructor(private compilationSettings: CompilerOptions, + public filename: string, + public referencedFiles: string[], + private scriptSnapshot: TypeScript.IScriptSnapshot, + public byteOrderMark: ByteOrderMark, + public version: number, + public _isOpen: boolean, + private syntaxTree: TypeScript.SyntaxTree, + private soruceFile: SourceFile) { + } + + public getFilename(): string { + return this.filename; + } + + public getVersion(): number { + return this.version; + } + + public isOpen(): boolean { + return this._isOpen; + } + + public getByteOrderMark(): ByteOrderMark { + return this.byteOrderMark; + } + public isDeclareFile(): boolean { + return TypeScript.isDTSFile(this.filename); + } + + public getSourceUnit(): TypeScript.SourceUnitSyntax { + // If we don't have a script, create one from our parse tree. + return this.getSyntaxTree().sourceUnit(); + } + + public getLineMap(): TypeScript.LineMap { + return this.getSyntaxTree().lineMap(); + } + + public getSyntaxTree(): TypeScript.SyntaxTree { + if (!this.syntaxTree) { + var start = new Date().getTime(); + + this.syntaxTree = TypeScript.Parser.parse( + this.filename, TypeScript.SimpleText.fromScriptSnapshot(this.scriptSnapshot), this.compilationSettings.target, this.isDeclareFile()); + + var time = new Date().getTime() - start; + + //TypeScript.syntaxTreeParseTime += time; + } + + return this.syntaxTree; + } + + public getSourceFile(): SourceFile { + if (!this.soruceFile) { + var start = new Date().getTime(); + + this.soruceFile = createSourceFile(this.filename, this.scriptSnapshot.getText(0, this.scriptSnapshot.getLength()), this.compilationSettings.target); + + var time = new Date().getTime() - start; + + //TypeScript.astParseTime += time; + } + + return this.soruceFile; + } + + public getBloomFilter(): TypeScript.BloomFilter { + if (!this.bloomFilter) { + var identifiers = TypeScript.createIntrinsicsObject(); + var pre = function (cur: TypeScript.ISyntaxElement) { + if (TypeScript.ASTHelpers.isValidAstNode(cur)) { + if (cur.kind() === TypeScript.SyntaxKind.IdentifierName) { + var nodeText = TypeScript.tokenValueText((cur)); + + identifiers[nodeText] = true; + } + } + }; + + TypeScript.getAstWalkerFactory().simpleWalk(this.getSourceUnit(), pre, null, identifiers); + + var identifierCount = 0; + for (var name in identifiers) { + if (identifiers[name]) { + identifierCount++; + } + } + + this.bloomFilter = new TypeScript.BloomFilter(identifierCount); + this.bloomFilter.addKeys(identifiers); + } + return this.bloomFilter; + } + + // Returns true if this file should get emitted into its own unique output file. + // Otherwise, it should be written into a single output file along with the rest of hte + // documents in the compilation. + public emitToOwnOutputFile(): boolean { + // If we haven't specified an output file in our settings, then we're definitely + // emitting to our own file. Also, if we're an external module, then we're + // definitely emitting to our own file. + return !this.compilationSettings.out || this.getSyntaxTree().isExternalModule(); + } + + public update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): Document { + // See if we are currently holding onto a syntax tree. We may not be because we're + // either a closed file, or we've just been lazy and haven't had to create the syntax + // tree yet. Access the field instead of the method so we don't accidently realize + // the old syntax tree. + var oldSyntaxTree = this.syntaxTree; + + if (textChangeRange !== null && Debug.shouldAssert(AssertionLevel.Normal)) { + var oldText = this.scriptSnapshot; + var newText = scriptSnapshot; + + TypeScript.Debug.assert((oldText.getLength() - textChangeRange.span().length() + textChangeRange.newLength()) === newText.getLength()); + + if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) { + var oldTextPrefix = oldText.getText(0, textChangeRange.span().start()); + var newTextPrefix = newText.getText(0, textChangeRange.span().start()); + TypeScript.Debug.assert(oldTextPrefix === newTextPrefix); + + var oldTextSuffix = oldText.getText(textChangeRange.span().end(), oldText.getLength()); + var newTextSuffix = newText.getText(textChangeRange.newSpan().end(), newText.getLength()); + TypeScript.Debug.assert(oldTextSuffix === newTextSuffix); + } + } + + var text = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot); + + // If we don't have a text change, or we don't have an old syntax tree, then do a full + // parse. Otherwise, do an incremental parse. + var newSyntaxTree = textChangeRange === null || oldSyntaxTree === null || DocumentObject.incrementalParse === null + ? TypeScript.Parser.parse(this.filename, text, this.compilationSettings.target, TypeScript.isDTSFile(this.filename)) + : DocumentObject.incrementalParse(oldSyntaxTree, textChangeRange, text); + + return new DocumentObject(this.compilationSettings, this.filename, this.referencedFiles, scriptSnapshot, this.byteOrderMark, version, isOpen, newSyntaxTree, /*soruceFile*/ null); + } + } + + export function createDocument(compilationSettings: CompilerOptions, fileName: string, scriptSnapshot: TypeScript.IScriptSnapshot, byteOrderMark: ByteOrderMark, version: number, isOpen: boolean, referencedFiles: string[]): Document { + return new DocumentObject(compilationSettings, fileName, referencedFiles, scriptSnapshot, byteOrderMark, version, isOpen, /*syntaxTree:*/ null, /*soruceFile*/ null); + } + + /// Language Service + + interface CompletionSession { + filename: string; // the file where the completion was requested + position: number; // position in the file where the completion was requested + entries: CompletionEntry[]; // entries for this completion + symbols: Map; // symbols by entry name map + location: Node; // the node where the completion was requested + typeChecker: TypeChecker;// the typeChecker used to generate this completion + } + + interface FormattingOptions { + useTabs: boolean; + spacesPerTab: number; + indentSpaces: number; + newLineCharacter: string; + } + + // Information about a specific host file. + interface HostFileInformation { + filename: string; + version: number; + isOpen: boolean; + byteOrderMark: ByteOrderMark; + sourceText?: TypeScript.IScriptSnapshot; + } + + interface DocumentRegistryEntry { + document: Document; + refCount: number; + owners: string[]; + } + + export function getDefaultCompilerOptions(): CompilerOptions { + // Set "ES5" target by default for language service + return { + target: ScriptTarget.ES5, + module: ModuleKind.None, + }; + } + + export function compareDataObjects(dst: any, src: any): boolean { + for (var e in dst) { + if (typeof dst[e] === "object") { + if (!compareDataObjects(dst[e], src[e])) + return false; + } + else if (typeof dst[e] !== "function") { + if (dst[e] !== src[e]) + return false; + } + } + return true; + } + + export class OperationCanceledException { } + + class CancellationTokenObject { + + public static None: CancellationTokenObject = new CancellationTokenObject(null) + + constructor(private cancellationToken: CancellationToken) { + } + + public isCancellationRequested() { + return this.cancellationToken && this.cancellationToken.isCancellationRequested(); + } + + public throwIfCancellationRequested(): void { + if (this.isCancellationRequested()) { + throw new OperationCanceledException(); + } + } + } + + // Cache host information about scrip Should be refreshed + // at each language service public entry point, since we don't know when + // set of scripts handled by the host changes. + class HostCache { + private filenameToEntry: Map; + private _compilationSettings: CompilerOptions; + + constructor(private host: LanguageServiceHost) { + // script id => script index + this.filenameToEntry = {}; + + var filenames = host.getScriptFileNames(); + for (var i = 0, n = filenames.length; i < n; i++) { + var filename = filenames[i]; + this.filenameToEntry[TypeScript.switchToForwardSlashes(filename)] = { + filename: filename, + version: host.getScriptVersion(filename), + isOpen: host.getScriptIsOpen(filename), + byteOrderMark: host.getScriptByteOrderMark(filename) + }; + } + + this._compilationSettings = host.getCompilationSettings() || getDefaultCompilerOptions(); + } + + public compilationSettings() { + return this._compilationSettings; + } + + public getEntry(filename: string): HostFileInformation { + filename = TypeScript.switchToForwardSlashes(filename); + return lookUp(this.filenameToEntry, filename); + } + + public contains(filename: string): boolean { + return !!this.getEntry(filename); + } + + public getHostfilename(filename: string) { + var hostCacheEntry = this.getEntry(filename); + if (hostCacheEntry) { + return hostCacheEntry.filename; + } + return filename; + } + + public getFilenames(): string[] { + var fileNames: string[] = []; + + forEachKey(this.filenameToEntry, key => { + if (hasProperty(this.filenameToEntry, key)) + fileNames.push(key); + }); + + return fileNames; + } + + public getVersion(filename: string): number { + return this.getEntry(filename).version; + } + + public isOpen(filename: string): boolean { + return this.getEntry(filename).isOpen; + } + + public getByteOrderMark(filename: string): ByteOrderMark { + return this.getEntry(filename).byteOrderMark; + } + + public getScriptSnapshot(filename: string): TypeScript.IScriptSnapshot { + var file = this.getEntry(filename); + if (!file.sourceText) { + file.sourceText = this.host.getScriptSnapshot(file.filename); + } + return file.sourceText; + } + + public getScriptTextChangeRangeSinceVersion(filename: string, lastKnownVersion: number): TypeScript.TextChangeRange { + var currentVersion = this.getVersion(filename); + if (lastKnownVersion === currentVersion) { + return TypeScript.TextChangeRange.unchanged; // "No changes" + } + + var scriptSnapshot = this.getScriptSnapshot(filename); + return scriptSnapshot.getTextChangeRangeSinceVersion(lastKnownVersion); + } + } + + class SyntaxTreeCache { + private hostCache: HostCache; + + // For our syntactic only features, we also keep a cache of the syntax tree for the + // currently edited file. + private currentfilename: string = ""; + private currentFileVersion: number = -1; + private currentFileSyntaxTree: TypeScript.SyntaxTree = null; + private currentFileScriptSnapshot: TypeScript.IScriptSnapshot = null; + + constructor(private host: LanguageServiceHost) { + this.hostCache = new HostCache(host); + } + + public getCurrentFileSyntaxTree(filename: string): TypeScript.SyntaxTree { + this.hostCache = new HostCache(this.host); + + var version = this.hostCache.getVersion(filename); + var syntaxTree: TypeScript.SyntaxTree = null; + + if (this.currentFileSyntaxTree === null || this.currentfilename !== filename) { + var scriptSnapshot = this.hostCache.getScriptSnapshot(filename); + syntaxTree = this.createSyntaxTree(filename, scriptSnapshot); + } + else if (this.currentFileVersion !== version) { + var scriptSnapshot = this.hostCache.getScriptSnapshot(filename); + syntaxTree = this.updateSyntaxTree(filename, scriptSnapshot, this.currentFileSyntaxTree, this.currentFileVersion); + } + + if (syntaxTree !== null) { + // All done, ensure state is up to date + this.currentFileScriptSnapshot = scriptSnapshot; + this.currentFileVersion = version; + this.currentfilename = filename; + this.currentFileSyntaxTree = syntaxTree; + } + + return this.currentFileSyntaxTree; + } + + public getCurrentScriptSnapshot(filename: string): TypeScript.IScriptSnapshot { + // update currentFileScriptSnapshot as a part of 'getCurrentFileSyntaxTree' call + this.getCurrentFileSyntaxTree(filename); + return this.currentFileScriptSnapshot; + } + + private createSyntaxTree(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot): TypeScript.SyntaxTree { + var text = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot); + + // For the purposes of features that use this syntax tree, we can just use the default + // compilation settings. The features only use the syntax (and not the diagnostics), + // and the syntax isn't affected by the compilation settings. + var syntaxTree = TypeScript.Parser.parse(filename, text, getDefaultCompilerOptions().target, TypeScript.isDTSFile(filename)); + + return syntaxTree; + } + + private updateSyntaxTree(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, previousSyntaxTree: TypeScript.SyntaxTree, previousFileVersion: number): TypeScript.SyntaxTree { + var editRange = this.hostCache.getScriptTextChangeRangeSinceVersion(filename, previousFileVersion); + + // Debug.assert(newLength >= 0); + + // The host considers the entire buffer changed. So parse a completely new tree. + if (editRange === null) { + return this.createSyntaxTree(filename, scriptSnapshot); + } + + var nextSyntaxTree = TypeScript.IncrementalParser.parse( + previousSyntaxTree, editRange, TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot)); + + this.ensureInvariants(filename, editRange, nextSyntaxTree, this.currentFileScriptSnapshot, scriptSnapshot); + + return nextSyntaxTree; + } + + private ensureInvariants(filename: string, editRange: TypeScript.TextChangeRange, incrementalTree: TypeScript.SyntaxTree, oldScriptSnapshot: TypeScript.IScriptSnapshot, newScriptSnapshot: TypeScript.IScriptSnapshot) { + // First, verify that the edit range and the script snapshots make sense. + + // If this fires, then the edit range is completely bogus. Somehow the lengths of the + // old snapshot, the change range and the new snapshot aren't in sync. This is very + // bad. + var expectedNewLength = oldScriptSnapshot.getLength() - editRange.span().length() + editRange.newLength(); + var actualNewLength = newScriptSnapshot.getLength(); + + function provideMoreDebugInfo() { + + var debugInformation = ["expected length:", expectedNewLength, "and actual length:", actualNewLength, "are not equal\r\n"]; + + var oldSpan = editRange.span(); + + function prettyPrintString(s: string): string { + return '"' + s.replace(/\r/g, '\\r').replace(/\n/g, '\\n') + '"'; + } + + debugInformation.push('Edit range (old text) (start: ' + oldSpan.start() + ', end: ' + oldSpan.end() + ') \r\n'); + debugInformation.push('Old text edit range contents: ' + prettyPrintString(oldScriptSnapshot.getText(oldSpan.start(), oldSpan.end()))); + + var newSpan = editRange.newSpan(); + + debugInformation.push('Edit range (new text) (start: ' + newSpan.start() + ', end: ' + newSpan.end() + ') \r\n'); + debugInformation.push('New text edit range contents: ' + prettyPrintString(newScriptSnapshot.getText(newSpan.start(), newSpan.end()))); + + return debugInformation.join(' '); + } + + Debug.assert( + expectedNewLength === actualNewLength, + "Expected length is different from actual!", + provideMoreDebugInfo); + + if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) { + // If this fires, the text change range is bogus. It says the change starts at point + // 'X', but we can see a text difference *before* that point. + var oldPrefixText = oldScriptSnapshot.getText(0, editRange.span().start()); + var newPrefixText = newScriptSnapshot.getText(0, editRange.span().start()); + Debug.assert(oldPrefixText === newPrefixText, 'Expected equal prefix texts!'); + + // If this fires, the text change range is bogus. It says the change goes only up to + // point 'X', but we can see a text difference *after* that point. + var oldSuffixText = oldScriptSnapshot.getText(editRange.span().end(), oldScriptSnapshot.getLength()); + var newSuffixText = newScriptSnapshot.getText(editRange.newSpan().end(), newScriptSnapshot.getLength()); + Debug.assert(oldSuffixText === newSuffixText, 'Expected equal suffix texts!'); + + // Ok, text change range and script snapshots look ok. Let's verify that our + // incremental parsing worked properly. + //var normalTree = this.createSyntaxTree(filename, newScriptSnapshot); + //Debug.assert(normalTree.structuralEquals(incrementalTree), 'Expected equal incremental and normal trees'); + + // Ok, the trees looked good. So at least our incremental parser agrees with the + // normal parser. Now, verify that the incremental tree matches the contents of the + // script snapshot. + var incrementalTreeText = TypeScript.fullText(incrementalTree.sourceUnit()); + var actualSnapshotText = newScriptSnapshot.getText(0, newScriptSnapshot.getLength()); + Debug.assert(incrementalTreeText === actualSnapshotText, 'Expected full texts to be equal'); + } + } + } + + export function createDocumentRegistry(): DocumentRegistry { + var buckets: Map> = {}; + + function getKeyFromCompilationSettings(settings: CompilerOptions): string { + return "_" + ScriptTarget[settings.target]; // + "|" + settings.propagateEnumConstantoString() + } + + function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): Map { + var key = getKeyFromCompilationSettings(settings); + var bucket = lookUp(buckets, key); + if (!bucket && createIfMissing) { + buckets[key] = bucket = {}; + } + return bucket; + } + + function reportStats() { + var bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === '_').map(name => { + var entries = lookUp(buckets, name); + var documents: { name: string; refCount: number; references: string[]; }[] = []; + for (var i in entries) { + var entry = entries[i]; + documents.push({ + name: i, + refCount: entry.refCount, + references: entry.owners.slice(0) + }); + } + documents.sort((x, y) => y.refCount - x.refCount); + return { bucket: name, documents: documents } + }); + return JSON.stringify(bucketInfoArray, null, 2); + } + + function acquireDocument( + filename: string, + compilationSettings: CompilerOptions, + scriptSnapshot: TypeScript.IScriptSnapshot, + byteOrderMark: ByteOrderMark, + version: number, + isOpen: boolean, + referencedFiles: string[]= []): Document { + + var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); + var entry = lookUp(bucket, filename); + if (!entry) { + var document = createDocument(compilationSettings, filename, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles); + + bucket[filename] = entry = { + document: document, + refCount: 0, + owners: [] + }; + } + entry.refCount++; + + return entry.document; + } + + function updateDocument( + document: Document, + filename: string, + compilationSettings: CompilerOptions, + scriptSnapshot: TypeScript.IScriptSnapshot, + version: number, + isOpen: boolean, + textChangeRange: TypeScript.TextChangeRange + ): Document { + + var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ false); + Debug.assert(bucket); + var entry = lookUp(bucket, filename); + Debug.assert(entry); + + if (entry.document.isOpen() === isOpen && entry.document.getVersion() === version) { + return entry.document; + } + + entry.document = entry.document.update(scriptSnapshot, version, isOpen, textChangeRange); + return entry.document; + } + + function releaseDocument(filename: string, compilationSettings: CompilerOptions): void { + var bucket = getBucketForCompilationSettings(compilationSettings, false); + Debug.assert(bucket); + + var entry = lookUp(bucket, filename); + entry.refCount--; + + Debug.assert(entry.refCount >= 0); + if (entry.refCount === 0) { + delete bucket[filename]; + } + } + + return { + acquireDocument: acquireDocument, + updateDocument: updateDocument, + releaseDocument: releaseDocument, + reportStats: reportStats + }; + } + + // A cache of completion entries for keywords, these do not change between sessions + var keywordCompletions:CompletionEntry[] = []; + for (var i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) { + keywordCompletions.push({ + name: tokenToString(i), + kind: ScriptElementKind.keyword, + kindModifiers: ScriptElementKindModifier.none + }); + } + + export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry): LanguageService { + var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host); + var formattingRulesProvider: TypeScript.Services.Formatting.RulesProvider; + var hostCache: HostCache; // A cache of all the information about the files on the host side. var program: Program; var typeChecker: TypeChecker; - var filesByName: Map; + var useCaseSensitivefilenames = false; + var documentsByName: Map = {}; + var documentRegistry = documentRegistry; + var cancellationToken = new CancellationTokenObject(host.getCancellationToken()); + var activeCompletionSession: CompletionSession; // The current active completion session, used to get the completion entry details + + // Check if the localized messages json is set, otherwise query the host for it + if (!TypeScript.LocalizedDiagnosticMessages) { + TypeScript.LocalizedDiagnosticMessages = host.getLocalizedDiagnosticMessages(); + } + + function getDocument(filename: string): Document { + return lookUp(documentsByName, filename); + } function createCompilerHost(): CompilerHost { return { getSourceFile: (filename, languageVersion) => { - var hostFile = filesByName[filename]; + var document = getDocument(filename); - // TODO use the document registry to get or update + Debug.assert(!!document, "document can not be undefined"); - if (!hostFile.sourceText) { - hostFile.sourceText = host.getScriptSnapshot(filename); - } - - // TODO add support for IScriptSnapshot in the parser - return createSourceFile(filename, hostFile.sourceText.getText(0, hostFile.sourceText.getLength()), languageVersion); + return document.getSourceFile(); }, + getCancellationToken: () => cancellationToken, + getCanonicalFileName: (filename) => useCaseSensitivefilenames ? filename : filename.toLowerCase(), + useCaseSensitiveFileNames: () => useCaseSensitivefilenames, + getNewLine: () => "\r\n", // Need something that doesn't depend on sys.ts here - getDefaultLibFilename: () => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), "lib.d.ts"), - getCancellationToken: (): CancellationToken => undefined, - writeFile: (fileName, data) => { + getDefaultLibFilename: (): string => { + throw Error("TOD:: getDefaultLibfilename"); + }, + writeFile: (filename, data) => { throw Error("TODO: write file"); }, getCurrentDirectory: (): string => { throw Error("TODO: getCurrentDirectory"); - }, - getCanonicalFileName: getCanonicalFileName, - useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames + } }; } function synchronizeHostData(): void { - // Build the cache - filesByName = {}; - var files = host.getScriptFileNames(); - forEach(files, (f) => { - filesByName[f] = { - version: host.getScriptVersion(f), - isOpen: host.getScriptIsOpen(f), - byteOrderMark: host.getScriptByteOrderMark(f) - }; - }); + // Reset the cache at start of every refresh + hostCache = new HostCache(host); - var currentProgram = program; + var compilationSettings = hostCache.compilationSettings(); - var options = host.getCompilationSettings(); + // TODO: check if we need to create a new compiler to start with + // 1. files are identical + // 2. compilation settings are identical - // update the program - program = createProgram(files, options, createCompilerHost()); + // Now, remove any files from the compiler that are no longer in the host. + var oldProgram = program; + if (oldProgram) { + var oldSettings = program.getCompilerOptions(); - // Update the typeChecker + // If the language version changed, then that affects what types of things we parse. So + // we have to dump all syntax trees. + // TODO: handle propagateEnumConstants + // TODO: is module still needed + var settingsChangeAffectsSyntax = oldSettings.target !== compilationSettings.target || oldSettings.module !== compilationSettings.module; + + var changesInCompilationSettingsAffectSyntax = + oldSettings && compilationSettings && !compareDataObjects(oldSettings, compilationSettings) && settingsChangeAffectsSyntax; + var oldSourceFiles = program.getSourceFiles(); + + for (var i = 0, n = oldSourceFiles.length; i < n; i++) { + cancellationToken.throwIfCancellationRequested(); + var filename = oldSourceFiles[i].filename; + if (!hostCache.contains(filename) || changesInCompilationSettingsAffectSyntax) { + documentRegistry.releaseDocument(filename, oldSettings); + delete documentsByName[filename]; + } + } + } + + // Now, for every file the host knows about, either add the file (if the compiler + // doesn't know about it.). Or notify the compiler about any changes (if it does + // know about it.) + var hostfilenames = hostCache.getFilenames(); + + for (var i = 0, n = hostfilenames.length; i < n; i++) { + var filename = hostfilenames[i]; + + var version = hostCache.getVersion(filename); + var isOpen = hostCache.isOpen(filename); + var scriptSnapshot = hostCache.getScriptSnapshot(filename); + + var document: Document = getDocument(filename); + if (document) { + // + // If the document is the same, assume no update + // + if (document.getVersion() === version && document.isOpen() === isOpen) { + continue; + } + + // Only perform incremental parsing on open files that are being edited. If a file was + // open, but is now closed, we want to reparse entirely so we don't have any tokens that + // are holding onto expensive script snapshot instances on the host. Similarly, if a + // file was closed, then we always want to reparse. This is so our tree doesn't keep + // the old buffer alive that represented the file on disk (as the host has moved to a + // new text buffer). + var textChangeRange: TypeScript.TextChangeRange = null; + if (document.isOpen && isOpen) { + textChangeRange = hostCache.getScriptTextChangeRangeSinceVersion(filename, document.getVersion()); + } + + document = documentRegistry.updateDocument(document, filename, compilationSettings, scriptSnapshot, version, isOpen, textChangeRange); + } + else { + document = documentRegistry.acquireDocument(filename, compilationSettings, scriptSnapshot, hostCache.getByteOrderMark(filename), version, isOpen, []); + } + + // Remeber the new document + documentsByName[filename] = document; + } + + // Now create a new compiler + program = createProgram(hostfilenames, compilationSettings, createCompilerHost()); typeChecker = program.getTypeChecker(); + } - // TODO release old sources from the registry - if (currentProgram) { - + function dispose(): void { + if (program) { + forEach(program.getSourceFiles(), + (f) => { documentRegistry.releaseDocument(f.filename, program.getCompilerOptions()); }); } } - function getSyntacticDiagnostics(filename: string): Diagnostic[] { + /// Diagnostics + function getSyntacticDiagnostics(filename: string) { synchronizeHostData(); - var sourceFile = program.getSourceFile(filename); - return sourceFile ? program.getDiagnostics(sourceFile) : []; + return program.getDiagnostics(program.getSourceFile(filename)); } - function getSemanticDiagnostics(filename: string): Diagnostic[] { + function getSemanticDiagnostics(filename: string) { synchronizeHostData(); - var sourceFile = program.getSourceFile(filename); - return sourceFile ? typeChecker.getDiagnostics(sourceFile) : []; + return typeChecker.getDiagnostics(program.getSourceFile(filename)); + } + + function getCompilerOptionsDiagnostics() { + synchronizeHostData(); + return program.getGlobalDiagnostics(); + } + + /// Completion + function getValidCompletionEntryDisplayName(displayName: string, target: ScriptTarget): string { + if (displayName && displayName.length > 0) { + var firstChar = displayName.charCodeAt(0); + if (firstChar === TypeScript.CharacterCodes.singleQuote || firstChar === TypeScript.CharacterCodes.doubleQuote) { + // If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an + // invalid identifer name. We need to check if whatever was inside the quotes is actually a valid identifier name. + displayName = TypeScript.stripStartAndEndQuotes(displayName); + } + + if (TypeScript.Scanner.isValidIdentifier(TypeScript.SimpleText.fromString(displayName), target)) { + return displayName; + } + } + + return undefined; + } + + function createCompletionEntry(symbol: Symbol): CompletionEntry { + // Try to get a valid display name for this symbol, if we could not find one, then ignore it. + // We would like to only show things that can be added after a dot, so for instance numeric properties can + // not be accessed with a dot (a.1 <- invalid) + var displayName = getValidCompletionEntryDisplayName(symbol.getName(), program.getCompilerOptions().target); + if (!displayName) { + return undefined; + } + + var declarations = symbol.getDeclarations(); + var firstDeclaration = [0]; + return { + name: displayName, + kind: getSymbolKind(symbol), + kindModifiers: declarations ? getNodeModifiers(declarations[0]) : ScriptElementKindModifier.none + }; + } + + function getCompletionsAtPosition(filename: string, position: number, isMemberCompletion: boolean) { + function getCompletionEntriesFromSymbols(symbols: Symbol[], session: CompletionSession): void { + forEach(symbols, (symbol) => { + var entry = createCompletionEntry(symbol); + if (entry) { + session.entries.push(entry); + session.symbols[entry.name] = symbol; + } + }); + } + + function isCompletionListBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { + // We shouldn't be getting a possition that is outside the file because + // isEntirelyInsideComment can't handle when the position is out of bounds, + // callers should be fixed, however we should be resiliant to bad inputs + // so we return true (this position is a blocker for getting completions) + if (position < 0 || position > TypeScript.fullWidth(sourceUnit)) { + return true; + } + + // This method uses Fidelity completely. Some information can be reached using the AST, but not everything. + return TypeScript.Syntax.isEntirelyInsideComment(sourceUnit, position) || + TypeScript.Syntax.isEntirelyInStringOrRegularExpressionLiteral(sourceUnit, position) || + isIdentifierDefinitionLocation(sourceUnit, position) || + isRightOfIllegalDot(sourceUnit, position); + } + + function getContainingObjectLiteralApplicableForCompletion(sourceUnit: TypeScript.SourceUnitSyntax, position: number): TypeScript.ISyntaxElement { + // The locations in an object literal expression that are applicable for completion are property name definition locations. + var previousToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); + + if (previousToken) { + var parent = previousToken.parent; + + switch (previousToken.kind()) { + case TypeScript.SyntaxKind.OpenBraceToken: // var x = { | + case TypeScript.SyntaxKind.CommaToken: // var x = { a: 0, | + if (parent && parent.kind() === TypeScript.SyntaxKind.SeparatedList) { + parent = parent.parent; + } + + if (parent && parent.kind() === TypeScript.SyntaxKind.ObjectLiteralExpression) { + return parent; + } + + break; + } + } + + return undefined; + } + + function isIdentifierDefinitionLocation(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { + var positionedToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); + + if (positionedToken) { + var containingNodeKind = TypeScript.Syntax.containingNode(positionedToken) && TypeScript.Syntax.containingNode(positionedToken).kind(); + switch (positionedToken.kind()) { + case TypeScript.SyntaxKind.CommaToken: + return containingNodeKind === TypeScript.SyntaxKind.ParameterList || + containingNodeKind === TypeScript.SyntaxKind.VariableDeclaration || + containingNodeKind === TypeScript.SyntaxKind.EnumDeclaration; // enum { foo, | + + case TypeScript.SyntaxKind.OpenParenToken: + return containingNodeKind === TypeScript.SyntaxKind.ParameterList || + containingNodeKind === TypeScript.SyntaxKind.CatchClause; + + case TypeScript.SyntaxKind.OpenBraceToken: + return containingNodeKind === TypeScript.SyntaxKind.EnumDeclaration; // enum { | + + case TypeScript.SyntaxKind.PublicKeyword: + case TypeScript.SyntaxKind.PrivateKeyword: + case TypeScript.SyntaxKind.StaticKeyword: + case TypeScript.SyntaxKind.DotDotDotToken: + return containingNodeKind === TypeScript.SyntaxKind.Parameter; + + case TypeScript.SyntaxKind.ClassKeyword: + case TypeScript.SyntaxKind.ModuleKeyword: + case TypeScript.SyntaxKind.EnumKeyword: + case TypeScript.SyntaxKind.InterfaceKeyword: + case TypeScript.SyntaxKind.FunctionKeyword: + case TypeScript.SyntaxKind.VarKeyword: + case TypeScript.SyntaxKind.GetKeyword: + case TypeScript.SyntaxKind.SetKeyword: + return true; + } + + // Previous token may have been a keyword that was converted to an identifier. + switch (positionedToken.text()) { + case "class": + case "interface": + case "enum": + case "module": + return true; + } + } + + return false; + } + + function getNonIdentifierCompleteTokenOnLeft(sourceUnit: TypeScript.SourceUnitSyntax, position: number): TypeScript.ISyntaxToken { + var positionedToken = TypeScript.Syntax.findCompleteTokenOnLeft(sourceUnit, position, /*includeSkippedTokens*/true); + + if (positionedToken && position === TypeScript.end(positionedToken) && positionedToken.kind() == TypeScript.SyntaxKind.EndOfFileToken) { + // EndOfFile token is not intresting, get the one before it + positionedToken = TypeScript. previousToken(positionedToken, /*includeSkippedTokens*/true); + } + + if (positionedToken && position === TypeScript.end(positionedToken) && positionedToken.kind() === TypeScript.SyntaxKind.IdentifierName) { + // The caret is at the end of an identifier, the decession to provide completion depends on the previous token + positionedToken = TypeScript.previousToken(positionedToken, /*includeSkippedTokens*/true); + } + + return positionedToken; + } + + function isRightOfIllegalDot(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { + var positionedToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); + + if (positionedToken) { + switch (positionedToken.kind()) { + case TypeScript.SyntaxKind.DotToken: + var leftOfDotPositionedToken = TypeScript.previousToken(positionedToken, /*includeSkippedTokens*/true); + return leftOfDotPositionedToken && leftOfDotPositionedToken.kind() === TypeScript.SyntaxKind.NumericLiteral; + + case TypeScript.SyntaxKind.NumericLiteral: + var text = positionedToken.text(); + return text.charAt(text.length - 1) === "."; + } + } + + return false; + } + + synchronizeHostData(); + + filename = TypeScript.switchToForwardSlashes(filename); + + var document = getDocument(filename); + var sourceUnit = document.getSourceUnit(); + + if (isCompletionListBlocker(document.getSyntaxTree().sourceUnit(), position)) { + host.log("Returning an empty list because completion was blocked."); + return null; + } + + var node = TypeScript.ASTHelpers.getAstAtPosition(sourceUnit, position, /*useTrailingTriviaAsLimChar*/ true, /*forceInclusive*/ true); + + if (node && node.kind() === TypeScript.SyntaxKind.IdentifierName && + TypeScript.start(node) === TypeScript.end(node)) { + // Ignore missing name nodes + node = node.parent; + } + + var isRightOfDot = false; + if (node && + node.kind() === TypeScript.SyntaxKind.MemberAccessExpression && + TypeScript.end((node).expression) < position) { + + isRightOfDot = true; + node = (node).expression; + } + else if (node && + node.kind() === TypeScript.SyntaxKind.QualifiedName && + TypeScript.end((node).left) < position) { + + isRightOfDot = true; + node = (node).left; + } + else if (node && node.parent && + node.kind() === TypeScript.SyntaxKind.IdentifierName && + node.parent.kind() === TypeScript.SyntaxKind.MemberAccessExpression && + (node.parent).name === node) { + + isRightOfDot = true; + node = (node.parent).expression; + } + else if (node && node.parent && + node.kind() === TypeScript.SyntaxKind.IdentifierName && + node.parent.kind() === TypeScript.SyntaxKind.QualifiedName && + (node.parent).right === node) { + + isRightOfDot = true; + node = (node.parent).left; + } + + // TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node + var mappedNode = getNodeAtPosition(document.getSourceFile(), TypeScript.end(node) - 1); + + Debug.assert(mappedNode, "Could not map a Fidelity node to an AST node"); + + // Get the completions + activeCompletionSession = { + filename: filename, + position: position, + entries: [], + symbols: {}, + location: mappedNode, + typeChecker: typeChecker + }; + + // Right of dot member completion list + if (isRightOfDot) { + var type: Type = typeChecker.getTypeOfExpression(mappedNode); + if (!type) { + return undefined; + } + + var symbols = type.getApparentProperties(); + isMemberCompletion = true; + getCompletionEntriesFromSymbols(symbols, activeCompletionSession); + } + else { + var containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(document.getSyntaxTree().sourceUnit(), position); + + // Object literal expression, look up possible property names from contextual type + if (containingObjectLiteral) { + var searchPosition = Math.min(position, TypeScript.end(containingObjectLiteral)); + var path = TypeScript.ASTHelpers.getAstAtPosition(sourceUnit, searchPosition); + // Get the object literal node + + while (node && node.kind() !== TypeScript.SyntaxKind.ObjectLiteralExpression) { + node = node.parent; + } + + if (!node || node.kind() !== TypeScript.SyntaxKind.ObjectLiteralExpression) { + // AST Path look up did not result in the same node as Fidelity Syntax Tree look up. + // Once we remove AST this will no longer be a problem. + return null; + } + + isMemberCompletion = true; + + //// Try to get the object members form contextual typing + //var contextualMembers = compiler.getContextualMembersFromAST(node, document); + //if (contextualMembers && contextualMembers.symbols && contextualMembers.symbols.length > 0) { + // // get existing members + // var existingMembers = compiler.getVisibleMemberSymbolsFromAST(node, document); + + // // Add filtterd items to the completion list + // getCompletionEntriesFromSymbols({ + // symbols: filterContextualMembersList(contextualMembers.symbols, existingMembers, filename, position), + // enclosingScopeSymbol: contextualMembers.enclosingScopeSymbol + // }, entries); + //} + } + // Get scope memebers + else { + isMemberCompletion = false; + /// TODO filter meaning based on the current context + var symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace; + var symbols = typeChecker.getSymbolsInScope(mappedNode, symbolMeanings); + + getCompletionEntriesFromSymbols(symbols, activeCompletionSession); + } + } + + // Add keywords if this is not a member completion list + if (!isMemberCompletion) { + Array.prototype.push.apply(activeCompletionSession.entries, keywordCompletions); + } + + return { + isMemberCompletion: isMemberCompletion, + entries: activeCompletionSession.entries + }; + } + + function getCompletionEntryDetails(filename: string, position: number, entryName: string) { + // Note: No need to call synchronizeHostData, as we have captured all the data we need + // in the getCompletionsAtPosition erlier + filename = TypeScript.switchToForwardSlashes(filename); + + var session = activeCompletionSession; + + // Ensure that the current active completion session is still valid for this request + if (!session || session.filename !== filename || session.position !== position) { + return undefined; + } + + var symbol = lookUp(activeCompletionSession.symbols, entryName); + if (symbol) { + var type = session.typeChecker.getTypeOfSymbol(symbol); + Debug.assert(type, "Could not find type for symbol"); + var completionEntry = createCompletionEntry(symbol); + return { + name: entryName, + kind: completionEntry.kind, + kindModifiers: completionEntry.kindModifiers, + type: session.typeChecker.typeToString(type, session.location), + fullSymbolName: typeChecker.symbolToString(symbol, session.location), + docComment: "" + }; + } + else { + // No symbol, it is a keyword + return { + name: entryName, + kind: ScriptElementKind.keyword, + kindModifiers: ScriptElementKindModifier.none, + type: undefined, + fullSymbolName: entryName, + docComment: undefined + }; + } + } + + function getNodeAtPosition(sourceFile: SourceFile, position: number) { + var current: Node = sourceFile; + outer: while (true) { + // find the child that has this + for (var i = 0, n = current.getChildCount(); i < n; i++) { + var child = current.getChildAt(i); + if (child.getStart() <= position && position < child.getEnd()) { + current = child; + continue outer; + } + if (child.end > position) break; + } + return current; + } + } + + function getEnclosingDeclaration(node: Node): Node { + while (true) { + node = node.parent; + if (!node) { + return node; + } + switch (node.kind) { + case SyntaxKind.Method: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ModuleDeclaration: + return node; + } + } + } + + function getSymbolKind(symbol: Symbol): string { + var flags = symbol.getFlags(); + + if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; + if (flags & SymbolFlags.Class) return ScriptElementKind.classElement; + if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; + if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; + if (flags & SymbolFlags.Variable) return ScriptElementKind.variableElement; + if (flags & SymbolFlags.Function) return ScriptElementKind.functionElement; + if (flags & SymbolFlags.GetAccessor) return ScriptElementKind.memberGetAccessorElement; + if (flags & SymbolFlags.SetAccessor) return ScriptElementKind.memberSetAccessorElement; + if (flags & SymbolFlags.Method) return ScriptElementKind.memberFunctionElement; + if (flags & SymbolFlags.Property) return ScriptElementKind.memberVariableElement; + if (flags & SymbolFlags.IndexSignature) return ScriptElementKind.indexSignatureElement; + if (flags & SymbolFlags.ConstructSignature) return ScriptElementKind.constructSignatureElement; + if (flags & SymbolFlags.CallSignature) return ScriptElementKind.callSignatureElement; + if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; + if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; + if (flags & SymbolFlags.EnumMember) return ScriptElementKind.variableElement; + + return ScriptElementKind.unknown; + } + + function getTypeKind(type: Type): string { + var flags = type.getFlags(); + + if (flags & TypeFlags.Enum) return ScriptElementKind.enumElement; + if (flags & TypeFlags.Class) return ScriptElementKind.classElement; + if (flags & TypeFlags.Interface) return ScriptElementKind.interfaceElement; + if (flags & TypeFlags.TypeParameter) return ScriptElementKind.typeParameterElement; + if (flags & TypeFlags.Intrinsic) return ScriptElementKind.primitiveType; + if (flags & TypeFlags.StringLiteral) return ScriptElementKind.primitiveType; + + return ScriptElementKind.unknown; + } + + function getNodeModifiers(node: Node): string { + var flags = node.flags; + var result: string[] = []; + + if (flags & NodeFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier); + if (flags & NodeFlags.Public) result.push(ScriptElementKindModifier.publicMemberModifier); + if (flags & NodeFlags.Static) result.push(ScriptElementKindModifier.staticModifier); + if (flags & NodeFlags.Export) result.push(ScriptElementKindModifier.exportedModifier); + if (isInAmbientContext(node)) result.push(ScriptElementKindModifier.ambientModifier); + + return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none; + } + + /// QuickInfo + function getTypeAtPosition(filename: string, position: number): TypeInfo { + synchronizeHostData(); + + filename = TypeScript.switchToForwardSlashes(filename); + var document = getDocument(filename); + var node = getNodeAtPosition(document.getSourceFile(), position); + if (!node) return undefined; + + switch (node.kind) { + // A declaration + case SyntaxKind.Identifier: + if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { + // TODO: handle new and call expressions + } + + var symbol = typeChecker.getSymbolOfIdentifier(node); + Debug.assert(symbol, "getTypeAtPosition: Could not find symbol for node"); + var type = typeChecker.getTypeOfSymbol(symbol); + + return { + memberName: new TypeScript.MemberNameString(typeChecker.typeToString(type)), + docComment: "", + fullSymbolName: typeChecker.symbolToString(symbol, getEnclosingDeclaration(node)), + kind: getSymbolKind(symbol), + minChar: node.pos, + limChar: node.end + }; + + // An Expression + case SyntaxKind.ThisKeyword: + case SyntaxKind.QualifiedName: + case SyntaxKind.SuperKeyword: + case SyntaxKind.StringLiteral: + var type = typeChecker.getTypeOfExpression(node); + Debug.assert(type, "getTypeAtPosition: Could not find type for node"); + return { + memberName: new TypeScript.MemberNameString(""), + docComment: "", + fullSymbolName: typeChecker.typeToString(type, getEnclosingDeclaration(node)), + kind: getTypeKind(type), + minChar: node.pos, + limChar: node.end + }; + break; + } + } + + /// Syntactic features + function getSyntaxTree(filename: string): TypeScript.SyntaxTree { + filename = TypeScript.switchToForwardSlashes(filename); + return syntaxTreeCache.getCurrentFileSyntaxTree(filename); + } + + function getNameOrDottedNameSpan(filename: string, startPos: number, endPos: number): SpanInfo { + function getTypeInfoEligiblePath(filename: string, position: number, isConstructorValidPosition: boolean) { + var sourceUnit = syntaxTreeCache.getCurrentFileSyntaxTree(filename).sourceUnit(); + + var ast = TypeScript.ASTHelpers.getAstAtPosition(sourceUnit, position, /*useTrailingTriviaAsLimChar*/ false, /*forceInclusive*/ true); + if (ast === null) { + return null; + } + + if (ast.kind() === TypeScript.SyntaxKind.ParameterList && ast.parent.kind() === TypeScript.SyntaxKind.CallSignature && ast.parent.parent.kind() === TypeScript.SyntaxKind.ConstructorDeclaration) { + ast = ast.parent.parent; + } + + switch (ast.kind()) { + default: + return null; + case TypeScript.SyntaxKind.ConstructorDeclaration: + var constructorAST = ast; + if (!isConstructorValidPosition || !(position >= TypeScript.start(constructorAST) && position <= TypeScript.start(constructorAST) + "constructor".length)) { + return null; + } + else { + return ast; + } + case TypeScript.SyntaxKind.FunctionDeclaration: + return null; + case TypeScript.SyntaxKind.MemberAccessExpression: + case TypeScript.SyntaxKind.QualifiedName: + case TypeScript.SyntaxKind.SuperKeyword: + case TypeScript.SyntaxKind.StringLiteral: + case TypeScript.SyntaxKind.ThisKeyword: + case TypeScript.SyntaxKind.IdentifierName: + return ast; + } + } + + filename = TypeScript.switchToForwardSlashes(filename); + + var node = getTypeInfoEligiblePath(filename, startPos, false); + if (!node) return null; + + while (node) { + if (TypeScript.ASTHelpers.isNameOfMemberAccessExpression(node) || + TypeScript.ASTHelpers.isRightSideOfQualifiedName(node)) { + node = node.parent; + } + else { + break; + } + } + + return { + minChar: TypeScript.start(node), + limChar: TypeScript.end(node) + }; + } + + function getBreakpointStatementAtPosition(filename: string, position: number) { + // doesn't use compiler - no need to synchronize with host + filename = TypeScript.switchToForwardSlashes(filename); + + var syntaxtree = getSyntaxTree(filename); + return TypeScript.Services.Breakpoints.getBreakpointLocation(syntaxtree, position); + } + + function getScriptLexicalStructure(filename: string) { + filename = TypeScript.switchToForwardSlashes(filename); + var syntaxTree = getSyntaxTree(filename); + var items: NavigateToItem[] = []; + TypeScript.Services.GetScriptLexicalStructureWalker.getListsOfAllScriptLexicalStructure(items, filename, syntaxTree.sourceUnit()); + return items; + } + + function getOutliningRegions(filename: string) { + // doesn't use compiler - no need to synchronize with host + filename = TypeScript.switchToForwardSlashes(filename); + var syntaxTree = getSyntaxTree(filename); + return TypeScript.Services.OutliningElementsCollector.collectElements(syntaxTree.sourceUnit()); + } + + function getBraceMatchingAtPosition(filename: string, position: number) { + filename = TypeScript.switchToForwardSlashes(filename); + var syntaxTree = getSyntaxTree(filename); + return TypeScript.Services.BraceMatcher.getMatchSpans(syntaxTree, position); + } + + function getIndentationAtPosition(filename: string, position: number, editorOptions: EditorOptions) { + filename = TypeScript.switchToForwardSlashes(filename); + + var syntaxTree = getSyntaxTree(filename); + + var scriptSnapshot = syntaxTreeCache.getCurrentScriptSnapshot(filename); + var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot); + var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText); + var options = new TypeScript.FormattingOptions(!editorOptions.ConvertTabsToSpaces, editorOptions.TabSize, editorOptions.IndentSize, editorOptions.NewLineCharacter) + + return TypeScript.Services.Formatting.SingleTokenIndenter.getIndentationAmount(position, syntaxTree.sourceUnit(), textSnapshot, options); + } + + function getFormattingManager(filename: string, options: FormatCodeOptions) { + // Ensure rules are initialized and up to date wrt to formatting options + if (formattingRulesProvider == null) { + formattingRulesProvider = new TypeScript.Services.Formatting.RulesProvider(host); + } + + formattingRulesProvider.ensureUpToDate(options); + + // Get the Syntax Tree + var syntaxTree = getSyntaxTree(filename); + + // Convert IScriptSnapshot to ITextSnapshot + var scriptSnapshot = syntaxTreeCache.getCurrentScriptSnapshot(filename); + var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot); + var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText); + + var manager = new TypeScript.Services.Formatting.FormattingManager(syntaxTree, textSnapshot, formattingRulesProvider, options); + + return manager; + } + + function getFormattingEditsForRange(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] { + filename = TypeScript.switchToForwardSlashes(filename); + + var manager = getFormattingManager(filename, options); + return manager.formatSelection(minChar, limChar); + } + + function getFormattingEditsForDocument(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] { + filename = TypeScript.switchToForwardSlashes(filename); + + var manager = getFormattingManager(filename, options); + return manager.formatDocument(minChar, limChar); + } + + function getFormattingEditsOnPaste(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] { + filename = TypeScript.switchToForwardSlashes(filename); + + var manager = getFormattingManager(filename, options); + return manager.formatOnPaste(minChar, limChar); + } + + function getFormattingEditsAfterKeystroke(filename: string, position: number, key: string, options: FormatCodeOptions): TextEdit[] { + filename = TypeScript.switchToForwardSlashes(filename); + + var manager = getFormattingManager(filename, options); + if (key === "}") return manager.formatOnClosingCurlyBrace(position); + else if (key === ";") return manager.formatOnSemicolon(position); + else if (key === "\n") return manager.formatOnEnter(position); + else return []; + } + + + return { + dispose: dispose, + refresh: () => { }, + cleanupSemanticCache: () => { }, + getSyntacticDiagnostics: getSyntacticDiagnostics, + getSemanticDiagnostics: getSemanticDiagnostics, + getCompilerOptionsDiagnostics: getCompilerOptionsDiagnostics, + getCompletionsAtPosition: getCompletionsAtPosition, + getCompletionEntryDetails: getCompletionEntryDetails, + getTypeAtPosition: getTypeAtPosition, + getSignatureAtPosition: (filename, position): SignatureInfo => undefined, + getDefinitionAtPosition: (filename, position) => [], + getReferencesAtPosition: (filename, position) => [], + getOccurrencesAtPosition: (filename, position) => [], + getImplementorsAtPosition: (filename, position) => [], + getNameOrDottedNameSpan: getNameOrDottedNameSpan, + getBreakpointStatementAtPosition: getBreakpointStatementAtPosition, + getNavigateToItems: (searchValue) => [], + getScriptLexicalStructure: getScriptLexicalStructure, + getOutliningRegions: getOutliningRegions, + getBraceMatchingAtPosition: getBraceMatchingAtPosition, + getIndentationAtPosition: getIndentationAtPosition, + getFormattingEditsForRange: getFormattingEditsForRange, + getFormattingEditsForDocument: getFormattingEditsForDocument, + getFormattingEditsOnPaste: getFormattingEditsOnPaste, + getFormattingEditsAfterKeystroke: getFormattingEditsAfterKeystroke, + getEmitOutput: (filename): EmitOutput => undefined, + }; + } + + /// Classifier + + export function createClassifier(host: Logger): Classifier { + var scanner: TypeScript.Scanner.IScanner; + var lastDiagnosticKey: string = null; + var noRegexTable: boolean[]; + var reportDiagnostic = (position: number, fullWidth: number, key: string, args: any[]) => { + lastDiagnosticKey = key; + }; + + if (!noRegexTable) { + noRegexTable = []; + noRegexTable[TypeScript.SyntaxKind.IdentifierName] = true; + noRegexTable[TypeScript.SyntaxKind.StringLiteral] = true; + noRegexTable[TypeScript.SyntaxKind.NumericLiteral] = true; + noRegexTable[TypeScript.SyntaxKind.RegularExpressionLiteral] = true; + noRegexTable[TypeScript.SyntaxKind.ThisKeyword] = true; + noRegexTable[TypeScript.SyntaxKind.PlusPlusToken] = true; + noRegexTable[TypeScript.SyntaxKind.MinusMinusToken] = true; + noRegexTable[TypeScript.SyntaxKind.CloseParenToken] = true; + noRegexTable[TypeScript.SyntaxKind.CloseBracketToken] = true; + noRegexTable[TypeScript.SyntaxKind.CloseBraceToken] = true; + noRegexTable[TypeScript.SyntaxKind.TrueKeyword] = true; + noRegexTable[TypeScript.SyntaxKind.FalseKeyword] = true; + } + + function getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult { + var offset = 0; + if (lexState !== EndOfLineState.Start) { + // If we're in a string literal, then prepend: "\ + // (and a newline). That way when we lex we'll think we're still in a string literal. + // + // If we're in a multiline comment, then prepend: /* + // (and a newline). That way when we lex we'll think we're still in a multiline comment. + if (lexState === EndOfLineState.InDoubleQuoteStringLiteral) { + text = '"\\\n' + text; + } + else if (lexState === EndOfLineState.InSingleQuoteStringLiteral) { + text = "'\\\n" + text; + } + else if (lexState === EndOfLineState.InMultiLineCommentTrivia) { + text = "/*\n" + text; + } + + offset = 3; + } + + var result: ClassificationResult = { + finalLexState: EndOfLineState.Start, + entries: [] + }; + + var simpleText = TypeScript.SimpleText.fromString(text); + scanner = TypeScript.Scanner.createScanner(ScriptTarget.ES5, simpleText, reportDiagnostic); + + var lastTokenKind = TypeScript.SyntaxKind.None; + var token: TypeScript.ISyntaxToken = null; + do { + lastDiagnosticKey = null; + + token = scanner.scan(!noRegexTable[lastTokenKind]); + lastTokenKind = token.kind(); + + processToken(text, simpleText, offset, token, result); + } + while (token.kind() !== TypeScript.SyntaxKind.EndOfFileToken); + + lastDiagnosticKey = null; + return result; + } + + function processToken(text: string, simpleText: TypeScript.ISimpleText, offset: number, token: TypeScript.ISyntaxToken, result: ClassificationResult): void { + processTriviaList(text, offset, token.leadingTrivia(simpleText), result); + addResult(text, offset, result, TypeScript.width(token), token.kind()); + processTriviaList(text, offset, token.trailingTrivia(simpleText), result); + + if (TypeScript.fullEnd(token) >= text.length) { + // We're at the end. + if (lastDiagnosticKey === TypeScript.DiagnosticCode.AsteriskSlash_expected) { + result.finalLexState = EndOfLineState.InMultiLineCommentTrivia; + return; + } + + if (token.kind() === TypeScript.SyntaxKind.StringLiteral) { + var tokenText = token.text(); + if (tokenText.length > 0 && tokenText.charCodeAt(tokenText.length - 1) === TypeScript.CharacterCodes.backslash) { + var quoteChar = tokenText.charCodeAt(0); + result.finalLexState = quoteChar === TypeScript.CharacterCodes.doubleQuote + ? EndOfLineState.InDoubleQuoteStringLiteral + : EndOfLineState.InSingleQuoteStringLiteral; + return; + } + } + } + } + + function processTriviaList(text: string, offset: number, triviaList: TypeScript.ISyntaxTriviaList, result: ClassificationResult): void { + for (var i = 0, n = triviaList.count(); i < n; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + addResult(text, offset, result, trivia.fullWidth(), trivia.kind()); + } + } + + function addResult(text: string, offset: number, result: ClassificationResult, length: number, kind: TypeScript.SyntaxKind): void { + if (length > 0) { + // If this is the first classification we're adding to the list, then remove any + // offset we have if we were continuing a construct from the previous line. + if (result.entries.length === 0) { + length -= offset; + } + + result.entries.push({ length: length, classification: classFromKind(kind) }); + } + } + + function classFromKind(kind: TypeScript.SyntaxKind) { + if (TypeScript.SyntaxFacts.isAnyKeyword(kind)) { + return TokenClass.Keyword; + } + else if (TypeScript.SyntaxFacts.isBinaryExpressionOperatorToken(kind) || + TypeScript.SyntaxFacts.isPrefixUnaryExpressionOperatorToken(kind)) { + return TokenClass.Operator; + } + else if (TypeScript.SyntaxFacts.isAnyPunctuation(kind)) { + return TokenClass.Punctuation; + } + + switch (kind) { + case TypeScript.SyntaxKind.WhitespaceTrivia: + return TokenClass.Whitespace; + case TypeScript.SyntaxKind.MultiLineCommentTrivia: + case TypeScript.SyntaxKind.SingleLineCommentTrivia: + return TokenClass.Comment; + case TypeScript.SyntaxKind.NumericLiteral: + return TokenClass.NumberLiteral; + case TypeScript.SyntaxKind.StringLiteral: + return TokenClass.StringLiteral; + case TypeScript.SyntaxKind.RegularExpressionLiteral: + return TokenClass.RegExpLiteral; + case TypeScript.SyntaxKind.IdentifierName: + default: + return TokenClass.Identifier; + } } return { - getSyntacticDiagnostics: getSyntacticDiagnostics, - getSemanticDiagnostics: getSemanticDiagnostics, + getClassificationsForLine: getClassificationsForLine }; } @@ -436,4 +2233,4 @@ module ts { } initializeServices(); -} +} \ No newline at end of file diff --git a/src/services/shims.ts b/src/services/shims.ts index 68e15ff6095..f02b9b62088 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1,18 +1,26 @@ -/// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// + +/// +/// + +var debugObjectHost = (this); module ts { - export interface LanguageServiceShimHost { - log(s: string): void; - getCompilationSettings(): string; - getScriptFileNames(): string; - getScriptVersion(fileName: string): string; - getScriptIsOpen(fileName: string): boolean; - getScriptByteOrderMark(fileName: string): number; - getScriptSnapshot(fileName: string): ScriptSnapshotShim; - getLocalizedDiagnosticMessages(): string; - // getCancellationToken(): CancellationToken - } - export interface ScriptSnapshotShim { // Get's a portion of the script snapshot specified by [start, end). getText(start: number, end: number): string; @@ -28,10 +36,230 @@ module ts { // { span: { start: number; length: number }; newLength: number } // // Or null value if there was no change. - getChangeRange(oldSnapshot: ScriptSnapshotShim): string; + getTextChangeRangeSinceVersion(scriptVersion: number): string; } - class ScriptSnapshotShimAdapter implements IScriptSnapshot { + // + // Public interface of the host of a language service shim instance. + // + export interface LanguageServiceShimHost extends Logger { + getCompilationSettings(): string; + + // Returns a JSON encoded value of the type: + // string[] + getScriptFileNames(): string; + getScriptVersion(fileName: string): number; + getScriptIsOpen(fileName: string): boolean; + getScriptByteOrderMark(fileName: string): number; + getScriptSnapshot(fileName: string): ScriptSnapshotShim; + getLocalizedDiagnosticMessages(): string; + getCancellationToken(): CancellationToken; + } + + // + // Public interface of of a language service instance shim. + // + export interface ShimFactory { + registerShim(shim: Shim): void; + unregisterShim(shim: Shim): void; + } + + export interface Shim { + dispose(dummy: any): void; + } + + export interface LanguageServiceShim extends Shim { + languageService: LanguageService; + + dispose(dummy: any): void; + + refresh(throwOnError: boolean): void; + + cleanupSemanticCache(): void; + + getSyntacticDiagnostics(fileName: string): string; + getSemanticDiagnostics(fileName: string): string; + getCompilerOptionsDiagnostics(): string; + + getCompletionsAtPosition(fileName: string, position: number, isMemberCompletion: boolean): string; + getCompletionEntryDetails(fileName: string, position: number, entryName: string): string; + + getTypeAtPosition(fileName: string, position: number): string; + getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string; + getBreakpointStatementAtPosition(fileName: string, position: number): string; + getSignatureAtPosition(fileName: string, position: number): string; + + // Returns a JSON encoded value of the type: + // { fileName: string; minChar: number; limChar: number; kind: string; name: string; containerKind: string; containerName: string } + // + // Or null value if no definition can be found. + getDefinitionAtPosition(fileName: string, position: number): string; + + // Returns a JSON encoded value of the type: + // { fileName: string; minChar: number; limChar: number; isWriteAccess: boolean }[] + getReferencesAtPosition(fileName: string, position: number): string; + + // Returns a JSON encoded value of the type: + // { fileName: string; minChar: number; limChar: number; isWriteAccess: boolean }[] + getOccurrencesAtPosition(fileName: string, position: number): string; + + // Returns a JSON encoded value of the type: + // { fileName: string; minChar: number; limChar: number; isWriteAccess: boolean }[] + getImplementorsAtPosition(fileName: string, position: number): string; + + // Returns a JSON encoded value of the type: + // { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; minChar: number; limChar: number; } [] = []; + getNavigateToItems(searchValue: string): string; + + // Returns a JSON encoded value of the type: + // { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; minChar: number; limChar: number; } [] = []; + getScriptLexicalStructure(fileName: string): string; + + // Returns a JSON encoded value of the type: + // { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; minChar: number; limChar: number; } [] = []; + getOutliningRegions(fileName: string): string; + + getBraceMatchingAtPosition(fileName: string, position: number): string; + getIndentationAtPosition(fileName: string, position: number, options: string/*Services.EditorOptions*/): string; + + getFormattingEditsForRange(fileName: string, minChar: number, limChar: number, options: string/*Services.FormatCodeOptions*/): string; + getFormattingEditsForDocument(fileName: string, minChar: number, limChar: number, options: string/*Services.FormatCodeOptions*/): string; + getFormattingEditsOnPaste(fileName: string, minChar: number, limChar: number, options: string/*Services.FormatCodeOptions*/): string; + getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: string/*Services.FormatCodeOptions*/): string; + + getEmitOutput(fileName: string): string; + } + + export interface ClassifierShim extends Shim { + getClassificationsForLine(text: string, lexState: EndOfLineState): string; + } + + export interface CoreServicesShim extends Shim { + getPreProcessedFileInfo(fileName: string, sourceText: TypeScript.IScriptSnapshot): string; + getDefaultCompilationSettings(): string; + } + + /// TODO: delete this, it is only needed untill the VS interface is updated + enum LanguageVersion { + EcmaScript3 = 0, + EcmaScript5 = 1, + } + + enum ModuleGenTarget { + Unspecified = 0, + Synchronous = 1, + Asynchronous = 2, + } + + interface CompilationSettings { + propagateEnumConstants?: boolean; + removeComments?: boolean; + watch?: boolean; + noResolve?: boolean; + allowAutomaticSemicolonInsertion?: boolean; + noImplicitAny?: boolean; + noLib?: boolean; + codeGenTarget?: LanguageVersion; + moduleGenTarget?: ModuleGenTarget; + outFileOption?: string; + outDirOption?: string; + mapSourceFiles?: boolean; + mapRoot?: string; + sourceRoot?: string; + generateDeclarationFiles?: boolean; + useCaseSensitiveFileResolution?: boolean; + gatherDiagnostics?: boolean; + codepage?: number; + } + + function languageVersionToScriptTarget(languageVersion: LanguageVersion): ScriptTarget { + if (typeof languageVersion === "undefined") return undefined; + + switch (languageVersion) { + case LanguageVersion.EcmaScript3: return ScriptTarget.ES3; + case LanguageVersion.EcmaScript5: return ScriptTarget.ES5; + default: throw Error("unsuported LanguageVersion value: " + languageVersion); + } + } + + function moduleGenTargetToModuleKind(moduleGenTarget: ModuleGenTarget): ModuleKind { + if (typeof moduleGenTarget === "undefined") return undefined; + + switch (moduleGenTarget) { + case ModuleGenTarget.Asynchronous: return ModuleKind.AMD; + case ModuleGenTarget.Synchronous: return ModuleKind.CommonJS; + case ModuleGenTarget.Unspecified: return ModuleKind.None; + default: throw Error("unsuported ModuleGenTarget value: " + moduleGenTarget); + } + } + + function scriptTargetTolanguageVersion(scriptTarget: ScriptTarget): LanguageVersion { + if (typeof scriptTarget === "undefined") return undefined; + + switch (scriptTarget) { + case ScriptTarget.ES3: return LanguageVersion.EcmaScript3; + case ScriptTarget.ES5: return LanguageVersion.EcmaScript5; + default: throw Error("unsuported ScriptTarget value: " + scriptTarget); + } + } + + function moduleKindToModuleGenTarget(moduleKind: ModuleKind): ModuleGenTarget { + if (typeof moduleKind === "undefined") return undefined; + + switch (moduleKind) { + case ModuleKind.AMD: return ModuleGenTarget.Asynchronous; + case ModuleKind.CommonJS: return ModuleGenTarget.Synchronous; + case ModuleKind.None: return ModuleGenTarget.Unspecified; + default: throw Error("unsuported ModuleKind value: " + moduleKind); + } + } + + function compilationSettingsToCompilerOptions(settings: CompilationSettings): CompilerOptions { + // TODO: we should not be converting, but use options all the way + var options: CompilerOptions = {}; + //options.propagateEnumConstants = settings.propagateEnumConstants; + options.removeComments = settings.removeComments; + options.noResolve = settings.noResolve; + options.noImplicitAny = settings.noImplicitAny; + options.noLib = settings.noLib; + options.target = languageVersionToScriptTarget(settings.codeGenTarget); + options.module = moduleGenTargetToModuleKind(settings.moduleGenTarget); + options.out = settings.outFileOption; + options.outDir = settings.outDirOption; + options.sourceMap = settings.mapSourceFiles; + options.mapRoot = settings.mapRoot; + options.sourceRoot = settings.sourceRoot; + options.declaration = settings.generateDeclarationFiles; + //options.useCaseSensitiveFileResolution = settings.useCaseSensitiveFileResolution; + options.codepage = settings.codepage; + return options; + } + + function compilerOptionsToCompilationSettings(options: CompilerOptions): CompilationSettings { + var settings: CompilationSettings = {}; + //options.propagateEnumConstants = settings.propagateEnumConstants; + settings.removeComments = options.removeComments; + settings.noResolve = options.noResolve; + settings.noImplicitAny = options.noImplicitAny; + settings.noLib = options.noLib; + settings.codeGenTarget = scriptTargetTolanguageVersion(options.target); + settings.moduleGenTarget = moduleKindToModuleGenTarget(options.module); + settings.outFileOption = options.out; + settings.outDirOption = options.outDir; + settings.mapSourceFiles = options.sourceMap; + settings.mapRoot = options.mapRoot; + settings.sourceRoot = options.sourceRoot; + settings.generateDeclarationFiles = options.declaration; + // settings.useCaseSensitiveFileResolution = options.useCaseSensitiveFileResolution; + settings.codepage = options.codepage; + return settings; + } + + function logInternalError(logger: Logger, err: Error) { + logger.log("*INTERNAL ERROR* - Exception in typescript services: " + err.message); + } + + class ScriptSnapshotShimAdapter implements TypeScript.IScriptSnapshot { private lineStartPositions: number[] = null; constructor(private scriptSnapshotShim: ScriptSnapshotShim) { @@ -53,41 +281,42 @@ module ts { return this.lineStartPositions; } - - public getChangeRange(scriptSnapshot: IScriptSnapshot): TextChangeRange { - function createTextRange(start: number, length: number) { - function createSpan(start: number, length: number) { - return { - start: () => start, - end: () => start + length, - lenth: () => length, - isEmpty: () => length === 0 - }; - } - - return { - span: () => createSpan(start, length), - newLength: () => length, - newSpan: () => createSpan(start, length), - isUnchanged: () => length === 0 - }; - } - - var encoded = this.scriptSnapshotShim.getChangeRange((scriptSnapshot).scriptSnapshotShim); + public getTextChangeRangeSinceVersion(scriptVersion: number): TypeScript.TextChangeRange { + var encoded = this.scriptSnapshotShim.getTextChangeRangeSinceVersion(scriptVersion); if (encoded == null) { return null; } var decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded); - - return createTextRange(decoded.span.start, decoded.span.length); + return new TypeScript.TextChangeRange( + new TypeScript.TextSpan(decoded.span.start, decoded.span.length), decoded.newLength); } } - export class LanguageServiceShimHostAdapter implements LanguageServiceHost { + class LanguageServiceShimHostAdapter implements LanguageServiceHost { constructor(private shimHost: LanguageServiceShimHost) { } + public information(): boolean { + return this.shimHost.information(); + } + + public debug(): boolean { + return this.shimHost.debug(); + } + + public warning(): boolean { + return this.shimHost.warning(); + } + + public error(): boolean { + return this.shimHost.error(); + } + + public fatal(): boolean { + return this.shimHost.fatal(); + } + public log(s: string): void { this.shimHost.log(s); } @@ -95,10 +324,18 @@ module ts { public getCompilationSettings(): CompilerOptions { var settingsJson = this.shimHost.getCompilationSettings(); if (settingsJson == null || settingsJson == "") { - return {}; + throw Error("LanguageServiceShimHostAdapter.getCompilationSettings: empty compilationSettings"); + return null; } - var settings: CompilerOptions = JSON.parse(settingsJson); - return settings; + var options = compilationSettingsToCompilerOptions(JSON.parse(settingsJson)); + + /// TODO: this should be pushed into VS. + /// We can not ask the LS instance to resolve, as this will lead to asking the host about files it does not know about, + /// something it is not desinged to handle. for now make sure we never get a "noresolve == false". + /// This value should not matter, as the host runs resolution logic independentlly + options.noResolve = true; + + return options; } public getScriptFileNames(): string[] { @@ -106,11 +343,11 @@ module ts { return JSON.parse(encoded); } - public getScriptSnapshot(fileName: string): IScriptSnapshot { + public getScriptSnapshot(fileName: string): TypeScript.IScriptSnapshot { return new ScriptSnapshotShimAdapter(this.shimHost.getScriptSnapshot(fileName)); } - public getScriptVersion(fileName: string): string { + public getScriptVersion(fileName: string): number { return this.shimHost.getScriptVersion(fileName); } @@ -136,10 +373,527 @@ module ts { } } - //public getCancellationToken(): CancellationToken { - // return this.shimHost.getCancellationToken(); - //} - - + public getCancellationToken(): CancellationToken { + return this.shimHost.getCancellationToken(); + } } -} \ No newline at end of file + + function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any): any { + logger.log(actionDescription); + var start = Date.now(); + var result = action(); + var end = Date.now(); + logger.log(actionDescription + " completed in " + (end - start) + " msec"); + if (typeof (result) === "string") { + var str = result; + if (str.length > 128) { + str = str.substring(0, 128) + "..."; + } + logger.log(" result.length=" + str.length + ", result='" + JSON.stringify(str) + "'"); + } + return result; + } + + function forwardJSONCall(logger: Logger, actionDescription: string, action: () => any): string { + try { + var result = simpleForwardCall(logger, actionDescription, action); + return JSON.stringify({ result: result }); + } + catch (err) { + if (err instanceof OperationCanceledException) { + return JSON.stringify({ canceled: true }); + } + logInternalError(logger, err); + err.description = actionDescription; + return JSON.stringify({ error: err }); + } + } + + class ShimBase implements Shim { + constructor(private factory: ShimFactory) { + factory.registerShim(this); + } + public dispose(dummy: any): void { + this.factory.unregisterShim(this); + } + } + + class LanguageServiceShimObject extends ShimBase implements LanguageServiceShim { + private logger: Logger; + + constructor(factory: ShimFactory, + private host: LanguageServiceShimHost, + public languageService: LanguageService) { + super(factory); + this.logger = this.host; + } + + public forwardJSONCall(actionDescription: string, action: () => any): string { + return forwardJSONCall(this.logger, actionDescription, action); + } + + // DISPOSE + // Ensure (almost) determinstic release of internal Javascript resources when + // some external native objects holds onto us (e.g. Com/Interop). + public dispose(dummy: any): void { + this.logger.log("dispose()"); + this.languageService.dispose(); + this.languageService = null; + + // force a GC + if (debugObjectHost && debugObjectHost.CollectGarbage) { + debugObjectHost.CollectGarbage(); + this.logger.log("CollectGarbage()"); + } + + this.logger = null; + + super.dispose(dummy); + } + + // REFRESH + // Update the list of scripts known to the compiler + public refresh(throwOnError: boolean): void { + this.forwardJSONCall( + "refresh(" + throwOnError + ")", + () => { + this.languageService.refresh(); + return null; + }); + } + + public cleanupSemanticCache(): void { + this.forwardJSONCall( + "cleanupSemanticCache()", + () => { + this.languageService.cleanupSemanticCache(); + return null; + }); + } + /// SQUIGGLES + /// + + private static realizeDiagnostic(diagnostic: Diagnostic): { message: string; start: number; length: number; category: string; } { + return { + message: diagnostic.messageText, + start: diagnostic.start, + length: diagnostic.length, + /// TODO: no need for the tolowerCase call + category: DiagnosticCategory[diagnostic.category].toLowerCase() + }; + } + + private realizeDiagnosticWithFileName(diagnostic: Diagnostic): { fileName: string; message: string; start: number; length: number; category: string; } { + return { + fileName: diagnostic.file.filename, + message: diagnostic.messageText, + start: diagnostic.start, + length: diagnostic.length, + /// TODO: no need for the tolowerCase call + category: DiagnosticCategory[diagnostic.category].toLowerCase() + }; + } + + public getSyntacticDiagnostics(fileName: string): string { + return this.forwardJSONCall( + "getSyntacticDiagnostics(\"" + fileName + "\")", + () => { + var errors = this.languageService.getSyntacticDiagnostics(fileName); + return errors.map(LanguageServiceShimObject.realizeDiagnostic); + }); + } + + public getSemanticDiagnostics(fileName: string): string { + return this.forwardJSONCall( + "getSemanticDiagnostics(\"" + fileName + "\")", + () => { + var errors = this.languageService.getSemanticDiagnostics(fileName); + return errors.map(LanguageServiceShimObject.realizeDiagnostic); + }); + } + + public getCompilerOptionsDiagnostics(): string { + return this.forwardJSONCall( + "getCompilerOptionsDiagnostics()", + () => { + var errors = this.languageService.getCompilerOptionsDiagnostics(); + return errors.map(d => this.realizeDiagnosticWithFileName(d)) + }); + } + + /// QUICKINFO + /// Computes a string representation of the type at the requested position + /// in the active file. + public getTypeAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getTypeAtPosition(\"" + fileName + "\", " + position + ")", + () => { + var typeInfo = this.languageService.getTypeAtPosition(fileName, position); + return typeInfo; + }); + } + + /// NAMEORDOTTEDNAMESPAN + /// Computes span information of the name or dotted name at the requested position + // in the active file. + public getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string { + return this.forwardJSONCall( + "getNameOrDottedNameSpan(\"" + fileName + "\", " + startPos + ", " + endPos + ")", + () => { + var spanInfo = this.languageService.getNameOrDottedNameSpan(fileName, startPos, endPos); + return spanInfo; + }); + } + + /// STATEMENTSPAN + /// Computes span information of statement at the requested position in the active file. + public getBreakpointStatementAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getBreakpointStatementAtPosition(\"" + fileName + "\", " + position + ")", + () => { + var spanInfo = this.languageService.getBreakpointStatementAtPosition(fileName, position); + return spanInfo; + }); + } + + /// SIGNATUREHELP + /// Computes a string representation of the signatures at the requested position + /// in the active file. + public getSignatureAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getSignatureAtPosition(\"" + fileName + "\", " + position + ")", + () => { + var signatureInfo = this.languageService.getSignatureAtPosition(fileName, position); + return signatureInfo; + }); + } + + /// GOTO DEFINITION + /// Computes the definition location and file for the symbol + /// at the requested position. + public getDefinitionAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getDefinitionAtPosition(\"" + fileName + "\", " + position + ")", + () => { + return this.languageService.getDefinitionAtPosition(fileName, position); + }); + } + + /// GET BRACE MATCHING + public getBraceMatchingAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getBraceMatchingAtPosition(\"" + fileName + "\", " + position + ")", + () => { + var textRanges = this.languageService.getBraceMatchingAtPosition(fileName, position); + return textRanges; + }); + } + + /// GET SMART INDENT + public getIndentationAtPosition(fileName: string, position: number, options: string /*Services.EditorOptions*/): string { + return this.forwardJSONCall( + "getIndentationAtPosition(\"" + fileName + "\", " + position + ")", + () => { + var localOptions: EditorOptions = JSON.parse(options); + var columnOffset = this.languageService.getIndentationAtPosition(fileName, position, localOptions); + return { value: columnOffset }; + }); + } + + /// GET REFERENCES + /// Return references to a symbol at the requested position. + /// References are separated by "\n". + /// Each reference is a "fileindex min lim" sub-string. + public getReferencesAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getReferencesAtPosition(\"" + fileName + "\", " + position + ")", + () => { + return this.languageService.getReferencesAtPosition(fileName, position); + }); + } + + public getOccurrencesAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getOccurrencesAtPosition(\"" + fileName + "\", " + position + ")", + () => { + return this.languageService.getOccurrencesAtPosition(fileName, position); + }); + } + + /// GET IMPLEMENTORS + public getImplementorsAtPosition(fileName: string, position: number): string { + return this.forwardJSONCall( + "getImplementorsAtPosition(\"" + fileName + "\", " + position + ")", + () => { + return this.languageService.getImplementorsAtPosition(fileName, position); + }); + } + + + /// COMPLETION LISTS + /// Get a string based representation of the completions + /// to provide at the given source position and providing a member completion + /// list if requested. + public getCompletionsAtPosition(fileName: string, position: number, isMemberCompletion: boolean) { + return this.forwardJSONCall( + "getCompletionsAtPosition(\"" + fileName + "\", " + position + ", " + isMemberCompletion + ")", + () => { + var completion = this.languageService.getCompletionsAtPosition(fileName, position, isMemberCompletion); + return completion; + }); + } + + /// Get a string based representation of a completion list entry details + public getCompletionEntryDetails(fileName: string, position: number, entryName: string) { + return this.forwardJSONCall( + "getCompletionEntryDetails(\"" + fileName + "\", " + position + ", " + entryName + ")", + () => { + var details = this.languageService.getCompletionEntryDetails(fileName, position, entryName); + return details; + }); + } + + /// FORMAT SELECTION + public getFormattingEditsForRange(fileName: string, minChar: number, limChar: number, options: string/*Services.FormatCodeOptions*/): string { + return this.forwardJSONCall( + "getFormattingEditsForRange(\"" + fileName + "\", " + minChar + ", " + limChar + ")", + () => { + var localOptions: FormatCodeOptions = JSON.parse(options); + var edits = this.languageService.getFormattingEditsForRange(fileName, minChar, limChar, localOptions); + return edits; + }); + } + + /// FORMAT DOCUMENT + public getFormattingEditsForDocument(fileName: string, minChar: number, limChar: number, options: string/*Services.FormatCodeOptions*/): string { + return this.forwardJSONCall( + "getFormattingEditsForDocument(\"" + fileName + "\", " + minChar + ", " + limChar + ")", + () => { + var localOptions: FormatCodeOptions = JSON.parse(options); + var edits = this.languageService.getFormattingEditsForDocument(fileName, minChar, limChar, localOptions); + return edits; + }); + } + + /// FORMAT ON PASTE + public getFormattingEditsOnPaste(fileName: string, minChar: number, limChar: number, options: string/*Services.FormatCodeOptions*/): string { + return this.forwardJSONCall( + "getFormattingEditsOnPaste(\"" + fileName + "\", " + minChar + ", " + limChar + ")", + () => { + var localOptions: FormatCodeOptions = JSON.parse(options); + var edits = this.languageService.getFormattingEditsOnPaste(fileName, minChar, limChar, localOptions); + return edits; + }); + } + + /// FORMAT + public getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: string/*Services.FormatCodeOptions*/): string { + return this.forwardJSONCall( + "getFormattingEditsAfterKeystroke(\"" + fileName + "\", " + position + ", \"" + key + "\")", + () => { + var localOptions: FormatCodeOptions = JSON.parse(options); + var edits = this.languageService.getFormattingEditsAfterKeystroke(fileName, position, key, localOptions); + return edits; + }); + } + + /// NAVIGATE TO + /// Return a list of symbols that are interesting to navigate to + public getNavigateToItems(searchValue: string): string { + return this.forwardJSONCall( + "getNavigateToItems(\"" + searchValue + "\")", + () => { + var items = this.languageService.getNavigateToItems(searchValue); + var result = this._navigateToItemsToString(items); + return result; + }); + } + + // GET SCRIPT LEXICAL STRUCTURE + // + public getScriptLexicalStructure(fileName: string): string { + return this.forwardJSONCall( + "getScriptLexicalStructure(\"" + fileName + "\")", + () => { + var items = this.languageService.getScriptLexicalStructure(fileName); + var result = this._navigateToItemsToString(items); + return result; + }); + } + + // GET OUTLINING REGIONS + // + public getOutliningRegions(fileName: string): string { + return this.forwardJSONCall( + "getOutliningRegions(\"" + fileName + "\")", + () => { + var items = this.languageService.getOutliningRegions(fileName); + return items; + }); + } + + /// Emit + public getEmitOutput(fileName: string): string { + return this.forwardJSONCall( + "getEmitOutput(\"" + fileName + "\")", + () => { + var output = this.languageService.getEmitOutput(fileName); + return output; + }); + } + + private _navigateToItemsToString(items: NavigateToItem[]): any { + var result: { + name: string; + kind: string; + kindModifiers: string; + containerName: string; + containerKind: string; + matchKind: string; + fileName: string; + minChar: number; + limChar: number; + additionalSpans?: { start: number; end: number; }[]; + }[] = []; + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + result.push({ + name: item.name, + kind: item.kind, + kindModifiers: item.kindModifiers, + containerName: item.containerName, + containerKind: item.containerKind, + matchKind: item.matchKind, + fileName: item.fileName, + minChar: item.minChar, + limChar: item.limChar, + additionalSpans: item.additionalSpans ? item.additionalSpans.map(i => { return { start: i.minChar, end: i.limChar }; }) : undefined + }); + } + + return result; + } + } + + class ClassifierShimObject extends ShimBase implements ClassifierShim { + public classifier: Classifier; + + constructor(factory: ShimFactory, public host: Logger) { + super(factory); + this.classifier = createClassifier(this.host); + } + + /// COLORIZATION + public getClassificationsForLine(text: string, lexState: EndOfLineState): string { + var classification = this.classifier.getClassificationsForLine(text, lexState); + var items = classification.entries; + var result = ""; + for (var i = 0; i < items.length; i++) { + result += items[i].length + "\n"; + result += items[i].classification + "\n"; + } + result += classification.finalLexState; + return result; + } + } + + class CoreServicesShimObject extends ShimBase implements CoreServicesShim { + constructor(factory: ShimFactory, public host: Logger) { + super(factory); + } + + private forwardJSONCall(actionDescription: string, action: () => any): any { + return forwardJSONCall(this.host, actionDescription, action); + } + + /// + /// getPreProcessedFileInfo + /// + public getPreProcessedFileInfo(fileName: string, sourceText: TypeScript.IScriptSnapshot): string { + return this.forwardJSONCall( + "getPreProcessedFileInfo(\"" + fileName + "\")", + () => { + var result = TypeScript.preProcessFile(fileName, sourceText); + return result; + }); + } + + /// + /// getDefaultCompilationSettings + /// + public getDefaultCompilationSettings(): string { + return this.forwardJSONCall( + "getDefaultCompilationSettings()", + () => { + return compilerOptionsToCompilationSettings(getDefaultCompilerOptions()); + }); + } + } + + export class TypeScriptServicesFactory implements ShimFactory { + private _shims: Shim[] = []; + private documentRegistry: DocumentRegistry = createDocumentRegistry(); + + public createLanguageServiceShim(host: LanguageServiceShimHost): LanguageServiceShim { + try { + var hostAdapter = new LanguageServiceShimHostAdapter(host); + var pullLanguageService = createLanguageService(hostAdapter, this.documentRegistry); + return new LanguageServiceShimObject(this, host, pullLanguageService); + } + catch (err) { + logInternalError(host, err); + throw err; + } + } + + public createClassifierShim(host: Logger): ClassifierShim { + try { + return new ClassifierShimObject(this, host); + } + catch (err) { + logInternalError(host, err); + throw err; + } + } + + public createCoreServicesShim(host: Logger): CoreServicesShim { + try { + return new CoreServicesShimObject(this, host); + } + catch (err) { + logInternalError(host, err); + throw err; + } + } + + public close(): void { + // Forget all the registered shims + this._shims = []; + this.documentRegistry = createDocumentRegistry(); + } + + public registerShim(shim: Shim): void { + this._shims.push(shim); + } + + public unregisterShim(shim: Shim): void { + for (var i = 0, n = this._shims.length; i < n; i++) { + if (this._shims[i] === shim) { + delete this._shims[i]; + return; + } + } + + throw TypeScript.Errors.invalidOperation(); + } + } +} + + +/// TODO: this is used by VS, clean this up on both sides of the interfrace +module TypeScript.Services { + export var TypeScriptServicesFactory = ts.TypeScriptServicesFactory; +} + diff --git a/src/services/signatureInfoHelpers.ts b/src/services/signatureInfoHelpers.ts new file mode 100644 index 00000000000..145dcc02381 --- /dev/null +++ b/src/services/signatureInfoHelpers.ts @@ -0,0 +1,348 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. +// See LICENSE.txt in the project root for complete license information. + +/// + +module TypeScript.Services { + + export interface IPartiallyWrittenTypeArgumentListInformation { + genericIdentifer: TypeScript.ISyntaxToken; + lessThanToken: TypeScript.ISyntaxToken; + argumentIndex: number; + } + + export interface IExpressionWithArgumentListSyntax extends IExpressionSyntax { + expression: IExpressionSyntax; + argumentList: ArgumentListSyntax; + } + + export class SignatureInfoHelpers { + + // A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression + // or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference. + // To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it + // will return the generic identifier that started the expression (e.g. "foo" in "foo 1; + + for (var i = 0, n = signatures.length; i < n; i++) { + var signature = signatures[i]; + + // filter out the definition signature if there are overloads + if (hasOverloads && signature.isDefinition()) { + continue; + } + + var signatureGroupInfo = new FormalSignatureItemInfo(); + var paramIndexInfo: number[] = []; + var functionName = signature.getScopedNameEx(enclosingScopeSymbol).toString(); + if (!functionName && (!symbol.isType() || (symbol).isNamedTypeSymbol())) { + functionName = symbol.getScopedNameEx(enclosingScopeSymbol).toString(); + } + + var signatureMemberName = signature.getSignatureTypeNameEx(functionName, /*shortform*/ false, /*brackets*/ false, enclosingScopeSymbol, /*getParamMarkerInfo*/ true, /*getTypeParameterMarkerInfo*/ true); + signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(signatureMemberName, paramIndexInfo); + signatureGroupInfo.docComment = signature.docComments(); + + var parameterMarkerIndex = 0; + + if (signature.isGeneric()) { + var typeParameters = signature.getTypeParameters(); + for (var j = 0, m = typeParameters.length; j < m; j++) { + var typeParameter = typeParameters[j]; + var signatureTypeParameterInfo = new FormalTypeParameterInfo(); + signatureTypeParameterInfo.name = typeParameter.getDisplayName(); + signatureTypeParameterInfo.docComment = typeParameter.docComments(); + signatureTypeParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; + signatureTypeParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; + parameterMarkerIndex++; + signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); + } + } + + var parameters = signature.parameters; + for (var j = 0, m = parameters.length; j < m; j++) { + var parameter = parameters[j]; + var signatureParameterInfo = new FormalParameterInfo(); + signatureParameterInfo.isVariable = signature.hasVarArgs && (j === parameters.length - 1); + signatureParameterInfo.name = parameter.getDisplayName(); + signatureParameterInfo.docComment = parameter.docComments(); + signatureParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; + signatureParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; + parameterMarkerIndex++; + signatureGroupInfo.parameters.push(signatureParameterInfo); + } + + signatureGroup.push(signatureGroupInfo); + } + + return signatureGroup; + } + + public static getSignatureInfoFromGenericSymbol(symbol: TypeScript.PullSymbol, enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) { + var signatureGroupInfo = new FormalSignatureItemInfo(); + + var paramIndexInfo: number[] = []; + var symbolName = symbol.getScopedNameEx(enclosingScopeSymbol, /*skipTypeParametersInName*/ false, /*useConstaintInName*/ true, /*getPrettyTypeName*/ false, /*getTypeParamMarkerInfo*/ true); + + signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(symbolName, paramIndexInfo); + signatureGroupInfo.docComment = symbol.docComments(); + + var parameterMarkerIndex = 0; + + var typeSymbol = symbol.type; + + var typeParameters = typeSymbol.getTypeParameters(); + for (var i = 0, n = typeParameters.length; i < n; i++) { + var typeParameter = typeParameters[i]; + var signatureTypeParameterInfo = new FormalTypeParameterInfo(); + signatureTypeParameterInfo.name = typeParameter.getDisplayName(); + signatureTypeParameterInfo.docComment = typeParameter.docComments(); + signatureTypeParameterInfo.minChar = paramIndexInfo[2 * i]; + signatureTypeParameterInfo.limChar = paramIndexInfo[2 * i + 1]; + signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); + } + + return [signatureGroupInfo]; + } + + public static getActualSignatureInfoFromCallExpression(ast: IExpressionWithArgumentListSyntax, caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { + if (!ast) { + return null; + } + + var result = new ActualSignatureInfo(); + + // The expression is not guaranteed to be complete, we need to populate the min and lim with the most accurate information we have about + // type argument and argument lists + var parameterMinChar = caretPosition; + var parameterLimChar = caretPosition; + + if (ast.argumentList.typeArgumentList) { + parameterMinChar = Math.min(start(ast.argumentList.typeArgumentList)); + parameterLimChar = Math.max(Math.max(start(ast.argumentList.typeArgumentList), end(ast.argumentList.typeArgumentList) + trailingTriviaWidth(ast.argumentList.typeArgumentList))); + } + + if (ast.argumentList.arguments) { + parameterMinChar = Math.min(parameterMinChar, end(ast.argumentList.openParenToken)); + parameterLimChar = Math.max(parameterLimChar, + ast.argumentList.closeParenToken.fullWidth() > 0 ? start(ast.argumentList.closeParenToken) : fullEnd(ast.argumentList)); + } + + result.parameterMinChar = parameterMinChar; + result.parameterLimChar = parameterLimChar; + result.currentParameterIsTypeParameter = false; + result.currentParameter = -1; + + if (typeParameterInformation) { + result.currentParameterIsTypeParameter = true; + result.currentParameter = typeParameterInformation.argumentIndex; + } + else if (ast.argumentList.arguments && ast.argumentList.arguments.length > 0) { + result.currentParameter = 0; + for (var index = 0; index < ast.argumentList.arguments.length; index++) { + if (caretPosition > end(ast.argumentList.arguments[index]) + lastToken(ast.argumentList.arguments[index]).trailingTriviaWidth()) { + result.currentParameter++; + } + } + } + + return result; + } + + public static getActualSignatureInfoFromPartiallyWritenGenericExpression(caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { + var result = new ActualSignatureInfo(); + + result.parameterMinChar = start(typeParameterInformation.lessThanToken); + result.parameterLimChar = Math.max(fullEnd(typeParameterInformation.lessThanToken), caretPosition); + result.currentParameterIsTypeParameter = true; + result.currentParameter = typeParameterInformation.argumentIndex; + + return result; + } + + public static isSignatureHelpBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { + // We shouldn't be getting a possition that is outside the file because + // isEntirelyInsideComment can't handle when the position is out of bounds, + // callers should be fixed, however we should be resiliant to bad inputs + // so we return true (this position is a blocker for getting signature help) + if (position < 0 || position > fullWidth(sourceUnit)) { + return true; + } + + return TypeScript.Syntax.isEntirelyInsideComment(sourceUnit, position); + } + + public static isTargetOfObjectCreationExpression(positionedToken: TypeScript.ISyntaxToken): boolean { + var positionedParent = TypeScript.Syntax.getAncestorOfKind(positionedToken, TypeScript.SyntaxKind.ObjectCreationExpression); + if (positionedParent) { + var objectCreationExpression = positionedParent; + var expressionRelativeStart = objectCreationExpression.newKeyword.fullWidth(); + var tokenRelativeStart = positionedToken.fullStart() - fullStart(positionedParent); + return tokenRelativeStart >= expressionRelativeStart && + tokenRelativeStart <= (expressionRelativeStart + fullWidth(objectCreationExpression.expression)); + } + + return false; + } + + private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken { + if (!token || token.kind() !== tokenKind) { + throw TypeScript.Errors.invalidOperation(); + } + + // Skip the current token + token = previousToken(token, /*includeSkippedTokens*/ true); + + var stack = 0; + + while (token) { + if (token.kind() === matchingTokenKind) { + if (stack === 0) { + // Found the matching token, return + return token; + } + else if (stack < 0) { + // tokens overlapped.. bail out. + break; + } + else { + stack--; + } + } + else if (token.kind() === tokenKind) { + stack++; + } + + // Move back + token = previousToken(token, /*includeSkippedTokens*/ true); + } + + // Did not find matching token + return null; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/SyntaxGenerator.d.ts b/src/services/syntax/SyntaxGenerator.d.ts new file mode 100644 index 00000000000..4a833f07587 --- /dev/null +++ b/src/services/syntax/SyntaxGenerator.d.ts @@ -0,0 +1,1139 @@ +declare var require: any; +declare var module: any; +declare module TypeScript { + var DiagnosticCode: { + error_TS_0_1: string; + warning_TS_0_1: string; + Unrecognized_escape_sequence: string; + Unexpected_character_0: string; + Missing_close_quote_character: string; + Identifier_expected: string; + _0_keyword_expected: string; + _0_expected: string; + Identifier_expected_0_is_a_keyword: string; + Automatic_semicolon_insertion_not_allowed: string; + Unexpected_token_0_expected: string; + Trailing_separator_not_allowed: string; + AsteriskSlash_expected: string; + public_or_private_modifier_must_precede_static: string; + Unexpected_token: string; + Catch_clause_parameter_cannot_have_a_type_annotation: string; + Rest_parameter_must_be_last_in_list: string; + Parameter_cannot_have_question_mark_and_initializer: string; + Required_parameter_cannot_follow_optional_parameter: string; + Index_signatures_cannot_have_rest_parameters: string; + Index_signature_parameter_cannot_have_accessibility_modifiers: string; + Index_signature_parameter_cannot_have_a_question_mark: string; + Index_signature_parameter_cannot_have_an_initializer: string; + Index_signature_must_have_a_type_annotation: string; + Index_signature_parameter_must_have_a_type_annotation: string; + Index_signature_parameter_type_must_be_string_or_number: string; + extends_clause_already_seen: string; + extends_clause_must_precede_implements_clause: string; + Classes_can_only_extend_a_single_class: string; + implements_clause_already_seen: string; + Accessibility_modifier_already_seen: string; + _0_modifier_must_precede_1_modifier: string; + _0_modifier_already_seen: string; + _0_modifier_cannot_appear_on_a_class_element: string; + Interface_declaration_cannot_have_implements_clause: string; + super_invocation_cannot_have_type_arguments: string; + Only_ambient_modules_can_use_quoted_names: string; + Statements_are_not_allowed_in_ambient_contexts: string; + Implementations_are_not_allowed_in_ambient_contexts: string; + declare_modifier_not_allowed_for_code_already_in_an_ambient_context: string; + Initializers_are_not_allowed_in_ambient_contexts: string; + Parameter_property_declarations_can_only_be_used_in_a_non_ambient_constructor_declaration: string; + Function_implementation_expected: string; + Constructor_implementation_expected: string; + Function_overload_name_must_be_0: string; + _0_modifier_cannot_appear_on_a_module_element: string; + declare_modifier_cannot_appear_on_an_interface_declaration: string; + declare_modifier_required_for_top_level_element: string; + Rest_parameter_cannot_be_optional: string; + Rest_parameter_cannot_have_an_initializer: string; + set_accessor_must_have_one_and_only_one_parameter: string; + set_accessor_parameter_cannot_be_optional: string; + set_accessor_parameter_cannot_have_an_initializer: string; + set_accessor_cannot_have_rest_parameter: string; + get_accessor_cannot_have_parameters: string; + Modifiers_cannot_appear_here: string; + Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher: string; + Class_name_cannot_be_0: string; + Interface_name_cannot_be_0: string; + Enum_name_cannot_be_0: string; + Module_name_cannot_be_0: string; + Enum_member_must_have_initializer: string; + Export_assignment_cannot_be_used_in_internal_modules: string; + Export_assignment_not_allowed_in_module_with_exported_element: string; + Module_cannot_have_multiple_export_assignments: string; + Ambient_enum_elements_can_only_have_integer_literal_initializers: string; + module_class_interface_enum_import_or_statement: string; + constructor_function_accessor_or_variable: string; + statement: string; + case_or_default_clause: string; + identifier: string; + call_construct_index_property_or_function_signature: string; + expression: string; + type_name: string; + property_or_accessor: string; + parameter: string; + type: string; + type_parameter: string; + declare_modifier_not_allowed_on_import_declaration: string; + Function_overload_must_be_static: string; + Function_overload_must_not_be_static: string; + Parameter_property_declarations_cannot_be_used_in_a_constructor_overload: string; + Invalid_reference_directive_syntax: string; + Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher: string; + Accessors_are_not_allowed_in_ambient_contexts: string; + _0_modifier_cannot_appear_on_a_constructor_declaration: string; + _0_modifier_cannot_appear_on_a_parameter: string; + Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement: string; + Type_parameters_cannot_appear_on_a_constructor_declaration: string; + Type_annotation_cannot_appear_on_a_constructor_declaration: string; + Duplicate_identifier_0: string; + The_name_0_does_not_exist_in_the_current_scope: string; + The_name_0_does_not_refer_to_a_value: string; + super_can_only_be_used_inside_a_class_instance_method: string; + The_left_hand_side_of_an_assignment_expression_must_be_a_variable_property_or_indexer: string; + Value_of_type_0_is_not_callable_Did_you_mean_to_include_new: string; + Value_of_type_0_is_not_callable: string; + Value_of_type_0_is_not_newable: string; + Value_of_type_0_is_not_indexable_by_type_1: string; + Operator_0_cannot_be_applied_to_types_1_and_2: string; + Operator_0_cannot_be_applied_to_types_1_and_2_3: string; + Cannot_convert_0_to_1: string; + Cannot_convert_0_to_1_NL_2: string; + Expected_var_class_interface_or_module: string; + Operator_0_cannot_be_applied_to_type_1: string; + Getter_0_already_declared: string; + Setter_0_already_declared: string; + Exported_class_0_extends_private_class_1: string; + Exported_class_0_implements_private_interface_1: string; + Exported_interface_0_extends_private_interface_1: string; + Exported_class_0_extends_class_from_inaccessible_module_1: string; + Exported_class_0_implements_interface_from_inaccessible_module_1: string; + Exported_interface_0_extends_interface_from_inaccessible_module_1: string; + Public_static_property_0_of_exported_class_has_or_is_using_private_type_1: string; + Public_property_0_of_exported_class_has_or_is_using_private_type_1: string; + Property_0_of_exported_interface_has_or_is_using_private_type_1: string; + Exported_variable_0_has_or_is_using_private_type_1: string; + Public_static_property_0_of_exported_class_is_using_inaccessible_module_1: string; + Public_property_0_of_exported_class_is_using_inaccessible_module_1: string; + Property_0_of_exported_interface_is_using_inaccessible_module_1: string; + Exported_variable_0_is_using_inaccessible_module_1: string; + Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_type_1: string; + Parameter_0_of_public_static_property_setter_from_exported_class_has_or_is_using_private_type_1: string; + Parameter_0_of_public_property_setter_from_exported_class_has_or_is_using_private_type_1: string; + Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_type_1: string; + Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_type_1: string; + Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_type_1: string; + Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_type_1: string; + Parameter_0_of_method_from_exported_interface_has_or_is_using_private_type_1: string; + Parameter_0_of_exported_function_has_or_is_using_private_type_1: string; + Parameter_0_of_constructor_from_exported_class_is_using_inaccessible_module_1: string; + Parameter_0_of_public_static_property_setter_from_exported_class_is_using_inaccessible_module_1: string; + Parameter_0_of_public_property_setter_from_exported_class_is_using_inaccessible_module_1: string; + Parameter_0_of_constructor_signature_from_exported_interface_is_using_inaccessible_module_1: string; + Parameter_0_of_call_signature_from_exported_interface_is_using_inaccessible_module_1: string; + Parameter_0_of_public_static_method_from_exported_class_is_using_inaccessible_module_1: string; + Parameter_0_of_public_method_from_exported_class_is_using_inaccessible_module_1: string; + Parameter_0_of_method_from_exported_interface_is_using_inaccessible_module_1: string; + Parameter_0_of_exported_function_is_using_inaccessible_module_1: string; + Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_private_type_0: string; + Return_type_of_public_property_getter_from_exported_class_has_or_is_using_private_type_0: string; + Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_type_0: string; + Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_type_0: string; + Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_type_0: string; + Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_type_0: string; + Return_type_of_public_method_from_exported_class_has_or_is_using_private_type_0: string; + Return_type_of_method_from_exported_interface_has_or_is_using_private_type_0: string; + Return_type_of_exported_function_has_or_is_using_private_type_0: string; + Return_type_of_public_static_property_getter_from_exported_class_is_using_inaccessible_module_0: string; + Return_type_of_public_property_getter_from_exported_class_is_using_inaccessible_module_0: string; + Return_type_of_constructor_signature_from_exported_interface_is_using_inaccessible_module_0: string; + Return_type_of_call_signature_from_exported_interface_is_using_inaccessible_module_0: string; + Return_type_of_index_signature_from_exported_interface_is_using_inaccessible_module_0: string; + Return_type_of_public_static_method_from_exported_class_is_using_inaccessible_module_0: string; + Return_type_of_public_method_from_exported_class_is_using_inaccessible_module_0: string; + Return_type_of_method_from_exported_interface_is_using_inaccessible_module_0: string; + Return_type_of_exported_function_is_using_inaccessible_module_0: string; + new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead: string; + A_parameter_list_must_follow_a_generic_type_argument_list_expected: string; + Multiple_constructor_implementations_are_not_allowed: string; + Unable_to_resolve_external_module_0: string; + Module_cannot_be_aliased_to_a_non_module_type: string; + A_class_may_only_extend_another_class: string; + A_class_may_only_implement_another_class_or_interface: string; + An_interface_may_only_extend_another_class_or_interface: string; + Unable_to_resolve_type: string; + Unable_to_resolve_type_of_0: string; + Unable_to_resolve_type_parameter_constraint: string; + Type_parameter_constraint_cannot_be_a_primitive_type: string; + Supplied_parameters_do_not_match_any_signature_of_call_target: string; + Supplied_parameters_do_not_match_any_signature_of_call_target_NL_0: string; + Invalid_new_expression: string; + Call_signatures_used_in_a_new_expression_must_have_a_void_return_type: string; + Could_not_select_overload_for_new_expression: string; + Type_0_does_not_satisfy_the_constraint_1_for_type_parameter_2: string; + Could_not_select_overload_for_call_expression: string; + Cannot_invoke_an_expression_whose_type_lacks_a_call_signature: string; + Calls_to_super_are_only_valid_inside_a_class: string; + Generic_type_0_requires_1_type_argument_s: string; + Type_of_array_literal_cannot_be_determined_Best_common_type_could_not_be_found_for_array_elements: string; + Could_not_find_enclosing_symbol_for_dotted_name_0: string; + The_property_0_does_not_exist_on_value_of_type_1: string; + Could_not_find_symbol_0: string; + get_and_set_accessor_must_have_the_same_type: string; + this_cannot_be_referenced_in_current_location: string; + Static_members_cannot_reference_class_type_parameters: string; + Class_0_is_recursively_referenced_as_a_base_type_of_itself: string; + Interface_0_is_recursively_referenced_as_a_base_type_of_itself: string; + super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class: string; + super_cannot_be_referenced_in_non_derived_classes: string; + A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties: string; + Constructors_for_derived_classes_must_contain_a_super_call: string; + Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors: string; + _0_1_is_inaccessible: string; + this_cannot_be_referenced_within_module_bodies: string; + Invalid_expression_types_not_known_to_support_the_addition_operator: string; + The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: string; + The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type: string; + The_type_of_a_unary_arithmetic_operation_operand_must_be_of_type_any_number_or_an_enum_type: string; + Variable_declarations_of_a_for_statement_cannot_use_a_type_annotation: string; + Variable_declarations_of_a_for_statement_must_be_of_types_string_or_any: string; + The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter: string; + The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number: string; + The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: string; + The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: string; + The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type: string; + Setters_cannot_return_a_value: string; + Tried_to_query_type_of_uninitialized_module_0: string; + Tried_to_set_variable_type_to_uninitialized_module_type_0: string; + Type_0_does_not_have_type_parameters: string; + Getters_must_return_a_value: string; + Getter_and_setter_accessors_do_not_agree_in_visibility: string; + Invalid_left_hand_side_of_assignment_expression: string; + Function_declared_a_non_void_return_type_but_has_no_return_expression: string; + Cannot_resolve_return_type_reference: string; + Constructors_cannot_have_a_return_type_of_void: string; + Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2: string; + All_symbols_within_a_with_block_will_be_resolved_to_any: string; + Import_declarations_in_an_internal_module_cannot_reference_an_external_module: string; + Class_0_declares_interface_1_but_does_not_implement_it_NL_2: string; + Class_0_declares_class_1_as_an_interface_but_does_not_implement_it_NL_2: string; + The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer: string; + this_cannot_be_referenced_in_static_initializers_in_a_class_body: string; + Class_0_cannot_extend_class_1_NL_2: string; + Interface_0_cannot_extend_class_1_NL_2: string; + Interface_0_cannot_extend_interface_1_NL_2: string; + Overload_signature_is_not_compatible_with_function_definition: string; + Overload_signature_is_not_compatible_with_function_definition_NL_0: string; + Overload_signatures_must_all_be_public_or_private: string; + Overload_signatures_must_all_be_exported_or_not_exported: string; + Overload_signatures_must_all_be_ambient_or_non_ambient: string; + Overload_signatures_must_all_be_optional_or_required: string; + Specialized_overload_signature_is_not_assignable_to_any_non_specialized_signature: string; + this_cannot_be_referenced_in_constructor_arguments: string; + Instance_member_cannot_be_accessed_off_a_class: string; + Untyped_function_calls_may_not_accept_type_arguments: string; + Non_generic_functions_may_not_accept_type_arguments: string; + A_generic_type_may_not_reference_itself_with_a_wrapped_form_of_its_own_type_parameters: string; + Rest_parameters_must_be_array_types: string; + Overload_signature_implementation_cannot_use_specialized_type: string; + Export_assignments_may_only_be_used_at_the_top_level_of_external_modules: string; + Export_assignments_may_only_be_made_with_variables_functions_classes_interfaces_enums_and_internal_modules: string; + Only_public_methods_of_the_base_class_are_accessible_via_the_super_keyword: string; + Numeric_indexer_type_0_must_be_assignable_to_string_indexer_type_1: string; + Numeric_indexer_type_0_must_be_assignable_to_string_indexer_type_1_NL_2: string; + All_numerically_named_properties_must_be_assignable_to_numeric_indexer_type_0: string; + All_numerically_named_properties_must_be_assignable_to_numeric_indexer_type_0_NL_1: string; + All_named_properties_must_be_assignable_to_string_indexer_type_0: string; + All_named_properties_must_be_assignable_to_string_indexer_type_0_NL_1: string; + Generic_type_references_must_include_all_type_arguments: string; + Default_arguments_are_only_allowed_in_implementation: string; + Function_expression_declared_a_non_void_return_type_but_has_no_return_expression: string; + Import_declaration_referencing_identifier_from_internal_module_can_only_be_made_with_variables_functions_classes_interfaces_enums_and_internal_modules: string; + Could_not_find_symbol_0_in_module_1: string; + Unable_to_resolve_module_reference_0: string; + Could_not_find_module_0_in_module_1: string; + Exported_import_declaration_0_is_assigned_value_with_type_that_has_or_is_using_private_type_1: string; + Exported_import_declaration_0_is_assigned_value_with_type_that_is_using_inaccessible_module_1: string; + Exported_import_declaration_0_is_assigned_type_that_has_or_is_using_private_type_1: string; + Exported_import_declaration_0_is_assigned_type_that_is_using_inaccessible_module_1: string; + Exported_import_declaration_0_is_assigned_container_that_is_or_is_using_inaccessible_module_1: string; + Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_1: string; + Internal_module_reference_0_in_import_declaration_does_not_reference_module_instance_for_1: string; + Module_0_cannot_merge_with_previous_declaration_of_1_in_a_different_file_2: string; + Interface_0_cannot_simultaneously_extend_types_1_and_2_NL_3: string; + Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: string; + Ambient_external_module_declaration_cannot_be_reopened: string; + All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported: string; + super_cannot_be_referenced_in_constructor_arguments: string; + Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: string; + Ambient_external_module_declaration_must_be_defined_in_global_context: string; + Ambient_external_module_declaration_cannot_specify_relative_module_name: string; + Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name: string; + Could_not_find_the_best_common_type_of_types_of_all_return_statement_expressions: string; + Import_declaration_cannot_refer_to_external_module_reference_when_noResolve_option_is_set: string; + Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference: string; + continue_statement_can_only_be_used_within_an_enclosing_iteration_statement: string; + break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement: string; + Jump_target_not_found: string; + Jump_target_cannot_cross_function_boundary: string; + Duplicate_identifier_super_Compiler_uses_super_to_capture_base_class_reference: string; + Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference: string; + Expression_resolves_to_super_that_compiler_uses_to_capture_base_class_reference: string; + TypeParameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_type_1: string; + TypeParameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_type_1: string; + TypeParameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_type_1: string; + TypeParameter_0_of_public_method_from_exported_class_has_or_is_using_private_type_1: string; + TypeParameter_0_of_method_from_exported_interface_has_or_is_using_private_type_1: string; + TypeParameter_0_of_exported_function_has_or_is_using_private_type_1: string; + TypeParameter_0_of_constructor_signature_from_exported_interface_is_using_inaccessible_module_1: string; + TypeParameter_0_of_call_signature_from_exported_interface_is_using_inaccessible_module_1: string; + TypeParameter_0_of_public_static_method_from_exported_class_is_using_inaccessible_module_1: string; + TypeParameter_0_of_public_method_from_exported_class_is_using_inaccessible_module_1: string; + TypeParameter_0_of_method_from_exported_interface_is_using_inaccessible_module_1: string; + TypeParameter_0_of_exported_function_is_using_inaccessible_module_1: string; + TypeParameter_0_of_exported_class_has_or_is_using_private_type_1: string; + TypeParameter_0_of_exported_interface_has_or_is_using_private_type_1: string; + TypeParameter_0_of_exported_class_is_using_inaccessible_module_1: string; + TypeParameter_0_of_exported_interface_is_using_inaccessible_module_1: string; + Duplicate_identifier_i_Compiler_uses_i_to_initialize_rest_parameter: string; + Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters: string; + Type_of_conditional_0_must_be_identical_to_1_or_2: string; + Type_of_conditional_0_must_be_identical_to_1_2_or_3: string; + Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_an_external_module: string; + Constraint_of_a_type_parameter_cannot_reference_any_type_parameter_from_the_same_type_parameter_list: string; + Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: string; + Parameter_0_cannot_be_referenced_in_its_initializer: string; + Duplicate_string_index_signature: string; + Duplicate_number_index_signature: string; + All_declarations_of_an_interface_must_have_identical_type_parameters: string; + Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter: string; + Type_0_is_missing_property_1_from_type_2: string; + Types_of_property_0_of_types_1_and_2_are_incompatible: string; + Types_of_property_0_of_types_1_and_2_are_incompatible_NL_3: string; + Property_0_defined_as_private_in_type_1_is_defined_as_public_in_type_2: string; + Property_0_defined_as_public_in_type_1_is_defined_as_private_in_type_2: string; + Types_0_and_1_define_property_2_as_private: string; + Call_signatures_of_types_0_and_1_are_incompatible: string; + Call_signatures_of_types_0_and_1_are_incompatible_NL_2: string; + Type_0_requires_a_call_signature_but_type_1_lacks_one: string; + Construct_signatures_of_types_0_and_1_are_incompatible: string; + Construct_signatures_of_types_0_and_1_are_incompatible_NL_2: string; + Type_0_requires_a_construct_signature_but_type_1_lacks_one: string; + Index_signatures_of_types_0_and_1_are_incompatible: string; + Index_signatures_of_types_0_and_1_are_incompatible_NL_2: string; + Call_signature_expects_0_or_fewer_parameters: string; + Could_not_apply_type_0_to_argument_1_which_is_of_type_2: string; + Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function: string; + Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function: string; + Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor: string; + Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property: string; + Types_of_static_property_0_of_class_1_and_class_2_are_incompatible: string; + Types_of_static_property_0_of_class_1_and_class_2_are_incompatible_NL_3: string; + Type_reference_cannot_refer_to_container_0: string; + Type_reference_must_refer_to_type: string; + In_enums_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_the_first_enum_element: string; + _0_overload_s: string; + Variable_declaration_cannot_have_the_same_name_as_an_import_declaration: string; + Signature_expected_0_type_arguments_got_1_instead: string; + Property_0_defined_as_optional_in_type_1_but_is_required_in_type_2: string; + Types_0_and_1_originating_in_infinitely_expanding_type_reference_do_not_refer_to_same_named_type: string; + Types_0_and_1_originating_in_infinitely_expanding_type_reference_have_incompatible_type_arguments: string; + Types_0_and_1_originating_in_infinitely_expanding_type_reference_have_incompatible_type_arguments_NL_2: string; + Named_properties_0_of_types_1_and_2_are_not_identical: string; + Types_of_string_indexer_of_types_0_and_1_are_not_identical: string; + Types_of_number_indexer_of_types_0_and_1_are_not_identical: string; + Type_of_number_indexer_in_type_0_is_not_assignable_to_string_indexer_type_in_type_1_NL_2: string; + Type_of_property_0_in_type_1_is_not_assignable_to_string_indexer_type_in_type_2_NL_3: string; + Type_of_property_0_in_type_1_is_not_assignable_to_number_indexer_type_in_type_2_NL_3: string; + Static_property_0_defined_as_private_in_type_1_is_defined_as_public_in_type_2: string; + Static_property_0_defined_as_public_in_type_1_is_defined_as_private_in_type_2: string; + Types_0_and_1_define_static_property_2_as_private: string; + Current_host_does_not_support_0_option: string; + ECMAScript_target_version_0_not_supported_Specify_a_valid_target_version_1_default_or_2: string; + Module_code_generation_0_not_supported: string; + Could_not_find_file_0: string; + A_file_cannot_have_a_reference_to_itself: string; + Cannot_resolve_referenced_file_0: string; + Cannot_find_the_common_subdirectory_path_for_the_input_files: string; + Emit_Error_0: string; + Cannot_read_file_0_1: string; + Unsupported_file_encoding: string; + Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1: string; + Unsupported_locale_0: string; + Execution_Failed_NL: string; + Invalid_call_to_up: string; + Invalid_call_to_down: string; + Base64_value_0_finished_with_a_continuation_bit: string; + Unknown_option_0: string; + Expected_0_arguments_to_message_got_1_instead: string; + Expected_the_message_0_to_have_1_arguments_but_it_had_2: string; + Could_not_delete_file_0: string; + Could_not_create_directory_0: string; + Error_while_executing_file_0: string; + Cannot_compile_external_modules_unless_the_module_flag_is_provided: string; + Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option: string; + Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option: string; + Options_mapRoot_and_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option: string; + Option_0_specified_without_1: string; + codepage_option_not_supported_on_current_platform: string; + Concatenate_and_emit_output_to_single_file: string; + Generates_corresponding_0_file: string; + Specifies_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations: string; + Specifies_the_location_where_debugger_should_locate_TypeScript_files_instead_of_source_locations: string; + Watch_input_files: string; + Redirect_output_structure_to_the_directory: string; + Do_not_emit_comments_to_output: string; + Skip_resolution_and_preprocessing: string; + Specify_ECMAScript_target_version_0_default_or_1: string; + Specify_module_code_generation_0_or_1: string; + Print_this_message: string; + Print_the_compiler_s_version_0: string; + Allow_use_of_deprecated_0_keyword_when_referencing_an_external_module: string; + Specify_locale_for_errors_and_messages_For_example_0_or_1: string; + Syntax_0: string; + options: string; + file1: string; + Examples: string; + Options: string; + Insert_command_line_options_and_files_from_a_file: string; + Version_0: string; + Use_the_0_flag_to_see_options: string; + NL_Recompiling_0: string; + STRING: string; + KIND: string; + file2: string; + VERSION: string; + LOCATION: string; + DIRECTORY: string; + NUMBER: string; + Specify_the_codepage_to_use_when_opening_source_files: string; + Additional_locations: string; + This_version_of_the_Javascript_runtime_does_not_support_the_0_function: string; + Unknown_rule: string; + Invalid_line_number_0: string; + Warn_on_expressions_and_declarations_with_an_implied_any_type: string; + Variable_0_implicitly_has_an_any_type: string; + Parameter_0_of_1_implicitly_has_an_any_type: string; + Parameter_0_of_function_type_implicitly_has_an_any_type: string; + Member_0_of_object_type_implicitly_has_an_any_type: string; + new_expression_which_lacks_a_constructor_signature_implicitly_has_an_any_type: string; + _0_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: string; + Function_expression_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: string; + Parameter_0_of_lambda_function_implicitly_has_an_any_type: string; + Constructor_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: string; + Lambda_Function_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: string; + Array_Literal_implicitly_has_an_any_type_from_widening: string; + _0_which_lacks_get_accessor_and_parameter_type_annotation_on_set_accessor_implicitly_has_an_any_type: string; + Index_signature_of_object_type_implicitly_has_an_any_type: string; + Object_literal_s_property_0_implicitly_has_an_any_type_from_widening: string; + }; +} +declare module TypeScript { + enum DiagnosticCategory { + Warning = 0, + Error = 1, + Message = 2, + NoPrefix = 3, + } +} +declare module TypeScript { + var diagnosticInformationMap: IIndexable; +} +declare module TypeScript { + class ArrayUtilities { + static isArray(value: any): boolean; + static sequenceEquals(array1: T[], array2: T[], equals: (v1: T, v2: T) => boolean): boolean; + static contains(array: T[], value: T): boolean; + static groupBy(array: T[], func: (v: T) => string): any; + static distinct(array: T[], equalsFn?: (a: T, b: T) => boolean): T[]; + static min(array: T[], func: (v: T) => number): number; + static max(array: T[], func: (v: T) => number): number; + static last(array: T[]): T; + static lastOrDefault(array: T[], predicate: (v: T, index: number) => boolean): T; + static firstOrDefault(array: T[], func: (v: T, index: number) => boolean): T; + static first(array: T[], func?: (v: T, index: number) => boolean): T; + static sum(array: T[], func: (v: T) => number): number; + static select(values: T[], func: (v: T) => S): S[]; + static where(values: T[], func: (v: T) => boolean): T[]; + static any(array: T[], func: (v: T) => boolean): boolean; + static all(array: T[], func: (v: T) => boolean): boolean; + static binarySearch(array: number[], value: number): number; + static createArray(length: number, defaultValue: any): T[]; + static grow(array: T[], length: number, defaultValue: T): void; + static copy(sourceArray: T[], sourceIndex: number, destinationArray: T[], destinationIndex: number, length: number): void; + static indexOf(array: T[], predicate: (v: T) => boolean): number; + } +} +declare module TypeScript { + interface IBitVector { + valueAt(index: number): boolean; + setValueAt(index: number, value: boolean): void; + release(): void; + } + module BitVector { + function getBitVector(allowUndefinedValues: boolean): IBitVector; + } +} +declare module TypeScript { + interface IBitMatrix { + valueAt(x: number, y: number): boolean; + setValueAt(x: number, y: number, value: boolean): void; + release(): void; + } + module BitMatrix { + function getBitMatrix(allowUndefinedValues: boolean): IBitMatrix; + } +} +declare module TypeScript { + enum Constants { + Max31BitInteger = 1073741823, + Min31BitInteger = -1073741824, + } +} +declare module TypeScript { + enum AssertionLevel { + None = 0, + Normal = 1, + Aggressive = 2, + VeryAggressive = 3, + } + class Debug { + private static currentAssertionLevel; + static shouldAssert(level: AssertionLevel): boolean; + static assert(expression: any, message?: string, verboseDebugInfo?: () => string): void; + static fail(message?: string): void; + } +} +declare module TypeScript { + var LocalizedDiagnosticMessages: IIndexable; + class Location { + private _fileName; + private _lineMap; + private _start; + private _length; + constructor(fileName: string, lineMap: LineMap, start: number, length: number); + public fileName(): string; + public lineMap(): LineMap; + public line(): number; + public character(): number; + public start(): number; + public length(): number; + static equals(location1: Location, location2: Location): boolean; + } + class Diagnostic extends Location { + private _diagnosticKey; + private _arguments; + private _additionalLocations; + constructor(fileName: string, lineMap: LineMap, start: number, length: number, diagnosticKey: string, _arguments?: any[], additionalLocations?: Location[]); + public toJSON(key: any): any; + public diagnosticKey(): string; + public arguments(): any[]; + public text(): string; + public message(): string; + public additionalLocations(): Location[]; + static equals(diagnostic1: Diagnostic, diagnostic2: Diagnostic): boolean; + public info(): DiagnosticInfo; + } + function newLine(): string; + function getLocalizedText(diagnosticKey: string, args: any[]): string; + function getDiagnosticMessage(diagnosticKey: string, args: any[]): string; +} +declare module TypeScript { + interface DiagnosticInfo { + category: DiagnosticCategory; + message: string; + code: number; + } +} +declare module TypeScript { + class Errors { + static argument(argument: string, message?: string): Error; + static argumentOutOfRange(argument: string): Error; + static argumentNull(argument: string): Error; + static abstract(): Error; + static notYetImplemented(): Error; + static invalidOperation(message?: string): Error; + } +} +declare module TypeScript { + class Hash { + private static FNV_BASE; + private static FNV_PRIME; + private static computeFnv1aCharArrayHashCode(text, start, len); + static computeSimple31BitCharArrayHashCode(key: number[], start: number, len: number): number; + static computeSimple31BitStringHashCode(key: string): number; + static computeMurmur2StringHashCode(key: string, seed: number): number; + private static primes; + static getPrime(min: number): number; + static expandPrime(oldSize: number): number; + static combine(value: number, currentHash: number): number; + } +} +declare module TypeScript.Collections { + var DefaultHashTableCapacity: number; + class HashTable { + private hash; + private entries; + private count; + constructor(capacity: number, hash: (k: TKey) => number); + public set(key: TKey, value: TValue): void; + public add(key: TKey, value: TValue): void; + public containsKey(key: TKey): boolean; + public get(key: TKey): TValue; + private computeHashCode(key); + private addOrSet(key, value, throwOnExistingEntry); + private findEntry(key, hashCode); + private addEntry(key, value, hashCode); + private grow(); + } + function createHashTable(capacity?: number, hash?: (k: TKey) => number): HashTable; + function identityHashCode(value: any): number; +} +declare class Enumerator { + public atEnd(): boolean; + public moveNext(): boolean; + public item(): any; + constructor(o: any); +} +declare module process { + var argv: string[]; + var platform: string; + function on(event: string, handler: (arg: any) => void): void; + module stdout { + function write(str: string): any; + function on(event: string, action: () => void): void; + } + module stderr { + function write(str: string): any; + function on(event: string, action: () => void): void; + } + module mainModule { + var filename: string; + } + function exit(exitCode?: number): any; +} +declare var Buffer: new(str: string, encoding?: string) => any; +declare module TypeScript { + var nodeMakeDirectoryTime: number; + var nodeCreateBufferTime: number; + var nodeWriteFileSyncTime: number; + enum ByteOrderMark { + None = 0, + Utf8 = 1, + Utf16BigEndian = 2, + Utf16LittleEndian = 3, + } + class FileInformation { + public contents: string; + public byteOrderMark: ByteOrderMark; + constructor(contents: string, byteOrderMark: ByteOrderMark); + } + interface IEnvironment { + supportsCodePage(): boolean; + readFile(path: string, codepage: number): FileInformation; + writeFile(path: string, contents: string, writeByteOrderMark: boolean): void; + deleteFile(path: string): void; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + listFiles(path: string, re?: RegExp, options?: { + recursive?: boolean; + }): string[]; + arguments: string[]; + standardOut: ITextWriter; + currentDirectory(): string; + newLine: string; + } + var Environment: IEnvironment; +} +declare module TypeScript { + interface IIndexable { + [s: string]: T; + } +} +declare module TypeScript { + module IntegerUtilities { + function integerDivide(numerator: number, denominator: number): number; + function integerMultiplyLow32Bits(n1: number, n2: number): number; + function integerMultiplyHigh32Bits(n1: number, n2: number): number; + function isInteger(text: string): boolean; + function isHexInteger(text: string): boolean; + } +} +declare module TypeScript { + interface Iterator { + moveNext(): boolean; + current(): T; + } +} +declare module TypeScript { + interface ILineAndCharacter { + line: number; + character: number; + } +} +declare module TypeScript { + class LineMap { + private _computeLineStarts; + private length; + static empty: LineMap; + private _lineStarts; + constructor(_computeLineStarts: () => number[], length: number); + public toJSON(key: any): { + lineStarts: number[]; + length: number; + }; + public equals(other: LineMap): boolean; + public lineStarts(): number[]; + public lineCount(): number; + public getPosition(line: number, character: number): number; + public getLineNumberFromPosition(position: number): number; + public getLineStartPosition(lineNumber: number): number; + public fillLineAndCharacterFromPosition(position: number, lineAndCharacter: ILineAndCharacter): void; + public getLineAndCharacterFromPosition(position: number): LineAndCharacter; + } +} +declare module TypeScript { + class LineAndCharacter { + private _line; + private _character; + constructor(line: number, character: number); + public line(): number; + public character(): number; + } +} +declare module TypeScript { + class MathPrototype { + static max(a: number, b: number): number; + static min(a: number, b: number): number; + } +} +declare module TypeScript.Collections { + var DefaultStringTableCapacity: number; + class StringTable { + private entries; + private count; + constructor(capacity: number); + public addCharArray(key: number[], start: number, len: number): string; + private findCharArrayEntry(key, start, len, hashCode); + private addEntry(text, hashCode); + private grow(); + private static textCharArrayEquals(text, array, start, length); + } + var DefaultStringTable: StringTable; +} +declare module TypeScript { + class StringUtilities { + static isString(value: any): boolean; + static fromCharCodeArray(array: number[]): string; + static endsWith(string: string, value: string): boolean; + static startsWith(string: string, value: string): boolean; + static copyTo(source: string, sourceIndex: number, destination: number[], destinationIndex: number, count: number): void; + static repeat(value: string, count: number): string; + static stringEquals(val1: string, val2: string): boolean; + } +} +declare var global: any; +declare module TypeScript { + class Timer { + public startTime: number; + public time: number; + public start(): void; + public end(): void; + } +} +declare module TypeScript { + enum SyntaxKind { + None = 0, + List = 1, + SeparatedList = 2, + TriviaList = 3, + WhitespaceTrivia = 4, + NewLineTrivia = 5, + MultiLineCommentTrivia = 6, + SingleLineCommentTrivia = 7, + SkippedTokenTrivia = 8, + ErrorToken = 9, + EndOfFileToken = 10, + IdentifierName = 11, + RegularExpressionLiteral = 12, + NumericLiteral = 13, + StringLiteral = 14, + NoSubstitutionStringTemplateLiteral = 15, + StringTemplateHead = 16, + StringTemplateMiddle = 17, + StringTemplateTail = 18, + DollarSignOpenBraceToken = 19, + BreakKeyword = 20, + CaseKeyword = 21, + CatchKeyword = 22, + ContinueKeyword = 23, + DebuggerKeyword = 24, + DefaultKeyword = 25, + DeleteKeyword = 26, + DoKeyword = 27, + ElseKeyword = 28, + FalseKeyword = 29, + FinallyKeyword = 30, + ForKeyword = 31, + FunctionKeyword = 32, + IfKeyword = 33, + InKeyword = 34, + InstanceOfKeyword = 35, + NewKeyword = 36, + NullKeyword = 37, + ReturnKeyword = 38, + SwitchKeyword = 39, + ThisKeyword = 40, + ThrowKeyword = 41, + TrueKeyword = 42, + TryKeyword = 43, + TypeOfKeyword = 44, + VarKeyword = 45, + VoidKeyword = 46, + WhileKeyword = 47, + WithKeyword = 48, + ClassKeyword = 49, + ConstKeyword = 50, + EnumKeyword = 51, + ExportKeyword = 52, + ExtendsKeyword = 53, + ImportKeyword = 54, + SuperKeyword = 55, + ImplementsKeyword = 56, + InterfaceKeyword = 57, + LetKeyword = 58, + PackageKeyword = 59, + PrivateKeyword = 60, + ProtectedKeyword = 61, + PublicKeyword = 62, + StaticKeyword = 63, + YieldKeyword = 64, + AnyKeyword = 65, + BooleanKeyword = 66, + ConstructorKeyword = 67, + DeclareKeyword = 68, + GetKeyword = 69, + ModuleKeyword = 70, + RequireKeyword = 71, + NumberKeyword = 72, + SetKeyword = 73, + StringKeyword = 74, + OpenBraceToken = 75, + CloseBraceToken = 76, + OpenParenToken = 77, + CloseParenToken = 78, + OpenBracketToken = 79, + CloseBracketToken = 80, + DotToken = 81, + DotDotDotToken = 82, + SemicolonToken = 83, + CommaToken = 84, + LessThanToken = 85, + GreaterThanToken = 86, + LessThanEqualsToken = 87, + GreaterThanEqualsToken = 88, + EqualsEqualsToken = 89, + EqualsGreaterThanToken = 90, + ExclamationEqualsToken = 91, + EqualsEqualsEqualsToken = 92, + ExclamationEqualsEqualsToken = 93, + PlusToken = 94, + MinusToken = 95, + AsteriskToken = 96, + PercentToken = 97, + PlusPlusToken = 98, + MinusMinusToken = 99, + LessThanLessThanToken = 100, + GreaterThanGreaterThanToken = 101, + GreaterThanGreaterThanGreaterThanToken = 102, + AmpersandToken = 103, + BarToken = 104, + CaretToken = 105, + ExclamationToken = 106, + TildeToken = 107, + AmpersandAmpersandToken = 108, + BarBarToken = 109, + QuestionToken = 110, + ColonToken = 111, + EqualsToken = 112, + PlusEqualsToken = 113, + MinusEqualsToken = 114, + AsteriskEqualsToken = 115, + PercentEqualsToken = 116, + LessThanLessThanEqualsToken = 117, + GreaterThanGreaterThanEqualsToken = 118, + GreaterThanGreaterThanGreaterThanEqualsToken = 119, + AmpersandEqualsToken = 120, + BarEqualsToken = 121, + CaretEqualsToken = 122, + SlashToken = 123, + SlashEqualsToken = 124, + SourceUnit = 125, + QualifiedName = 126, + ObjectType = 127, + FunctionType = 128, + ArrayType = 129, + ConstructorType = 130, + GenericType = 131, + TypeQuery = 132, + InterfaceDeclaration = 133, + FunctionDeclaration = 134, + ModuleDeclaration = 135, + ClassDeclaration = 136, + EnumDeclaration = 137, + ImportDeclaration = 138, + ExportAssignment = 139, + MemberFunctionDeclaration = 140, + MemberVariableDeclaration = 141, + ConstructorDeclaration = 142, + IndexMemberDeclaration = 143, + GetAccessor = 144, + SetAccessor = 145, + PropertySignature = 146, + CallSignature = 147, + ConstructSignature = 148, + IndexSignature = 149, + MethodSignature = 150, + Block = 151, + IfStatement = 152, + VariableStatement = 153, + ExpressionStatement = 154, + ReturnStatement = 155, + SwitchStatement = 156, + BreakStatement = 157, + ContinueStatement = 158, + ForStatement = 159, + ForInStatement = 160, + EmptyStatement = 161, + ThrowStatement = 162, + WhileStatement = 163, + TryStatement = 164, + LabeledStatement = 165, + DoStatement = 166, + DebuggerStatement = 167, + WithStatement = 168, + PlusExpression = 169, + NegateExpression = 170, + BitwiseNotExpression = 171, + LogicalNotExpression = 172, + PreIncrementExpression = 173, + PreDecrementExpression = 174, + DeleteExpression = 175, + TypeOfExpression = 176, + VoidExpression = 177, + CommaExpression = 178, + AssignmentExpression = 179, + AddAssignmentExpression = 180, + SubtractAssignmentExpression = 181, + MultiplyAssignmentExpression = 182, + DivideAssignmentExpression = 183, + ModuloAssignmentExpression = 184, + AndAssignmentExpression = 185, + ExclusiveOrAssignmentExpression = 186, + OrAssignmentExpression = 187, + LeftShiftAssignmentExpression = 188, + SignedRightShiftAssignmentExpression = 189, + UnsignedRightShiftAssignmentExpression = 190, + ConditionalExpression = 191, + LogicalOrExpression = 192, + LogicalAndExpression = 193, + BitwiseOrExpression = 194, + BitwiseExclusiveOrExpression = 195, + BitwiseAndExpression = 196, + EqualsWithTypeConversionExpression = 197, + NotEqualsWithTypeConversionExpression = 198, + EqualsExpression = 199, + NotEqualsExpression = 200, + LessThanExpression = 201, + GreaterThanExpression = 202, + LessThanOrEqualExpression = 203, + GreaterThanOrEqualExpression = 204, + InstanceOfExpression = 205, + InExpression = 206, + LeftShiftExpression = 207, + SignedRightShiftExpression = 208, + UnsignedRightShiftExpression = 209, + MultiplyExpression = 210, + DivideExpression = 211, + ModuloExpression = 212, + AddExpression = 213, + SubtractExpression = 214, + PostIncrementExpression = 215, + PostDecrementExpression = 216, + MemberAccessExpression = 217, + InvocationExpression = 218, + ArrayLiteralExpression = 219, + ObjectLiteralExpression = 220, + ObjectCreationExpression = 221, + ParenthesizedExpression = 222, + ParenthesizedArrowFunctionExpression = 223, + SimpleArrowFunctionExpression = 224, + CastExpression = 225, + ElementAccessExpression = 226, + FunctionExpression = 227, + StringTemplateSubstitutionExpression = 228, + SubstitutionStringTemplate = 229, + NoSubstitutionStringTemplateInvocationExpression = 230, + SubstitutionStringTemplateInvocationExpression = 231, + OmittedExpression = 232, + VariableDeclaration = 233, + VariableDeclarator = 234, + ArgumentList = 235, + ParameterList = 236, + TypeArgumentList = 237, + TypeParameterList = 238, + ExtendsHeritageClause = 239, + ImplementsHeritageClause = 240, + EqualsValueClause = 241, + CaseSwitchClause = 242, + DefaultSwitchClause = 243, + ElseClause = 244, + CatchClause = 245, + FinallyClause = 246, + TypeParameter = 247, + Constraint = 248, + SimplePropertyAssignment = 249, + FunctionPropertyAssignment = 250, + Parameter = 251, + EnumElement = 252, + TypeAnnotation = 253, + ExternalModuleReference = 254, + ModuleNameModuleReference = 255, + Last = 255, + FirstStandardKeyword = 20, + LastStandardKeyword = 48, + FirstFutureReservedKeyword = 49, + LastFutureReservedKeyword = 55, + FirstFutureReservedStrictKeyword = 56, + LastFutureReservedStrictKeyword = 64, + FirstTypeScriptKeyword = 65, + LastTypeScriptKeyword = 74, + FirstKeyword = 20, + LastKeyword = 74, + FirstToken = 9, + LastToken = 124, + FirstPunctuation = 75, + LastPunctuation = 124, + FirstFixedWidth = 20, + LastFixedWidth = 124, + FirstTrivia = 4, + LastTrivia = 8, + } +} +declare module TypeScript.SyntaxFacts { + function getTokenKind(text: string): SyntaxKind; + function getText(kind: SyntaxKind): string; + function isTokenKind(kind: SyntaxKind): boolean; + function isAnyKeyword(kind: SyntaxKind): boolean; + function isStandardKeyword(kind: SyntaxKind): boolean; + function isFutureReservedKeyword(kind: SyntaxKind): boolean; + function isFutureReservedStrictKeyword(kind: SyntaxKind): boolean; + function isAnyPunctuation(kind: SyntaxKind): boolean; + function isPrefixUnaryExpressionOperatorToken(tokenKind: SyntaxKind): boolean; + function isBinaryExpressionOperatorToken(tokenKind: SyntaxKind): boolean; + function getPrefixUnaryExpressionFromOperatorToken(tokenKind: SyntaxKind): SyntaxKind; + function getPostfixUnaryExpressionFromOperatorToken(tokenKind: SyntaxKind): SyntaxKind; + function getBinaryExpressionFromOperatorToken(tokenKind: SyntaxKind): SyntaxKind; + function getOperatorTokenFromBinaryExpression(tokenKind: SyntaxKind): SyntaxKind; + function isAnyDivideToken(kind: SyntaxKind): boolean; + function isAnyDivideOrRegularExpressionToken(kind: SyntaxKind): boolean; +} +declare var argumentChecks: boolean; +declare var forPrettyPrinter: boolean; +interface ITypeDefinition { + name: string; + baseType: string; + interfaces?: string[]; + children: IMemberDefinition[]; + isTypeScriptSpecific: boolean; +} +interface IMemberDefinition { + name: string; + type?: string; + isToken?: boolean; + isList?: boolean; + isSeparatedList?: boolean; + requiresAtLeastOneItem?: boolean; + isOptional?: boolean; + tokenKinds?: string[]; + isTypeScriptSpecific: boolean; + elementType?: string; +} +declare var interfaces: TypeScript.IIndexable; +declare var definitions: ITypeDefinition[]; +declare function getStringWithoutSuffix(definition: string): string; +declare function getNameWithoutSuffix(definition: ITypeDefinition): string; +declare function getType(child: IMemberDefinition): string; +declare var hasKind: boolean; +declare function pascalCase(value: string): string; +declare function camelCase(value: string): string; +declare function getSafeName(child: IMemberDefinition): string; +declare function getPropertyAccess(child: IMemberDefinition): string; +declare function generateProperties(definition: ITypeDefinition): string; +declare function generateNullChecks(definition: ITypeDefinition): string; +declare function generateIfKindCheck(child: IMemberDefinition, tokenKinds: string[], indent: string): string; +declare function generateSwitchCase(tokenKind: string, indent: string): string; +declare function generateBreakStatement(indent: string): string; +declare function generateSwitchCases(tokenKinds: string[], indent: string): string; +declare function generateDefaultCase(child: IMemberDefinition, indent: string): string; +declare function generateSwitchKindCheck(child: IMemberDefinition, tokenKinds: string[], indent: string): string; +declare function tokenKinds(child: IMemberDefinition): string[]; +declare function generateKindCheck(child: IMemberDefinition): string; +declare function generateKindChecks(definition: ITypeDefinition): string; +declare function generateArgumentChecks(definition: ITypeDefinition): string; +declare function generateConstructor(definition: ITypeDefinition): string; +declare function isOptional(child: IMemberDefinition): boolean; +declare function generateFactory1Method(definition: ITypeDefinition): string; +declare function isKeywordOrPunctuation(kind: string): boolean; +declare function isDefaultConstructable(definition: ITypeDefinition): boolean; +declare function isMandatory(child: IMemberDefinition): boolean; +declare function generateFactory2Method(definition: ITypeDefinition): string; +declare function generateFactoryMethod(definition: ITypeDefinition): string; +declare function generateAcceptMethods(definition: ITypeDefinition): string; +declare function generateIsMethod(definition: ITypeDefinition): string; +declare function generateKindMethod(definition: ITypeDefinition): string; +declare function generateSlotMethods(definition: ITypeDefinition): string; +declare function generateFirstTokenMethod(definition: ITypeDefinition): string; +declare function generateLastTokenMethod(definition: ITypeDefinition): string; +declare function generateInsertChildrenIntoMethod(definition: ITypeDefinition): string; +declare function baseType(definition: ITypeDefinition): ITypeDefinition; +declare function memberDefinitionType(child: IMemberDefinition): ITypeDefinition; +declare function derivesFrom(def1: ITypeDefinition, def2: ITypeDefinition): boolean; +declare function contains(definition: ITypeDefinition, child: IMemberDefinition): boolean; +declare function generateAccessors(definition: ITypeDefinition): string; +declare function generateWithMethod(definition: ITypeDefinition, child: IMemberDefinition): string; +declare function generateWithMethods(definition: ITypeDefinition): string; +declare function generateTriviaMethods(definition: ITypeDefinition): string; +declare function generateUpdateMethod(definition: ITypeDefinition): string; +declare function generateIsTypeScriptSpecificMethod(definition: ITypeDefinition): string; +declare function couldBeRegularExpressionToken(child: IMemberDefinition): boolean; +declare function generateStructuralEqualsMethod(definition: ITypeDefinition): string; +declare function generateNode(definition: ITypeDefinition): string; +declare function generateNodes(): string; +declare function isInterface(name: string): boolean; +declare function isNodeOrToken(child: IMemberDefinition): boolean; +declare function generateRewriter(): string; +declare function generateToken(isFixedWidth: boolean, leading: boolean, trailing: boolean): string; +declare function generateTokens(): string; +declare function generateWalker(): string; +declare function firstEnumName(e: any, value: number): any; +declare function generateKeywordCondition(keywords: { + text: string; + kind: TypeScript.SyntaxKind; +}[], currentCharacter: number, indent: string): string; +declare function generateScannerUtilities(): string; +declare function generateVisitor(): string; +declare function generateFactory(): string; +declare var syntaxNodes: string; +declare var rewriter: string; +declare var tokens: string; +declare var walker: string; +declare var scannerUtilities: string; +declare var visitor: string; +declare var factory: string; diff --git a/src/services/syntax/characterInfo.ts b/src/services/syntax/characterInfo.ts new file mode 100644 index 00000000000..6ab4a91ddc8 --- /dev/null +++ b/src/services/syntax/characterInfo.ts @@ -0,0 +1,70 @@ +/// + +module TypeScript { + export module CharacterInfo { + export function isDecimalDigit(c: number): boolean { + return c >= CharacterCodes._0 && c <= CharacterCodes._9; + } + + export function isOctalDigit(c: number): boolean { + return c >= CharacterCodes._0 && c <= CharacterCodes._7; + } + + export function isHexDigit(c: number): boolean { + return CharacterInfo.isDecimalDigit(c) || + (c >= CharacterCodes.A && c <= CharacterCodes.F) || + (c >= CharacterCodes.a && c <= CharacterCodes.f); + } + + export function hexValue(c: number): number { + // Debug.assert(isHexDigit(c)); + return CharacterInfo.isDecimalDigit(c) + ? (c - CharacterCodes._0) + : (c >= CharacterCodes.A && c <= CharacterCodes.F) + ? c - CharacterCodes.A + 10 + : c - CharacterCodes.a + 10; + } + + export function isWhitespace(ch: number): boolean { + switch (ch) { + // Unicode 3.0 space characters. + case CharacterCodes.space: + case CharacterCodes.nonBreakingSpace: + case CharacterCodes.enQuad: + case CharacterCodes.emQuad: + case CharacterCodes.enSpace: + case CharacterCodes.emSpace: + case CharacterCodes.threePerEmSpace: + case CharacterCodes.fourPerEmSpace: + case CharacterCodes.sixPerEmSpace: + case CharacterCodes.figureSpace: + case CharacterCodes.punctuationSpace: + case CharacterCodes.thinSpace: + case CharacterCodes.hairSpace: + case CharacterCodes.zeroWidthSpace: + case CharacterCodes.narrowNoBreakSpace: + case CharacterCodes.ideographicSpace: + + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.byteOrderMark: + return true; + } + + return false; + } + + export function isLineTerminator(ch: number): boolean { + switch (ch) { + case CharacterCodes.carriageReturn: + case CharacterCodes.lineFeed: + case CharacterCodes.paragraphSeparator: + case CharacterCodes.lineSeparator: + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/constants.ts b/src/services/syntax/constants.ts new file mode 100644 index 00000000000..13b3d8ad754 --- /dev/null +++ b/src/services/syntax/constants.ts @@ -0,0 +1,26 @@ +/// + +module TypeScript { + export enum SyntaxConstants { + None = 0, + + // Masks that we use to place information about a node into a single int. The first bit tells + // us if we've computed the data for a node. + // + // The second bit tells us if the node is incrementally reusable if it does not + // containe any skipped tokens, zero width tokens, regex tokens in it ("/", "/=" or "/.../"), + // and contains no tokens that were parser generated. + // + // The next bit lets us know if the nodes was parsed in a strict context or node. A node can + // only be used by the incremental parser if it is parsed in the same strict context as before. + // last masks off the part of the int + // + // The width of the node is stored in the remainder of the int. This allows us up to 512MB + // for a node by using all 29 bits. However, in the common case, we'll use less than 29 bits + // for the width. Thus, the info will be stored in a single int in chakra. + NodeDataComputed = 0x00000001, // 0000 0000 0000 0000 0000 0000 0000 0001 + NodeIncrementallyUnusableMask = 0x00000002, // 0000 0000 0000 0000 0000 0000 0000 0010 + NodeParsedInStrictModeMask = 0x00000004, // 0000 0000 0000 0000 0000 0000 0000 0100 + NodeFullWidthShift = 3, // 1111 1111 1111 1111 1111 1111 1111 1000 + } +} \ No newline at end of file diff --git a/src/services/syntax/defaultSyntaxVisitor.generated.ts b/src/services/syntax/defaultSyntaxVisitor.generated.ts new file mode 100644 index 00000000000..041b72b9359 --- /dev/null +++ b/src/services/syntax/defaultSyntaxVisitor.generated.ts @@ -0,0 +1,353 @@ +/// + +module TypeScript { + export class SyntaxVisitor implements ISyntaxVisitor { + public defaultVisit(node: ISyntaxNodeOrToken): any { + return null; + } + + public visitToken(token: ISyntaxToken): any { + return this.defaultVisit(token); + } + + public visitSourceUnit(node: SourceUnitSyntax): any { + return this.defaultVisit(node); + } + + public visitQualifiedName(node: QualifiedNameSyntax): any { + return this.defaultVisit(node); + } + + public visitObjectType(node: ObjectTypeSyntax): any { + return this.defaultVisit(node); + } + + public visitFunctionType(node: FunctionTypeSyntax): any { + return this.defaultVisit(node); + } + + public visitArrayType(node: ArrayTypeSyntax): any { + return this.defaultVisit(node); + } + + public visitConstructorType(node: ConstructorTypeSyntax): any { + return this.defaultVisit(node); + } + + public visitGenericType(node: GenericTypeSyntax): any { + return this.defaultVisit(node); + } + + public visitTypeQuery(node: TypeQuerySyntax): any { + return this.defaultVisit(node); + } + + public visitInterfaceDeclaration(node: InterfaceDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitFunctionDeclaration(node: FunctionDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitModuleDeclaration(node: ModuleDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitClassDeclaration(node: ClassDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitEnumDeclaration(node: EnumDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitImportDeclaration(node: ImportDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitExportAssignment(node: ExportAssignmentSyntax): any { + return this.defaultVisit(node); + } + + public visitMemberFunctionDeclaration(node: MemberFunctionDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitMemberVariableDeclaration(node: MemberVariableDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitConstructorDeclaration(node: ConstructorDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitIndexMemberDeclaration(node: IndexMemberDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitGetAccessor(node: GetAccessorSyntax): any { + return this.defaultVisit(node); + } + + public visitSetAccessor(node: SetAccessorSyntax): any { + return this.defaultVisit(node); + } + + public visitPropertySignature(node: PropertySignatureSyntax): any { + return this.defaultVisit(node); + } + + public visitCallSignature(node: CallSignatureSyntax): any { + return this.defaultVisit(node); + } + + public visitConstructSignature(node: ConstructSignatureSyntax): any { + return this.defaultVisit(node); + } + + public visitIndexSignature(node: IndexSignatureSyntax): any { + return this.defaultVisit(node); + } + + public visitMethodSignature(node: MethodSignatureSyntax): any { + return this.defaultVisit(node); + } + + public visitBlock(node: BlockSyntax): any { + return this.defaultVisit(node); + } + + public visitIfStatement(node: IfStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitVariableStatement(node: VariableStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitExpressionStatement(node: ExpressionStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitReturnStatement(node: ReturnStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitSwitchStatement(node: SwitchStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitBreakStatement(node: BreakStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitContinueStatement(node: ContinueStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitForStatement(node: ForStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitForInStatement(node: ForInStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitEmptyStatement(node: EmptyStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitThrowStatement(node: ThrowStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitWhileStatement(node: WhileStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitTryStatement(node: TryStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitLabeledStatement(node: LabeledStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitDoStatement(node: DoStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitDebuggerStatement(node: DebuggerStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitWithStatement(node: WithStatementSyntax): any { + return this.defaultVisit(node); + } + + public visitPrefixUnaryExpression(node: PrefixUnaryExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitDeleteExpression(node: DeleteExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitTypeOfExpression(node: TypeOfExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitVoidExpression(node: VoidExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitConditionalExpression(node: ConditionalExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitBinaryExpression(node: BinaryExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitPostfixUnaryExpression(node: PostfixUnaryExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitMemberAccessExpression(node: MemberAccessExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitInvocationExpression(node: InvocationExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitArrayLiteralExpression(node: ArrayLiteralExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitObjectLiteralExpression(node: ObjectLiteralExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitObjectCreationExpression(node: ObjectCreationExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitParenthesizedExpression(node: ParenthesizedExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitParenthesizedArrowFunctionExpression(node: ParenthesizedArrowFunctionExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitSimpleArrowFunctionExpression(node: SimpleArrowFunctionExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitCastExpression(node: CastExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitElementAccessExpression(node: ElementAccessExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitFunctionExpression(node: FunctionExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitOmittedExpression(node: OmittedExpressionSyntax): any { + return this.defaultVisit(node); + } + + public visitVariableDeclaration(node: VariableDeclarationSyntax): any { + return this.defaultVisit(node); + } + + public visitVariableDeclarator(node: VariableDeclaratorSyntax): any { + return this.defaultVisit(node); + } + + public visitArgumentList(node: ArgumentListSyntax): any { + return this.defaultVisit(node); + } + + public visitParameterList(node: ParameterListSyntax): any { + return this.defaultVisit(node); + } + + public visitTypeArgumentList(node: TypeArgumentListSyntax): any { + return this.defaultVisit(node); + } + + public visitTypeParameterList(node: TypeParameterListSyntax): any { + return this.defaultVisit(node); + } + + public visitHeritageClause(node: HeritageClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitEqualsValueClause(node: EqualsValueClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitCaseSwitchClause(node: CaseSwitchClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitDefaultSwitchClause(node: DefaultSwitchClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitElseClause(node: ElseClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitCatchClause(node: CatchClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitFinallyClause(node: FinallyClauseSyntax): any { + return this.defaultVisit(node); + } + + public visitTypeParameter(node: TypeParameterSyntax): any { + return this.defaultVisit(node); + } + + public visitConstraint(node: ConstraintSyntax): any { + return this.defaultVisit(node); + } + + public visitSimplePropertyAssignment(node: SimplePropertyAssignmentSyntax): any { + return this.defaultVisit(node); + } + + public visitFunctionPropertyAssignment(node: FunctionPropertyAssignmentSyntax): any { + return this.defaultVisit(node); + } + + public visitParameter(node: ParameterSyntax): any { + return this.defaultVisit(node); + } + + public visitEnumElement(node: EnumElementSyntax): any { + return this.defaultVisit(node); + } + + public visitTypeAnnotation(node: TypeAnnotationSyntax): any { + return this.defaultVisit(node); + } + + public visitExternalModuleReference(node: ExternalModuleReferenceSyntax): any { + return this.defaultVisit(node); + } + + public visitModuleNameModuleReference(node: ModuleNameModuleReferenceSyntax): any { + return this.defaultVisit(node); + } + } +} \ No newline at end of file diff --git a/src/services/syntax/depthLimitedWalker.ts b/src/services/syntax/depthLimitedWalker.ts new file mode 100644 index 00000000000..79df1ebd836 --- /dev/null +++ b/src/services/syntax/depthLimitedWalker.ts @@ -0,0 +1,21 @@ +/// + +module TypeScript { + export class DepthLimitedWalker extends SyntaxWalker { + private _depth: number = 0; + private _maximumDepth: number = 0; + + constructor(maximumDepth: number) { + super(); + this._maximumDepth = maximumDepth; + } + + public visitNode(node: ISyntaxNode): void { + if (this._depth < this._maximumDepth) { + this._depth++; + super.visitNode(node); + this._depth--; + } + } + } +} \ No newline at end of file diff --git a/src/services/syntax/emitter.ts b/src/services/syntax/emitter.ts new file mode 100644 index 00000000000..bfcae7d5bb3 --- /dev/null +++ b/src/services/syntax/emitter.ts @@ -0,0 +1,1246 @@ +/// + +module TypeScript.Emitter1 { + function callSignature(parameter: ParameterSyntax): CallSignatureSyntax { + return CallSignatureSyntax.create1().withParameterList( + ParameterListSyntax.create1().withParameter(parameter)); + } + + // Class that makes sure we're not reusing tokens in a tree + class EnsureTokenUniquenessRewriter extends SyntaxRewriter { + private tokenTable = Collections.createHashTable(Collections.DefaultHashTableCapacity, Collections.identityHashCode); + + public visitToken(token: ISyntaxToken): ISyntaxToken { + if (this.tokenTable.containsKey(token)) { + // already saw this token. so clone it and return a new one. so that the tree stays + // unique/ + return token.clone(); + } + + this.tokenTable.add(token, token); + return token; + } + } + + class EmitterImpl extends SyntaxRewriter { + private space: ISyntaxTriviaList; + private newLine: ISyntaxTriviaList; + private factory: Syntax.IFactory = Syntax.normalModeFactory; + + // A copy of the syntax tree we keep around so that we can determine where tokens were + // before we started moving them around. + private syntaxTreeCopy: SyntaxTree; + + constructor(syntaxTree: SyntaxTree, private options: FormattingOptions) { + super(); + + this.options = options || FormattingOptions.defaultOptions; + + // TODO: use proper new line based on options. + this.space = Syntax.spaceTriviaList; + this.newLine = Syntax.triviaList([Syntax.carriageReturnLineFeedTrivia]); + + this.syntaxTreeCopy = Parser.parse(syntaxTree.fileName(), SimpleText.fromString(syntaxTree.sourceUnit().fullText()), syntaxTree.isDeclaration(), syntaxTree.parseOptions()); + } + + private columnForStartOfToken(token: ISyntaxToken): number { + return Indentation.columnForStartOfTokenAtPosition(this.syntaxTreeCopy, token.fullStart(), this.options); + } + + private columnForEndOfToken(token: ISyntaxToken): number { + return Indentation.columnForEndOfTokenAtPosition(this.syntaxTreeCopy, token.fullStart(), this.options); + } + + private indentationTrivia(column: number): ISyntaxTriviaList { + var triviaArray = column === 0 ? null : [Indentation.indentationTrivia(column, this.options)]; + return Syntax.triviaList(triviaArray); + } + + private indentationTriviaForStartOfNode(node: ISyntaxNodeOrToken): ISyntaxTriviaList { + var column = this.columnForStartOfToken(node.firstToken()); + return this.indentationTrivia(column); + } + + private changeIndentation(node: T, changeFirstToken: boolean, indentAmount: number): T { + if (indentAmount === 0) { + return node; + } + else if (indentAmount > 0) { + return SyntaxIndenter.indentNode(node, + /*indentFirstToken:*/ changeFirstToken, /*indentAmount:*/ indentAmount, + this.options); + } + else { + // Dedent the node. But don't allow it go before the minimum indent amount. + return SyntaxDedenter.dedentNode(node, + /*dedentFirstToken:*/ changeFirstToken, /*dedentAmount:*/-indentAmount, + /*minimumColumn:*/this.options.indentSpaces, this.options); + } + } + + private withNoTrivia(token: ISyntaxToken): ISyntaxToken { + return token.withLeadingTrivia(Syntax.emptyTriviaList).withTrailingTrivia(Syntax.emptyTriviaList); + } + + public visitSourceUnit(node: SourceUnitSyntax): SourceUnitSyntax { + return node.withModuleElements(Syntax.list( + this.convertModuleElements(node.moduleElements))); + } + + private convertModuleElements(list: ISyntaxList): IModuleElementSyntax[] { + var moduleElements: IModuleElementSyntax[] = []; + + for (var i = 0, n = list.childCount(); i < n; i++) { + var moduleElement = list.childAt(i); + + var converted = this.visitNode(moduleElement); + if (converted !== null) { + if (ArrayUtilities.isArray(converted)) { + moduleElements.push.apply(moduleElements, converted); + } + else { + moduleElements.push(converted); + } + } + } + + return moduleElements; + } + + private static splitModuleName(name: INameSyntax): ISyntaxToken[] { + var result: ISyntaxToken[] = []; + while (true) { + if (name.kind() === SyntaxKind.IdentifierName) { + result.unshift(name); + return result; + } + else if (name.kind() === SyntaxKind.QualifiedName) { + var qualifiedName = name; + result.unshift(qualifiedName.right); + name = qualifiedName.left; + } + else { + throw Errors.invalidOperation(); + } + } + } + + private leftmostName(name: INameSyntax): ISyntaxToken { + while (name.kind() === SyntaxKind.QualifiedName) { + name = (name).left; + } + + return name; + } + + private rightmostName(name: INameSyntax): ISyntaxToken { + if (name.kind() === SyntaxKind.QualifiedName) { + return (name).right; + } + + return name; + } + + private containsToken(list: ISyntaxList, kind: SyntaxKind): boolean { + for (var i = 0, n = list.childCount(); i < n; i++) { + if (list.childAt(i).kind() === kind) { + return true; + } + } + + return false; + } + + private exportModuleElement(moduleIdentifier: ISyntaxToken, + moduleElement: IModuleElementSyntax, + elementIdentifier: ISyntaxToken): ExpressionStatementSyntax { + elementIdentifier = this.withNoTrivia(elementIdentifier); + + // M1.e = e; + return ExpressionStatementSyntax.create1( + this.factory.binaryExpression( + SyntaxKind.AssignmentExpression, + MemberAccessExpressionSyntax.create1( + this.withNoTrivia(moduleIdentifier), + elementIdentifier.withTrailingTrivia(Syntax.spaceTriviaList)), + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + elementIdentifier)) + .withLeadingTrivia(this.indentationTriviaForStartOfNode(moduleElement)) + .withTrailingTrivia(this.newLine); + } + + private handleExportedModuleElement(parentModule: ISyntaxToken, + moduleElement: IModuleElementSyntax, + elements: IModuleElementSyntax[]): void { + if (moduleElement.kind() === SyntaxKind.VariableStatement) { + var variableStatement = moduleElement; + if (this.containsToken(variableStatement.modifiers, SyntaxKind.ExportKeyword)) { + var declarators = variableStatement.variableDeclaration.variableDeclarators; + for (var i = 0, n = declarators.nonSeparatorCount(); i < n; i++) { + var declarator = declarators.nonSeparatorAt(i); + elements.push(this.exportModuleElement(parentModule, moduleElement, declarator.propertyName)); + } + } + } + else if (moduleElement.kind() === SyntaxKind.FunctionDeclaration) { + var functionDeclaration = moduleElement; + if (this.containsToken(functionDeclaration.modifiers, SyntaxKind.ExportKeyword)) { + elements.push(this.exportModuleElement( + parentModule, moduleElement, functionDeclaration.identifier)); + } + } + else if (moduleElement.kind() === SyntaxKind.ClassDeclaration) { + var classDeclaration = moduleElement; + if (this.containsToken(classDeclaration.modifiers, SyntaxKind.ExportKeyword)) { + elements.push(this.exportModuleElement(parentModule, moduleElement, classDeclaration.identifier)); + } + } + else if (moduleElement.kind() === SyntaxKind.ModuleDeclaration) { + var childModule = moduleElement; + if (this.containsToken(childModule.modifiers, SyntaxKind.ExportKeyword)) { + elements.push(this.exportModuleElement( + parentModule, moduleElement, this.leftmostName(childModule.name))); + } + } + } + + public visitModuleDeclaration(node: ModuleDeclarationSyntax): IModuleElementSyntax[] { + var _this = this; + + // Recurse downwards and get the rewritten children. + var moduleElements = this.convertModuleElements(node.moduleElements); + + if (this.mustCaptureThisInModule(node)) { + // TODO: determine the right column for the 'this capture' statment. + moduleElements.unshift(this.generateThisCaptureStatement(0)); + } + + // Handle the case where the child is an export. + var parentModule = this.rightmostName(node.name); + for (var i = 0, n = node.moduleElements.childCount(); i < n; i++) { + this.handleExportedModuleElement( + parentModule, node.moduleElements.childAt(i), moduleElements); + } + + // Break up the dotted name into pieces. + var names = EmitterImpl.splitModuleName(node.name); + + // Then, for all the names left of that name, wrap what we've created in a larger module. + for (var nameIndex = names.length - 1; nameIndex >= 0; nameIndex--) { + moduleElements = this.convertModuleDeclaration( + node, names[nameIndex], moduleElements, nameIndex === 0); + + if (nameIndex > 0) { + // We're popping out and generate each outer module. As we do so, we have to + // indent whatever we've created so far appropriately. + moduleElements.push(this.exportModuleElement( + names[nameIndex - 1], node, names[nameIndex])); + + moduleElements = ArrayUtilities.select(moduleElements, + e => _this.changeIndentation(e, /*indentFirstToken:*/ true, _this.options.indentSpaces)); + } + } + + return moduleElements; + } + + private initializedVariable(name: ISyntaxToken): BinaryExpressionSyntax { + return this.factory.binaryExpression(SyntaxKind.LogicalOrExpression, + name, + Syntax.token(SyntaxKind.BarBarToken), + ParenthesizedExpressionSyntax.create1( + Syntax.assignmentExpression( + name, + Syntax.token(SyntaxKind.EqualsToken), + ObjectLiteralExpressionSyntax.create1()))); + } + + private convertModuleDeclaration(moduleDeclaration: ModuleDeclarationSyntax, + moduleName: ISyntaxToken, + moduleElements: IModuleElementSyntax[], + outermost: boolean): IModuleElementSyntax[] { + moduleName = moduleName.withLeadingTrivia(Syntax.emptyTriviaList).withTrailingTrivia(Syntax.emptyTriviaList); + var moduleIdentifier = moduleName; + + var moduleIndentation = this.indentationTriviaForStartOfNode(moduleDeclaration); + var leadingTrivia = outermost ? moduleDeclaration.leadingTrivia() : moduleIndentation; + + // var M; + var variableStatement = VariableStatementSyntax.create1(this.factory.variableDeclaration( + Syntax.token(SyntaxKind.VarKeyword).withTrailingTrivia(this.space), + Syntax.separatedList( + [VariableDeclaratorSyntax.create(moduleIdentifier)]))) + .withLeadingTrivia(leadingTrivia).withTrailingTrivia(this.newLine); + + // function(M) { ... } + var functionExpression = FunctionExpressionSyntax.create1() + .withCallSignature(callSignature(ParameterSyntax.create(moduleIdentifier)).withTrailingTrivia(this.space)) + .withBlock(this.factory.block( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.newLine), + Syntax.list(moduleElements), + Syntax.token(SyntaxKind.CloseBraceToken).withLeadingTrivia(moduleIndentation))); + + // (function(M) { ... })(M||(M={})); + var expressionStatement = ExpressionStatementSyntax.create1( + this.factory.invocationExpression( + ParenthesizedExpressionSyntax.create1(functionExpression), + ArgumentListSyntax.create1().withArgument(this.initializedVariable(moduleName)))) + .withLeadingTrivia(moduleIndentation).withTrailingTrivia(this.newLine); + + return [variableStatement, expressionStatement]; + } + + public visitExpressionStatement(node: ExpressionStatementSyntax): ExpressionStatementSyntax { + // Can't have an expression statement with an anonymous function expression in it. + var rewritten: ExpressionStatementSyntax = super.visitExpressionStatement(node); + + // convert: function() { ... }; to (function() { ... }); + if (rewritten.expression.kind() === SyntaxKind.FunctionExpression) { + // Wasn a function expression + var functionExpression = rewritten.expression; + if (functionExpression.identifier === null) { + // Was anonymous. + + // Remove the leading trivia from the function keyword. We'll put it on the open paren + // token instead. + + // Now, wrap the function expression in parens to make it legal in javascript. + var parenthesizedExpression = ParenthesizedExpressionSyntax.create1( + functionExpression.withLeadingTrivia(Syntax.emptyTriviaList)).withLeadingTrivia(functionExpression.leadingTrivia()); + + return rewritten.withExpression(parenthesizedExpression); + } + } + + return rewritten; + } + + public visitSimpleArrowFunctionExpression(node: SimpleArrowFunctionExpressionSyntax): FunctionExpressionSyntax { + return FunctionExpressionSyntax.create1() + .withCallSignature(callSignature(ParameterSyntax.create(this.withNoTrivia(node.identifier))).withTrailingTrivia(this.space)) + .withBlock(this.convertArrowFunctionBody(node)).withLeadingTrivia(node.leadingTrivia()); + } + + public visitParenthesizedArrowFunctionExpression(node: ParenthesizedArrowFunctionExpressionSyntax): FunctionExpressionSyntax { + return FunctionExpressionSyntax.create1() + .withCallSignature(CallSignatureSyntax.create(node.callSignature.parameterList.accept(this))) + .withBlock(this.convertArrowFunctionBody(node)).withLeadingTrivia(node.leadingTrivia()); + } + + private convertArrowFunctionBody(arrowFunction: IArrowFunctionExpressionSyntax): BlockSyntax { + var rewrittenBody = arrowFunction.block ? this.visitNode(arrowFunction.block) : this.visitNodeOrToken(arrowFunction.expression); + + if (rewrittenBody.kind() === SyntaxKind.Block) { + return rewrittenBody; + } + + var arrowToken = arrowFunction.equalsGreaterThanToken; + + // first, attach the expression to the return statement + var returnStatement = this.factory.returnStatement( + Syntax.token(SyntaxKind.ReturnKeyword, { trailingTrivia: arrowToken.trailingTrivia().toArray() }), + rewrittenBody, + Syntax.token(SyntaxKind.SemicolonToken)).withTrailingTrivia(this.newLine); + + // We want to adjust the indentation of the expression so that is aligns as it + // did before. For example, if we started with: + // + // a => foo().bar() + // .baz() + // + // Then we want to end up with: + // + // return foo().bar() + // .baz() + // + // To do this we look at where the previous token (=>) used to end and where the new pevious + // token (return) ends. The difference (in this case '2') is our offset. + + var difference = 0; + if (arrowToken.hasTrailingNewLine()) { + // The expression is on the next line. i.e. + // + // foo => + // expr + // + // So we want it to immediately follow the return statement. i.e.: + // + // return + // expr; + // + // and we adjust based on the column difference between the start of the arrow function + // and the start of the expr. + var arrowFunctionStart = this.columnForStartOfToken(arrowFunction.firstToken()); + difference = -arrowFunctionStart; + } + else { + // the expression immediately follows the arrow. i.e.: + // + // foo => expr + // + // So we want it to immediately follow the return statement. i.e.: + // + // return expr; + // + // and we adjust based on the column difference between the end of the arrow token and + // the end of the return statement. + var arrowEndColumn = this.columnForEndOfToken(arrowToken); + var returnKeywordEndColumn = returnStatement.returnKeyword.width(); + difference = returnKeywordEndColumn - arrowEndColumn; + } + + returnStatement = this.changeIndentation( + returnStatement, /*changeFirstToken:*/ false, difference); + + // Next, indent the return statement. It's going in a block, so it needs to be properly + // indented. Note we do this *after* we've ensured the expression aligns properly. + + returnStatement = this.changeIndentation( + returnStatement, /*indentFirstToken:*/ true, this.options.indentSpaces); + + // Now wrap the return statement in a block. + var block = this.factory.block( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.newLine), + Syntax.list([returnStatement]), + Syntax.token(SyntaxKind.CloseBraceToken)); + + // Note: if we started with something like: + // + // var v = a => 1; + // + // Then we want to convert that to: + // + // var v = function(a) { + // return 1; + // }; + // + // However, right now what we've created is: + // + // { + // return 1; + // } + // + // So we need to indent the block with our current column indent so that it aligns with the + // parent structure. Note: we don't wan to adjust the leading brace as that's going to go + // after the function sigature. + + return this.changeIndentation(block, /*indentFirstToken:*/ false, + Indentation.columnForStartOfFirstTokenInLineContainingPosition( + this.syntaxTreeCopy, arrowFunction.firstToken().fullStart(), this.options)); + } + + private static methodSignatureDefaultParameters(signature: MethodSignatureSyntax): ParameterSyntax[] { + return EmitterImpl.callSignatureDefaultParameters(signature.callSignature); + } + + private static callSignatureDefaultParameters(callSignature: CallSignatureSyntax): ParameterSyntax[] { + return EmitterImpl.parameterListDefaultParameters(callSignature.parameterList); + } + + private static parameterListDefaultParameters(parameterList: ParameterListSyntax): ParameterSyntax[] { + return ArrayUtilities.where(parameterList.parameters.toNonSeparatorArray(), p => p.equalsValueClause !== null); + } + + private generatePropertyAssignmentStatement(parameter: ParameterSyntax): ExpressionStatementSyntax { + var identifier = this.withNoTrivia(parameter.identifier); + + // this.foo = foo; + return ExpressionStatementSyntax.create1( + Syntax.assignmentExpression( + MemberAccessExpressionSyntax.create1( + Syntax.token(SyntaxKind.ThisKeyword), + identifier.withTrailingTrivia(Syntax.spaceTriviaList)), + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + identifier)).withTrailingTrivia(this.newLine); + } + + private generateDefaultValueAssignmentStatement(parameter: ParameterSyntax): IfStatementSyntax { + var identifierName = this.withNoTrivia(parameter.identifier).withTrailingTrivia(this.space); + + // typeof foo === 'undefined' + var condition = this.factory.binaryExpression( + SyntaxKind.EqualsExpression, + this.factory.typeOfExpression( + Syntax.token(SyntaxKind.TypeOfKeyword).withTrailingTrivia(this.space), + identifierName), + Syntax.token(SyntaxKind.EqualsEqualsEqualsToken).withTrailingTrivia(this.space), + Syntax.stringLiteralExpression('"undefined"')); + + // foo = expr; + var assignmentStatement = ExpressionStatementSyntax.create1( + Syntax.assignmentExpression( + identifierName, + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + parameter.equalsValueClause.value.accept(this))).withTrailingTrivia(this.space); + + var block = this.factory.block( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.space), + Syntax.list([assignmentStatement]), + Syntax.token(SyntaxKind.CloseBraceToken)).withTrailingTrivia(this.newLine); + + // if (typeof foo === 'undefined') { foo = expr; } + return this.factory.ifStatement( + Syntax.token(SyntaxKind.IfKeyword).withTrailingTrivia(this.space), + Syntax.token(SyntaxKind.OpenParenToken), + condition, + Syntax.token(SyntaxKind.CloseParenToken).withTrailingTrivia(this.space), + block, null); + } + + public visitFunctionDeclaration(node: FunctionDeclarationSyntax): FunctionDeclarationSyntax { + if (node.block === null) { + // Function overloads aren't emitted. + return null; + } + + var rewritten = super.visitFunctionDeclaration(node); + var parametersWithDefaults = EmitterImpl.callSignatureDefaultParameters(node.callSignature); + + if (parametersWithDefaults.length !== 0) { + var defaultValueAssignmentStatements = ArrayUtilities.select( + parametersWithDefaults, p => this.generateDefaultValueAssignmentStatement(p)); + + var statementColumn = this.columnForStartOfToken(node.firstToken()) + this.options.indentSpaces; + var statements = ArrayUtilities.select(defaultValueAssignmentStatements, + s => this.changeIndentation(s, /*indentFirstToken:*/ true, statementColumn)); + + // Capture _this if necessary + if (this.mustCaptureThisInFunction(node)) { + statements.push(this.generateThisCaptureStatement(statementColumn)); + } + + statements.push.apply(statements, rewritten.block.statements.toArray()); + + rewritten = rewritten.withBlock(rewritten.block.withStatements( + Syntax.list(statements))); + } + + return rewritten.withModifiers(Syntax.emptyList()) + .withLeadingTrivia(rewritten.leadingTrivia()); + } + + public visitParameter(node: ParameterSyntax): ParameterSyntax { + // transfer the trivia from the first token to the the identifier. + return ParameterSyntax.create(node.identifier) + .withLeadingTrivia(node.leadingTrivia()) + .withTrailingTrivia(node.trailingTrivia()) + } + + private generatePropertyAssignment(classDeclaration: ClassDeclarationSyntax, + static: boolean, + memberDeclaration: MemberVariableDeclarationSyntax): ExpressionStatementSyntax { + var isStatic = this.containsToken(memberDeclaration.modifiers, SyntaxKind.StaticKeyword); + var declarator = memberDeclaration.variableDeclarator; + if (static !== isStatic || declarator.equalsValueClause === null) { + return null; + } + + // this.foo = expr; + var receiver = MemberAccessExpressionSyntax.create1( + static ? this.withNoTrivia(classDeclaration.identifier) + : Syntax.token(SyntaxKind.ThisKeyword), + this.withNoTrivia(declarator.propertyName)).withTrailingTrivia(Syntax.spaceTriviaList); + + return ExpressionStatementSyntax.create1( + Syntax.assignmentExpression( + receiver, + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + declarator.equalsValueClause.value.accept(this).withTrailingTrivia(Syntax.emptyTriviaList))) + .withLeadingTrivia(memberDeclaration.leadingTrivia()).withTrailingTrivia(this.newLine); + } + + private generatePropertyAssignments(classDeclaration: ClassDeclarationSyntax, + static: boolean): ExpressionStatementSyntax[] { + var result: ExpressionStatementSyntax[] = []; + + // TODO: handle alignment here. + for (var i = 0, n = classDeclaration.classElements.childCount(); i < n; i++) { + var classElement = classDeclaration.classElements.childAt(i); + + if (classElement.kind() === SyntaxKind.MemberVariableDeclaration) { + var statement = this.generatePropertyAssignment( + classDeclaration, static, classElement); + if (statement !== null) { + result.push(statement); + } + } + } + + return result; + } + + private createDefaultConstructorDeclaration(classDeclaration: ClassDeclarationSyntax): FunctionDeclarationSyntax { + var classIndentationColumn = this.columnForStartOfToken(classDeclaration.firstToken()); + var statementIndentationColumn = classIndentationColumn + this.options.indentSpaces; + + var statements: IStatementSyntax[] = []; + if (classDeclaration.heritageClauses.childCount() > 0) { + statements.push(ExpressionStatementSyntax.create1( + this.factory.invocationExpression( + MemberAccessExpressionSyntax.create1( + Syntax.identifierName("_super"), Syntax.identifierName("apply")), + ArgumentListSyntax.create1().withArguments( + Syntax.separatedList([ + Syntax.token(SyntaxKind.ThisKeyword), + Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.space), + Syntax.identifierName("arguments")]))) + ).withLeadingTrivia(this.indentationTrivia(statementIndentationColumn)) + .withTrailingTrivia(this.newLine)); + } + + if (this.mustCaptureThisInClass(classDeclaration)) { + statements.push(this.generateThisCaptureStatement(statementIndentationColumn)); + } + + statements.push.apply(statements, this.generatePropertyAssignments(classDeclaration, /*static:*/ false)); + + var indentationTrivia = this.indentationTrivia(classIndentationColumn); + + var functionDeclaration = FunctionDeclarationSyntax.create( + Syntax.token(SyntaxKind.FunctionKeyword).withLeadingTrivia(indentationTrivia).withTrailingTrivia(this.space), + this.withNoTrivia(classDeclaration.identifier), + CallSignatureSyntax.create1().withTrailingTrivia(this.space)) + .withBlock(this.factory.block( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.newLine), + Syntax.list(statements), + Syntax.token(SyntaxKind.CloseBraceToken).withLeadingTrivia(indentationTrivia))).withTrailingTrivia(this.newLine); + + return this.changeIndentation( + functionDeclaration, /*indentFirstToken:*/ true, this.options.indentSpaces); + } + + private convertConstructorDeclaration(classDeclaration: ClassDeclarationSyntax, + constructorDeclaration: ConstructorDeclarationSyntax): FunctionDeclarationSyntax { + if (constructorDeclaration.block === null) { + return null; + } + + var i: number; + var identifier = this.withNoTrivia(classDeclaration.identifier); + + var constructorIndentationColumn = this.columnForStartOfToken(constructorDeclaration.firstToken()); + var originalParameterListindentation = this.columnForStartOfToken(constructorDeclaration.callSignature.firstToken()); + + // The original indent + "function" + + "ClassName" + var newParameterListIndentation = + constructorIndentationColumn + SyntaxFacts.getText(SyntaxKind.FunctionKeyword).length + 1 + identifier.width(); + + var callSignature = constructorDeclaration.callSignature.accept(this); + callSignature= this.changeIndentation( + callSignature, /*changeFirstToken:*/ false, newParameterListIndentation - originalParameterListindentation); + + var block = constructorDeclaration.block; + var allStatements = block.statements.toArray(); + + var normalStatements: IStatementSyntax[] = ArrayUtilities.select(ArrayUtilities.where(allStatements, + s => !Syntax.isSuperInvocationExpressionStatement(s)), s => s.accept(this)); + + var instanceAssignments = this.generatePropertyAssignments(classDeclaration, /*static:*/ false); + + for (i = instanceAssignments.length - 1; i >= 0; i--) { + normalStatements.unshift(this.changeIndentation( + instanceAssignments[i], /*changeFirstToken:*/ true, this.options.indentSpaces)); + } + + var parameterPropertyAssignments = ArrayUtilities.select( + ArrayUtilities.where(constructorDeclaration.callSignature.parameterList.parameters.toNonSeparatorArray(), p => p.modifiers.childCount() > 0), + p => this.generatePropertyAssignmentStatement(p)); + + for (i = parameterPropertyAssignments.length - 1; i >= 0; i--) { + normalStatements.unshift(this.changeIndentation( + parameterPropertyAssignments[i], /*changeFirstToken:*/ true, this.options.indentSpaces + constructorIndentationColumn)); + } + + var superStatements: IStatementSyntax[] = ArrayUtilities.select(ArrayUtilities.where(allStatements, + s => Syntax.isSuperInvocationExpressionStatement(s)), s => s.accept(this)); + + normalStatements.unshift.apply(normalStatements, superStatements); + + // TODO: use typecheck to determine if 'this' needs to be captured. + if (this.mustCaptureThisInConstructor(constructorDeclaration)) { + normalStatements.unshift(this.generateThisCaptureStatement(this.options.indentSpaces + constructorIndentationColumn)); + } + + var defaultValueAssignments = ArrayUtilities.select( + EmitterImpl.parameterListDefaultParameters(constructorDeclaration.callSignature.parameterList), + p => this.generateDefaultValueAssignmentStatement(p)); + + for (i = defaultValueAssignments.length - 1; i >= 0; i--) { + normalStatements.unshift(this.changeIndentation( + defaultValueAssignments[i], /*changeFirstToken:*/ true, this.options.indentSpaces + constructorIndentationColumn)); + } + + // function C(...) { ... } + return FunctionDeclarationSyntax.create( + Syntax.token(SyntaxKind.FunctionKeyword).withTrailingTrivia(this.space), + identifier, callSignature) + .withBlock(block.withStatements(Syntax.list(normalStatements))).withLeadingTrivia(constructorDeclaration.leadingTrivia()); + } + + private convertMemberFunctionDeclaration(classDeclaration: ClassDeclarationSyntax, + functionDeclaration: MemberFunctionDeclarationSyntax): ExpressionStatementSyntax { + var _this = this; + if (functionDeclaration.block === null) { + return null; + } + + var classIdentifier = this.withNoTrivia(classDeclaration.identifier); + var functionIdentifier = this.withNoTrivia(functionDeclaration.propertyName); + + var receiver: ILeftHandSideExpressionSyntax = classIdentifier.withLeadingTrivia(functionDeclaration.leadingTrivia()); + + receiver = this.containsToken(functionDeclaration.modifiers, SyntaxKind.StaticKeyword) + ? receiver + : MemberAccessExpressionSyntax.create1(receiver, Syntax.identifierName("prototype")); + + receiver = MemberAccessExpressionSyntax.create1( + receiver, functionIdentifier.withTrailingTrivia(Syntax.spaceTriviaList)); + + var block: BlockSyntax = functionDeclaration.block.accept(this); + var blockTrailingTrivia = block.trailingTrivia(); + + block = block.withTrailingTrivia(Syntax.emptyTriviaList); + + var defaultValueAssignments = ArrayUtilities.select( + EmitterImpl.callSignatureDefaultParameters(functionDeclaration.callSignature), + p => _this.generateDefaultValueAssignmentStatement(p)); + + var functionColumn = this.columnForStartOfToken(functionDeclaration.firstToken()); + + var blockStatements = block.statements.toArray(); + for (var i = defaultValueAssignments.length - 1; i >= 0; i--) { + blockStatements.unshift(this.changeIndentation( + defaultValueAssignments[i], /*changeFirstToken:*/ true, functionColumn + this.options.indentSpaces)); + } + + var callSignatureParameterList = functionDeclaration.callSignature.parameterList.accept(this); + if (!callSignatureParameterList.hasTrailingTrivia()) { + callSignatureParameterList = callSignatureParameterList.withTrailingTrivia(Syntax.spaceTriviaList); + } + + // C.prototype.f = function (p1, p2) { ... }; + return ExpressionStatementSyntax.create1(Syntax.assignmentExpression( + receiver, + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + FunctionExpressionSyntax.create1() + .withCallSignature(CallSignatureSyntax.create(callSignatureParameterList)) + .withBlock(block.withStatements( + Syntax.list(blockStatements))))).withTrailingTrivia(blockTrailingTrivia); + } + + private convertMemberAccessor(memberAccessor: SyntaxNode): SimplePropertyAssignmentSyntax { + var propertyName = memberAccessor.kind() === SyntaxKind.GetAccessor + ? "get" : "set"; + + var parameterList = (memberAccessor).parameterList.accept(this); + if (!parameterList.hasTrailingTrivia()) { + parameterList = parameterList.withTrailingTrivia(Syntax.spaceTriviaList); + } + + return this.factory.simplePropertyAssignment( + Syntax.identifier(propertyName), + Syntax.token(SyntaxKind.ColonToken).withTrailingTrivia(this.space), + FunctionExpressionSyntax.create( + Syntax.token(SyntaxKind.FunctionKeyword), + CallSignatureSyntax.create(parameterList), + (memberAccessor).block.accept(this).withTrailingTrivia(Syntax.emptyTriviaList))) + .withLeadingTrivia(this.indentationTriviaForStartOfNode(memberAccessor)); + } + + private convertMemberAccessorDeclaration(classDeclaration: ClassDeclarationSyntax, + memberAccessor: SyntaxNode, + classElements: IClassElementSyntax[]): IStatementSyntax { + var name = (memberAccessor).propertyName.valueText(); + var i: number; + + // Find all the accessors with that name. + var accessors: any[] = [memberAccessor]; + + for (i = classElements.length - 1; i >= 0; i--) { + var element = classElements[i]; + if (element.kind() === SyntaxKind.GetAccessor || + element.kind() === SyntaxKind.SetAccessor) { + + var otherAccessor = element; + if ((otherAccessor).propertyName.value() === name && + (otherAccessor).block !== null) { + accessors.push(otherAccessor); + classElements.splice(i, 1); + } + } + } + + var arguments = [ + MemberAccessExpressionSyntax.create1( + this.withNoTrivia(classDeclaration.identifier), Syntax.identifierName("prototype")), + Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.space), + Syntax.stringLiteralExpression('"' + (memberAccessor).propertyName.text() + '"'), + Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.space) + ]; + + var propertyAssignments: ISyntaxNodeOrToken[] = []; + for (i = 0; i < accessors.length; i++) { + var converted = this.convertMemberAccessor(accessors[i]); + converted = this.changeIndentation( + converted, /*changeFirstToken:*/ true, this.options.indentSpaces); + propertyAssignments.push(converted); + propertyAssignments.push( + Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.newLine)); + } + + var accessorColumn = this.columnForStartOfToken(memberAccessor.firstToken()); + var accessorTrivia = this.indentationTrivia(accessorColumn); + var propertyTrivia = this.indentationTrivia(accessorColumn + this.options.indentSpaces); + + propertyAssignments.push(this.factory.simplePropertyAssignment( + Syntax.identifier("enumerable"), + Syntax.token(SyntaxKind.ColonToken).withTrailingTrivia(this.space), + Syntax.trueExpression()).withLeadingTrivia(propertyTrivia)); + propertyAssignments.push(Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.newLine)); + + propertyAssignments.push(this.factory.simplePropertyAssignment( + Syntax.identifier("configurable"), + Syntax.token(SyntaxKind.ColonToken).withTrailingTrivia(this.space), + Syntax.trueExpression()).withLeadingTrivia(propertyTrivia).withTrailingTrivia(this.newLine)); + + arguments.push(this.factory.objectLiteralExpression( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.newLine), + Syntax.separatedList(propertyAssignments), + Syntax.token(SyntaxKind.CloseBraceToken).withLeadingTrivia(accessorTrivia))); + + return ExpressionStatementSyntax.create1( + this.factory.invocationExpression( + MemberAccessExpressionSyntax.create1(Syntax.identifierName("Object"), Syntax.identifierName("defineProperty")), + ArgumentListSyntax.create1().withArguments(Syntax.separatedList(arguments)))) + .withLeadingTrivia(memberAccessor.leadingTrivia()).withTrailingTrivia(this.newLine); + } + + private convertClassElements(classDeclaration: ClassDeclarationSyntax): IStatementSyntax[] { + var result: IStatementSyntax[] = []; + + var classElements = classDeclaration.classElements.toArray(); + while (classElements.length > 0) { + var classElement = classElements.shift(); + + var converted: IStatementSyntax = null; + if (classElement.kind() === SyntaxKind.MemberFunctionDeclaration) { + converted = this.convertMemberFunctionDeclaration(classDeclaration, classElement); + } + else if (classElement.kind() === SyntaxKind.MemberVariableDeclaration) { + converted = this.generatePropertyAssignment(classDeclaration, /*static:*/ true, classElement); + } + else if (classElement.kind() === SyntaxKind.GetAccessor || + classElement.kind() === SyntaxKind.SetAccessor) { + converted = this.convertMemberAccessorDeclaration(classDeclaration, classElement, classElements); + } + + if (converted !== null) { + result.push(converted); + } + } + + return result; + } + + public visitClassDeclaration(node: ClassDeclarationSyntax): VariableStatementSyntax { + var identifier = this.withNoTrivia(node.identifier); + + var statements: IStatementSyntax[] = []; + var statementIndentation = this.indentationTrivia(this.options.indentSpaces + this.columnForStartOfToken(node.firstToken())); + + if (node.heritageClauses.childCount() > 0) { + // __extends(C, _super); + statements.push(ExpressionStatementSyntax.create1( + this.factory.invocationExpression( + Syntax.identifierName("__extends"), + ArgumentListSyntax.create1().withArguments(Syntax.separatedList([ + identifier, + Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.space), + Syntax.identifierName("_super")])))).withLeadingTrivia(statementIndentation).withTrailingTrivia(this.newLine)); + } + + var constructorDeclaration = ArrayUtilities.firstOrDefault( + node.classElements.toArray(), c => c.kind() === SyntaxKind.ConstructorDeclaration); + + var constructorFunctionDeclaration = constructorDeclaration === null + ? this.createDefaultConstructorDeclaration(node) + : this.convertConstructorDeclaration(node, constructorDeclaration); + + if (constructorFunctionDeclaration !== null) { + statements.push(constructorFunctionDeclaration) + } + + statements.push.apply(statements, this.convertClassElements(node)); + + // return C; + statements.push(this.factory.returnStatement( + Syntax.token(SyntaxKind.ReturnKeyword).withTrailingTrivia(this.space), + identifier, + Syntax.token(SyntaxKind.SemicolonToken)) + .withLeadingTrivia(statementIndentation).withTrailingTrivia(this.newLine)); + + var block = this.factory.block( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.newLine), + Syntax.list(statements), + Syntax.token(SyntaxKind.CloseBraceToken).withLeadingTrivia(this.indentationTriviaForStartOfNode(node))); + + var callParameters: ParameterSyntax[] = []; + if (node.heritageClauses.childCount() > 0) { + callParameters.push(ParameterSyntax.create(Syntax.identifier("_super"))); + } + + var callSignature = CallSignatureSyntax.create( + ParameterListSyntax.create1().withParameters( + Syntax.separatedList(callParameters))).withTrailingTrivia(this.space); + + var invocationParameters: ISyntaxNodeOrToken[] = []; + if (node.heritageClauses.childCount() > 0) { + var heritageClause = node.heritageClauses.childAt(0); + if (heritageClause.typeNames.nonSeparatorCount() > 0) { + invocationParameters.push(heritageClause.typeNames.nonSeparatorAt(0) + .withLeadingTrivia(Syntax.emptyTriviaList) + .withTrailingTrivia(Syntax.emptyTriviaList)); + } + } + + // (function(_super) { ... })(BaseType) + var invocationExpression = this.factory.invocationExpression( + ParenthesizedExpressionSyntax.create1(FunctionExpressionSyntax.create1() + .withCallSignature(callSignature) + .withBlock(block)), + ArgumentListSyntax.create1().withArguments( + Syntax.separatedList(invocationParameters))); + + // C = (function(_super) { ... })(BaseType) + var variableDeclarator = VariableDeclaratorSyntax.create( + identifier.withTrailingTrivia(Syntax.spaceTriviaList)).withEqualsValueClause( + this.factory.equalsValueClause( + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + invocationExpression)); + + // var C = (function(_super) { ... })(BaseType); + return VariableStatementSyntax.create1(this.factory.variableDeclaration( + Syntax.token(SyntaxKind.VarKeyword).withTrailingTrivia(this.space), + Syntax.separatedList([variableDeclarator]))) + .withLeadingTrivia(node.leadingTrivia()).withTrailingTrivia(this.newLine); + } + + public visitVariableDeclarator(node: VariableDeclaratorSyntax): VariableDeclaratorSyntax { + var result: VariableDeclaratorSyntax = super.visitVariableDeclarator(node); + if (result.typeAnnotation === null) { + return result; + } + + var newTrailingTrivia = result.propertyName.trailingTrivia().concat(result.typeAnnotation.trailingTrivia()); + + return result.withTypeAnnotation(null) + .withPropertyName(result.propertyName.withTrailingTrivia(newTrailingTrivia)); + } + + public visitCallSignature(node: CallSignatureSyntax): CallSignatureSyntax { + var result: CallSignatureSyntax = super.visitCallSignature(node); + if (result.typeAnnotation === null) { + return result; + } + + var newTrailingTrivia = result.parameterList.trailingTrivia().concat( + result.typeAnnotation.trailingTrivia()); + + return result.withTypeAnnotation(null).withTrailingTrivia(newTrailingTrivia); + } + + public visitCastExpression(node: CastExpressionSyntax): IExpressionSyntax { + var result: CastExpressionSyntax = super.visitCastExpression(node); + + var subExpression = result.expression; + var totalTrivia = result.leadingTrivia().concat(subExpression.leadingTrivia()); + + return subExpression.withLeadingTrivia(totalTrivia); + } + + public visitInterfaceDeclaration(node: InterfaceDeclarationSyntax): InterfaceDeclarationSyntax { + // TODO: transfer trivia if important. + return null; + } + + public visitTypeParameterList(node: TypeParameterListSyntax): TypeParameterListSyntax { + return null; + } + + private generateEnumValueExpression(enumDeclaration: EnumDeclarationSyntax, + enumElement: EnumElementSyntax, + assignDefaultValues: boolean, + index: number): IExpressionSyntax { + if (enumElement.equalsValueClause !== null) { + // Use the value if one is provided. + return enumElement.equalsValueClause.value.accept(this).withTrailingTrivia(Syntax.emptyTriviaList); + } + + // Didn't have a value. Synthesize one if we're doing that, or use the previous item's value + // (plus one). + if (assignDefaultValues) { + return Syntax.numericLiteralExpression(index.toString()); + } + + // Add one to the previous value. + var enumIdentifier = this.withNoTrivia(enumDeclaration.identifier); + var previousEnumElement = enumDeclaration.enumElements.nonSeparatorAt(index - 1); + var variableIdentifier = this.withNoTrivia(previousEnumElement.propertyName); + + var receiver = variableIdentifier.kind() === SyntaxKind.StringLiteral + ? ElementAccessExpressionSyntax.create1(enumIdentifier, variableIdentifier) + : MemberAccessExpressionSyntax.create1(enumIdentifier, variableIdentifier); + + return this.factory.binaryExpression(SyntaxKind.PlusExpression, + receiver.withTrailingTrivia(Syntax.spaceTriviaList), + Syntax.token(SyntaxKind.PlusToken).withTrailingTrivia(this.space), + Syntax.numericLiteralExpression("1")); + } + + private generateEnumFunctionExpression(node: EnumDeclarationSyntax): FunctionExpressionSyntax { + var identifier = this.withNoTrivia(node.identifier); + + var enumColumn = this.columnForStartOfToken(node.firstToken()); + + var statements: IStatementSyntax[] = []; + + var initIndentationColumn = enumColumn + this.options.indentSpaces; + var initIndentationTrivia = this.indentationTrivia(initIndentationColumn); + + if (node.enumElements.nonSeparatorCount() > 0) { + var assignDefaultValues = { value: true }; + for (var i = 0, n = node.enumElements.nonSeparatorCount(); i < n; i++) { + var enumElement = node.enumElements.nonSeparatorAt(i) + var variableIdentifier = this.withNoTrivia(enumElement.propertyName); + + assignDefaultValues.value = assignDefaultValues.value && (enumElement.equalsValueClause === null); + + // "E.Foo = 1" or "E['A B'] = 1" + var left = variableIdentifier.kind() === SyntaxKind.StringLiteral + ? ElementAccessExpressionSyntax.create1(identifier, variableIdentifier) + : MemberAccessExpressionSyntax.create1(identifier, variableIdentifier); + var innerAssign = Syntax.assignmentExpression( + left.withTrailingTrivia(this.space), + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + this.generateEnumValueExpression(node, enumElement, assignDefaultValues.value, i)) + + // E[E.Foo = 1] + var elementAccessExpression = ElementAccessExpressionSyntax.create1(identifier, innerAssign) + .withLeadingTrivia(enumElement.leadingTrivia()).withTrailingTrivia(this.space); + + // E[E.Foo = 1] = "Foo" + var outerAssign = Syntax.assignmentExpression( + elementAccessExpression, + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + variableIdentifier.kind() === SyntaxKind.StringLiteral + ? variableIdentifier + : Syntax.stringLiteralExpression('"' + variableIdentifier.text() + '"')); + + var expressionStatement = ExpressionStatementSyntax.create1( + outerAssign).withTrailingTrivia(this.newLine); + + statements.push(expressionStatement); + } + } + + var block = this.factory.block( + Syntax.token(SyntaxKind.OpenBraceToken).withTrailingTrivia(this.newLine), + Syntax.list(statements), + Syntax.token(SyntaxKind.CloseBraceToken) + .withLeadingTrivia(this.indentationTrivia(enumColumn))); + + var parameterList = ParameterListSyntax.create1().withParameter(ParameterSyntax.create1(identifier)).withTrailingTrivia(this.space); + + return FunctionExpressionSyntax.create1() + .withCallSignature(CallSignatureSyntax.create(parameterList)) + .withBlock(block); + } + + public visitEnumDeclaration(node: EnumDeclarationSyntax): IStatementSyntax[] { + var identifier = this.withNoTrivia(node.identifier); + + // Copy existing leading trivia of the enum declaration to this node. + // var E; + var variableStatement: IStatementSyntax = VariableStatementSyntax.create1(this.factory.variableDeclaration( + Syntax.token(SyntaxKind.VarKeyword).withTrailingTrivia(this.space), + Syntax.separatedList([VariableDeclaratorSyntax.create(identifier)]))) + .withLeadingTrivia(node.leadingTrivia()).withTrailingTrivia(this.newLine); + + // (function(E) { E[E.e1 = ... })(E||(E={})); + var expressionStatement: IStatementSyntax = ExpressionStatementSyntax.create1( + this.factory.invocationExpression( + ParenthesizedExpressionSyntax.create1(this.generateEnumFunctionExpression(node)), + ArgumentListSyntax.create1().withArgument(this.initializedVariable(identifier)))) + .withLeadingTrivia(this.indentationTriviaForStartOfNode(node)) + .withTrailingTrivia(this.newLine); + + return [variableStatement, expressionStatement]; + } + + private convertSuperInvocationExpression(node: InvocationExpressionSyntax): InvocationExpressionSyntax { + var result: InvocationExpressionSyntax = super.visitInvocationExpression(node); + + var expression = MemberAccessExpressionSyntax.create1(Syntax.identifierName("_super"), Syntax.identifierName("call")); + + var arguments = result.argumentList.arguments.toArray(); + if (arguments.length > 0) { + arguments.unshift(Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.space)); + } + + arguments.unshift(Syntax.token(SyntaxKind.ThisKeyword)); + + return result.withExpression(expression) + .withArgumentList(result.argumentList.withArguments(Syntax.separatedList(arguments))) + .withLeadingTrivia(result.leadingTrivia()); + } + + private convertSuperMemberAccessInvocationExpression(node: InvocationExpressionSyntax): InvocationExpressionSyntax { + var result: InvocationExpressionSyntax = super.visitInvocationExpression(node); + + var arguments = result.argumentList.arguments.toArray(); + if (arguments.length > 0) { + arguments.unshift(Syntax.token(SyntaxKind.CommaToken).withTrailingTrivia(this.space)); + } + + arguments.unshift(Syntax.token(SyntaxKind.ThisKeyword)); + + var expression = MemberAccessExpressionSyntax.create1(result.expression, Syntax.identifierName("call")); + return result.withExpression(expression) + .withArgumentList(result.argumentList.withArguments(Syntax.separatedList(arguments))); + } + + public visitInvocationExpression(node: InvocationExpressionSyntax): InvocationExpressionSyntax { + if (Syntax.isSuperInvocationExpression(node)) { + return this.convertSuperInvocationExpression(node); + } + else if (Syntax.isSuperMemberAccessInvocationExpression(node)) { + return this.convertSuperMemberAccessInvocationExpression(node); + } + + return super.visitInvocationExpression(node); + } + + public visitVariableStatement(node: VariableStatementSyntax): VariableStatementSyntax { + var result: VariableStatementSyntax = super.visitVariableStatement(node); + + return result.withModifiers(Syntax.emptyList()) + .withLeadingTrivia(result.leadingTrivia()); + } + + public visitMemberAccessExpression(node: MemberAccessExpressionSyntax): MemberAccessExpressionSyntax { + var result: MemberAccessExpressionSyntax = super.visitMemberAccessExpression(node); + + if (Syntax.isSuperMemberAccessExpression(result)) { + return MemberAccessExpressionSyntax.create1( + MemberAccessExpressionSyntax.create1(Syntax.identifierName("_super"), Syntax.identifierName("prototype")), + result.name).withLeadingTrivia(result.leadingTrivia()); + } + + return result; + } + + public visitToken(token: ISyntaxToken): ISyntaxToken { + if (token.kind() === SyntaxKind.IdentifierName) { + return this.visitIdentifierName(token); + } + + if (token.kind() === SyntaxKind.ThisKeyword) { + return this.visitThisKeyword(token); + } + + return token; + } + + public visitThisKeyword(token: ISyntaxToken): ISyntaxToken { + // TODO: use typecheck information to tell if we're accessing 'this' in a lambda and + // should use "_this" instead. + return token; + } + + public visitIdentifierName(token: ISyntaxToken): INameSyntax { + // Check if a name token needs to become fully qualified. + var parent = token.parent; + + // We never qualify in a qualified name. A qualified name only shows up in type + // contexts, and will be removed anyways. + if (parent.kind() === SyntaxKind.QualifiedName) { + return token; + } + + // Same issue for a generic name. + if (parent.kind() === SyntaxKind.GenericType) { + return token; + } + + // We never qualify the right hand side of a dot. + if (parent.kind() === SyntaxKind.MemberAccessExpression && (parent).name === token) { + return token; + } + + // TODO(cyrusn): Implement this when we have type check support. + + // Ok. We're a name token that isn't on the right side of a dot. We may need to be + // qualified. Get the symbol that this token binds to. If it is a module/class and + // has a full name that is larger than this token, then return the full name as a + // member access expression. + return token; + } + + private generateThisCaptureStatement(indentationColumn: number): VariableStatementSyntax { + // var _this = this; + return VariableStatementSyntax.create1(this.factory.variableDeclaration( + Syntax.token(SyntaxKind.VarKeyword).withTrailingTrivia(this.space), + Syntax.separatedList([ + this.factory.variableDeclarator( + Syntax.identifier("_this").withTrailingTrivia(this.space), + null, + this.factory.equalsValueClause( + Syntax.token(SyntaxKind.EqualsToken).withTrailingTrivia(this.space), + Syntax.token(SyntaxKind.ThisKeyword))) + ]))).withLeadingTrivia(this.indentationTrivia(indentationColumn)).withTrailingTrivia(this.newLine); + } + + private mustCaptureThisInConstructor(constructorDeclaration: ConstructorDeclarationSyntax): boolean { + // TODO: use typecheck to answer this question properly. + return false; + } + + private mustCaptureThisInClass(classDeclaratoin: ClassDeclarationSyntax): boolean { + // TODO: use typecheck to answer this question properly. + return false; + } + + private mustCaptureThisInModule(moduleDeclaration: ModuleDeclarationSyntax): boolean { + // TODO: use typecheck to answer this question properly. + return false; + } + + private mustCaptureThisInFunction(functionDeclaration: FunctionDeclarationSyntax): boolean { + // TODO: use typecheck to answer this question properly. + return false; + } + } + + export function emit(input: SourceUnitSyntax, options: FormattingOptions = null): SourceUnitSyntax { + // Make sure no one is passing us a bogus tree. + SyntaxNodeInvariantsChecker.checkInvariants(input); + + // If there's nothing typescript specific about this node, then just return it as is. + if (!input.isTypeScriptSpecific()) { + return input; + } + + // Do the initial conversion. Note: the result at this point may be 'bogus'. For example, + // it make contain the same token instance multiple times in the tree. + var output: SourceUnitSyntax = input.accept(new EmitterImpl(input.syntaxTree(), options)); + + // Make sure we clone any nodes/tokens we used in multiple places in the result. That way + // we don't break the invariant that all tokens in a tree are unique. + output = output.accept(new EnsureTokenUniquenessRewriter()); + + SyntaxNodeInvariantsChecker.checkInvariants(output); + Debug.assert(!output.isTypeScriptSpecific()); + + return output; + } +} \ No newline at end of file diff --git a/src/services/syntax/formattingOptions.ts b/src/services/syntax/formattingOptions.ts new file mode 100644 index 00000000000..ddbf7af9301 --- /dev/null +++ b/src/services/syntax/formattingOptions.ts @@ -0,0 +1,13 @@ +/// + +module TypeScript { + export class FormattingOptions { + constructor(public useTabs: boolean, + public spacesPerTab: number, + public indentSpaces: number, + public newLineCharacter: string) { + } + + public static defaultOptions = new FormattingOptions(/*useTabs:*/ false, /*spacesPerTab:*/ 4, /*indentSpaces:*/ 4, /*newLineCharacter*/ "\r\n"); + } +} \ No newline at end of file diff --git a/src/services/syntax/incrementalParser.ts b/src/services/syntax/incrementalParser.ts new file mode 100644 index 00000000000..94423418f42 --- /dev/null +++ b/src/services/syntax/incrementalParser.ts @@ -0,0 +1,865 @@ +/// + +module TypeScript.IncrementalParser { + interface IParserRewindPoint { + // Information used by the incremental parser source. + oldSourceUnitCursor: SyntaxCursor; + changeDelta: number; + changeRange: TextChangeRange; + } + + // Parser source used in incremental scenarios. This parser source wraps an old tree, text + // change and new text, and uses all three to provide nodes and tokens to the parser. In + // general, nodes from the old tree are returned as long as they do not intersect with the text + // change. Then, once the text change is reached, tokens from the old tree are returned as + // long as they do not intersect with the text change. Then, the text that is actually changed + // will be scanned using a normal scanner. Then, once the new text is scanned, the source will + // attempt to sync back up with nodes or tokens that started where the new tokens end. Once it + // can do that, then all subsequent data will come from the original tree. + // + // This allows for an enormous amount of tree reuse in common scenarios. Situations that + // prevent this level of reuse include substantially destructive operations like introducing + // "/*" without a "*/" nearby to terminate the comment. + function createParserSource(oldSyntaxTree: SyntaxTree, textChangeRange: TextChangeRange, text: ISimpleText): Parser.IParserSource { + var fileName = oldSyntaxTree.fileName(); + var languageVersion = oldSyntaxTree.languageVersion(); + + // The underlying source that we will use to scan tokens from any new text, or any tokens + // from the old tree that we decide we can't use for any reason. We will also continue + // scanning tokens from this source until we've decided that we're resynchronized and can + // read in subsequent data from the old tree. + // + // This parser source also keeps track of the absolute position in the text that we're in, + // and any token diagnostics produced. That way we dont' have to track that ourselves. + var _scannerParserSource: Scanner.IScannerParserSource; + + // The range of text in the *original* text that was changed, and the new length of it after + // the change. + var _changeRange: TextChangeRange; + + // Cached value of _changeRange.newSpan(). Cached for performance. + var _changeRangeNewSpan: TextSpan; + + // This number represents how our position in the old tree relates to the position we're + // pointing at in the new text. If it is 0 then our positions are in sync and we can read + // nodes or tokens from the old tree. If it is non-zero, then our positions are not in + // sync and we cannot use nodes or tokens from the old tree. + // + // Now, changeDelta could be negative or positive. Negative means 'the position we're at + // in the original tree is behind the position we're at in the text'. In this case we + // keep throwing out old nodes or tokens (and thus move forward in the original tree) until + // changeDelta becomes 0 again or positive. If it becomes 0 then we are resynched and can + // read nodes or tokesn from the tree. + // + // If changeDelta is positive, that means the current node or token we're pointing at in + // the old tree is at a further ahead position than the position we're pointing at in the + // new text. In this case we have no choice but to scan tokens from teh new text. We will + // continue to do so until, again, changeDelta becomes 0 and we've resynced, or change delta + // becomes negative and we need to skip nodes or tokes in the original tree. + var _changeDelta: number = 0; + + // The cursor we use to navigate through and retrieve nodes and tokens from the old tree. + var _oldSourceUnitCursor = getSyntaxCursor(); + var oldSourceUnit = oldSyntaxTree.sourceUnit(); + + var _outstandingRewindPointCount = 0; + + // Start the cursor pointing at the first element in the source unit (if it exists). + if (oldSourceUnit.moduleElements.length > 0) { + _oldSourceUnitCursor.pushElement(childAt(oldSourceUnit.moduleElements, 0), /*indexInParent:*/ 0); + } + + // In general supporting multiple individual edits is just not that important. So we + // just collapse this all down to a single range to make the code here easier. The only + // time this could be problematic would be if the user made a ton of discontinuous edits. + // For example, doing a column select on a *large* section of a code. If this is a + // problem, we can always update this code to handle multiple changes. + _changeRange = extendToAffectedRange(textChangeRange, oldSourceUnit); + _changeRangeNewSpan = _changeRange.newSpan(); + + // The old tree's length, plus whatever length change was caused by the edit + // Had better equal the new text's length! + if (Debug.shouldAssert(AssertionLevel.Aggressive)) { + Debug.assert((fullWidth(oldSourceUnit) - _changeRange.span().length() + _changeRange.newLength()) === text.length()); + } + + // Set up a scanner so that we can scan tokens out of the new text. + _scannerParserSource = Scanner.createParserSource(oldSyntaxTree.fileName(), text, oldSyntaxTree.languageVersion()); + + function release() { + _scannerParserSource.release(); + _scannerParserSource = null; + _oldSourceUnitCursor = null; + _outstandingRewindPointCount = 0; + } + + function extendToAffectedRange(changeRange: TextChangeRange, + sourceUnit: SourceUnitSyntax): TextChangeRange { + // Consider the following code: + // void foo() { /; } + // + // If the text changes with an insertion of / just before the semicolon then we end up with: + // void foo() { //; } + // + // If we were to just use the changeRange a is, then we would not rescan the { token + // (as it does not intersect the actual original change range). Because an edit may + // change the token touching it, we actually need to look back *at least* one token so + // that the prior token sees that change. + // + // Note: i believe (outside of regex tokens) max lookahead is just one token for + // TypeScript. However, if this turns out to be wrong, we may have to increase how much + // futher we look back. + // + // Note: lookahead handling for regex characters is handled specially in during + // incremental parsing, and does not need to be handled here. + + var maxLookahead = 1; + + var start = changeRange.span().start(); + + // the first iteration aligns us with the change start. subsequent iteration move us to + // the left by maxLookahead tokens. We only need to do this as long as we're not at the + // start of the tree. + for (var i = 0; start > 0 && i <= maxLookahead; i++) { + var token = findToken(sourceUnit, start); + + // Debug.assert(token.kind !== SyntaxKind.None); + // Debug.assert(token.kind() === SyntaxKind.EndOfFileToken || token.fullWidth() > 0); + + var position = token.fullStart(); + + start = Math.max(0, position - 1); + } + + var finalSpan = TextSpan.fromBounds(start, changeRange.span().end()); + var finalLength = changeRange.newLength() + (changeRange.span().start() - start); + + return new TextChangeRange(finalSpan, finalLength); + } + + function absolutePosition() { + return _scannerParserSource.absolutePosition(); + } + + function tokenDiagnostics(): Diagnostic[] { + return _scannerParserSource.tokenDiagnostics(); + } + + function getRewindPoint() { + // Get a rewind point for our new text reader and for our old source unit cursor. + var rewindPoint = _scannerParserSource.getRewindPoint(); + + // Clone our cursor. That way we can restore to that point if hte parser needs to rewind. + var oldSourceUnitCursorClone = cloneSyntaxCursor(_oldSourceUnitCursor); + + // Store where we were when the rewind point was created. + rewindPoint.changeDelta = _changeDelta; + rewindPoint.changeRange = _changeRange; + rewindPoint.oldSourceUnitCursor = _oldSourceUnitCursor; + + _oldSourceUnitCursor = oldSourceUnitCursorClone; + + // Debug.assert(rewindPoint.pinCount === _oldSourceUnitCursor.pinCount()); + + _outstandingRewindPointCount++; + return rewindPoint; + } + + function rewind(rewindPoint: IParserRewindPoint): void { + // Restore our state to the values when the rewind point was created. + _changeRange = rewindPoint.changeRange; + _changeDelta = rewindPoint.changeDelta; + + // Reset the cursor to what it was when we got the rewind point. Make sure to return + // our existing cursor to the pool so it can be reused. + returnSyntaxCursor(_oldSourceUnitCursor); + _oldSourceUnitCursor = rewindPoint.oldSourceUnitCursor; + + // Null out the cursor that the rewind point points to. This way we don't try + // to return it in 'releaseRewindPoint'. + rewindPoint.oldSourceUnitCursor = null; + + _scannerParserSource.rewind(rewindPoint); + } + + function releaseRewindPoint(rewindPoint: IParserRewindPoint): void { + if (rewindPoint.oldSourceUnitCursor !== null) { + returnSyntaxCursor(rewindPoint.oldSourceUnitCursor); + } + + _scannerParserSource.releaseRewindPoint(rewindPoint); + _outstandingRewindPointCount--; + Debug.assert(_outstandingRewindPointCount >= 0); + } + + function isPinned() { + return _outstandingRewindPointCount > 0; + } + + function canReadFromOldSourceUnit() { + // If we're currently pinned, then do not want to touch the cursor. Here's why. First, + // recall that we're 'pinned' when we're speculatively parsing. So say we were to allow + // returning old nodes/tokens while speculatively parsing. Then, the parser might start + // mutating the nodes and tokens we returned (i.e. by setting their parents). Then, + // when we rewound, those nodes and tokens would still have those updated parents. + // Parents which we just decided we did *not* want to parse (hence why we rewound). For + // Example, say we have something like: + // + // var v = fe; // note: this is not generic. + // + // When incrementally parsing, we will need to speculatively parse to determine if the + // above is generic. This will cause us to reuse the "a, b, c" tokens, and set their + // parent to a new type argument list. A type argument list we will then throw away once + // we decide that it isn't actually generic. We will have now 'broken' the original tree. + // + // As such, the rule is simple. We only return nodes/tokens from teh original tree if + // we know the parser will accept and consume them and never rewind back before them. + if (isPinned()) { + return false; + } + + // If our current absolute position is in the middle of the changed range in the new text + // then we definitely can't read from the old source unit right now. + if (_changeRange !== null && _changeRangeNewSpan.intersectsWithPosition(absolutePosition())) { + return false; + } + + // First, try to sync up with the new text if we're behind. + syncCursorToNewTextIfBehind(); + + // Now, if we're synced up *and* we're not currently pinned in the new text scanner, + // then we can read a node from the cursor. If we're pinned in the scanner then we + // can't read a node from the cursor because we will mess up the pinned scanner when + // we try to move it forward past this node. + return _changeDelta === 0 && + !_oldSourceUnitCursor.isFinished(); + } + + function updateTokens(nodeOrToken: ISyntaxNodeOrToken): void { + // If we got a node or token, and we're past the range of edited text, then walk its + // constituent tokens, making sure all their positions are correct. We don't need to + // do this for the tokens before the edited range (since their positions couldn't have + // been affected by the edit), and we don't need to do this for the tokens in the + // edited range, as their positions will be correct when the underlying parser source + // creates them. + + var position = absolutePosition(); + var tokenWasMoved = isPastChangeRange() && fullStart(nodeOrToken) !== position; + + if (tokenWasMoved) { + setTokenFullStartWalker.position = position; + + visitNodeOrToken(setTokenFullStartWalker, nodeOrToken); + } + } + + function currentNode(): ISyntaxNode { + if (canReadFromOldSourceUnit()) { + // Try to read a node. If we can't then our caller will call back in and just try + // to get a token. + var node = tryGetNodeFromOldSourceUnit(); + if (node !== null) { + // Make sure the positions for the tokens in this node are correct. + updateTokens(node); + return node; + } + } + + // Either we were ahead of the old text, or we were pinned. No node can be read here. + return null; + } + + function currentToken(): ISyntaxToken { + if (canReadFromOldSourceUnit()) { + var token = tryGetTokenFromOldSourceUnit(); + if (token !== null) { + // Make sure the token's position/text is correct. + updateTokens(token); + return token; + } + } + + // Either we couldn't read from the old source unit, or we weren't able to successfully + // get a token from it. In this case we need to read a token from the underlying text. + return _scannerParserSource.currentToken(); + } + + function currentContextualToken(): ISyntaxToken { + // Just delegate to the underlying source to handle + return _scannerParserSource.currentContextualToken(); + } + + function syncCursorToNewTextIfBehind() { + while (true) { + if (_oldSourceUnitCursor.isFinished()) { + // Can't sync up if the cursor is finished. + break; + } + + if (_changeDelta >= 0) { + // Nothing to do if we're synced up or ahead of the text. + break; + } + + // We're behind in the original tree. Throw out a node or token in an attempt to + // catch up to the position we're at in the new text. + + var currentNodeOrToken = _oldSourceUnitCursor.currentNodeOrToken(); + + // If we're pointing at a node, and that node's width is less than our delta, + // then we can just skip that node. Otherwise, if we're pointing at a node + // whose width is greater than the delta, then crumble it and try again. + // Otherwise, we must be pointing at a token. Just skip it and try again. + + if (isNode(currentNodeOrToken) && (fullWidth(currentNodeOrToken) > Math.abs(_changeDelta))) { + // We were pointing at a node whose width was more than changeDelta. Crumble the + // node and try again. Note: we haven't changed changeDelta. So the callers loop + // will just repeat this until we get to a node or token that we can skip over. + _oldSourceUnitCursor.moveToFirstChild(); + } + else { + _oldSourceUnitCursor.moveToNextSibling(); + + // Get our change delta closer to 0 as we skip past this item. + _changeDelta += fullWidth(currentNodeOrToken); + + // If this was a node, then our changeDelta is 0 or negative. If this was a + // token, then we could still be negative (and we have to read another token), + // we could be zero (we're done), or we could be positive (we've moved ahead + // of the new text). Only if we're negative will we continue looping. + } + } + + // At this point, we must be either: + // a) done with the cursor + // b) (ideally) caught up to the new text position. + // c) ahead of the new text position. + // In case 'b' we can try to reuse a node from teh old tree. + // Debug.assert(_oldSourceUnitCursor.isFinished() || _changeDelta >= 0); + } + + function intersectsWithChangeRangeSpanInOriginalText(start: number, length: number) { + return !isPastChangeRange() && _changeRange.span().intersectsWith(start, length); + } + + function tryGetNodeFromOldSourceUnit(): ISyntaxNode { + // Debug.assert(canReadFromOldSourceUnit()); + + // Keep moving the cursor down to the first node that is safe to return. A node is + // safe to return if: + // a) it does not intersect the changed text. + // b) it does not contain skipped text. + // c) it does not have any zero width tokens in it. + // d) it does not have a regex token in it. + // e) we are still in the same strict or non-strict state that the node was originally parsed in. + while (true) { + var node = _oldSourceUnitCursor.currentNode(); + if (node === null) { + // Couldn't even read a node, nothing to return. + return null; + } + + if (!intersectsWithChangeRangeSpanInOriginalText(absolutePosition(), fullWidth(node))) { + // Didn't intersect with the change range. + var isIncrementallyUnusuable = TypeScript.isIncrementallyUnusable(node); + if (!isIncrementallyUnusuable) { + + // Didn't contain anything that would make it unusable. Awesome. This is + // a node we can reuse. + return node; + } + } + + // We couldn't use currentNode. Try to move to its first child (in case that's a + // node). If it is we can try using that. Otherwise we'll just bail out in the + // next iteration of the loop. + _oldSourceUnitCursor.moveToFirstChild(); + } + } + + function canReuseTokenFromOldSourceUnit(position: number, token: ISyntaxToken): boolean { + // A token is safe to return if: + // a) it does not intersect the changed text. + // b) it does not contain skipped text. + // c) it is not zero width. + // d) it is not a contextual parser token. + // + // NOTE: It is safe to get a token regardless of what our strict context was/is. That's + // because the strict context doesn't change what tokens are scanned, only how the + // parser reacts to them. + // + // NOTE: we don't mark a keyword that was converted to an identifier as 'incrementally + // unusable. This is because we don't want to mark it's containing parent node as + // unusable. i.e. if i have this: "public Foo(string: Type) { }", then that *entire* node + // is reusuable even though "string" was converted to an identifier. However, we still + // need to make sure that if that the parser asks for a *token* we don't return it. + // Converted identifiers can't ever be created by the scanner, and as such, should not + // be returned by this source. + if (token !== null) { + if (!intersectsWithChangeRangeSpanInOriginalText(position, token.fullWidth())) { + // Didn't intersect with the change range. + if (!token.isIncrementallyUnusable() && !Scanner.isContextualToken(token)) { + + // Didn't contain anything that would make it unusable. Awesome. This is + // a token we can reuse. + return true; + } + } + } + + return false; + } + + function tryGetTokenFromOldSourceUnit(): ISyntaxToken { + // Debug.assert(canReadFromOldSourceUnit()); + + // get the current token that the cursor is pointing at. + var token = _oldSourceUnitCursor.currentToken(); + + return canReuseTokenFromOldSourceUnit(absolutePosition(), token) + ? token : null; + } + + function peekToken(n: number): ISyntaxToken { + if (canReadFromOldSourceUnit()) { + var token = tryPeekTokenFromOldSourceUnit(n); + if (token !== null) { + return token; + } + } + + // Couldn't peek this far in the old tree. Get the token from the new text. + return _scannerParserSource.peekToken(n); + } + + function tryPeekTokenFromOldSourceUnit(n: number): ISyntaxToken { + // Debug.assert(canReadFromOldSourceUnit()); + + // clone the existing cursor so we can move it forward and then restore ourselves back + // to where we started from. + + var cursorClone = cloneSyntaxCursor(_oldSourceUnitCursor); + + var token = tryPeekTokenFromOldSourceUnitWorker(n); + + returnSyntaxCursor(_oldSourceUnitCursor); + _oldSourceUnitCursor = cursorClone; + + return token; + } + + function tryPeekTokenFromOldSourceUnitWorker(n: number): ISyntaxToken { + // In order to peek the 'nth' token we need all the tokens up to that point. That way + // we know we know position that the nth token is at. The position is necessary so + // that we can test if this token (or any that precede it cross the change range). + var currentPosition = absolutePosition(); + + // First, make sure the cursor is pointing at a token. + _oldSourceUnitCursor.moveToFirstToken(); + + // Now, keep walking forward to successive tokens. + for (var i = 0; i < n; i++) { + var interimToken = _oldSourceUnitCursor.currentToken(); + + if (!canReuseTokenFromOldSourceUnit(currentPosition, interimToken)) { + return null; + } + + currentPosition += interimToken.fullWidth(); + _oldSourceUnitCursor.moveToNextSibling(); + } + + var token = _oldSourceUnitCursor.currentToken(); + return canReuseTokenFromOldSourceUnit(currentPosition, token) + ? token : null; + } + + function consumeNode(node: ISyntaxNode): void { + // A node could have only come from the old source unit cursor. Update it and our + // current state. + // Debug.assert(_changeDelta === 0); + // Debug.assert(currentNode() === node); + + _oldSourceUnitCursor.moveToNextSibling(); + + // Update the underlying source with where it should now be currently pointin. + var _absolutePosition = absolutePosition() + fullWidth(node); + _scannerParserSource.resetToPosition(_absolutePosition); + + // Debug.assert(previousToken !== null); + // Debug.assert(previousToken.width() > 0); + + //if (!isPastChangeRange()) { + // // If we still have a change range, then this node must have ended before the + // // change range starts. Thus, we don't need to call 'skipPastChanges'. + // Debug.assert(absolutePosition() < _changeRange.span().start()); + //} + } + + function consumeToken(currentToken: ISyntaxToken): void { + // This token may have come from the old source unit, or from the new text. Handle + // both accordingly. + + if (_oldSourceUnitCursor.currentToken() === currentToken) { + // The token came from the old source unit. So our tree and text must be in sync. + // Debug.assert(_changeDelta === 0); + + // Move the cursor past this token. + _oldSourceUnitCursor.moveToNextSibling(); + + // Debug.assert(!_normalParserSource.isPinned()); + + // Update the underlying source with where it should now be currently pointing. We + // don't need to do this when the token came from the new text as the source will + // automatically be placed in the right position. + var _absolutePosition = absolutePosition() + currentToken.fullWidth(); + _scannerParserSource.resetToPosition(_absolutePosition); + + // Debug.assert(previousToken !== null); + // Debug.assert(previousToken.width() > 0); + + //if (!isPastChangeRange()) { + // // If we still have a change range, then this token must have ended before the + // // change range starts. Thus, we don't need to call 'skipPastChanges'. + // Debug.assert(absolutePosition() < _changeRange.span().start()); + //} + } + else { + // the token came from the new text. That means the normal source moved forward, + // while the syntax cursor stayed in the same place. Thus our delta moves even + // further back. + _changeDelta -= currentToken.fullWidth(); + + // Move our underlying source forward. + _scannerParserSource.consumeToken(currentToken); + + // Because we read a token from the new text, we may have moved ourselves past the + // change range. If we did, then we may also have to update our change delta to + // compensate for the length change between the old and new text. + if (!isPastChangeRange()) { + // var changeEndInNewText = _changeRange.span().start() + _changeRange.newLength(); + if (absolutePosition() >= _changeRangeNewSpan.end()) { + _changeDelta += _changeRange.newLength() - _changeRange.span().length(); + + // Once we're past the change range, we no longer need it. Null it out. + // From now on we can check if we're past the change range just by seeing + // if this is null. + _changeRange = null; + } + } + } + } + + function isPastChangeRange(): boolean { + return _changeRange === null; + } + + return { + text: text, + fileName: fileName, + languageVersion: languageVersion, + currentNode: currentNode, + currentToken: currentToken, + currentContextualToken: currentContextualToken, + peekToken: peekToken, + consumeNode: consumeNode, + consumeToken: consumeToken, + getRewindPoint: getRewindPoint, + rewind: rewind, + releaseRewindPoint: releaseRewindPoint, + tokenDiagnostics: tokenDiagnostics, + release: release + }; + } + + interface SyntaxCursorPiece { + element: ISyntaxElement; + indexInParent: number + } + + function createSyntaxCursorPiece(element: ISyntaxElement, indexInParent: number) { + return { element: element, indexInParent: indexInParent }; + } + + // Pool syntax cursors so we don't churn too much memory when we need temporary cursors. + // i.e. when we're speculatively parsing, we can cheaply get a pooled cursor and then + // return it when we no longer need it. + var syntaxCursorPool: SyntaxCursor[] = []; + var syntaxCursorPoolCount: number = 0; + + function returnSyntaxCursor(cursor: SyntaxCursor): void { + // Make sure the cursor isn't holding onto any syntax elements. We don't want to leak + // them when we return the cursor to the pool. + cursor.clean(); + + syntaxCursorPool[syntaxCursorPoolCount] = cursor; + syntaxCursorPoolCount++; + } + + function getSyntaxCursor(): SyntaxCursor { + // Get an existing cursor from the pool if we have one. Or create a new one if we don't. + var cursor = syntaxCursorPoolCount > 0 + ? syntaxCursorPool[syntaxCursorPoolCount - 1] + : createSyntaxCursor(); + + if (syntaxCursorPoolCount > 0) { + // If we reused an existing cursor, take it out of the pool so no one else uses it. + syntaxCursorPoolCount--; + syntaxCursorPool[syntaxCursorPoolCount] = null; + } + + return cursor; + } + + function cloneSyntaxCursor(cursor: SyntaxCursor): SyntaxCursor { + var newCursor = getSyntaxCursor(); + + // Make the new cursor a *deep* copy of the cursor passed in. This ensures each cursor can + // be moved without affecting the other. + newCursor.deepCopyFrom(cursor); + + return newCursor; + } + + interface SyntaxCursor { + pieces: SyntaxCursorPiece[]; + + clean(): void; + isFinished(): boolean; + moveToFirstChild(): void; + moveToFirstToken(): void; + moveToNextSibling(): void; + currentNodeOrToken(): ISyntaxNodeOrToken; + currentNode(): ISyntaxNode; + currentToken(): ISyntaxToken; + pushElement(element: ISyntaxElement, indexInParent: number): void; + deepCopyFrom(other: SyntaxCursor): void; + } + + function createSyntaxCursor(): SyntaxCursor { + // Our list of path pieces. The piece pointed to by 'currentPieceIndex' must be a node or + // token. However, pieces earlier than that may point to list nodes. + // + // For perf we reuse pieces as much as possible. i.e. instead of popping items off the + // list, we just will change currentPieceIndex so we can reuse that piece later. + var pieces: SyntaxCursorPiece[] = []; + var currentPieceIndex: number = -1; + + // Cleans up this cursor so that it doesn't have any references to actual syntax nodes. + // This sould be done before returning the cursor to the pool so that the Parser module + // doesn't unnecessarily keep old syntax trees alive. + function clean(): void { + for (var i = 0, n = pieces.length; i < n; i++) { + var piece = pieces[i]; + + if (piece.element === null) { + break; + } + + piece.element = null; + piece.indexInParent = -1; + } + + currentPieceIndex = -1; + } + + // Makes this cursor into a deep copy of the cursor passed in. + function deepCopyFrom(other: SyntaxCursor): void { + // Debug.assert(currentPieceIndex === -1); + for (var i = 0, n = other.pieces.length; i < n; i++) { + var piece = other.pieces[i]; + + if (piece.element === null) { + break; + } + + pushElement(piece.element, piece.indexInParent); + } + + // Debug.assert(currentPieceIndex === other.currentPieceIndex); + } + + function isFinished(): boolean { + return currentPieceIndex < 0; + } + + function currentNodeOrToken(): ISyntaxNodeOrToken { + if (isFinished()) { + return null; + } + + var result = pieces[currentPieceIndex].element; + + // The current element must always be a node or a token. + // Debug.assert(result !== null); + // Debug.assert(result.isNode() || result.isToken()); + + return result; + } + + function currentNode(): ISyntaxNode { + var element = currentNodeOrToken(); + return isNode(element) ? element : null; + } + + function moveToFirstChild() { + var nodeOrToken = currentNodeOrToken(); + if (nodeOrToken === null) { + return; + } + + if (isToken(nodeOrToken)) { + // If we're already on a token, there's nothing to do. + return; + } + + // The last element must be a token or a node. + // Debug.assert(isNode(nodeOrToken)); + + // Either the node has some existent child, then move to it. if it doesn't, then it's + // an empty node. Conceptually the first child of an empty node is really just the + // next sibling of the empty node. + for (var i = 0, n = childCount(nodeOrToken); i < n; i++) { + var child = childAt(nodeOrToken, i); + if (child !== null && !isShared(child)) { + // Great, we found a real child. Push that. + pushElement(child, /*indexInParent:*/ i); + + // If it was a list, make sure we're pointing at its first element. We know we + // must have one because this is a non-shared list. + moveToFirstChildIfList(); + return; + } + } + + // This element must have been an empty node. Moving to its 'first child' is equivalent to just + // moving to the next sibling. + + // Debug.assert(fullWidth(nodeOrToken) === 0); + moveToNextSibling(); + } + + function moveToNextSibling(): void { + while (!isFinished()) { + // first look to our parent and see if it has a sibling of us that we can move to. + var currentPiece = pieces[currentPieceIndex]; + var parent = currentPiece.element.parent; + + // We start searching at the index one past our own index in the parent. + for (var i = currentPiece.indexInParent + 1, n = childCount(parent); i < n; i++) { + var sibling = childAt(parent, i); + + if (sibling !== null && !isShared(sibling)) { + // We found a good sibling that we can move to. Just reuse our existing piece + // so we don't have to push/pop. + currentPiece.element = sibling; + currentPiece.indexInParent = i; + + // The sibling might have been a list. Move to it's first child. it must have + // one since this was a non-shared element. + moveToFirstChildIfList(); + return; + } + } + + // Didn't have a sibling for this element. Go up to our parent and get its sibling. + + // Clear the data from the old piece. We don't want to keep any elements around + // unintentionally. + currentPiece.element = null; + currentPiece.indexInParent = -1; + + // Point at the parent. if we move past the top of the path, then we're finished. + currentPieceIndex--; + } + } + + function moveToFirstChildIfList(): void { + var element = pieces[currentPieceIndex].element; + + if (isList(element) || isSeparatedList(element)) { + // We cannot ever get an empty list in our piece path. Empty lists are 'shared' and + // we make sure to filter that out before pushing any children. + // Debug.assert(childCount(element) > 0); + + pushElement(childAt(element, 0), /*indexInParent:*/ 0); + } + } + + function pushElement(element: ISyntaxElement, indexInParent: number): void { + // Debug.assert(element !== null); + // Debug.assert(indexInParent >= 0); + currentPieceIndex++; + + // Reuse an existing piece if we have one. Otherwise, push a new piece to our list. + if (currentPieceIndex === pieces.length) { + pieces.push(createSyntaxCursorPiece(element, indexInParent)); + } + else { + var piece = pieces[currentPieceIndex]; + piece.element = element; + piece.indexInParent = indexInParent; + } + } + + function moveToFirstToken(): void { + while (!isFinished()) { + var element = pieces[currentPieceIndex].element; + if (isNode(element)) { + moveToFirstChild(); + continue; + } + + // Debug.assert(isToken(element)); + return; + } + } + + function currentToken(): ISyntaxToken { + moveToFirstToken(); + + var element = currentNodeOrToken(); + // Debug.assert(element === null || element.isToken()); + return element === null ? null : element; + } + + return { + pieces: pieces, + clean: clean, + isFinished: isFinished, + moveToFirstChild: moveToFirstChild, + moveToFirstToken: moveToFirstToken, + moveToNextSibling: moveToNextSibling, + currentNodeOrToken: currentNodeOrToken, + currentNode: currentNode, + currentToken: currentToken, + pushElement: pushElement, + deepCopyFrom: deepCopyFrom + }; + } + + // A simple walker we use to hit all the tokens of a node and update their positions when they + // are reused in a different location because of an incremental parse. + + class SetTokenFullStartWalker extends SyntaxWalker { + public position: number; + + public visitToken(token: ISyntaxToken): void { + var position = this.position; + token.setFullStart(position); + + this.position = position + token.fullWidth(); + } + } + + var setTokenFullStartWalker = new SetTokenFullStartWalker(); + + export function parse(oldSyntaxTree: SyntaxTree, textChangeRange: TextChangeRange, newText: ISimpleText): SyntaxTree { + Debug.assert(oldSyntaxTree.isConcrete(), "Can only incrementally parse a concrete syntax tree."); + if (textChangeRange.isUnchanged()) { + return oldSyntaxTree; + } + + return Parser.parseSource(createParserSource(oldSyntaxTree, textChangeRange, newText), oldSyntaxTree.isDeclaration()); + } +} \ No newline at end of file diff --git a/src/services/syntax/languageVersion.ts b/src/services/syntax/languageVersion.ts new file mode 100644 index 00000000000..0c662a0307c --- /dev/null +++ b/src/services/syntax/languageVersion.ts @@ -0,0 +1,3 @@ +module TypeScript { + +} \ No newline at end of file diff --git a/src/services/syntax/parser.ts b/src/services/syntax/parser.ts new file mode 100644 index 00000000000..0c886f129fc --- /dev/null +++ b/src/services/syntax/parser.ts @@ -0,0 +1,4385 @@ +/// + +module TypeScript.Parser { + // The factory used to produce parse tree nodes. Injected normally by the + // TypeScript.Syntax.Abstract or TypeScript.Syntax.Conrete modules. + export var syntaxFactory: Syntax.ISyntaxFactory; + + // Interface that represents the source that the parser pulls tokens from. Essentially, this + // is the interface that the parser needs an underlying scanner to provide. This allows us to + // separate out "what" the parser does with the tokens it retrieves versus "how" it obtains + // the tokens. i.e. all the logic for parsing language constructs sits in ParserImpl, while + // all the logic for retrieving tokens sits in individual IParserSources. + // + // By separating out this interface, we also make incremental parsing much easier. Instead of + // having the parser directly sit on top of the scanner, we sit it on this abstraction. Then + // in incremental scenarios, we can use the IncrementalParserSource to pull tokens (or even + // full nodes) from the previous tree when possible. Of course, we'll still end up using a + // scanner for new text. But that can all happen inside the source, with none of the logic in + // the parser having to be aware of it. + // + // In general terms, a parser source represents a position within a text. At that position, + // one can ask for the 'currentToken' that the source is pointing at. Then, once the parser + // consumes that token it can ask the source to 'moveToNextToken'. + // + // Additional special abilities include: + // 1) Being able to peek an arbitrary number of tokens ahead efficiently. + // 2) Being able to retrieve fully parsed nodes from the source, not just tokens. This happens + // in incremental scenarios when the source is certain that the node is completley safe to + // reuse. + // 3) Being able to get a 'rewind point' to the current location. This allows the parser to + // speculatively parse as much as it wants, and then reset itself back to that point, + // ensuring that no state changes that occurred after getting the 'rewing point' are + // observable. + // 4) Being able to reinterpret the current token being pointed at as a regular expression + // token. This is necessary as the scanner does not have enough information to correctly + // distinguish "/" or "/=" as divide tokens, versus "/..../" as a regex token. If the + // parser sees a "/" in a place where a divide is not allowed, but a regex would be, then + // it can call into the source and ask if a regex token could be returned instead. The + // sources are smart enough to do that and not be affected by any additional work they may + // have done when they originally scanned that token. + export interface IParserSource { + // The text we are parsing. + text: ISimpleText; + + // the name of the file we're parsing. + fileName: string; + + // The version of the language we're using while parsing. Does not affect the final tree, + // but can affect the diagnostics produced while parsing. + languageVersion: ts.ScriptTarget; + + // The current syntax node the source is pointing at. Only available in incremental settings. + // The source can point at a node if that node doesn't intersect any of the text changes in + // the file, and doesn't contain certain unacceptable constructs. For example, if the node + // contains skipped text, then it will not be reused. + currentNode(): ISyntaxNode; + + // The current token the source is pointing at. + currentToken(): ISyntaxToken; + + // The current token reinterpretted contextually based on where the parser is. If the + // source is on a / or /= token, then it can be reinterpretted as a regex token. If the + // source is on a > token, it may be reinterpretted to: >> >>> >= >>= >>>= + currentContextualToken(): ISyntaxToken; + + // Peek any number of tokens ahead from the current location in source. peekToken(0) is + // equivalent to 'currentToken', peekToken(1) is the next token, peekToken(2) the token + // after that, etc. If the caller peeks past the end of the text, then EndOfFile tokens + // will be returned. + peekToken(n: number): ISyntaxToken; + + // Called to move the source to the next node or token once the parser has consumed the + // current one. + consumeNode(node: ISyntaxNode): void; + consumeToken(token: ISyntaxToken): void; + + // Gets a rewind point that the parser can use to move back to after it speculatively + // parses something. The source guarantees that if the parser calls 'rewind' with that + // point that it will be mostly in the same state that it was in when 'getRewindPoint' + // was called. i.e. calling currentToken, peekToken, tokenDiagnostics, etc. will result + // in the same values. One allowed exemption to this is 'currentNode'. If a rewind point + // is requested and rewound, then getting the currentNode may not be possible. However, + // as this is purely a performance optimization, it will not affect correctness. + // + // Note: that rewind points are not free (but they should also not be too expensive). So + // they should be used judiciously. While a rewind point is held by the parser, the source + // is not free to do things that it would normally do. For example, it cannot throw away + // tokens that it has scanned on or after the rewind point as it must keep them alive for + // the parser to move back to. + // + // Rewind points also work in a stack fashion. The first rewind point given out must be + // the last rewind point released. Do not release them out of order, or bad things can + // happen. + // + // Do *NOT* forget to release a rewind point. Always put them in a finally block to ensure + // that they are released. If they are not released, things will still work, you will just + // consume far more memory than necessary. + getRewindPoint(): IRewindPoint; + + // Rewinds the source to the position and state it was at when this rewind point was created. + // This does not need to be called if the parser decides it does not need to rewind. For + // example, the parser may speculatively parse out a lambda expression when it sees something + // ambiguous like "(a = b, c = ...". If it succeeds parsing that as a lambda, then it will + // just return that result. However, if it fails *then* it will rewind and try it again as + // a parenthesized expression. + rewind(rewindPoint: IRewindPoint): void; + + // Called when the parser is done speculative parsing and no longer needs the rewind point. + // Must be called for every rewind point retrived. + releaseRewindPoint(rewindPoint: IRewindPoint): void; + + // Retrieves the diagnostics generated while the source was producing nodes or tokens. + // Should generally only be called after the document has been completely parsed. + tokenDiagnostics(): Diagnostic[]; + + release(): void; + } + + // Information the parser needs to effectively rewind. + export interface IRewindPoint { + } + + var arrayPool: any[][] = []; + var arrayPoolCount: number = 0; + + function getArray(): any[] { + if (arrayPoolCount === 0) { + return []; + } + + arrayPoolCount--; + var result = arrayPool[arrayPoolCount]; + arrayPool[arrayPoolCount] = null; + + return result; + } + + function returnZeroLengthArray(array: any[]) { + if (array.length === 0) { + returnArray(array); + } + } + + function returnArray(array: any[]) { + array.length = 0; + arrayPool[arrayPoolCount] = array; + arrayPoolCount++; + } + + interface IParserRewindPoint extends IRewindPoint { + // As we speculatively parse, we may build up diagnostics. When we rewind we want to + // 'forget' that information.In order to do that we store the count of diagnostics and + // when we start speculating, and we reset to that count when we're done. That way the + // speculative parse does not affect any further results. + diagnosticsCount: number; + + // isInStrictMode and listParsingState should not have to be tracked by a rewind point. + // Because they are naturally mutated and restored based on the normal stack movement of + // the parser, they should automatically return to whatever value they had to begin with + // if the parser decides to rewind or not. However, to ensure that this is true, we track + // these variables and check if they have the same value when we're rewinding/releasing. + isInStrictMode: boolean; + listParsingState: ListParsingState; + } + + // Contains the actual logic to parse typescript/javascript. This is the code that generally + // represents the logic necessary to handle all the language grammar constructs. When the + // language changes, this should generally only be the place necessary to fix up. + function createParseSyntaxTree(): (source: IParserSource, isDeclaration: boolean) => SyntaxTree { + // Name of the file we're parsing. + var fileName: string; + + // Underlying source where we pull nodes and tokens from. + var source: IParserSource; + + var languageVersion: ts.ScriptTarget; + + // TODO: do we need to store/restore this when speculative parsing? I don't think so. The + // parsing logic already handles storing/restoring this and should work properly even if we're + // speculative parsing. + var listParsingState: number = 0; + + // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is + // that some tokens that would be considered identifiers may be considered keywords. When + // rewinding, we need to store and restore this as the mode may have changed. + // + // TODO: do we need to store/restore this when speculative parsing? I don't think so. The + // parsing logic already handles storing/restoring this and should work properly even if we're + // speculative parsing. + var isInStrictMode: boolean = false; + + // Current state of the parser. If we need to rewind we will store and reset these values as + // appropriate. + + // Diagnostics created when parsing invalid code. Any diagnosics created when speculative + // parsing need to removed when rewinding. To do this we store the count of diagnostics when + // we start speculative parsing. And if we rewind, we restore this to the same count that we + // started at. + var diagnostics: Diagnostic[] = []; + + var parseNodeData: number = 0; + + function parseSyntaxTree(_source: IParserSource, isDeclaration: boolean): SyntaxTree { + // First, set up our state. + fileName = _source.fileName; + source = _source; + languageVersion = source.languageVersion; + + // Now actually parse the tree. + var result = parseSyntaxTreeWorker(isDeclaration); + + // Now, clear out our state so that our singleton parser doesn't keep things alive. + diagnostics = []; + parseNodeData = SyntaxConstants.None; + fileName = null; + source.release(); + source = null; _source = null; + + return result; + } + + function parseSyntaxTreeWorker(isDeclaration: boolean): SyntaxTree { + var sourceUnit = parseSourceUnit(); + + var allDiagnostics = source.tokenDiagnostics().concat(diagnostics); + allDiagnostics.sort((a: Diagnostic, b: Diagnostic) => a.start() - b.start()); + + return new SyntaxTree(syntaxFactory.isConcrete, sourceUnit, isDeclaration, allDiagnostics, fileName, source.text, languageVersion); + } + + function getRewindPoint(): IParserRewindPoint { + var rewindPoint = source.getRewindPoint(); + + rewindPoint.diagnosticsCount = diagnostics.length; + + // Values we keep around for debug asserting purposes. + rewindPoint.isInStrictMode = isInStrictMode; + rewindPoint.listParsingState = listParsingState; + + return rewindPoint; + } + + function rewind(rewindPoint: IParserRewindPoint): void { + source.rewind(rewindPoint); + + diagnostics.length = rewindPoint.diagnosticsCount; + } + + function releaseRewindPoint(rewindPoint: IParserRewindPoint): void { + // Debug.assert(listParsingState === rewindPoint.listParsingState); + // Debug.assert(isInStrictMode === rewindPoint.isInStrictMode); + + source.releaseRewindPoint(rewindPoint); + } + + function currentNode(): ISyntaxNode { + var node = source.currentNode(); + + // We can only reuse a node if it was parsed under the same strict mode that we're + // currently in. i.e. if we originally parsed a node in non-strict mode, but then + // the user added 'using strict' at the top of the file, then we can't use that node + // again as the presense of strict mode may cause us to parse the tokens in the file + // differetly. + // + // Note: we *can* reuse tokens when the strict mode changes. That's because tokens + // are unaffected by strict mode. It's just the parser will decide what to do with it + // differently depending on what mode it is in. + if (node === null || parsedInStrictMode(node) !== isInStrictMode) { + return null; + } + + return node; + } + + function currentToken(): ISyntaxToken { + return source.currentToken(); + } + + function currentContextualToken(): ISyntaxToken { + // We're mutating the source here. We are potentially overwriting the original token we + // scanned with a regex token. So we have to clear our state. + return source.currentContextualToken(); + } + + function peekToken(n: number): ISyntaxToken { + return source.peekToken(n); + } + + function consumeToken(token: ISyntaxToken): ISyntaxToken { + source.consumeToken(token); + return token; + } + + function consumeNode(node: ISyntaxNode): void { + source.consumeNode(node); + } + + //this method is called very frequently + //we should keep it simple so that it can be inlined. + function eatToken(kind: SyntaxKind): ISyntaxToken { + var token = currentToken(); + if (token.kind() === kind) { + return consumeToken(token); + } + + //slow part of EatToken(SyntaxKind kind) + return createMissingToken(kind, token); + } + + // Eats the token if it is there. Otherwise does nothing. Will not report errors. + function tryEatToken(kind: SyntaxKind): ISyntaxToken { + var _currentToken = currentToken(); + if (_currentToken.kind() === kind) { + return consumeToken(_currentToken); + } + + return null; + } + + // An identifier is basically any word, unless it is a reserved keyword. so 'foo' is an + // identifier and 'return' is not. Note: a word may or may not be an identifier depending + // on the state of the parser. For example, 'yield' is an identifier *unless* the parser + // is in strict mode. + function isIdentifier(token: ISyntaxToken): boolean { + var tokenKind = token.kind(); + + if (tokenKind === SyntaxKind.IdentifierName) { + return true; + } + + // Keywords are only identifiers if they're FutureReservedStrictWords and we're in + // strict mode. *Or* if it's a typescript 'keyword'. + if (tokenKind >= SyntaxKind.FirstFutureReservedStrictKeyword) { + if (tokenKind <= SyntaxKind.LastFutureReservedStrictKeyword) { + // Could be a keyword or identifier. It's an identifier if we're not in strict + // mode. + return !isInStrictMode; + } + + // If it's typescript keyword, then it's actually a javascript identifier. + return tokenKind <= SyntaxKind.LastTypeScriptKeyword; + } + + // Anything else is not an identifier. + return false; + } + + // This method should be called when the grammar calls for an *IdentifierName* and not an + // *Identifier*. + function eatIdentifierNameToken(): ISyntaxToken { + var token = currentToken(); + + // If we have an identifier name, then consume and return it. + var tokenKind = token.kind(); + if (tokenKind === SyntaxKind.IdentifierName) { + return consumeToken(token); + } + + // If we have a keyword, then it can be used as an identifier name. However, we need + // to convert it to an identifier so that no later parts of the systems see it as a + // keyword. + if (SyntaxFacts.isAnyKeyword(tokenKind)) { + return TypeScript.Syntax.convertKeywordToIdentifier(consumeToken(token)); + } + + return createMissingToken(SyntaxKind.IdentifierName, token); + } + + function eatOptionalIdentifierToken(): ISyntaxToken { + return isIdentifier(currentToken()) ? eatIdentifierToken() : null; + } + + // This method should be called when the grammar calls for an *Identifier* and not an + // *IdentifierName*. + function eatIdentifierToken(diagnosticCode?: string): ISyntaxToken { + var token = currentToken(); + if (isIdentifier(token)) { + consumeToken(token); + + if (token.kind() === SyntaxKind.IdentifierName) { + return token; + } + + return TypeScript.Syntax.convertKeywordToIdentifier(token); + } + + return createMissingToken(SyntaxKind.IdentifierName, token, diagnosticCode); + } + + function previousTokenHasTrailingNewLine(token: ISyntaxToken): boolean { + var tokenFullStart = token.fullStart(); + if (tokenFullStart === 0) { + // First token in the document. Thus it has no 'previous' token, and there is + // no preceding newline. + return false; + } + + // If our previous token ended with a newline, then *by definition* we must have started + // at the beginning of a line. + var lineNumber = source.text.lineMap().getLineNumberFromPosition(tokenFullStart); + var lineStart = source.text.lineMap().getLineStartPosition(lineNumber); + + return lineStart == tokenFullStart; + } + + function canEatAutomaticSemicolon(allowWithoutNewLine: boolean): boolean { + var token = currentToken(); + + // An automatic semicolon is always allowed if we're at the end of the file. + var tokenKind = token.kind(); + if (tokenKind === SyntaxKind.EndOfFileToken) { + return true; + } + + // Or if the next token is a close brace (regardless of which line it is on). + if (tokenKind === SyntaxKind.CloseBraceToken) { + return true; + } + + if (allowWithoutNewLine) { + return true; + } + + // It is also allowed if there is a newline between the last token seen and the next one. + if (previousTokenHasTrailingNewLine(token)) { + return true; + } + + return false; + } + + function canEatExplicitOrAutomaticSemicolon(allowWithoutNewline: boolean): boolean { + var token = currentToken(); + + if (token.kind() === SyntaxKind.SemicolonToken) { + return true; + } + + return canEatAutomaticSemicolon(allowWithoutNewline); + } + + function eatExplicitOrAutomaticSemicolon(allowWithoutNewline: boolean): ISyntaxToken { + var token = currentToken(); + + // If we see a semicolon, then we can definitely eat it. + if (token.kind() === SyntaxKind.SemicolonToken) { + return consumeToken(token); + } + + // Check if an automatic semicolon could go here. If so, then there's no problem and + // we can proceed without error. Return 'null' as there's no actual token for this + // position. + if (canEatAutomaticSemicolon(allowWithoutNewline)) { + return null; + } + + // No semicolon could be consumed here at all. Just call the standard eating function + // so we get the token and the error for it. + return eatToken(SyntaxKind.SemicolonToken); + } + + function createMissingToken(expectedKind: SyntaxKind, actual: ISyntaxToken, diagnosticCode?: string): ISyntaxToken { + var diagnostic = getExpectedTokenDiagnostic(expectedKind, actual, diagnosticCode); + addDiagnostic(diagnostic); + + // The missing token will be at the full start of the current token. That way empty tokens + // will always be between real tokens and not inside an actual token. + return Syntax.emptyToken(expectedKind); + } + + function getExpectedTokenDiagnostic(expectedKind: SyntaxKind, actual: ISyntaxToken, diagnosticCode: string): Diagnostic { + var token = currentToken(); + + var args: any[] = null; + // If a specialized diagnostic message was provided, just use that. + if (!diagnosticCode) { + // They wanted something specific, just report that that token was missing. + if (SyntaxFacts.isAnyKeyword(expectedKind) || SyntaxFacts.isAnyPunctuation(expectedKind)) { + diagnosticCode = DiagnosticCode._0_expected; + args = [SyntaxFacts.getText(expectedKind)]; + } + else { + // They wanted an identifier. + + // If the user supplied a keyword, give them a specialized message. + if (actual !== null && SyntaxFacts.isAnyKeyword(actual.kind())) { + diagnosticCode = DiagnosticCode.Identifier_expected_0_is_a_keyword; + args = [SyntaxFacts.getText(actual.kind())]; + } + else { + // Otherwise just report that an identifier was expected. + diagnosticCode = DiagnosticCode.Identifier_expected; + } + } + } + + return new Diagnostic(fileName, source.text.lineMap(), start(token, source.text), width(token), diagnosticCode, args); + } + + function getBinaryExpressionPrecedence(tokenKind: SyntaxKind): BinaryExpressionPrecedence { + switch (tokenKind) { + case SyntaxKind.BarBarToken: return BinaryExpressionPrecedence.LogicalOrExpressionPrecedence; + case SyntaxKind.AmpersandAmpersandToken: return BinaryExpressionPrecedence.LogicalAndExpressionPrecedence; + case SyntaxKind.BarToken: return BinaryExpressionPrecedence.BitwiseOrExpressionPrecedence; + case SyntaxKind.CaretToken: return BinaryExpressionPrecedence.BitwiseExclusiveOrExpressionPrecedence; + case SyntaxKind.AmpersandToken: return BinaryExpressionPrecedence.BitwiseAndExpressionPrecedence; + case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.ExclamationEqualsToken: + case SyntaxKind.EqualsEqualsEqualsToken: + case SyntaxKind.ExclamationEqualsEqualsToken: + return BinaryExpressionPrecedence.EqualityExpressionPrecedence; + case SyntaxKind.LessThanToken: + case SyntaxKind.GreaterThanToken: + case SyntaxKind.LessThanEqualsToken: + case SyntaxKind.GreaterThanEqualsToken: + case SyntaxKind.InstanceOfKeyword: + case SyntaxKind.InKeyword: + return BinaryExpressionPrecedence.RelationalExpressionPrecedence; + case SyntaxKind.LessThanLessThanToken: + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + return BinaryExpressionPrecedence.ShiftExpressionPrecdence; + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + return BinaryExpressionPrecedence.AdditiveExpressionPrecedence; + case SyntaxKind.AsteriskToken: + case SyntaxKind.SlashToken: + case SyntaxKind.PercentToken: + return BinaryExpressionPrecedence.MultiplicativeExpressionPrecedence; + } + + throw Errors.invalidOperation(); + } + + function addSkippedTokenAfterNodeOrToken(nodeOrToken: ISyntaxNodeOrToken, skippedToken: ISyntaxToken): ISyntaxNodeOrToken { + if (isToken(nodeOrToken)) { + return addSkippedTokenAfterToken(nodeOrToken, skippedToken); + } + else if (isNode(nodeOrToken)) { + return addSkippedTokenAfterNode(nodeOrToken, skippedToken); + } + else { + throw Errors.invalidOperation(); + } + } + + function replaceTokenInParent(oldToken: ISyntaxToken, newToken: ISyntaxToken): void { + // oldToken may be parented by a node or a list. + replaceTokenInParentWorker(oldToken, newToken); + + var parent = oldToken.parent; + newToken.parent = parent; + + // Parent must be a list or a node. All of those have a 'data' element. + Debug.assert(isNode(parent) || isList(parent) || isSeparatedList(parent)); + var dataElement = <{ data: number }>parent; + if (dataElement.data) { + dataElement.data &= SyntaxConstants.NodeParsedInStrictModeMask + } + } + + function replaceTokenInParentWorker(oldToken: ISyntaxToken, newToken: ISyntaxToken): void { + var parent = oldToken.parent; + + if (isNode(parent)) { + var node = parent; + for (var key in node) { + if (node[key] === oldToken) { + node[key] = newToken; + return; + } + } + } + else if (isList(parent)) { + var list1 = parent; + for (var i = 0, n = list1.length; i < n; i++) { + if (list1[i] === oldToken) { + list1[i] = newToken; + return; + } + } + } + else if (isSeparatedList(parent)) { + var list2 = parent; + for (var i = 0, n = childCount(list2); i < n; i++) { + if (childAt(list2, i) === oldToken) { + if (i % 2 === 0) { + list2[i / 2] = newToken; + } + else { + list2.separators[(i - 1) / 2] = newToken; + } + return; + } + } + } + + throw Errors.invalidOperation(); + } + + function addSkippedTokenAfterNode(node: ISyntaxNode, skippedToken: ISyntaxToken): ISyntaxNode { + var oldToken = lastToken(node); + var newToken = addSkippedTokenAfterToken(oldToken, skippedToken); + + replaceTokenInParent(oldToken, newToken); + return node; + } + + function addSkippedTokensBeforeNode(node: ISyntaxNode, skippedTokens: ISyntaxToken[]): ISyntaxNode { + if (skippedTokens.length > 0) { + var oldToken = firstToken(node); + var newToken = addSkippedTokensBeforeToken(oldToken, skippedTokens); + + replaceTokenInParent(oldToken, newToken); + } + + return node; + } + + function addSkippedTokensBeforeToken(token: ISyntaxToken, skippedTokens: ISyntaxToken[]): ISyntaxToken { + // Debug.assert(token.fullWidth() > 0 || token.kind() === SyntaxKind.EndOfFileToken); + // Debug.assert(skippedTokens.length > 0); + + var leadingTrivia: ISyntaxTrivia[] = []; + for (var i = 0, n = skippedTokens.length; i < n; i++) { + var skippedToken = skippedTokens[i]; + addSkippedTokenToTriviaArray(leadingTrivia, skippedToken); + } + + addTriviaTo(token.leadingTrivia(source.text), leadingTrivia); + + var updatedToken = Syntax.withLeadingTrivia(token, Syntax.triviaList(leadingTrivia), source.text); + + // We've prepending this token with new leading trivia. This means the full start of + // the token is not where the scanner originally thought it was, but is instead at the + // start of the first skipped token. + updatedToken.setFullStart(skippedTokens[0].fullStart()); + + // Don't need this array anymore. Give it back so we can reuse it. + returnArray(skippedTokens); + + return updatedToken; + } + + function addSkippedTokensAfterToken(token: ISyntaxToken, skippedTokens: ISyntaxToken[]): ISyntaxToken { + // Debug.assert(token.fullWidth() > 0); + if (skippedTokens.length === 0) { + returnArray(skippedTokens); + return token; + } + + var trailingTrivia = token.trailingTrivia(source.text).toArray(); + + for (var i = 0, n = skippedTokens.length; i < n; i++) { + addSkippedTokenToTriviaArray(trailingTrivia, skippedTokens[i]); + } + + // Don't need this array anymore. Give it back so we can reuse it. + returnArray(skippedTokens); + return Syntax.withTrailingTrivia(token, Syntax.triviaList(trailingTrivia), source.text); + } + + function addSkippedTokenAfterToken(token: ISyntaxToken, skippedToken: ISyntaxToken): ISyntaxToken { + // Debug.assert(token.fullWidth() > 0); + var trailingTrivia = token.trailingTrivia(source.text).toArray(); + addSkippedTokenToTriviaArray(trailingTrivia, skippedToken); + + return Syntax.withTrailingTrivia(token, Syntax.triviaList(trailingTrivia), source.text); + } + + function addSkippedTokenToTriviaArray(array: ISyntaxTrivia[], skippedToken: ISyntaxToken): void { + // Debug.assert(skippedToken.text().length > 0); + + // first, add the leading trivia of the skipped token to the array + addTriviaTo(skippedToken.leadingTrivia(source.text), array); + + // now, add the text of the token as skipped text to the trivia array. + var trimmedToken = Syntax.withTrailingTrivia(Syntax.withLeadingTrivia(skippedToken, Syntax.emptyTriviaList, source.text), Syntax.emptyTriviaList, source.text); + + // Because we removed the leading trivia from the skipped token, the full start of the + // trimmed token is the start of the skipped token. + trimmedToken.setFullStart(start(skippedToken, source.text)); + + array.push(Syntax.skippedTokenTrivia(trimmedToken, source.text)); + + // Finally, add the trailing trivia of the skipped token to the trivia array. + addTriviaTo(skippedToken.trailingTrivia(source.text), array); + } + + function addTriviaTo(list: ISyntaxTriviaList, array: ISyntaxTrivia[]): void { + for (var i = 0, n = list.count(); i < n; i++) { + array.push(list.syntaxTriviaAt(i)); + } + } + + function setStrictMode(_isInStrictMode: boolean) { + isInStrictMode = _isInStrictMode; + parseNodeData = _isInStrictMode ? SyntaxConstants.NodeParsedInStrictModeMask : 0; + } + + function parseSourceUnit(): SourceUnitSyntax { + var savedIsInStrictMode = isInStrictMode + + var skippedTokens: ISyntaxToken[] = getArray(); + var moduleElements = parseSyntaxList(ListParsingState.SourceUnit_ModuleElements, skippedTokens, updateStrictModeState); + + setStrictMode(savedIsInStrictMode); + + var sourceUnit = new syntaxFactory.SourceUnitSyntax(parseNodeData, moduleElements, currentToken()); + + sourceUnit = addSkippedTokensBeforeNode(sourceUnit, skippedTokens); + + if (Debug.shouldAssert(AssertionLevel.Aggressive)) { + Debug.assert(fullWidth(sourceUnit) === source.text.length()); + + if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) { + Debug.assert(fullText(sourceUnit) === source.text.substr(0, source.text.length())); + } + } + + return sourceUnit; + } + + function updateStrictModeState(items: any[]): void { + if (!isInStrictMode) { + // Check if all the items are directive prologue elements. + for (var i = 0; i < items.length; i++) { + var item = items[i]; + if (!SyntaxFacts.isDirectivePrologueElement(item)) { + return; + } + } + + setStrictMode(SyntaxFacts.isUseStrictDirective(items[items.length - 1])); + } + } + + function isModuleElement(inErrorRecovery: boolean): boolean { + if (SyntaxUtilities.isModuleElement(currentNode())) { + return true; + } + + var _modifierCount = modifierCount(); + return isInterfaceEnumClassModuleImportOrExport(_modifierCount) || + isStatement(_modifierCount, inErrorRecovery); + } + + function tryParseModuleElement(inErrorRecovery: boolean): IModuleElementSyntax { + var node = currentNode(); + if (SyntaxUtilities.isModuleElement(node)) { + consumeNode(node); + return node; + } + + var _currentToken = currentToken(); + var _modifierCount = modifierCount(); + + if (_modifierCount) { + // if we have modifiers, then these are definitely TS constructs and we can + // immediately start parsing them. + switch (peekToken(_modifierCount).kind()) { + case SyntaxKind.ImportKeyword: return parseImportDeclaration(); + case SyntaxKind.ModuleKeyword: return parseModuleDeclaration(); + case SyntaxKind.InterfaceKeyword: return parseInterfaceDeclaration(); + case SyntaxKind.ClassKeyword: return parseClassDeclaration(); + case SyntaxKind.EnumKeyword: return parseEnumDeclaration(); + } + } + + // No modifiers. If we see 'class, enum, import and export' we could technically + // aggressively consume them as they can't start another construct. However, it's + // not uncommon in error recovery to run into a situation where we see those keywords, + // but the code was using it as the name of an object property. To avoid overzealously + // consuming these, we only parse them out if we can see enough context to 'prove' that + // they really do start the module element + var nextToken = peekToken(1); + var currentTokenKind = _currentToken.kind(); + switch (currentTokenKind) { + case SyntaxKind.ModuleKeyword: + if (isIdentifier(nextToken) || nextToken.kind() === SyntaxKind.StringLiteral) { + return parseModuleDeclaration(); + } + break; + + case SyntaxKind.ImportKeyword: + if (isIdentifier(nextToken)) { + return parseImportDeclaration(); + } + break; + + case SyntaxKind.ClassKeyword: + if (isIdentifier(nextToken)) { + return parseClassDeclaration(); + } + break; + + case SyntaxKind.EnumKeyword: + if (isIdentifier(nextToken)) { + return parseEnumDeclaration(); + } + break; + + case SyntaxKind.InterfaceKeyword: + if (isIdentifier(nextToken)) { + return parseInterfaceDeclaration(); + } + break; + + case SyntaxKind.ExportKeyword: + // 'export' could be a modifier on a statement (like export var ...). So we + // only want to parse out an export assignment here if we actually see the equals. + if (nextToken.kind() === SyntaxKind.EqualsToken) { + return parseExportAssignment(); + } + break; + } + + return tryParseStatementWorker(_currentToken, currentTokenKind, _modifierCount, inErrorRecovery); + } + + function parseImportDeclaration(): ImportDeclarationSyntax { + return new syntaxFactory.ImportDeclarationSyntax(parseNodeData, + parseModifiers(), eatToken(SyntaxKind.ImportKeyword), eatIdentifierToken(), eatToken(SyntaxKind.EqualsToken), parseModuleReference(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseExportAssignment(): ExportAssignmentSyntax { + return new syntaxFactory.ExportAssignmentSyntax(parseNodeData, + eatToken(SyntaxKind.ExportKeyword), eatToken(SyntaxKind.EqualsToken), eatIdentifierToken(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseModuleReference(): IModuleReferenceSyntax { + return isExternalModuleReference() ? parseExternalModuleReference() : parseModuleNameModuleReference(); + } + + function isExternalModuleReference(): boolean { + return currentToken().kind() === SyntaxKind.RequireKeyword && + peekToken(1).kind() === SyntaxKind.OpenParenToken; + } + + function parseExternalModuleReference(): ExternalModuleReferenceSyntax { + return new syntaxFactory.ExternalModuleReferenceSyntax(parseNodeData, + eatToken(SyntaxKind.RequireKeyword), eatToken(SyntaxKind.OpenParenToken), eatToken(SyntaxKind.StringLiteral), eatToken(SyntaxKind.CloseParenToken)); + } + + function parseModuleNameModuleReference(): ModuleNameModuleReferenceSyntax { + return new syntaxFactory.ModuleNameModuleReferenceSyntax(parseNodeData, parseName(/*allowIdentifierNames:*/ false)); + } + + function tryParseTypeArgumentList(inExpression: boolean): TypeArgumentListSyntax { + var _currentToken = currentToken(); + if (_currentToken.kind() !== SyntaxKind.LessThanToken) { + return null; + } + + if (!inExpression) { + // if we're not in an expression, this must be a type argument list. Just parse + // it out as such. + var lessThanToken = consumeToken(_currentToken); + + var skippedTokens: ISyntaxToken[] = getArray(); + var typeArguments = parseSeparatedSyntaxList(ListParsingState.TypeArgumentList_Types, skippedTokens); + lessThanToken = addSkippedTokensAfterToken(lessThanToken, skippedTokens); + + return new syntaxFactory.TypeArgumentListSyntax(parseNodeData, lessThanToken, typeArguments, eatToken(SyntaxKind.GreaterThanToken)); + } + + // If we're in an expression, then we only want to consume this as a type argument list + // if we're sure that it's a type arg list and not an arithmetic expression. + + var rewindPoint = getRewindPoint(); + + // We've seen a '<'. Try to parse it out as a type argument list. + var lessThanToken = consumeToken(_currentToken); + + var skippedTokens: ISyntaxToken[] = getArray(); + var typeArguments = parseSeparatedSyntaxList(ListParsingState.TypeArgumentList_Types, skippedTokens); + var lessThanToken = addSkippedTokensAfterToken(lessThanToken, skippedTokens); + + var greaterThanToken = eatToken(SyntaxKind.GreaterThanToken); + + // We're in a context where '<' could be the start of a type argument list, or part + // of an arithmetic expression. We'll presume it's the latter unless we see the '>' + // and a following token that guarantees that it's supposed to be a type argument list. + if (greaterThanToken.fullWidth() === 0 || !canFollowTypeArgumentListInExpression(currentToken().kind())) { + rewind(rewindPoint); + releaseRewindPoint(rewindPoint); + return null; + } + else { + releaseRewindPoint(rewindPoint); + return new syntaxFactory.TypeArgumentListSyntax(parseNodeData, lessThanToken, typeArguments, greaterThanToken); + } + } + + function canFollowTypeArgumentListInExpression(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.OpenParenToken: // foo( + case SyntaxKind.DotToken: // foo. + // These two cases are the only cases where this token can legally follow a + // type argument list. So we definitely want to treat this as a type arg list. + + case SyntaxKind.CloseParenToken: // foo) + case SyntaxKind.CloseBracketToken: // foo] + case SyntaxKind.ColonToken: // foo: + case SyntaxKind.SemicolonToken: // foo; + case SyntaxKind.CommaToken: // foo, + case SyntaxKind.QuestionToken: // foo? + case SyntaxKind.EqualsEqualsToken: // foo == + case SyntaxKind.EqualsEqualsEqualsToken: // foo === + case SyntaxKind.ExclamationEqualsToken: // foo != + case SyntaxKind.ExclamationEqualsEqualsToken: // foo !== + case SyntaxKind.AmpersandAmpersandToken: // foo && + case SyntaxKind.BarBarToken: // foo || + case SyntaxKind.CaretToken: // foo ^ + case SyntaxKind.AmpersandToken: // foo & + case SyntaxKind.BarToken: // foo | + case SyntaxKind.CloseBraceToken: // foo } + case SyntaxKind.EndOfFileToken: // foo + // these cases can't legally follow a type arg list. However, they're not legal + // expressions either. The user is probably in the middle of a generic type. So + // treat it as such. + return true; + + default: + // Anything else treat as an expression. + return false; + } + } + + function parseName(allowIdentifierName: boolean): INameSyntax { + return tryParseName(allowIdentifierName) || eatIdentifierToken(); + } + + function eatRightSideOfName(allowIdentifierNames: boolean): ISyntaxToken { + var _currentToken = currentToken(); + + // Technically a keyword is valid here as all keywords are identifier names. + // However, often we'll encounter this in error situations when the keyword + // is actually starting another valid construct. + + // So, we check for the following specific case: + + // name. + // keyword identifierNameOrKeyword + + // Note: the newlines are important here. For example, if that above code + // were rewritten into: + + // name.keyword + // identifierNameOrKeyword + + // Then we would consider it valid. That's because ASI would take effect and + // the code would be implicitly: "name.keyword; identifierNameOrKeyword". + // In the first case though, ASI will not take effect because there is not a + // line terminator after the keyword. + if (SyntaxFacts.isAnyKeyword(_currentToken.kind()) && + previousTokenHasTrailingNewLine(_currentToken)) { + + var token1 = peekToken(1); + if (!existsNewLineBetweenTokens(_currentToken, token1, source.text) && + SyntaxFacts.isIdentifierNameOrAnyKeyword(token1)) { + + return createMissingToken(SyntaxKind.IdentifierName, _currentToken); + } + } + + return allowIdentifierNames ? eatIdentifierNameToken() : eatIdentifierToken(); + } + + function tryParseName(allowIdentifierNames: boolean): INameSyntax { + var token0 = currentToken(); + var shouldContinue = isIdentifier(token0); + if (!shouldContinue) { + return null; + } + + // Call eatIdentifierName to convert the token to an identifier if it is as keyword. + var current: INameSyntax = eatIdentifierToken(); + + while (shouldContinue && currentToken().kind() === SyntaxKind.DotToken) { + var dotToken = consumeToken(currentToken()); + var identifierName = eatRightSideOfName(allowIdentifierNames); + + current = new syntaxFactory.QualifiedNameSyntax(parseNodeData, current, dotToken, identifierName); + shouldContinue = identifierName.fullWidth() > 0; + } + + return current; + } + + function parseEnumDeclaration(): EnumDeclarationSyntax { + var modifiers = parseModifiers(); + var enumKeyword = eatToken(SyntaxKind.EnumKeyword); + var identifier = eatIdentifierToken(); + + var openBraceToken = eatToken(SyntaxKind.OpenBraceToken); + var enumElements = Syntax.emptySeparatedList(); + + if (openBraceToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + enumElements = parseSeparatedSyntaxList(ListParsingState.EnumDeclaration_EnumElements, skippedTokens); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + } + + return new syntaxFactory.EnumDeclarationSyntax(parseNodeData, modifiers, enumKeyword, identifier, openBraceToken, enumElements, eatToken(SyntaxKind.CloseBraceToken)); + } + + function isEnumElement(inErrorRecovery: boolean): boolean { + var node = currentNode(); + if (node !== null && node.kind() === SyntaxKind.EnumElement) { + return true; + } + + return isPropertyName(currentToken(), inErrorRecovery); + } + + function tryParseEnumElementEqualsValueClause(): EqualsValueClauseSyntax { + return isEqualsValueClause(/*inParameter*/ false) ? parseEqualsValueClause(/*allowIn:*/ true) : null; + } + + function tryParseEnumElement(inErrorRecovery: boolean): EnumElementSyntax { + var node = currentNode(); + if (node !== null && node.kind() === SyntaxKind.EnumElement) { + consumeNode(node); + return node; + } + + if (!isPropertyName(currentToken(), inErrorRecovery)) { + return null; + } + + return new syntaxFactory.EnumElementSyntax(parseNodeData, eatPropertyName(), tryParseEnumElementEqualsValueClause()); + } + + function isModifierKind(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.ExportKeyword: + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.DeclareKeyword: + return true; + } + + return false; + } + + function isModifier(token: ISyntaxToken, index: number): boolean { + if (isModifierKind(token.kind())) { + // These are modifiers only if we see an actual keyword, identifier, string literal + // or number following. + // Note: we also allow [ for error conditions. + // [ is for: static [a: number] + var nextToken = peekToken(index + 1); + var nextTokenKind = nextToken.kind(); + + switch (nextTokenKind) { + case SyntaxKind.IdentifierName: + case SyntaxKind.OpenBracketToken: + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + return true; + default: + return SyntaxFacts.isAnyKeyword(nextTokenKind); + } + } + + return false; + } + + function modifierCount(): number { + var modifierCount = 0; + while (isModifier(peekToken(modifierCount), modifierCount)) { + modifierCount++; + } + + return modifierCount; + } + + function parseModifiers(): ISyntaxToken[] { + var tokens: ISyntaxToken[] = getArray(); + + while (true) { + var token = currentToken(); + if (isModifier(token, /*index:*/ 0)) { + tokens.push(consumeToken(token)); + continue; + } + + break; + } + + var result = Syntax.list(tokens); + + // If the tokens array is greater than one, then we can't return it. It will have been + // copied directly into the syntax list. + returnZeroLengthArray(tokens); + + return result; + } + + function parseHeritageClauses(): HeritageClauseSyntax[] { + var heritageClauses = Syntax.emptyList(); + + if (isHeritageClause()) { + // NOTE: we can pass "null" for the skipped tokens here as we know we can't get + // any leading skipped tokens. We have an 'extends' or 'implements' keyword, so + // any skipped tokeds will get attached to that instead. + heritageClauses= parseSyntaxList(ListParsingState.ClassOrInterfaceDeclaration_HeritageClauses, null); + } + + return heritageClauses; + } + + function tryParseHeritageClauseTypeName(): ITypeSyntax { + return isHeritageClauseTypeName() ? tryParseNameOrGenericType() : null; + } + + function parseClassDeclaration(): ClassDeclarationSyntax { + var modifiers = parseModifiers(); + var classKeyword = eatToken(SyntaxKind.ClassKeyword); + var identifier = eatIdentifierToken(); + var typeParameterList = tryParseTypeParameterList(/*requireCompleteTypeParameterList:*/ false); + var heritageClauses = parseHeritageClauses(); + var openBraceToken = eatToken(SyntaxKind.OpenBraceToken); + var classElements = Syntax.emptyList(); + + if (openBraceToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + classElements = parseSyntaxList(ListParsingState.ClassDeclaration_ClassElements, skippedTokens); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + }; + + return new syntaxFactory.ClassDeclarationSyntax(parseNodeData, + modifiers, classKeyword, identifier, typeParameterList, heritageClauses, openBraceToken, classElements, eatToken(SyntaxKind.CloseBraceToken)); + } + + function isAccessor(modifierCount: number, inErrorRecovery: boolean): boolean { + var tokenKind = peekToken(modifierCount).kind(); + if (tokenKind !== SyntaxKind.GetKeyword && + tokenKind !== SyntaxKind.SetKeyword) { + return false; + } + + return isPropertyName(peekToken(modifierCount + 1), inErrorRecovery); + } + + function parseAccessor(checkForStrictMode: boolean): ISyntaxNode { + var modifiers = parseModifiers(); + var _currenToken = currentToken(); + var tokenKind = _currenToken.kind(); + + if (tokenKind === SyntaxKind.GetKeyword) { + return parseGetMemberAccessorDeclaration(modifiers, _currenToken, checkForStrictMode); + } + else if (tokenKind === SyntaxKind.SetKeyword) { + return parseSetMemberAccessorDeclaration(modifiers, _currenToken, checkForStrictMode); + } + else { + throw Errors.invalidOperation(); + } + } + + function parseGetMemberAccessorDeclaration(modifiers: ISyntaxToken[], getKeyword: ISyntaxToken, checkForStrictMode: boolean): GetAccessorSyntax { + return new syntaxFactory.GetAccessorSyntax(parseNodeData, + modifiers, consumeToken(getKeyword), eatPropertyName(), + parseCallSignature(/*requireCompleteTypeParameterList:*/ false), + parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, checkForStrictMode)); + } + + function parseSetMemberAccessorDeclaration(modifiers: ISyntaxToken[], setKeyword: ISyntaxToken, checkForStrictMode: boolean): SetAccessorSyntax { + return new syntaxFactory.SetAccessorSyntax(parseNodeData, + modifiers, consumeToken(setKeyword), eatPropertyName(), + parseCallSignature(/*requireCompleteTypeParameterList:*/ false), + parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, checkForStrictMode)); + } + + function isClassElement(inErrorRecovery: boolean): boolean { + if (SyntaxUtilities.isClassElement(currentNode())) { + return true; + } + + // Note: the order of these calls is important. Specifically, isMemberVariableDeclaration + // checks for a subset of the conditions of the previous two calls. + var _modifierCount = modifierCount(); + return isConstructorDeclaration(_modifierCount) || + isMemberFunctionDeclaration(_modifierCount, inErrorRecovery) || + isAccessor(_modifierCount, inErrorRecovery) || + isMemberVariableDeclaration(_modifierCount, inErrorRecovery) || + isIndexMemberDeclaration(_modifierCount); + } + + function tryParseClassElement(inErrorRecovery: boolean): IClassElementSyntax { + var node = currentNode(); + if (SyntaxUtilities.isClassElement(node)) { + consumeNode(node); + return node; + } + + var _modifierCount = modifierCount(); + if (isConstructorDeclaration(_modifierCount)) { + return parseConstructorDeclaration(); + } + else if (isMemberFunctionDeclaration(_modifierCount, inErrorRecovery)) { + return parseMemberFunctionDeclaration(); + } + else if (isAccessor(_modifierCount, inErrorRecovery)) { + return parseAccessor(/*checkForStrictMode:*/ false); + } + else if (isMemberVariableDeclaration(_modifierCount, inErrorRecovery)) { + return parseMemberVariableDeclaration(); + } + else if (isIndexMemberDeclaration(_modifierCount)) { + return parseIndexMemberDeclaration(); + } + else { + return null; + } + } + + function isConstructorDeclaration(modifierCount: number): boolean { + // Note: we deviate slightly from the spec here. If we see 'constructor' then we + // assume this is a constructor. That means, if a user writes "public constructor;" + // it won't be viewed as a member. As a workaround, they can simply write: + // public 'constructor'; + return peekToken(modifierCount).kind() === SyntaxKind.ConstructorKeyword; + } + + function parseConstructorDeclaration(): ConstructorDeclarationSyntax { + var modifiers = parseModifiers(); + var constructorKeyword = eatToken(SyntaxKind.ConstructorKeyword); + var callSignature = parseCallSignature(/*requireCompleteTypeParameterList:*/ false); + + var semicolonToken: ISyntaxToken = null; + var block: BlockSyntax = null; + + if (isBlock()) { + block = parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ true); + } + else { + semicolonToken = eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false); + } + + return new syntaxFactory.ConstructorDeclarationSyntax(parseNodeData, modifiers, constructorKeyword, callSignature, block, semicolonToken); + } + + function isMemberFunctionDeclaration(modifierCount: number, inErrorRecovery: boolean): boolean { + return isPropertyName(peekToken(modifierCount), inErrorRecovery) && isCallSignature(modifierCount + 1); + } + + function parseMemberFunctionDeclaration(): MemberFunctionDeclarationSyntax { + var modifiers = parseModifiers(); + var propertyName = eatPropertyName(); + var callSignature = parseCallSignature(/*requireCompleteTypeParameterList:*/ false); + + // If we got an errant => then we want to parse what's coming up without requiring an + // open brace. + var parseBlockEvenWithNoOpenBrace = tryAddUnexpectedEqualsGreaterThanToken(callSignature); + + var block: BlockSyntax = null; + var semicolon: ISyntaxToken = null; + + if (parseBlockEvenWithNoOpenBrace || isBlock()) { + block = parseBlock(parseBlockEvenWithNoOpenBrace, /*checkForStrictMode:*/ true); + } + else { + semicolon = eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false); + } + + return new syntaxFactory.MemberFunctionDeclarationSyntax(parseNodeData, modifiers, propertyName, callSignature, block, semicolon); + } + + function isDefinitelyMemberVariablePropertyName(index: number): boolean { + // keywords are also property names. Only accept a keyword as a property + // name if is of the form: + // public; + // public= + // public: + // public } + // public + // public + if (SyntaxFacts.isAnyKeyword(peekToken(index).kind())) { + var nextToken = peekToken(index + 1); + switch (nextToken.kind()) { + case SyntaxKind.SemicolonToken: + case SyntaxKind.EqualsToken: + case SyntaxKind.ColonToken: + case SyntaxKind.CloseBraceToken: + case SyntaxKind.EndOfFileToken: + return true; + default: + return previousTokenHasTrailingNewLine(nextToken); + } + } + else { + // If was a property name and not a keyword, then we're good to go. + return true; + } + } + + function isMemberVariableDeclaration(modifierCount: number, inErrorRecover: boolean): boolean { + return isPropertyName(peekToken(modifierCount), inErrorRecover) && isDefinitelyMemberVariablePropertyName(modifierCount); + } + + function parseMemberVariableDeclaration(): MemberVariableDeclarationSyntax { + return new syntaxFactory.MemberVariableDeclarationSyntax(parseNodeData, + parseModifiers(), + tryParseVariableDeclarator(/*allowIn:*/ true, /*allowPropertyName:*/ true), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function isIndexMemberDeclaration(modifierCount: number): boolean { + return isIndexSignature(modifierCount); + } + + function parseIndexMemberDeclaration(): IndexMemberDeclarationSyntax { + return new syntaxFactory.IndexMemberDeclarationSyntax(parseNodeData, + parseModifiers(), parseIndexSignature(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewLine:*/ false)); + } + + function tryAddUnexpectedEqualsGreaterThanToken(callSignature: CallSignatureSyntax): boolean { + var token0 = currentToken(); + + var hasEqualsGreaterThanToken = token0.kind() === SyntaxKind.EqualsGreaterThanToken; + if (hasEqualsGreaterThanToken) { + // We can only do this if the call signature actually contains a final token that we + // could add the => to. + var _lastToken = lastToken(callSignature); + if (_lastToken && _lastToken.fullWidth() > 0) { + // Previously the language allowed "function f() => expr;" as a shorthand for + // "function f() { return expr; }. + // + // Detect if the user is typing this and attempt recovery. + var diagnostic = new Diagnostic(fileName, source.text.lineMap(), + start(token0, source.text), width(token0), DiagnosticCode.Unexpected_token_0_expected, [SyntaxFacts.getText(SyntaxKind.OpenBraceToken)]); + addDiagnostic(diagnostic); + + consumeToken(token0); + + // Note: we only do this if we're creating a concrete syntax tree (which contains + // everything, including skipped tokens, in it). + if (syntaxFactory.isConcrete) { + addSkippedTokenAfterNode(callSignature, token0); + } + return true; + } + } + + + return false; + } + + function isFunctionDeclaration(modifierCount: number): boolean { + return peekToken(modifierCount).kind() === SyntaxKind.FunctionKeyword; + } + + function parseFunctionDeclaration(): FunctionDeclarationSyntax { + var modifiers = parseModifiers(); + var functionKeyword = eatToken(SyntaxKind.FunctionKeyword); + var identifier = eatIdentifierToken(); + var callSignature = parseCallSignature(/*requireCompleteTypeParameterList:*/ false); + + // If we got an errant => then we want to parse what's coming up without requiring an + // open brace. + var parseBlockEvenWithNoOpenBrace = tryAddUnexpectedEqualsGreaterThanToken(callSignature); + + var semicolonToken: ISyntaxToken = null; + var block: BlockSyntax = null; + + // Parse a block if we're on a bock, or if we saw a '=>' + if (parseBlockEvenWithNoOpenBrace || isBlock()) { + block = parseBlock(parseBlockEvenWithNoOpenBrace, /*checkForStrictMode:*/ true); + } + else { + semicolonToken = eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false); + } + + return new syntaxFactory.FunctionDeclarationSyntax(parseNodeData, modifiers, functionKeyword, identifier, callSignature, block, semicolonToken); + } + + function parseModuleDeclaration(): ModuleDeclarationSyntax { + var modifiers = parseModifiers(); + var moduleKeyword = eatToken(SyntaxKind.ModuleKeyword); + + var moduleName: INameSyntax = null; + var stringLiteral: ISyntaxToken = null; + + if (currentToken().kind() === SyntaxKind.StringLiteral) { + stringLiteral = eatToken(SyntaxKind.StringLiteral); + } + else { + moduleName = parseName(/*allowIdentifierNames*/ false); + } + + var openBraceToken = eatToken(SyntaxKind.OpenBraceToken); + + var moduleElements = Syntax.emptyList(); + if (openBraceToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + moduleElements = parseSyntaxList(ListParsingState.ModuleDeclaration_ModuleElements, skippedTokens); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + } + + return new syntaxFactory.ModuleDeclarationSyntax(parseNodeData, + modifiers, moduleKeyword, moduleName, stringLiteral, openBraceToken, moduleElements, eatToken(SyntaxKind.CloseBraceToken)); + } + + function parseInterfaceDeclaration(): InterfaceDeclarationSyntax { + return new syntaxFactory.InterfaceDeclarationSyntax(parseNodeData, + parseModifiers(), eatToken(SyntaxKind.InterfaceKeyword), eatIdentifierToken(), + tryParseTypeParameterList(/*requireCompleteTypeParameterList:*/ false), parseHeritageClauses(), parseObjectType()); + } + + function parseObjectType(): ObjectTypeSyntax { + var openBraceToken = eatToken(SyntaxKind.OpenBraceToken); + + var typeMembers = Syntax.emptySeparatedList(); + if (openBraceToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + typeMembers = parseSeparatedSyntaxList(ListParsingState.ObjectType_TypeMembers, skippedTokens); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + } + + return new syntaxFactory.ObjectTypeSyntax(parseNodeData, openBraceToken, typeMembers, eatToken(SyntaxKind.CloseBraceToken)); + } + + function isTypeMember(inErrorRecovery: boolean): boolean { + if (SyntaxUtilities.isTypeMember(currentNode())) { + return true; + } + + return isCallSignature(/*tokenIndex:*/ 0) || + isConstructSignature() || + isIndexSignature(/*tokenIndex:*/ 0) || + isMethodSignature(inErrorRecovery) || + isPropertySignature(inErrorRecovery); + } + + function tryParseTypeMember(inErrorRecovery: boolean): ITypeMemberSyntax { + var node = currentNode(); + if (SyntaxUtilities.isTypeMember(node)) { + consumeNode(node); + return node; + } + + if (isCallSignature(/*tokenIndex:*/ 0)) { + return parseCallSignature(/*requireCompleteTypeParameterList:*/ false); + } + else if (isConstructSignature()) { + return parseConstructSignature(); + } + else if (isIndexSignature(/*tokenIndex:*/ 0)) { + return parseIndexSignature(); + } + else if (isMethodSignature(inErrorRecovery)) { + // Note: it is important that isFunctionSignature is called before isPropertySignature. + // isPropertySignature checks for a subset of isFunctionSignature. + return parseMethodSignature(); + } + else if (isPropertySignature(inErrorRecovery)) { + return parsePropertySignature(); + } + else { + return null; + } + } + + function parseConstructSignature(): ConstructSignatureSyntax { + return new syntaxFactory.ConstructSignatureSyntax(parseNodeData, eatToken(SyntaxKind.NewKeyword), parseCallSignature(/*requireCompleteTypeParameterList:*/ false)); + } + + function parseIndexSignature(): IndexSignatureSyntax { + var openBracketToken = eatToken(SyntaxKind.OpenBracketToken); + + var skippedTokens: ISyntaxToken[] = getArray(); + var parameters = parseSeparatedSyntaxList(ListParsingState.IndexSignature_Parameters, skippedTokens); + openBracketToken = addSkippedTokensAfterToken(openBracketToken, skippedTokens); + + return new syntaxFactory.IndexSignatureSyntax(parseNodeData, + openBracketToken, parameters, eatToken(SyntaxKind.CloseBracketToken), parseOptionalTypeAnnotation(/*allowStringLiteral:*/ false)); + } + + function parseMethodSignature(): MethodSignatureSyntax { + return new syntaxFactory.MethodSignatureSyntax(parseNodeData, + eatPropertyName(), tryEatToken(SyntaxKind.QuestionToken), parseCallSignature(/*requireCompleteTypeParameterList:*/ false)); + } + + function parsePropertySignature(): PropertySignatureSyntax { + return new syntaxFactory.PropertySignatureSyntax(parseNodeData, + eatPropertyName(), tryEatToken(SyntaxKind.QuestionToken), parseOptionalTypeAnnotation(/*allowStringLiteral:*/ false)); + } + + function isCallSignature(peekIndex: number): boolean { + var tokenKind = peekToken(peekIndex).kind(); + return tokenKind === SyntaxKind.OpenParenToken || tokenKind === SyntaxKind.LessThanToken; + } + + function isConstructSignature(): boolean { + if (currentToken().kind() !== SyntaxKind.NewKeyword) { + return false; + } + + return isCallSignature(/*peekIndex:*/1); + } + + function isIndexSignature(peekIndex: number): boolean { + return peekToken(peekIndex).kind() === SyntaxKind.OpenBracketToken; + } + + function isMethodSignature(inErrorRecovery: boolean): boolean { + if (isPropertyName(currentToken(), inErrorRecovery)) { + // id( + if (isCallSignature(1)) { + return true; + } + + // id?( + if (peekToken(1).kind() === SyntaxKind.QuestionToken && + isCallSignature(2)) { + return true; + } + } + + return false; + } + + function isPropertySignature(inErrorRecovery: boolean): boolean { + var _currentToken = currentToken(); + + // Keywords can start properties. However, they're often intended to start something + // else. If we see a modifier before something that can be a property, then don't + // try parse it out as a property. For example, if we have: + // + // public foo + // + // Then don't parse 'public' as a property name. Note: if you have: + // + // public + // foo + // + // Then we *should* parse it as a property name, as ASI takes effect here. + if (isModifier(_currentToken, /*index:*/ 0)) { + if (!existsNewLineBetweenTokens(_currentToken, peekToken(1), source.text) && + isPropertyName(peekToken(1), inErrorRecovery)) { + + return false; + } + } + + // Note: property names also start function signatures. So it's important that we call this + // after we calll isFunctionSignature. + return isPropertyName(_currentToken, inErrorRecovery); + } + + function isHeritageClause(): boolean { + var tokenKind = currentToken().kind(); + return tokenKind === SyntaxKind.ExtendsKeyword || tokenKind === SyntaxKind.ImplementsKeyword; + } + + function isNotHeritageClauseTypeName(): boolean { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.ImplementsKeyword || + tokenKind === SyntaxKind.ExtendsKeyword) { + + return isIdentifier(peekToken(1)); + } + + return false; + } + + function isHeritageClauseTypeName(): boolean { + if (isIdentifier(currentToken())) { + // We want to make sure that the "extends" in "extends foo" or the "implements" in + // "implements foo" is not considered a type name. + return !isNotHeritageClauseTypeName(); + } + + return false; + } + + function tryParseHeritageClause(): HeritageClauseSyntax { + var extendsOrImplementsKeyword = currentToken(); + var tokenKind = extendsOrImplementsKeyword.kind(); + if (tokenKind !== SyntaxKind.ExtendsKeyword && tokenKind !== SyntaxKind.ImplementsKeyword) { + return null; + } + + consumeToken(extendsOrImplementsKeyword); + + var skippedTokens: ISyntaxToken[] = getArray(); + var typeNames = parseSeparatedSyntaxList(ListParsingState.HeritageClause_TypeNameList, skippedTokens); + extendsOrImplementsKeyword = addSkippedTokensAfterToken(extendsOrImplementsKeyword, skippedTokens); + + return new syntaxFactory.HeritageClauseSyntax(parseNodeData, extendsOrImplementsKeyword, typeNames); + } + + function isInterfaceEnumClassModuleImportOrExport(modifierCount: number): boolean { + var _currentToken = currentToken(); + + if (modifierCount) { + // Any of these keywords following a modifier is definitely a TS construct. + switch (peekToken(modifierCount).kind()) { + case SyntaxKind.ImportKeyword: + case SyntaxKind.ModuleKeyword: + case SyntaxKind.InterfaceKeyword: + case SyntaxKind.ClassKeyword: + case SyntaxKind.EnumKeyword: + return true; + } + } + + // no modifiers. While certain of these keywords are javascript keywords as well, it + // is possible to run into them in some circumstances in error recovery where we don't + // want to consider them the start of the module element construct. For example, they + // might be hte name in an object literal. Because of that, we check the next token to + // make sure it really is the start of a module element. + var nextToken = peekToken(1); + + switch (_currentToken.kind()) { + case SyntaxKind.ModuleKeyword: + if (isIdentifier(nextToken) || nextToken.kind() === SyntaxKind.StringLiteral) { + return true; + } + break; + + case SyntaxKind.ImportKeyword: + case SyntaxKind.ClassKeyword: + case SyntaxKind.EnumKeyword: + case SyntaxKind.InterfaceKeyword: + if (isIdentifier(nextToken)) { + return true; + } + break; + + case SyntaxKind.ExportKeyword: + if (nextToken.kind() === SyntaxKind.EqualsToken) { + return true; + } + break; + } + + return false; + } + + function isStatement(modifierCount: number, inErrorRecovery: boolean): boolean { + if (SyntaxUtilities.isStatement(currentNode())) { + return true; + } + + var _currentToken = currentToken(); + var currentTokenKind = _currentToken.kind(); + switch (currentTokenKind) { + // ERROR RECOVERY + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.StaticKeyword: + // None of the above are actually keywords. And they might show up in a real + // statement (i.e. "public();"). However, if we see 'public ' then + // that can't possibly be a statement (and instead will be a class element), + // and we should not parse it out here. + var token1 = peekToken(1); + if (SyntaxFacts.isIdentifierNameOrAnyKeyword(token1)) { + // Definitely not a statement. + return false; + } + + // Handle this below in 'isExpressionStatement()' + break; + + // Common cases that we can immediately assume are statements. + case SyntaxKind.IfKeyword: + case SyntaxKind.OpenBraceToken: + case SyntaxKind.ReturnKeyword: + case SyntaxKind.SwitchKeyword: + case SyntaxKind.ThrowKeyword: + case SyntaxKind.BreakKeyword: + case SyntaxKind.ContinueKeyword: + case SyntaxKind.ForKeyword: + case SyntaxKind.WhileKeyword: + case SyntaxKind.WithKeyword: + case SyntaxKind.DoKeyword: + case SyntaxKind.TryKeyword: + case SyntaxKind.DebuggerKeyword: + return true; + } + + // Check for common things that might appear where we expect a statement, but which we + // do not want to consume. This can happen when the user does not terminate their + // existing block properly. We don't want to accidently consume these as expression + // below. + if (isInterfaceEnumClassModuleImportOrExport(modifierCount)) { + return false; + } + + // More complicated cases. + return isLabeledStatement(_currentToken) || + isVariableStatement(modifierCount) || + isFunctionDeclaration(modifierCount) || + isEmptyStatement(_currentToken, inErrorRecovery) || + isExpressionStatement(_currentToken); + } + + function parseStatement(inErrorRecovery: boolean): IStatementSyntax { + return tryParseStatement(inErrorRecovery) || parseExpressionStatement(); + } + + function tryParseStatement(inErrorRecovery: boolean): IStatementSyntax { + var node = currentNode(); + if (SyntaxUtilities.isStatement(node)) { + consumeNode(node); + return node; + } + + var _currentToken = currentToken(); + var currentTokenKind = _currentToken.kind(); + return tryParseStatementWorker(_currentToken, currentTokenKind, modifierCount(), inErrorRecovery); + } + + function tryParseStatementWorker(_currentToken: ISyntaxToken, currentTokenKind: SyntaxKind, modifierCount: number, inErrorRecovery: boolean): IStatementSyntax { + switch (currentTokenKind) { + // ERROR RECOVERY + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.StaticKeyword: + // None of the above are actually keywords. And they might show up in a real + // statement (i.e. "public();"). However, if we see 'public ' then + // that can't possibly be a statement (and instead will be a class element), + // and we should not parse it out here. + if (SyntaxFacts.isIdentifierNameOrAnyKeyword(peekToken(1))) { + // Definitely not a statement. + return null; + } + else { + break; + } + + case SyntaxKind.IfKeyword: return parseIfStatement(_currentToken); + case SyntaxKind.OpenBraceToken: return parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ false); + case SyntaxKind.ReturnKeyword: return parseReturnStatement(_currentToken); + case SyntaxKind.SwitchKeyword: return parseSwitchStatement(_currentToken); + case SyntaxKind.ThrowKeyword: return parseThrowStatement(_currentToken); + case SyntaxKind.BreakKeyword: return parseBreakStatement(_currentToken); + case SyntaxKind.ContinueKeyword: return parseContinueStatement(_currentToken); + case SyntaxKind.ForKeyword: return parseForOrForInStatement(_currentToken); + case SyntaxKind.WhileKeyword: return parseWhileStatement(_currentToken); + case SyntaxKind.WithKeyword: return parseWithStatement(_currentToken); + case SyntaxKind.DoKeyword: return parseDoStatement(_currentToken); + case SyntaxKind.TryKeyword: return parseTryStatement(_currentToken); + case SyntaxKind.DebuggerKeyword: return parseDebuggerStatement(_currentToken); + } + + // Check for common things that might appear where we expect a statement, but which we + // do not want to consume. This can happen when the user does not terminate their + // existing block properly. We don't want to accidently consume these as expression + // below. + if (isInterfaceEnumClassModuleImportOrExport(modifierCount)) { + return null; + } + else if (isVariableStatement(modifierCount)) { + return parseVariableStatement(); + } + else if (isLabeledStatement(_currentToken)) { + return parseLabeledStatement(_currentToken); + } + else if (isFunctionDeclaration(modifierCount)) { + return parseFunctionDeclaration(); + } + else if (isEmptyStatement(_currentToken, inErrorRecovery)) { + return parseEmptyStatement(_currentToken); + } + else if (isExpressionStatement(_currentToken)) { + return parseExpressionStatement(); + } + else { + return null; + } + } + + function parseDebuggerStatement(debuggerKeyword: ISyntaxToken): DebuggerStatementSyntax { + return new syntaxFactory.DebuggerStatementSyntax(parseNodeData, consumeToken(debuggerKeyword), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseDoStatement(doKeyword: ISyntaxToken): DoStatementSyntax { + // From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html + // 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in + // spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby + // do;while(0)x will have a semicolon inserted before x. + return new syntaxFactory.DoStatementSyntax(parseNodeData, + consumeToken(doKeyword), parseStatement(/*inErrorRecovery:*/ false), eatToken(SyntaxKind.WhileKeyword), eatToken(SyntaxKind.OpenParenToken), + parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ true)); + } + + function isLabeledStatement(currentToken: ISyntaxToken): boolean { + return isIdentifier(currentToken) && peekToken(1).kind() === SyntaxKind.ColonToken; + } + + function parseLabeledStatement(identifierToken: ISyntaxToken): LabeledStatementSyntax { + return new syntaxFactory.LabeledStatementSyntax(parseNodeData, + consumeToken(identifierToken), eatToken(SyntaxKind.ColonToken), parseStatement(/*inErrorRecovery:*/ false)); + } + + function parseTryStatement(tryKeyword: ISyntaxToken): TryStatementSyntax { + var tryKeyword = consumeToken(tryKeyword); + + var savedListParsingState = listParsingState; + listParsingState |= (1 << ListParsingState.TryBlock_Statements); + var block = parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ false); + listParsingState = savedListParsingState; + + var catchClause: CatchClauseSyntax = null; + if (currentToken().kind() === SyntaxKind.CatchKeyword) { + catchClause = parseCatchClause(); + } + + // If we don't have a catch clause, then we must have a finally clause. Try to parse + // one out no matter what. + var finallyClause: FinallyClauseSyntax = null; + if (catchClause === null || currentToken().kind() === SyntaxKind.FinallyKeyword) { + finallyClause = parseFinallyClause(); + } + + return new syntaxFactory.TryStatementSyntax(parseNodeData, tryKeyword, block, catchClause, finallyClause); + } + + function parseCatchClauseBlock(): BlockSyntax { + var savedListParsingState = listParsingState; + listParsingState |= (1 << ListParsingState.CatchBlock_Statements); + var block = parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ false); + listParsingState = savedListParsingState; + + return block; + } + + function parseCatchClause(): CatchClauseSyntax { + return new syntaxFactory.CatchClauseSyntax(parseNodeData, + eatToken(SyntaxKind.CatchKeyword), eatToken(SyntaxKind.OpenParenToken), eatIdentifierToken(), + parseOptionalTypeAnnotation(/*allowStringLiteral:*/ false), eatToken(SyntaxKind.CloseParenToken), parseCatchClauseBlock()); + } + + function parseFinallyClause(): FinallyClauseSyntax { + return new syntaxFactory.FinallyClauseSyntax(parseNodeData, + eatToken(SyntaxKind.FinallyKeyword), parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ false)); + } + + function parseWithStatement(withKeyword: ISyntaxToken): WithStatementSyntax { + return new syntaxFactory.WithStatementSyntax(parseNodeData, + consumeToken(withKeyword), eatToken(SyntaxKind.OpenParenToken), parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false)); + } + + function parseWhileStatement(whileKeyword: ISyntaxToken): WhileStatementSyntax { + return new syntaxFactory.WhileStatementSyntax(parseNodeData, + consumeToken(whileKeyword), eatToken(SyntaxKind.OpenParenToken), parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false)); + } + + function isEmptyStatement(currentToken: ISyntaxToken, inErrorRecovery: boolean): boolean { + // If we're in error recovery, then we don't want to treat ';' as an empty statement. + // The problem is that ';' can show up in far too many contexts, and if we see one + // and assume it's a statement, then we may bail out innapropriately from whatever + // we're parsing. For example, if we have a semicolon in the middle of a class, then + // we really don't want to assume the class is over and we're on a statement in the + // outer module. We just want to consume and move on. + if (inErrorRecovery) { + return false; + } + + return currentToken.kind() === SyntaxKind.SemicolonToken; + } + + function parseEmptyStatement(semicolonToken: ISyntaxToken): EmptyStatementSyntax { + return new syntaxFactory.EmptyStatementSyntax(parseNodeData, consumeToken(semicolonToken)); + } + + function parseForOrForInStatement(forKeyword: ISyntaxToken): IStatementSyntax { + // Debug.assert(isForOrForInStatement()); + + consumeToken(forKeyword); + var openParenToken = eatToken(SyntaxKind.OpenParenToken); + + var _currentToken = currentToken(); + var tokenKind = _currentToken.kind(); + if (tokenKind === SyntaxKind.VarKeyword) { + // for ( var VariableDeclarationListNoIn; Expressionopt ; Expressionopt ) Statement + // for ( var VariableDeclarationNoIn in Expression ) Statement + return parseForOrForInStatementWithVariableDeclaration(forKeyword, openParenToken); + } + else if (tokenKind === SyntaxKind.SemicolonToken) { + // for ( ; Expressionopt ; Expressionopt ) Statement + return parseForStatementWithNoVariableDeclarationOrInitializer(forKeyword, openParenToken); + } + else { + // for ( ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement + // for ( LeftHandSideExpression in Expression ) Statement + return parseForOrForInStatementWithInitializer(forKeyword, openParenToken); + } + } + + function parseForOrForInStatementWithVariableDeclaration(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken): IStatementSyntax { + // Debug.assert(forKeyword.kind === SyntaxKind.ForKeyword && openParenToken.kind() === SyntaxKind.OpenParenToken); + // Debug.assert(currentToken().kind() === SyntaxKind.VarKeyword); + + // for ( var VariableDeclarationListNoIn; Expressionopt ; Expressionopt ) Statement + // for ( var VariableDeclarationNoIn in Expression ) Statement + + var variableDeclaration = parseVariableDeclaration(/*allowIn:*/ false); + return currentToken().kind() === SyntaxKind.InKeyword + ? parseForInStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, variableDeclaration, null) + : parseForStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, variableDeclaration, null); + } + + function parseForInStatementWithVariableDeclarationOrInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax): ForInStatementSyntax { + // for ( var VariableDeclarationNoIn in Expression ) Statement + + return new syntaxFactory.ForInStatementSyntax(parseNodeData, + forKeyword, openParenToken, variableDeclaration, initializer, eatToken(SyntaxKind.InKeyword), + parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false)); + } + + function parseForOrForInStatementWithInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken): IStatementSyntax { + // Debug.assert(forKeyword.kind() === SyntaxKind.ForKeyword && openParenToken.kind() === SyntaxKind.OpenParenToken); + + // for ( ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement + // for ( LeftHandSideExpression in Expression ) Statement + + var initializer = parseExpression(/*allowIn:*/ false); + return currentToken().kind() === SyntaxKind.InKeyword + ? parseForInStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, null, initializer) + : parseForStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, null, initializer); + } + + function parseForStatementWithNoVariableDeclarationOrInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken): ForStatementSyntax { + // Debug.assert(forKeyword.kind() === SyntaxKind.ForKeyword && openParenToken.kind() === SyntaxKind.OpenParenToken); + // Debug.assert(currentToken().kind() === SyntaxKind.SemicolonToken); + // for ( ; Expressionopt ; Expressionopt ) Statement + + return parseForStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, /*variableDeclaration:*/ null, /*initializer:*/ null); + } + + function tryParseForStatementCondition(): IExpressionSyntax { + var tokenKind = currentToken().kind(); + if (tokenKind !== SyntaxKind.SemicolonToken && + tokenKind !== SyntaxKind.CloseParenToken && + tokenKind !== SyntaxKind.EndOfFileToken) { + return parseExpression(/*allowIn:*/ true); + } + + return null; + } + + function tryParseForStatementIncrementor(): IExpressionSyntax { + var tokenKind = currentToken().kind(); + if (tokenKind !== SyntaxKind.CloseParenToken && + tokenKind !== SyntaxKind.EndOfFileToken) { + return parseExpression(/*allowIn:*/ true); + } + + return null; + } + + function parseForStatementWithVariableDeclarationOrInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax): ForStatementSyntax { + // NOTE: From the es5 section on Automatic Semicolon Insertion. + // a semicolon is never inserted automatically if the semicolon would then ... become + // one of the two semicolons in the header of a for statement + + return new syntaxFactory.ForStatementSyntax(parseNodeData, + forKeyword, openParenToken, variableDeclaration, initializer, + eatToken(SyntaxKind.SemicolonToken), tryParseForStatementCondition(), + eatToken(SyntaxKind.SemicolonToken), tryParseForStatementIncrementor(), + eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false)); + } + + function tryEatBreakOrContinueLabel(): ISyntaxToken { + // If there is no newline after the break keyword, then we can consume an optional + // identifier. + var identifier: ISyntaxToken = null; + if (!canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)) { + if (isIdentifier(currentToken())) { + return eatIdentifierToken(); + } + } + + return null; + } + + function parseBreakStatement(breakKeyword: ISyntaxToken): BreakStatementSyntax { + return new syntaxFactory.BreakStatementSyntax(parseNodeData, + consumeToken(breakKeyword), tryEatBreakOrContinueLabel(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseContinueStatement(continueKeyword: ISyntaxToken): ContinueStatementSyntax { + return new syntaxFactory.ContinueStatementSyntax(parseNodeData, + consumeToken(continueKeyword), tryEatBreakOrContinueLabel(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseSwitchStatement(switchKeyword: ISyntaxToken) { + // Debug.assert(isSwitchStatement()); + + consumeToken(switchKeyword); + var openParenToken = eatToken(SyntaxKind.OpenParenToken); + var expression = parseExpression(/*allowIn:*/ true); + var closeParenToken = eatToken(SyntaxKind.CloseParenToken); + var openBraceToken = eatToken(SyntaxKind.OpenBraceToken); + + var switchClauses = Syntax.emptyList(); + if (openBraceToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + switchClauses = parseSyntaxList(ListParsingState.SwitchStatement_SwitchClauses, skippedTokens); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + } + + return new syntaxFactory.SwitchStatementSyntax(parseNodeData, switchKeyword, openParenToken, expression, closeParenToken, openBraceToken, switchClauses, eatToken(SyntaxKind.CloseBraceToken)); + } + + function isSwitchClause(): boolean { + if (SyntaxUtilities.isSwitchClause(currentNode())) { + return true; + } + + var currentTokenKind = currentToken().kind(); + return currentTokenKind === SyntaxKind.CaseKeyword || currentTokenKind === SyntaxKind.DefaultKeyword; + } + + function tryParseSwitchClause(): ISwitchClauseSyntax { + // Debug.assert(isSwitchClause()); + var node = currentNode(); + if (SyntaxUtilities.isSwitchClause(node)) { + consumeNode(node); + return node; + } + + var _currentToken = currentToken(); + var kind = _currentToken.kind(); + if (kind === SyntaxKind.CaseKeyword) { + return parseCaseSwitchClause(_currentToken); + } + else if (kind === SyntaxKind.DefaultKeyword) { + return parseDefaultSwitchClause(_currentToken); + } + else { + return null; + } + } + + function parseCaseSwitchClause(caseKeyword: ISyntaxToken): CaseSwitchClauseSyntax { + // Debug.assert(isCaseSwitchClause()); + + consumeToken(caseKeyword); + var expression = parseExpression(/*allowIn:*/ true); + var colonToken = eatToken(SyntaxKind.ColonToken); + var statements = Syntax.emptyList(); + + // TODO: allow parsing of the list evne if there's no colon. However, we have to make + // sure we add any skipped tokens to the right previous node or token. + if (colonToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + statements = parseSyntaxList(ListParsingState.SwitchClause_Statements, skippedTokens); + colonToken = addSkippedTokensAfterToken(colonToken, skippedTokens); + } + + return new syntaxFactory.CaseSwitchClauseSyntax(parseNodeData, caseKeyword, expression, colonToken, statements); + } + + function parseDefaultSwitchClause(defaultKeyword: ISyntaxToken): DefaultSwitchClauseSyntax { + // Debug.assert(isDefaultSwitchClause()); + + consumeToken(defaultKeyword); + var colonToken = eatToken(SyntaxKind.ColonToken); + var statements = Syntax.emptyList(); + + // TODO: Allow parsing without a colon here. However, ensure that we attach any skipped + // tokens to the defaultKeyword. + if (colonToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + statements = parseSyntaxList(ListParsingState.SwitchClause_Statements, skippedTokens); + colonToken = addSkippedTokensAfterToken(colonToken, skippedTokens); + } + + return new syntaxFactory.DefaultSwitchClauseSyntax(parseNodeData, defaultKeyword, colonToken, statements); + } + + function parseThrowStatementExpression(): IExpressionSyntax { + // Because of automatic semicolon insertion, we need to report error if this + // throw could be terminated with a semicolon. Note: we can't call 'parseExpression' + // directly as that might consume an expression on the following line. + return canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false) + ? createMissingToken(SyntaxKind.IdentifierName, null) + : parseExpression(/*allowIn:*/ true); + } + + function parseThrowStatement(throwKeyword: ISyntaxToken): ThrowStatementSyntax { + return new syntaxFactory.ThrowStatementSyntax(parseNodeData, + consumeToken(throwKeyword), parseThrowStatementExpression(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function tryParseReturnStatementExpression(): IExpressionSyntax { + return !canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false) ? parseExpression(/*allowIn:*/ true) : null; + } + + function parseReturnStatement(returnKeyword: ISyntaxToken): ReturnStatementSyntax { + return new syntaxFactory.ReturnStatementSyntax(parseNodeData, + consumeToken(returnKeyword), tryParseReturnStatementExpression(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function isExpressionStatement(currentToken: ISyntaxToken): boolean { + // As per the gramar, neither { nor 'function' can start an expression statement. + var tokenKind = currentToken.kind(); + return tokenKind !== SyntaxKind.OpenBraceToken && tokenKind !== SyntaxKind.FunctionKeyword && isExpression(currentToken); + } + + function isAssignmentOrOmittedExpression(): boolean { + var _currentToken = currentToken(); + return _currentToken.kind() === SyntaxKind.CommaToken || isExpression(_currentToken); + } + + function tryParseAssignmentOrOmittedExpression(): IExpressionSyntax { + // Debug.assert(isAssignmentOrOmittedExpression()); + + if (currentToken().kind() === SyntaxKind.CommaToken) { + return new syntaxFactory.OmittedExpressionSyntax(parseNodeData); + } + + return tryParseAssignmentExpressionOrHigher(/*force:*/ false, /*allowIn:*/ true); + } + + function isExpression(currentToken: ISyntaxToken): boolean { + switch (currentToken.kind()) { + // Literals + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + + // For array literals. + case SyntaxKind.OpenBracketToken: + + // For parenthesized expressions + case SyntaxKind.OpenParenToken: + + // For cast expressions. + case SyntaxKind.LessThanToken: + + // Prefix unary expressions. + case SyntaxKind.PlusPlusToken: + case SyntaxKind.MinusMinusToken: + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + case SyntaxKind.TildeToken: + case SyntaxKind.ExclamationToken: + + // For object type literal expressions. + case SyntaxKind.OpenBraceToken: + + // ERROR TOLERANCE: + // If we see a => then we know the user was probably trying to type in an arrow + // function. So allow this as the start of an expression, knowing that when we + // actually try to parse it we'll report the missing identifier. + case SyntaxKind.EqualsGreaterThanToken: + + case SyntaxKind.SlashToken: + case SyntaxKind.SlashEqualsToken: + // Note: if we see a / or /= token then we always consider this an expression. Why? + // Well, either that / or /= is actually a regular expression, in which case we're + // definitely an expression. Or, it's actually a divide. In which case, we *still* + // want to think of ourself as an expression. "But wait", you say. '/' doesn't + // start an expression. That's true. BUt like the above check for =>, for error + // tolerance, we will consider ourselves in an expression. We'll then parse out an + // missing identifier and then will consume the / token naturally as a binary + // expression. + + // Simple epxressions. + case SyntaxKind.SuperKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + + // For object creation expressions. + case SyntaxKind.NewKeyword: + + // Prefix unary expressions + case SyntaxKind.DeleteKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.TypeOfKeyword: + + // For function expressions. + case SyntaxKind.FunctionKeyword: + return true; + } + + return isIdentifier(currentToken); + } + + function parseExpressionStatement(): ExpressionStatementSyntax { + return new syntaxFactory.ExpressionStatementSyntax(parseNodeData, parseExpression(/*allowIn:*/ true), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseIfStatement(ifKeyword: ISyntaxToken): IfStatementSyntax { + return new syntaxFactory.IfStatementSyntax(parseNodeData, + consumeToken(ifKeyword), eatToken(SyntaxKind.OpenParenToken), parseExpression(/*allowIn:*/ true), + eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false), parseOptionalElseClause()); + } + + function parseOptionalElseClause(): ElseClauseSyntax { + return currentToken().kind() === SyntaxKind.ElseKeyword ? parseElseClause() : null; + } + + function parseElseClause(): ElseClauseSyntax { + return new syntaxFactory.ElseClauseSyntax(parseNodeData, eatToken(SyntaxKind.ElseKeyword), parseStatement(/*inErrorRecovery:*/ false)); + } + + function isVariableStatement(modifierCount: number): boolean { + return peekToken(modifierCount).kind() === SyntaxKind.VarKeyword; + } + + function parseVariableStatement(): VariableStatementSyntax { + return new syntaxFactory.VariableStatementSyntax(parseNodeData, + parseModifiers(), parseVariableDeclaration(/*allowIn:*/ true), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)); + } + + function parseVariableDeclaration(allowIn: boolean): VariableDeclarationSyntax { + // Debug.assert(currentToken().kind() === SyntaxKind.VarKeyword); + + var varKeyword = eatToken(SyntaxKind.VarKeyword); + // Debug.assert(varKeyword.fullWidth() > 0); + + var listParsingState = allowIn + ? ListParsingState.VariableDeclaration_VariableDeclarators_AllowIn + : ListParsingState.VariableDeclaration_VariableDeclarators_DisallowIn; + + var skippedTokens: ISyntaxToken[] = getArray(); + var variableDeclarators = parseSeparatedSyntaxList(listParsingState, skippedTokens); + varKeyword = addSkippedTokensAfterToken(varKeyword, skippedTokens); + + return new syntaxFactory.VariableDeclarationSyntax(parseNodeData, varKeyword, variableDeclarators); + } + + function isVariableDeclarator(): boolean { + var node = currentNode(); + if (node !== null && node.kind() === SyntaxKind.VariableDeclarator) { + return true; + } + + return isIdentifier(currentToken()); + } + + function canReuseVariableDeclaratorNode(node: ISyntaxNode) { + if (node === null || node.kind() !== SyntaxKind.VariableDeclarator) { + return false; + } + + // Very subtle incremental parsing bug. Consider the following code: + // + // var v = new List < A, B + // + // This is actually legal code. It's a list of variable declarators "v = new List() + // + // then we have a problem. "v = new Listnode; + return variableDeclarator.equalsValueClause === null; + } + + function tryParseVariableDeclarator(allowIn: boolean, allowPropertyName: boolean): VariableDeclaratorSyntax { + // TODO(cyrusn): What if the 'allowIn' context has changed between when we last parsed + // and now? We could end up with an incorrect tree. For example, say we had in the old + // tree "var i = a in b". Then, in the new tree the declarator portion moved into: + // "for (var i = a in b". We would not want to reuse the declarator as the "in b" portion + // would need to be consumed by the for declaration instead. Need to see if it is possible + // to hit this case. + var node = currentNode(); + if (canReuseVariableDeclaratorNode(node)) { + consumeNode(node); + return node; + } + + if (allowPropertyName) { + // Debug.assert(isPropertyName(currentToken(), /*inErrorRecovery:*/ false)); + } + + if (!allowPropertyName && !isIdentifier(currentToken())) { + return null; + } + + var propertyName = allowPropertyName ? eatPropertyName() : eatIdentifierToken(); + var equalsValueClause: EqualsValueClauseSyntax = null; + var typeAnnotation: TypeAnnotationSyntax = null; + + if (propertyName.fullWidth() > 0) { + typeAnnotation = parseOptionalTypeAnnotation(/*allowStringLiteral:*/ false); + + if (isEqualsValueClause(/*inParameter*/ false)) { + equalsValueClause = parseEqualsValueClause(allowIn); + } + } + + return new syntaxFactory.VariableDeclaratorSyntax(parseNodeData, propertyName, typeAnnotation, equalsValueClause); + } + + function isEqualsValueClause(inParameter: boolean): boolean { + var token0 = currentToken(); + if (token0.kind() === SyntaxKind.EqualsToken) { + return true; + } + + // It's not uncommon during typing for the user to miss writing the '=' token. Check if + // there is no newline after the last token and if we're on an expression. If so, parse + // this as an equals-value clause with a missing equals. + if (!previousTokenHasTrailingNewLine(token0)) { + var tokenKind = token0.kind(); + + // The 'isExpression' call below returns true for "=>". That's because it smartly + // assumes that there is just a missing identifier and the user wanted a lambda. + // While this is sensible, we don't want to allow that here as that would mean we're + // glossing over multiple erorrs and we're probably making things worse. So don't + // treat this as an equals value clause and let higher up code handle things. + if (tokenKind === SyntaxKind.EqualsGreaterThanToken) { + return false; + } + + // There are two places where we allow equals-value clauses. The first is in a + // variable declarator. The second is with a parameter. For variable declarators + // it's more likely that a { would be a allowed (as an object literal). While this + // is also allowed for parameters, the risk is that we consume the { as an object + // literal when it really will be for the block following the parameter. + if (tokenKind === SyntaxKind.OpenBraceToken && + inParameter) { + return false; + } + + return isExpression(token0); + } + + return false; + } + + function parseEqualsValueClause(allowIn: boolean): EqualsValueClauseSyntax { + return new syntaxFactory.EqualsValueClauseSyntax(parseNodeData, + eatToken(SyntaxKind.EqualsToken), tryParseAssignmentExpressionOrHigher(/*force:*/ true, allowIn)); + } + + function parseExpression(allowIn: boolean): IExpressionSyntax { + // Expression[in]: + // AssignmentExpression[in] + // Expression[in] , AssignmentExpression[in] + + var leftOperand = tryParseAssignmentExpressionOrHigher(/*force:*/ true, allowIn); + while (true) { + var _currentToken = currentToken(); + if (_currentToken.kind() !== SyntaxKind.CommaToken) { + break; + } + + leftOperand = new syntaxFactory.BinaryExpressionSyntax(parseNodeData, leftOperand, consumeToken(_currentToken), + tryParseAssignmentExpressionOrHigher(/*force:*/ true, allowIn)); + } + + return leftOperand; + } + + // Called when you need to parse an expression, but you do not want to allow 'CommaExpressions'. + // i.e. if you have "var a = 1, b = 2" then when we parse '1' we want to parse with higher + // precedence than 'comma'. Otherwise we'll get: "var a = (1, (b = 2))", instead of + // "var a = (1), b = (2)"); + function tryParseAssignmentExpressionOrHigher(force: boolean, allowIn: boolean): IExpressionSyntax { + // Augmented by TypeScript: + // + // AssignmentExpression[in]: + // 1) ConditionalExpression[in] + // 2) LeftHandSideExpression = AssignmentExpression[in] + // 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[in] + // 4) ArrowFunctionExpression <-- added by TypeScript + // + // Open spec question. Right now, there is no 'ArrowFunctionExpression[in]' variant. + // Thus, if the user has: + // + // for (var a = () => b in c) {} + // + // Then we will fail to parse (because the 'in' will be consumed as part of the body of + // the lambda, and not as part of the 'for' statement). This is likely not an issue + // whatsoever as there seems to be no good reason why anyone would ever write code like + // the above. + // + // Note: for ease of implementation we treat productions '2' and '3' as the same thing. + // (i.e. they're both BinaryExpressions with an assignment operator in it). + + // First, check if we have production '4' (an arrow function). Note that if we do, we + // must *not* recurse for productsion 1, 2 or 3. An ArrowFunction is not a + // LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done + // with AssignmentExpression if we see one. + var _currentToken = currentToken(); + var arrowFunction = tryParseAnyArrowFunctionExpression(_currentToken); + if (arrowFunction !== null) { + return arrowFunction; + } + + // Now try to see if we're in production '1', '2' or '3'. A conditional expression can + // start with a LogicalOrExpression, while the assignment productions can only start with + // LeftHandSideExpressions. + // + // So, first, we try to just parse out a BinaryExpression. If we get something that is a + // LeftHandSide or higher, then we can try to parse out the assignment expression part. + // Otherwise, we try to parse out the conditional expression bit. We want to allow any + // binary expression here, so we pass in the 'lowest' precedence here so that it matches + // and consumes anything. + var leftOperand = tryParseBinaryExpressionOrHigher(_currentToken, force, BinaryExpressionPrecedence.Lowest, allowIn); + if (leftOperand === null) { + return null; + } + + if (SyntaxUtilities.isLeftHandSizeExpression(leftOperand)) { + // Note: we call currentOperatorToken so that we get an appropriately merged token + // for cases like > > = becoming >>= + var operatorToken = currentOperatorToken(); + + // Check for recursive assignment expressions. + if (SyntaxFacts.isAssignmentOperatorToken(operatorToken.kind())) { + return new syntaxFactory.BinaryExpressionSyntax(parseNodeData, leftOperand, consumeToken(operatorToken), + tryParseAssignmentExpressionOrHigher(/*force:*/ true, allowIn)); + } + } + + // It wasn't an assignment or a lambda. This is a conditional expression: + return parseConditionalExpressionRest(allowIn, leftOperand); + } + + function tryParseAnyArrowFunctionExpression(_currentToken: ISyntaxToken): IExpressionSyntax { + return isSimpleArrowFunctionExpression(_currentToken) + ? parseSimpleArrowFunctionExpression() + : tryParseParenthesizedArrowFunctionExpression(); + } + + function tryParseUnaryExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean): IUnaryExpressionSyntax { + var currentTokenKind = _currentToken.kind(); + + switch (currentTokenKind) { + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + case SyntaxKind.TildeToken: + case SyntaxKind.ExclamationToken: + case SyntaxKind.PlusPlusToken: + case SyntaxKind.MinusMinusToken: + return new syntaxFactory.PrefixUnaryExpressionSyntax(parseNodeData, consumeToken(_currentToken), tryParseUnaryExpressionOrHigher(currentToken(), /*force:*/ true)); + case SyntaxKind.TypeOfKeyword: return parseTypeOfExpression(_currentToken); + case SyntaxKind.VoidKeyword: return parseVoidExpression(_currentToken); + case SyntaxKind.DeleteKeyword: return parseDeleteExpression(_currentToken); + case SyntaxKind.LessThanToken: return parseCastExpression(_currentToken); + default: + return tryParsePostfixExpressionOrHigher(_currentToken, force); + } + } + + function tryParseBinaryExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean, precedence: BinaryExpressionPrecedence, allowIn: boolean): IExpressionSyntax { + // The binary expressions are incredibly left recursive in their definitions. We + // clearly can't implement that through recursion. So, instead, we first bottom out + // of all the recursion by jumping to this production and consuming a UnaryExpression + // first. + // + // MultiplicativeExpression: See 11.5 + // UnaryExpression + var leftOperand = tryParseUnaryExpressionOrHigher(_currentToken, force); + if (leftOperand === null) { + return null; + } + + // We then pop up the stack consuming the other side of the binary exprssion if it exists. + return parseBinaryExpressionRest(precedence, allowIn, leftOperand); + } + + function parseConditionalExpressionRest(allowIn: boolean, leftOperand: IExpressionSyntax): IExpressionSyntax { + // Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher. + + var _currentToken = currentToken(); + + // Now check for conditional expression. + if (_currentToken.kind() !== SyntaxKind.QuestionToken) { + return leftOperand; + } + + // Note: we explicitly do *not* pass 'allowIn' to the whenTrue part. An 'in' expression is always + // allowed in the 'true' part of a conditional expression. + + return new syntaxFactory.ConditionalExpressionSyntax(parseNodeData, + leftOperand, consumeToken(_currentToken), tryParseAssignmentExpressionOrHigher(/*force:*/ true, /*allowIn:*/ true), + eatToken(SyntaxKind.ColonToken), tryParseAssignmentExpressionOrHigher(/*force:*/ true, allowIn)); + } + + function parseBinaryExpressionRest(precedence: BinaryExpressionPrecedence, allowIn: boolean, leftOperand: IExpressionSyntax): IExpressionSyntax { + while (true) { + // We either have a binary operator here, or we're finished. We call + // currentOperatorToken versus currentToken here so that we merge token sequences + // like > and = into >= + var operatorToken = currentOperatorToken(); + var tokenKind = operatorToken.kind(); + + // Only proceed if we see binary expression token. However we don't parse + // assignment expressions or comma expressions here. Those are taken care of + // respectively in parseAssignmentExpression and parseExpression. + if (!SyntaxFacts.isBinaryExpressionOperatorToken(tokenKind) || + tokenKind === SyntaxKind.CommaToken || + SyntaxFacts.isAssignmentOperatorToken(tokenKind)) { + + break; + } + + // also, if it's the 'in' operator, only allow if our caller allows it. + if (tokenKind === SyntaxKind.InKeyword && !allowIn) { + break; + } + + var newPrecedence = getBinaryExpressionPrecedence(tokenKind); + + // All binary operators must have precedence > 0 + // Debug.assert(newPrecedence > 0); + + // Check the precedence to see if we should "take" this operator + if (newPrecedence <= precedence) { + break; + } + + // Precedence is okay, so we'll "take" this operator. + // Now skip the operator token we're on. + + leftOperand = new syntaxFactory.BinaryExpressionSyntax(parseNodeData, leftOperand, consumeToken(operatorToken), + tryParseBinaryExpressionOrHigher(currentToken(), /*force:*/ true, newPrecedence, allowIn)); + } + + return leftOperand; + } + + function currentOperatorToken(): ISyntaxToken { + var token0 = currentToken(); + + // If we see a > we need to see if we can actually merge this contextually into a + // >> >>> >= >>= >>>= token. + if (token0.kind() === SyntaxKind.GreaterThanToken) { + return currentContextualToken(); + // var kind = token0.kind; + //Debug.assert(kind() === SyntaxKind.GreaterThanToken || kind() === SyntaxKind.GreaterThanGreaterThanToken || + // kind() === SyntaxKind.GreaterThanGreaterThanGreaterThanToken || kind() === SyntaxKind.GreaterThanEqualsToken || + // kind() === SyntaxKind.GreaterThanGreaterThanEqualsToken || kind() === SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken); + } + + return token0; + } + + function tryParseMemberExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean, inObjectCreation: boolean): IMemberExpressionSyntax { + // Note: to make our lives simpler, we decompose the the NewExpression productions and + // place ObjectCreationExpression and FunctionExpression into PrimaryExpression. + // like so: + // + // PrimaryExpression : See 11.1 + // this + // Identifier + // Literal + // ArrayLiteral + // ObjectLiteral + // (Expression) + // FunctionExpression + // new MemberExpression Arguments? + // + // MemberExpression : See 11.2 + // PrimaryExpression + // MemberExpression[Expression] + // MemberExpression.IdentifierName + // + // CallExpression : See 11.2 + // MemberExpression + // CallExpression Arguments + // CallExpression[Expression] + // CallExpression.IdentifierName + // + // Technically this is ambiguous. i.e. CallExpression defines: + // + // CallExpression: + // CallExpression Arguments + // + // If you see: "new Foo()" + // + // Then that could be treated as a single ObjectCreationExpression, or it could be + // treated as the invocation of "new Foo". We disambiguate that in code (to match + // the original grammar) by making sure that if we see an ObjectCreationExpression + // we always consume arguments if they are there. So we treat "new Foo()" as an + // object creation only, and not at all as an invocation) Another way to think + // about this is that for every "new" that we see, we will consume an argument list if + // it is there as part of the *associated* object creation node. Any additional + // argument lists we see, will become invocation expressions. + // + // Because there are no other places in the grammar now that refer to FunctionExpression + // or ObjectCreationExpression, it is safe to push down into the PrimaryExpression + // production. + // + // Because CallExpression and MemberExpression are left recursive, we need to bottom out + // of the recursion immediately. So we parse out a primary expression to start with. + var expression: IMemberExpressionSyntax = tryParsePrimaryExpression(_currentToken, force); + if (expression === null) { + return null; + } + + return parseMemberExpressionRest(expression, inObjectCreation); + } + + function parseCallExpressionRest(expression: ILeftHandSideExpressionSyntax): ILeftHandSideExpressionSyntax { + while (true) { + var _currentToken = currentToken(); + var currentTokenKind = _currentToken.kind(); + + switch (currentTokenKind) { + case SyntaxKind.OpenParenToken: + expression = new syntaxFactory.InvocationExpressionSyntax(parseNodeData, expression, parseArgumentList(/*typeArgumentList:*/ null)); + continue; + + case SyntaxKind.LessThanToken: + // See if this is the start of a generic invocation. If so, consume it and + // keep checking for postfix expressions. Otherwise, it's just a '<' that's + // part of an arithmetic expression. Break out so we consume it higher in the + // stack. + var argumentList = tryParseArgumentList(); + if (argumentList === null) { + break; + } + + expression = new syntaxFactory.InvocationExpressionSyntax(parseNodeData, expression, argumentList); + continue; + + case SyntaxKind.OpenBracketToken: + expression = parseElementAccessExpression(expression, _currentToken, /*inObjectCreation:*/ false); + continue; + + case SyntaxKind.DotToken: + expression = new syntaxFactory.MemberAccessExpressionSyntax(parseNodeData, expression, consumeToken(_currentToken), eatIdentifierNameToken()); + continue; + } + + return expression; + } + } + + function parseMemberExpressionRest(expression: IMemberExpressionSyntax, inObjectCreation: boolean): IMemberExpressionSyntax { + while (true) { + var _currentToken = currentToken(); + var currentTokenKind = _currentToken.kind(); + + switch (currentTokenKind) { + case SyntaxKind.OpenBracketToken: + expression = parseElementAccessExpression(expression, _currentToken, inObjectCreation); + continue; + + case SyntaxKind.DotToken: + expression = new syntaxFactory.MemberAccessExpressionSyntax(parseNodeData, expression, consumeToken(_currentToken), eatIdentifierNameToken()); + continue; + } + + return expression; + } + } + + function tryParseLeftHandSideExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean): ILeftHandSideExpressionSyntax { + // Original Ecma: + // LeftHandSideExpression: See 11.2 + // NewExpression + // CallExpression + // + // Our simplification: + // + // LeftHandSideExpression: See 11.2 + // MemberExpression + // CallExpression + // + // See comment in parseMemberExpressionOrHigher on how we replaced NewExpression with + // MemberExpression to make our lives easier. + // + // to best understand the below code, it's important to see how CallExpression expands + // out into its own productions: + // + // CallExpression: + // MemberExpression Arguments + // CallExpression Arguments + // CallExpression[Expression] + // CallExpression.IdentifierName + // super ( ArgumentListopt ) + // super.IdentifierName + // + // Because of the recursion in these calls, we need to bottom out first. There are two + // bottom out states we can run into. Either we see 'super' which must start either of + // the last two CallExpression productions. Or we have a MemberExpression which either + // completes the LeftHandSideExpression, or starts the beginning of the first four + // CallExpression productions. + + var expression: ILeftHandSideExpressionSyntax = null; + if (_currentToken.kind() === SyntaxKind.SuperKeyword) { + expression = parseSuperExpression(_currentToken); + } + else { + expression = tryParseMemberExpressionOrHigher(_currentToken, force, /*inObjectCreation:*/ false); + if (expression === null) { + return null; + } + } + + // Now, we *may* be complete. However, we might have consumed the start of a + // CallExpression. As such, we need to consume the rest of it here to be complete. + return parseCallExpressionRest(expression); + } + + function parseSuperExpression(superToken: ISyntaxToken): ILeftHandSideExpressionSyntax { + var expression: ILeftHandSideExpressionSyntax = consumeToken(superToken); + + // If we have seen "super" it must be followed by '(' or '.'. + // If it wasn't then just try to parse out a '.' and report an error. + var currentTokenKind = currentToken().kind(); + return currentTokenKind === SyntaxKind.OpenParenToken || currentTokenKind === SyntaxKind.DotToken + ? expression + : new syntaxFactory.MemberAccessExpressionSyntax(parseNodeData, expression, eatToken(SyntaxKind.DotToken), eatIdentifierNameToken()); + } + + function tryParsePostfixExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean): IPostfixExpressionSyntax { + var expression = tryParseLeftHandSideExpressionOrHigher(_currentToken, force); + if (expression === null) { + return null; + } + + var _currentToken = currentToken(); + var currentTokenKind = _currentToken.kind(); + + switch (currentTokenKind) { + case SyntaxKind.PlusPlusToken: + case SyntaxKind.MinusMinusToken: + // Because of automatic semicolon insertion, we should only consume the ++ or -- + // if it is on the same line as the previous token. + if (previousTokenHasTrailingNewLine(_currentToken)) { + break; + } + + return new syntaxFactory.PostfixUnaryExpressionSyntax(parseNodeData, expression, consumeToken(_currentToken)); + } + + return expression; + } + + function tryParseGenericArgumentList(): ArgumentListSyntax { + // Debug.assert(currentToken().kind() === SyntaxKind.LessThanToken); + // If we have a '<', then only parse this as a arugment list if the type arguments + // are complete and we have an open paren. if we don't, rewind and return nothing. + var rewindPoint = getRewindPoint(); + + var typeArgumentList = tryParseTypeArgumentList(/*inExpression:*/ true); + var token0 = currentToken(); + var tokenKind = token0.kind(); + + var isOpenParen = tokenKind === SyntaxKind.OpenParenToken; + var isDot = tokenKind === SyntaxKind.DotToken; + var isOpenParenOrDot = isOpenParen || isDot; + + var argumentList: ArgumentListSyntax = null; + if (typeArgumentList === null || !isOpenParenOrDot) { + // Wasn't generic. Rewind to where we started so this can be parsed as an + // arithmetic expression. + rewind(rewindPoint); + releaseRewindPoint(rewindPoint); + return null; + } + else { + releaseRewindPoint(rewindPoint); + // It's not uncommon for a user to type: "Foo." + // + // This is not legal in typescript (as an parameter list must follow the type + // arguments). We want to give a good error message for this as otherwise + // we'll bail out here and give a poor error message when we try to parse this + // as an arithmetic expression. + if (isDot) { + // A parameter list must follow a generic type argument list. + var diagnostic = new Diagnostic(fileName, source.text.lineMap(), start(token0, source.text), width(token0), + DiagnosticCode.A_parameter_list_must_follow_a_generic_type_argument_list_expected, null); + addDiagnostic(diagnostic); + + return new syntaxFactory.ArgumentListSyntax(parseNodeData, typeArgumentList, + Syntax.emptyToken(SyntaxKind.OpenParenToken), Syntax.emptySeparatedList(), Syntax.emptyToken(SyntaxKind.CloseParenToken)); + } + else { + return parseArgumentList(typeArgumentList); + } + } + } + + function tryParseArgumentList(): ArgumentListSyntax { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.LessThanToken) { + return tryParseGenericArgumentList(); + } + + if (tokenKind === SyntaxKind.OpenParenToken) { + return parseArgumentList(null); + } + + return null; + } + + function parseArgumentList(typeArgumentList: TypeArgumentListSyntax): ArgumentListSyntax { + var openParenToken = eatToken(SyntaxKind.OpenParenToken); + + // Don't use the name 'arguments' it prevents V8 from optimizing this method. + var _arguments = Syntax.emptySeparatedList(); + + if (openParenToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + _arguments = parseSeparatedSyntaxList(ListParsingState.ArgumentList_AssignmentExpressions, skippedTokens); + openParenToken = addSkippedTokensAfterToken(openParenToken, skippedTokens); + } + + return new syntaxFactory.ArgumentListSyntax(parseNodeData, typeArgumentList, openParenToken, _arguments, eatToken(SyntaxKind.CloseParenToken)); + } + + function tryParseArgumentListExpression(): IExpressionSyntax { + // Generally while parsing lists, we don't want to 'force' the parser to parse + // the item. That way, if the expected item isn't htere, we can bail out and + // move to a higher stage of list parsing. However, it's extremely common to + // see something like "Foo(, a". in this case, even though there isn't an expression + // after the open paren, we still want to force parsing an expression (which will + // cause a missing identiifer to be created), so that we will then consume the + // comma and the following list items). + var force = currentToken().kind() === SyntaxKind.CommaToken; + return tryParseAssignmentExpressionOrHigher(force, /*allowIn:*/ true); + } + + function parseElementAccessArgumentExpression(openBracketToken: ISyntaxToken, inObjectCreation: boolean) { + // It's not uncommon for a user to write: "new Type[]". Check for that common pattern + // and report a better error message. + if (inObjectCreation && currentToken().kind() === SyntaxKind.CloseBracketToken) { + var errorStart = start(openBracketToken, source.text); + var errorEnd = end(currentToken(), source.text); + var diagnostic = new Diagnostic(fileName, source.text.lineMap(), errorStart, errorEnd - errorStart, + DiagnosticCode.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead, null); + addDiagnostic(diagnostic); + + return Syntax.emptyToken(SyntaxKind.IdentifierName); + } + else { + return parseExpression(/*allowIn:*/ true); + } + } + + function parseElementAccessExpression(expression: ILeftHandSideExpressionSyntax, openBracketToken: ISyntaxToken, inObjectCreation: boolean): ElementAccessExpressionSyntax { + // Debug.assert(currentToken().kind() === SyntaxKind.OpenBracketToken); + return new syntaxFactory.ElementAccessExpressionSyntax(parseNodeData, expression, consumeToken(openBracketToken), + parseElementAccessArgumentExpression(openBracketToken, inObjectCreation), eatToken(SyntaxKind.CloseBracketToken)); + } + + function tryParsePrimaryExpression(_currentToken: ISyntaxToken, force: boolean): IPrimaryExpressionSyntax { + if (isIdentifier(_currentToken)) { + return eatIdentifierToken(); + } + + var currentTokenKind = _currentToken.kind(); + switch (currentTokenKind) { + case SyntaxKind.ThisKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.NumericLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.StringLiteral: + return consumeToken(_currentToken); + + case SyntaxKind.FunctionKeyword: return parseFunctionExpression(_currentToken); + case SyntaxKind.OpenBracketToken: return parseArrayLiteralExpression(_currentToken); + case SyntaxKind.OpenBraceToken: return parseObjectLiteralExpression(_currentToken); + case SyntaxKind.OpenParenToken: return parseParenthesizedExpression(_currentToken); + case SyntaxKind.NewKeyword: return parseObjectCreationExpression(_currentToken); + + case SyntaxKind.SlashToken: + case SyntaxKind.SlashEqualsToken: + // If we see a standalone / or /= and we're expecting a term, then try to reparse + // it as a regular expression. + var result = tryReparseDivideAsRegularExpression(); + + // If we get a result, then use it. Otherwise, create a missing identifier so + // that parsing can continue. Note: we do this even if 'force' is false. That's + // because we *do* want to consider a standalone / as an expression that should be + // returned from tryParseExpression even when 'force' is set to false. + return result || eatIdentifierToken(DiagnosticCode.Expression_expected); + } + + if (!force) { + return null; + } + + // Nothing else worked, report an error and produce a missing token. + return eatIdentifierToken(DiagnosticCode.Expression_expected); + } + + function tryReparseDivideAsRegularExpression(): IPrimaryExpressionSyntax { + // If we see a / or /= token, then that may actually be the start of a regex in certain + // contexts. + + // var currentToken = this.currentToken(); + // Debug.assert(SyntaxFacts.isAnyDivideToken(currentToken.kind())); + + // Ok, from our quick lexical check, this could be a place where a regular expression could + // go. Now we have to do a bunch of work. Ask the source to retrive the token at the + // current position again. But this time allow it to retrieve it as a regular expression. + var currentToken = currentContextualToken(); + + // Note: we *must* have gotten a /, /= or regular expression. Or else something went *very* + // wrong with our logic above. + // Debug.assert(SyntaxFacts.isAnyDivideOrRegularExpressionToken(currentToken.kind())); + + var tokenKind = currentToken.kind(); + if (tokenKind === SyntaxKind.SlashToken || tokenKind === SyntaxKind.SlashEqualsToken) { + // Still came back as a / or /=. This is not a regular expression literal. + return null; + } + else if (tokenKind === SyntaxKind.RegularExpressionLiteral) { + return consumeToken(currentToken); + } + else { + // Something *very* wrong happened. This is an internal parser fault that we need + // to figure out and fix. + throw Errors.invalidOperation(); + } + } + + function parseTypeOfExpression(typeOfKeyword: ISyntaxToken): TypeOfExpressionSyntax { + return new syntaxFactory.TypeOfExpressionSyntax(parseNodeData, consumeToken(typeOfKeyword), tryParseUnaryExpressionOrHigher(currentToken(), /*force:*/ true)); + } + + function parseDeleteExpression(deleteKeyword: ISyntaxToken): DeleteExpressionSyntax { + return new syntaxFactory.DeleteExpressionSyntax(parseNodeData, consumeToken(deleteKeyword), tryParseUnaryExpressionOrHigher(currentToken(), /*force:*/ true)); + } + + function parseVoidExpression(voidKeyword: ISyntaxToken): VoidExpressionSyntax { + return new syntaxFactory.VoidExpressionSyntax(parseNodeData, consumeToken(voidKeyword), tryParseUnaryExpressionOrHigher(currentToken(), /*force:*/ true)); + } + + function parseFunctionExpression(functionKeyword: ISyntaxToken): FunctionExpressionSyntax { + return new syntaxFactory.FunctionExpressionSyntax(parseNodeData, + consumeToken(functionKeyword), eatOptionalIdentifierToken(), + parseCallSignature(/*requireCompleteTypeParameterList:*/ false), + parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ true)); + } + + function parseObjectCreationExpression(newKeyword: ISyntaxToken): ObjectCreationExpressionSyntax { + // ObjectCreationExpression + // new MemberExpression Arguments? + // + // Note: if we see arguments we absolutely take them and attach them tightly to this + // object creation expression. + // + // See comment in tryParseMemberExpressionOrHigher for a more complete explanation of + // this decision. + + return new syntaxFactory.ObjectCreationExpressionSyntax(parseNodeData, + consumeToken(newKeyword), tryParseMemberExpressionOrHigher(currentToken(), /*force:*/ true, /*inObjectCreation:*/ true), tryParseArgumentList()); + } + + function parseCastExpression(lessThanToken: ISyntaxToken): CastExpressionSyntax { + return new syntaxFactory.CastExpressionSyntax(parseNodeData, + consumeToken(lessThanToken), parseType(), eatToken(SyntaxKind.GreaterThanToken), tryParseUnaryExpressionOrHigher(currentToken(), /*force:*/ true)); + } + + function parseParenthesizedExpression(openParenToken: ISyntaxToken): ParenthesizedExpressionSyntax { + return new syntaxFactory.ParenthesizedExpressionSyntax(parseNodeData, + consumeToken(openParenToken), parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken)); + } + + function tryParseParenthesizedArrowFunctionExpression(): ParenthesizedArrowFunctionExpressionSyntax { + var tokenKind = currentToken().kind(); + if (tokenKind !== SyntaxKind.OpenParenToken && tokenKind !== SyntaxKind.LessThanToken) { + return null; + } + + // Because arrow functions and parenthesized expressions look similar, we have to check far + // enough ahead to be sure we've actually got an arrow function. For example, both nodes can + // start with: + // (a = b, c = d, ..., e = f). + //So we effectively need infinite lookahead to decide which node we're in. + // + // First, check for things that definitely have enough information to let us know it's an + // arrow function. + + if (isDefinitelyArrowFunctionExpression()) { + // We have something like "() =>" or "(a) =>". Definitely a lambda, so parse it + // unilaterally as such. + return tryParseParenthesizedArrowFunctionExpressionWorker(/*requiresArrow:*/ false); + } + + // Now, look for cases where we're sure it's not an arrow function. This will help save us + // a costly parse. + if (!isPossiblyArrowFunctionExpression()) { + return null; + } + + // Then, try to actually parse it as a arrow function, and only return if we see an => + var rewindPoint = getRewindPoint(); + + var arrowFunction = tryParseParenthesizedArrowFunctionExpressionWorker(/*requiresArrow:*/ true); + if (arrowFunction === null) { + rewind(rewindPoint); + } + + releaseRewindPoint(rewindPoint); + return arrowFunction; + } + + function tryParseParenthesizedArrowFunctionExpressionWorker(requireArrow: boolean): ParenthesizedArrowFunctionExpressionSyntax { + var _currentToken = currentToken(); + // Debug.assert(currentToken.kind() === SyntaxKind.OpenParenToken || currentToken.kind() === SyntaxKind.LessThanToken); + + var callSignature = parseCallSignature(/*requireCompleteTypeParameterList:*/ true); + + if (requireArrow && currentToken().kind() !== SyntaxKind.EqualsGreaterThanToken) { + return null; + } + + var equalsGreaterThanToken = eatToken(SyntaxKind.EqualsGreaterThanToken); + + var block = tryParseArrowFunctionBlock(); + var expression: IExpressionSyntax = null; + if (block === null) { + expression = tryParseAssignmentExpressionOrHigher(/*force:*/ true, /*allowIn:*/ true); + } + + return new syntaxFactory.ParenthesizedArrowFunctionExpressionSyntax(parseNodeData, callSignature, equalsGreaterThanToken, block, expression); + } + + function tryParseArrowFunctionBlock(): BlockSyntax { + if (isBlock()) { + return parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ false); + } + else { + // We didn't have a block. However, we may be in an error situation. For example, + // if the user wrote: + // + // a => + // var v = 0; + // } + // + // (i.e. they're missing the open brace). See if that's the case so we can try to + // recover better. If we don't do this, then the next close curly we see may end + // up preemptively closing the containing construct. + var _modifierCount = modifierCount(); + if (isStatement(_modifierCount, /*inErrorRecovery:*/ false) && + !isExpressionStatement(currentToken()) && + !isFunctionDeclaration(_modifierCount)) { + // We've seen a statement (and it isn't an expressionStatement like 'foo()'), + // so treat this like a block with a missing open brace. + return parseBlock(/*parseStatementsEvenWithNoOpenBrace:*/ true, /*checkForStrictMode:*/ false); + } + else { + return null; + } + } + } + + function isSimpleArrowFunctionExpression(_currentToken: ISyntaxToken): boolean { + // ERROR RECOVERY TWEAK: + // If we see a standalone => try to parse it as an arrow function as that's likely what + // the user intended to write. + if (_currentToken.kind() === SyntaxKind.EqualsGreaterThanToken) { + return true; + } + + return isIdentifier(_currentToken) && + peekToken(1).kind() === SyntaxKind.EqualsGreaterThanToken; + } + + function parseSimpleArrowFunctionExpression(): SimpleArrowFunctionExpressionSyntax { + // Debug.assert(isSimpleArrowFunctionExpression()); + + var parameter = eatSimpleParameter(); + var equalsGreaterThanToken = eatToken(SyntaxKind.EqualsGreaterThanToken); + + var block = tryParseArrowFunctionBlock(); + var expression: IExpressionSyntax = null; + if (block === null) { + expression = tryParseAssignmentExpressionOrHigher(/*force:*/ true, /*allowIn:*/ true); + } + + return new syntaxFactory.SimpleArrowFunctionExpressionSyntax(parseNodeData, parameter, equalsGreaterThanToken, block, expression); + } + + function isBlock(): boolean { + return currentToken().kind() === SyntaxKind.OpenBraceToken; + } + + function isDefinitelyArrowFunctionExpression(): boolean { + var token0 = currentToken(); + if (token0.kind() !== SyntaxKind.OpenParenToken) { + // If it didn't start with an (, then it could be generic. That's too complicated + // and we can't say it's 'definitely' an arrow function. + return false; + } + + var token1 = peekToken(1); + var token1Kind = token1.kind(); + + var token2: ISyntaxToken; + + if (token1Kind === SyntaxKind.CloseParenToken) { + // () + // Definitely an arrow function. Could never be a parenthesized expression. + // *However*, because of error situations, we could end up with things like "().foo". + // In this case, we don't want to think of this as the start of an arrow function. + // To prevent this, we are a little stricter, and we require that we at least see: + // "():" or "() =>" or "() {}". Note: the last one is illegal. However it + // most likely is a missing => and not a parenthesized expression. + token2 = peekToken(2); + var token2Kind = token2.kind(); + return token2Kind === SyntaxKind.ColonToken || + token2Kind === SyntaxKind.EqualsGreaterThanToken || + token2Kind === SyntaxKind.OpenBraceToken; + } + + if (token1Kind === SyntaxKind.DotDotDotToken) { + // (... + // Definitely an arrow function. Could never be a parenthesized expression. + return true; + } + + token2 = peekToken(2); + token2Kind = token2.kind(); + + if (token1Kind === SyntaxKind.PublicKeyword || token1Kind === SyntaxKind.PrivateKeyword) { + if (isIdentifier(token2)) { + // "(public id" or "(function id". Definitely an arrow function. Could never + // be a parenthesized expression. Note: this will be an *illegal* arrow + // function (as accessibility modifiers are not allowed in it). However, that + // will be reported by the grammar checker walker. + return true; + } + } + + if (!isIdentifier(token1)) { + // All other arrow functions must start with (id + // so this is definitely not an arrow function. + return false; + } + + // (id + // + // Lots of options here. Check for things that make us certain it's an + // arrow function. + if (token2Kind === SyntaxKind.ColonToken) { + // (id: + // Definitely an arrow function. Could never be a parenthesized expression. + return true; + } + + var token3 = peekToken(3); + var token3Kind = token3.kind(); + if (token2Kind === SyntaxKind.QuestionToken) { + // (id? + // Could be an arrow function, or a parenthesized conditional expression. + + // Check for the things that could only be arrow functions. + if (token3Kind === SyntaxKind.ColonToken || + token3Kind === SyntaxKind.CloseParenToken || + token3Kind === SyntaxKind.CommaToken) { + // (id?: + // (id?) + // (id?, + // These are the only cases where this could be an arrow function. + // And none of them can be parenthesized expression. + return true; + } + } + + if (token2Kind === SyntaxKind.CloseParenToken) { + // (id) + // Could be an arrow function, or a parenthesized conditional expression. + + if (token3Kind === SyntaxKind.EqualsGreaterThanToken) { + // (id) => + // Definitely an arrow function. Could not be a parenthesized expression. + return true; + } + + // Note: "(id):" *looks* like it could be an arrow function. However, it could + // show up in: "foo ? (id): + // So we can't return true here for that case. + } + + // TODO: Add more cases if you're sure that there is enough information to know to + // parse this as an arrow function. Note: be very careful here. + + // Anything else wasn't clear enough. Try to parse the expression as an arrow function and bail out + // if we fail. + return false; + } + + function isPossiblyArrowFunctionExpression(): boolean { + var token0 = currentToken(); + if (token0.kind() !== SyntaxKind.OpenParenToken) { + // If it didn't start with an (, then it could be generic. That's too complicated + // and we have to say it's possibly an arrow function. + return true; + } + + var token1 = peekToken(1); + + if (!isIdentifier(token1)) { + // All other arrow functions must start with (id + // so this is definitely not an arrow function. + return false; + } + + var token2 = peekToken(2); + var token2Kind = token2.kind(); + if (token2Kind === SyntaxKind.EqualsToken) { + // (id = + // + // This *could* be an arrow function. i.e. (id = 0) => { } + // Or it could be a parenthesized expression. So we'll have to actually + // try to parse it. + return true; + } + + if (token2Kind === SyntaxKind.CommaToken) { + // (id, + + // This *could* be an arrow function. i.e. (id, id2) => { } + // Or it could be a parenthesized expression (as javascript supports + // the comma operator). So we'll have to actually try to parse it. + return true; + } + + if (token2Kind === SyntaxKind.CloseParenToken) { + // (id) + + var token3 = peekToken(3); + if (token3.kind() === SyntaxKind.ColonToken) { + // (id): + // + // This could be an arrow function. i.e. (id): number => { } + // Or it could be parenthesized exprssion: foo ? (id) : + // So we'll have to actually try to parse it. + return true; + } + } + + // Nothing else could be an arrow function. + return false; + } + + function parseObjectLiteralExpression(openBraceToken: ISyntaxToken): ObjectLiteralExpressionSyntax { + // Debug.assert(currentToken().kind() === SyntaxKind.OpenBraceToken); + + consumeToken(openBraceToken); + // Debug.assert(openBraceToken.fullWidth() > 0); + + var skippedTokens: ISyntaxToken[] = getArray(); + var propertyAssignments = parseSeparatedSyntaxList(ListParsingState.ObjectLiteralExpression_PropertyAssignments, skippedTokens); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + + return new syntaxFactory.ObjectLiteralExpressionSyntax(parseNodeData, openBraceToken, propertyAssignments, eatToken(SyntaxKind.CloseBraceToken)); + } + + function tryParsePropertyAssignment(inErrorRecovery: boolean): IPropertyAssignmentSyntax { + // Debug.assert(isPropertyAssignment(/*inErrorRecovery:*/ false)); + + if (isAccessor(modifierCount(), inErrorRecovery)) { + return parseAccessor(/*checkForStrictMode:*/ true); + } + else if (isFunctionPropertyAssignment(inErrorRecovery)) { + return parseFunctionPropertyAssignment(); + } + else if (isSimplePropertyAssignment(inErrorRecovery)) { + return parseSimplePropertyAssignment(); + } + else { + return null; + } + } + + function isPropertyAssignment(inErrorRecovery: boolean): boolean { + return isAccessor(modifierCount(), inErrorRecovery) || + isFunctionPropertyAssignment(inErrorRecovery) || + isSimplePropertyAssignment(inErrorRecovery); + } + + function eatPropertyName(): ISyntaxToken { + var _currentToken = currentToken(); + return SyntaxFacts.isIdentifierNameOrAnyKeyword(_currentToken) + ? eatIdentifierNameToken() + : consumeToken(_currentToken); + } + + function isFunctionPropertyAssignment(inErrorRecovery: boolean): boolean { + return isPropertyName(currentToken(), inErrorRecovery) && + isCallSignature(/*peekIndex:*/ 1); + } + + function parseFunctionPropertyAssignment(): FunctionPropertyAssignmentSyntax { + return new syntaxFactory.FunctionPropertyAssignmentSyntax(parseNodeData, + eatPropertyName(), parseCallSignature(/*requireCompleteTypeParameterList:*/ false), + parseBlock(/*parseBlockEvenWithNoOpenBrace:*/ false, /*checkForStrictMode:*/ true)); + } + + function isSimplePropertyAssignment(inErrorRecovery: boolean): boolean { + return isPropertyName(currentToken(), inErrorRecovery); + } + + function parseSimplePropertyAssignment(): SimplePropertyAssignmentSyntax { + return new syntaxFactory.SimplePropertyAssignmentSyntax(parseNodeData, + eatPropertyName(), eatToken(SyntaxKind.ColonToken), tryParseAssignmentExpressionOrHigher(/*force:*/ true, /*allowIn:*/ true)); + } + + function isPropertyName(token: ISyntaxToken, inErrorRecovery: boolean): boolean { + // NOTE: we do *not* want to check "isIdentifier" here. Any IdentifierName is + // allowed here, even reserved words like keywords. + if (SyntaxFacts.isIdentifierNameOrAnyKeyword(token)) { + // Except: if we're in error recovery, then we don't want to consider keywords. + // After all, if we have: + // + // { a: 1 + // return + // + // we don't want consider 'return' to be the next property in the object literal. + if (inErrorRecovery) { + return isIdentifier(token); + } + else { + return true; + } + } + + var kind = token.kind(); + return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral; + } + + function parseArrayLiteralExpression(openBracketToken: ISyntaxToken): ArrayLiteralExpressionSyntax { + // Debug.assert(currentToken().kind() === SyntaxKind.OpenBracketToken); + consumeToken(openBracketToken); + // Debug.assert(openBracketToken.fullWidth() > 0); + + var skippedTokens: ISyntaxToken[] = getArray(); + var expressions = parseSeparatedSyntaxList(ListParsingState.ArrayLiteralExpression_AssignmentExpressions, skippedTokens); + openBracketToken = addSkippedTokensAfterToken(openBracketToken, skippedTokens); + + return new syntaxFactory.ArrayLiteralExpressionSyntax(parseNodeData, openBracketToken, expressions, eatToken(SyntaxKind.CloseBracketToken)); + } + + function parseBlock(parseBlockEvenWithNoOpenBrace: boolean, checkForStrictMode: boolean): BlockSyntax { + var openBraceToken = eatToken(SyntaxKind.OpenBraceToken); + var statements = Syntax.emptyList(); + + if (parseBlockEvenWithNoOpenBrace || openBraceToken.fullWidth() > 0) { + var savedIsInStrictMode = isInStrictMode; + + var processItems = checkForStrictMode ? updateStrictModeState : null; + var skippedTokens: ISyntaxToken[] = getArray(); + var statements = parseSyntaxList(ListParsingState.Block_Statements, skippedTokens, processItems); + openBraceToken = addSkippedTokensAfterToken(openBraceToken, skippedTokens); + + setStrictMode(savedIsInStrictMode); + } + + return new syntaxFactory.BlockSyntax(parseNodeData, openBraceToken, statements, eatToken(SyntaxKind.CloseBraceToken)); + } + + function parseCallSignature(requireCompleteTypeParameterList: boolean): CallSignatureSyntax { + return new syntaxFactory.CallSignatureSyntax(parseNodeData, + tryParseTypeParameterList(requireCompleteTypeParameterList), parseParameterList(), parseOptionalTypeAnnotation(/*allowStringLiteral:*/ false)); + } + + function tryParseTypeParameterList(requireCompleteTypeParameterList: boolean): TypeParameterListSyntax { + var _currentToken = currentToken(); + if (_currentToken.kind() !== SyntaxKind.LessThanToken) { + return null; + } + + var rewindPoint = getRewindPoint(); + + var lessThanToken = consumeToken(_currentToken); + + var skippedTokens: ISyntaxToken[] = getArray(); + var typeParameters = parseSeparatedSyntaxList(ListParsingState.TypeParameterList_TypeParameters, skippedTokens); + lessThanToken = addSkippedTokensAfterToken(lessThanToken, skippedTokens); + + var greaterThanToken = eatToken(SyntaxKind.GreaterThanToken); + + // return null if we were required to have a '>' token and we did not have one. + if (requireCompleteTypeParameterList && greaterThanToken.fullWidth() === 0) { + rewind(rewindPoint); + releaseRewindPoint(rewindPoint); + return null; + } + else { + releaseRewindPoint(rewindPoint); + return new syntaxFactory.TypeParameterListSyntax(parseNodeData, lessThanToken, typeParameters, greaterThanToken); + } + } + + function isTypeParameter(): boolean { + return isIdentifier(currentToken()); + } + + function tryParseTypeParameter(): TypeParameterSyntax { + // Debug.assert(isTypeParameter()); + if (!isIdentifier(currentToken())) { + return null; + } + + return new syntaxFactory.TypeParameterSyntax(parseNodeData, eatIdentifierToken(), tryParseConstraint()); + } + + function tryParseConstraint(): ConstraintSyntax { + if (currentToken().kind() !== SyntaxKind.ExtendsKeyword) { + return null; + } + + return new syntaxFactory.ConstraintSyntax(parseNodeData, eatToken(SyntaxKind.ExtendsKeyword), parseTypeOrExpression()); + } + + function tryParseParameterList(): ParameterListSyntax { + if (currentToken().kind() === SyntaxKind.OpenParenToken) { + var token1 = peekToken(1); + + if (token1.kind() === SyntaxKind.CloseParenToken || isParameterHelper(token1)) { + return parseParameterList(); + } + } + + return null; + } + + function parseParameterList(): ParameterListSyntax { + var openParenToken = eatToken(SyntaxKind.OpenParenToken); + var parameters = Syntax.emptySeparatedList(); + + if (openParenToken.fullWidth() > 0) { + var skippedTokens: ISyntaxToken[] = getArray(); + parameters = parseSeparatedSyntaxList(ListParsingState.ParameterList_Parameters, skippedTokens); + openParenToken = addSkippedTokensAfterToken(openParenToken, skippedTokens); + } + + return new syntaxFactory.ParameterListSyntax(parseNodeData, openParenToken, parameters, eatToken(SyntaxKind.CloseParenToken)); + } + + function parseOptionalTypeAnnotation(allowStringLiteral: boolean): TypeAnnotationSyntax { + return currentToken().kind() === SyntaxKind.ColonToken ? parseTypeAnnotation(allowStringLiteral) : null; + } + + function parseTypeAnnotationType(allowStringLiteral: boolean): ITypeSyntax { + if (allowStringLiteral) { + var _currentToken = currentToken(); + if (_currentToken.kind() === SyntaxKind.StringLiteral) { + return consumeToken(_currentToken); + } + } + + return parseType(); + } + + function parseTypeAnnotation(allowStringLiteral: boolean): TypeAnnotationSyntax { + return new syntaxFactory.TypeAnnotationSyntax(parseNodeData, consumeToken(currentToken()), parseTypeAnnotationType(allowStringLiteral)); + } + + function isType(): boolean { + var _currentToken = currentToken(); + + switch (_currentToken.kind()) { + case SyntaxKind.TypeOfKeyword: + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.OpenBraceToken: + case SyntaxKind.OpenParenToken: + case SyntaxKind.LessThanToken: + case SyntaxKind.NewKeyword: + return true; + default: + return isIdentifier(_currentToken); + } + } + + function parseTypeOrExpression(): ISyntaxNodeOrToken { + var result: ISyntaxNodeOrToken = tryParseType(); + if (result) { + return result; + } + + var _currentToken = currentToken(); + if (isExpression(_currentToken)) { + // We parse out an expression here, but we very specifically ask for a unary + // expression, and not just any expression. That's because if we have: + // + // + // + // We do not want the > to be consumed as part of the "" expression. By starting + // at 'unary' expression and not 'binary' expression, we ensure that we don't accidently + // consume the >. + return tryParseUnaryExpressionOrHigher(_currentToken, /*force:*/ true); + } + + return eatIdentifierToken(DiagnosticCode.Type_expected); + } + + function parseType(): ITypeSyntax { + return tryParseType() || eatIdentifierToken(DiagnosticCode.Type_expected); + } + + function tryParseType(): ITypeSyntax { + // First consume any underlying element type. + var type = tryParseNonArrayType(); + + // ArrayType: + // ElementType [no LineTerminator here] [ ] + + // Now, we want to keep consuming pairs of brackets, as long as the opening bracket + // is on the same line as the last token. + while (type) { + var _currentToken = currentToken(); + + if (previousTokenHasTrailingNewLine(_currentToken) || + _currentToken.kind() !== SyntaxKind.OpenBracketToken) { + break; + } + + type = new syntaxFactory.ArrayTypeSyntax(parseNodeData, type, consumeToken(_currentToken), eatToken(SyntaxKind.CloseBracketToken)); + } + + return type; + } + + function parseTypeQuery(typeOfKeyword: ISyntaxToken): TypeQuerySyntax { + return new syntaxFactory.TypeQuerySyntax(parseNodeData, consumeToken(typeOfKeyword), parseName(/*allowIdentifierNames:*/ true)); + } + + function tryParseNonArrayType(): ITypeSyntax { + var _currentToken = currentToken(); + switch (_currentToken.kind()) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.StringKeyword: + // if any of these are followed by '.', then this is actually a module name, + // and these keywords will be reinterpreted as an identifier. + if (peekToken(1).kind() === SyntaxKind.DotToken) { + break; + } + + return consumeToken(_currentToken); + case SyntaxKind.OpenParenToken: + case SyntaxKind.LessThanToken: return tryParseFunctionType(); + case SyntaxKind.VoidKeyword: return consumeToken(_currentToken); + case SyntaxKind.OpenBraceToken: return parseObjectType(); + case SyntaxKind.NewKeyword: return parseConstructorType(); + case SyntaxKind.TypeOfKeyword: return parseTypeQuery(_currentToken); + } + + return tryParseNameOrGenericType(); + } + + function tryParseNameOrGenericType(): ITypeSyntax { + var name = tryParseName(/*allowIdentifierNames*/ false); + if (name === null) { + return null; + } + + // TypeReference: + // TypeName [no LineTerminator here] TypeArgumentsopt + // + // Only consume type arguments if they appear on the same line. + if (previousTokenHasTrailingNewLine(currentToken())) { + return name; + } + + var typeArgumentList = tryParseTypeArgumentList(/*inExpression:*/ false); + return typeArgumentList === null + ? name + : new syntaxFactory.GenericTypeSyntax(parseNodeData, name, typeArgumentList); + } + + function tryParseFunctionType(): FunctionTypeSyntax { + var typeParameterList = tryParseTypeParameterList(/*requireCompleteTypeParameterList:*/ false); + var parameterList: ParameterListSyntax = null; + if (typeParameterList === null) { + parameterList = tryParseParameterList(); + if (parameterList === null) { + return null; + } + } + else { + parameterList = parseParameterList(); + } + + return new syntaxFactory.FunctionTypeSyntax(parseNodeData, + typeParameterList, parameterList, eatToken(SyntaxKind.EqualsGreaterThanToken), parseType()); + } + + function parseConstructorType(): ConstructorTypeSyntax { + return new syntaxFactory.ConstructorTypeSyntax(parseNodeData, + eatToken(SyntaxKind.NewKeyword), tryParseTypeParameterList(/*requireCompleteTypeParameterList:*/ false), + parseParameterList(), eatToken(SyntaxKind.EqualsGreaterThanToken), parseType()); + } + + function isParameter(): boolean { + if (currentNode() !== null && currentNode().kind() === SyntaxKind.Parameter) { + return true; + } + + return isParameterHelper(currentToken()); + } + + function isParameterHelper(token: ISyntaxToken): boolean { + var tokenKind = token.kind(); + return tokenKind === SyntaxKind.DotDotDotToken || + isModifierKind(tokenKind) || + isIdentifier(token); + } + + function eatSimpleParameter() { + return new syntaxFactory.ParameterSyntax(parseNodeData, + /*dotDotDotToken:*/ null, /*modifiers:*/ Syntax.emptyList(), eatIdentifierToken(), + /*questionToken:*/ null, /*typeAnnotation:*/ null, /*equalsValueClause:*/ null); + } + + function tryParseParameter(): ParameterSyntax { + var node = currentNode(); + if (node !== null && node.kind() === SyntaxKind.Parameter) { + consumeNode(node); + return node; + } + + var dotDotDotToken = tryEatToken(SyntaxKind.DotDotDotToken); + var modifiers = parseModifiers(); + + // If we're not forcing, and we don't see anything to indicate this is a parameter, then + // bail out. + var _currentToken = currentToken(); + if (!isIdentifier(_currentToken) && dotDotDotToken === null && modifiers.length === 0) { + // ERROR RECOVERY: + // If we see a modifier alone in a parameter list, like: foo(static) + // + // then treat it like modifier, and continue parsing the parameter. + if (isModifierKind(_currentToken.kind())) { + modifiers = Syntax.list([consumeToken(_currentToken)]); + } + else { + return null; + } + } + + var identifier = eatIdentifierToken(); + var questionToken = tryEatToken(SyntaxKind.QuestionToken); + var typeAnnotation = parseOptionalTypeAnnotation(/*allowStringLiteral:*/ true); + + var equalsValueClause: EqualsValueClauseSyntax = null; + if (isEqualsValueClause(/*inParameter*/ true)) { + equalsValueClause = parseEqualsValueClause(/*allowIn:*/ true); + } + + return new syntaxFactory.ParameterSyntax(parseNodeData, dotDotDotToken, modifiers, identifier, questionToken, typeAnnotation, equalsValueClause); + } + + function parseSyntaxList( + currentListType: ListParsingState, skippedTokens: ISyntaxToken[], processItems: (items: any[]) => void = null): T[] { + var savedListParsingState = listParsingState; + listParsingState |= (1 << currentListType); + + var result = parseSyntaxListWorker(currentListType, skippedTokens, processItems); + + listParsingState = savedListParsingState; + + return result; + } + + function parseSeparatedSyntaxList(currentListType: ListParsingState, skippedTokens: ISyntaxToken[]): T[] { + var savedListParsingState = listParsingState; + listParsingState |= (1 << currentListType); + + var result = parseSeparatedSyntaxListWorker(currentListType, skippedTokens); + + listParsingState = savedListParsingState; + + return result; + } + + // Returns true if we should abort parsing. + function abortParsingListOrMoveToNextToken( + currentListType: ListParsingState, nodes: T[], separators: ISyntaxToken[], skippedTokens: ISyntaxToken[]): boolean { + // Ok. We're at a token that is not a terminator for the list and wasn't the start of + // an item in the list. Definitely report an error for this token. + reportUnexpectedTokenDiagnostic(currentListType); + + // Now, check if the token is a terminator for one our parent lists, or the start of an + // item in one of our parent lists. If so, we won't want to consume the token. We've + // already reported the error, so just return to our caller so that a higher up + // production can consume it. + for (var state = ListParsingState.LastListParsingState; state >= ListParsingState.FirstListParsingState; state--) { + if ((listParsingState & (1 << state)) !== 0) { + if (isExpectedListTerminator(state) || isExpectedListItem(state, /*inErrorRecovery:*/ true)) { + // Abort parsing this list. + return true; + } + } + } + + // Otherwise, if none of the lists we're in can capture this token, then we need to + // unilaterally skip it. Note: we've already reported an error above. + addSkippedTokenToList(nodes, separators, skippedTokens, consumeToken(currentToken())); + + // Continue parsing this list. Attach this token to whatever we've seen already. + return false; + } + + function addSkippedTokenToList( + nodes: T[], separators: ISyntaxToken[], skippedTokens: ISyntaxToken[], skippedToken: ISyntaxToken): void { + // Now, add this skipped token to the last item we successfully parsed in the list. Or + // add it to the list of skipped tokens if we haven't parsed anything. Our caller will + // have to deal with them. + // + // Note: we only bother doing this if we're creating a concrete syntax tree. + if (syntaxFactory.isConcrete) { + var length = nodes.length + (separators ? separators.length : 0); + + for (var i = length - 1; i >= 0; i--) { + var array: ISyntaxNodeOrToken[] = separators && (i % 2 === 1) ? separators : nodes; + var arrayIndex = separators ? IntegerUtilities.integerDivide(i, 2) : i; + + var item = array[arrayIndex]; + var _lastToken = lastToken(item); + if (_lastToken && _lastToken.fullWidth() > 0) { + array[arrayIndex] = addSkippedTokenAfterNodeOrToken(item, skippedToken); + return; + } + } + + // Didn't have anything in the list we could add to. Add to the skipped items array + // for our caller to handle. + skippedTokens.push(skippedToken); + } + } + + function tryParseExpectedListItem( + currentListType: ListParsingState, inErrorRecovery: boolean, items: ISyntaxElement[], processItems: (items: any[]) => void): boolean { + var item = tryParseExpectedListItemWorker(currentListType, inErrorRecovery); + + if (item === null) { + return false; + } + // Debug.assert(item !== null); + + items.push(item); + + if (processItems !== null) { + processItems(items); + } + + return true; + } + + function listIsTerminated(currentListType: ListParsingState): boolean { + return isExpectedListTerminator(currentListType) || + currentToken().kind() === SyntaxKind.EndOfFileToken; + } + + function parseSyntaxListWorker(currentListType: ListParsingState, skippedTokens: ISyntaxToken[], processItems: (items: any[]) => void ): T[] { + var items: T[] = getArray(); + + while (true) { + // Try to parse an item of the list. If we fail then decide if we need to abort or + // continue parsing. + var succeeded = tryParseExpectedListItem(currentListType, /*inErrorRecovery:*/ false, items, processItems); + + if (!succeeded) { + // We weren't able to parse out a list element. + + // That may have been because the list is complete. In that case, break out + // and return the items we were able parse. + if (listIsTerminated(currentListType)) { + break; + } + + // List wasn't complete and we didn't get an item. Figure out if we should bail out + // or skip a token and continue. + var abort = abortParsingListOrMoveToNextToken(currentListType, items, null, skippedTokens); + if (abort) { + break; + } + } + + // We either parsed an element. Or we failed to, but weren't at the end of the list + // and didn't want to abort. Continue parsing elements. + } + + var result = Syntax.list(items); + + // Can't return if it has more then 1 element. In that case, the list will have been + // copied into the SyntaxList. + returnZeroLengthArray(items); + + return result; + } + + function parseSeparatedSyntaxListWorker(currentListType: ListParsingState, skippedTokens: ISyntaxToken[]): T[] { + var nodes: T[] = getArray(); + var separators: ISyntaxToken[] = getArray(); + + // Debug.assert(nodes.length === 0); + // Debug.assert(separators.length === 0); + // Debug.assert(skippedTokens.length === 0); + // Debug.assert(skippedTokens !== nodes); + // Debug.assert(skippedTokens !== separators); + // Debug.assert(nodes !== separators); + + var _separatorKind = currentListType === ListParsingState.ObjectType_TypeMembers ? SyntaxKind.SemicolonToken : SyntaxKind.CommaToken; + var allowAutomaticSemicolonInsertion = _separatorKind === SyntaxKind.SemicolonToken; + + var inErrorRecovery = false; + while (true) { + // Try to parse an item of the list. If we fail then decide if we need to abort or + // continue parsing. + + // Debug.assert(oldItemsCount % 2 === 0); + var succeeded = tryParseExpectedListItem(currentListType, inErrorRecovery, nodes, null); + + if (!succeeded) { + // We weren't able to parse out a list element. + // Debug.assert(items === null || items.length % 2 === 0); + + // That may have been because the list is complete. In that case, break out + // and return the items we were able parse. + if (listIsTerminated(currentListType)) { + break; + } + + // List wasn't complete and we didn't get an item. Figure out if we should bail out + // or skip a token and continue. + var abort = abortParsingListOrMoveToNextToken(currentListType, nodes, separators, skippedTokens); + if (abort) { + break; + } + else { + // We just skipped a token. We're now in error recovery mode. + inErrorRecovery = true; + continue; + } + } + + // Debug.assert(newItemsCount % 2 === 1); + + // We were able to successfully parse out a list item. So we're no longer in error + // recovery. + inErrorRecovery = false; + + // Now, we have to see if we have a separator or not. If we do have a separator + // we've got to consume it and continue trying to parse list items. Note: we always + // allow 'comma' as a separator (for error tolerance). We will later do a post pass + // to report when a comma was used improperly in a list that needed semicolons. + var _currentToken = currentToken(); + var tokenKind = _currentToken.kind(); + if (tokenKind === _separatorKind || tokenKind === SyntaxKind.CommaToken) { + // Consume the last separator and continue parsing list elements. + separators.push(consumeToken(_currentToken)); + continue; + } + + // We didn't see the expected separator. There are two reasons this might happen. + // First, we may actually be at the end of the list. If we are, then we're done + // parsing list elements. + if (listIsTerminated(currentListType)) { + break; + } + + // Otherwise, it might be a case where we can parse out an implicit semicolon. + + // Note: it's important that we check this *after* the check above for + // 'listIsTerminated'. Consider the following case: + // + // { + // a // <-- just finished parsing 'a' + // } + // + // Automatic semicolon insertion rules state: "When, as the program is parsed from + // left to right, a token (called the offending token) is encountered that is not + // allowed by any production of the grammar". So we should only ever insert a + // semicolon if we couldn't consume something normally. in the above case, we can + // consume the '}' just fine. So ASI doesn't apply. + + if (allowAutomaticSemicolonInsertion && canEatAutomaticSemicolon(/*allowWithoutNewline:*/ false)) { + var semicolonToken = eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false) || Syntax.emptyToken(SyntaxKind.SemicolonToken); + separators.push(semicolonToken); + // Debug.assert(items.length % 2 === 0); + continue; + } + + // We weren't at the end of the list. And thre was no separator we could parse out. + // Try parse the separator we expected, and continue parsing more list elements. + // This time mark that we're in error recovery mode though. + // + // Note: trying to eat this token will emit the appropriate diagnostic. + separators.push(eatToken(_separatorKind)); + + // Now that we're in 'error recovery' mode we cantweak some parsing rules as + // appropriate. For example, if we have: + // + // var v = { a + // return + // + // Then we'll be missing the comma. As such, we want to parse 'return' in a less + // tolerant manner. Normally 'return' could be a property in an object literal. + // However, in error recovery mode, we do *not* want it to be. + // + // Continue trying to parse out list elements. + inErrorRecovery = true; + } + + var result = Syntax.separatedList(nodes, separators); + + // Can't return if it has more then 0 elements. In that case, the list will have been + // copied into the SyntaxList. + returnZeroLengthArray(nodes); + returnZeroLengthArray(separators); + + return result; + } + + function reportUnexpectedTokenDiagnostic(listType: ListParsingState): void { + var token = currentToken(); + + var diagnostic = new Diagnostic(fileName, source.text.lineMap(), + start(token, source.text), width(token), DiagnosticCode.Unexpected_token_0_expected, [getExpectedListElementType(listType)]); + addDiagnostic(diagnostic); + } + + function addDiagnostic(diagnostic: Diagnostic): void { + // Except: if we already have a diagnostic for this position, don't report another one. + if (diagnostics.length > 0 && + diagnostics[diagnostics.length - 1].start() === diagnostic.start()) { + return; + } + + diagnostics.push(diagnostic); + } + + function isExpectedListTerminator(currentListType: ListParsingState): boolean { + switch (currentListType) { + case ListParsingState.SourceUnit_ModuleElements: return isExpectedSourceUnit_ModuleElementsTerminator(); + case ListParsingState.ClassDeclaration_ClassElements: return isExpectedClassDeclaration_ClassElementsTerminator(); + case ListParsingState.ModuleDeclaration_ModuleElements: return isExpectedModuleDeclaration_ModuleElementsTerminator(); + case ListParsingState.SwitchStatement_SwitchClauses: return isExpectedSwitchStatement_SwitchClausesTerminator(); + case ListParsingState.SwitchClause_Statements: return isExpectedSwitchClause_StatementsTerminator(); + case ListParsingState.Block_Statements: return isExpectedBlock_StatementsTerminator(); + case ListParsingState.TryBlock_Statements: return isExpectedTryBlock_StatementsTerminator(); + case ListParsingState.CatchBlock_Statements: return isExpectedCatchBlock_StatementsTerminator(); + case ListParsingState.EnumDeclaration_EnumElements: return isExpectedEnumDeclaration_EnumElementsTerminator(); + case ListParsingState.ObjectType_TypeMembers: return isExpectedObjectType_TypeMembersTerminator(); + case ListParsingState.ClassOrInterfaceDeclaration_HeritageClauses: return isExpectedClassOrInterfaceDeclaration_HeritageClausesTerminator(); + case ListParsingState.HeritageClause_TypeNameList: return isExpectedHeritageClause_TypeNameListTerminator(); + case ListParsingState.VariableDeclaration_VariableDeclarators_AllowIn: return isExpectedVariableDeclaration_VariableDeclarators_AllowInTerminator(); + case ListParsingState.VariableDeclaration_VariableDeclarators_DisallowIn: return isExpectedVariableDeclaration_VariableDeclarators_DisallowInTerminator(); + case ListParsingState.ArgumentList_AssignmentExpressions: return isExpectedArgumentList_AssignmentExpressionsTerminator(); + case ListParsingState.ObjectLiteralExpression_PropertyAssignments: return isExpectedObjectLiteralExpression_PropertyAssignmentsTerminator(); + case ListParsingState.ArrayLiteralExpression_AssignmentExpressions: return isExpectedLiteralExpression_AssignmentExpressionsTerminator(); + case ListParsingState.ParameterList_Parameters: return isExpectedParameterList_ParametersTerminator(); + case ListParsingState.IndexSignature_Parameters: return isExpectedIndexSignature_ParametersTerminator(); + case ListParsingState.TypeArgumentList_Types: return isExpectedTypeArgumentList_TypesTerminator(); + case ListParsingState.TypeParameterList_TypeParameters: return isExpectedTypeParameterList_TypeParametersTerminator(); + default: + throw Errors.invalidOperation(); + } + } + + function isExpectedSourceUnit_ModuleElementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.EndOfFileToken; + } + + function isExpectedEnumDeclaration_EnumElementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedModuleDeclaration_ModuleElementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedObjectType_TypeMembersTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedObjectLiteralExpression_PropertyAssignmentsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedLiteralExpression_AssignmentExpressionsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBracketToken; + } + + function isExpectedTypeArgumentList_TypesTerminator(): boolean { + var token = currentToken(); + var tokenKind = token.kind(); + if (tokenKind === SyntaxKind.GreaterThanToken) { + return true; + } + + // If we're at a token that can follow the type argument list, then we'll also consider + // the list terminated. + if (canFollowTypeArgumentListInExpression(tokenKind)) { + return true; + } + + // TODO: add more cases as necessary for error tolerance. + return false; + } + + function isExpectedTypeParameterList_TypeParametersTerminator(): boolean { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.GreaterThanToken) { + return true; + } + + // These commonly follow type parameter lists. + if (tokenKind === SyntaxKind.OpenParenToken || + tokenKind === SyntaxKind.OpenBraceToken || + tokenKind === SyntaxKind.ExtendsKeyword || + tokenKind === SyntaxKind.ImplementsKeyword) { + return true; + } + + // TODO: add more cases as necessary for error tolerance. + return false; + } + + function isExpectedParameterList_ParametersTerminator(): boolean { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.CloseParenToken) { + return true; + } + + // We may also see a { in an error case. i.e.: + // function (a, b, c { + if (tokenKind === SyntaxKind.OpenBraceToken) { + return true; + } + + // We may also see a => in an error case. i.e.: + // (f: number => { ... } + if (tokenKind === SyntaxKind.EqualsGreaterThanToken) { + return true; + } + + return false; + } + + function isExpectedIndexSignature_ParametersTerminator() { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.CloseBracketToken) { + return true; + } + + // We may also see a { in an error case. i.e.: + // function (a, b, c { + if (tokenKind === SyntaxKind.OpenBraceToken) { + return true; + } + + return false; + } + + function isExpectedVariableDeclaration_VariableDeclarators_DisallowInTerminator(): boolean { + // This is the case when we're parsing variable declarations in a for/for-in statement. + var tokenKind = currentToken().kind(); + + if (tokenKind === SyntaxKind.SemicolonToken || + tokenKind === SyntaxKind.CloseParenToken) { + return true; + } + + if (tokenKind === SyntaxKind.InKeyword) { + return true; + } + + return false; + } + + function isExpectedVariableDeclaration_VariableDeclarators_AllowInTerminator(): boolean { + //// This is the case when we're parsing variable declarations in a variable statement. + + // ERROR RECOVERY TWEAK: + // For better error recovery, if we see a => then we just stop immediately. We've got an + // arrow function here and it's going to be very unlikely that we'll resynchronize and get + // another variable declaration. + if (currentToken().kind() === SyntaxKind.EqualsGreaterThanToken) { + return true; + } + + // We're done when we can eat a semicolon. + return canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false); + } + + function isExpectedClassOrInterfaceDeclaration_HeritageClausesTerminator(): boolean { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.OpenBraceToken || tokenKind === SyntaxKind.CloseBraceToken) { + return true; + } + + return false; + } + + function isExpectedHeritageClause_TypeNameListTerminator(): boolean { + var tokenKind = currentToken().kind(); + if (tokenKind === SyntaxKind.ExtendsKeyword || tokenKind === SyntaxKind.ImplementsKeyword) { + return true; + } + + if (isExpectedClassOrInterfaceDeclaration_HeritageClausesTerminator()) { + return true; + } + + return false; + } + + function isExpectedArgumentList_AssignmentExpressionsTerminator(): boolean { + var token0 = currentToken(); + var tokenKind = token0.kind(); + return tokenKind === SyntaxKind.CloseParenToken || + tokenKind === SyntaxKind.SemicolonToken; + } + + function isExpectedClassDeclaration_ClassElementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedSwitchStatement_SwitchClausesTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedSwitchClause_StatementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken || + isSwitchClause(); + } + + function isExpectedBlock_StatementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.CloseBraceToken; + } + + function isExpectedTryBlock_StatementsTerminator(): boolean { + var tokenKind = currentToken().kind(); + return tokenKind === SyntaxKind.CatchKeyword || + tokenKind === SyntaxKind.FinallyKeyword; + } + + function isExpectedCatchBlock_StatementsTerminator(): boolean { + return currentToken().kind() === SyntaxKind.FinallyKeyword; + } + + function isExpectedListItem(currentListType: ListParsingState, inErrorRecovery: boolean): any { + switch (currentListType) { + case ListParsingState.SourceUnit_ModuleElements: return isModuleElement(inErrorRecovery); + case ListParsingState.ClassDeclaration_ClassElements: return isClassElement(inErrorRecovery); + case ListParsingState.ModuleDeclaration_ModuleElements: return isModuleElement(inErrorRecovery); + case ListParsingState.SwitchStatement_SwitchClauses: return isSwitchClause(); + case ListParsingState.SwitchClause_Statements: return isStatement(modifierCount(), inErrorRecovery); + case ListParsingState.Block_Statements: return isStatement(modifierCount(), inErrorRecovery); + // These two are special. They're just augmentations of "Block_Statements" + // used so we can abort out of the try block if we see a 'catch' or 'finally' + // keyword. There are no additional list items that they add, so we just + // return 'false' here. + case ListParsingState.TryBlock_Statements: return false; + case ListParsingState.CatchBlock_Statements: return false; + case ListParsingState.EnumDeclaration_EnumElements: return isEnumElement(inErrorRecovery); + case ListParsingState.ObjectType_TypeMembers: return isTypeMember(inErrorRecovery); + case ListParsingState.ClassOrInterfaceDeclaration_HeritageClauses: return isHeritageClause(); + case ListParsingState.HeritageClause_TypeNameList: return isHeritageClauseTypeName(); + case ListParsingState.VariableDeclaration_VariableDeclarators_AllowIn: return isVariableDeclarator(); + case ListParsingState.VariableDeclaration_VariableDeclarators_DisallowIn: return isVariableDeclarator(); + case ListParsingState.ArgumentList_AssignmentExpressions: return isExpectedArgumentList_AssignmentExpression(); + case ListParsingState.ObjectLiteralExpression_PropertyAssignments: return isPropertyAssignment(inErrorRecovery); + case ListParsingState.ArrayLiteralExpression_AssignmentExpressions: return isAssignmentOrOmittedExpression(); + case ListParsingState.ParameterList_Parameters: return isParameter(); + case ListParsingState.IndexSignature_Parameters: return isParameter(); + case ListParsingState.TypeArgumentList_Types: return isType(); + case ListParsingState.TypeParameterList_TypeParameters: return isTypeParameter(); + default: throw Errors.invalidOperation(); + } + } + + function isExpectedArgumentList_AssignmentExpression(): boolean { + var _currentToken = currentToken(); + if (isExpression(_currentToken)) { + return true; + } + + // If we're on a comma then the user has written something like "Foo(a,," or "Foo(,". + // Instead of skipping the comma, create an empty expression to go before the comma + // so that the tree is more well formed and doesn't have skipped tokens. + if (_currentToken.kind() === SyntaxKind.CommaToken) { + return true; + } + + return false; + } + + function tryParseExpectedListItemWorker(currentListType: ListParsingState, inErrorRecovery: boolean): ISyntaxNodeOrToken { + switch (currentListType) { + case ListParsingState.SourceUnit_ModuleElements: return tryParseModuleElement(inErrorRecovery); + case ListParsingState.ClassDeclaration_ClassElements: return tryParseClassElement(inErrorRecovery); + case ListParsingState.ModuleDeclaration_ModuleElements: return tryParseModuleElement(inErrorRecovery); + case ListParsingState.SwitchStatement_SwitchClauses: return tryParseSwitchClause(); + case ListParsingState.SwitchClause_Statements: return tryParseStatement(inErrorRecovery); + case ListParsingState.Block_Statements: return tryParseStatement(inErrorRecovery); + case ListParsingState.TryBlock_Statements: return tryParseStatement(inErrorRecovery); + case ListParsingState.CatchBlock_Statements: return tryParseStatement(inErrorRecovery); + case ListParsingState.EnumDeclaration_EnumElements: return tryParseEnumElement(inErrorRecovery); + case ListParsingState.ObjectType_TypeMembers: return tryParseTypeMember(inErrorRecovery); + case ListParsingState.ClassOrInterfaceDeclaration_HeritageClauses: return tryParseHeritageClause(); + case ListParsingState.HeritageClause_TypeNameList: return tryParseHeritageClauseTypeName(); + case ListParsingState.VariableDeclaration_VariableDeclarators_AllowIn: return tryParseVariableDeclarator(/*allowIn:*/ true, /*allowIdentifierName:*/ false); + case ListParsingState.VariableDeclaration_VariableDeclarators_DisallowIn: return tryParseVariableDeclarator(/*allowIn:*/ false, /*allowIdentifierName:*/ false); + case ListParsingState.ArgumentList_AssignmentExpressions: return tryParseArgumentListExpression(); + case ListParsingState.ObjectLiteralExpression_PropertyAssignments: return tryParsePropertyAssignment(inErrorRecovery); + case ListParsingState.ArrayLiteralExpression_AssignmentExpressions: return tryParseAssignmentOrOmittedExpression(); + case ListParsingState.ParameterList_Parameters: return tryParseParameter(); + case ListParsingState.IndexSignature_Parameters: return tryParseParameter(); + case ListParsingState.TypeArgumentList_Types: return tryParseType(); + case ListParsingState.TypeParameterList_TypeParameters: return tryParseTypeParameter(); + default: throw Errors.invalidOperation(); + } + } + + function getExpectedListElementType(currentListType: ListParsingState): string { + switch (currentListType) { + case ListParsingState.SourceUnit_ModuleElements: return getLocalizedText(DiagnosticCode.module_class_interface_enum_import_or_statement, null); + case ListParsingState.ClassOrInterfaceDeclaration_HeritageClauses: return '{'; + case ListParsingState.ClassDeclaration_ClassElements: return getLocalizedText(DiagnosticCode.constructor_function_accessor_or_variable, null); + case ListParsingState.ModuleDeclaration_ModuleElements: return getLocalizedText(DiagnosticCode.module_class_interface_enum_import_or_statement, null); + case ListParsingState.SwitchStatement_SwitchClauses: return getLocalizedText(DiagnosticCode.case_or_default_clause, null); + case ListParsingState.SwitchClause_Statements: return getLocalizedText(DiagnosticCode.statement, null); + case ListParsingState.Block_Statements: return getLocalizedText(DiagnosticCode.statement, null); + case ListParsingState.VariableDeclaration_VariableDeclarators_AllowIn: return getLocalizedText(DiagnosticCode.identifier, null); + case ListParsingState.VariableDeclaration_VariableDeclarators_DisallowIn: return getLocalizedText(DiagnosticCode.identifier, null); + case ListParsingState.EnumDeclaration_EnumElements: return getLocalizedText(DiagnosticCode.identifier, null); + case ListParsingState.ObjectType_TypeMembers: return getLocalizedText(DiagnosticCode.call_construct_index_property_or_function_signature, null); + case ListParsingState.ArgumentList_AssignmentExpressions: return getLocalizedText(DiagnosticCode.expression, null); + case ListParsingState.HeritageClause_TypeNameList: return getLocalizedText(DiagnosticCode.type_name, null); + case ListParsingState.ObjectLiteralExpression_PropertyAssignments: return getLocalizedText(DiagnosticCode.property_or_accessor, null); + case ListParsingState.ParameterList_Parameters: return getLocalizedText(DiagnosticCode.parameter, null); + case ListParsingState.IndexSignature_Parameters: return getLocalizedText(DiagnosticCode.parameter, null); + case ListParsingState.TypeArgumentList_Types: return getLocalizedText(DiagnosticCode.type, null); + case ListParsingState.TypeParameterList_TypeParameters: return getLocalizedText(DiagnosticCode.type_parameter, null); + case ListParsingState.ArrayLiteralExpression_AssignmentExpressions: return getLocalizedText(DiagnosticCode.expression, null); + default: throw Errors.invalidOperation(); + } + } + + return parseSyntaxTree; + } + + // The precedence of expressions in typescript. While we're parsing an expression, we will + // continue to consume and form new trees if the precedence is *strictly* greater than our current + // precedence. For example, if we have: a + b * c, we will first parse 'a' with precedence 1 (Lowest). + // We will then see the + with precedence 10. 10 is greater than 1 so we will decide to create + // a binary expression with the result of parsing the sub expression "b * c". We'll then parse + // the term 'b' (passing in precedence 10). We will then see the * with precedence 11. 11 is + // greater than 10, so we will create a binary expression from "b" and "c", return that, and + // join it with "a" producing: + // + // + + // / \ + // a * + // / \ + // b c + // + // If we instead had: "a * b + c", we would first parser 'a' with precedence 1 (lowest). We would then see + // the * with precedence 11. 11 is greater than 1 so we will decide to create a binary expression + // with the result of parsing the sub expression "b + c". We'll then parse the term 'b' (passing in + // precedence 11). We will then see the + with precedence 10. 10 is less than 11, so we won't + // continue parsing subexpressions and will just return the expression 'b'. The caller will join + // that into "a * b" (and will be back at precedence 1). It will then see the + with precedence 10. + // 10 is greater than 1 so it will parse the sub expression and make a binary expression out of it + // producing: + // + // + + // / \ + // * c + // / \ + // a b + // + // Note: because all these binary expressions have left-to-right precedence, if we see a * b * c + // then we parse it as: + // + // * + // / \ + // * c + // / \ + // a b + // + // The code to do this uses the above logic. It will see an operator with the same precedence, + // and so it won't consume it. + enum BinaryExpressionPrecedence { + Lowest = 1, + + // Intuitively, logical || have the lowest precedence. "a || b && c" is "a || (b && c)", not + // "(a || b) && c" + LogicalOrExpressionPrecedence = 2, + LogicalAndExpressionPrecedence = 3, + BitwiseOrExpressionPrecedence = 4, + BitwiseExclusiveOrExpressionPrecedence = 5, + BitwiseAndExpressionPrecedence = 6, + EqualityExpressionPrecedence = 7, + RelationalExpressionPrecedence = 8, + ShiftExpressionPrecdence = 9, + AdditiveExpressionPrecedence = 10, + + // Intuitively, multiplicative expressions have the highest precedence. After all, if you have: + // a + b * c + // + // Then you have "a + (b * c)" not "(a + b) * c" + MultiplicativeExpressionPrecedence = 11, + } + + // The current state of the parser wrt to list parsing. The way to read these is as: + // CurrentProduction_SubList. i.e. "Block_Statements" means "we're parsing a Block, and we're + // currently parsing list of statements within it". This is used by the list parsing mechanism + // to parse the elements of the lists, and recover from errors we encounter when we run into + // unexpected code. + // + // For example, when we are in ArgumentList_Arguments, we will continue trying to consume code + // as long as "isArgument" is true. If we run into a token for which "isArgument" is not true + // we will do the following: + // + // If the token is a StopToken for ArgumentList_Arguments (like ")" ) then we will stop parsing + // the list of arguments with no error. + // + // Otherwise, we *do* report an error for this unexpected token, and then enter error recovery + // mode to decide how to try to recover from this unexpected token. + // + // Error recovery will walk up the list of states we're in seeing if the token is a stop token + // for that construct *or* could start another element within what construct. For example, if + // the unexpected token was '}' then that would be a stop token for Block_Statements. + // Alternatively, if the unexpected token was 'return', then that would be a start token for + // the next statment in Block_Statements. + // + // If either of those cases are true, We will then return *without* consuming that token. + // (Remember, we've already reported an error). Now we're just letting the higher up parse + // constructs eventually try to consume that token. + // + // If none of the higher up states consider this a stop or start token, then we will simply + // consume the token and add it to our list of 'skipped tokens'. We will then repeat the + // above algorithm until we resynchronize at some point. + enum ListParsingState { + SourceUnit_ModuleElements = 0, + ClassDeclaration_ClassElements = 1, + ModuleDeclaration_ModuleElements = 2, + SwitchStatement_SwitchClauses = 3, + SwitchClause_Statements = 4, + Block_Statements = 5, + TryBlock_Statements = 6, + CatchBlock_Statements = 7, + EnumDeclaration_EnumElements = 8, + ObjectType_TypeMembers = 9, + ClassOrInterfaceDeclaration_HeritageClauses = 10, + HeritageClause_TypeNameList = 11, + VariableDeclaration_VariableDeclarators_AllowIn = 12, + VariableDeclaration_VariableDeclarators_DisallowIn = 13, + ArgumentList_AssignmentExpressions = 14, + ObjectLiteralExpression_PropertyAssignments = 15, + ArrayLiteralExpression_AssignmentExpressions = 16, + ParameterList_Parameters = 17, + IndexSignature_Parameters = 18, + TypeArgumentList_Types = 19, + TypeParameterList_TypeParameters = 20, + + FirstListParsingState = SourceUnit_ModuleElements, + LastListParsingState = TypeParameterList_TypeParameters, + } + + // We keep the parser around as a singleton. This is because calling createParser is actually + // expensive in V8 currently. We then clear it after a parse so that it doesn't keep state + // alive unintentionally. + var parseSyntaxTree = createParseSyntaxTree(); + + export function parse(fileName: string, text: ISimpleText, languageVersion: ts.ScriptTarget, isDeclaration: boolean): SyntaxTree { + return parseSource(Scanner.createParserSource(fileName, text, languageVersion), isDeclaration); + } + + export function parseSource(source: IParserSource, isDeclaration: boolean): SyntaxTree { + return parseSyntaxTree(source, isDeclaration); + } +} \ No newline at end of file diff --git a/src/services/syntax/prettyPrinter.ts b/src/services/syntax/prettyPrinter.ts new file mode 100644 index 00000000000..f19b9224f7e --- /dev/null +++ b/src/services/syntax/prettyPrinter.ts @@ -0,0 +1,1007 @@ +/// + +module TypeScript.PrettyPrinter { + export function prettyPrint(node: ISyntaxNode, indentWhitespace: string = " "): string { + var impl = new PrettyPrinterImpl(indentWhitespace); + visitNodeOrToken(impl, node); + return impl.result.join(""); + } + + class PrettyPrinterImpl implements ISyntaxVisitor { + public result: string[] = []; + private indentations: string[] = []; + private indentation: number = 0; + + constructor(private indentWhitespace: string) { + } + + private newLineCountBetweenModuleElements(element1: IModuleElementSyntax, element2: IModuleElementSyntax): number { + if (element1 === null || element2 === null) { + return 0; + } + + if (lastToken(element1).kind() === SyntaxKind.CloseBraceToken) { + return 2; + } + + return 1; + } + + private newLineCountBetweenClassElements(element1: IClassElementSyntax, element2: IClassElementSyntax): number { + if (element1 === null || element2 === null) { + return 0; + } + + return 1; + } + + private newLineCountBetweenStatements(element1: IClassElementSyntax, element2: IClassElementSyntax): number { + if (element1 === null || element2 === null) { + return 0; + } + + if (lastToken(element1).kind() === SyntaxKind.CloseBraceToken) { + return 2; + } + + return 1; + } + + private newLineCountBetweenSwitchClauses(element1: ISwitchClauseSyntax, element2: ISwitchClauseSyntax): number { + if (element1 === null || element2 === null) { + return 0; + } + + if (childCount(element1.statements) === 0) { + return 1; + } + + return 2; + } + + private ensureSpace(): void { + if (this.result.length > 0) { + var last = ArrayUtilities.last(this.result); + if (last !== " " && last !== "\r\n") { + this.appendText(" "); + } + } + } + + private ensureNewLine(): void { + if (this.result.length > 0) { + var last = ArrayUtilities.last(this.result); + if (last !== "\r\n") { + this.appendText("\r\n"); + } + } + } + + private appendNewLines(count: number): void { + for (var i = 0; i < count; i++) { + this.appendText("\r\n"); + } + } + + private getIndentation(count: number): string { + for (var i = this.indentations.length; i <= count; i++) { + var text = i === 0 + ? "" + : this.indentations[i - 1] + this.indentWhitespace; + this.indentations[i] = text; + } + + return this.indentations[count]; + } + + private appendIndentationIfAfterNewLine(): void { + if (this.result.length > 0) { + if (ArrayUtilities.last(this.result) === "\r\n") { + this.result.push(this.getIndentation(this.indentation)); + } + } + } + + private appendText(text: string): void { + this.result.push(text); + } + + private appendElement(element: ISyntaxElement): void { + if (isToken(element)) { + this.appendToken(element); + } + else if (isNode(element)) { + this.appendNode(element); + } + } + + private appendNode(node: ISyntaxNode): void { + visitNodeOrToken(this, node); + } + + private appendToken(token: ISyntaxToken): void { + if (token !== null && token.fullWidth() > 0) { + this.appendIndentationIfAfterNewLine(); + this.appendText(token.text()); + } + } + + public visitToken(token: ISyntaxToken): void { + this.appendToken(token); + } + + private appendSpaceList(list: ISyntaxNodeOrToken[]): void { + for (var i = 0, n = childCount(list); i < n; i++) { + if (isNode(childAt(list, i))) { + this.appendNode(childAt(list, i)); + } + else { + this.appendToken(childAt(list, i)); + } + + this.ensureSpace(); + } + } + + private appendSeparatorSpaceList(list: ISyntaxNodeOrToken[]): void { + for (var i = 0, n = childCount(list); i < n; i++) { + if (i % 2 === 0) { + if (i > 0) { + this.ensureSpace(); + } + + visitNodeOrToken(this, childAt(list, i)); + } + else { + this.appendToken(childAt(list, i)); + } + } + } + + private appendSeparatorNewLineList(list: ISyntaxNodeOrToken[]): void { + for (var i = 0, n = childCount(list); i < n; i++) { + if (i % 2 === 0) { + if (i > 0) { + this.ensureNewLine(); + } + + visitNodeOrToken(this, childAt(list, i)); + } + else { + this.appendToken(childAt(list, i)); + } + } + } + + private appendModuleElements(list: IModuleElementSyntax[]): void { + var lastModuleElement: IModuleElementSyntax = null; + for (var i = 0, n = list.length; i < n; i++) { + var moduleElement = list[i]; + var newLineCount = this.newLineCountBetweenModuleElements(lastModuleElement, moduleElement); + + this.appendNewLines(newLineCount); + visitNodeOrToken(this, moduleElement); + + lastModuleElement = moduleElement; + } + } + + public visitSourceUnit(node: SourceUnitSyntax): void { + this.appendModuleElements(node.moduleElements); + } + + public visitExternalModuleReference(node: ExternalModuleReferenceSyntax): void { + this.appendToken(node.requireKeyword); + this.appendToken(node.openParenToken); + this.appendToken(node.stringLiteral); + this.appendToken(node.closeParenToken); + } + + public visitModuleNameModuleReference(node: ModuleNameModuleReferenceSyntax): void { + visitNodeOrToken(this, node.moduleName); + } + + public visitImportDeclaration(node: ImportDeclarationSyntax): void { + this.appendToken(node.importKeyword); + this.ensureSpace(); + this.appendToken(node.identifier); + this.ensureSpace(); + this.appendToken(node.equalsToken); + this.ensureSpace(); + visitNodeOrToken(this, node.moduleReference); + this.appendToken(node.semicolonToken); + } + + public visitExportAssignment(node: ExportAssignmentSyntax): void { + this.appendToken(node.exportKeyword); + this.ensureSpace(); + this.appendToken(node.equalsToken); + this.ensureSpace(); + this.appendToken(node.identifier); + this.appendToken(node.semicolonToken); + } + + public visitClassDeclaration(node: ClassDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.classKeyword); + this.ensureSpace(); + this.appendToken(node.identifier); + this.appendNode(node.typeParameterList); + this.ensureSpace(); + this.appendSpaceList(node.heritageClauses); + this.ensureSpace(); + this.appendToken(node.openBraceToken); + this.ensureNewLine(); + + this.indentation++; + + var lastClassElement: IClassElementSyntax = null; + for (var i = 0, n = node.classElements.length; i < n; i++) { + var classElement = node.classElements[i]; + var newLineCount = this.newLineCountBetweenClassElements(lastClassElement, classElement); + + this.appendNewLines(newLineCount); + visitNodeOrToken(this, classElement); + + lastClassElement = classElement; + } + + this.indentation--; + + this.ensureNewLine(); + this.appendToken(node.closeBraceToken); + } + + public visitInterfaceDeclaration(node: InterfaceDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.interfaceKeyword); + this.ensureSpace(); + this.appendToken(node.identifier); + this.appendNode(node.typeParameterList); + this.ensureSpace(); + this.appendSpaceList(node.heritageClauses); + this.ensureSpace(); + this.appendObjectType(node.body, /*appendNewLines:*/ true); + } + + private appendObjectType(node: ObjectTypeSyntax, appendNewLines: boolean): void { + this.appendToken(node.openBraceToken); + + if (appendNewLines) { + this.ensureNewLine(); + this.indentation++; + } + else { + this.ensureSpace(); + } + + for (var i = 0, n = childCount(node.typeMembers); i < n; i++) { + visitNodeOrToken(this, childAt(node.typeMembers, i)); + + if (appendNewLines) { + this.ensureNewLine(); + } + else { + this.ensureSpace(); + } + } + + this.indentation--; + this.appendToken(node.closeBraceToken); + } + + public visitHeritageClause(node: HeritageClauseSyntax): void { + this.appendToken(node.extendsOrImplementsKeyword); + this.ensureSpace(); + this.appendSeparatorSpaceList(node.typeNames); + } + + public visitModuleDeclaration(node: ModuleDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.moduleKeyword); + this.ensureSpace(); + this.appendElement(node.name); + this.ensureSpace(); + this.appendToken(node.stringLiteral); + this.ensureSpace(); + + this.appendToken(node.openBraceToken); + this.ensureNewLine(); + + this.indentation++; + + this.appendModuleElements(node.moduleElements); + + this.indentation--; + this.appendToken(node.closeBraceToken); + } + + private appendBlockOrSemicolon(block: BlockSyntax, semicolonToken: ISyntaxToken) { + if (block) { + this.ensureSpace(); + visitNodeOrToken(this, block); + } + else { + this.appendToken(semicolonToken); + } + } + + public visitFunctionDeclaration(node: FunctionDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.functionKeyword); + this.ensureSpace(); + this.appendToken(node.identifier); + this.appendNode(node.callSignature); + this.appendBlockOrSemicolon(node.block, node.semicolonToken); + } + + public visitVariableStatement(node: VariableStatementSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + visitNodeOrToken(this, node.variableDeclaration); + this.appendToken(node.semicolonToken); + } + + public visitVariableDeclaration(node: VariableDeclarationSyntax): void { + this.appendToken(node.varKeyword); + this.ensureSpace(); + this.appendSeparatorSpaceList(node.variableDeclarators); + } + + public visitVariableDeclarator(node: VariableDeclaratorSyntax): void { + this.appendToken(node.propertyName); + this.appendNode(node.equalsValueClause); + } + + public visitEqualsValueClause(node: EqualsValueClauseSyntax): void { + this.ensureSpace(); + this.appendToken(node.equalsToken); + this.ensureSpace(); + visitNodeOrToken(this, node.value); + } + + public visitPrefixUnaryExpression(node: PrefixUnaryExpressionSyntax): void { + this.appendToken(node.operatorToken); + visitNodeOrToken(this, node.operand); + } + + public visitArrayLiteralExpression(node: ArrayLiteralExpressionSyntax): void { + this.appendToken(node.openBracketToken); + this.appendSeparatorSpaceList(node.expressions); + this.appendToken(node.closeBracketToken); + } + + public visitOmittedExpression(node: OmittedExpressionSyntax): void { + // Nothing to do. + } + + public visitParenthesizedExpression(node: ParenthesizedExpressionSyntax): void { + this.appendToken(node.openParenToken); + visitNodeOrToken(this, node.expression); + this.appendToken(node.closeParenToken); + } + + public visitSimpleArrowFunctionExpression(node: SimpleArrowFunctionExpressionSyntax): void { + this.appendNode(node.parameter); + this.ensureSpace(); + this.appendToken(node.equalsGreaterThanToken); + this.ensureSpace(); + this.appendNode(node.block); + this.appendElement(node.expression); + } + + public visitParenthesizedArrowFunctionExpression(node: ParenthesizedArrowFunctionExpressionSyntax): void { + visitNodeOrToken(this, node.callSignature); + this.ensureSpace(); + this.appendToken(node.equalsGreaterThanToken); + this.ensureSpace(); + this.appendNode(node.block); + this.appendElement(node.expression); + } + + public visitQualifiedName(node: QualifiedNameSyntax): void { + visitNodeOrToken(this, node.left); + this.appendToken(node.dotToken); + this.appendToken(node.right); + } + + public visitTypeQuery(node: TypeQuerySyntax): void { + this.appendToken(node.typeOfKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.name); + } + + public visitTypeArgumentList(node: TypeArgumentListSyntax): void { + this.appendToken(node.lessThanToken); + this.appendSeparatorSpaceList(node.typeArguments); + this.appendToken(node.greaterThanToken); + } + + public visitConstructorType(node: ConstructorTypeSyntax): void { + this.appendToken(node.newKeyword); + this.ensureSpace(); + this.appendNode(node.typeParameterList); + visitNodeOrToken(this, node.parameterList); + this.ensureSpace(); + this.appendToken(node.equalsGreaterThanToken); + this.ensureSpace(); + visitNodeOrToken(this, node.type); + } + + public visitFunctionType(node: FunctionTypeSyntax): void { + this.appendNode(node.typeParameterList); + visitNodeOrToken(this, node.parameterList); + this.ensureSpace(); + this.appendToken(node.equalsGreaterThanToken); + this.ensureSpace(); + visitNodeOrToken(this, node.type); + } + + public visitObjectType(node: ObjectTypeSyntax): void { + this.appendToken(node.openBraceToken); + this.ensureSpace(); + this.appendSeparatorSpaceList(node.typeMembers); + this.appendToken(node.closeBraceToken); + } + + public visitArrayType(node: ArrayTypeSyntax): void { + visitNodeOrToken(this, node.type); + this.appendToken(node.openBracketToken); + this.appendToken(node.closeBracketToken); + } + + public visitGenericType(node: GenericTypeSyntax): void { + visitNodeOrToken(this, node.name); + visitNodeOrToken(this, node.typeArgumentList); + } + + public visitTypeAnnotation(node: TypeAnnotationSyntax): void { + this.appendToken(node.colonToken); + this.ensureSpace(); + visitNodeOrToken(this, node.type); + } + + private appendStatements(statements: IStatementSyntax[]): void { + var lastStatement: IStatementSyntax = null; + for (var i = 0, n = statements.length; i < n; i++) { + var statement = statements[i]; + + var newLineCount = this.newLineCountBetweenStatements(lastStatement, statement); + + this.appendNewLines(newLineCount); + visitNodeOrToken(this, statement); + + lastStatement = statement; + } + } + + public visitBlock(node: BlockSyntax): void { + this.appendToken(node.openBraceToken); + this.ensureNewLine(); + this.indentation++; + + this.appendStatements(node.statements); + + this.indentation--; + this.ensureNewLine(); + this.appendToken(node.closeBraceToken); + } + + public visitParameter(node: ParameterSyntax): void { + this.appendToken(node.dotDotDotToken); + this.appendSpaceList(node.modifiers); + this.appendToken(node.identifier); + this.appendToken(node.questionToken); + this.appendNode(node.typeAnnotation); + this.appendNode(node.equalsValueClause); + } + + public visitMemberAccessExpression(node: MemberAccessExpressionSyntax): void { + visitNodeOrToken(this, node.expression); + this.appendToken(node.dotToken); + this.appendToken(node.name); + } + + public visitPostfixUnaryExpression(node: PostfixUnaryExpressionSyntax): void { + visitNodeOrToken(this, node.operand); + this.appendToken(node.operatorToken); + } + + public visitElementAccessExpression(node: ElementAccessExpressionSyntax): void { + visitNodeOrToken(this, node.expression); + this.appendToken(node.openBracketToken); + visitNodeOrToken(this, node.argumentExpression); + this.appendToken(node.closeBracketToken); + } + + public visitInvocationExpression(node: InvocationExpressionSyntax): void { + visitNodeOrToken(this, node.expression); + visitNodeOrToken(this, node.argumentList); + } + + public visitArgumentList(node: ArgumentListSyntax): void { + this.appendToken(node.openParenToken); + this.appendSeparatorSpaceList(node.arguments); + this.appendToken(node.closeParenToken); + } + + public visitBinaryExpression(node: BinaryExpressionSyntax): void { + visitNodeOrToken(this, node.left); + + if (node.kind() !== SyntaxKind.CommaExpression) { + this.ensureSpace(); + } + + this.appendToken(node.operatorToken); + this.ensureSpace(); + visitNodeOrToken(this, node.right); + } + + public visitConditionalExpression(node: ConditionalExpressionSyntax): void { + visitNodeOrToken(this, node.condition); + this.ensureSpace(); + this.appendToken(node.questionToken); + this.ensureSpace(); + visitNodeOrToken(this, node.whenTrue); + this.ensureSpace(); + this.appendToken(node.colonToken); + this.ensureSpace(); + visitNodeOrToken(this, node.whenFalse); + } + + public visitConstructSignature(node: ConstructSignatureSyntax): void { + this.appendToken(node.newKeyword); + visitNodeOrToken(this, node.callSignature); + } + + public visitMethodSignature(node: MethodSignatureSyntax): void { + this.appendToken(node.propertyName); + this.appendToken(node.questionToken); + visitNodeOrToken(this, node.callSignature); + } + + public visitIndexSignature(node: IndexSignatureSyntax): void { + this.appendToken(node.openBracketToken); + this.appendSeparatorSpaceList(node.parameters) + this.appendToken(node.closeBracketToken); + this.appendNode(node.typeAnnotation); + } + + public visitPropertySignature(node: PropertySignatureSyntax): void { + this.appendToken(node.propertyName); + this.appendToken(node.questionToken); + this.appendNode(node.typeAnnotation); + } + + public visitParameterList(node: ParameterListSyntax): void { + this.appendToken(node.openParenToken); + this.appendSeparatorSpaceList(node.parameters); + this.appendToken(node.closeParenToken); + } + + public visitCallSignature(node: CallSignatureSyntax): void { + this.appendNode(node.typeParameterList); + visitNodeOrToken(this, node.parameterList); + this.appendNode(node.typeAnnotation); + } + + public visitTypeParameterList(node: TypeParameterListSyntax): void { + this.appendToken(node.lessThanToken); + this.appendSeparatorSpaceList(node.typeParameters); + this.appendToken(node.greaterThanToken); + } + + public visitTypeParameter(node: TypeParameterSyntax): void { + this.appendToken(node.identifier); + this.appendNode(node.constraint); + } + + public visitConstraint(node: ConstraintSyntax): void { + this.ensureSpace(); + this.appendToken(node.extendsKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.typeOrExpression); + } + + private appendBlockOrStatement(node: IStatementSyntax): void { + if (node.kind() === SyntaxKind.Block) { + this.ensureSpace(); + visitNodeOrToken(this, node); + } + else { + this.ensureNewLine(); + this.indentation++; + visitNodeOrToken(this, node); + this.indentation--; + } + } + + public visitIfStatement(node: IfStatementSyntax): void { + this.appendToken(node.ifKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + visitNodeOrToken(this, node.condition); + this.appendToken(node.closeParenToken); + this.appendBlockOrStatement(node.statement); + this.appendNode(node.elseClause); + } + + public visitElseClause(node: ElseClauseSyntax): void { + this.ensureNewLine(); + this.appendToken(node.elseKeyword); + + if (node.statement.kind() === SyntaxKind.IfStatement) { + this.ensureSpace(); + visitNodeOrToken(this, node.statement); + } + else { + this.appendBlockOrStatement(node.statement); + } + } + + public visitExpressionStatement(node: ExpressionStatementSyntax): void { + visitNodeOrToken(this, node.expression); + this.appendToken(node.semicolonToken); + } + + public visitConstructorDeclaration(node: ConstructorDeclarationSyntax): void { + this.appendToken(node.constructorKeyword); + visitNodeOrToken(this, node.callSignature); + this.appendBlockOrSemicolon(node.block, node.semicolonToken); + } + + public visitIndexMemberDeclaration(node: IndexMemberDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + visitNodeOrToken(this, node.indexSignature); + this.appendToken(node.semicolonToken); + } + + public visitMemberFunctionDeclaration(node: MemberFunctionDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.propertyName); + visitNodeOrToken(this, node.callSignature); + this.appendBlockOrSemicolon(node.block, node.semicolonToken); + } + + public visitGetAccessor(node: GetAccessorSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.getKeyword); + this.ensureSpace(); + this.appendToken(node.propertyName); + visitNodeOrToken(this, node.callSignature); + this.ensureSpace(); + visitNodeOrToken(this, node.block); + } + + public visitSetAccessor(node: SetAccessorSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.setKeyword); + this.ensureSpace(); + this.appendToken(node.propertyName); + visitNodeOrToken(this, node.callSignature) + this.ensureSpace(); + visitNodeOrToken(this, node.block); + } + + public visitMemberVariableDeclaration(node: MemberVariableDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + visitNodeOrToken(this, node.variableDeclarator); + this.appendToken(node.semicolonToken); + } + + public visitThrowStatement(node: ThrowStatementSyntax): void { + this.appendToken(node.throwKeyword); + + if (node.expression) { + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + } + + this.appendToken(node.semicolonToken); + } + + public visitReturnStatement(node: ReturnStatementSyntax): void { + this.appendToken(node.returnKeyword); + + if (node.expression) { + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + } + + this.appendToken(node.semicolonToken); + } + + public visitObjectCreationExpression(node: ObjectCreationExpressionSyntax): void { + this.appendToken(node.newKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + this.appendNode(node.argumentList); + } + + public visitSwitchStatement(node: SwitchStatementSyntax): void { + this.appendToken(node.switchKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + visitNodeOrToken(this, node.expression); + this.appendToken(node.closeParenToken); + this.ensureSpace(); + this.appendToken(node.openBraceToken); + this.ensureNewLine(); + + var lastSwitchClause: ISwitchClauseSyntax = null; + for (var i = 0, n = node.switchClauses.length; i < n; i++) { + var switchClause = node.switchClauses[i]; + + var newLineCount = this.newLineCountBetweenSwitchClauses(lastSwitchClause, switchClause); + + this.appendNewLines(newLineCount); + visitNodeOrToken(this, switchClause); + + lastSwitchClause = switchClause; + } + + this.ensureNewLine(); + this.appendToken(node.closeBraceToken); + } + + private appendSwitchClauseStatements(node: ISwitchClauseSyntax): void { + if (childCount(node.statements) === 1 && childAt(node.statements, 0).kind() === SyntaxKind.Block) { + this.ensureSpace(); + visitNodeOrToken(this, childAt(node.statements, 0)); + } + else if (childCount(node.statements) > 0) { + this.ensureNewLine(); + + this.indentation++; + this.appendStatements(node.statements); + this.indentation--; + } + } + + public visitCaseSwitchClause(node: CaseSwitchClauseSyntax): void { + this.appendToken(node.caseKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + this.appendToken(node.colonToken); + this.appendSwitchClauseStatements(node); + } + + public visitDefaultSwitchClause(node: DefaultSwitchClauseSyntax): void { + this.appendToken(node.defaultKeyword); + this.appendToken(node.colonToken); + this.appendSwitchClauseStatements(node); + } + + public visitBreakStatement(node: BreakStatementSyntax): void { + this.appendToken(node.breakKeyword); + if (node.identifier) { + this.ensureSpace(); + this.appendToken(node.identifier); + } + + this.appendToken(node.semicolonToken); + } + + public visitContinueStatement(node: ContinueStatementSyntax): void { + this.appendToken(node.continueKeyword); + if (node.identifier) { + this.ensureSpace(); + this.appendToken(node.identifier); + } + + this.appendToken(node.semicolonToken); + } + + public visitForStatement(node: ForStatementSyntax): void { + this.appendToken(node.forKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + this.appendNode(node.variableDeclaration); + this.appendElement(node.initializer); + this.appendToken(node.firstSemicolonToken); + + if (node.condition) { + this.ensureSpace(); + visitNodeOrToken(this, node.condition); + } + + this.appendToken(node.secondSemicolonToken); + + if (node.incrementor) { + this.ensureSpace(); + visitNodeOrToken(this, node.incrementor); + } + + this.appendToken(node.closeParenToken); + this.appendBlockOrStatement(node.statement); + } + + public visitForInStatement(node: ForInStatementSyntax): void { + this.appendToken(node.forKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + this.appendNode(node.variableDeclaration); + this.appendElement(node.left); + this.ensureSpace(); + this.appendToken(node.inKeyword); + this.ensureSpace(); + this.appendElement(node.expression); + this.appendToken(node.closeParenToken); + this.appendBlockOrStatement(node.statement); + } + + public visitWhileStatement(node: WhileStatementSyntax): void { + this.appendToken(node.whileKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + visitNodeOrToken(this, node.condition); + this.appendToken(node.closeParenToken); + this.appendBlockOrStatement(node.statement); + } + + public visitWithStatement(node: WithStatementSyntax): void { + this.appendToken(node.withKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + visitNodeOrToken(this, node.condition); + this.appendToken(node.closeParenToken); + this.appendBlockOrStatement(node.statement); + } + + public visitEnumDeclaration(node: EnumDeclarationSyntax): void { + this.appendSpaceList(node.modifiers); + this.ensureSpace(); + this.appendToken(node.enumKeyword); + this.ensureSpace(); + this.appendToken(node.identifier); + this.ensureSpace(); + this.appendToken(node.openBraceToken); + this.ensureNewLine(); + + this.indentation++; + this.appendSeparatorNewLineList(node.enumElements); + this.indentation--; + + this.appendToken(node.closeBraceToken); + } + + public visitEnumElement(node: EnumElementSyntax): void { + this.appendToken(node.propertyName); + this.ensureSpace(); + this.appendNode(node.equalsValueClause); + } + + public visitCastExpression(node: CastExpressionSyntax): void { + this.appendToken(node.lessThanToken); + visitNodeOrToken(this, node.type); + this.appendToken(node.greaterThanToken); + visitNodeOrToken(this, node.expression); + } + + public visitObjectLiteralExpression(node: ObjectLiteralExpressionSyntax): void { + this.appendToken(node.openBraceToken); + + if (childCount(node.propertyAssignments) === 1) { + this.ensureSpace(); + visitNodeOrToken(this, childAt(node.propertyAssignments, 0)); + this.ensureSpace(); + } + else if (childCount(node.propertyAssignments) > 0) { + this.indentation++; + this.ensureNewLine(); + this.appendSeparatorNewLineList(node.propertyAssignments); + this.ensureNewLine(); + this.indentation--; + } + + this.appendToken(node.closeBraceToken); + } + + public visitSimplePropertyAssignment(node: SimplePropertyAssignmentSyntax): void { + this.appendToken(node.propertyName); + this.appendToken(node.colonToken); + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + } + + public visitFunctionPropertyAssignment(node: FunctionPropertyAssignmentSyntax): void { + this.appendToken(node.propertyName); + visitNodeOrToken(this, node.callSignature); + this.ensureSpace(); + visitNodeOrToken(this, node.block); + } + + public visitFunctionExpression(node: FunctionExpressionSyntax): void { + this.appendToken(node.functionKeyword); + + if (node.identifier) { + this.ensureSpace(); + this.appendToken(node.identifier); + } + + visitNodeOrToken(this, node.callSignature); + this.ensureSpace(); + visitNodeOrToken(this, node.block); + } + + public visitEmptyStatement(node: EmptyStatementSyntax): void { + this.appendToken(node.semicolonToken); + } + + public visitTryStatement(node: TryStatementSyntax): void { + this.appendToken(node.tryKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.block); + this.appendNode(node.catchClause); + this.appendNode(node.finallyClause); + } + + public visitCatchClause(node: CatchClauseSyntax): void { + this.ensureNewLine(); + this.appendToken(node.catchKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + this.appendToken(node.identifier); + this.appendToken(node.closeParenToken); + this.ensureSpace(); + visitNodeOrToken(this, node.block); + } + + public visitFinallyClause(node: FinallyClauseSyntax): void { + this.ensureNewLine(); + this.appendToken(node.finallyKeyword); + this.ensureNewLine(); + visitNodeOrToken(this, node.block); + } + + public visitLabeledStatement(node: LabeledStatementSyntax): void { + this.appendToken(node.identifier); + this.appendToken(node.colonToken); + this.appendBlockOrStatement(node.statement); + } + + public visitDoStatement(node: DoStatementSyntax): void { + this.appendToken(node.doKeyword); + this.appendBlockOrStatement(node.statement); + this.ensureNewLine(); + this.appendToken(node.whileKeyword); + this.ensureSpace(); + this.appendToken(node.openParenToken); + visitNodeOrToken(this, node.condition); + this.appendToken(node.closeParenToken); + this.appendToken(node.semicolonToken); + } + + public visitTypeOfExpression(node: TypeOfExpressionSyntax): void { + this.appendToken(node.typeOfKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + } + + public visitDeleteExpression(node: DeleteExpressionSyntax): void { + this.appendToken(node.deleteKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + } + + public visitVoidExpression(node: VoidExpressionSyntax): void { + this.appendToken(node.voidKeyword); + this.ensureSpace(); + visitNodeOrToken(this, node.expression); + } + + public visitDebuggerStatement(node: DebuggerStatementSyntax): void { + this.appendToken(node.debuggerKeyword); + this.appendToken(node.semicolonToken); + } + } +} \ No newline at end of file diff --git a/src/services/syntax/references.ts b/src/services/syntax/references.ts new file mode 100644 index 00000000000..a4eb9efc595 --- /dev/null +++ b/src/services/syntax/references.ts @@ -0,0 +1,49 @@ +/// + +/// +/// +/// +// /// +/// + +// Scanner depends on SyntaxKind and SyntaxFacts +/// +/// +/// + +/// +/// +/// +/// +/// +/// +/// +/// +/// + +// SyntaxDedenter depends on SyntaxRewriter +// /// +// SyntaxIndenter depends on SyntaxRewriter +// /// + +/// +/// +/// +/// +/// +/// + +// SyntaxInformationMap depends on SyntaxWalker +// /// + +// DepthLimitedWalker depends on PositionTrackingWalker +/// +/// + +// Concrete nodes depend on the parser. +/// + +// SyntaxTree depends on PositionTrackingWalker +/// + +/// diff --git a/src/services/syntax/scanner.ts b/src/services/syntax/scanner.ts new file mode 100644 index 00000000000..8f937fa540c --- /dev/null +++ b/src/services/syntax/scanner.ts @@ -0,0 +1,1678 @@ +/// + +module TypeScript.Scanner { + // Make sure we can encode a token's kind in 7 bits. + Debug.assert(SyntaxKind.LastToken <= 127); + + // Fixed width tokens (keywords and punctuation) that have no trivia generally make up 30% of + // all the tokens in a program. We heavily optimize for that case with a token instance that + // just needs a parent pointer and a single 30bit int like so: + // + // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0xxx xxxx <-- kind + // 0000 0000 0000 0000 0000 0000 0000 0000 00xx xxxx xxxx xxxx xxxx xxxx x000 0000 <-- full start + // ^ ^ ^ + // | | | + // Bit 64 Bit 30 Bit 1 + // + // This gives us 23 bits for the start of the token. We don't need to store the width as it + // can be inferred from the 'kind' for a fixed width token. + // + // For small tokens, we encode the data in one 30bit int like so: + // + // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0xxx xxxx <-- kind + // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 xxxx x000 0000 <-- full width + // 0000 0000 0000 0000 0000 0000 0000 0000 00xx xxxx xxxx xxxx xxxx 0000 0000 0000 <-- full start + // ^ ^ ^ + // | | | + // Bit 64 Bit 30 Bit 1 + // + // This allows for 5bits for teh width. i.e. tokens up to 31 chars in width. And 18 bits for + // the full start. This allows for tokens starting up to and including position 262,143. + // + // In practice, for codebases we have measured, these values are sufficient to cover ~85% of + // all tokens. If a token won't fit within those limits, we make a large token for it. + // + // + // For large tokens, we encode data with two 30 bit ints like so: + // + // _packedFullStartAndInfo: + // + // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 000x <-- has leading trivia + // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 00x0 <-- has trailing trivia + // 0000 0000 0000 0000 0000 0000 0000 0000 00xx xxxx xxxx xxxx xxxx xxxx xxxx xx00 <-- full start + // ^ ^ ^ + // | | | + // Bit 64 Bit 30 Bit 1 + // + // This gives us 28 bits for the start of the token. At 256MB That's more than enough for + // any codebase. + // + // _packedFullWidthAndKind: + // + // 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0xxx xxxx <-- kind + // 0000 0000 0000 0000 0000 0000 0000 0000 00xx xxxx xxxx xxxx xxxx xxxx x000 0000 <-- full width + // ^ ^ ^ + // | | | + // Bit 64 Bit 30 Bit 1 + // + // This gives us 23bit for width (or 8MB of width which should be enough for any codebase). + + enum ScannerConstants { + LargeTokenFullStartShift = 2, + LargeTokenFullWidthShift = 7, + + FixedWidthTokenFullStartShift = 7, + + SmallTokenFullWidthShift = 7, + SmallTokenFullStartShift = 12, + + KindMask = 0x7F, // 01111111 + IsVariableWidthMask = 0x80, // 10000000 + + LargeTokenLeadingTriviaBitMask = 0x01, // 00000001 + LargeTokenTrailingTriviaBitMask = 0x02, // 00000010 + + SmallTokenFullWidthMask = 0x1F, // 00011111 + + FixedWidthTokenMaxFullStart = 0x7FFFFF, // 23 ones. + SmallTokenMaxFullStart = 0x3FFFF, // 18 ones. + SmallTokenMaxFullWidth = 0x1F, // 5 ones + } + + // Make sure our math works for packing/unpacking large fullStarts. + Debug.assert(largeTokenUnpackFullStart(largeTokenPackFullStartAndInfo(1 << 28, 1, 1)) === (1 << 28)); + Debug.assert(largeTokenUnpackFullStart(largeTokenPackFullStartAndInfo(3 << 27, 0, 1)) === (3 << 27)); + Debug.assert(largeTokenUnpackFullStart(largeTokenPackFullStartAndInfo(10 << 25, 1, 0)) === (10 << 25)); + + function fixedWidthTokenPackData(fullStart: number, kind: SyntaxKind) { + return (fullStart << ScannerConstants.FixedWidthTokenFullStartShift) | kind; + } + + function fixedWidthTokenUnpackFullStart(packedData: number) { + return packedData >> ScannerConstants.FixedWidthTokenFullStartShift; + } + + function smallTokenPackData(fullStart: number, fullWidth: number, kind: SyntaxKind) { + return (fullStart << ScannerConstants.SmallTokenFullStartShift) | + (fullWidth << ScannerConstants.SmallTokenFullWidthShift) | + kind; + } + + function smallTokenUnpackFullWidth(packedData: number): SyntaxKind { + return (packedData >> ScannerConstants.SmallTokenFullWidthShift) & ScannerConstants.SmallTokenFullWidthMask; + } + + function smallTokenUnpackFullStart(packedData: number): number { + return packedData >> ScannerConstants.SmallTokenFullStartShift; + } + + function largeTokenPackFullStartAndInfo(fullStart: number, hasLeadingTriviaInfo: number, hasTrailingTriviaInfo: number): number { + return (fullStart << ScannerConstants.LargeTokenFullStartShift) | hasLeadingTriviaInfo | hasTrailingTriviaInfo; + } + + function largeTokenUnpackFullWidth(packedFullWidthAndKind: number) { + return packedFullWidthAndKind >> ScannerConstants.LargeTokenFullWidthShift; + } + + function largeTokenUnpackFullStart(packedFullStartAndInfo: number): number { + return packedFullStartAndInfo >> ScannerConstants.LargeTokenFullStartShift; + } + + function largeTokenUnpackHasLeadingTriviaInfo(packed: number): number { + return packed & ScannerConstants.LargeTokenLeadingTriviaBitMask; + } + + function largeTokenUnpackHasTrailingTriviaInfo(packed: number): number { + return packed & ScannerConstants.LargeTokenTrailingTriviaBitMask; + } + + var isKeywordStartCharacter: number[] = ArrayUtilities.createArray(CharacterCodes.maxAsciiCharacter, 0); + var isIdentifierStartCharacter: boolean[] = ArrayUtilities.createArray(CharacterCodes.maxAsciiCharacter, false); + var isIdentifierPartCharacter: boolean[] = ArrayUtilities.createArray(CharacterCodes.maxAsciiCharacter, false); + + for (var character = 0; character < CharacterCodes.maxAsciiCharacter; character++) { + if ((character >= CharacterCodes.a && character <= CharacterCodes.z) || + (character >= CharacterCodes.A && character <= CharacterCodes.Z) || + character === CharacterCodes._ || character === CharacterCodes.$) { + + isIdentifierStartCharacter[character] = true; + isIdentifierPartCharacter[character] = true; + } + else if (character >= CharacterCodes._0 && character <= CharacterCodes._9) { + isIdentifierPartCharacter[character] = true; + } + } + + for (var keywordKind = SyntaxKind.FirstKeyword; keywordKind <= SyntaxKind.LastKeyword; keywordKind++) { + var keyword = SyntaxFacts.getText(keywordKind); + isKeywordStartCharacter[keyword.charCodeAt(0)] = 1; + } + + export function isContextualToken(token: ISyntaxToken): boolean { + // These tokens are contextually created based on parsing decisions. We can't reuse + // them in incremental scenarios as we may be in a context where the parser would not + // create them. + switch (token.kind()) { + // Created by the parser when it sees / or /= in a location where it needs an expression. + case SyntaxKind.RegularExpressionLiteral: + + // Created by the parser when it sees > in a binary expression operator context. + case SyntaxKind.GreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case SyntaxKind.GreaterThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + return true; + + default: + return token.isKeywordConvertedToIdentifier(); + } + } + + var lastTokenInfo = { leadingTriviaWidth: -1, width: -1 }; + var lastTokenInfoTokenID: number = -1; + + var triviaScanner = createScannerInternal(ts.ScriptTarget.ES5, SimpleText.fromString(""), () => { }); + + interface IScannerToken extends ISyntaxToken { + } + + function fillSizeInfo(token: IScannerToken, text: ISimpleText): void { + if (lastTokenInfoTokenID !== syntaxID(token)) { + triviaScanner.fillTokenInfo(token, text, lastTokenInfo); + lastTokenInfoTokenID = syntaxID(token); + } + } + + function fullText(token: IScannerToken, text: ISimpleText): string { + return text.substr(token.fullStart(), token.fullWidth()); + } + + function leadingTrivia(token: IScannerToken, text: ISimpleText): ISyntaxTriviaList { + if (!token.hasLeadingTrivia()) { + return Syntax.emptyTriviaList; + } + + return triviaScanner.scanTrivia(token, text, /*isTrailing:*/ false); + } + + function trailingTrivia(token: IScannerToken, text: ISimpleText): ISyntaxTriviaList { + if (!token.hasTrailingTrivia()) { + return Syntax.emptyTriviaList; + } + + return triviaScanner.scanTrivia(token, text, /*isTrailing:*/ true); + } + + function leadingTriviaWidth(token: IScannerToken, text: ISimpleText): number { + if (!token.hasLeadingTrivia()) { + return 0; + } + + fillSizeInfo(token, text); + return lastTokenInfo.leadingTriviaWidth; + } + + function trailingTriviaWidth(token: IScannerToken, text: ISimpleText): number { + if (!token.hasTrailingTrivia()) { + return 0; + } + + fillSizeInfo(token, text); + return token.fullWidth() - lastTokenInfo.leadingTriviaWidth - lastTokenInfo.width; + } + + function tokenIsIncrementallyUnusable(token: IScannerToken): boolean { + // No scanner tokens make their *containing node* incrementally unusable. + // Note: several scanner tokens may themselves be unusable. i.e. if the parser asks + // for a full node, then that ndoe can be returned even if it contains parser generated + // tokens (like regexs and merged operator tokens). However, if the parser asks for a + // for a token, then those contextual tokens will not be reusable. + return false; + } + + class FixedWidthTokenWithNoTrivia implements ISyntaxToken { + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; public _typeBrand: any; + + constructor(private _packedData: number) { + } + + public setFullStart(fullStart: number): void { + this._packedData = fixedWidthTokenPackData(fullStart, this.kind()); + } + + public isIncrementallyUnusable(): boolean { return false; } + public isKeywordConvertedToIdentifier(): boolean { return false; } + public hasSkippedToken(): boolean { return false; } + public fullText(): string { return SyntaxFacts.getText(this.kind()); } + public text(): string { return this.fullText(); } + public leadingTrivia(): ISyntaxTriviaList { return Syntax.emptyTriviaList; } + public trailingTrivia(): ISyntaxTriviaList { return Syntax.emptyTriviaList; } + public leadingTriviaWidth(): number { return 0; } + public trailingTriviaWidth(): number { return 0; } + + public kind(): SyntaxKind { return this._packedData & ScannerConstants.KindMask; } + public fullWidth(): number { return this.fullText().length; } + public fullStart(): number { return fixedWidthTokenUnpackFullStart(this._packedData); } + public hasLeadingTrivia(): boolean { return false; } + public hasTrailingTrivia(): boolean { return false; } + public clone(): ISyntaxToken { return new FixedWidthTokenWithNoTrivia(this._packedData); } + } + + class LargeScannerToken implements ISyntaxToken { + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; public _typeBrand: any; + + private cachedText: string; + constructor(private _packedFullStartAndInfo: number, private _packedFullWidthAndKind: number, cachedText: string) { + if (cachedText !== undefined) { + this.cachedText = cachedText; + } + } + + public setFullStart(fullStart: number): void { + this._packedFullStartAndInfo = largeTokenPackFullStartAndInfo(fullStart, + largeTokenUnpackHasLeadingTriviaInfo(this._packedFullStartAndInfo), + largeTokenUnpackHasTrailingTriviaInfo(this._packedFullStartAndInfo)); + } + + private syntaxTreeText(text: ISimpleText) { + var result = text || syntaxTree(this).text; + Debug.assert(result); + return result; + } + + public isIncrementallyUnusable(): boolean { return tokenIsIncrementallyUnusable(this); } + public isKeywordConvertedToIdentifier(): boolean { return false; } + public hasSkippedToken(): boolean { return false; } + + public fullText(text?: ISimpleText): string { + return fullText(this, this.syntaxTreeText(text)); + } + + public text(): string { + var cachedText = this.cachedText; + return cachedText !== undefined ? cachedText : SyntaxFacts.getText(this.kind()); + } + + public leadingTrivia(text?: ISimpleText): ISyntaxTriviaList { return leadingTrivia(this, this.syntaxTreeText(text)); } + public trailingTrivia(text?: ISimpleText): ISyntaxTriviaList { return trailingTrivia(this, this.syntaxTreeText(text)); } + + public leadingTriviaWidth(text?: ISimpleText): number { + return leadingTriviaWidth(this, this.syntaxTreeText(text)); + } + + public trailingTriviaWidth(text?: ISimpleText): number { + return trailingTriviaWidth(this, this.syntaxTreeText(text)); + } + + public kind(): SyntaxKind { return this._packedFullWidthAndKind & ScannerConstants.KindMask; } + public fullWidth(): number { return largeTokenUnpackFullWidth(this._packedFullWidthAndKind); } + public fullStart(): number { return largeTokenUnpackFullStart(this._packedFullStartAndInfo); } + public hasLeadingTrivia(): boolean { return largeTokenUnpackHasLeadingTriviaInfo(this._packedFullStartAndInfo) !== 0; } + public hasTrailingTrivia(): boolean { return largeTokenUnpackHasTrailingTriviaInfo(this._packedFullStartAndInfo) !== 0; } + public clone(): ISyntaxToken { return new LargeScannerToken(this._packedFullStartAndInfo, this._packedFullWidthAndKind, this.cachedText); } + } + + export interface DiagnosticCallback { + (position: number, width: number, key: string, arguments: any[]): void; + } + + interface TokenInfo { + leadingTriviaWidth: number; + width: number; + } + + interface IScannerInternal extends IScanner { + fillTokenInfo(token: IScannerToken, text: ISimpleText, tokenInfo: TokenInfo): void; + scanTrivia(token: IScannerToken, text: ISimpleText, isTrailing: boolean): ISyntaxTriviaList; + } + + export interface IScanner { + setIndex(index: number): void; + scan(allowContextualToken: boolean): ISyntaxToken; + } + + export function createScanner(languageVersion: ts.ScriptTarget, text: ISimpleText, reportDiagnostic: DiagnosticCallback): IScanner { + var scanner = createScannerInternal(languageVersion, text, reportDiagnostic); + return { + setIndex: scanner.setIndex, + scan: scanner.scan, + }; + } + + function createScannerInternal(languageVersion: ts.ScriptTarget, text: ISimpleText, reportDiagnostic: DiagnosticCallback): IScannerInternal { + var str: string; + var index: number; + var start: number; + var end: number; + + function setIndex(_index: number) { + index = _index; + } + + function reset(_text: ISimpleText, _start: number, _end: number) { + Debug.assert(_start <= _text.length()); + Debug.assert(_end <= _text.length()); + + if (!str || text !== _text) { + text = _text; + str = _text.substr(0, _text.length()); + } + + start = _start; + end = _end; + index = _start; + } + + function scan(allowContextualToken: boolean): ISyntaxToken { + var fullStart = index; + var leadingTriviaInfo = scanTriviaInfo(/*isTrailing: */ false); + + var start = index; + var kindAndIsVariableWidth = scanSyntaxKind(allowContextualToken); + + var end = index; + var trailingTriviaInfo = scanTriviaInfo(/*isTrailing: */true); + + var fullWidth = index - fullStart; + + // If we have no trivia, and we are a fixed width token kind, and our size isn't too + // large, and we're a real fixed width token (and not something like "\u0076ar"). + var kind = kindAndIsVariableWidth & ScannerConstants.KindMask; + var isFixedWidth = kind >= SyntaxKind.FirstFixedWidth && kind <= SyntaxKind.LastFixedWidth && + ((kindAndIsVariableWidth & ScannerConstants.IsVariableWidthMask) === 0); + + if (isFixedWidth && + leadingTriviaInfo === 0 && trailingTriviaInfo === 0 && + fullStart <= ScannerConstants.FixedWidthTokenMaxFullStart && + (kindAndIsVariableWidth & ScannerConstants.IsVariableWidthMask) === 0) { + + return new FixedWidthTokenWithNoTrivia((fullStart << ScannerConstants.FixedWidthTokenFullStartShift) | kind); + } + else { + // inline the packing logic for perf. + var packedFullStartAndTriviaInfo = (fullStart << ScannerConstants.LargeTokenFullStartShift) | + leadingTriviaInfo | (trailingTriviaInfo << 1); + + var packedFullWidthAndKind = (fullWidth << ScannerConstants.LargeTokenFullWidthShift) | kind; + var cachedText = isFixedWidth ? undefined : text.substr(start, end - start); + return new LargeScannerToken(packedFullStartAndTriviaInfo, packedFullWidthAndKind, cachedText); + } + } + + function scanTrivia(parent: IScannerToken, text: ISimpleText, isTrailing: boolean): ISyntaxTriviaList { + var tokenFullStart = parent.fullStart(); + var tokenStart = tokenFullStart + leadingTriviaWidth(parent, text) + + if (isTrailing) { + reset(text, tokenStart + parent.text().length, tokenFullStart + parent.fullWidth()); + } + else { + reset(text, tokenFullStart, tokenStart); + } + // Debug.assert(length > 0); + + // Keep this exactly in sync with scanTriviaInfo + var trivia: ISyntaxTrivia[] = []; + + while (true) { + if (index < end) { + var ch = str.charCodeAt(index); + switch (ch) { + // Unicode 3.0 space characters + case CharacterCodes.space: + case CharacterCodes.nonBreakingSpace: + case CharacterCodes.enQuad: + case CharacterCodes.emQuad: + case CharacterCodes.enSpace: + case CharacterCodes.emSpace: + case CharacterCodes.threePerEmSpace: + case CharacterCodes.fourPerEmSpace: + case CharacterCodes.sixPerEmSpace: + case CharacterCodes.figureSpace: + case CharacterCodes.punctuationSpace: + case CharacterCodes.thinSpace: + case CharacterCodes.hairSpace: + case CharacterCodes.zeroWidthSpace: + case CharacterCodes.narrowNoBreakSpace: + case CharacterCodes.ideographicSpace: + + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.byteOrderMark: + // Normal whitespace. Consume and continue. + trivia.push(scanWhitespaceTrivia()); + continue; + + case CharacterCodes.slash: + // Potential comment. Consume if so. Otherwise, break out and return. + var ch2 = str.charCodeAt(index + 1); + if (ch2 === CharacterCodes.slash) { + trivia.push(scanSingleLineCommentTrivia()); + continue; + } + + if (ch2 === CharacterCodes.asterisk) { + trivia.push(scanMultiLineCommentTrivia()); + continue; + } + + // Not a comment. Don't consume. + throw Errors.invalidOperation(); + + case CharacterCodes.carriageReturn: + case CharacterCodes.lineFeed: + case CharacterCodes.paragraphSeparator: + case CharacterCodes.lineSeparator: + trivia.push(scanLineTerminatorSequenceTrivia(ch)); + + // If we're consuming leading trivia, then we will continue consuming more + // trivia (including newlines) up to the first token we see. If we're + // consuming trailing trivia, then we break after the first newline we see. + if (!isTrailing) { + continue; + } + + break; + + default: + throw Errors.invalidOperation(); + } + } + + // Debug.assert(trivia.length > 0); + var triviaList = Syntax.triviaList(trivia); + triviaList.parent = parent; + + return triviaList; + } + } + + // Returns 0 if there was no trivia, or 1 if there was trivia. Returned as an int instead + // of a boolean because we'll need a numerical value later on to store in our tokens. + function scanTriviaInfo(isTrailing: boolean): number { + // Keep this exactly in sync with scanTrivia + var result = 0; + var _end = end; + + while (index < _end) { + var ch = str.charCodeAt(index); + + switch (ch) { + case CharacterCodes.tab: + case CharacterCodes.space: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + index++; + result = 1; + continue; + + case CharacterCodes.carriageReturn: + if ((index + 1) < end && str.charCodeAt(index + 1) === CharacterCodes.lineFeed) { + index++; + } + // fall through. + case CharacterCodes.lineFeed: + index++; + + // If we're consuming leading trivia, then we will continue consuming more + // trivia (including newlines) up to the first token we see. If we're + // consuming trailing trivia, then we break after the first newline we see. + if (isTrailing) { + return 1; + } + + result = 1; + continue; + + case CharacterCodes.slash: + if ((index + 1) < _end) { + var ch2 = str.charCodeAt(index + 1); + if (ch2 === CharacterCodes.slash) { + result = 1; + skipSingleLineCommentTrivia(); + continue; + } + + if (ch2 === CharacterCodes.asterisk) { + result = 1; + skipMultiLineCommentTrivia(); + continue; + } + } + + // Not a comment. Don't consume. + return result; + + default: + if (ch > CharacterCodes.maxAsciiCharacter && slowScanTriviaInfo(ch)) { + result = 1; + continue; + } + + return result; + } + } + + return result; + } + + function slowScanTriviaInfo(ch: number): boolean { + switch (ch) { + case CharacterCodes.nonBreakingSpace: + case CharacterCodes.enQuad: + case CharacterCodes.emQuad: + case CharacterCodes.enSpace: + case CharacterCodes.emSpace: + case CharacterCodes.threePerEmSpace: + case CharacterCodes.fourPerEmSpace: + case CharacterCodes.sixPerEmSpace: + case CharacterCodes.figureSpace: + case CharacterCodes.punctuationSpace: + case CharacterCodes.thinSpace: + case CharacterCodes.hairSpace: + case CharacterCodes.zeroWidthSpace: + case CharacterCodes.narrowNoBreakSpace: + case CharacterCodes.ideographicSpace: + case CharacterCodes.byteOrderMark: + case CharacterCodes.paragraphSeparator: + case CharacterCodes.lineSeparator: + index++; + return true; + + default: + return false; + } + } + + function isNewLineCharacter(ch: number): boolean { + switch (ch) { + case CharacterCodes.carriageReturn: + case CharacterCodes.lineFeed: + case CharacterCodes.paragraphSeparator: + case CharacterCodes.lineSeparator: + return true; + default: + return false; + } + } + + function scanWhitespaceTrivia(): ISyntaxTrivia { + // We're going to be extracting text out of sliding window. Make sure it can't move past + // this point. + var absoluteStartIndex = index; + + while (true) { + var ch = str.charCodeAt(index); + + switch (ch) { + // Unicode 3.0 space characters + case CharacterCodes.space: + case CharacterCodes.nonBreakingSpace: + case CharacterCodes.enQuad: + case CharacterCodes.emQuad: + case CharacterCodes.enSpace: + case CharacterCodes.emSpace: + case CharacterCodes.threePerEmSpace: + case CharacterCodes.fourPerEmSpace: + case CharacterCodes.sixPerEmSpace: + case CharacterCodes.figureSpace: + case CharacterCodes.punctuationSpace: + case CharacterCodes.thinSpace: + case CharacterCodes.hairSpace: + case CharacterCodes.zeroWidthSpace: + case CharacterCodes.narrowNoBreakSpace: + case CharacterCodes.ideographicSpace: + + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.byteOrderMark: + // Normal whitespace. Consume and continue. + index++; + continue; + } + + break; + } + + return createTrivia(SyntaxKind.WhitespaceTrivia, absoluteStartIndex); + } + + function createTrivia(kind: SyntaxKind, absoluteStartIndex: number): ISyntaxTrivia { + var fullWidth = index - absoluteStartIndex; + return Syntax.deferredTrivia(kind, text, absoluteStartIndex, fullWidth); + } + + function scanSingleLineCommentTrivia(): ISyntaxTrivia { + var absoluteStartIndex = index; + skipSingleLineCommentTrivia(); + + return createTrivia(SyntaxKind.SingleLineCommentTrivia, absoluteStartIndex); + } + + function skipSingleLineCommentTrivia(): void { + index += 2; + + // The '2' is for the "//" we consumed. + while (index < end) { + if (isNewLineCharacter(str.charCodeAt(index))) { + return; + } + + index++; + } + } + + function scanMultiLineCommentTrivia(): ISyntaxTrivia { + var absoluteStartIndex = index; + skipMultiLineCommentTrivia(); + + return createTrivia(SyntaxKind.MultiLineCommentTrivia, absoluteStartIndex); + } + + function skipMultiLineCommentTrivia(): number { + // The '2' is for the "/*" we consumed. + index += 2; + + while (true) { + if (index === end) { + reportDiagnostic(end, 0, DiagnosticCode.AsteriskSlash_expected, null); + return; + } + + if ((index + 1) < end && + str.charCodeAt(index) === CharacterCodes.asterisk && + str.charCodeAt(index + 1) === CharacterCodes.slash) { + + index += 2; + return; + } + + index++; + } + } + + function scanLineTerminatorSequenceTrivia(ch: number): ISyntaxTrivia { + var absoluteStartIndex = index; + skipLineTerminatorSequence(ch); + + return createTrivia(SyntaxKind.NewLineTrivia, absoluteStartIndex); + } + + function skipLineTerminatorSequence(ch: number): void { + // Consume the first of the line terminator we saw. + index++; + + // If it happened to be a \r and there's a following \n, then consume both. + if (ch === CharacterCodes.carriageReturn && str.charCodeAt(index) === CharacterCodes.lineFeed) { + index++; + } + } + + function scanSyntaxKind(allowContextualToken: boolean): SyntaxKind { + if (index >= end) { + return SyntaxKind.EndOfFileToken; + } + + var character = str.charCodeAt(index); + index++; + + switch (character) { + case CharacterCodes.exclamation /*33*/: return scanExclamationToken(); + case CharacterCodes.doubleQuote/*34*/: return scanStringLiteral(character); + case CharacterCodes.percent /*37*/: return scanPercentToken(); + case CharacterCodes.ampersand /*38*/: return scanAmpersandToken(); + case CharacterCodes.singleQuote/*39*/: return scanStringLiteral(character); + case CharacterCodes.openParen/*40*/: return SyntaxKind.OpenParenToken; + case CharacterCodes.closeParen/*41*/: return SyntaxKind.CloseParenToken; + case CharacterCodes.asterisk/*42*/: return scanAsteriskToken(); + case CharacterCodes.plus/*43*/: return scanPlusToken(); + case CharacterCodes.comma/*44*/: return SyntaxKind.CommaToken; + case CharacterCodes.minus/*45*/: return scanMinusToken(); + case CharacterCodes.dot/*46*/: return scanDotToken(); + case CharacterCodes.slash/*47*/: return scanSlashToken(allowContextualToken); + + case CharacterCodes._0/*48*/: case CharacterCodes._1: case CharacterCodes._2: case CharacterCodes._3: + case CharacterCodes._4: case CharacterCodes._5: case CharacterCodes._6: case CharacterCodes._7: + case CharacterCodes._8: case CharacterCodes._9/*57*/: + return scanNumericLiteral(character); + + case CharacterCodes.colon/*58*/: return SyntaxKind.ColonToken; + case CharacterCodes.semicolon/*59*/: return SyntaxKind.SemicolonToken; + case CharacterCodes.lessThan/*60*/: return scanLessThanToken(); + case CharacterCodes.equals/*61*/: return scanEqualsToken(); + case CharacterCodes.greaterThan/*62*/: return scanGreaterThanToken(allowContextualToken); + case CharacterCodes.question/*63*/: return SyntaxKind.QuestionToken; + + case CharacterCodes.openBracket/*91*/: return SyntaxKind.OpenBracketToken; + case CharacterCodes.closeBracket/*93*/: return SyntaxKind.CloseBracketToken; + case CharacterCodes.caret/*94*/: return scanCaretToken(); + + case CharacterCodes.openBrace/*123*/: return SyntaxKind.OpenBraceToken; + case CharacterCodes.bar/*124*/: return scanBarToken(); + case CharacterCodes.closeBrace/*125*/: return SyntaxKind.CloseBraceToken; + case CharacterCodes.tilde/*126*/: return SyntaxKind.TildeToken; + } + + // We run into so many identifiers (and keywords) when scanning, that we want the code to + // be as fast as possible. To that end, we have an extremely fast path for scanning that + // handles the 99.9% case of no-unicode characters and no unicode escapes. + if (isIdentifierStartCharacter[character]) { + var result = tryFastScanIdentifierOrKeyword(character); + if (result !== SyntaxKind.None) { + return result; + } + } + + // Move the index back one and try the slow path. + index--; + if (isIdentifierStart(peekCharOrUnicodeEscape())) { + return slowScanIdentifierOrKeyword(); + } + + // Was nothing that we could understand. Report the issue and keep moving on. + var text = String.fromCharCode(character); + var messageText = getErrorMessageText(text); + reportDiagnostic(index, 1, DiagnosticCode.Unexpected_character_0, [messageText]); + + index++; + + return SyntaxKind.ErrorToken; + } + + function isIdentifierStart(interpretedChar: number): boolean { + if (isIdentifierStartCharacter[interpretedChar]) { + return true; + } + + return interpretedChar > CharacterCodes.maxAsciiCharacter && Unicode.isIdentifierStart(interpretedChar, languageVersion); + } + + function isIdentifierPart(interpretedChar: number): boolean { + if (isIdentifierPartCharacter[interpretedChar]) { + return true; + } + + return interpretedChar > CharacterCodes.maxAsciiCharacter && Unicode.isIdentifierPart(interpretedChar, languageVersion); + } + + function tryFastScanIdentifierOrKeyword(firstCharacter: number): SyntaxKind { + var startIndex = index; + var character = firstCharacter; + + // Note that we go up to the windowCount-1 so that we can read the character at the end + // of the window and check if it's *not* an identifier part character. + while (index < end) { + character = str.charCodeAt(index); + if (!isIdentifierPartCharacter[character]) { + break; + } + + index++; + } + + if (index < end && (character === CharacterCodes.backslash || character > CharacterCodes.maxAsciiCharacter)) { + // We saw a \ (which could start a unicode escape), or we saw a unicode character. + // This can't be scanned quickly. Don't update the window position and just bail out + // to the slow path. + index = startIndex; + return SyntaxKind.None; + } + else { + // Saw an ascii character that wasn't a backslash and wasn't an identifier + // character. Or we hit the end of the file This identifier is done. + + // Also check if it a keyword if it started with a keyword start char. + if (isKeywordStartCharacter[firstCharacter]) { + return ScannerUtilities.identifierKind(str, startIndex - 1, index - startIndex + 1); + } + else { + return SyntaxKind.IdentifierName; + } + } + } + + // A slow path for scanning identifiers. Called when we run into a unicode character or + // escape sequence while processing the fast path. + function slowScanIdentifierOrKeyword(): SyntaxKind { + var startIndex = index; + + do { + scanCharOrUnicodeEscape(); + } + while (isIdentifierPart(peekCharOrUnicodeEscape())); + + // From ES6 specification. + // The ReservedWord definitions are specified as literal sequences of Unicode + // characters.However, any Unicode character in a ReservedWord can also be + // expressed by a \ UnicodeEscapeSequence that expresses that same Unicode + // character's code point.Use of such escape sequences does not change the meaning + // of the ReservedWord. + // + // i.e. "\u0076ar" is the keyword 'var'. Check for that here. + var length = index - startIndex; + var text = str.substr(startIndex, length); + var valueText = massageEscapes(text); + + var keywordKind = SyntaxFacts.getTokenKind(valueText); + if (keywordKind >= SyntaxKind.FirstKeyword && keywordKind <= SyntaxKind.LastKeyword) { + // We have a keyword, but it is also variable width. We can't put represent this + // width a fixed width token. + return keywordKind | ScannerConstants.IsVariableWidthMask; + } + + return SyntaxKind.IdentifierName; + } + + function scanNumericLiteral(ch: number): SyntaxKind { + if (isHexNumericLiteral(ch)) { + scanHexNumericLiteral(); + } + else if (isOctalNumericLiteral(ch)) { + scanOctalNumericLiteral(); + } + else { + scanDecimalNumericLiteral(); + } + + return SyntaxKind.NumericLiteral; + } + + function isOctalNumericLiteral(ch: number): boolean { + return ch === CharacterCodes._0 && + CharacterInfo.isOctalDigit(str.charCodeAt(index)); + } + + function scanOctalNumericLiteral(): void { + var start = index - 1; + + while (CharacterInfo.isOctalDigit(str.charCodeAt(index))) { + index++; + } + + if (languageVersion >= ts.ScriptTarget.ES5) { + reportDiagnostic( + start, index - start, DiagnosticCode.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher, null); + } + } + + function scanDecimalDigits(): void { + while (CharacterInfo.isDecimalDigit(str.charCodeAt(index))) { + index++; + } + } + + function scanDecimalNumericLiteral(): void { + scanDecimalDigits(); + + if (str.charCodeAt(index) === CharacterCodes.dot) { + index++; + } + + scanDecimalNumericLiteralAfterDot(); + } + + function scanDecimalNumericLiteralAfterDot() { + scanDecimalDigits(); + + // If we see an 'e' or 'E' we should only consume it if its of the form: + // e or E + // e+ E+ + // e- E- + var ch = str.charCodeAt(index); + if (ch === CharacterCodes.e || ch === CharacterCodes.E) { + // Ok, we've got 'e' or 'E'. Make sure it's followed correctly. + var nextChar1 = str.charCodeAt(index + 1); + + if (CharacterInfo.isDecimalDigit(nextChar1)) { + // e or E + // Consume 'e' or 'E' and the number portion. + index++; + scanDecimalDigits(); + } + else if (nextChar1 === CharacterCodes.minus || nextChar1 === CharacterCodes.plus) { + // e+ or E+ or e- or E- + var nextChar2 = str.charCodeAt(index + 2); + if (CharacterInfo.isDecimalDigit(nextChar2)) { + // e+ or E+ or e- or E- + // Consume first two characters and the number portion. + index += 2; + scanDecimalDigits(); + } + } + } + } + + function scanHexNumericLiteral(): void { + // Move past the x. + index++; + + while (CharacterInfo.isHexDigit(str.charCodeAt(index))) { + index++; + } + } + + function isHexNumericLiteral(ch: number): boolean { + if (ch === CharacterCodes._0) { + var ch = str.charCodeAt(index); + + if (ch === CharacterCodes.x || ch === CharacterCodes.X) { + return CharacterInfo.isHexDigit(str.charCodeAt(index + 1)); + } + } + + return false; + } + + function scanLessThanToken(): SyntaxKind { + var ch0 = str.charCodeAt(index); + if (ch0 === CharacterCodes.equals) { + index++; + return SyntaxKind.LessThanEqualsToken; + } + else if (ch0 === CharacterCodes.lessThan) { + index++; + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + return SyntaxKind.LessThanLessThanEqualsToken; + } + else { + return SyntaxKind.LessThanLessThanToken; + } + } + else { + return SyntaxKind.LessThanToken; + } + } + + function scanGreaterThanToken(allowContextualToken: boolean): SyntaxKind { + if (allowContextualToken) { + var ch0 = str.charCodeAt(index); + if (ch0 === CharacterCodes.greaterThan) { + // >> + index++; + var ch1 = str.charCodeAt(index); + if (ch1 === CharacterCodes.greaterThan) { + // >>> + index++; + var ch2 = str.charCodeAt(index); + if (ch2 === CharacterCodes.equals) { + // >>>= + index++; + return SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; + } + else { + return SyntaxKind.GreaterThanGreaterThanGreaterThanToken; + } + } + else if (ch1 === CharacterCodes.equals) { + // >>= + index++; + return SyntaxKind.GreaterThanGreaterThanEqualsToken; + } + else { + return SyntaxKind.GreaterThanGreaterThanToken; + } + } + else if (ch0 === CharacterCodes.equals) { + // >= + index++; + return SyntaxKind.GreaterThanEqualsToken; + } + } + + return SyntaxKind.GreaterThanToken; + } + + function scanBarToken(): SyntaxKind { + var ch = str.charCodeAt(index); + if (ch === CharacterCodes.equals) { + index++; + return SyntaxKind.BarEqualsToken; + } + else if (ch === CharacterCodes.bar) { + index++; + return SyntaxKind.BarBarToken; + } + else { + return SyntaxKind.BarToken; + } + } + + function scanCaretToken(): SyntaxKind { + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + return SyntaxKind.CaretEqualsToken; + } + else { + return SyntaxKind.CaretToken; + } + } + + function scanAmpersandToken(): SyntaxKind { + var character = str.charCodeAt(index); + if (character === CharacterCodes.equals) { + index++; + return SyntaxKind.AmpersandEqualsToken; + } + else if (character === CharacterCodes.ampersand) { + index++; + return SyntaxKind.AmpersandAmpersandToken; + } + else { + return SyntaxKind.AmpersandToken; + } + } + + function scanPercentToken(): SyntaxKind { + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + return SyntaxKind.PercentEqualsToken; + } + else { + return SyntaxKind.PercentToken; + } + } + + function scanMinusToken(): SyntaxKind { + var character = str.charCodeAt(index); + + if (character === CharacterCodes.equals) { + index++; + return SyntaxKind.MinusEqualsToken; + } + else if (character === CharacterCodes.minus) { + index++; + return SyntaxKind.MinusMinusToken; + } + else { + return SyntaxKind.MinusToken; + } + } + + function scanPlusToken(): SyntaxKind { + var character = str.charCodeAt(index); + if (character === CharacterCodes.equals) { + index++; + return SyntaxKind.PlusEqualsToken; + } + else if (character === CharacterCodes.plus) { + index++; + return SyntaxKind.PlusPlusToken; + } + else { + return SyntaxKind.PlusToken; + } + } + + function scanAsteriskToken(): SyntaxKind { + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + return SyntaxKind.AsteriskEqualsToken; + } + else { + return SyntaxKind.AsteriskToken; + } + } + + function scanEqualsToken(): SyntaxKind { + var character = str.charCodeAt(index); + if (character === CharacterCodes.equals) { + index++; + + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + + return SyntaxKind.EqualsEqualsEqualsToken; + } + else { + return SyntaxKind.EqualsEqualsToken; + } + } + else if (character === CharacterCodes.greaterThan) { + index++; + return SyntaxKind.EqualsGreaterThanToken; + } + else { + return SyntaxKind.EqualsToken; + } + } + + function scanDotToken(): SyntaxKind { + var nextChar = str.charCodeAt(index); + if (CharacterInfo.isDecimalDigit(nextChar)) { + scanDecimalNumericLiteralAfterDot(); + return SyntaxKind.NumericLiteral; + } + + if (nextChar === CharacterCodes.dot && + str.charCodeAt(index + 1) === CharacterCodes.dot) { + + index += 2; + return SyntaxKind.DotDotDotToken; + } + else { + return SyntaxKind.DotToken; + } + } + + function scanSlashToken(allowContextualToken: boolean): SyntaxKind { + // NOTE: By default, we do not try scanning a / as a regexp here. We instead consider it a + // div or div-assign. Later on, if the parser runs into a situation where it would like a + // term, and it sees one of these then it may restart us asking specifically if we could + // scan out a regex. + if (allowContextualToken) { + var result = tryScanRegularExpressionToken(); + if (result !== SyntaxKind.None) { + return result; + } + } + + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + return SyntaxKind.SlashEqualsToken; + } + else { + return SyntaxKind.SlashToken; + } + } + + function tryScanRegularExpressionToken(): SyntaxKind { + var startIndex = index; + + var inEscape = false; + var inCharacterClass = false; + while (true) { + var ch = str.charCodeAt(index); + + if (isNaN(ch) || isNewLineCharacter(ch)) { + index = startIndex; + return SyntaxKind.None; + } + + index++; + if (inEscape) { + inEscape = false; + continue; + } + + switch (ch) { + case CharacterCodes.backslash: + // We're now in an escape. Consume the next character we see (unless it's + // a newline or null. + inEscape = true; + continue; + + case CharacterCodes.openBracket: + // If we see a [ then we're starting an character class. Note: it's ok if + // we then hit another [ inside a character class. We'll just set the value + // to true again and that's ok. + inCharacterClass = true; + continue; + + case CharacterCodes.closeBracket: + // If we ever hit a cloe bracket then we're now no longer in a character + // class. If we weren't in a character class to begin with, then this has + // no effect. + inCharacterClass = false; + continue; + + case CharacterCodes.slash: + // If we see a slash, and we're in a character class, then ignore it. + if (inCharacterClass) { + continue; + } + + // We're done with the regex. Break out of the switch (which will break + // out of hte loop. + break; + + default: + // Just consume any other characters. + continue; + } + + break; + } + + // TODO: The grammar says any identifier part is allowed here. Do we need to support + // \u identifiers here? The existing typescript parser does not. + while (isIdentifierPartCharacter[str.charCodeAt(index)]) { + index++; + } + + return SyntaxKind.RegularExpressionLiteral; + } + + function scanExclamationToken(): SyntaxKind { + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + + if (str.charCodeAt(index) === CharacterCodes.equals) { + index++; + + return SyntaxKind.ExclamationEqualsEqualsToken; + } + else { + return SyntaxKind.ExclamationEqualsToken; + } + } + else { + return SyntaxKind.ExclamationToken; + } + } + + // Convert text into a printable form usable for an error message. This will both quote the + // string, and ensure all characters printable (i.e. by using unicode escapes when they're not). + function getErrorMessageText(text: string): string { + // For just a simple backslash, we return it as is. The default behavior of JSON.stringify + // is not what we want here. + if (text === "\\") { + return '"\\"'; + } + + return JSON.stringify(text); + } + + function skipEscapeSequence(): void { + var rewindPoint = index; + + // Consume the backslash. + index++; + + // Get the char after the backslash + var ch = str.charCodeAt(index); + if (isNaN(ch)) { + // if we're at teh end of the file, just return, the string scanning code will + // report an appropriate error. + return; + } + + index++; + switch (ch) { + case CharacterCodes.x: + case CharacterCodes.u: + index = rewindPoint; + var value = scanUnicodeOrHexEscape(/*report:*/ true); + break; + + case CharacterCodes.carriageReturn: + // If it's \r\n then consume both characters. + if (str.charCodeAt(index) === CharacterCodes.lineFeed) { + index++; + } + break; + + // We don't have to do anything special about these characters. I'm including them + // Just so it's clear that we intentially process them in the exact same way: + //case CharacterCodes.singleQuote: + //case CharacterCodes.doubleQuote: + //case CharacterCodes.backslash: + //case CharacterCodes._0: + //case CharacterCodes.b: + //case CharacterCodes.f: + //case CharacterCodes.n: + //case CharacterCodes.r: + //case CharacterCodes.t: + //case CharacterCodes.v: + //case CharacterCodes.lineFeed: + //case CharacterCodes.paragraphSeparator: + //case CharacterCodes.lineSeparator: + default: + // Any other character is ok as well. As per rule: + // EscapeSequence :: CharacterEscapeSequence + // CharacterEscapeSequence :: NonEscapeCharacter + // NonEscapeCharacter :: SourceCharacter but notEscapeCharacter or LineTerminator + break; + } + } + + function scanStringLiteral(quoteCharacter: number): SyntaxKind { + // Debug.assert(quoteCharacter === CharacterCodes.singleQuote || quoteCharacter === CharacterCodes.doubleQuote); + + while (true) { + var ch = str.charCodeAt(index); + if (ch === CharacterCodes.backslash) { + skipEscapeSequence(); + } + else if (ch === quoteCharacter) { + index++; + break; + } + else if (isNaN(ch) || isNewLineCharacter(ch)) { + reportDiagnostic(Math.min(index, end), 1, DiagnosticCode.Missing_close_quote_character, null); + break; + } + else { + index++; + } + } + + return SyntaxKind.StringLiteral; + } + + function isUnicodeEscape(character: number): boolean { + return character === CharacterCodes.backslash && + str.charCodeAt(index + 1) === CharacterCodes.u; + } + + function peekCharOrUnicodeEscape(): number { + var character = str.charCodeAt(index); + if (isUnicodeEscape(character)) { + return peekUnicodeOrHexEscape(); + } + else { + return character; + } + } + + function peekUnicodeOrHexEscape(): number { + var startIndex = index; + + // if we're peeking, then we don't want to change the position + var ch = scanUnicodeOrHexEscape(/*report:*/ false); + + index = startIndex; + + return ch; + } + + // Returns true if this was a unicode escape. + function scanCharOrUnicodeEscape(): void { + if (str.charCodeAt(index) === CharacterCodes.backslash && + str.charCodeAt(index + 1) === CharacterCodes.u) { + + scanUnicodeOrHexEscape(/*report:*/ true); + } + else { + index++; + } + } + + function scanUnicodeOrHexEscape(report: boolean): number { + var start = index; + var character = str.charCodeAt(index); + // Debug.assert(character === CharacterCodes.backslash); + index++; + + character = str.charCodeAt(index); + // Debug.assert(character === CharacterCodes.u || character === CharacterCodes.x); + + var intChar = 0; + index++; + + var count = character === CharacterCodes.u ? 4 : 2; + + for (var i = 0; i < count; i++) { + var ch2 = str.charCodeAt(index); + if (!CharacterInfo.isHexDigit(ch2)) { + if (report) { + reportDiagnostic(start, index - start, DiagnosticCode.Unrecognized_escape_sequence, null) + } + + break; + } + + intChar = (intChar << 4) + CharacterInfo.hexValue(ch2); + index++; + } + + return intChar; + } + + function fillTokenInfo(token: IScannerToken, text: ISimpleText, tokenInfo: TokenInfo): void { + var fullStart = token.fullStart(); + var fullEnd = fullStart + token.fullWidth(); + reset(text, fullStart, fullEnd); + + var leadingTriviaInfo = scanTriviaInfo(/*isTrailing: */ false); + + var start = index; + scanSyntaxKind(isContextualToken(token)); + var end = index; + + tokenInfo.leadingTriviaWidth = start - fullStart; + tokenInfo.width = end - start; + } + + reset(text, 0, text.length()); + + return { + setIndex: setIndex, + scan: scan, + fillTokenInfo: fillTokenInfo, + scanTrivia: scanTrivia, + }; + } + + export function isValidIdentifier(text: ISimpleText, languageVersion: ts.ScriptTarget): boolean { + var hadError = false; + var scanner = createScanner(languageVersion, text, () => hadError = true); + + var token = scanner.scan(/*allowContextualToken:*/ false); + + return !hadError && SyntaxFacts.isIdentifierNameOrAnyKeyword(token) && width(token) === text.length(); + } + + // A parser source that gets its data from an underlying scanner. + export interface IScannerParserSource extends Parser.IParserSource { + // The position that the scanner is currently at. + absolutePosition(): number; + + // Resets the source to this position. Any diagnostics produced after this point will be + // removed. + resetToPosition(absolutePosition: number): void; + } + + interface IScannerRewindPoint extends Parser.IRewindPoint { + // Information used by normal parser source. + absolutePosition: number; + slidingWindowIndex: number; + } + + // Parser source used in batch scenarios. Directly calls into an underlying text scanner and + // supports none of the functionality to reuse nodes. Good for when you just want want to do + // a single parse of a file. + export function createParserSource(fileName: string, text: ISimpleText, languageVersion: ts.ScriptTarget): IScannerParserSource { + // The absolute position we're at in the text we're reading from. + var _absolutePosition: number = 0; + + // The diagnostics we get while scanning. Note: this never gets rewound when we do a normal + // rewind. That's because rewinding doesn't affect the tokens created. It only affects where + // in the token stream we're pointing at. However, it will get modified if we we decide to + // reparse a / or /= as a regular expression. + var _tokenDiagnostics: Diagnostic[] = []; + + // Pool of rewind points we give out if the parser needs one. + var rewindPointPool: IScannerRewindPoint[] = []; + var rewindPointPoolCount = 0; + + var lastDiagnostic: Diagnostic = null; + var reportDiagnostic = (position: number, fullWidth: number, diagnosticKey: string, args: any[]) => { + lastDiagnostic = new Diagnostic(fileName, text.lineMap(), position, fullWidth, diagnosticKey, args); + }; + + // The sliding window that we store tokens in. + var slidingWindow = new SlidingWindow(fetchNextItem, ArrayUtilities.createArray(/*defaultWindowSize:*/ 1024, null), null); + + // The scanner we're pulling tokens from. + var scanner = createScanner(languageVersion, text, reportDiagnostic); + + function release() { + slidingWindow = null; + scanner = null; + _tokenDiagnostics = []; + rewindPointPool = []; + lastDiagnostic = null; + reportDiagnostic = null; + } + + function currentNode(): ISyntaxNode { + // The normal parser source never returns nodes. They're only returned by the + // incremental parser source. + return null; + } + + function consumeNode(node: ISyntaxNode): void { + // Should never get called. + throw Errors.invalidOperation(); + } + + function absolutePosition() { + return _absolutePosition; + } + + function tokenDiagnostics(): Diagnostic[] { + return _tokenDiagnostics; + } + + function getOrCreateRewindPoint(): IScannerRewindPoint { + if (rewindPointPoolCount === 0) { + return {}; + } + + rewindPointPoolCount--; + var result = rewindPointPool[rewindPointPoolCount]; + rewindPointPool[rewindPointPoolCount] = null; + return result; + } + + function getRewindPoint(): IScannerRewindPoint { + var slidingWindowIndex = slidingWindow.getAndPinAbsoluteIndex(); + + var rewindPoint = getOrCreateRewindPoint(); + + rewindPoint.slidingWindowIndex = slidingWindowIndex; + rewindPoint.absolutePosition = _absolutePosition; + + // rewindPoint.pinCount = slidingWindow.pinCount(); + + return rewindPoint; + } + + function rewind(rewindPoint: IScannerRewindPoint): void { + slidingWindow.rewindToPinnedIndex(rewindPoint.slidingWindowIndex); + + _absolutePosition = rewindPoint.absolutePosition; + } + + function releaseRewindPoint(rewindPoint: IScannerRewindPoint): void { + // Debug.assert(slidingWindow.pinCount() === rewindPoint.pinCount); + slidingWindow.releaseAndUnpinAbsoluteIndex((rewindPoint).absoluteIndex); + + rewindPointPool[rewindPointPoolCount] = rewindPoint; + rewindPointPoolCount++; + } + + function fetchNextItem(allowContextualToken: boolean): ISyntaxToken { + // Assert disabled because it is actually expensive enugh to affect perf. + // Debug.assert(spaceAvailable > 0); + var token = scanner.scan(allowContextualToken); + + if (lastDiagnostic === null) { + return token; + } + + // If we produced any diagnostics while creating this token, then realize the token so + // it won't be reused in incremental scenarios. + + _tokenDiagnostics.push(lastDiagnostic); + lastDiagnostic = null; + return Syntax.realizeToken(token, text); + } + + function peekToken(n: number): ISyntaxToken { + return slidingWindow.peekItemN(n); + } + + function consumeToken(token: ISyntaxToken): void { + // Debug.assert(currentToken() === token); + _absolutePosition += token.fullWidth(); + + slidingWindow.moveToNextItem(); + } + + function currentToken(): ISyntaxToken { + return slidingWindow.currentItem(/*allowContextualToken:*/ false); + } + + function removeDiagnosticsOnOrAfterPosition(position: number): void { + // walk backwards, removing any diagnostics that came after the the current token's + // full start position. + var tokenDiagnosticsLength = _tokenDiagnostics.length; + while (tokenDiagnosticsLength > 0) { + var diagnostic = _tokenDiagnostics[tokenDiagnosticsLength - 1]; + if (diagnostic.start() >= position) { + tokenDiagnosticsLength--; + } + else { + break; + } + } + + _tokenDiagnostics.length = tokenDiagnosticsLength; + } + + function resetToPosition(absolutePosition: number): void { + _absolutePosition = absolutePosition; + + // First, remove any diagnostics that came after this position. + removeDiagnosticsOnOrAfterPosition(absolutePosition); + + // Now, tell our sliding window to throw away all tokens after this position as well. + slidingWindow.disgardAllItemsFromCurrentIndexOnwards(); + + // Now tell the scanner to reset its position to this position as well. That way + // when we try to scan the next item, we'll be at the right location. + scanner.setIndex(absolutePosition); + } + + function currentContextualToken(): ISyntaxToken { + // We better be on a / or > token right now. + // Debug.assert(SyntaxFacts.isAnyDivideToken(currentToken().kind())); + + // First, we're going to rewind all our data to the point where this / or /= token started. + // That's because if it does turn out to be a regular expression, then any tokens or token + // diagnostics we produced after the original / may no longer be valid. This would actually + // be a fairly expected case. For example, if you had: / ... gibberish ... /, we may have + // produced several diagnostics in the process of scanning the tokens after the first / as + // they may not have been legal javascript okens. + // + // We also need to remove all the tokens we've gotten from the slash and onwards. They may + // not have been what the scanner would have produced if it decides that this is actually + // a regular expresion. + resetToPosition(_absolutePosition); + + // Now actually fetch the token again from the scanner. This time let it know that it + // can scan it as a regex token if it wants to. + var token = slidingWindow.currentItem(/*allowContextualToken:*/ true); + + // We have better gotten some sort of regex token. Otherwise, something *very* wrong has + // occurred. + // Debug.assert(SyntaxFacts.isAnyDivideOrRegularExpressionToken(token.kind())); + + return token; + } + + return { + text: text, + fileName: fileName, + languageVersion: languageVersion, + currentNode: currentNode, + currentToken: currentToken, + currentContextualToken: currentContextualToken, + peekToken: peekToken, + consumeNode: consumeNode, + consumeToken: consumeToken, + getRewindPoint: getRewindPoint, + rewind: rewind, + releaseRewindPoint: releaseRewindPoint, + tokenDiagnostics: tokenDiagnostics, + release: release, + absolutePosition: absolutePosition, + resetToPosition: resetToPosition, + }; + } +} \ No newline at end of file diff --git a/src/services/syntax/scannerUtilities.generated.ts b/src/services/syntax/scannerUtilities.generated.ts new file mode 100644 index 00000000000..fbf1b1cb952 --- /dev/null +++ b/src/services/syntax/scannerUtilities.generated.ts @@ -0,0 +1,142 @@ +/// + +module TypeScript { + export class ScannerUtilities { + public static identifierKind(str: string, start: number, length: number): SyntaxKind { + switch (length) { + case 2: // do, if, in + switch(str.charCodeAt(start)) { + case CharacterCodes.d: return (str.charCodeAt(start + 1) === CharacterCodes.o) ? SyntaxKind.DoKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.i: // if, in + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.f: return SyntaxKind.IfKeyword; + case CharacterCodes.n: return SyntaxKind.InKeyword; + default: return SyntaxKind.IdentifierName; + } + default: return SyntaxKind.IdentifierName; + } + case 3: // any, for, get, let, new, set, try, var + switch(str.charCodeAt(start)) { + case CharacterCodes.a: return (str.charCodeAt(start + 1) === CharacterCodes.n && str.charCodeAt(start + 2) === CharacterCodes.y) ? SyntaxKind.AnyKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.f: return (str.charCodeAt(start + 1) === CharacterCodes.o && str.charCodeAt(start + 2) === CharacterCodes.r) ? SyntaxKind.ForKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.g: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.t) ? SyntaxKind.GetKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.l: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.t) ? SyntaxKind.LetKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.n: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.w) ? SyntaxKind.NewKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.s: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.t) ? SyntaxKind.SetKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.t: return (str.charCodeAt(start + 1) === CharacterCodes.r && str.charCodeAt(start + 2) === CharacterCodes.y) ? SyntaxKind.TryKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.v: return (str.charCodeAt(start + 1) === CharacterCodes.a && str.charCodeAt(start + 2) === CharacterCodes.r) ? SyntaxKind.VarKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 4: // case, else, enum, null, this, true, void, with + switch(str.charCodeAt(start)) { + case CharacterCodes.c: return (str.charCodeAt(start + 1) === CharacterCodes.a && str.charCodeAt(start + 2) === CharacterCodes.s && str.charCodeAt(start + 3) === CharacterCodes.e) ? SyntaxKind.CaseKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.e: // else, enum + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.l: return (str.charCodeAt(start + 2) === CharacterCodes.s && str.charCodeAt(start + 3) === CharacterCodes.e) ? SyntaxKind.ElseKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.n: return (str.charCodeAt(start + 2) === CharacterCodes.u && str.charCodeAt(start + 3) === CharacterCodes.m) ? SyntaxKind.EnumKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.n: return (str.charCodeAt(start + 1) === CharacterCodes.u && str.charCodeAt(start + 2) === CharacterCodes.l && str.charCodeAt(start + 3) === CharacterCodes.l) ? SyntaxKind.NullKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.t: // this, true + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.h: return (str.charCodeAt(start + 2) === CharacterCodes.i && str.charCodeAt(start + 3) === CharacterCodes.s) ? SyntaxKind.ThisKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.r: return (str.charCodeAt(start + 2) === CharacterCodes.u && str.charCodeAt(start + 3) === CharacterCodes.e) ? SyntaxKind.TrueKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.v: return (str.charCodeAt(start + 1) === CharacterCodes.o && str.charCodeAt(start + 2) === CharacterCodes.i && str.charCodeAt(start + 3) === CharacterCodes.d) ? SyntaxKind.VoidKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.w: return (str.charCodeAt(start + 1) === CharacterCodes.i && str.charCodeAt(start + 2) === CharacterCodes.t && str.charCodeAt(start + 3) === CharacterCodes.h) ? SyntaxKind.WithKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 5: // break, catch, class, const, false, super, throw, while, yield + switch(str.charCodeAt(start)) { + case CharacterCodes.b: return (str.charCodeAt(start + 1) === CharacterCodes.r && str.charCodeAt(start + 2) === CharacterCodes.e && str.charCodeAt(start + 3) === CharacterCodes.a && str.charCodeAt(start + 4) === CharacterCodes.k) ? SyntaxKind.BreakKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.c: // catch, class, const + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.a: return (str.charCodeAt(start + 2) === CharacterCodes.t && str.charCodeAt(start + 3) === CharacterCodes.c && str.charCodeAt(start + 4) === CharacterCodes.h) ? SyntaxKind.CatchKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.l: return (str.charCodeAt(start + 2) === CharacterCodes.a && str.charCodeAt(start + 3) === CharacterCodes.s && str.charCodeAt(start + 4) === CharacterCodes.s) ? SyntaxKind.ClassKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.o: return (str.charCodeAt(start + 2) === CharacterCodes.n && str.charCodeAt(start + 3) === CharacterCodes.s && str.charCodeAt(start + 4) === CharacterCodes.t) ? SyntaxKind.ConstKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.f: return (str.charCodeAt(start + 1) === CharacterCodes.a && str.charCodeAt(start + 2) === CharacterCodes.l && str.charCodeAt(start + 3) === CharacterCodes.s && str.charCodeAt(start + 4) === CharacterCodes.e) ? SyntaxKind.FalseKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.s: return (str.charCodeAt(start + 1) === CharacterCodes.u && str.charCodeAt(start + 2) === CharacterCodes.p && str.charCodeAt(start + 3) === CharacterCodes.e && str.charCodeAt(start + 4) === CharacterCodes.r) ? SyntaxKind.SuperKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.t: return (str.charCodeAt(start + 1) === CharacterCodes.h && str.charCodeAt(start + 2) === CharacterCodes.r && str.charCodeAt(start + 3) === CharacterCodes.o && str.charCodeAt(start + 4) === CharacterCodes.w) ? SyntaxKind.ThrowKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.w: return (str.charCodeAt(start + 1) === CharacterCodes.h && str.charCodeAt(start + 2) === CharacterCodes.i && str.charCodeAt(start + 3) === CharacterCodes.l && str.charCodeAt(start + 4) === CharacterCodes.e) ? SyntaxKind.WhileKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.y: return (str.charCodeAt(start + 1) === CharacterCodes.i && str.charCodeAt(start + 2) === CharacterCodes.e && str.charCodeAt(start + 3) === CharacterCodes.l && str.charCodeAt(start + 4) === CharacterCodes.d) ? SyntaxKind.YieldKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 6: // delete, export, import, module, number, public, return, static, string, switch, typeof + switch(str.charCodeAt(start)) { + case CharacterCodes.d: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.l && str.charCodeAt(start + 3) === CharacterCodes.e && str.charCodeAt(start + 4) === CharacterCodes.t && str.charCodeAt(start + 5) === CharacterCodes.e) ? SyntaxKind.DeleteKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.e: return (str.charCodeAt(start + 1) === CharacterCodes.x && str.charCodeAt(start + 2) === CharacterCodes.p && str.charCodeAt(start + 3) === CharacterCodes.o && str.charCodeAt(start + 4) === CharacterCodes.r && str.charCodeAt(start + 5) === CharacterCodes.t) ? SyntaxKind.ExportKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.i: return (str.charCodeAt(start + 1) === CharacterCodes.m && str.charCodeAt(start + 2) === CharacterCodes.p && str.charCodeAt(start + 3) === CharacterCodes.o && str.charCodeAt(start + 4) === CharacterCodes.r && str.charCodeAt(start + 5) === CharacterCodes.t) ? SyntaxKind.ImportKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.m: return (str.charCodeAt(start + 1) === CharacterCodes.o && str.charCodeAt(start + 2) === CharacterCodes.d && str.charCodeAt(start + 3) === CharacterCodes.u && str.charCodeAt(start + 4) === CharacterCodes.l && str.charCodeAt(start + 5) === CharacterCodes.e) ? SyntaxKind.ModuleKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.n: return (str.charCodeAt(start + 1) === CharacterCodes.u && str.charCodeAt(start + 2) === CharacterCodes.m && str.charCodeAt(start + 3) === CharacterCodes.b && str.charCodeAt(start + 4) === CharacterCodes.e && str.charCodeAt(start + 5) === CharacterCodes.r) ? SyntaxKind.NumberKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.p: return (str.charCodeAt(start + 1) === CharacterCodes.u && str.charCodeAt(start + 2) === CharacterCodes.b && str.charCodeAt(start + 3) === CharacterCodes.l && str.charCodeAt(start + 4) === CharacterCodes.i && str.charCodeAt(start + 5) === CharacterCodes.c) ? SyntaxKind.PublicKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.r: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.t && str.charCodeAt(start + 3) === CharacterCodes.u && str.charCodeAt(start + 4) === CharacterCodes.r && str.charCodeAt(start + 5) === CharacterCodes.n) ? SyntaxKind.ReturnKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.s: // static, string, switch + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.t: // static, string + switch(str.charCodeAt(start + 2)) { + case CharacterCodes.a: return (str.charCodeAt(start + 3) === CharacterCodes.t && str.charCodeAt(start + 4) === CharacterCodes.i && str.charCodeAt(start + 5) === CharacterCodes.c) ? SyntaxKind.StaticKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.r: return (str.charCodeAt(start + 3) === CharacterCodes.i && str.charCodeAt(start + 4) === CharacterCodes.n && str.charCodeAt(start + 5) === CharacterCodes.g) ? SyntaxKind.StringKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.w: return (str.charCodeAt(start + 2) === CharacterCodes.i && str.charCodeAt(start + 3) === CharacterCodes.t && str.charCodeAt(start + 4) === CharacterCodes.c && str.charCodeAt(start + 5) === CharacterCodes.h) ? SyntaxKind.SwitchKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.t: return (str.charCodeAt(start + 1) === CharacterCodes.y && str.charCodeAt(start + 2) === CharacterCodes.p && str.charCodeAt(start + 3) === CharacterCodes.e && str.charCodeAt(start + 4) === CharacterCodes.o && str.charCodeAt(start + 5) === CharacterCodes.f) ? SyntaxKind.TypeOfKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 7: // boolean, declare, default, extends, finally, package, private, require + switch(str.charCodeAt(start)) { + case CharacterCodes.b: return (str.charCodeAt(start + 1) === CharacterCodes.o && str.charCodeAt(start + 2) === CharacterCodes.o && str.charCodeAt(start + 3) === CharacterCodes.l && str.charCodeAt(start + 4) === CharacterCodes.e && str.charCodeAt(start + 5) === CharacterCodes.a && str.charCodeAt(start + 6) === CharacterCodes.n) ? SyntaxKind.BooleanKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.d: // declare, default + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.e: // declare, default + switch(str.charCodeAt(start + 2)) { + case CharacterCodes.c: return (str.charCodeAt(start + 3) === CharacterCodes.l && str.charCodeAt(start + 4) === CharacterCodes.a && str.charCodeAt(start + 5) === CharacterCodes.r && str.charCodeAt(start + 6) === CharacterCodes.e) ? SyntaxKind.DeclareKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.f: return (str.charCodeAt(start + 3) === CharacterCodes.a && str.charCodeAt(start + 4) === CharacterCodes.u && str.charCodeAt(start + 5) === CharacterCodes.l && str.charCodeAt(start + 6) === CharacterCodes.t) ? SyntaxKind.DefaultKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.e: return (str.charCodeAt(start + 1) === CharacterCodes.x && str.charCodeAt(start + 2) === CharacterCodes.t && str.charCodeAt(start + 3) === CharacterCodes.e && str.charCodeAt(start + 4) === CharacterCodes.n && str.charCodeAt(start + 5) === CharacterCodes.d && str.charCodeAt(start + 6) === CharacterCodes.s) ? SyntaxKind.ExtendsKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.f: return (str.charCodeAt(start + 1) === CharacterCodes.i && str.charCodeAt(start + 2) === CharacterCodes.n && str.charCodeAt(start + 3) === CharacterCodes.a && str.charCodeAt(start + 4) === CharacterCodes.l && str.charCodeAt(start + 5) === CharacterCodes.l && str.charCodeAt(start + 6) === CharacterCodes.y) ? SyntaxKind.FinallyKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.p: // package, private + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.a: return (str.charCodeAt(start + 2) === CharacterCodes.c && str.charCodeAt(start + 3) === CharacterCodes.k && str.charCodeAt(start + 4) === CharacterCodes.a && str.charCodeAt(start + 5) === CharacterCodes.g && str.charCodeAt(start + 6) === CharacterCodes.e) ? SyntaxKind.PackageKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.r: return (str.charCodeAt(start + 2) === CharacterCodes.i && str.charCodeAt(start + 3) === CharacterCodes.v && str.charCodeAt(start + 4) === CharacterCodes.a && str.charCodeAt(start + 5) === CharacterCodes.t && str.charCodeAt(start + 6) === CharacterCodes.e) ? SyntaxKind.PrivateKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case CharacterCodes.r: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.q && str.charCodeAt(start + 3) === CharacterCodes.u && str.charCodeAt(start + 4) === CharacterCodes.i && str.charCodeAt(start + 5) === CharacterCodes.r && str.charCodeAt(start + 6) === CharacterCodes.e) ? SyntaxKind.RequireKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 8: // continue, debugger, function + switch(str.charCodeAt(start)) { + case CharacterCodes.c: return (str.charCodeAt(start + 1) === CharacterCodes.o && str.charCodeAt(start + 2) === CharacterCodes.n && str.charCodeAt(start + 3) === CharacterCodes.t && str.charCodeAt(start + 4) === CharacterCodes.i && str.charCodeAt(start + 5) === CharacterCodes.n && str.charCodeAt(start + 6) === CharacterCodes.u && str.charCodeAt(start + 7) === CharacterCodes.e) ? SyntaxKind.ContinueKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.d: return (str.charCodeAt(start + 1) === CharacterCodes.e && str.charCodeAt(start + 2) === CharacterCodes.b && str.charCodeAt(start + 3) === CharacterCodes.u && str.charCodeAt(start + 4) === CharacterCodes.g && str.charCodeAt(start + 5) === CharacterCodes.g && str.charCodeAt(start + 6) === CharacterCodes.e && str.charCodeAt(start + 7) === CharacterCodes.r) ? SyntaxKind.DebuggerKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.f: return (str.charCodeAt(start + 1) === CharacterCodes.u && str.charCodeAt(start + 2) === CharacterCodes.n && str.charCodeAt(start + 3) === CharacterCodes.c && str.charCodeAt(start + 4) === CharacterCodes.t && str.charCodeAt(start + 5) === CharacterCodes.i && str.charCodeAt(start + 6) === CharacterCodes.o && str.charCodeAt(start + 7) === CharacterCodes.n) ? SyntaxKind.FunctionKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 9: // interface, protected + switch(str.charCodeAt(start)) { + case CharacterCodes.i: return (str.charCodeAt(start + 1) === CharacterCodes.n && str.charCodeAt(start + 2) === CharacterCodes.t && str.charCodeAt(start + 3) === CharacterCodes.e && str.charCodeAt(start + 4) === CharacterCodes.r && str.charCodeAt(start + 5) === CharacterCodes.f && str.charCodeAt(start + 6) === CharacterCodes.a && str.charCodeAt(start + 7) === CharacterCodes.c && str.charCodeAt(start + 8) === CharacterCodes.e) ? SyntaxKind.InterfaceKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.p: return (str.charCodeAt(start + 1) === CharacterCodes.r && str.charCodeAt(start + 2) === CharacterCodes.o && str.charCodeAt(start + 3) === CharacterCodes.t && str.charCodeAt(start + 4) === CharacterCodes.e && str.charCodeAt(start + 5) === CharacterCodes.c && str.charCodeAt(start + 6) === CharacterCodes.t && str.charCodeAt(start + 7) === CharacterCodes.e && str.charCodeAt(start + 8) === CharacterCodes.d) ? SyntaxKind.ProtectedKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + case 10: // implements, instanceof + switch(str.charCodeAt(start)) { + case CharacterCodes.i: // implements, instanceof + switch(str.charCodeAt(start + 1)) { + case CharacterCodes.m: return (str.charCodeAt(start + 2) === CharacterCodes.p && str.charCodeAt(start + 3) === CharacterCodes.l && str.charCodeAt(start + 4) === CharacterCodes.e && str.charCodeAt(start + 5) === CharacterCodes.m && str.charCodeAt(start + 6) === CharacterCodes.e && str.charCodeAt(start + 7) === CharacterCodes.n && str.charCodeAt(start + 8) === CharacterCodes.t && str.charCodeAt(start + 9) === CharacterCodes.s) ? SyntaxKind.ImplementsKeyword : SyntaxKind.IdentifierName; + case CharacterCodes.n: return (str.charCodeAt(start + 2) === CharacterCodes.s && str.charCodeAt(start + 3) === CharacterCodes.t && str.charCodeAt(start + 4) === CharacterCodes.a && str.charCodeAt(start + 5) === CharacterCodes.n && str.charCodeAt(start + 6) === CharacterCodes.c && str.charCodeAt(start + 7) === CharacterCodes.e && str.charCodeAt(start + 8) === CharacterCodes.o && str.charCodeAt(start + 9) === CharacterCodes.f) ? SyntaxKind.InstanceOfKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + default: return SyntaxKind.IdentifierName; + } + case 11: return (str.charCodeAt(start) === CharacterCodes.c && str.charCodeAt(start + 1) === CharacterCodes.o && str.charCodeAt(start + 2) === CharacterCodes.n && str.charCodeAt(start + 3) === CharacterCodes.s && str.charCodeAt(start + 4) === CharacterCodes.t && str.charCodeAt(start + 5) === CharacterCodes.r && str.charCodeAt(start + 6) === CharacterCodes.u && str.charCodeAt(start + 7) === CharacterCodes.c && str.charCodeAt(start + 8) === CharacterCodes.t && str.charCodeAt(start + 9) === CharacterCodes.o && str.charCodeAt(start + 10) === CharacterCodes.r) ? SyntaxKind.ConstructorKeyword : SyntaxKind.IdentifierName; + default: return SyntaxKind.IdentifierName; + } + } + } +} \ No newline at end of file diff --git a/src/services/syntax/slidingWindow.ts b/src/services/syntax/slidingWindow.ts new file mode 100644 index 00000000000..d90fa4892c9 --- /dev/null +++ b/src/services/syntax/slidingWindow.ts @@ -0,0 +1,189 @@ +/// + +module TypeScript { + export interface ISlidingWindowSource { + // Asks the source to copy items starting at sourceIndex into the window at 'destinationIndex' + // with up to 'spaceAvailable' items. The actual number of items fetched should be given as + // the return value. + (argument: any): any; + } + + export class SlidingWindow { + + // The number of valid items in window. + public windowCount: number = 0; + + // The *absolute* index in the *full* array of items the *window* array starts at. i.e. + // if there were 100 items, and window contains tokens [70, 80), then this value would be + // 70. + public windowAbsoluteStartIndex: number = 0; + + // The index in the window array that we're at. i.e. if there 100 items and + // window contains tokens [70, 80), and we're on item 75, then this value would be '5'. + // Note: it is not absolute. It is relative to the start of the window. + public currentRelativeItemIndex: number = 0; + + // The number of pinned points there are. As long as there is at least one pinned point, we + // will not advance the start of the window array past the item marked by that pin point. + private _pinCount: number = 0; + + // If there are any outstanding rewind points, this is index in the full array of items + // that the first rewind point points to. If this is not -1, then we will not shift the + // start of the items array past this point. + private firstPinnedAbsoluteIndex: number = -1; + + constructor(// Underlying source that we retrieve items from. + private fetchNextItem: ISlidingWindowSource, + // A window of items that has been read in from the underlying source. + public window: any[], + // The default value to return when there are no more items left in the window. + private defaultValue: any, + // The length of the source we're reading from if we know it up front. -1 if we do not. + private sourceLength = -1) { + } + + private addMoreItemsToWindow(argument: any): boolean { + var sourceLength = this.sourceLength; + if (sourceLength >= 0 && this.absoluteIndex() >= sourceLength) { + return false; + } + + // First, make room for the new items if we're out of room. + if (this.windowCount >= this.window.length) { + this.tryShiftOrGrowWindow(); + } + + var item = this.fetchNextItem(argument); + + this.window[this.windowCount] = item; + + // Assert disabled because it is actually expensive enugh to affect perf. + + this.windowCount++; + return true; + } + + private tryShiftOrGrowWindow(): void { + // We want to shift if our current item is past the halfway point of the current item window. + var currentIndexIsPastWindowHalfwayPoint = this.currentRelativeItemIndex > (this.window.length >>> 1); + + // However, we can only shift if we have no outstanding rewind points. Or, if we have an + // outstanding rewind point, that it points to some point after the start of the window. + var isAllowedToShift = + this.firstPinnedAbsoluteIndex === -1 || + this.firstPinnedAbsoluteIndex > this.windowAbsoluteStartIndex; + + if (currentIndexIsPastWindowHalfwayPoint && isAllowedToShift) { + // Figure out where we're going to start shifting from. If we have no oustanding rewind + // points, then we'll start shifting over all the items starting from the current + // token we're point out. Otherwise, we'll shift starting from the first item that + // the rewind point is pointing at. + // + // We'll call that point 'N' from now on. + var shiftStartIndex = this.firstPinnedAbsoluteIndex === -1 + ? this.currentRelativeItemIndex + : this.firstPinnedAbsoluteIndex - this.windowAbsoluteStartIndex; + + // We have to shift the number of elements between the start index and the number of + // items in the window. + var shiftCount = this.windowCount - shiftStartIndex; + + // Debug.assert(shiftStartIndex > 0); + if (shiftCount > 0) { + ArrayUtilities.copy(this.window, shiftStartIndex, this.window, 0, shiftCount); + } + + // The window has now moved over to the right by N. + this.windowAbsoluteStartIndex += shiftStartIndex; + + // The number of valid items in the window has now decreased by N. + this.windowCount -= shiftStartIndex; + + // The current item now starts further to the left in the window. + this.currentRelativeItemIndex -= shiftStartIndex; + } + else { + // Grow the exisitng array. + // this.window[this.window.length * 2 - 1] = this.defaultValue; + ArrayUtilities.grow(this.window, this.window.length * 2, this.defaultValue); + } + } + + public absoluteIndex(): number { + return this.windowAbsoluteStartIndex + this.currentRelativeItemIndex; + } + + public isAtEndOfSource(): boolean { + return this.absoluteIndex() >= this.sourceLength; + } + + public getAndPinAbsoluteIndex(): number { + // Find the absolute index of this pin point. i.e. it's the index as if we had an + // array containing *all* tokens. + var absoluteIndex = this.absoluteIndex(); + var pinCount = this._pinCount++; + if (pinCount === 0) { + // If this is the first pinned point, then store off this index. We will ensure that + // we never shift the window past this point. + this.firstPinnedAbsoluteIndex = absoluteIndex; + } + + return absoluteIndex; + } + + public releaseAndUnpinAbsoluteIndex(absoluteIndex: number) { + this._pinCount--; + if (this._pinCount === 0) { + // If we just released the last outstanding pin, then we no longer need to 'fix' the + // token window so it can't move forward. Set the index to -1 so that we can shift + // things over the next time we read past the end of the array. + this.firstPinnedAbsoluteIndex = -1; + } + } + + public rewindToPinnedIndex(absoluteIndex: number): void { + // The rewind point shows which absolute item we want to rewind to. Get the relative + // index in the actual array that we want to point to. + var relativeIndex = absoluteIndex - this.windowAbsoluteStartIndex; + + // Make sure we haven't screwed anything up. + // Debug.assert(relativeIndex >= 0 && relativeIndex < this.windowCount); + + // Set ourselves back to that point. + this.currentRelativeItemIndex = relativeIndex; + } + + public currentItem(argument: any): any { + if (this.currentRelativeItemIndex >= this.windowCount) { + if (!this.addMoreItemsToWindow(argument)) { + return this.defaultValue; + } + } + + return this.window[this.currentRelativeItemIndex]; + } + + public peekItemN(n: number): any { + // Assert disabled because it is actually expensive enugh to affect perf. + // Debug.assert(n >= 0); + while (this.currentRelativeItemIndex + n >= this.windowCount) { + if (!this.addMoreItemsToWindow(/*argument:*/ null)) { + return this.defaultValue; + } + } + + return this.window[this.currentRelativeItemIndex + n]; + } + + public moveToNextItem(): void { + this.currentRelativeItemIndex++; + } + + public disgardAllItemsFromCurrentIndexOnwards(): void { + // By setting the window count to the current relative offset, we are effectively making + // any items we added to the window from the current offset onwards unusable. When we + // try to get the next item, we'll be forced to refetch them from the underlying source. + this.windowCount = this.currentRelativeItemIndex; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntax.ts b/src/services/syntax/syntax.ts new file mode 100644 index 00000000000..2cc55ba0620 --- /dev/null +++ b/src/services/syntax/syntax.ts @@ -0,0 +1,283 @@ +/// + +module TypeScript.Syntax { + export var _nextSyntaxID: number = 1; + + export function childIndex(parent: ISyntaxElement, child: ISyntaxElement) { + for (var i = 0, n = childCount(parent); i < n; i++) { + var current = childAt(parent, i); + if (current === child) { + return i; + } + } + + throw Errors.invalidOperation(); + } + + export function nodeHasSkippedOrMissingTokens(node: ISyntaxNode): boolean { + for (var i = 0; i < childCount(node); i++) { + var child = childAt(node, i); + if (isToken(child)) { + var token = child; + // If a token is skipped, return true. Or if it is a missing token. The only empty token that is not missing is EOF + if (token.hasSkippedToken() || (width(token) === 0 && token.kind() !== SyntaxKind.EndOfFileToken)) { + return true; + } + } + } + + return false; + } + + export function isUnterminatedStringLiteral(token: ISyntaxToken): boolean { + if (token && token.kind() === SyntaxKind.StringLiteral) { + var text = token.text(); + return text.length < 2 || text.charCodeAt(text.length - 1) !== text.charCodeAt(0); + } + + return false; + } + + export function isUnterminatedMultilineCommentTrivia(trivia: ISyntaxTrivia): boolean { + if (trivia && trivia.kind() === SyntaxKind.MultiLineCommentTrivia) { + var text = trivia.fullText(); + return text.length < 4 || text.substring(text.length - 2) !== "*/"; + } + return false; + } + + export function isEntirelyInsideCommentTrivia(trivia: ISyntaxTrivia, fullStart: number, position: number): boolean { + if (trivia && trivia.isComment() && position > fullStart) { + var end = fullStart + trivia.fullWidth(); + if (position < end) { + return true; + } + else if (position === end) { + return trivia.kind() === SyntaxKind.SingleLineCommentTrivia || isUnterminatedMultilineCommentTrivia(trivia); + } + } + + return false; + } + + export function isEntirelyInsideComment(sourceUnit: SourceUnitSyntax, position: number): boolean { + var positionedToken = findToken(sourceUnit, position); + var fullStart = positionedToken.fullStart(); + var triviaList: ISyntaxTriviaList = null; + var lastTriviaBeforeToken: ISyntaxTrivia = null; + + if (positionedToken.kind() === SyntaxKind.EndOfFileToken) { + // Check if the trivia is leading on the EndOfFile token + if (positionedToken.hasLeadingTrivia()) { + triviaList = positionedToken.leadingTrivia(); + } + // Or trailing on the previous token + else { + positionedToken = previousToken(positionedToken); + if (positionedToken) { + if (positionedToken && positionedToken.hasTrailingTrivia()) { + triviaList = positionedToken.trailingTrivia(); + fullStart = end(positionedToken); + } + } + } + } + else { + if (position <= (fullStart + positionedToken.leadingTriviaWidth())) { + triviaList = positionedToken.leadingTrivia(); + } + else if (position >= (fullStart + width(positionedToken))) { + triviaList = positionedToken.trailingTrivia(); + fullStart = end(positionedToken); + } + } + + if (triviaList) { + // Try to find the trivia matching the position + for (var i = 0, n = triviaList.count(); i < n; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + if (position <= fullStart) { + // Moved passed the trivia we need + break; + } + else if (position <= fullStart + trivia.fullWidth() && trivia.isComment()) { + // Found the comment trivia we were looking for + lastTriviaBeforeToken = trivia; + break; + } + + fullStart += trivia.fullWidth(); + } + } + + return lastTriviaBeforeToken && isEntirelyInsideCommentTrivia(lastTriviaBeforeToken, fullStart, position); + } + + export function isEntirelyInStringOrRegularExpressionLiteral(sourceUnit: SourceUnitSyntax, position: number): boolean { + var positionedToken = findToken(sourceUnit, position); + + if (positionedToken) { + if (positionedToken.kind() === SyntaxKind.EndOfFileToken) { + // EndOfFile token, enusre it did not follow an unterminated string literal + positionedToken = previousToken(positionedToken); + return positionedToken && positionedToken.trailingTriviaWidth() === 0 && isUnterminatedStringLiteral(positionedToken); + } + else if (position > start(positionedToken)) { + // Ensure position falls enterily within the literal if it is terminated, or the line if it is not + return (position < end(positionedToken) && (positionedToken.kind() === TypeScript.SyntaxKind.StringLiteral || positionedToken.kind() === TypeScript.SyntaxKind.RegularExpressionLiteral)) || + (position <= end(positionedToken) && isUnterminatedStringLiteral(positionedToken)); + } + } + + return false; + } + + function findSkippedTokenOnLeftInTriviaList(positionedToken: ISyntaxToken, position: number, lookInLeadingTriviaList: boolean): ISyntaxToken { + var triviaList: TypeScript.ISyntaxTriviaList = null; + var fullEnd: number; + + if (lookInLeadingTriviaList) { + triviaList = positionedToken.leadingTrivia(); + fullEnd = positionedToken.fullStart() + triviaList.fullWidth(); + } + else { + triviaList = positionedToken.trailingTrivia(); + fullEnd = TypeScript.fullEnd(positionedToken); + } + + if (triviaList && triviaList.hasSkippedToken()) { + for (var i = triviaList.count() - 1; i >= 0; i--) { + var trivia = triviaList.syntaxTriviaAt(i); + var triviaWidth = trivia.fullWidth(); + + if (trivia.isSkippedToken() && position >= fullEnd) { + return trivia.skippedToken(); + } + + fullEnd -= triviaWidth; + } + } + + return null; + } + + export function findSkippedTokenOnLeft(positionedToken: ISyntaxToken, position: number): ISyntaxToken { + var positionInLeadingTriviaList = (position < start(positionedToken)); + return findSkippedTokenOnLeftInTriviaList(positionedToken, position, /*lookInLeadingTriviaList*/ positionInLeadingTriviaList); + } + + export function getAncestorOfKind(positionedToken: ISyntaxElement, kind: SyntaxKind): ISyntaxElement { + while (positionedToken && positionedToken.parent) { + if (positionedToken.parent.kind() === kind) { + return positionedToken.parent; + } + + positionedToken = positionedToken.parent; + } + + return null; + } + + export function hasAncestorOfKind(positionedToken: ISyntaxElement, kind: SyntaxKind): boolean { + return getAncestorOfKind(positionedToken, kind) !== null; + } + + export function isIntegerLiteral(expression: IExpressionSyntax): boolean { + if (expression) { + switch (expression.kind()) { + case SyntaxKind.PlusExpression: + case SyntaxKind.NegateExpression: + // Note: if there is a + or - sign, we can only allow a normal integer following + // (and not a hex integer). i.e. -0xA is a legal expression, but it is not a + // *literal*. + expression = (expression).operand; + return isToken(expression) && IntegerUtilities.isInteger((expression).text()); + + case SyntaxKind.NumericLiteral: + // If it doesn't have a + or -, then either an integer literal or a hex literal + // is acceptable. + var text = ( expression).text(); + return IntegerUtilities.isInteger(text) || IntegerUtilities.isHexInteger(text); + } + } + + return false; + } + + export function containingNode(element: ISyntaxElement): ISyntaxNode { + var current = element.parent; + + while (current !== null && !isNode(current)) { + current = current.parent; + } + + return current; + } + + export function findTokenOnLeft(element: ISyntaxElement, position: number, includeSkippedTokens: boolean = false): ISyntaxToken { + var positionedToken = findToken(element, position, /*includeSkippedTokens*/ false); + var _start = start(positionedToken); + + // Position better fall within this token. + // Debug.assert(position >= positionedToken.fullStart()); + // Debug.assert(position < positionedToken.fullEnd() || positionedToken.token().tokenKind === SyntaxKind.EndOfFileToken); + + if (includeSkippedTokens) { + positionedToken = findSkippedTokenOnLeft(positionedToken, position) || positionedToken; + } + + // if position is after the start of the token, then this token is the token on the left. + if (position > _start) { + return positionedToken; + } + + // we're in the trivia before the start of the token. Need to return the previous token. + if (positionedToken.fullStart() === 0) { + // Already on the first token. Nothing before us. + return null; + } + + return previousToken(positionedToken, includeSkippedTokens); + } + + export function findCompleteTokenOnLeft(element: ISyntaxElement, position: number, includeSkippedTokens: boolean = false): ISyntaxToken { + var positionedToken = findToken(element, position, /*includeSkippedTokens*/ false); + + // Position better fall within this token. + // Debug.assert(position >= positionedToken.fullStart()); + // Debug.assert(position < positionedToken.fullEnd() || positionedToken.token().tokenKind === SyntaxKind.EndOfFileToken); + + if (includeSkippedTokens) { + positionedToken = findSkippedTokenOnLeft(positionedToken, position) || positionedToken; + } + + // if position is after the end of the token, then this token is the token on the left. + if (width(positionedToken) > 0 && position >= end(positionedToken)) { + return positionedToken; + } + + return previousToken(positionedToken, includeSkippedTokens); + } + + export function firstTokenInLineContainingPosition(syntaxTree: SyntaxTree, position: number): ISyntaxToken { + var current = findToken(syntaxTree.sourceUnit(), position); + while (true) { + if (isFirstTokenInLine(current, syntaxTree.lineMap())) { + break; + } + + current = previousToken(current); + } + + return current; + } + + function isFirstTokenInLine(token: ISyntaxToken, lineMap: LineMap): boolean { + var _previousToken = previousToken(token); + if (_previousToken === null) { + return true; + } + + return lineMap.getLineNumberFromPosition(end(_previousToken)) !== lineMap.getLineNumberFromPosition(start(token)); + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxDedenter.ts b/src/services/syntax/syntaxDedenter.ts new file mode 100644 index 00000000000..3c3cdb7b25f --- /dev/null +++ b/src/services/syntax/syntaxDedenter.ts @@ -0,0 +1,193 @@ +/// + +module TypeScript { + export class SyntaxDedenter extends SyntaxRewriter { + private lastTriviaWasNewLine: boolean; + + constructor(dedentFirstToken: boolean, + private dedentationAmount: number, + private minimumIndent: number, + private options: FormattingOptions) { + super(); + this.lastTriviaWasNewLine = dedentFirstToken; + } + + private abort(): void { + this.lastTriviaWasNewLine = false; + this.dedentationAmount = 0; + } + + private isAborted(): boolean { + return this.dedentationAmount === 0; + } + + public visitToken(token: ISyntaxToken): ISyntaxToken { + if (token.width() === 0) { + return token; + } + + var result = token; + if (this.lastTriviaWasNewLine) { + // have to add our indentation to every line that this token hits. + result = token.withLeadingTrivia(this.dedentTriviaList(token.leadingTrivia())); + } + + if (this.isAborted()) { + // If we've decided to stop dedenting. Then just return immediately. + return token; + } + + this.lastTriviaWasNewLine = token.hasTrailingNewLine(); + return result; + } + + private dedentTriviaList(triviaList: ISyntaxTriviaList): ISyntaxTriviaList { + var result: ISyntaxTrivia[] = []; + var dedentNextWhitespace = true; + + // Keep walking through all our trivia (as long as we haven't decided to stop dedenting). + // Adjust the indentation on any whitespace trivia at the start of a line, or any multi-line + // trivia that span multiple lines. + for (var i = 0, n = triviaList.count(); i < n && !this.isAborted(); i++) { + var trivia = triviaList.syntaxTriviaAt(i); + + var dedentThisTrivia = dedentNextWhitespace; + dedentNextWhitespace = false; + + if (dedentThisTrivia) { + if (trivia.kind() === SyntaxKind.WhitespaceTrivia) { + // We pass in if there was a following newline after this whitespace. If there + // is, then it's fine if we dedent this newline all the way to 0. Otherwise, + // if the whitespace is followed by something, then we need to determine how + // much of the whitespace we can remove. If we can't remove all that we want, + // we'll need to adjust the dedentAmount. And, if we can't remove at all, then + // we need to stop dedenting entirely. + var hasFollowingNewLine = (i < triviaList.count() - 1) && + triviaList.syntaxTriviaAt(i + 1).kind() === SyntaxKind.NewLineTrivia; + result.push(this.dedentWhitespace(trivia, hasFollowingNewLine)); + continue; + } + else if (trivia.kind() !== SyntaxKind.NewLineTrivia) { + // We wanted to dedent, but the trivia we're on isn't whitespace and wasn't a + // newline. That means that we have something like a comment at the beginning + // of the line that we can't dedent. And, if we can't dedent it, then we + // shouldn't dedent this token or any more tokens. + this.abort(); + break; + } + } + + if (trivia.kind() === SyntaxKind.MultiLineCommentTrivia) { + // This trivia may span multiple lines. If it does, we need to dedent each + // successive line of it until it terminates. + result.push(this.dedentMultiLineComment(trivia)); + continue; + } + + // All other trivia we just append to the list. + result.push(trivia); + if (trivia.kind() === SyntaxKind.NewLineTrivia) { + // We hit a newline processing the trivia. We need to add the indentation to the + // next line as well. + dedentNextWhitespace = true; + } + } + + if (dedentNextWhitespace) { + // We hit a new line as the last trivia (or there was no trivia). We want to dedent + // the next trivia, but we can't (because the token starts at the start of the line). + // If we can't dedent this, then we shouldn't dedent anymore. + this.abort(); + } + + if (this.isAborted()) { + return triviaList; + } + + return Syntax.triviaList(result); + } + + private dedentSegment(segment: string, hasFollowingNewLineTrivia: boolean): string { + // Find the position of the first non whitespace character in the segment. + var firstNonWhitespacePosition = Indentation.firstNonWhitespacePosition(segment); + + if (firstNonWhitespacePosition === segment.length) { + if (hasFollowingNewLineTrivia) { + // It was entirely whitespace trivia, with a newline after it. Just trim this down + // to an empty string. + return ""; + } + } + else if (CharacterInfo.isLineTerminator(segment.charCodeAt(firstNonWhitespacePosition))) { + // It was entirely whitespace, with a newline after it. Just trim this down to + // the newline + return segment.substring(firstNonWhitespacePosition); + } + + // It was whitespace without a newline following it. We need to try to dedent this a bit. + + // Convert that position to a column. + var firstNonWhitespaceColumn = Indentation.columnForPositionInString(segment, firstNonWhitespacePosition, this.options); + + // Find the new column we want the nonwhitespace text to start at. Ideally it would be + // whatever column it was minus the dedentation amount. However, we won't go below a + // specified minimum indent (hence, max(initial - dedentAmount, minIndent). *But* if + // the initial column was less than that minimum indent, then we'll keep it at that column. + // (hence min(initial, desired)). + var newFirstNonWhitespaceColumn = + MathPrototype.min(firstNonWhitespaceColumn, + MathPrototype.max(firstNonWhitespaceColumn - this.dedentationAmount, this.minimumIndent)); + + if (newFirstNonWhitespaceColumn === firstNonWhitespaceColumn) { + // We aren't able to detent this token. Abort what we're doing + this.abort(); + return segment; + } + + // Update the dedentation amount for all subsequent tokens we run into. + this.dedentationAmount = firstNonWhitespaceColumn - newFirstNonWhitespaceColumn; + Debug.assert(this.dedentationAmount >= 0); + + // Compute an indentation string for that. + var indentationString = Indentation.indentationString(newFirstNonWhitespaceColumn, this.options); + + // Join the new indentation and the original string without its indentation. + return indentationString + segment.substring(firstNonWhitespacePosition); + } + + private dedentWhitespace(trivia: ISyntaxTrivia, hasFollowingNewLineTrivia: boolean): ISyntaxTrivia { + var newIndentation = this.dedentSegment(trivia.fullText(), hasFollowingNewLineTrivia); + return Syntax.whitespace(newIndentation); + } + + private dedentMultiLineComment(trivia: ISyntaxTrivia): ISyntaxTrivia { + var segments = Syntax.splitMultiLineCommentTriviaIntoMultipleLines(trivia); + if (segments.length === 1) { + // If there was only one segment, then this wasn't multiline. + return trivia; + } + + for (var i = 1; i < segments.length; i++) { + var segment = segments[i]; + segments[i] = this.dedentSegment(segment, /*hasFollowingNewLineTrivia*/ false); + } + + var result = segments.join(""); + + // Create a new trivia token out of the indented lines. + return Syntax.multiLineComment(result); + } + + public static dedentNode(node: T, dedentFirstToken: boolean, dedentAmount: number, minimumIndent: number, options: FormattingOptions): T { + var dedenter = new SyntaxDedenter(dedentFirstToken, dedentAmount, minimumIndent, options); + var result = node.accept(dedenter); + + if (dedenter.isAborted()) { + // We failed to dedent a token in this node. Return the original node as is. + return node; + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxElement.ts b/src/services/syntax/syntaxElement.ts new file mode 100644 index 00000000000..211e8b1dd86 --- /dev/null +++ b/src/services/syntax/syntaxElement.ts @@ -0,0 +1,557 @@ +/// + +module TypeScript { + // True if there is only a single instance of this element (and thus can be reused in many + // places in a syntax tree). Examples of this include our empty lists. Because empty + // lists can be found all over the tree, we want to save on memory by using this single + // instance instead of creating new objects for each case. Note: because of this, shared + // nodes don't have positions or parents. + export function isShared(element: ISyntaxElement): boolean { + var kind = element.kind(); + return (kind === SyntaxKind.List || kind === SyntaxKind.SeparatedList) && (element).length === 0; + } + + export function childCount(element: ISyntaxElement): number { + var kind = element.kind(); + if (kind === SyntaxKind.List) { + return (element).length; + } + else if (kind === SyntaxKind.SeparatedList) { + return (element).length + (element).separators.length; + } + else if (kind >= SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken) { + return 0; + } + else { + return nodeMetadata[kind].length; + } + } + + export function childAt(element: ISyntaxElement, index: number): ISyntaxElement { + var kind = element.kind(); + if (kind === SyntaxKind.List) { + return (element)[index]; + } + else if (kind === SyntaxKind.SeparatedList) { + return (index % 2 === 0) ? (element)[index / 2] : (element).separators[(index - 1) / 2]; + } + else { + // Debug.assert(isNode(element)); + return (element)[nodeMetadata[element.kind()][index]]; + } + } + + export function syntaxTree(element: ISyntaxElement): SyntaxTree { + if (element) { + Debug.assert(!isShared(element)); + + while (element) { + if (element.kind() === SyntaxKind.SourceUnit) { + return (element).syntaxTree; + } + + element = element.parent; + } + } + + return null; + } + + export function parsedInStrictMode(node: ISyntaxNode): boolean { + var info = node.data; + if (info === undefined) { + return false; + } + + return (info & SyntaxConstants.NodeParsedInStrictModeMask) !== 0; + } + + export function previousToken(token: ISyntaxToken, includeSkippedTokens: boolean = false): ISyntaxToken { + if (includeSkippedTokens) { + var triviaList = token.leadingTrivia(); + if (triviaList && triviaList.hasSkippedToken()) { + var currentTriviaEndPosition = TypeScript.start(token); + for (var i = triviaList.count() - 1; i >= 0; i--) { + var trivia = triviaList.syntaxTriviaAt(i); + if (trivia.isSkippedToken()) { + return trivia.skippedToken(); + } + + currentTriviaEndPosition -= trivia.fullWidth(); + } + } + } + + var start = token.fullStart(); + if (start === 0) { + return null; + } + + return findToken(syntaxTree(token).sourceUnit(), start - 1, includeSkippedTokens); + } + + /** + * Finds a token according to the following rules: + * 1) If position matches the End of the node/s FullSpan and the node is SourceUnitSyntax, + * then the EOF token is returned. + * + * 2) If node.FullSpan.Contains(position) then the token that contains given position is + * returned. + * + * 3) Otherwise an ArgumentOutOfRangeException is thrown + * + * Note: findToken will always return a non-missing token with width greater than or equal to + * 1 (except for EOF). Empty tokens synthesized by the parser are never returned. + */ + export function findToken(element: ISyntaxElement, position: number, includeSkippedTokens: boolean = false): ISyntaxToken { + var endOfFileToken = tryGetEndOfFileAt(element, position); + if (endOfFileToken !== null) { + return endOfFileToken; + } + + if (position < 0 || position >= fullWidth(element)) { + throw Errors.argumentOutOfRange("position"); + } + + var positionedToken = findTokenWorker(element, position); + + if (includeSkippedTokens) { + return findSkippedTokenInPositionedToken(positionedToken, position) || positionedToken; + } + + // Could not find a better match + return positionedToken; + } + + export function findSkippedTokenInPositionedToken(positionedToken: ISyntaxToken, position: number): ISyntaxToken { + var positionInLeadingTriviaList = (position < start(positionedToken)); + return findSkippedTokenInTriviaList(positionedToken, position, /*lookInLeadingTriviaList*/ positionInLeadingTriviaList); + } + + export function findSkippedTokenInLeadingTriviaList(positionedToken: ISyntaxToken, position: number): ISyntaxToken { + return findSkippedTokenInTriviaList(positionedToken, position, /*lookInLeadingTriviaList*/ true); + } + + export function findSkippedTokenInTrailingTriviaList(positionedToken: ISyntaxToken, position: number): ISyntaxToken { + return findSkippedTokenInTriviaList(positionedToken, position, /*lookInLeadingTriviaList*/ false); + } + + function findSkippedTokenInTriviaList(positionedToken: ISyntaxToken, position: number, lookInLeadingTriviaList: boolean): ISyntaxToken { + var triviaList: TypeScript.ISyntaxTriviaList = null; + var fullStart: number; + + if (lookInLeadingTriviaList) { + triviaList = positionedToken.leadingTrivia(); + fullStart = positionedToken.fullStart(); + } + else { + triviaList = positionedToken.trailingTrivia(); + fullStart = end(positionedToken); + } + + if (triviaList && triviaList.hasSkippedToken()) { + for (var i = 0, n = triviaList.count(); i < n; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + var triviaWidth = trivia.fullWidth(); + + if (trivia.isSkippedToken() && position >= fullStart && position <= fullStart + triviaWidth) { + return trivia.skippedToken(); + } + + fullStart += triviaWidth; + } + } + + return null; + } + + function findTokenWorker(element: ISyntaxElement, position: number): ISyntaxToken { + // Debug.assert(position >= 0 && position < this.fullWidth()); + if (isToken(element)) { + Debug.assert(fullWidth(element) > 0); + return element; + } + + if (isShared(element)) { + // This should never have been called on this element. It has a 0 width, so the client + // should have skipped over this. + throw Errors.invalidOperation(); + } + + // Consider: we could use a binary search here to find the child more quickly. + for (var i = 0, n = childCount(element); i < n; i++) { + var child = childAt(element, i); + + if (child !== null) { + var childFullWidth = fullWidth(child); + if (childFullWidth > 0) { + var childFullStart = fullStart(child); + + if (position >= childFullStart) { + var childFullEnd = childFullStart + childFullWidth; + + if (position < childFullEnd) { + return findTokenWorker(child, position); + } + } + } + } + } + + throw Errors.invalidOperation(); + } + + function tryGetEndOfFileAt(element: ISyntaxElement, position: number): ISyntaxToken { + if (element.kind() === SyntaxKind.SourceUnit && position === fullWidth(element)) { + var sourceUnit = element; + return sourceUnit.endOfFileToken; + } + + return null; + } + + export function nextToken(token: ISyntaxToken, text?: ISimpleText, includeSkippedTokens: boolean = false): ISyntaxToken { + if (token.kind() === SyntaxKind.EndOfFileToken) { + return null; + } + + if (includeSkippedTokens) { + var triviaList = token.trailingTrivia(text); + if (triviaList && triviaList.hasSkippedToken()) { + for (var i = 0, n = triviaList.count(); i < n; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + if (trivia.isSkippedToken()) { + return trivia.skippedToken(); + } + } + } + } + + return findToken(syntaxTree(token).sourceUnit(), fullEnd(token), includeSkippedTokens); + } + + export function isNode(element: ISyntaxElement): boolean { + if (element !== null) { + var kind = element.kind(); + return kind >= SyntaxKind.FirstNode && kind <= SyntaxKind.LastNode; + } + + return false; + } + + function isTokenKind(kind: SyntaxKind) { + return kind >= SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken + } + + export function isToken(element: ISyntaxElement): boolean { + if (element !== null) { + return isTokenKind(element.kind()); + } + + return false; + } + + export function isList(element: ISyntaxElement): boolean { + return element !== null && element.kind() === SyntaxKind.List; + } + + export function isSeparatedList(element: ISyntaxElement): boolean { + return element !== null && element.kind() === SyntaxKind.SeparatedList; + } + + export function syntaxID(element: ISyntaxElement): number { + if (isShared(element)) { + throw Errors.invalidOperation("Should not use shared syntax element as a key."); + } + + var obj = element; + if (obj._syntaxID === undefined) { + obj._syntaxID = TypeScript.Syntax._nextSyntaxID++; + } + + return obj._syntaxID; + } + + function collectTextElements(element: ISyntaxElement, elements: string[], text: ISimpleText): void { + if (element) { + if (isToken(element)) { + elements.push((element).fullText(text)); + } + else { + for (var i = 0, n = childCount(element); i < n; i++) { + collectTextElements(childAt(element, i), elements, text); + } + } + } + } + + export function fullText(element: ISyntaxElement, text?: ISimpleText): string { + if (isToken(element)) { + return (element).fullText(text); + } + + var elements: string[] = []; + collectTextElements(element, elements, text); + + return elements.join(""); + } + + export function leadingTriviaWidth(element: ISyntaxElement, text?: ISimpleText): number { + var token = firstToken(element); + return token ? token.leadingTriviaWidth(text) : 0; + } + + export function trailingTriviaWidth(element: ISyntaxElement, text?: ISimpleText): number { + var token = lastToken(element); + return token ? token.trailingTriviaWidth(text) : 0; + } + + export function firstToken(element: ISyntaxElement): ISyntaxToken { + if (element) { + var kind = element.kind(); + + if (isTokenKind(kind)) { + return fullWidth(element) > 0 || element.kind() === SyntaxKind.EndOfFileToken ? element : null; + } + + if (kind === SyntaxKind.List) { + var array = element; + for (var i = 0, n = array.length; i < n; i++) { + var token = firstToken(array[i]); + if (token) { + return token; + } + } + } + else if (kind === SyntaxKind.SeparatedList) { + var array = element; + var separators = array.separators; + for (var i = 0, n = array.length + separators.length; i < n; i++) { + var token = firstToken(i % 2 === 0 ? array[i / 2] : separators[(i - 1) / 2]); + if (token) { + return token; + } + } + } + else { + var metadata = nodeMetadata[kind]; + for (var i = 0, n = metadata.length; i < n; i++) { + var child = (element)[metadata[i]]; + var token = firstToken(child); + if (token) { + return token; + } + } + + if (element.kind() === SyntaxKind.SourceUnit) { + return (element).endOfFileToken; + } + } + } + + return null; + } + + export function lastToken(element: ISyntaxElement): ISyntaxToken { + if (isToken(element)) { + return fullWidth(element) > 0 || element.kind() === SyntaxKind.EndOfFileToken ? element : null; + } + + if (element.kind() === SyntaxKind.SourceUnit) { + return (element).endOfFileToken; + } + + for (var i = childCount(element) - 1; i >= 0; i--) { + var child = childAt(element, i); + if (child !== null) { + var token = lastToken(child); + if (token) { + return token; + } + } + } + + return null; + } + + export function fullStart(element: ISyntaxElement): number { + Debug.assert(!isShared(element)); + var token = isToken(element) ? element : firstToken(element); + return token ? token.fullStart() : -1; + } + + export function fullWidth(element: ISyntaxElement): number { + if (isToken(element)) { + return (element).fullWidth(); + } + + if (isShared(element)) { + return 0; + } + + var info = data(element); + return info >>> SyntaxConstants.NodeFullWidthShift; + } + + export function isIncrementallyUnusable(element: ISyntaxElement): boolean { + if (isToken(element)) { + return (element).isIncrementallyUnusable(); + } + + if (isShared(element)) { + // All shared lists are reusable. + return false; + } + + return (data(element) & SyntaxConstants.NodeIncrementallyUnusableMask) !== 0; + } + + function data(element: ISyntaxElement): number { + Debug.assert(isNode(element) || isList(element) || isSeparatedList(element)); + + // Lists and nodes all have a 'data' element. + var dataElement = <{ data: number }>element; + + var info = dataElement.data; + if (info === undefined) { + info = 0; + } + + if ((info & SyntaxConstants.NodeDataComputed) === 0) { + info |= computeData(element); + dataElement.data = info; + } + + return info; + } + + function computeData(element: ISyntaxElement): number { + var slotCount = childCount(element); + + var fullWidth = 0; + + // If we have no children (like an OmmittedExpressionSyntax), we're automatically not reusable. + var isIncrementallyUnusable = slotCount === 0; + + for (var i = 0, n = slotCount; i < n; i++) { + var child = childAt(element, i); + + if (child) { + fullWidth += TypeScript.fullWidth(child); + + isIncrementallyUnusable = isIncrementallyUnusable || TypeScript.isIncrementallyUnusable(child); + } + } + + return (fullWidth << SyntaxConstants.NodeFullWidthShift) + | (isIncrementallyUnusable ? SyntaxConstants.NodeIncrementallyUnusableMask : 0) + | SyntaxConstants.NodeDataComputed; + } + + export function start(element: ISyntaxElement, text?: ISimpleText): number { + var token = isToken(element) ? element : firstToken(element); + return token ? token.fullStart() + token.leadingTriviaWidth(text) : -1; + } + + export function end(element: ISyntaxElement, text?: ISimpleText): number { + var token = isToken(element) ? element : lastToken(element); + return token ? fullEnd(token) - token.trailingTriviaWidth(text) : -1; + } + + export function width(element: ISyntaxElement, text?: ISimpleText): number { + if (isToken(element)) { + return (element).text().length; + } + return fullWidth(element) - leadingTriviaWidth(element, text) - trailingTriviaWidth(element, text); + } + + export function fullEnd(element: ISyntaxElement): number { + return fullStart(element) + fullWidth(element); + } + + export function existsNewLineBetweenTokens(token1: ISyntaxToken, token2: ISyntaxToken, text: ISimpleText) { + if (token1 === token2) { + return false; + } + + if (token1 === null || token2 === null) { + return true; + } + + var lineMap = text.lineMap(); + return lineMap.getLineNumberFromPosition(end(token1, text)) !== lineMap.getLineNumberFromPosition(start(token2, text)); + } + + export interface ISyntaxElement { + kind(): SyntaxKind; + parent?: ISyntaxElement; + } + + export interface ISyntaxNode extends ISyntaxNodeOrToken { + data: number; + } + + export interface IModuleReferenceSyntax extends ISyntaxNode { + _moduleReferenceBrand: any; + } + + export interface IModuleElementSyntax extends ISyntaxNode { + } + + export interface IStatementSyntax extends IModuleElementSyntax { + _statementBrand: any; + } + + export interface ITypeMemberSyntax extends ISyntaxNode { + } + + export interface IClassElementSyntax extends ISyntaxNode { + } + + export interface IMemberDeclarationSyntax extends IClassElementSyntax { + } + + export interface IPropertyAssignmentSyntax extends IClassElementSyntax { + } + + export interface ISwitchClauseSyntax extends ISyntaxNode { + _switchClauseBrand: any; + statements: IStatementSyntax[]; + } + + export interface IExpressionSyntax extends ISyntaxNodeOrToken { + _expressionBrand: any; + } + + export interface IUnaryExpressionSyntax extends IExpressionSyntax { + _unaryExpressionBrand: any; + } + + export interface IPostfixExpressionSyntax extends IUnaryExpressionSyntax { + _postfixExpressionBrand: any; + } + + export interface ILeftHandSideExpressionSyntax extends IPostfixExpressionSyntax { + _leftHandSideExpressionBrand: any; + } + + export interface IMemberExpressionSyntax extends ILeftHandSideExpressionSyntax { + _memberExpressionBrand: any; + } + + export interface ICallExpressionSyntax extends ILeftHandSideExpressionSyntax { + _callExpressionBrand: any; + expression: IExpressionSyntax; + } + + export interface IPrimaryExpressionSyntax extends IMemberExpressionSyntax { + _primaryExpressionBrand: any; + } + + export interface ITypeSyntax extends ISyntaxNodeOrToken { + _typeBrand: any; + } + + export interface INameSyntax extends ITypeSyntax { + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxFacts.ts b/src/services/syntax/syntaxFacts.ts new file mode 100644 index 00000000000..3b8d877429c --- /dev/null +++ b/src/services/syntax/syntaxFacts.ts @@ -0,0 +1,301 @@ +/// + +module TypeScript.SyntaxFacts { + var textToKeywordKind: any = { + "any": SyntaxKind.AnyKeyword, + "boolean": SyntaxKind.BooleanKeyword, + "break": SyntaxKind.BreakKeyword, + "case": SyntaxKind.CaseKeyword, + "catch": SyntaxKind.CatchKeyword, + "class": SyntaxKind.ClassKeyword, + "continue": SyntaxKind.ContinueKeyword, + "const": SyntaxKind.ConstKeyword, + "constructor": SyntaxKind.ConstructorKeyword, + "debugger": SyntaxKind.DebuggerKeyword, + "declare": SyntaxKind.DeclareKeyword, + "default": SyntaxKind.DefaultKeyword, + "delete": SyntaxKind.DeleteKeyword, + "do": SyntaxKind.DoKeyword, + "else": SyntaxKind.ElseKeyword, + "enum": SyntaxKind.EnumKeyword, + "export": SyntaxKind.ExportKeyword, + "extends": SyntaxKind.ExtendsKeyword, + "false": SyntaxKind.FalseKeyword, + "finally": SyntaxKind.FinallyKeyword, + "for": SyntaxKind.ForKeyword, + "function": SyntaxKind.FunctionKeyword, + "get": SyntaxKind.GetKeyword, + "if": SyntaxKind.IfKeyword, + "implements": SyntaxKind.ImplementsKeyword, + "import": SyntaxKind.ImportKeyword, + "in": SyntaxKind.InKeyword, + "instanceof": SyntaxKind.InstanceOfKeyword, + "interface": SyntaxKind.InterfaceKeyword, + "let": SyntaxKind.LetKeyword, + "module": SyntaxKind.ModuleKeyword, + "new": SyntaxKind.NewKeyword, + "null": SyntaxKind.NullKeyword, + "number":SyntaxKind.NumberKeyword, + "package": SyntaxKind.PackageKeyword, + "private": SyntaxKind.PrivateKeyword, + "protected": SyntaxKind.ProtectedKeyword, + "public": SyntaxKind.PublicKeyword, + "require": SyntaxKind.RequireKeyword, + "return": SyntaxKind.ReturnKeyword, + "set": SyntaxKind.SetKeyword, + "static": SyntaxKind.StaticKeyword, + "string": SyntaxKind.StringKeyword, + "super": SyntaxKind.SuperKeyword, + "switch": SyntaxKind.SwitchKeyword, + "this": SyntaxKind.ThisKeyword, + "throw": SyntaxKind.ThrowKeyword, + "true": SyntaxKind.TrueKeyword, + "try": SyntaxKind.TryKeyword, + "typeof": SyntaxKind.TypeOfKeyword, + "var": SyntaxKind.VarKeyword, + "void": SyntaxKind.VoidKeyword, + "while": SyntaxKind.WhileKeyword, + "with": SyntaxKind.WithKeyword, + "yield": SyntaxKind.YieldKeyword, + + "{": SyntaxKind.OpenBraceToken, + "}": SyntaxKind.CloseBraceToken, + "(": SyntaxKind.OpenParenToken, + ")": SyntaxKind.CloseParenToken, + "[": SyntaxKind.OpenBracketToken, + "]": SyntaxKind.CloseBracketToken, + ".": SyntaxKind.DotToken, + "...": SyntaxKind.DotDotDotToken, + ";": SyntaxKind.SemicolonToken, + ",": SyntaxKind.CommaToken, + "<": SyntaxKind.LessThanToken, + ">": SyntaxKind.GreaterThanToken, + "<=": SyntaxKind.LessThanEqualsToken, + ">=": SyntaxKind.GreaterThanEqualsToken, + "==": SyntaxKind.EqualsEqualsToken, + "=>": SyntaxKind.EqualsGreaterThanToken, + "!=": SyntaxKind.ExclamationEqualsToken, + "===": SyntaxKind.EqualsEqualsEqualsToken, + "!==": SyntaxKind.ExclamationEqualsEqualsToken, + "+": SyntaxKind.PlusToken, + "-": SyntaxKind.MinusToken, + "*": SyntaxKind.AsteriskToken, + "%": SyntaxKind.PercentToken, + "++": SyntaxKind.PlusPlusToken, + "--": SyntaxKind.MinusMinusToken, + "<<": SyntaxKind.LessThanLessThanToken, + ">>": SyntaxKind.GreaterThanGreaterThanToken, + ">>>": SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + "&": SyntaxKind.AmpersandToken, + "|": SyntaxKind.BarToken, + "^": SyntaxKind.CaretToken, + "!": SyntaxKind.ExclamationToken, + "~": SyntaxKind.TildeToken, + "&&": SyntaxKind.AmpersandAmpersandToken, + "||": SyntaxKind.BarBarToken, + "?": SyntaxKind.QuestionToken, + ":": SyntaxKind.ColonToken, + "=": SyntaxKind.EqualsToken, + "+=": SyntaxKind.PlusEqualsToken, + "-=": SyntaxKind.MinusEqualsToken, + "*=": SyntaxKind.AsteriskEqualsToken, + "%=": SyntaxKind.PercentEqualsToken, + "<<=": SyntaxKind.LessThanLessThanEqualsToken, + ">>=": SyntaxKind.GreaterThanGreaterThanEqualsToken, + ">>>=": SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + "&=": SyntaxKind.AmpersandEqualsToken, + "|=": SyntaxKind.BarEqualsToken, + "^=": SyntaxKind.CaretEqualsToken, + "/": SyntaxKind.SlashToken, + "/=": SyntaxKind.SlashEqualsToken, + }; + + var kindToText = new Array(); + + for (var name in textToKeywordKind) { + if (textToKeywordKind.hasOwnProperty(name)) { + // Debug.assert(kindToText[textToKeywordKind[name]] === undefined); + kindToText[textToKeywordKind[name]] = name; + } + } + + // Manually work around a bug in the CScript 5.8 runtime where 'constructor' is not + // listed when SyntaxFacts.textToKeywordKind is enumerated because it is the name of + // the constructor function. + kindToText[SyntaxKind.ConstructorKeyword] = "constructor"; + + export function getTokenKind(text: string): SyntaxKind { + if (textToKeywordKind.hasOwnProperty(text)) { + return textToKeywordKind[text]; + } + + return SyntaxKind.None; + } + + export function getText(kind: SyntaxKind): string { + var result = kindToText[kind]; + return result !== undefined ? result : null; + } + + export function isAnyKeyword(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstKeyword && kind <= SyntaxKind.LastKeyword; + } + + export function isAnyPunctuation(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation; + } + + export function isPrefixUnaryExpressionOperatorToken(tokenKind: SyntaxKind): boolean { + return getPrefixUnaryExpressionFromOperatorToken(tokenKind) !== SyntaxKind.None; + } + + export function isBinaryExpressionOperatorToken(tokenKind: SyntaxKind): boolean { + return getBinaryExpressionFromOperatorToken(tokenKind) !== SyntaxKind.None; + } + + export function getPrefixUnaryExpressionFromOperatorToken(tokenKind: SyntaxKind): SyntaxKind { + switch (tokenKind) { + case SyntaxKind.PlusToken: return SyntaxKind.PlusExpression; + case SyntaxKind.MinusToken: return SyntaxKind.NegateExpression; + case SyntaxKind.TildeToken: return SyntaxKind.BitwiseNotExpression; + case SyntaxKind.ExclamationToken: return SyntaxKind.LogicalNotExpression; + case SyntaxKind.PlusPlusToken: return SyntaxKind.PreIncrementExpression; + case SyntaxKind.MinusMinusToken: return SyntaxKind.PreDecrementExpression; + default: return SyntaxKind.None; + } + } + + export function getPostfixUnaryExpressionFromOperatorToken(tokenKind: SyntaxKind): SyntaxKind { + switch (tokenKind) { + case SyntaxKind.PlusPlusToken: return SyntaxKind.PostIncrementExpression; + case SyntaxKind.MinusMinusToken: return SyntaxKind.PostDecrementExpression; + default: return SyntaxKind.None; + } + } + + export function getBinaryExpressionFromOperatorToken(tokenKind: SyntaxKind): SyntaxKind { + switch (tokenKind) { + case SyntaxKind.AsteriskToken: return SyntaxKind.MultiplyExpression; + case SyntaxKind.SlashToken: return SyntaxKind.DivideExpression; + case SyntaxKind.PercentToken: return SyntaxKind.ModuloExpression; + case SyntaxKind.PlusToken: return SyntaxKind.AddExpression; + case SyntaxKind.MinusToken: return SyntaxKind.SubtractExpression; + case SyntaxKind.LessThanLessThanToken: return SyntaxKind.LeftShiftExpression; + case SyntaxKind.GreaterThanGreaterThanToken: return SyntaxKind.SignedRightShiftExpression; + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return SyntaxKind.UnsignedRightShiftExpression; + case SyntaxKind.LessThanToken: return SyntaxKind.LessThanExpression; + case SyntaxKind.GreaterThanToken: return SyntaxKind.GreaterThanExpression; + case SyntaxKind.LessThanEqualsToken: return SyntaxKind.LessThanOrEqualExpression; + case SyntaxKind.GreaterThanEqualsToken: return SyntaxKind.GreaterThanOrEqualExpression; + case SyntaxKind.InstanceOfKeyword: return SyntaxKind.InstanceOfExpression; + case SyntaxKind.InKeyword: return SyntaxKind.InExpression; + case SyntaxKind.EqualsEqualsToken: return SyntaxKind.EqualsWithTypeConversionExpression; + case SyntaxKind.ExclamationEqualsToken: return SyntaxKind.NotEqualsWithTypeConversionExpression; + case SyntaxKind.EqualsEqualsEqualsToken: return SyntaxKind.EqualsExpression; + case SyntaxKind.ExclamationEqualsEqualsToken: return SyntaxKind.NotEqualsExpression; + case SyntaxKind.AmpersandToken: return SyntaxKind.BitwiseAndExpression; + case SyntaxKind.CaretToken: return SyntaxKind.BitwiseExclusiveOrExpression; + case SyntaxKind.BarToken: return SyntaxKind.BitwiseOrExpression; + case SyntaxKind.AmpersandAmpersandToken: return SyntaxKind.LogicalAndExpression; + case SyntaxKind.BarBarToken: return SyntaxKind.LogicalOrExpression; + case SyntaxKind.BarEqualsToken: return SyntaxKind.OrAssignmentExpression; + case SyntaxKind.AmpersandEqualsToken: return SyntaxKind.AndAssignmentExpression; + case SyntaxKind.CaretEqualsToken: return SyntaxKind.ExclusiveOrAssignmentExpression; + case SyntaxKind.LessThanLessThanEqualsToken: return SyntaxKind.LeftShiftAssignmentExpression; + case SyntaxKind.GreaterThanGreaterThanEqualsToken: return SyntaxKind.SignedRightShiftAssignmentExpression; + case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: return SyntaxKind.UnsignedRightShiftAssignmentExpression; + case SyntaxKind.PlusEqualsToken: return SyntaxKind.AddAssignmentExpression; + case SyntaxKind.MinusEqualsToken: return SyntaxKind.SubtractAssignmentExpression; + case SyntaxKind.AsteriskEqualsToken: return SyntaxKind.MultiplyAssignmentExpression; + case SyntaxKind.SlashEqualsToken: return SyntaxKind.DivideAssignmentExpression; + case SyntaxKind.PercentEqualsToken: return SyntaxKind.ModuloAssignmentExpression; + case SyntaxKind.EqualsToken: return SyntaxKind.AssignmentExpression; + case SyntaxKind.CommaToken: return SyntaxKind.CommaExpression; + default: return SyntaxKind.None; + } + } + + export function getOperatorTokenFromBinaryExpression(tokenKind: SyntaxKind): SyntaxKind { + switch (tokenKind) { + case SyntaxKind.MultiplyExpression: return SyntaxKind.AsteriskToken; + case SyntaxKind.DivideExpression: return SyntaxKind.SlashToken; + case SyntaxKind.ModuloExpression: return SyntaxKind.PercentToken; + case SyntaxKind.AddExpression: return SyntaxKind.PlusToken; + case SyntaxKind.SubtractExpression: return SyntaxKind.MinusToken; + case SyntaxKind.LeftShiftExpression: return SyntaxKind.LessThanLessThanToken; + case SyntaxKind.SignedRightShiftExpression: return SyntaxKind.GreaterThanGreaterThanToken; + case SyntaxKind.UnsignedRightShiftExpression: return SyntaxKind.GreaterThanGreaterThanGreaterThanToken; + case SyntaxKind.LessThanExpression: return SyntaxKind.LessThanToken; + case SyntaxKind.GreaterThanExpression: return SyntaxKind.GreaterThanToken; + case SyntaxKind.LessThanOrEqualExpression: return SyntaxKind.LessThanEqualsToken; + case SyntaxKind.GreaterThanOrEqualExpression: return SyntaxKind.GreaterThanEqualsToken; + case SyntaxKind.InstanceOfExpression: return SyntaxKind.InstanceOfKeyword; + case SyntaxKind.InExpression: return SyntaxKind.InKeyword; + case SyntaxKind.EqualsWithTypeConversionExpression: return SyntaxKind.EqualsEqualsToken; + case SyntaxKind.NotEqualsWithTypeConversionExpression: return SyntaxKind.ExclamationEqualsToken; + case SyntaxKind.EqualsExpression: return SyntaxKind.EqualsEqualsEqualsToken; + case SyntaxKind.NotEqualsExpression: return SyntaxKind.ExclamationEqualsEqualsToken; + case SyntaxKind.BitwiseAndExpression: return SyntaxKind.AmpersandToken; + case SyntaxKind.BitwiseExclusiveOrExpression: return SyntaxKind.CaretToken; + case SyntaxKind.BitwiseOrExpression: return SyntaxKind.BarToken; + case SyntaxKind.LogicalAndExpression: return SyntaxKind.AmpersandAmpersandToken; + case SyntaxKind.LogicalOrExpression: return SyntaxKind.BarBarToken; + case SyntaxKind.OrAssignmentExpression: return SyntaxKind.BarEqualsToken; + case SyntaxKind.AndAssignmentExpression: return SyntaxKind.AmpersandEqualsToken; + case SyntaxKind.ExclusiveOrAssignmentExpression: return SyntaxKind.CaretEqualsToken; + case SyntaxKind.LeftShiftAssignmentExpression: return SyntaxKind.LessThanLessThanEqualsToken; + case SyntaxKind.SignedRightShiftAssignmentExpression: return SyntaxKind.GreaterThanGreaterThanEqualsToken; + case SyntaxKind.UnsignedRightShiftAssignmentExpression: return SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; + case SyntaxKind.AddAssignmentExpression: return SyntaxKind.PlusEqualsToken; + case SyntaxKind.SubtractAssignmentExpression: return SyntaxKind.MinusEqualsToken; + case SyntaxKind.MultiplyAssignmentExpression: return SyntaxKind.AsteriskEqualsToken; + case SyntaxKind.DivideAssignmentExpression: return SyntaxKind.SlashEqualsToken; + case SyntaxKind.ModuloAssignmentExpression: return SyntaxKind.PercentEqualsToken; + case SyntaxKind.AssignmentExpression: return SyntaxKind.EqualsToken; + case SyntaxKind.CommaExpression: return SyntaxKind.CommaToken; + default: return SyntaxKind.None; + } + } + + export function isAssignmentOperatorToken(tokenKind: SyntaxKind): boolean { + switch (tokenKind) { + case SyntaxKind.BarEqualsToken: + case SyntaxKind.AmpersandEqualsToken: + case SyntaxKind.CaretEqualsToken: + case SyntaxKind.LessThanLessThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanEqualsToken: + case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + case SyntaxKind.PlusEqualsToken: + case SyntaxKind.MinusEqualsToken: + case SyntaxKind.AsteriskEqualsToken: + case SyntaxKind.SlashEqualsToken: + case SyntaxKind.PercentEqualsToken: + case SyntaxKind.EqualsToken: + return true; + + default: + return false; + } + } + + export function isType(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.ArrayType: + case SyntaxKind.AnyKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.FunctionType: + case SyntaxKind.ObjectType: + case SyntaxKind.ConstructorType: + case SyntaxKind.TypeQuery: + case SyntaxKind.GenericType: + case SyntaxKind.QualifiedName: + case SyntaxKind.IdentifierName: + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxFacts2.ts b/src/services/syntax/syntaxFacts2.ts new file mode 100644 index 00000000000..ec59a583e05 --- /dev/null +++ b/src/services/syntax/syntaxFacts2.ts @@ -0,0 +1,29 @@ +/// + +module TypeScript.SyntaxFacts { + export function isDirectivePrologueElement(node: ISyntaxNodeOrToken): boolean { + if (node.kind() === SyntaxKind.ExpressionStatement) { + var expressionStatement = node; + var expression = expressionStatement.expression; + + if (expression.kind() === SyntaxKind.StringLiteral) { + return true; + } + } + + return false; + } + + export function isUseStrictDirective(node: ISyntaxNodeOrToken): boolean { + var expressionStatement = node; + var stringLiteral = expressionStatement.expression; + + var text = stringLiteral.text(); + return text === '"use strict"' || text === "'use strict'"; + } + + export function isIdentifierNameOrAnyKeyword(token: ISyntaxToken): boolean { + var tokenKind = token.kind(); + return tokenKind === SyntaxKind.IdentifierName || SyntaxFacts.isAnyKeyword(tokenKind); + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxGenerator.ts b/src/services/syntax/syntaxGenerator.ts new file mode 100644 index 00000000000..21f65bb79e3 --- /dev/null +++ b/src/services/syntax/syntaxGenerator.ts @@ -0,0 +1,2883 @@ +/// +/// +/// +/// + +// Adds argument checking to the generated nodes. Argument checking appears to slow things down +// parsing about 7%. If we want to get that perf back, we can always remove this. +var argumentChecks = false; +var forPrettyPrinter = false; + +interface ITypeDefinition { + name: string; + baseType: string; + interfaces?: string[]; + children: IMemberDefinition[]; + syntaxKinds?: string[]; + isTypeScriptSpecific: boolean; +} + +interface IMemberDefinition { + name: string; + type?: string; + isToken?: boolean; + isList?: boolean; + isSeparatedList?: boolean; + requiresAtLeastOneItem?: boolean; + isOptional?: boolean; + tokenKinds?: string[]; + isTypeScriptSpecific: boolean; + elementType?: string; + excludeFromAST?: boolean; +} + +var interfaces: TypeScript.IIndexable = { + IMemberDeclarationSyntax: 'IClassElementSyntax', + IStatementSyntax: 'IModuleElementSyntax', + INameSyntax: 'ITypeSyntax', + IUnaryExpressionSyntax: 'IExpressionSyntax', + IPostfixExpressionSyntax: 'IUnaryExpressionSyntax', + ILeftHandSideExpressionSyntax: 'IPostfixExpressionSyntax', + // Note: for simplicity's sake, we merge CallExpression, NewExpression and MemberExpression + // into IMemberExpression. + IMemberExpressionSyntax: 'ILeftHandSideExpressionSyntax', + ICallExpressionSyntax: 'ILeftHandSideExpressionSyntax', + IPrimaryExpressionSyntax: 'IMemberExpressionSyntax', +}; + +var definitions:ITypeDefinition[] = [ + { + name: 'SourceUnitSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'moduleElements', isList: true, elementType: 'IModuleElementSyntax' }, + { name: 'endOfFileToken', isToken: true } + ] + }, + { + name: 'ExternalModuleReferenceSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleReferenceSyntax'], + children: [ + { name: 'requireKeyword', isToken: true, tokenKinds: ['RequireKeyword'], excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'stringLiteral', isToken: true, tokenKinds: ['StringLiteral'] }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ModuleNameModuleReferenceSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleReferenceSyntax'], + children: [ + { name: 'moduleName', type: 'INameSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'ImportDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'importKeyword', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'equalsToken', isToken: true, excludeFromAST: true }, + { name: 'moduleReference', type: 'IModuleReferenceSyntax' }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ExportAssignmentSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleElementSyntax'], + children: [ + { name: 'exportKeyword', isToken: true, excludeFromAST: true }, + { name: 'equalsToken', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ClassDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'classKeyword', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true }, + { name: 'heritageClauses', isList: true, elementType: 'HeritageClauseSyntax' }, + { name: 'openBraceToken', isToken: true, excludeFromAST: true }, + { name: 'classElements', isList: true, elementType: 'IClassElementSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'InterfaceDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'interfaceKeyword', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true }, + { name: 'heritageClauses', isList: true, elementType: 'HeritageClauseSyntax' }, + { name: 'body', type: 'ObjectTypeSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'HeritageClauseSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'extendsOrImplementsKeyword', isToken: true, tokenKinds: ['ExtendsKeyword', 'ImplementsKeyword'] }, + { name: 'typeNames', isSeparatedList: true, requiresAtLeastOneItem: true, elementType: 'INameSyntax' } + ], + syntaxKinds: ["ExtendsHeritageClause", "ImplementsHeritageClause"], + isTypeScriptSpecific: true + }, + { + name: 'ModuleDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'moduleKeyword', isToken: true, excludeFromAST: true }, + { name: 'name', type: 'INameSyntax', isOptional: true }, + { name: 'stringLiteral', isToken: true, isOptional: true, tokenKinds: ['StringLiteral'] }, + { name: 'openBraceToken', isToken: true, excludeFromAST: true }, + { name: 'moduleElements', isList: true, elementType: 'IModuleElementSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'FunctionDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken', isTypeScriptSpecific: true }, + { name: 'functionKeyword', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax', isOptional: true }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'VariableStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken', isTypeScriptSpecific: true }, + { name: 'variableDeclaration', type: 'VariableDeclarationSyntax' }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'VariableDeclarationSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'varKeyword', isToken: true }, + { name: 'variableDeclarators', isSeparatedList: true, requiresAtLeastOneItem: true, elementType: 'VariableDeclaratorSyntax' } + ] + }, + { + name: 'VariableDeclaratorSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecific: true }, + { name: 'equalsValueClause', type: 'EqualsValueClauseSyntax', isOptional: true } + ] + }, + { + name: 'EqualsValueClauseSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'equalsToken', isToken: true, excludeFromAST: true }, + { name: 'value', type: 'IExpressionSyntax' } + ] + }, + { + name: 'PrefixUnaryExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'operatorToken', isToken: true, tokenKinds: ['PlusPlusToken', 'MinusMinusToken', 'PlusToken', 'MinusToken', 'TildeToken', 'ExclamationToken'] }, + { name: 'operand', type: 'IUnaryExpressionSyntax' } + ], + syntaxKinds: ["PreIncrementExpression", "PreDecrementExpression", "PlusExpression", "NegateExpression", "BitwiseNotExpression", "LogicalNotExpression"], + }, + { + name: 'ArrayLiteralExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPrimaryExpressionSyntax'], + children: [ + { name: 'openBracketToken', isToken: true, excludeFromAST: true }, + { name: 'expressions', isSeparatedList: true, elementType: 'IExpressionSyntax' }, + { name: 'closeBracketToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'OmittedExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IExpressionSyntax'], + children: [] + }, + { + name: 'ParenthesizedExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPrimaryExpressionSyntax'], + children: [ + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'SimpleArrowFunctionExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'parameter', type: 'ParameterSyntax' }, + { name: 'equalsGreaterThanToken', isToken: true, excludeFromAST: true }, + { name: 'block', type: 'BlockSyntax', isOptional: true }, + { name: 'expression', type: 'IExpressionSyntax', isOptional: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ParenthesizedArrowFunctionExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'equalsGreaterThanToken', isToken: true, excludeFromAST: true }, + { name: 'block', type: 'BlockSyntax', isOptional: true }, + { name: 'expression', type: 'IExpressionSyntax', isOptional: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'QualifiedNameSyntax', + baseType: 'ISyntaxNode', + interfaces: ['INameSyntax'], + children: [ + { name: 'left', type: 'INameSyntax' }, + { name: 'dotToken', isToken: true, excludeFromAST: true }, + { name: 'right', isToken: true, tokenKinds:['IdentifierName'] } + ], + // Qualified names only show up in Types, which are TypeScript specific. Note that a dotted + // expression (like A.B.Foo()) is a MemberAccessExpression, not a QualifiedName. + isTypeScriptSpecific: true + }, + { + name: 'TypeArgumentListSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'lessThanToken', isToken: true }, + { name: 'typeArguments', isSeparatedList: true, elementType: 'ITypeSyntax' }, + { name: 'greaterThanToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ConstructorTypeSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeSyntax'], + children: [ + { name: 'newKeyword', isToken: true, excludeFromAST: true }, + { name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true }, + { name: 'parameterList', type: 'ParameterListSyntax' }, + { name: 'equalsGreaterThanToken', isToken: true, excludeFromAST: true }, + { name: 'type', type: 'ITypeSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'FunctionTypeSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeSyntax'], + children: [ + { name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true }, + { name: 'parameterList', type: 'ParameterListSyntax' }, + { name: 'equalsGreaterThanToken', isToken: true, excludeFromAST: true }, + { name: 'type', type: 'ITypeSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'ObjectTypeSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeSyntax'], + children: [ + { name: 'openBraceToken', isToken: true, excludeFromAST: true }, + { name: 'typeMembers', isSeparatedList: true, elementType: 'ITypeMemberSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ArrayTypeSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeSyntax'], + children: [ + { name: 'type', type: 'ITypeSyntax' }, + { name: 'openBracketToken', isToken: true, excludeFromAST: true }, + { name: 'closeBracketToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'GenericTypeSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeSyntax'], + children: [ + { name: 'name', type: 'INameSyntax' }, + { name: 'typeArgumentList', type: 'TypeArgumentListSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'TypeQuerySyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeSyntax'], + children: [ + { name: 'typeOfKeyword', isToken: true, excludeFromAST: true }, + { name: 'name', type: 'INameSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'TypeAnnotationSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'colonToken', isToken: true, excludeFromAST: true }, + { name: 'type', type: 'ITypeSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'BlockSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'openBraceToken', isToken: true }, + { name: 'statements', isList: true, elementType: 'IStatementSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'ParameterSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'dotDotDotToken', isToken: true, isOptional: true, isTypeScriptSpecific: true }, + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'questionToken', isToken: true, isOptional: true, isTypeScriptSpecific: true }, + { name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecific: true }, + { name: 'equalsValueClause', type: 'EqualsValueClauseSyntax', isOptional: true, isTypeScriptSpecific: true } + ] + }, + { + name: 'MemberAccessExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IMemberExpressionSyntax', 'ICallExpressionSyntax'], + children: [ + { name: 'expression', type: 'ILeftHandSideExpressionSyntax' }, + { name: 'dotToken', isToken: true, excludeFromAST: true }, + { name: 'name', isToken: true, tokenKinds: ['IdentifierName'] } + ] + }, + { + name: 'PostfixUnaryExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPostfixExpressionSyntax'], + children: [ + { name: 'operand', type: 'ILeftHandSideExpressionSyntax' }, + { name: 'operatorToken', isToken: true, tokenKinds:['PlusPlusToken', 'MinusMinusToken'] } + ], + syntaxKinds: ["PostIncrementExpression", "PostDecrementExpression"], + }, + { + name: 'ElementAccessExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IMemberExpressionSyntax', 'ICallExpressionSyntax'], + children: [ + { name: 'expression', type: 'ILeftHandSideExpressionSyntax' }, + { name: 'openBracketToken', isToken: true, excludeFromAST: true }, + { name: 'argumentExpression', type: 'IExpressionSyntax' }, + { name: 'closeBracketToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'InvocationExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ICallExpressionSyntax'], + children: [ + { name: 'expression', type: 'ILeftHandSideExpressionSyntax' }, + { name: 'argumentList', type: 'ArgumentListSyntax' } + ] + }, + { + name: 'ArgumentListSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'typeArgumentList', type: 'TypeArgumentListSyntax', isOptional: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'arguments', isSeparatedList: true, elementType: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'BinaryExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IExpressionSyntax'], + children: [ + { name: 'left', type: 'IExpressionSyntax' }, + { name: 'operatorToken', isToken: true, + tokenKinds:['AsteriskToken', 'SlashToken', 'PercentToken', 'PlusToken', 'MinusToken', 'LessThanLessThanToken', + 'GreaterThanGreaterThanToken', 'GreaterThanGreaterThanGreaterThanToken', 'LessThanToken', + 'GreaterThanToken', 'LessThanEqualsToken', 'GreaterThanEqualsToken', 'InstanceOfKeyword', + 'InKeyword', 'EqualsEqualsToken', 'ExclamationEqualsToken', 'EqualsEqualsEqualsToken', + 'ExclamationEqualsEqualsToken', 'AmpersandToken', 'CaretToken', 'BarToken', 'AmpersandAmpersandToken', + 'BarBarToken', 'BarEqualsToken', 'AmpersandEqualsToken', 'CaretEqualsToken', 'LessThanLessThanEqualsToken', + 'GreaterThanGreaterThanEqualsToken', 'GreaterThanGreaterThanGreaterThanEqualsToken', 'PlusEqualsToken', + 'MinusEqualsToken', 'AsteriskEqualsToken', 'SlashEqualsToken', 'PercentEqualsToken', 'EqualsToken', + 'CommaToken'] }, + { name: 'right', type: 'IExpressionSyntax' } + ], + syntaxKinds: ["MultiplyExpression", "DivideExpression", "ModuloExpression", "AddExpression", "SubtractExpression", "LeftShiftExpression", + "SignedRightShiftExpression", "UnsignedRightShiftExpression", "LessThanExpression", + "GreaterThanExpression", "LessThanOrEqualExpression", "GreaterThanOrEqualExpression", "InstanceOfExpression", + "InExpression", "EqualsWithTypeConversionExpression", "NotEqualsWithTypeConversionExpression", "EqualsExpression", + "NotEqualsExpression", "BitwiseAndExpression", "BitwiseExclusiveOrExpression", "BitwiseOrExpression", "LogicalAndExpression", + "LogicalOrExpression", "OrAssignmentExpression", "AndAssignmentExpression", "ExclusiveOrAssignmentExpression", "LeftShiftAssignmentExpression", + "SignedRightShiftAssignmentExpression", "UnsignedRightShiftAssignmentExpression", "AddAssignmentExpression", + "SubtractAssignmentExpression", "MultiplyAssignmentExpression", "DivideAssignmentExpression", "ModuloAssignmentExpression", "AssignmentExpression", + "CommaExpression"] + }, + { + name: 'ConditionalExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IExpressionSyntax'], + children: [ + { name: 'condition', type: 'IExpressionSyntax' }, + { name: 'questionToken', isToken: true, excludeFromAST: true }, + { name: 'whenTrue', type: 'IExpressionSyntax' }, + { name: 'colonToken', isToken: true, excludeFromAST: true }, + { name: 'whenFalse', type: 'IExpressionSyntax' } + ] + }, + { + name: 'ConstructSignatureSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeMemberSyntax'], + children: [ + { name: 'newKeyword', isToken: true, excludeFromAST: true }, + { name: 'callSignature', type: 'CallSignatureSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'MethodSignatureSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeMemberSyntax'], + children: [ + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'questionToken', isToken: true, isOptional: true, itTypeScriptSpecific: true }, + { name: 'callSignature', type: 'CallSignatureSyntax' } + ] + }, + { + name: 'IndexSignatureSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeMemberSyntax'], + children: [ + { name: 'openBracketToken', isToken: true }, + { name: 'parameters', isSeparatedList: true, elementType: 'ParameterSyntax' }, + { name: 'closeBracketToken', isToken: true }, + { name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'PropertySignatureSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeMemberSyntax'], + children: [ + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'questionToken', isToken: true, isOptional: true }, + { name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'CallSignatureSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ITypeMemberSyntax'], + children: [ + { name: 'typeParameterList', type: 'TypeParameterListSyntax', isOptional: true, isTypeScriptSpecific: true }, + { name: 'parameterList', type: 'ParameterListSyntax' }, + { name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecific: true } + ] + }, + { + name: 'ParameterListSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'parameters', isSeparatedList: true, elementType: 'ParameterSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'TypeParameterListSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'lessThanToken', isToken: true }, + { name: 'typeParameters', isSeparatedList: true, elementType: 'TypeParameterSyntax' }, + { name: 'greaterThanToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'TypeParameterSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'constraint', type: 'ConstraintSyntax', isOptional: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ConstraintSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'extendsKeyword', isToken: true, excludeFromAST: true }, + // Expression only in error cases. + { name: 'typeOrExpression', type: 'ISyntaxNodeOrToken' } + ], + isTypeScriptSpecific: true + }, + { + name: 'ElseClauseSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'elseKeyword', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' } + ] + }, + { + name: 'IfStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'ifKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'condition', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' }, + { name: 'elseClause', type: 'ElseClauseSyntax', isOptional: true } + ] + }, + { + name: 'ExpressionStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'expression', type: 'IExpressionSyntax' }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'ConstructorDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IClassElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'constructorKeyword', isToken: true }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax', isOptional: true }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'MemberFunctionDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IMemberDeclarationSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax', isOptional: true }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'GetAccessorSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IMemberDeclarationSyntax', 'IPropertyAssignmentSyntax' ], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken', isTypeScriptSpecific: true }, + { name: 'getKeyword', isToken: true, excludeFromAST: true }, + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax' } + ] + }, + { + name: 'SetAccessorSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IMemberDeclarationSyntax', 'IPropertyAssignmentSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken', isTypeScriptSpecific: true }, + { name: 'setKeyword', isToken: true, excludeFromAST: true }, + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'MemberVariableDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IMemberDeclarationSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'variableDeclarator', type: 'VariableDeclaratorSyntax' }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'IndexMemberDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IClassElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'indexSignature', type: 'IndexSignatureSyntax' }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'ThrowStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'throwKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IExpressionSyntax' }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'ReturnStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'returnKeyword', isToken: true }, + { name: 'expression', type: 'IExpressionSyntax', isOptional: true }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'ObjectCreationExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPrimaryExpressionSyntax'], + children: [ + { name: 'newKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IMemberExpressionSyntax' }, + { name: 'argumentList', type: 'ArgumentListSyntax', isOptional: true } + ] + }, + { + name: 'SwitchStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'switchKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'openBraceToken', isToken: true, excludeFromAST: true }, + { name: 'switchClauses', isList: true, elementType: 'ISwitchClauseSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'CaseSwitchClauseSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ISwitchClauseSyntax'], + children: [ + { name: 'caseKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IExpressionSyntax' }, + { name: 'colonToken', isToken: true, excludeFromAST: true}, + { name: 'statements', isList: true, elementType: 'IStatementSyntax' } + ] + }, + { + name: 'DefaultSwitchClauseSyntax', + baseType: 'ISyntaxNode', + interfaces: ['ISwitchClauseSyntax'], + children: [ + { name: 'defaultKeyword', isToken: true, excludeFromAST: true }, + { name: 'colonToken', isToken: true, excludeFromAST: true }, + { name: 'statements', isList: true, elementType: 'IStatementSyntax' } + ] + }, + { + name: 'BreakStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'breakKeyword', isToken: true }, + { name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'ContinueStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'continueKeyword', isToken: true }, + { name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true } + ] + }, + { + name: 'ForStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'forKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'variableDeclaration', type: 'VariableDeclarationSyntax', isOptional: true }, + { name: 'initializer', type: 'IExpressionSyntax', isOptional: true }, + { name: 'firstSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true }, + { name: 'condition', type: 'IExpressionSyntax', isOptional: true }, + { name: 'secondSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true }, + { name: 'incrementor', type: 'IExpressionSyntax', isOptional: true }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' } + ] + }, + { + name: 'ForInStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'forKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'variableDeclaration', type: 'VariableDeclarationSyntax', isOptional: true }, + { name: 'left', type: 'IExpressionSyntax', isOptional: true }, + { name: 'inKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' } + ] + }, + { + name: 'WhileStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'whileKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'condition', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' } + ] + }, + { + name: 'WithStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'withKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'condition', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' } + ] + }, + { + name: 'EnumDeclarationSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IModuleElementSyntax'], + children: [ + { name: 'modifiers', isList: true, elementType: 'ISyntaxToken' }, + { name: 'enumKeyword', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'openBraceToken', isToken: true, excludeFromAST: true }, + { name: 'enumElements', isSeparatedList: true, elementType: 'EnumElementSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ], + isTypeScriptSpecific: true + }, + { + name: 'EnumElementSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'equalsValueClause', type: 'EqualsValueClauseSyntax', isOptional: true } + ] + }, + { + name: 'CastExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'lessThanToken', isToken: true, excludeFromAST: true }, + { name: 'type', type: 'ITypeSyntax' }, + { name: 'greaterThanToken', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IUnaryExpressionSyntax' } + ], + isTypeScriptSpecific: true + }, + { + name: 'ObjectLiteralExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPrimaryExpressionSyntax'], + children: [ + { name: 'openBraceToken', isToken: true, excludeFromAST: true }, + { name: 'propertyAssignments', isSeparatedList: true, elementType: 'IPropertyAssignmentSyntax' }, + { name: 'closeBraceToken', isToken: true, excludeFromAST: true } + ] + }, + { + name: 'SimplePropertyAssignmentSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPropertyAssignmentSyntax'], + children: [ + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'colonToken', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IExpressionSyntax' } + ] + }, + { + name: 'FunctionPropertyAssignmentSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPropertyAssignmentSyntax'], + children: [ + { name: 'propertyName', isToken: true, tokenKinds: ['IdentifierName', 'StringLiteral', 'NumericLiteral'] }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax' } + ] + }, + { + name: 'FunctionExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IPrimaryExpressionSyntax'], + children: [ + { name: 'functionKeyword', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, isOptional: true, tokenKinds: ['IdentifierName'] }, + { name: 'callSignature', type: 'CallSignatureSyntax' }, + { name: 'block', type: 'BlockSyntax' }] + }, + { + name: 'EmptyStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'semicolonToken', isToken: true }] + }, + { + name: 'TryStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'tryKeyword', isToken: true, excludeFromAST: true }, + { name: 'block', type: 'BlockSyntax' }, + { name: 'catchClause', type: 'CatchClauseSyntax', isOptional: true }, + { name: 'finallyClause', type: 'FinallyClauseSyntax', isOptional: true }] + }, + { + name: 'CatchClauseSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'catchKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'typeAnnotation', type: 'TypeAnnotationSyntax', isOptional: true, isTypeScriptSpecified: true }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'block', type: 'BlockSyntax' }] + }, + { + name: 'FinallyClauseSyntax', + baseType: 'ISyntaxNode', + children: [ + { name: 'finallyKeyword', isToken: true, excludeFromAST: true }, + { name: 'block', type: 'BlockSyntax' }] + }, + { + name: 'LabeledStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'identifier', isToken: true, tokenKinds: ['IdentifierName'] }, + { name: 'colonToken', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' }] + }, + { + name: 'DoStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'doKeyword', isToken: true, excludeFromAST: true }, + { name: 'statement', type: 'IStatementSyntax' }, + { name: 'whileKeyword', isToken: true, excludeFromAST: true }, + { name: 'openParenToken', isToken: true, excludeFromAST: true }, + { name: 'condition', type: 'IExpressionSyntax' }, + { name: 'closeParenToken', isToken: true, excludeFromAST: true }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }] + }, + { + name: 'TypeOfExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'typeOfKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IUnaryExpressionSyntax' }] + }, + { + name: 'DeleteExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'deleteKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IUnaryExpressionSyntax' }] + }, + { + name: 'VoidExpressionSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IUnaryExpressionSyntax'], + children: [ + { name: 'voidKeyword', isToken: true, excludeFromAST: true }, + { name: 'expression', type: 'IUnaryExpressionSyntax' }] + }, + { + name: 'DebuggerStatementSyntax', + baseType: 'ISyntaxNode', + interfaces: ['IStatementSyntax'], + children: [ + { name: 'debuggerKeyword', isToken: true }, + { name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }] + }]; + +function firstKind(definition: ITypeDefinition): TypeScript.SyntaxKind { + var kindName = definition.syntaxKinds ? definition.syntaxKinds[0] : getNameWithoutSuffix(definition); + //TypeScript.Environment.standardOut.WriteLine(kindName); + var kind = (TypeScript.SyntaxKind)[kindName]; + //TypeScript.Environment.standardOut.WriteLine(kind); + + return kind; +} + +var sortedDefinitions = definitions.sort((d1, d2) => firstKind(d1) - firstKind(d2)); + +//function endsWith(string: string, value: string): boolean { +// return string.substring(string.length - value.length, string.length) === value; +//} + +function getStringWithoutSuffix(definition: string) { + if (TypeScript.StringUtilities.endsWith(definition, "Syntax")) { + return definition.substring(0, definition.length - "Syntax".length); + } + + return definition; +} + +function getStringWithoutPrefix(definition: string) { + if (definition.charAt(0) == "I" && definition.charAt(1).toUpperCase() == definition.charAt(1)) { + return definition.substring(1); + } + + return definition; +} + +function getNameWithoutSuffix(definition: ITypeDefinition) { + return getStringWithoutSuffix(definition.name); +} + +function getType(child: IMemberDefinition): string { + if (child.isToken) { + return "ISyntaxToken"; + } + else if (child.isSeparatedList) { + return child.elementType + "[]"; + } + else if (child.isList) { + return child.elementType + "[]"; + } + else { + return child.type; + } +} + +var hasKind = false; + +function pascalCase(value: string): string { + return value.substr(0, 1).toUpperCase() + value.substr(1); +} + +function camelCase(value: string): string { + return value.substr(0, 1).toLowerCase() + value.substr(1); +} + +function getSafeName(child: IMemberDefinition) { + if (child.name === "arguments") { + return "_" + child.name; + } + + return child.name; +} + +function getPropertyAccess(child: IMemberDefinition, instance = "this"): string { + if (child.type === "SyntaxKind") { + return instance + "._kind"; + } + + return instance + "." + child.name; +} + +function generateProperties(definition: ITypeDefinition): string { + var result = ""; + + if (definition.name === "SourceUnitSyntax") { + result += " public syntaxTree: SyntaxTree = null;\r\n"; + } + + var newLine = false; + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + + if (getType(child) === "SyntaxKind") { + result += " private _" + child.name + ": " + getType(child) + ";\r\n"; + newLine = true; + } + else if (child.name === "arguments") { + result += " public " + child.name + ": " + getType(child) + ";\r\n"; + } + + hasKind = hasKind || (getType(child) === "SyntaxKind"); + } + + if (newLine) { + result += "\r\n"; + } + + return result; +} + +function generateNullChecks(definition: ITypeDefinition): string { + var result = ""; + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + + if (!child.isOptional && !child.isToken) { + result += " if (" + child.name + " === null) { throw Errors.argumentNull('" + child.name + "'); }\r\n"; + } + } + + return result; +} + +function generateIfKindCheck(child: IMemberDefinition, tokenKinds: string[], indent: string): string { + var result = ""; + + result += indent + " if ("; + + for (var j = 0; j < tokenKinds.length; j++) { + if (j > 0) { + result += " && "; + } + + var tokenKind = tokenKinds[j]; + if (tokenKind === "IdentifierName") { + result += "!SyntaxFacts.isIdentifierName(" + child.name + ".tokenKind)"; + } + else { + result += child.name + ".tokenKind !== SyntaxKind." + tokenKind; + } + } + + result += ") { throw Errors.argument('" + child.name + "'); }\r\n"; + return result; +} + +function generateSwitchCase(tokenKind: string, indent: string): string { + return indent + " case SyntaxKind." + tokenKind + ":\r\n"; +} + +function generateBreakStatement(indent: string): string { + return indent + " break;\r\n"; +} + +function generateSwitchCases(tokenKinds: string[], indent: string): string { + var result = ""; + for (var j = 0; j < tokenKinds.length; j++) { + var tokenKind = tokenKinds[j]; + + result += generateSwitchCase(tokenKind, indent); + } + + if (tokenKinds.length > 0) { + result += generateBreakStatement(indent); + } + + return result; +} + +function generateDefaultCase(child: IMemberDefinition, indent: string): string { + var result = ""; + + result += indent + " default:\r\n"; + result += indent + " throw Errors.argument('" + child.name + "');\r\n"; + + return result; +} + +function generateSwitchKindCheck(child: IMemberDefinition, tokenKinds: string[], indent: string): string { + if (tokenKinds.length === 0) { + return ""; + } + + var result = ""; + + var identifierName = TypeScript.ArrayUtilities.where(tokenKinds, v => v.indexOf("IdentifierName") >= 0); + var notIdentifierName = TypeScript.ArrayUtilities.where(tokenKinds, v => v.indexOf("IdentifierName") < 0); + + if (identifierName.length > 0) { + result += indent + " if (!SyntaxFacts.isIdentifierName(" + child.name + ".tokenKind)) {\r\n"; + if (notIdentifierName.length === 0) { + result += indent + " throw Errors.argument('" + child.name + "');\r\n"; + result += indent + " }\r\n"; + return result; + } + + indent += " "; + } + + if (notIdentifierName.length <= 2) { + result += generateIfKindCheck(child, notIdentifierName, indent); + } + else if (notIdentifierName.length > 2) { + result += indent + " switch (" + child.name + ".tokenKind) {\r\n"; + result += generateSwitchCases(notIdentifierName, indent); + result += generateDefaultCase(child, indent); + result += indent + " }\r\n"; + } + + if (identifierName.length > 0) { + result += indent + " }\r\n"; + } + + // result += indent + " }\r\n"; + return result; +} + +function tokenKinds(child: IMemberDefinition): string[] { + return child.tokenKinds + ? child.tokenKinds + : [pascalCase(child.name)]; +} + +function generateKindCheck(child: IMemberDefinition): string { + var indent = ""; + var result = ""; + + if (child.isOptional) { + indent = " "; + + result += " if (" + child.name + " !== null) {\r\n"; + } + + var kinds = tokenKinds(child); + + if (kinds.length <= 2) { + result += generateIfKindCheck(child, kinds, indent); + } + else { + result += generateSwitchKindCheck(child, kinds, indent); + } + + if (child.isOptional) { + result += " }\r\n"; + } + + return result; +} + +function generateKindChecks(definition: ITypeDefinition): string { + var result = ""; + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + + if (child.isToken) { + result += generateKindCheck(child); + } + } + + return result; +} + +function generateArgumentChecks(definition: ITypeDefinition): string { + var result = ""; + + if (argumentChecks) { + result += generateNullChecks(definition); + result += generateKindChecks(definition); + + if (definition.children.length > 0) { + result += "\r\n"; + } + } + + return result; +} + +function generateConstructor(definition: ITypeDefinition): string { + var i: number; + var child: IMemberDefinition; + var base = baseType(definition); + + var result = ""; + result += " constructor(" + + var children = definition.children; + var kindChild: IMemberDefinition = null; + for (i = 0; i < children.length; i++) { + child = children[i]; + + if (getType(child) === "SyntaxKind") { + kindChild = child; + } + + if (getType(child) !== "SyntaxKind" && child.name !== "arguments") { + result += "public "; + } + + result += getSafeName(child) + ": " + getType(child); + result += ",\r\n "; + } + + result += "data: number) {\r\n"; + + if (kindChild) { + result += " super(kind, data); \r\n"; + } + else { + result += " super(SyntaxKind." + getNameWithoutSuffix(definition) + ", data); \r\n"; + } + + if (definition.children.length > 0) { + result += "\r\n"; + } + + result += generateArgumentChecks(definition); + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + if (child.type === "SyntaxKind" || child.name === "arguments") { + result += " " + getPropertyAccess(child) + " = " + getSafeName(child) + ";\r\n"; + } + } + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + if (child.type !== "SyntaxKind") { + if (child.isOptional) { + result += " " + getSafeName(child) + " && (" + getSafeName(child) + ".parent = this);\r\n"; + } + else if (child.isList || child.isSeparatedList) { + result += " !isShared(" + getSafeName(child) + ") && (" + getSafeName(child) + ".parent = this);\r\n"; + } + else { + result += " " + getSafeName(child) + ".parent = this;\r\n"; + } + } + } + + //result += " Syntax.setParentForChildren(this);\r\n"; + result += " }\r\n"; + + return result; +} + +function isOptional(child: IMemberDefinition) { + if (child.isOptional) { + return true; + } + + if (child.isList && !child.requiresAtLeastOneItem) { + return true; + } + + if (child.isSeparatedList && !child.requiresAtLeastOneItem) { + return true; + } + + return false; +} + +function generateFactory1Method(definition: ITypeDefinition): string { + return ""; + + var mandatoryChildren = TypeScript.ArrayUtilities.where( + definition.children, c => !isOptional(c)); + if (mandatoryChildren.length === definition.children.length) { + return ""; + } + + var result = "\r\n public static create(" + var i: number; + var child: IMemberDefinition; + + for (i = 0; i < mandatoryChildren.length; i++) { + child = mandatoryChildren[i]; + + result += child.name + ": " + getType(child); + + if (i < mandatoryChildren.length - 1) { + result += ",\r\n "; + } + } + + result += "): " + definition.name + " {\r\n"; + + result += " return new " + definition.name + "("; + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + if (!isOptional(child)) { + result += child.name; + } + else if (child.isList) { + result += "Syntax.emptyList<" + child.elementType + ">()"; + } + else if (child.isSeparatedList) { + result += "Syntax.emptySeparatedList<" + child.elementType + ">()"; + } + else { + result += "null"; + } + + result += ", "; + } + + result += "/*data:*/ 0);\r\n"; + result += " }\r\n"; + + return result; +} + +function isKeywordOrPunctuation(kind: string): boolean { + if (TypeScript.StringUtilities.endsWith(kind, "Keyword")) { + return true; + } + + if (TypeScript.StringUtilities.endsWith(kind, "Token") && + kind !== "IdentifierName" && + kind !== "EndOfFileToken") { + return true; + } + + return false; +} + +function isDefaultConstructable(definition: ITypeDefinition): boolean { + if (definition === null) { + return false; + } + + for (var i = 0; i < definition.children.length; i++) { + if (isMandatory(definition.children[i])) { + // If any child is mandatory, then the type is not default constructable. + return false; + } + } + + // We can default construct this. + return true; +} + +function isMandatory(child: IMemberDefinition): boolean { + // If it's optional then it's not mandatory. + if (isOptional(child)) { + return false; + } + + // Kinds are always mandatory. As are non-optional lists. + if (child.type === "SyntaxKind" || child.isList || child.isSeparatedList) { + return true; + } + + // We have a non optional node or token. Tokens are mandatory if they're not keywords or + // punctuation. + if (child.isToken) { + var kinds = tokenKinds(child); + var isFixed = kinds.length === 1 && isKeywordOrPunctuation(kinds[0]); + + return !isFixed; + } + + // It's a node. It's mandatory if we can't default construct it. + return !isDefaultConstructable(memberDefinitionType(child)); +} + +function generateFactory2Method(definition: ITypeDefinition): string { + return ""; + + var mandatoryChildren: IMemberDefinition[] = TypeScript.ArrayUtilities.where(definition.children, isMandatory); + if (mandatoryChildren.length === definition.children.length) { + return ""; + } + + var i: number; + var child: IMemberDefinition; + var result = "\r\n public static create1(" + + for (i = 0; i < mandatoryChildren.length; i++) { + child = mandatoryChildren[i]; + + result += child.name + ": " + getType(child); + + if (i < mandatoryChildren.length - 1) { + result += ",\r\n "; + } + } + + result += "): " + definition.name + " {\r\n"; + result += " return new " + definition.name + "("; + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + if (isMandatory(child)) { + result += child.name; + } + else if (child.isList) { + result += "Syntax.emptyList<" + child.elementType + ">()"; + } + else if (child.isSeparatedList) { + result += "Syntax.emptySeparatedList<" + child.elementType + ">()"; + } + else if (isOptional(child)) { + result += "null"; + } + else if (child.isToken) { + result += "Syntax.token(SyntaxKind." + tokenKinds(child)[0] + ")"; + } + else { + result += child.type + ".create1()"; + } + + result += ", "; + } + + result += "/*data:*/ 0);\r\n"; + result += " }\r\n"; + + return result; +} + +function generateFactoryMethod(definition: ITypeDefinition): string { + return generateFactory1Method(definition) + generateFactory2Method(definition); +} + +function generateBrands(definition: ITypeDefinition, accessibility: boolean): string { + var properties = ""; + + var types: string[] = []; + if (definition.interfaces) { + var ifaces = definition.interfaces.slice(0); + var i: number; + for (i = 0; i < ifaces.length; i++) { + var current = ifaces[i]; + + while (current !== undefined) { + if (!TypeScript.ArrayUtilities.contains(ifaces, current)) { + ifaces.push(current); + } + + current = interfaces[current]; + } + } + + for (i = 0; i < ifaces.length; i++) { + var type = ifaces[i]; + type = getStringWithoutSuffix(type); + if (isInterface(type)) { + type = "_" + type.substr(1, 1).toLowerCase() + type.substr(2) + "Brand"; + } + + types.push(type); + } + } + + if (types.length > 0) { + properties += " "; + + for (var i = 0; i < types.length; i++) { + if (accessibility) { + properties += " public "; + } + + properties += types[i] + ": any;"; + } + + properties += "\r\n"; + } + + return properties; +} + +function generateKindMethod(definition: ITypeDefinition): string { + var result = ""; + + //if (!hasKind) { + // result += "\r\n"; + // result += " public get kind(): SyntaxKind {\r\n"; + // result += " return SyntaxKind." + getNameWithoutSuffix(definition) + ";\r\n"; + // result += " }\r\n"; + //} + + return result; +} + +function generateSlotMethods(definition: ITypeDefinition): string { + var result = ""; + return result; + + result += "\r\n"; + result += " public childCount(): number {\r\n"; + var slotCount = hasKind ? (definition.children.length - 1) : definition.children.length; + + result += " return " + slotCount + ";\r\n"; + result += " }\r\n\r\n"; + + result += " public childAt(slot: number): ISyntaxElement {\r\n"; + + if (slotCount === 0) { + result += " throw Errors.invalidOperation();\r\n"; + } + else { + result += " switch (slot) {\r\n"; + + var index = 0; + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + if (child.type === "SyntaxKind") { + continue; + } + + result += " case " + index + ": return this." + child.name + ";\r\n"; + index++; + } + + result += " default: throw Errors.invalidOperation();\r\n"; + result += " }\r\n"; + } + + result += " }\r\n"; + + return result; +} + +function generateFirstTokenMethod(definition: ITypeDefinition): string { + var result = ""; + + result += "\r\n"; + result += " public firstToken(): ISyntaxToken {\r\n"; + result += " var token = null;\r\n"; + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + + if (getType(child) === "SyntaxKind") { + continue; + } + + if (child.name === "endOfFileToken") { + continue; + } + + result += " if ("; + + if (child.isOptional) { + result += getPropertyAccess(child) + " !== null && "; + } + + if (child.isToken) { + result += getPropertyAccess(child) + ".width() > 0"; + result += ") { return " + getPropertyAccess(child) + "; }\r\n"; + } + else { + result += "(token = " + getPropertyAccess(child) + ".firstToken()) !== null"; + result += ") { return token; }\r\n"; + } + } + + if (definition.name === "SourceUnitSyntax") { + result += " return this._endOfFileToken;\r\n"; + } + else { + result += " return null;\r\n"; + } + + result += " }\r\n"; + + result += " }\r\n"; + + return result; +} + +function generateLastTokenMethod(definition: ITypeDefinition): string { + var result = ""; + + result += "\r\n"; + result += " public lastToken(): ISyntaxToken {\r\n"; + + if (definition.name === "SourceUnitSyntax") { + result += " return this._endOfFileToken;\r\n"; + } + else { + result += " var token = null;\r\n"; + + for (var i = definition.children.length - 1; i >= 0; i--) { + var child = definition.children[i]; + + if (getType(child) === "SyntaxKind") { + continue; + } + + if (child.name === "endOfFileToken") { + continue; + } + + result += " if ("; + + if (child.isOptional) { + result += getPropertyAccess(child) + " !== null && "; + } + + if (child.isToken) { + result += getPropertyAccess(child) + ".width() > 0"; + result += ") { return " + getPropertyAccess(child) + "; }\r\n"; + } + else { + result += "(token = " + getPropertyAccess(child) + ".lastToken()) !== null"; + result += ") { return token; }\r\n"; + } + } + + result += " return null;\r\n"; + } + + result += " }\r\n"; + + return result; +} + +function baseType(definition: ITypeDefinition): ITypeDefinition { + return TypeScript.ArrayUtilities.firstOrDefault(definitions, d => d.name === definition.baseType); +} + +function memberDefinitionType(child: IMemberDefinition): ITypeDefinition { + // Debug.assert(child.type !== undefined); + return TypeScript.ArrayUtilities.firstOrDefault(definitions, d => d.name === child.type); +} + +function derivesFrom(def1: ITypeDefinition, def2: ITypeDefinition): boolean { + var current = def1; + while (current !== null) { + var base = baseType(current); + if (base === def2) { + return true; + } + + current = base; + } + + return false; +} + +function contains(definition: ITypeDefinition, child: IMemberDefinition) { + return TypeScript.ArrayUtilities.any(definition.children, + c => c.name === child.name && + c.isList === child.isList && + c.isSeparatedList === child.isSeparatedList && + c.isToken === child.isToken && + c.type === child.type); +} + +function generateAccessors(definition: ITypeDefinition): string { + var result = ""; + + //if (definition.name === "SourceUnitSyntax") { + // result += "\r\n"; + // result += " public syntaxTree(): SyntaxTree {\r\n"; + // result += " return this._syntaxTree;\r\n"; + // result += " }\r\n"; + //} + + //for (var i = 0; i < definition.children.length; i++) { + // var child = definition.children[i]; + + // if (child.type === "SyntaxKind") { + // result += "\r\n"; + // result += " public get " + child.name + "(): " + getType(child) + " {\r\n"; + // result += " return " + getPropertyAccess(child) + ";\r\n"; + // result += " }\r\n"; + // } + //} + + return result; +} + +function generateWithMethod(definition: ITypeDefinition, child: IMemberDefinition): string { + return ""; + + var result = ""; + result += "\r\n"; + result += " public with" + pascalCase(child.name) + "(" + getSafeName(child) + ": " + getType(child) + "): " + definition.name + " {\r\n"; + result += " return this.update(" + + for (var i = 0; i < definition.children.length; i++) { + if (i > 0) { + result += ", "; + } + + if (definition.children[i] === child) { + result += getSafeName(child); + } + else { + result += getPropertyAccess(definition.children[i]); + } + } + + result += ");\r\n"; + result += " }\r\n"; + + if (child.isList || child.isSeparatedList) { + if (TypeScript.StringUtilities.endsWith(child.name, "s")) { + var pascalName = pascalCase(child.name); + pascalName = pascalName.substring(0, pascalName.length - 1); + + var argName = getSafeName(child); + argName = argName.substring(0, argName.length - 1) + + result += "\r\n"; + result += " public with" + pascalName + "(" + argName + ": " + child.elementType + "): " + definition.name + " {\r\n"; + result += " return this.with" + pascalCase(child.name) + "(" + + if (child.isList) { + result += "Syntax.list<" + child.elementType + ">([" + argName + "])"; + } + else { + result += "Syntax.separatedList<" + child.elementType + ">([" + argName + "])"; + } + + result += ");\r\n"; + result += " }\r\n"; + } + } + + return result; +} + +function generateWithMethods(definition: ITypeDefinition): string { + var result = ""; + return ""; + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + result += generateWithMethod(definition, child); + } + + return result; +} + +function generateTriviaMethods(definition: ITypeDefinition): string { + return ""; + + var result = "\r\n"; + result += " public withLeadingTrivia(trivia: ISyntaxTriviaList): " + definition.name + " {\r\n"; + result += " return <" + definition.name + ">super.withLeadingTrivia(trivia);\r\n"; + result += " }\r\n\r\n"; + result += " public withTrailingTrivia(trivia: ISyntaxTriviaList): " + definition.name + " {\r\n"; + result += " return <" + definition.name + ">super.withTrailingTrivia(trivia);\r\n"; + result += " }\r\n"; + + return result; +} + +function generateUpdateMethod(definition: ITypeDefinition): string { + // return ""; + + var result = ""; + + result += "\r\n"; + result += " public update("; + + var i: number; + var child: IMemberDefinition; + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + result += getSafeName(child) + ": " + getType(child); + + if (i < definition.children.length - 1) { + result += ",\r\n "; + } + } + + result += "): " + definition.name + " {\r\n"; + + if (definition.children.length === 0) { + result += " return this;\r\n"; + } + else { + result += " if ("; + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + if (i !== 0) { + result += " && "; + } + + result += getPropertyAccess(child) + " === " + getSafeName(child); + } + + result += ") {\r\n"; + result += " return this;\r\n"; + result += " }\r\n\r\n"; + + result += " return new " + definition.name + "("; + + for (i = 0; i < definition.children.length; i++) { + child = definition.children[i]; + + result += getSafeName(child); + result += ", "; + } + + result += "this.parsedInStrictMode() ? SyntaxConstants.NodeParsedInStrictModeMask : 0);\r\n"; + } + + result += " }\r\n"; + + return result; +} + +function generateNode(definition: ITypeDefinition, abstract: boolean): string { + var result = " export class " + definition.name + " extends SyntaxNode" + + if (definition.interfaces) { + result += " implements "; + result += definition.interfaces.join(", "); + } + + result += " {\r\n"; + + if (definition.name === "SourceUnitSyntax") { + result += " public syntaxTree: SyntaxTree = null;\r\n"; + } + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + result += " public " + child.name + ": " + getType(child) + ";\r\n"; + } + + result += generateBrands(definition, /*accessibility:*/ true); + + result += " constructor(data: number"; + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + result += ", " + getSafeName(child) + ": " + getType(child); + } + + result += ") {\r\n"; + result += " super(data);\r\n"; + + if (definition.name === "SourceUnitSyntax") { + result += " this.parent = null,\r\n"; + } + + if (definition.children) { + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + if (child.excludeFromAST && abstract) { + continue; + } + + result += " this." + child.name + " = " + getSafeName(child) + ",\r\n"; + } + } + + if (definition.children.length > 0) { + var first = true; + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + if (child.excludeFromAST && abstract) { + continue; + } + + if (!first) { + result += ",\r\n"; + } + first = false; + + if (child.isList || child.isSeparatedList) { + result += " !isShared(" + getSafeName(child) + ") && (" + getSafeName(child) + ".parent = this)"; + } + else if (child.isOptional) { + result += " " + getSafeName(child) + " && (" + getSafeName(child) + ".parent = this)"; + } + else { + result += " " + getSafeName(child) + ".parent = this"; + } + } + result += ";\r\n"; + } + + result += " }\r\n"; + + if (definition.name === "BinaryExpressionSyntax") { + result += " public kind(): SyntaxKind { return SyntaxFacts.getBinaryExpressionFromOperatorToken(this.operatorToken.kind()); }\r\n"; + } + else if (definition.name === "PrefixUnaryExpressionSyntax") { + result += " public kind(): SyntaxKind { return SyntaxFacts.getPrefixUnaryExpressionFromOperatorToken(this.operatorToken.kind()); }\r\n"; + } + else if (definition.name === "PostfixUnaryExpressionSyntax") { + result += " public kind(): SyntaxKind { return SyntaxFacts.getPostfixUnaryExpressionFromOperatorToken(this.operatorToken.kind()); }\r\n"; + } + else if (definition.name === "HeritageClauseSyntax") { + result += " public kind(): SyntaxKind { return this.extendsOrImplementsKeyword.kind() === SyntaxKind.ExtendsKeyword ? SyntaxKind.ExtendsHeritageClause : SyntaxKind.ImplementsHeritageClause; }\r\n"; + } + + result += " }"; + return result; +} + +function syntaxKindName(kind: TypeScript.SyntaxKind): string { + for (var name in TypeScript.SyntaxKind) { + if (TypeScript.SyntaxKind[name] === kind) { + return name; + } + } + + throw new Error(); +} + +function getDefinitionForKind(kind: TypeScript.SyntaxKind): ITypeDefinition { + var kindName = syntaxKindName(kind); + + return TypeScript.ArrayUtilities.firstOrDefault(definitions, d => { + if (getNameWithoutSuffix(d) === kindName) { + return true; + } + + if (d.syntaxKinds) { + return TypeScript.ArrayUtilities.contains(d.syntaxKinds, kindName); + } + + return false; + }); +} + +function generateSyntaxInterfaces(): string { + var result = "///\r\n\r\n"; + + result += "module TypeScript {\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + if (i > 0) { + result += "\r\n"; + } + + result += generateSyntaxInterface(definition); + } + + result += "\r\n\r\n"; + + result += " export var nodeMetadata: string[][] = ["; + for (var i = 0; i <= TypeScript.SyntaxKind.LastNode; i++) { + if (i < TypeScript.SyntaxKind.FirstNode) { + result += "[],"; + continue; + } + + var kindName = syntaxKindName(i); + + var definition = getDefinitionForKind(i); + + var metadata = "["; + var children = definition.children.filter(m => m.type !== "SyntaxKind").map(m => '"' + m.name + '"'); + metadata += children.join(","); + metadata += "],"; + + result += metadata; + } + result += "];\r\n\r\n"; + + result += " export module Syntax {\r\n" + + result += " export interface ISyntaxFactory {\r\n"; + result += " isConcrete: boolean;\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + result += " " + definition.name + ": { new(data: number"; + + for (var j = 0; j < definition.children.length; j++) { + var child = definition.children[j]; + result += ", " + child.name + ": " + getType(child); + } + + result += "): " + definition.name + " };\r\n"; + } + + result += " }\r\n"; + result += " }\r\n"; + + result += "}"; + return result; +} + +function generateSyntaxInterface(definition: ITypeDefinition): string { + var result = " export interface " + definition.name + " extends ISyntaxNode" + + if (definition.interfaces) { + result += ", "; + result += definition.interfaces.join(", "); + } + + result += " {\r\n"; + + if (definition.name === "SourceUnitSyntax") { + result += " syntaxTree: SyntaxTree;\r\n"; + } + + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + result += " " + child.name + ": " + getType(child) + ";\r\n"; + } + + result += " }"; + + return result; +} + + +function generateNodes(abstract: boolean): string { + var result = "///\r\n\r\n"; + + result += "module TypeScript.Syntax."; + + var moduleName = abstract ? "Abstract" : "Concrete"; + result += moduleName; + + result += " {\r\n"; + result += " // Inject this module as the factory for producing syntax nodes in the parser.\r\n"; + result += " Parser.syntaxFactory = " + moduleName + ";\r\n"; + result += " export var isConcrete: boolean = " + !abstract + ";\r\n\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + if (i > 0) { + result += "\r\n"; + } + + result += generateNode(definition, abstract); + } + + result += "\r\n\r\n "; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + if (definition.syntaxKinds) { + continue; + } + + if (i) { + result += ", " + } + + result += "(" + definition.name + ").prototype.__kind = SyntaxKind." + getNameWithoutSuffix(definition) + } + + result += ";\r\n"; + + result += "}"; + return result; +} + +function isInterface(name: string) { + return name.substr(0, 1) === "I" && name.substr(1, 1).toUpperCase() === name.substr(1, 1) +} + +function isNodeOrToken(child: IMemberDefinition) { + // IWhatever. + return child.type && isInterface(child.type); +} + +function generateRewriter(): string { + var result = "///\r\n\r\n"; + + result += "module TypeScript {\r\n" + +" export class SyntaxRewriter implements ISyntaxVisitor {\r\n" + +" public visitToken(token: ISyntaxToken): ISyntaxToken {\r\n" + +" return token;\r\n" + +" }\r\n" + +"\r\n" + +" public visitNode(node: ISyntaxNode): ISyntaxNode {\r\n" + +" return visitNodeOrToken(this, node);\r\n" + +" }\r\n" + +"\r\n" + +" public visitNodeOrToken(node: ISyntaxNodeOrToken): ISyntaxNodeOrToken {\r\n" + +" return isToken(node) ? this.visitToken(node) : this.visitNode(node);\r\n" + +" }\r\n" + +"\r\n" + +" public visitList(list: T[]): T[] {\r\n" + +" var newItems: T[] = null;\r\n" + +"\r\n" + +" for (var i = 0, n = list.length; i < n; i++) {\r\n" + +" var item = list[i];\r\n" + +" var newItem = this.visitNodeOrToken(item);\r\n" + +"\r\n" + +" if (item !== newItem && newItems === null) {\r\n" + +" newItems = [];\r\n" + +" for (var j = 0; j < i; j++) {\r\n" + +" newItems.push(list[j]);\r\n" + +" }\r\n" + +" }\r\n" + +"\r\n" + +" if (newItems) {\r\n" + +" newItems.push(newItem);\r\n" + +" }\r\n" + +" }\r\n" + +"\r\n" + +" // Debug.assert(newItems === null || newItems.length === childCount(list));\r\n" + +" return newItems === null ? list : Syntax.list(newItems);\r\n" + +" }\r\n" + +"\r\n" + +" public visitSeparatedList(list: T[]): T[] {\r\n" + +" var newItems: ISyntaxNodeOrToken[] = null;\r\n" + +"\r\n" + +" for (var i = 0, n = childCount(list); i < n; i++) {\r\n" + +" var item = childAt(list, i);\r\n" + +" var newItem = isToken(item) ? this.visitToken(item) : this.visitNode(item);\r\n" + +"\r\n" + +" if (item !== newItem && newItems === null) {\r\n" + +" newItems = [];\r\n" + +" for (var j = 0; j < i; j++) {\r\n" + +" newItems.push(childAt(list, j));\r\n" + +" }\r\n" + +" }\r\n" + +"\r\n" + +" if (newItems) {\r\n" + +" newItems.push(newItem);\r\n" + +" }\r\n" + +" }\r\n" + +"\r\n" + +" // Debug.assert(newItems === null || newItems.length === childCount(list));\r\n" + +" return newItems === null ? list : Syntax.separatedList(newItems);\r\n" + +" }\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + result += "\r\n"; + result += " public visit" + getNameWithoutSuffix(definition) + "(node: " + definition.name + "): any {\r\n"; + + if (definition.children.length === 0) { + result += " return node;\r\n" + result += " }\r\n"; + continue; + } + + //if (definition.children.length === 1) { + // result += " return node.with" + pascalCase(definition.children[0].name) + "(\r\n"; + //} + //else { + result += " return node.update(\r\n"; + //} + + for (var j = 0; j < definition.children.length; j++) { + var child = definition.children[j]; + + result += " "; + if (child.isOptional) { + result += "node." + child.name + " === null ? null : "; + } + + if (child.isToken) { + result += "this.visitToken(node." + child.name + ")"; + } + else if (child.isList) { + result += "this.visitList(node." + child.name + ")"; + } + else if (child.isSeparatedList) { + result += "this.visitSeparatedList(node." + child.name + ")"; + } + else if (child.type === "SyntaxKind") { + result += "node.kind"; + } + else if (isNodeOrToken(child)) { + result += "<" + child.type + ">this.visitNodeOrToken(node." + child.name + ")"; + } + else { + result += "<" + child.type + ">this.visitNode(node." + child.name + ")"; + } + + if (j < definition.children.length - 1) { + result += ",\r\n"; + } + } + + result += ");\r\n"; + result += " }\r\n"; + } + + result += " }"; + result += "\r\n}"; + return result; +} + +function generateWalker(): string { + var result = ""; + + result += +"///\r\n"+ +"\r\n" + +"module TypeScript {\r\n" + +" export class SyntaxWalker implements ISyntaxVisitor {\r\n" + +" public visitToken(token: ISyntaxToken): void {\r\n" + +" }\r\n" + +"\r\n" + +" public visitNode(node: ISyntaxNode): void {\r\n" + +" visitNodeOrToken(this, node);\r\n" + +" }\r\n" + +"\r\n" + +" public visitNodeOrToken(nodeOrToken: ISyntaxNodeOrToken): void {\r\n" + +" if (isToken(nodeOrToken)) { \r\n" + +" this.visitToken(nodeOrToken);\r\n" + +" }\r\n" + +" else {\r\n" + +" this.visitNode(nodeOrToken);\r\n" + +" }\r\n" + +" }\r\n" + +"\r\n" + +" private visitOptionalToken(token: ISyntaxToken): void {\r\n" + +" if (token === null) {\r\n" + +" return;\r\n" + +" }\r\n" + +"\r\n" + +" this.visitToken(token);\r\n" + +" }\r\n" + +"\r\n" + +" public visitOptionalNode(node: ISyntaxNode): void {\r\n" + +" if (node === null) {\r\n" + +" return;\r\n" + +" }\r\n" + +"\r\n" + +" this.visitNode(node);\r\n" + +" }\r\n" + +"\r\n" + +" public visitOptionalNodeOrToken(nodeOrToken: ISyntaxNodeOrToken): void {\r\n" + +" if (nodeOrToken === null) {\r\n" + +" return;\r\n" + +" }\r\n" + +"\r\n" + +" this.visitNodeOrToken(nodeOrToken);\r\n" + +" }\r\n" + +"\r\n" + +" public visitList(list: ISyntaxNodeOrToken[]): void {\r\n" + +" for (var i = 0, n = list.length; i < n; i++) {\r\n" + +" this.visitNodeOrToken(list[i]);\r\n" + +" }\r\n" + +" }\r\n" + +"\r\n" + +" public visitSeparatedList(list: ISyntaxNodeOrToken[]): void {\r\n" + +" for (var i = 0, n = childCount(list); i < n; i++) {\r\n" + +" var item = childAt(list, i);\r\n" + +" this.visitNodeOrToken(item);\r\n" + +" }\r\n" + +" }\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + result += "\r\n"; + result += " public visit" + getNameWithoutSuffix(definition) + "(node: " + definition.name + "): void {\r\n"; + + for (var j = 0; j < definition.children.length; j++) { + var child = definition.children[j]; + + if (child.isToken) { + if (child.isOptional) { + result += " this.visitOptionalToken(node." + child.name + ");\r\n"; + } + else { + result += " this.visitToken(node." + child.name + ");\r\n"; + } + } + else if (child.isList) { + result += " this.visitList(node." + child.name + ");\r\n"; + } + else if (child.isSeparatedList) { + result += " this.visitSeparatedList(node." + child.name + ");\r\n"; + } + else if (isNodeOrToken(child)) { + if (child.isOptional) { + result += " this.visitOptionalNodeOrToken(node." + child.name + ");\r\n"; + } + else { + result += " this.visitNodeOrToken(node." + child.name + ");\r\n"; + } + } + else if (child.type !== "SyntaxKind") { + if (child.isOptional) { + result += " this.visitOptionalNode(node." + child.name + ");\r\n"; + } + else { + result += " this.visitNode(node." + child.name + ");\r\n"; + } + } + } + + result += " }\r\n"; + } + + result += " }"; + result += "\r\n}"; + return result; +} + +function firstEnumName(e: any, value: number) { + for (var name in e) { + if (e[name] === value) { + return name; + } + } +} + +function groupBy(array: T[], func: (v: T) => string): any { + var result: TypeScript.IIndexable = {}; + + for (var i = 0, n = array.length; i < n; i++) { + var v: any = array[i]; + var k = func(v); + + var list: T[] = result[k] || []; + list.push(v); + result[k] = list; + } + + return result; +} + +function generateKeywordCondition(keywords: { text: string; kind: TypeScript.SyntaxKind; }[], currentCharacter: number, indent: string): string { + var length = keywords[0].text.length; + + var result = ""; + var index: string; + + if (keywords.length === 1) { + var keyword = keywords[0]; + + if (currentCharacter === length) { + return " return SyntaxKind." + firstEnumName(TypeScript.SyntaxKind, keyword.kind) + ";\r\n"; + } + + var keywordText = keywords[0].text; + result = " return (" + + for (var i = currentCharacter; i < length; i++) { + if (i > currentCharacter) { + result += " && "; + } + + index = i === 0 ? "start" : ("start + " + i); + result += "str.charCodeAt(" + index + ") === CharacterCodes." + keywordText.substr(i, 1); + } + + result += ") ? SyntaxKind." + firstEnumName(TypeScript.SyntaxKind, keyword.kind) + " : SyntaxKind.IdentifierName;\r\n"; + } + else { + result += " // " + TypeScript.ArrayUtilities.select(keywords, k => k.text).join(", ") + "\r\n" + // result += "\r\n"; + index = currentCharacter === 0 ? "start" : ("start + " + currentCharacter); + result += indent + "switch(str.charCodeAt(" + index + ")) {\r\n" + + var groupedKeywords = groupBy(keywords, k => k.text.substr(currentCharacter, 1)); + + for (var c in groupedKeywords) { + if (groupedKeywords.hasOwnProperty(c)) { + result += indent + " case CharacterCodes." + c + ":"; + result += generateKeywordCondition(groupedKeywords[c], currentCharacter + 1, indent + " "); + } + } + + result += indent + " default: return SyntaxKind.IdentifierName;\r\n"; + result += indent + "}\r\n"; + } + + return result; +} + +function min(array: T[], func: (v: T) => number): number { + // Debug.assert(array.length > 0); + var min = func(array[0]); + + for (var i = 1; i < array.length; i++) { + var next = func(array[i]); + if (next < min) { + min = next; + } + } + + return min; +} + +function max(array: T[], func: (v: T) => number): number { + // Debug.assert(array.length > 0); + var max = func(array[0]); + + for (var i = 1; i < array.length; i++) { + var next = func(array[i]); + if (next > max) { + max = next; + } + } + + return max; +} + +function generateScannerUtilities(): string { + var result = "///\r\n" + + "\r\n" + + "module TypeScript {\r\n" + + " export class ScannerUtilities {\r\n"; + + var i: number; + var keywords: { text: string; kind: TypeScript.SyntaxKind; }[] = []; + + for (i = TypeScript.SyntaxKind.FirstKeyword; i <= TypeScript.SyntaxKind.LastKeyword; i++) { + keywords.push({ kind: i, text: TypeScript.SyntaxFacts.getText(i) }); + } + + keywords.sort((a, b) => a.text.localeCompare(b.text)); + + result += " public static identifierKind(str: string, start: number, length: number): SyntaxKind {\r\n"; + + var minTokenLength = min(keywords, k => k.text.length); + var maxTokenLength = max(keywords, k => k.text.length); + result += " switch (length) {\r\n"; + + + for (i = minTokenLength; i <= maxTokenLength; i++) { + var keywordsOfLengthI = TypeScript.ArrayUtilities.where(keywords, k => k.text.length === i); + if (keywordsOfLengthI.length > 0) { + result += " case " + i + ":"; + result += generateKeywordCondition(keywordsOfLengthI, 0, " "); + } + } + + result += " default: return SyntaxKind.IdentifierName;\r\n"; + result += " }\r\n"; + result += " }\r\n"; + + result += " }\r\n"; + result += "}"; + + return result; +} + +function generateVisitor(): string { + var result = ""; + + result += "///\r\n\r\n"; + + result += "module TypeScript {\r\n"; + result += " export function visitNodeOrToken(visitor: ISyntaxVisitor, element: ISyntaxNodeOrToken): any {\r\n"; + result += " if (element === null) { return null; }\r\n"; + result += " if (isToken(element)) { return visitor.visitToken(element); }\r\n"; + result += " switch (element.kind()) {\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + if (definition.syntaxKinds) { + result += " "; + for (var j = 0; j < definition.syntaxKinds.length; j++) { + result += " case SyntaxKind." + definition.syntaxKinds[j] + ":" + } + result += "\r\n "; + } + else { + result += " case SyntaxKind." + getNameWithoutSuffix(definition) + ": "; + } + + result += "return visitor.visit" + getNameWithoutSuffix(definition) + "(<" + definition.name + ">element);\r\n"; + } + + result += " }\r\n\r\n"; + result += " throw Errors.invalidOperation();\r\n"; + result += " }\r\n\r\n"; + + result += " export interface ISyntaxVisitor {\r\n"; + result += " visitToken(token: ISyntaxToken): any;\r\n"; + + for (i = 0; i < definitions.length; i++) { + definition = definitions[i]; + result += " visit" + getNameWithoutSuffix(definition) + "(node: " + definition.name + "): any;\r\n"; + } + + result += " }"; + + result += "\r\n}"; + + return result; +} + +function generateDefaultVisitor(): string { + var result = ""; + + result += "///\r\n\r\n"; + + result += "module TypeScript {\r\n"; + if (!forPrettyPrinter) { + result += " export class SyntaxVisitor implements ISyntaxVisitor {\r\n"; + result += " public defaultVisit(node: ISyntaxNodeOrToken): any {\r\n"; + result += " return null;\r\n"; + result += " }\r\n"; + result += "\r\n"; + result += " public visitToken(token: ISyntaxToken): any {\r\n"; + result += " return this.defaultVisit(token);\r\n"; + result += " }\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + + result += "\r\n public visit" + getNameWithoutSuffix(definition) + "(node: " + definition.name + "): any {\r\n"; + result += " return this.defaultVisit(node);\r\n"; + result += " }\r\n"; + } + + result += " }"; + } + + result += "\r\n}"; + + return result; +} + +function generateFactory(): string { + var result = "///\r\n"; + + result += "\r\nmodule TypeScript.Syntax {\r\n"; + result += " export interface IFactory {\r\n"; + + var i: number; + var j: number; + var definition: ITypeDefinition; + var child: IMemberDefinition; + + for (i = 0; i < definitions.length; i++) { + definition = definitions[i]; + result += " " + camelCase(getNameWithoutSuffix(definition)) + "("; + + for (j = 0; j < definition.children.length; j++) { + if (j > 0) { + result += ", "; + } + + child = definition.children[j]; + result += child.name + ": " + getType(child); + } + + result += "): " + definition.name + ";\r\n"; + } + + result += " }\r\n\r\n"; + + // TODO: stop exporting these once compiler bugs are fixed. + result += " export class NormalModeFactory implements IFactory {\r\n"; + + for (i = 0; i < definitions.length; i++) { + definition = definitions[i]; + result += " " + camelCase(getNameWithoutSuffix(definition)) + "("; + + for (j = 0; j < definition.children.length; j++) { + if (j > 0) { + result += ", "; + } + + child = definition.children[j]; + result += getSafeName(child) + ": " + getType(child); + } + + result += "): " + definition.name + " {\r\n"; + result += " return new " + definition.name + "("; + + for (j = 0; j < definition.children.length; j++) { + child = definition.children[j]; + result += getSafeName(child); + result += ", "; + } + + result += "/*data:*/ 0);\r\n"; + result += " }\r\n" + } + + result += " }\r\n\r\n"; + + // TODO: stop exporting these once compiler bugs are fixed. + result += " export class StrictModeFactory implements IFactory {\r\n"; + + for (i = 0; i < definitions.length; i++) { + definition = definitions[i]; + result += " " + camelCase(getNameWithoutSuffix(definition)) + "("; + + for (j = 0; j < definition.children.length; j++) { + if (j > 0) { + result += ", "; + } + + child = definition.children[j]; + result += getSafeName(child) + ": " + getType(child); + } + + result += "): " + definition.name + " {\r\n"; + result += " return new " + definition.name + "("; + + for (j = 0; j < definition.children.length; j++) { + child = definition.children[j]; + result += getSafeName(child); + result += ", "; + } + + result += "/*data:*/ SyntaxConstants.NodeParsedInStrictModeMask);\r\n"; + + result += " }\r\n" + } + + result += " }\r\n\r\n"; + + result += " export var normalModeFactory: IFactory = new NormalModeFactory();\r\n"; + result += " export var strictModeFactory: IFactory = new StrictModeFactory();\r\n"; + result += "}"; + + return result; +} + +function generateServicesUtilities(): string { + var result = ""; // "/// \r\n\r\n"; + + result += generateIsTypeScriptSpecific(); + + return result; +} + +function generateIsTypeScriptSpecific(): string { + var result = ""; + + result += "module TypeScript {\r\n"; + + result += " function isSeparatedListTypeScriptSpecific(list: ISyntaxNodeOrToken[]): boolean {\r\n" + result += " for (var i = 0, n = childCount(list); i < n; i++) {\r\n"; + result += " if (isTypeScriptSpecific(childAt(list, i))) {\r\n"; + result += " return true;\r\n"; + result += " }\r\n"; + result += " }\r\n\r\n"; + result += " return false;\r\n"; + result += " }\r\n\r\n"; + + result += " function isListTypeScriptSpecific(list: ISyntaxNodeOrToken[]): boolean {\r\n" + result += " for (var i = 0, n = list.length; i < n; i++) {\r\n"; + result += " if (isTypeScriptSpecific(list[i])) {\r\n"; + result += " return true;\r\n"; + result += " }\r\n"; + result += " }\r\n\r\n"; + result += " return false;\r\n"; + result += " }\r\n\r\n"; + + result += " export function isTypeScriptSpecific(element: ISyntaxElement): boolean {\r\n" + result += " if (element === null) { return false; }\r\n"; + result += " if (isToken(element)) { return false; }\r\n"; + result += " if (isList(element)) { return isListTypeScriptSpecific(element); }\r\n"; + result += " if (isSeparatedList(element)) { return isSeparatedListTypeScriptSpecific(element); }\r\n\r\n"; + result += " switch (element.kind()) {\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + if (!definition.isTypeScriptSpecific) { + continue; + } + + if (definition.syntaxKinds) { + for (var j = 0; j < definition.syntaxKinds.length; j++) { + result += " case SyntaxKind." + definition.syntaxKinds[j] + ":\r\n"; + } + } + else { + result += " case SyntaxKind." + getNameWithoutSuffix(definition) + ":\r\n"; + } + } + + result += " return true;\r\n"; + + var triviallyFalseDefinitions = definitions.filter(d => d.children.filter(c => c.type !== "SyntaxKind" && !c.isToken).length === 0); + for (var i = 0; i < triviallyFalseDefinitions.length; i++) { + var definition = triviallyFalseDefinitions[i]; + if (definition.isTypeScriptSpecific) { + continue; + } + + if (definition.syntaxKinds) { + for (var j = 0; j < definition.syntaxKinds.length; j++) { + result += " case SyntaxKind." + definition.syntaxKinds[j] + ":\r\n"; + } + } + else { + result += " case SyntaxKind." + getNameWithoutSuffix(definition) + ":\r\n"; + } + } + + result += " return false;\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + if (definition.isTypeScriptSpecific) { + continue; + } + + if (definition.children.filter(c => c.type !== "SyntaxKind" && !c.isToken).length === 0) { + continue; + } + + if (definition.syntaxKinds) { + result += " "; + for (var j = 0; j < definition.syntaxKinds.length; j++) { + result += " case SyntaxKind." + definition.syntaxKinds[j] + ":"; + } + } + else { + result += " case SyntaxKind." + getNameWithoutSuffix(definition) + ":"; + } + result += "\r\n"; + result += " return is" + getNameWithoutSuffix(definition) + "TypeScriptSpecific(<" + definition.name + ">element);\r\n"; + } + + result += " }\r\n"; + result += " }\r\n"; + + for (var i = 0; i < definitions.length; i++) { + var definition = definitions[i]; + if (definition.isTypeScriptSpecific) { + continue; + } + + var importantChildren = definition.children.filter(d => d.type !== "SyntaxKind" && !d.isToken); + if (importantChildren.length > 0) { + result += generateIsTypeScriptSpecificMethod(definition); + } + } + + result += "}"; + + return result; +} + +function generateIsTypeScriptSpecificMethod(definition: ITypeDefinition): string { + var result = "\r\n function is" + getNameWithoutSuffix(definition) + "TypeScriptSpecific(node: " + definition.name + "): boolean {\r\n"; + + result += " return "; + + var addedCheck = false; + for (var i = 0; i < definition.children.length; i++) { + var child = definition.children[i]; + + if (child.type === "SyntaxKind") { + continue; + } + + if (child.isToken) { + continue; + } + + if (addedCheck) { + result += " ||\r\n "; + } + + addedCheck = true; + + if (child.isTypeScriptSpecific) { + if (child.isList) { + result += getPropertyAccess(child, "node") + ".length > 0"; + } + else if (child.isSeparatedList) { + result += getPropertyAccess(child, "node") + ".childCount() > 0"; + } + else { + result += getPropertyAccess(child, "node") + " !== null"; + } + } + else { + result += "isTypeScriptSpecific(" + getPropertyAccess(child, "node") + ")"; + } + } + + if (!addedCheck) { + result += "false"; + } + + result += ";\r\n"; + result += " }\r\n"; + + return result; +} + +var syntaxNodesConcrete = generateNodes(/*abstract:*/ false); +var syntaxNodesAbstract = generateNodes(/*abstract:*/ true); +var syntaxInterfaces = generateSyntaxInterfaces(); +var rewriter = generateRewriter(); +var walker = generateWalker(); +var scannerUtilities = generateScannerUtilities(); +var visitor = generateVisitor(); +var defaultVisitor = generateDefaultVisitor(); +var servicesUtilities = generateServicesUtilities(); + +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\syntaxNodes.concrete.generated.ts", syntaxNodesConcrete, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\syntaxNodes.abstract.generated.ts", syntaxNodesAbstract, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\syntaxNodes.interfaces.generated.ts", syntaxInterfaces, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\services\\syntaxRewriter.generated.ts", rewriter, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\syntaxWalker.generated.ts", walker, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\scannerUtilities.generated.ts", scannerUtilities, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\syntaxVisitor.generated.ts", visitor, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\compiler\\syntax\\defaultSyntaxVisitor.generated.ts", defaultVisitor, false); +TypeScript.Environment.writeFile(TypeScript.Environment.currentDirectory() + "\\src\\services\\syntaxUtilities.generated.ts", servicesUtilities, false); diff --git a/src/services/syntax/syntaxIndenter.ts b/src/services/syntax/syntaxIndenter.ts new file mode 100644 index 00000000000..17b79356854 --- /dev/null +++ b/src/services/syntax/syntaxIndenter.ts @@ -0,0 +1,164 @@ +/// + +module TypeScript { + export class SyntaxIndenter extends SyntaxRewriter { + private lastTriviaWasNewLine: boolean; + private indentationTrivia: ISyntaxTrivia; + + constructor(indentFirstToken: boolean, + private indentationAmount: number, + private options: FormattingOptions) { + super(); + this.lastTriviaWasNewLine = indentFirstToken; + this.indentationTrivia = Indentation.indentationTrivia(this.indentationAmount, this.options); + } + + public visitToken(token: ISyntaxToken): ISyntaxToken { + if (token.width() === 0) { + return token; + } + + var result = token; + if (this.lastTriviaWasNewLine) { + // have to add our indentation to every line that this token hits. + result = token.withLeadingTrivia(this.indentTriviaList(token.leadingTrivia())); + } + + this.lastTriviaWasNewLine = token.hasTrailingNewLine(); + return result; + } + + public indentTriviaList(triviaList: ISyntaxTriviaList): ISyntaxTriviaList { + var result: ISyntaxTrivia[] = []; + + // First, update any existing trivia with the indent amount. For example, combine the + // indent with any whitespace trivia, or prepend any comments with the trivia. + var indentNextTrivia = true; + for (var i = 0, n = triviaList.count(); i < n; i++) { + var trivia = triviaList.syntaxTriviaAt(i); + + var indentThisTrivia = indentNextTrivia; + indentNextTrivia = false; + + switch (trivia.kind()) { + case SyntaxKind.MultiLineCommentTrivia: + this.indentMultiLineComment(trivia, indentThisTrivia, result); + continue; + + case SyntaxKind.SingleLineCommentTrivia: + case SyntaxKind.SkippedTokenTrivia: + this.indentSingleLineOrSkippedText(trivia, indentThisTrivia, result); + continue; + + case SyntaxKind.WhitespaceTrivia: + this.indentWhitespace(trivia, indentThisTrivia, result); + continue; + + case SyntaxKind.NewLineTrivia: + // We hit a newline processing the trivia. We need to add the indentation to the + // next line as well. Note: don't bother indenting the newline itself. This will + // just insert ugly whitespace that most users probably will not want. + result.push(trivia); + indentNextTrivia = true; + continue; + + default: + throw Errors.invalidOperation(); + } + } + + // Then, if the last trivia was a newline (or there was no trivia at all), then just add the + // indentation in right before the token. + if (indentNextTrivia) { + result.push(this.indentationTrivia); + } + + return Syntax.triviaList(result); + } + + private indentSegment(segment: string): string { + // Find the position of the first non whitespace character in the segment. + var firstNonWhitespacePosition = Indentation.firstNonWhitespacePosition(segment); + + if (firstNonWhitespacePosition < segment.length && + CharacterInfo.isLineTerminator(segment.charCodeAt(firstNonWhitespacePosition))) { + + // If this segment was just a newline, then don't bother indenting it. That will just + // leave the user with an ugly indent in their output that they probably do not want. + return segment; + } + + // Convert that position to a column. + var firstNonWhitespaceColumn = Indentation.columnForPositionInString(segment, firstNonWhitespacePosition, this.options); + + // Find the new column we want the nonwhitespace text to start at. + var newFirstNonWhitespaceColumn = firstNonWhitespaceColumn + this.indentationAmount; + + // Compute an indentation string for that. + var indentationString = Indentation.indentationString(newFirstNonWhitespaceColumn, this.options); + + // Join the new indentation and the original string without its indentation. + return indentationString + segment.substring(firstNonWhitespacePosition); + } + + private indentWhitespace(trivia: ISyntaxTrivia, indentThisTrivia: boolean, result: ISyntaxTrivia[]): void { + if (!indentThisTrivia) { + // Line didn't start with this trivia. So no need to touch it. Just add to the result + // and continue on. + result.push(trivia); + return; + } + + // Line started with this trivia. We want to figure out what the final column this + // whitespace goes to will be. To do that we add the column it is at now to the column we + // want to indent to. We then compute the final tabs+whitespace string for that. + var newIndentation = this.indentSegment(trivia.fullText()); + result.push(Syntax.whitespace(newIndentation)); + } + + private indentSingleLineOrSkippedText(trivia: ISyntaxTrivia, indentThisTrivia: boolean, result: ISyntaxTrivia[]): void { + if (indentThisTrivia) { + // The line started with a comment or skipped text. Add an indentation based + // on the desired settings, and then add the trivia itself. + result.push(this.indentationTrivia); + } + + result.push(trivia); + } + + private indentMultiLineComment(trivia: ISyntaxTrivia, indentThisTrivia: boolean, result: ISyntaxTrivia[]): void { + if (indentThisTrivia) { + // The line started with a multiline comment. Add an indentation based + // on the desired settings, and then add the trivia itself. + result.push(this.indentationTrivia); + } + + // If the multiline comment spans multiple lines, we need to add the right indent amount to + // each successive line segment as well. + var segments = Syntax.splitMultiLineCommentTriviaIntoMultipleLines(trivia); + + for (var i = 1; i < segments.length; i++) { + segments[i] = this.indentSegment(segments[i]); + } + + var newText = segments.join(""); + result.push(Syntax.multiLineComment(newText)); + } + + public static indentNode(node: ISyntaxNode, indentFirstToken: boolean, indentAmount: number, options: FormattingOptions): SyntaxNode { + var indenter = new SyntaxIndenter(indentFirstToken, indentAmount, options); + return node.accept(indenter); + } + + public static indentNodes(nodes: SyntaxNode[], indentFirstToken: boolean, indentAmount: number, options: FormattingOptions): SyntaxNode[] { + // Note: it is necessary for correctness that we reuse the same SyntaxIndenter here. + // That's because when working on nodes 1-N, we need to know if the previous node ended + // with a newline. The indenter will track that for us. + + var indenter = new SyntaxIndenter(indentFirstToken, indentAmount, options); + var result: SyntaxNode[] = ArrayUtilities.select(nodes, n => n.accept(indenter)); + + return result; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxInterfaces.generated.ts b/src/services/syntax/syntaxInterfaces.generated.ts new file mode 100644 index 00000000000..b7f99f68b28 --- /dev/null +++ b/src/services/syntax/syntaxInterfaces.generated.ts @@ -0,0 +1,499 @@ +/// + +module TypeScript { + export enum NodeFlags { + Export = 0x00000001, // Declarations + Ambient = 0x00000002, // Declarations + Optional = 0x00000004, // Parameter/Property/Method + Rest = 0x00000008, // Parameter + Public = 0x00000010, // Property/Method + Private = 0x00000020, // Property/Method + Static = 0x00000040, // Property/Method + } + + interface SyntaxElement { + kind: SyntaxKind; + } + + interface SyntaxNode extends SyntaxElement { + flags: NodeFlags; + } + + function nodeStart(node: Node): number { + } + + function nodeWidth(node: Node): number { + } + + interface SyntaxToken extends Name, PrimaryExpression { + } + + // The raw text of the token, as written in the original source. + function tokenText(token: SyntaxToken): string { + } + + // The token's javascript value. i.e. 0.0 in the text would have the javascript number value: 0. + function tokenValue(token: SyntaxToken): any { + } + + // The token's value in string form. i.e. \u0041 in the source text would result in a string with the text: A. + function tokenValueText(token: SyntaxToken): string { + } + + interface SyntaxList extends SyntaxElement { + length: number; + item(index: number): T; + } + + interface SourceUnit extends Node { + moduleElements: SyntaxList; + } + + interface QualifiedName extends Name { + left: Name; + right: SyntaxToken; + } + + interface ObjectType extends Type { + typeMembers: SyntaxList; + } + + interface FunctionType extends Type { + typeParameterList?: TypeParameterList; + parameterList: ParameterList; + type: Type; + } + + interface ArrayType extends Type { + type: Type; + } + + interface ConstructorType extends Type { + typeParameterList?: TypeParameterList; + parameterList: ParameterList; + type: Type; + } + + interface GenericType extends Type { + name: Name; + typeArgumentList: TypeArgumentList; + } + + interface TypeQuery extends Type { + name: Name; + } + + interface InterfaceDeclaration extends ModuleElement { + identifier: SyntaxToken; + typeParameterList?: TypeParameterList; + heritageClauses: SyntaxList; + body: ObjectType; + } + + interface FunctionDeclaration extends Statement { + identifier: SyntaxToken; + callSignature: CallSignature; + block?: Block; + } + + interface ModuleDeclaration extends ModuleElement { + name?: Name; + stringLiteral?: SyntaxToken; + moduleElements: SyntaxList; + } + + interface ClassDeclaration extends ModuleElement { + identifier: SyntaxToken; + typeParameterList?: TypeParameterList; + heritageClauses: SyntaxList; + classElements: SyntaxList; + } + + interface EnumDeclaration extends ModuleElement { + identifier: SyntaxToken; + enumElements: SyntaxList; + } + + interface ImportDeclaration extends ModuleElement { + identifier: SyntaxToken; + moduleReference: ModuleReference; + } + + interface ExportAssignment extends ModuleElement { + identifier: SyntaxToken; + } + + interface MemberFunctionDeclaration extends MemberDeclaration { + propertyName: SyntaxToken; + callSignature: CallSignature; + block?: Block; + } + + interface MemberVariableDeclaration extends MemberDeclaration { + variableDeclarator: VariableDeclarator; + } + + interface ConstructorDeclaration extends ClassElement { + callSignature: CallSignature; + block?: Block; + } + + interface IndexMemberDeclaration extends ClassElement { + indexSignature: IndexSignature; + } + + interface GetAccessor extends MemberDeclaration, PropertyAssignment { + propertyName: SyntaxToken; + callSignature: CallSignature; + block: Block; + } + + interface SetAccessor extends MemberDeclaration, PropertyAssignment { + propertyName: SyntaxToken; + callSignature: CallSignature; + block: Block; + } + + interface PropertySignature extends TypeMember { + propertyName: SyntaxToken; + typeAnnotation?: TypeAnnotation; + } + + interface CallSignature extends TypeMember { + typeParameterList?: TypeParameterList; + parameterList: ParameterList; + typeAnnotation?: TypeAnnotation; + } + + interface ConstructSignature extends TypeMember { + callSignature: CallSignature; + } + + interface IndexSignature extends TypeMember { + parameters: SyntaxList; + typeAnnotation?: TypeAnnotation; + } + + interface MethodSignature extends TypeMember { + propertyName: SyntaxToken; + callSignature: CallSignature; + } + + interface Block extends Statement { + statements: SyntaxList; + } + + interface IfStatement extends Statement { + condition: Expression; + statement: Statement; + elseClause?: ElseClause; + } + + interface VariableStatement extends Statement { + variableDeclaration: VariableDeclaration; + } + + interface ExpressionStatement extends Statement { + expression: Expression; + } + + interface ReturnStatement extends Statement { + expression?: Expression; + } + + interface SwitchStatement extends Statement { + expression: Expression; + switchClauses: SyntaxList; + } + + interface BreakStatement extends Statement { + identifier?: SyntaxToken; + } + + interface ContinueStatement extends Statement { + identifier?: SyntaxToken; + } + + interface ForStatement extends Statement { + variableDeclaration?: VariableDeclaration; + initializer?: Expression; + condition?: Expression; + incrementor?: Expression; + statement: Statement; + } + + interface ForInStatement extends Statement { + variableDeclaration?: VariableDeclaration; + left?: Expression; + expression: Expression; + statement: Statement; + } + + interface ThrowStatement extends Statement { + expression: Expression; + } + + interface WhileStatement extends Statement { + condition: Expression; + statement: Statement; + } + + interface TryStatement extends Statement { + block: Block; + catchClause?: CatchClause; + finallyClause?: FinallyClause; + } + + interface LabeledStatement extends Statement { + identifier: SyntaxToken; + statement: Statement; + } + + interface DoStatement extends Statement { + statement: Statement; + condition: Expression; + } + + interface WithStatement extends Statement { + condition: Expression; + statement: Statement; + } + + interface PrefixUnaryExpression extends UnaryExpression { + operand: UnaryExpression; + } + + interface DeleteExpression extends UnaryExpression { + expression: UnaryExpression; + } + + interface TypeOfExpression extends UnaryExpression { + expression: UnaryExpression; + } + + interface VoidExpression extends UnaryExpression { + expression: UnaryExpression; + } + + interface ConditionalExpression extends Expression { + condition: Expression; + whenTrue: Expression; + whenFalse: Expression; + } + + interface BinaryExpression extends Expression { + left: Expression; + right: Expression; + } + + interface PostfixUnaryExpression extends PostfixExpression { + operand: LeftHandSideExpression; + } + + interface MemberAccessExpression extends MemberExpression, CallExpression { + expression: LeftHandSideExpression; + name: SyntaxToken; + } + + interface InvocationExpression extends CallExpression { + expression: LeftHandSideExpression; + argumentList: ArgumentList; + } + + interface ArrayLiteralExpression extends PrimaryExpression { + expressions: SyntaxList; + } + + interface ObjectLiteralExpression extends PrimaryExpression { + propertyAssignments: SyntaxList; + } + + interface ObjectCreationExpression extends MemberExpression { + expression: MemberExpression; + argumentList?: ArgumentList; + } + + interface ParenthesizedExpression extends PrimaryExpression { + expression: Expression; + } + + interface ParenthesizedArrowFunctionExpression extends UnaryExpression { + callSignature: CallSignature; + block?: Block; + expression?: Expression; + } + + interface SimpleArrowFunctionExpression extends UnaryExpression { + parameter: Parameter; + block?: Block; + expression?: Expression; + } + + interface CastExpression extends UnaryExpression { + type: Type; + expression: UnaryExpression; + } + + interface ElementAccessExpression extends MemberExpression, CallExpression { + expression: LeftHandSideExpression; + argumentExpression: Expression; + } + + interface FunctionExpression extends PrimaryExpression { + identifier?: SyntaxToken; + callSignature: CallSignature; + block: Block; + } + + interface VariableDeclaration extends Node { + variableDeclarators: SyntaxList; + } + + interface VariableDeclarator extends Node { + propertyName: SyntaxToken; + typeAnnotation?: TypeAnnotation; + equalsValueClause?: EqualsValueClause; + } + + interface ArgumentList extends Node { + typeArgumentList?: TypeArgumentList; + arguments: SyntaxList; + } + + interface ParameterList extends Node { + parameters: SyntaxList; + } + + interface TypeArgumentList extends Node { + typeArguments: SyntaxList; + } + + interface TypeParameterList extends Node { + typeParameters: SyntaxList; + } + + interface HeritageClause extends Node { + typeNames: SyntaxList; + } + + interface EqualsValueClause extends Node { + value: Expression; + } + + interface CaseSwitchClause extends SwitchClause { + expression: Expression; + statements: SyntaxList; + } + + interface DefaultSwitchClause extends SwitchClause { + statements: SyntaxList; + } + + interface ElseClause extends Node { + statement: Statement; + } + + interface CatchClause extends Node { + identifier: SyntaxToken; + typeAnnotation?: TypeAnnotation; + block: Block; + } + + interface FinallyClause extends Node { + block: Block; + } + + interface TypeParameter extends Node { + identifier: SyntaxToken; + constraint?: Constraint; + } + + interface Constraint extends Node { + type: Type; + } + + interface SimplePropertyAssignment extends PropertyAssignment { + propertyName: SyntaxToken; + expression: Expression; + } + + interface FunctionPropertyAssignment extends PropertyAssignment { + propertyName: SyntaxToken; + callSignature: CallSignature; + block: Block; + } + + interface Parameter extends Node { + identifier: SyntaxToken; + typeAnnotation?: TypeAnnotation; + equalsValueClause?: EqualsValueClause; + } + + interface EnumElement extends Node { + propertyName: SyntaxToken; + equalsValueClause?: EqualsValueClause; + } + + interface TypeAnnotation extends Node { + type: Type; + } + + interface ExternalModuleReference extends ModuleReference { + stringLiteral: SyntaxToken; + } + + interface ModuleNameModuleReference extends ModuleReference { + moduleName: Name; + } + + interface MemberDeclaration extends ClassElement { + } + + interface Statement extends ModuleElement { + } + + interface Name extends Type { + } + + interface UnaryExpression extends Expression { + } + + interface PostfixExpression extends UnaryExpression { + } + + interface LeftHandSideExpression extends PostfixExpression { + } + + interface MemberExpression extends LeftHandSideExpression { + } + + interface CallExpression extends LeftHandSideExpression { + } + + interface PrimaryExpression extends MemberExpression { + } + + interface ModuleElement extends SyntaxElement { + } + + interface ModuleReference extends Node { + } + + interface ClassElement extends Node { + } + + interface TypeMember extends Node { + } + + interface PropertyAssignment extends Node { + } + + interface SwitchClause extends Node { + } + + interface Expression extends SyntaxElement { + } + + interface Type extends SyntaxElement { + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxKind.ts b/src/services/syntax/syntaxKind.ts new file mode 100644 index 00000000000..bcc6ab46eec --- /dev/null +++ b/src/services/syntax/syntaxKind.ts @@ -0,0 +1,337 @@ +// If you change anything in this enum, make sure you run SyntaxGenerator again! + +module TypeScript { + export enum SyntaxKind { + // Variable width tokens, trivia and lists. + None, + List, + SeparatedList, + TriviaList, + + // Trivia + WhitespaceTrivia, + NewLineTrivia, + MultiLineCommentTrivia, + SingleLineCommentTrivia, + SkippedTokenTrivia, + + // Note: all variable width tokens must come before all fixed width tokens. + + ErrorToken, + EndOfFileToken, + + // Tokens + IdentifierName, + + // LiteralTokens + RegularExpressionLiteral, + NumericLiteral, + StringLiteral, + + // All fixed width tokens follow. + + // Keywords + BreakKeyword, + CaseKeyword, + CatchKeyword, + ContinueKeyword, + DebuggerKeyword, + DefaultKeyword, + DeleteKeyword, + DoKeyword, + ElseKeyword, + FalseKeyword, + FinallyKeyword, + ForKeyword, + FunctionKeyword, + IfKeyword, + InKeyword, + InstanceOfKeyword, + NewKeyword, + NullKeyword, + ReturnKeyword, + SwitchKeyword, + ThisKeyword, + ThrowKeyword, + TrueKeyword, + TryKeyword, + TypeOfKeyword, + VarKeyword, + VoidKeyword, + WhileKeyword, + WithKeyword, + + // FutureReservedWords. + ClassKeyword, + ConstKeyword, + EnumKeyword, + ExportKeyword, + ExtendsKeyword, + ImportKeyword, + SuperKeyword, + + // FutureReservedStrictWords. + ImplementsKeyword, + InterfaceKeyword, + LetKeyword, + PackageKeyword, + PrivateKeyword, + ProtectedKeyword, + PublicKeyword, + StaticKeyword, + YieldKeyword, + + // TypeScript keywords. + AnyKeyword, + BooleanKeyword, + ConstructorKeyword, + DeclareKeyword, + GetKeyword, + ModuleKeyword, + RequireKeyword, + NumberKeyword, + SetKeyword, + StringKeyword, + + // Punctuators + OpenBraceToken, + CloseBraceToken, + OpenParenToken, + CloseParenToken, + OpenBracketToken, + CloseBracketToken, + DotToken, + DotDotDotToken, + SemicolonToken, + CommaToken, + LessThanToken, + GreaterThanToken, + LessThanEqualsToken, + GreaterThanEqualsToken, + EqualsEqualsToken, + EqualsGreaterThanToken, + ExclamationEqualsToken, + EqualsEqualsEqualsToken, + ExclamationEqualsEqualsToken, + PlusToken, + MinusToken, + AsteriskToken, + PercentToken, + PlusPlusToken, + MinusMinusToken, + LessThanLessThanToken, + GreaterThanGreaterThanToken, + GreaterThanGreaterThanGreaterThanToken, + AmpersandToken, + BarToken, + CaretToken, + ExclamationToken, + TildeToken, + AmpersandAmpersandToken, + BarBarToken, + QuestionToken, + ColonToken, + EqualsToken, + PlusEqualsToken, + MinusEqualsToken, + AsteriskEqualsToken, + PercentEqualsToken, + LessThanLessThanEqualsToken, + GreaterThanGreaterThanEqualsToken, + GreaterThanGreaterThanGreaterThanEqualsToken, + AmpersandEqualsToken, + BarEqualsToken, + CaretEqualsToken, + SlashToken, + SlashEqualsToken, + + // SyntaxNodes + SourceUnit, + + // Names + QualifiedName, + + // Types + ObjectType, + FunctionType, + ArrayType, + ConstructorType, + GenericType, + TypeQuery, + + // Module elements. + InterfaceDeclaration, + FunctionDeclaration, + ModuleDeclaration, + ClassDeclaration, + EnumDeclaration, + ImportDeclaration, + ExportAssignment, + + // ClassElements + MemberFunctionDeclaration, + MemberVariableDeclaration, + ConstructorDeclaration, + IndexMemberDeclaration, + + // ClassElement and PropertyAssignment + GetAccessor, + SetAccessor, + + // Type members. + PropertySignature, + CallSignature, + ConstructSignature, + IndexSignature, + MethodSignature, + + // Statements + Block, + IfStatement, + VariableStatement, + ExpressionStatement, + ReturnStatement, + SwitchStatement, + BreakStatement, + ContinueStatement, + ForStatement, + ForInStatement, + EmptyStatement, + ThrowStatement, + WhileStatement, + TryStatement, + LabeledStatement, + DoStatement, + DebuggerStatement, + WithStatement, + + // Expressions + PlusExpression, + NegateExpression, + BitwiseNotExpression, + LogicalNotExpression, + PreIncrementExpression, + PreDecrementExpression, + DeleteExpression, + TypeOfExpression, + VoidExpression, + CommaExpression, + AssignmentExpression, + AddAssignmentExpression, + SubtractAssignmentExpression, + MultiplyAssignmentExpression, + DivideAssignmentExpression, + ModuloAssignmentExpression, + AndAssignmentExpression, + ExclusiveOrAssignmentExpression, + OrAssignmentExpression, + LeftShiftAssignmentExpression, + SignedRightShiftAssignmentExpression, + UnsignedRightShiftAssignmentExpression, + ConditionalExpression, + LogicalOrExpression, + LogicalAndExpression, + BitwiseOrExpression, + BitwiseExclusiveOrExpression, + BitwiseAndExpression, + EqualsWithTypeConversionExpression, + NotEqualsWithTypeConversionExpression, + EqualsExpression, + NotEqualsExpression, + LessThanExpression, + GreaterThanExpression, + LessThanOrEqualExpression, + GreaterThanOrEqualExpression, + InstanceOfExpression, + InExpression, + LeftShiftExpression, + SignedRightShiftExpression, + UnsignedRightShiftExpression, + MultiplyExpression, + DivideExpression, + ModuloExpression, + AddExpression, + SubtractExpression, + PostIncrementExpression, + PostDecrementExpression, + MemberAccessExpression, + InvocationExpression, + ArrayLiteralExpression, + ObjectLiteralExpression, + ObjectCreationExpression, + ParenthesizedExpression, + ParenthesizedArrowFunctionExpression, + SimpleArrowFunctionExpression, + CastExpression, + ElementAccessExpression, + FunctionExpression, + OmittedExpression, + + // Variable declarations + VariableDeclaration, + VariableDeclarator, + + // Lists + ArgumentList, + ParameterList, + TypeArgumentList, + TypeParameterList, + + // Clauses + ExtendsHeritageClause, + ImplementsHeritageClause, + EqualsValueClause, + CaseSwitchClause, + DefaultSwitchClause, + ElseClause, + CatchClause, + FinallyClause, + + // Generics + TypeParameter, + Constraint, + + // Property Assignment + SimplePropertyAssignment, + // GetAccessorPropertyAssignment, + // SetAccessorPropertyAssignment, + FunctionPropertyAssignment, + + // Misc. + Parameter, + EnumElement, + TypeAnnotation, + ExternalModuleReference, + ModuleNameModuleReference, + + FirstStandardKeyword = BreakKeyword, + LastStandardKeyword = WithKeyword, + + FirstFutureReservedKeyword = ClassKeyword, + LastFutureReservedKeyword = SuperKeyword, + + FirstFutureReservedStrictKeyword = ImplementsKeyword, + LastFutureReservedStrictKeyword = YieldKeyword, + + FirstTypeScriptKeyword = AnyKeyword, + LastTypeScriptKeyword = StringKeyword, + + FirstKeyword = FirstStandardKeyword, + LastKeyword = LastTypeScriptKeyword, + + FirstToken = ErrorToken, + LastToken = SlashEqualsToken, + + FirstPunctuation = OpenBraceToken, + LastPunctuation = SlashEqualsToken, + + FirstFixedWidth = FirstKeyword, + LastFixedWidth = LastPunctuation, + + FirstTrivia = WhitespaceTrivia, + LastTrivia = SkippedTokenTrivia, + + FirstNode = SourceUnit, + LastNode = ModuleNameModuleReference, + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxList.ts b/src/services/syntax/syntaxList.ts new file mode 100644 index 00000000000..52bc02ecdca --- /dev/null +++ b/src/services/syntax/syntaxList.ts @@ -0,0 +1,95 @@ +/// + +interface Array { + data: number; + separators?: TypeScript.ISyntaxToken[]; + + kind(): TypeScript.SyntaxKind; + parent: TypeScript.ISyntaxElement; + + separatorCount(): number; + separatorAt(index: number): TypeScript.ISyntaxToken; +} + +module TypeScript.Syntax { + var _emptyList: ISyntaxNodeOrToken[] = []; + + var _emptySeparatedList: ISyntaxNodeOrToken[] = []; + var _emptySeparators: ISyntaxToken[] = []; + + _emptySeparatedList.separators = _emptySeparators; + + function assertEmptyLists() { + // Debug.assert(_emptyList.length === 0); + // var separators = _emptySeparatedList.separators; + // Debug.assert(!separators || separators.length === 0); + } + + Array.prototype.kind = function () { + return this.separators === undefined ? SyntaxKind.List : SyntaxKind.SeparatedList; + } + + Array.prototype.separatorCount = function (): number { + assertEmptyLists(); + // Debug.assert(this.kind === SyntaxKind.SeparatedList); + return this.separators.length; + } + + Array.prototype.separatorAt = function (index: number): ISyntaxToken { + assertEmptyLists(); + // Debug.assert(this.kind === SyntaxKind.SeparatedList); + // Debug.assert(index >= 0 && index < this.separators.length); + return this.separators[index]; + } + + export function emptyList(): T[] { + return _emptyList; + } + + export function emptySeparatedList(): T[] { + return _emptySeparatedList; + } + + export function list(nodes: T[]): T[] { + if (nodes === undefined || nodes === null || nodes.length === 0) { + return emptyList(); + } + + for (var i = 0, n = nodes.length; i < n; i++) { + nodes[i].parent = nodes; + } + + return nodes; + } + + export function separatedList(nodes: T[], separators: ISyntaxToken[]): T[] { + if (nodes === undefined || nodes === null || nodes.length === 0) { + return emptySeparatedList(); + } + + // Debug.assert(separators.length === nodes.length || separators.length == (nodes.length - 1)); + + for (var i = 0, n = nodes.length; i < n; i++) { + nodes[i].parent = nodes; + } + + for (var i = 0, n = separators.length; i < n; i++) { + separators[i].parent = nodes; + } + + + nodes.separators = separators.length === 0 ? _emptySeparators : separators; + + return nodes; + } + + export function nonSeparatorIndexOf(list: T[], ast: ISyntaxNodeOrToken): number { + for (var i = 0, n = list.length; i < n; i++) { + if (list[i] === ast) { + return i; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxNode.ts b/src/services/syntax/syntaxNode.ts new file mode 100644 index 00000000000..464c59793aa --- /dev/null +++ b/src/services/syntax/syntaxNode.ts @@ -0,0 +1,19 @@ +/// + +module TypeScript { + export class SyntaxNode implements ISyntaxNodeOrToken { + private __kind: SyntaxKind; + public data: number; + public parent: ISyntaxElement; + + constructor(data: number) { + if (data) { + this.data = data; + } + } + + public kind(): SyntaxKind { + return this.__kind; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxNodeInvariantsChecker.ts b/src/services/syntax/syntaxNodeInvariantsChecker.ts new file mode 100644 index 00000000000..259bd213fda --- /dev/null +++ b/src/services/syntax/syntaxNodeInvariantsChecker.ts @@ -0,0 +1,39 @@ +/// + +// A debug class that we use to make sure a syntax node is valid. Currently, this simply verifies +// that the same token does not appear in the tree multiple times. This is important for +// subsystems that want to map between tokens and positions. If a token shows up multiple times in +// the node, then it will not have a unique position, previous token, etc. etc. and that can screw +// many algorithms. For this reason, when generating trees, it is important that nodes that are +// reused are cloned before insertion. +module TypeScript { + export class SyntaxNodeInvariantsChecker extends SyntaxWalker { + private tokenTable = Collections.createHashTable(Collections.DefaultHashTableCapacity, Collections.identityHashCode); + + public static checkInvariants(node: ISyntaxNode): void { + visitNodeOrToken(new SyntaxNodeInvariantsChecker(), node); + } + + public visitNode(node: ISyntaxNode): void { + Debug.assert(node.kind === SyntaxKind.SourceUnit || node.parent); + super.visitNode(node); + } + + public visitList(list: ISyntaxNodeOrToken[]): void { + Debug.assert(isShared(list) || list.parent); + super.visitList(list); + } + + public visitSeparatedList(list: ISyntaxNodeOrToken[]): void { + Debug.assert(isShared(list) || list.parent); + super.visitSeparatedList(list); + } + + public visitToken(token: ISyntaxToken): void { + // We're calling 'add', so the table will throw if we try to put the same token in multiple + // times. + Debug.assert(token.parent); + this.tokenTable.add(token, token); + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxNodeOrToken.ts b/src/services/syntax/syntaxNodeOrToken.ts new file mode 100644 index 00000000000..695268d843f --- /dev/null +++ b/src/services/syntax/syntaxNodeOrToken.ts @@ -0,0 +1,6 @@ +/// + +module TypeScript { + export interface ISyntaxNodeOrToken extends ISyntaxElement { + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxNodes.abstract.generated.ts b/src/services/syntax/syntaxNodes.abstract.generated.ts new file mode 100644 index 00000000000..031b0363ca4 --- /dev/null +++ b/src/services/syntax/syntaxNodes.abstract.generated.ts @@ -0,0 +1,1194 @@ +/// + +module TypeScript.Syntax.Abstract { + // Inject this module as the factory for producing syntax nodes in the parser. + Parser.syntaxFactory = Abstract; + export var isConcrete: boolean = false; + + export class SourceUnitSyntax extends SyntaxNode { + public syntaxTree: SyntaxTree = null; + public moduleElements: IModuleElementSyntax[]; + public endOfFileToken: ISyntaxToken; + constructor(data: number, moduleElements: IModuleElementSyntax[], endOfFileToken: ISyntaxToken) { + super(data); + this.parent = null, + this.moduleElements = moduleElements, + this.endOfFileToken = endOfFileToken, + !isShared(moduleElements) && (moduleElements.parent = this), + endOfFileToken.parent = this; + } + } + export class QualifiedNameSyntax extends SyntaxNode implements INameSyntax { + public left: INameSyntax; + public dotToken: ISyntaxToken; + public right: ISyntaxToken; + public _nameBrand: any; public _typeBrand: any; + constructor(data: number, left: INameSyntax, dotToken: ISyntaxToken, right: ISyntaxToken) { + super(data); + this.left = left, + this.right = right, + left.parent = this, + right.parent = this; + } + } + export class ObjectTypeSyntax extends SyntaxNode implements ITypeSyntax { + public openBraceToken: ISyntaxToken; + public typeMembers: ITypeMemberSyntax[]; + public closeBraceToken: ISyntaxToken; + public _typeBrand: any; + constructor(data: number, openBraceToken: ISyntaxToken, typeMembers: ITypeMemberSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.typeMembers = typeMembers, + !isShared(typeMembers) && (typeMembers.parent = this); + } + } + export class FunctionTypeSyntax extends SyntaxNode implements ITypeSyntax { + public typeParameterList: TypeParameterListSyntax; + public parameterList: ParameterListSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public type: ITypeSyntax; + public _typeBrand: any; + constructor(data: number, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, equalsGreaterThanToken: ISyntaxToken, type: ITypeSyntax) { + super(data); + this.typeParameterList = typeParameterList, + this.parameterList = parameterList, + this.type = type, + typeParameterList && (typeParameterList.parent = this), + parameterList.parent = this, + type.parent = this; + } + } + export class ArrayTypeSyntax extends SyntaxNode implements ITypeSyntax { + public type: ITypeSyntax; + public openBracketToken: ISyntaxToken; + public closeBracketToken: ISyntaxToken; + public _typeBrand: any; + constructor(data: number, type: ITypeSyntax, openBracketToken: ISyntaxToken, closeBracketToken: ISyntaxToken) { + super(data); + this.type = type, + type.parent = this; + } + } + export class ConstructorTypeSyntax extends SyntaxNode implements ITypeSyntax { + public newKeyword: ISyntaxToken; + public typeParameterList: TypeParameterListSyntax; + public parameterList: ParameterListSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public type: ITypeSyntax; + public _typeBrand: any; + constructor(data: number, newKeyword: ISyntaxToken, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, equalsGreaterThanToken: ISyntaxToken, type: ITypeSyntax) { + super(data); + this.typeParameterList = typeParameterList, + this.parameterList = parameterList, + this.type = type, + typeParameterList && (typeParameterList.parent = this), + parameterList.parent = this, + type.parent = this; + } + } + export class GenericTypeSyntax extends SyntaxNode implements ITypeSyntax { + public name: INameSyntax; + public typeArgumentList: TypeArgumentListSyntax; + public _typeBrand: any; + constructor(data: number, name: INameSyntax, typeArgumentList: TypeArgumentListSyntax) { + super(data); + this.name = name, + this.typeArgumentList = typeArgumentList, + name.parent = this, + typeArgumentList.parent = this; + } + } + export class TypeQuerySyntax extends SyntaxNode implements ITypeSyntax { + public typeOfKeyword: ISyntaxToken; + public name: INameSyntax; + public _typeBrand: any; + constructor(data: number, typeOfKeyword: ISyntaxToken, name: INameSyntax) { + super(data); + this.name = name, + name.parent = this; + } + } + export class InterfaceDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public interfaceKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public typeParameterList: TypeParameterListSyntax; + public heritageClauses: HeritageClauseSyntax[]; + public body: ObjectTypeSyntax; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], interfaceKeyword: ISyntaxToken, identifier: ISyntaxToken, typeParameterList: TypeParameterListSyntax, heritageClauses: HeritageClauseSyntax[], body: ObjectTypeSyntax) { + super(data); + this.modifiers = modifiers, + this.identifier = identifier, + this.typeParameterList = typeParameterList, + this.heritageClauses = heritageClauses, + this.body = body, + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + typeParameterList && (typeParameterList.parent = this), + !isShared(heritageClauses) && (heritageClauses.parent = this), + body.parent = this; + } + } + export class FunctionDeclarationSyntax extends SyntaxNode implements IStatementSyntax { + public modifiers: ISyntaxToken[]; + public functionKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.identifier = identifier, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + callSignature.parent = this, + block && (block.parent = this); + } + } + export class ModuleDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public moduleKeyword: ISyntaxToken; + public name: INameSyntax; + public stringLiteral: ISyntaxToken; + public openBraceToken: ISyntaxToken; + public moduleElements: IModuleElementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], moduleKeyword: ISyntaxToken, name: INameSyntax, stringLiteral: ISyntaxToken, openBraceToken: ISyntaxToken, moduleElements: IModuleElementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.name = name, + this.stringLiteral = stringLiteral, + this.moduleElements = moduleElements, + !isShared(modifiers) && (modifiers.parent = this), + name && (name.parent = this), + stringLiteral && (stringLiteral.parent = this), + !isShared(moduleElements) && (moduleElements.parent = this); + } + } + export class ClassDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public classKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public typeParameterList: TypeParameterListSyntax; + public heritageClauses: HeritageClauseSyntax[]; + public openBraceToken: ISyntaxToken; + public classElements: IClassElementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], classKeyword: ISyntaxToken, identifier: ISyntaxToken, typeParameterList: TypeParameterListSyntax, heritageClauses: HeritageClauseSyntax[], openBraceToken: ISyntaxToken, classElements: IClassElementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.identifier = identifier, + this.typeParameterList = typeParameterList, + this.heritageClauses = heritageClauses, + this.classElements = classElements, + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + typeParameterList && (typeParameterList.parent = this), + !isShared(heritageClauses) && (heritageClauses.parent = this), + !isShared(classElements) && (classElements.parent = this); + } + } + export class EnumDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public enumKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public openBraceToken: ISyntaxToken; + public enumElements: EnumElementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], enumKeyword: ISyntaxToken, identifier: ISyntaxToken, openBraceToken: ISyntaxToken, enumElements: EnumElementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.identifier = identifier, + this.enumElements = enumElements, + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + !isShared(enumElements) && (enumElements.parent = this); + } + } + export class ImportDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public importKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public equalsToken: ISyntaxToken; + public moduleReference: IModuleReferenceSyntax; + public semicolonToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], importKeyword: ISyntaxToken, identifier: ISyntaxToken, equalsToken: ISyntaxToken, moduleReference: IModuleReferenceSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.identifier = identifier, + this.moduleReference = moduleReference, + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + moduleReference.parent = this; + } + } + export class ExportAssignmentSyntax extends SyntaxNode implements IModuleElementSyntax { + public exportKeyword: ISyntaxToken; + public equalsToken: ISyntaxToken; + public identifier: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, exportKeyword: ISyntaxToken, equalsToken: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.identifier = identifier, + identifier.parent = this; + } + } + export class MemberFunctionDeclarationSyntax extends SyntaxNode implements IMemberDeclarationSyntax { + public modifiers: ISyntaxToken[]; + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public semicolonToken: ISyntaxToken; + public _memberDeclarationBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + propertyName.parent = this, + callSignature.parent = this, + block && (block.parent = this); + } + } + export class MemberVariableDeclarationSyntax extends SyntaxNode implements IMemberDeclarationSyntax { + public modifiers: ISyntaxToken[]; + public variableDeclarator: VariableDeclaratorSyntax; + public semicolonToken: ISyntaxToken; + public _memberDeclarationBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], variableDeclarator: VariableDeclaratorSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.variableDeclarator = variableDeclarator, + !isShared(modifiers) && (modifiers.parent = this), + variableDeclarator.parent = this; + } + } + export class ConstructorDeclarationSyntax extends SyntaxNode implements IClassElementSyntax { + public modifiers: ISyntaxToken[]; + public constructorKeyword: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public semicolonToken: ISyntaxToken; + public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], constructorKeyword: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.constructorKeyword = constructorKeyword, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + constructorKeyword.parent = this, + callSignature.parent = this, + block && (block.parent = this); + } + } + export class IndexMemberDeclarationSyntax extends SyntaxNode implements IClassElementSyntax { + public modifiers: ISyntaxToken[]; + public indexSignature: IndexSignatureSyntax; + public semicolonToken: ISyntaxToken; + public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], indexSignature: IndexSignatureSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.indexSignature = indexSignature, + !isShared(modifiers) && (modifiers.parent = this), + indexSignature.parent = this; + } + } + export class GetAccessorSyntax extends SyntaxNode implements IMemberDeclarationSyntax, IPropertyAssignmentSyntax { + public modifiers: ISyntaxToken[]; + public getKeyword: ISyntaxToken; + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _memberDeclarationBrand: any; public _propertyAssignmentBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], getKeyword: ISyntaxToken, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.modifiers = modifiers, + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + propertyName.parent = this, + callSignature.parent = this, + block.parent = this; + } + } + export class SetAccessorSyntax extends SyntaxNode implements IMemberDeclarationSyntax, IPropertyAssignmentSyntax { + public modifiers: ISyntaxToken[]; + public setKeyword: ISyntaxToken; + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _memberDeclarationBrand: any; public _propertyAssignmentBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], setKeyword: ISyntaxToken, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.modifiers = modifiers, + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + propertyName.parent = this, + callSignature.parent = this, + block.parent = this; + } + } + export class PropertySignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public propertyName: ISyntaxToken; + public questionToken: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public _typeMemberBrand: any; + constructor(data: number, propertyName: ISyntaxToken, questionToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax) { + super(data); + this.propertyName = propertyName, + this.questionToken = questionToken, + this.typeAnnotation = typeAnnotation, + propertyName.parent = this, + questionToken && (questionToken.parent = this), + typeAnnotation && (typeAnnotation.parent = this); + } + } + export class CallSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public typeParameterList: TypeParameterListSyntax; + public parameterList: ParameterListSyntax; + public typeAnnotation: TypeAnnotationSyntax; + public _typeMemberBrand: any; + constructor(data: number, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, typeAnnotation: TypeAnnotationSyntax) { + super(data); + this.typeParameterList = typeParameterList, + this.parameterList = parameterList, + this.typeAnnotation = typeAnnotation, + typeParameterList && (typeParameterList.parent = this), + parameterList.parent = this, + typeAnnotation && (typeAnnotation.parent = this); + } + } + export class ConstructSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public newKeyword: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public _typeMemberBrand: any; + constructor(data: number, newKeyword: ISyntaxToken, callSignature: CallSignatureSyntax) { + super(data); + this.callSignature = callSignature, + callSignature.parent = this; + } + } + export class IndexSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public openBracketToken: ISyntaxToken; + public parameters: ParameterSyntax[]; + public closeBracketToken: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public _typeMemberBrand: any; + constructor(data: number, openBracketToken: ISyntaxToken, parameters: ParameterSyntax[], closeBracketToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax) { + super(data); + this.openBracketToken = openBracketToken, + this.parameters = parameters, + this.closeBracketToken = closeBracketToken, + this.typeAnnotation = typeAnnotation, + openBracketToken.parent = this, + !isShared(parameters) && (parameters.parent = this), + closeBracketToken.parent = this, + typeAnnotation && (typeAnnotation.parent = this); + } + } + export class MethodSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public propertyName: ISyntaxToken; + public questionToken: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public _typeMemberBrand: any; + constructor(data: number, propertyName: ISyntaxToken, questionToken: ISyntaxToken, callSignature: CallSignatureSyntax) { + super(data); + this.propertyName = propertyName, + this.questionToken = questionToken, + this.callSignature = callSignature, + propertyName.parent = this, + questionToken && (questionToken.parent = this), + callSignature.parent = this; + } + } + export class BlockSyntax extends SyntaxNode implements IStatementSyntax { + public openBraceToken: ISyntaxToken; + public statements: IStatementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, openBraceToken: ISyntaxToken, statements: IStatementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.openBraceToken = openBraceToken, + this.statements = statements, + openBraceToken.parent = this, + !isShared(statements) && (statements.parent = this); + } + } + export class IfStatementSyntax extends SyntaxNode implements IStatementSyntax { + public ifKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public elseClause: ElseClauseSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, ifKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax, elseClause: ElseClauseSyntax) { + super(data); + this.condition = condition, + this.statement = statement, + this.elseClause = elseClause, + condition.parent = this, + statement.parent = this, + elseClause && (elseClause.parent = this); + } + } + export class VariableStatementSyntax extends SyntaxNode implements IStatementSyntax { + public modifiers: ISyntaxToken[]; + public variableDeclaration: VariableDeclarationSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], variableDeclaration: VariableDeclarationSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.variableDeclaration = variableDeclaration, + !isShared(modifiers) && (modifiers.parent = this), + variableDeclaration.parent = this; + } + } + export class ExpressionStatementSyntax extends SyntaxNode implements IStatementSyntax { + public expression: IExpressionSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, expression: IExpressionSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.expression = expression, + expression.parent = this; + } + } + export class ReturnStatementSyntax extends SyntaxNode implements IStatementSyntax { + public returnKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, returnKeyword: ISyntaxToken, expression: IExpressionSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.returnKeyword = returnKeyword, + this.expression = expression, + returnKeyword.parent = this, + expression && (expression.parent = this); + } + } + export class SwitchStatementSyntax extends SyntaxNode implements IStatementSyntax { + public switchKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public expression: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public openBraceToken: ISyntaxToken; + public switchClauses: ISwitchClauseSyntax[]; + public closeBraceToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, switchKeyword: ISyntaxToken, openParenToken: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, openBraceToken: ISyntaxToken, switchClauses: ISwitchClauseSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.expression = expression, + this.switchClauses = switchClauses, + expression.parent = this, + !isShared(switchClauses) && (switchClauses.parent = this); + } + } + export class BreakStatementSyntax extends SyntaxNode implements IStatementSyntax { + public breakKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, breakKeyword: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.breakKeyword = breakKeyword, + this.identifier = identifier, + breakKeyword.parent = this, + identifier && (identifier.parent = this); + } + } + export class ContinueStatementSyntax extends SyntaxNode implements IStatementSyntax { + public continueKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, continueKeyword: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.continueKeyword = continueKeyword, + this.identifier = identifier, + continueKeyword.parent = this, + identifier && (identifier.parent = this); + } + } + export class ForStatementSyntax extends SyntaxNode implements IStatementSyntax { + public forKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public variableDeclaration: VariableDeclarationSyntax; + public initializer: IExpressionSyntax; + public firstSemicolonToken: ISyntaxToken; + public condition: IExpressionSyntax; + public secondSemicolonToken: ISyntaxToken; + public incrementor: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.variableDeclaration = variableDeclaration, + this.initializer = initializer, + this.condition = condition, + this.incrementor = incrementor, + this.statement = statement, + variableDeclaration && (variableDeclaration.parent = this), + initializer && (initializer.parent = this), + condition && (condition.parent = this), + incrementor && (incrementor.parent = this), + statement.parent = this; + } + } + export class ForInStatementSyntax extends SyntaxNode implements IStatementSyntax { + public forKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public variableDeclaration: VariableDeclarationSyntax; + public left: IExpressionSyntax; + public inKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, left: IExpressionSyntax, inKeyword: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.variableDeclaration = variableDeclaration, + this.left = left, + this.expression = expression, + this.statement = statement, + variableDeclaration && (variableDeclaration.parent = this), + left && (left.parent = this), + expression.parent = this, + statement.parent = this; + } + } + export class EmptyStatementSyntax extends SyntaxNode implements IStatementSyntax { + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, semicolonToken: ISyntaxToken) { + super(data); + this.semicolonToken = semicolonToken, + semicolonToken.parent = this; + } + } + export class ThrowStatementSyntax extends SyntaxNode implements IStatementSyntax { + public throwKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, throwKeyword: ISyntaxToken, expression: IExpressionSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.expression = expression, + expression.parent = this; + } + } + export class WhileStatementSyntax extends SyntaxNode implements IStatementSyntax { + public whileKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, whileKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.condition = condition, + this.statement = statement, + condition.parent = this, + statement.parent = this; + } + } + export class TryStatementSyntax extends SyntaxNode implements IStatementSyntax { + public tryKeyword: ISyntaxToken; + public block: BlockSyntax; + public catchClause: CatchClauseSyntax; + public finallyClause: FinallyClauseSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, tryKeyword: ISyntaxToken, block: BlockSyntax, catchClause: CatchClauseSyntax, finallyClause: FinallyClauseSyntax) { + super(data); + this.block = block, + this.catchClause = catchClause, + this.finallyClause = finallyClause, + block.parent = this, + catchClause && (catchClause.parent = this), + finallyClause && (finallyClause.parent = this); + } + } + export class LabeledStatementSyntax extends SyntaxNode implements IStatementSyntax { + public identifier: ISyntaxToken; + public colonToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, identifier: ISyntaxToken, colonToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.identifier = identifier, + this.statement = statement, + identifier.parent = this, + statement.parent = this; + } + } + export class DoStatementSyntax extends SyntaxNode implements IStatementSyntax { + public doKeyword: ISyntaxToken; + public statement: IStatementSyntax; + public whileKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, doKeyword: ISyntaxToken, statement: IStatementSyntax, whileKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.statement = statement, + this.condition = condition, + statement.parent = this, + condition.parent = this; + } + } + export class DebuggerStatementSyntax extends SyntaxNode implements IStatementSyntax { + public debuggerKeyword: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, debuggerKeyword: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.debuggerKeyword = debuggerKeyword, + debuggerKeyword.parent = this; + } + } + export class WithStatementSyntax extends SyntaxNode implements IStatementSyntax { + public withKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, withKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.condition = condition, + this.statement = statement, + condition.parent = this, + statement.parent = this; + } + } + export class PrefixUnaryExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public operatorToken: ISyntaxToken; + public operand: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, operatorToken: ISyntaxToken, operand: IUnaryExpressionSyntax) { + super(data); + this.operatorToken = operatorToken, + this.operand = operand, + operatorToken.parent = this, + operand.parent = this; + } + public kind(): SyntaxKind { return SyntaxFacts.getPrefixUnaryExpressionFromOperatorToken(this.operatorToken.kind()); } + } + export class DeleteExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public deleteKeyword: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, deleteKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.expression = expression, + expression.parent = this; + } + } + export class TypeOfExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public typeOfKeyword: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, typeOfKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.expression = expression, + expression.parent = this; + } + } + export class VoidExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public voidKeyword: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, voidKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.expression = expression, + expression.parent = this; + } + } + export class ConditionalExpressionSyntax extends SyntaxNode implements IExpressionSyntax { + public condition: IExpressionSyntax; + public questionToken: ISyntaxToken; + public whenTrue: IExpressionSyntax; + public colonToken: ISyntaxToken; + public whenFalse: IExpressionSyntax; + public _expressionBrand: any; + constructor(data: number, condition: IExpressionSyntax, questionToken: ISyntaxToken, whenTrue: IExpressionSyntax, colonToken: ISyntaxToken, whenFalse: IExpressionSyntax) { + super(data); + this.condition = condition, + this.whenTrue = whenTrue, + this.whenFalse = whenFalse, + condition.parent = this, + whenTrue.parent = this, + whenFalse.parent = this; + } + } + export class BinaryExpressionSyntax extends SyntaxNode implements IExpressionSyntax { + public left: IExpressionSyntax; + public operatorToken: ISyntaxToken; + public right: IExpressionSyntax; + public _expressionBrand: any; + constructor(data: number, left: IExpressionSyntax, operatorToken: ISyntaxToken, right: IExpressionSyntax) { + super(data); + this.left = left, + this.operatorToken = operatorToken, + this.right = right, + left.parent = this, + operatorToken.parent = this, + right.parent = this; + } + public kind(): SyntaxKind { return SyntaxFacts.getBinaryExpressionFromOperatorToken(this.operatorToken.kind()); } + } + export class PostfixUnaryExpressionSyntax extends SyntaxNode implements IPostfixExpressionSyntax { + public operand: ILeftHandSideExpressionSyntax; + public operatorToken: ISyntaxToken; + public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, operand: ILeftHandSideExpressionSyntax, operatorToken: ISyntaxToken) { + super(data); + this.operand = operand, + this.operatorToken = operatorToken, + operand.parent = this, + operatorToken.parent = this; + } + public kind(): SyntaxKind { return SyntaxFacts.getPostfixUnaryExpressionFromOperatorToken(this.operatorToken.kind()); } + } + export class MemberAccessExpressionSyntax extends SyntaxNode implements IMemberExpressionSyntax, ICallExpressionSyntax { + public expression: ILeftHandSideExpressionSyntax; + public dotToken: ISyntaxToken; + public name: ISyntaxToken; + public _memberExpressionBrand: any; public _callExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, expression: ILeftHandSideExpressionSyntax, dotToken: ISyntaxToken, name: ISyntaxToken) { + super(data); + this.expression = expression, + this.name = name, + expression.parent = this, + name.parent = this; + } + } + export class InvocationExpressionSyntax extends SyntaxNode implements ICallExpressionSyntax { + public expression: ILeftHandSideExpressionSyntax; + public argumentList: ArgumentListSyntax; + public _callExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, expression: ILeftHandSideExpressionSyntax, argumentList: ArgumentListSyntax) { + super(data); + this.expression = expression, + this.argumentList = argumentList, + expression.parent = this, + argumentList.parent = this; + } + } + export class ArrayLiteralExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public openBracketToken: ISyntaxToken; + public expressions: IExpressionSyntax[]; + public closeBracketToken: ISyntaxToken; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, openBracketToken: ISyntaxToken, expressions: IExpressionSyntax[], closeBracketToken: ISyntaxToken) { + super(data); + this.expressions = expressions, + !isShared(expressions) && (expressions.parent = this); + } + } + export class ObjectLiteralExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public openBraceToken: ISyntaxToken; + public propertyAssignments: IPropertyAssignmentSyntax[]; + public closeBraceToken: ISyntaxToken; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, openBraceToken: ISyntaxToken, propertyAssignments: IPropertyAssignmentSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.propertyAssignments = propertyAssignments, + !isShared(propertyAssignments) && (propertyAssignments.parent = this); + } + } + export class ObjectCreationExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public newKeyword: ISyntaxToken; + public expression: IMemberExpressionSyntax; + public argumentList: ArgumentListSyntax; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, newKeyword: ISyntaxToken, expression: IMemberExpressionSyntax, argumentList: ArgumentListSyntax) { + super(data); + this.expression = expression, + this.argumentList = argumentList, + expression.parent = this, + argumentList && (argumentList.parent = this); + } + } + export class ParenthesizedExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public openParenToken: ISyntaxToken; + public expression: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, openParenToken: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken) { + super(data); + this.expression = expression, + expression.parent = this; + } + } + export class ParenthesizedArrowFunctionExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public callSignature: CallSignatureSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public block: BlockSyntax; + public expression: IExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, callSignature: CallSignatureSyntax, equalsGreaterThanToken: ISyntaxToken, block: BlockSyntax, expression: IExpressionSyntax) { + super(data); + this.callSignature = callSignature, + this.block = block, + this.expression = expression, + callSignature.parent = this, + block && (block.parent = this), + expression && (expression.parent = this); + } + } + export class SimpleArrowFunctionExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public parameter: ParameterSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public block: BlockSyntax; + public expression: IExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, parameter: ParameterSyntax, equalsGreaterThanToken: ISyntaxToken, block: BlockSyntax, expression: IExpressionSyntax) { + super(data); + this.parameter = parameter, + this.block = block, + this.expression = expression, + parameter.parent = this, + block && (block.parent = this), + expression && (expression.parent = this); + } + } + export class CastExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public lessThanToken: ISyntaxToken; + public type: ITypeSyntax; + public greaterThanToken: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, lessThanToken: ISyntaxToken, type: ITypeSyntax, greaterThanToken: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.type = type, + this.expression = expression, + type.parent = this, + expression.parent = this; + } + } + export class ElementAccessExpressionSyntax extends SyntaxNode implements IMemberExpressionSyntax, ICallExpressionSyntax { + public expression: ILeftHandSideExpressionSyntax; + public openBracketToken: ISyntaxToken; + public argumentExpression: IExpressionSyntax; + public closeBracketToken: ISyntaxToken; + public _memberExpressionBrand: any; public _callExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, expression: ILeftHandSideExpressionSyntax, openBracketToken: ISyntaxToken, argumentExpression: IExpressionSyntax, closeBracketToken: ISyntaxToken) { + super(data); + this.expression = expression, + this.argumentExpression = argumentExpression, + expression.parent = this, + argumentExpression.parent = this; + } + } + export class FunctionExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public functionKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.identifier = identifier, + this.callSignature = callSignature, + this.block = block, + identifier && (identifier.parent = this), + callSignature.parent = this, + block.parent = this; + } + } + export class OmittedExpressionSyntax extends SyntaxNode implements IExpressionSyntax { + public _expressionBrand: any; + constructor(data: number) { + super(data); + } + } + export class VariableDeclarationSyntax extends SyntaxNode { + public varKeyword: ISyntaxToken; + public variableDeclarators: VariableDeclaratorSyntax[]; + constructor(data: number, varKeyword: ISyntaxToken, variableDeclarators: VariableDeclaratorSyntax[]) { + super(data); + this.varKeyword = varKeyword, + this.variableDeclarators = variableDeclarators, + varKeyword.parent = this, + !isShared(variableDeclarators) && (variableDeclarators.parent = this); + } + } + export class VariableDeclaratorSyntax extends SyntaxNode { + public propertyName: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public equalsValueClause: EqualsValueClauseSyntax; + constructor(data: number, propertyName: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, equalsValueClause: EqualsValueClauseSyntax) { + super(data); + this.propertyName = propertyName, + this.typeAnnotation = typeAnnotation, + this.equalsValueClause = equalsValueClause, + propertyName.parent = this, + typeAnnotation && (typeAnnotation.parent = this), + equalsValueClause && (equalsValueClause.parent = this); + } + } + export class ArgumentListSyntax extends SyntaxNode { + public typeArgumentList: TypeArgumentListSyntax; + public openParenToken: ISyntaxToken; + public arguments: IExpressionSyntax[]; + public closeParenToken: ISyntaxToken; + constructor(data: number, typeArgumentList: TypeArgumentListSyntax, openParenToken: ISyntaxToken, _arguments: IExpressionSyntax[], closeParenToken: ISyntaxToken) { + super(data); + this.typeArgumentList = typeArgumentList, + this.arguments = _arguments, + typeArgumentList && (typeArgumentList.parent = this), + !isShared(_arguments) && (_arguments.parent = this); + } + } + export class ParameterListSyntax extends SyntaxNode { + public openParenToken: ISyntaxToken; + public parameters: ParameterSyntax[]; + public closeParenToken: ISyntaxToken; + constructor(data: number, openParenToken: ISyntaxToken, parameters: ParameterSyntax[], closeParenToken: ISyntaxToken) { + super(data); + this.parameters = parameters, + !isShared(parameters) && (parameters.parent = this); + } + } + export class TypeArgumentListSyntax extends SyntaxNode { + public lessThanToken: ISyntaxToken; + public typeArguments: ITypeSyntax[]; + public greaterThanToken: ISyntaxToken; + constructor(data: number, lessThanToken: ISyntaxToken, typeArguments: ITypeSyntax[], greaterThanToken: ISyntaxToken) { + super(data); + this.lessThanToken = lessThanToken, + this.typeArguments = typeArguments, + lessThanToken.parent = this, + !isShared(typeArguments) && (typeArguments.parent = this); + } + } + export class TypeParameterListSyntax extends SyntaxNode { + public lessThanToken: ISyntaxToken; + public typeParameters: TypeParameterSyntax[]; + public greaterThanToken: ISyntaxToken; + constructor(data: number, lessThanToken: ISyntaxToken, typeParameters: TypeParameterSyntax[], greaterThanToken: ISyntaxToken) { + super(data); + this.lessThanToken = lessThanToken, + this.typeParameters = typeParameters, + lessThanToken.parent = this, + !isShared(typeParameters) && (typeParameters.parent = this); + } + } + export class HeritageClauseSyntax extends SyntaxNode { + public extendsOrImplementsKeyword: ISyntaxToken; + public typeNames: INameSyntax[]; + constructor(data: number, extendsOrImplementsKeyword: ISyntaxToken, typeNames: INameSyntax[]) { + super(data); + this.extendsOrImplementsKeyword = extendsOrImplementsKeyword, + this.typeNames = typeNames, + extendsOrImplementsKeyword.parent = this, + !isShared(typeNames) && (typeNames.parent = this); + } + public kind(): SyntaxKind { return this.extendsOrImplementsKeyword.kind() === SyntaxKind.ExtendsKeyword ? SyntaxKind.ExtendsHeritageClause : SyntaxKind.ImplementsHeritageClause; } + } + export class EqualsValueClauseSyntax extends SyntaxNode { + public equalsToken: ISyntaxToken; + public value: IExpressionSyntax; + constructor(data: number, equalsToken: ISyntaxToken, value: IExpressionSyntax) { + super(data); + this.value = value, + value.parent = this; + } + } + export class CaseSwitchClauseSyntax extends SyntaxNode implements ISwitchClauseSyntax { + public caseKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public colonToken: ISyntaxToken; + public statements: IStatementSyntax[]; + public _switchClauseBrand: any; + constructor(data: number, caseKeyword: ISyntaxToken, expression: IExpressionSyntax, colonToken: ISyntaxToken, statements: IStatementSyntax[]) { + super(data); + this.expression = expression, + this.statements = statements, + expression.parent = this, + !isShared(statements) && (statements.parent = this); + } + } + export class DefaultSwitchClauseSyntax extends SyntaxNode implements ISwitchClauseSyntax { + public defaultKeyword: ISyntaxToken; + public colonToken: ISyntaxToken; + public statements: IStatementSyntax[]; + public _switchClauseBrand: any; + constructor(data: number, defaultKeyword: ISyntaxToken, colonToken: ISyntaxToken, statements: IStatementSyntax[]) { + super(data); + this.statements = statements, + !isShared(statements) && (statements.parent = this); + } + } + export class ElseClauseSyntax extends SyntaxNode { + public elseKeyword: ISyntaxToken; + public statement: IStatementSyntax; + constructor(data: number, elseKeyword: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.statement = statement, + statement.parent = this; + } + } + export class CatchClauseSyntax extends SyntaxNode { + public catchKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public identifier: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public closeParenToken: ISyntaxToken; + public block: BlockSyntax; + constructor(data: number, catchKeyword: ISyntaxToken, openParenToken: ISyntaxToken, identifier: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, closeParenToken: ISyntaxToken, block: BlockSyntax) { + super(data); + this.identifier = identifier, + this.typeAnnotation = typeAnnotation, + this.block = block, + identifier.parent = this, + typeAnnotation && (typeAnnotation.parent = this), + block.parent = this; + } + } + export class FinallyClauseSyntax extends SyntaxNode { + public finallyKeyword: ISyntaxToken; + public block: BlockSyntax; + constructor(data: number, finallyKeyword: ISyntaxToken, block: BlockSyntax) { + super(data); + this.block = block, + block.parent = this; + } + } + export class TypeParameterSyntax extends SyntaxNode { + public identifier: ISyntaxToken; + public constraint: ConstraintSyntax; + constructor(data: number, identifier: ISyntaxToken, constraint: ConstraintSyntax) { + super(data); + this.identifier = identifier, + this.constraint = constraint, + identifier.parent = this, + constraint && (constraint.parent = this); + } + } + export class ConstraintSyntax extends SyntaxNode { + public extendsKeyword: ISyntaxToken; + public typeOrExpression: ISyntaxNodeOrToken; + constructor(data: number, extendsKeyword: ISyntaxToken, typeOrExpression: ISyntaxNodeOrToken) { + super(data); + this.typeOrExpression = typeOrExpression, + typeOrExpression.parent = this; + } + } + export class SimplePropertyAssignmentSyntax extends SyntaxNode implements IPropertyAssignmentSyntax { + public propertyName: ISyntaxToken; + public colonToken: ISyntaxToken; + public expression: IExpressionSyntax; + public _propertyAssignmentBrand: any; + constructor(data: number, propertyName: ISyntaxToken, colonToken: ISyntaxToken, expression: IExpressionSyntax) { + super(data); + this.propertyName = propertyName, + this.expression = expression, + propertyName.parent = this, + expression.parent = this; + } + } + export class FunctionPropertyAssignmentSyntax extends SyntaxNode implements IPropertyAssignmentSyntax { + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _propertyAssignmentBrand: any; + constructor(data: number, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + propertyName.parent = this, + callSignature.parent = this, + block.parent = this; + } + } + export class ParameterSyntax extends SyntaxNode { + public dotDotDotToken: ISyntaxToken; + public modifiers: ISyntaxToken[]; + public identifier: ISyntaxToken; + public questionToken: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public equalsValueClause: EqualsValueClauseSyntax; + constructor(data: number, dotDotDotToken: ISyntaxToken, modifiers: ISyntaxToken[], identifier: ISyntaxToken, questionToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, equalsValueClause: EqualsValueClauseSyntax) { + super(data); + this.dotDotDotToken = dotDotDotToken, + this.modifiers = modifiers, + this.identifier = identifier, + this.questionToken = questionToken, + this.typeAnnotation = typeAnnotation, + this.equalsValueClause = equalsValueClause, + dotDotDotToken && (dotDotDotToken.parent = this), + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + questionToken && (questionToken.parent = this), + typeAnnotation && (typeAnnotation.parent = this), + equalsValueClause && (equalsValueClause.parent = this); + } + } + export class EnumElementSyntax extends SyntaxNode { + public propertyName: ISyntaxToken; + public equalsValueClause: EqualsValueClauseSyntax; + constructor(data: number, propertyName: ISyntaxToken, equalsValueClause: EqualsValueClauseSyntax) { + super(data); + this.propertyName = propertyName, + this.equalsValueClause = equalsValueClause, + propertyName.parent = this, + equalsValueClause && (equalsValueClause.parent = this); + } + } + export class TypeAnnotationSyntax extends SyntaxNode { + public colonToken: ISyntaxToken; + public type: ITypeSyntax; + constructor(data: number, colonToken: ISyntaxToken, type: ITypeSyntax) { + super(data); + this.type = type, + type.parent = this; + } + } + export class ExternalModuleReferenceSyntax extends SyntaxNode implements IModuleReferenceSyntax { + public requireKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public stringLiteral: ISyntaxToken; + public closeParenToken: ISyntaxToken; + public _moduleReferenceBrand: any; + constructor(data: number, requireKeyword: ISyntaxToken, openParenToken: ISyntaxToken, stringLiteral: ISyntaxToken, closeParenToken: ISyntaxToken) { + super(data); + this.stringLiteral = stringLiteral, + stringLiteral.parent = this; + } + } + export class ModuleNameModuleReferenceSyntax extends SyntaxNode implements IModuleReferenceSyntax { + public moduleName: INameSyntax; + public _moduleReferenceBrand: any; + constructor(data: number, moduleName: INameSyntax) { + super(data); + this.moduleName = moduleName, + moduleName.parent = this; + } + } + + (SourceUnitSyntax).prototype.__kind = SyntaxKind.SourceUnit, (QualifiedNameSyntax).prototype.__kind = SyntaxKind.QualifiedName, (ObjectTypeSyntax).prototype.__kind = SyntaxKind.ObjectType, (FunctionTypeSyntax).prototype.__kind = SyntaxKind.FunctionType, (ArrayTypeSyntax).prototype.__kind = SyntaxKind.ArrayType, (ConstructorTypeSyntax).prototype.__kind = SyntaxKind.ConstructorType, (GenericTypeSyntax).prototype.__kind = SyntaxKind.GenericType, (TypeQuerySyntax).prototype.__kind = SyntaxKind.TypeQuery, (InterfaceDeclarationSyntax).prototype.__kind = SyntaxKind.InterfaceDeclaration, (FunctionDeclarationSyntax).prototype.__kind = SyntaxKind.FunctionDeclaration, (ModuleDeclarationSyntax).prototype.__kind = SyntaxKind.ModuleDeclaration, (ClassDeclarationSyntax).prototype.__kind = SyntaxKind.ClassDeclaration, (EnumDeclarationSyntax).prototype.__kind = SyntaxKind.EnumDeclaration, (ImportDeclarationSyntax).prototype.__kind = SyntaxKind.ImportDeclaration, (ExportAssignmentSyntax).prototype.__kind = SyntaxKind.ExportAssignment, (MemberFunctionDeclarationSyntax).prototype.__kind = SyntaxKind.MemberFunctionDeclaration, (MemberVariableDeclarationSyntax).prototype.__kind = SyntaxKind.MemberVariableDeclaration, (ConstructorDeclarationSyntax).prototype.__kind = SyntaxKind.ConstructorDeclaration, (IndexMemberDeclarationSyntax).prototype.__kind = SyntaxKind.IndexMemberDeclaration, (GetAccessorSyntax).prototype.__kind = SyntaxKind.GetAccessor, (SetAccessorSyntax).prototype.__kind = SyntaxKind.SetAccessor, (PropertySignatureSyntax).prototype.__kind = SyntaxKind.PropertySignature, (CallSignatureSyntax).prototype.__kind = SyntaxKind.CallSignature, (ConstructSignatureSyntax).prototype.__kind = SyntaxKind.ConstructSignature, (IndexSignatureSyntax).prototype.__kind = SyntaxKind.IndexSignature, (MethodSignatureSyntax).prototype.__kind = SyntaxKind.MethodSignature, (BlockSyntax).prototype.__kind = SyntaxKind.Block, (IfStatementSyntax).prototype.__kind = SyntaxKind.IfStatement, (VariableStatementSyntax).prototype.__kind = SyntaxKind.VariableStatement, (ExpressionStatementSyntax).prototype.__kind = SyntaxKind.ExpressionStatement, (ReturnStatementSyntax).prototype.__kind = SyntaxKind.ReturnStatement, (SwitchStatementSyntax).prototype.__kind = SyntaxKind.SwitchStatement, (BreakStatementSyntax).prototype.__kind = SyntaxKind.BreakStatement, (ContinueStatementSyntax).prototype.__kind = SyntaxKind.ContinueStatement, (ForStatementSyntax).prototype.__kind = SyntaxKind.ForStatement, (ForInStatementSyntax).prototype.__kind = SyntaxKind.ForInStatement, (EmptyStatementSyntax).prototype.__kind = SyntaxKind.EmptyStatement, (ThrowStatementSyntax).prototype.__kind = SyntaxKind.ThrowStatement, (WhileStatementSyntax).prototype.__kind = SyntaxKind.WhileStatement, (TryStatementSyntax).prototype.__kind = SyntaxKind.TryStatement, (LabeledStatementSyntax).prototype.__kind = SyntaxKind.LabeledStatement, (DoStatementSyntax).prototype.__kind = SyntaxKind.DoStatement, (DebuggerStatementSyntax).prototype.__kind = SyntaxKind.DebuggerStatement, (WithStatementSyntax).prototype.__kind = SyntaxKind.WithStatement, (DeleteExpressionSyntax).prototype.__kind = SyntaxKind.DeleteExpression, (TypeOfExpressionSyntax).prototype.__kind = SyntaxKind.TypeOfExpression, (VoidExpressionSyntax).prototype.__kind = SyntaxKind.VoidExpression, (ConditionalExpressionSyntax).prototype.__kind = SyntaxKind.ConditionalExpression, (MemberAccessExpressionSyntax).prototype.__kind = SyntaxKind.MemberAccessExpression, (InvocationExpressionSyntax).prototype.__kind = SyntaxKind.InvocationExpression, (ArrayLiteralExpressionSyntax).prototype.__kind = SyntaxKind.ArrayLiteralExpression, (ObjectLiteralExpressionSyntax).prototype.__kind = SyntaxKind.ObjectLiteralExpression, (ObjectCreationExpressionSyntax).prototype.__kind = SyntaxKind.ObjectCreationExpression, (ParenthesizedExpressionSyntax).prototype.__kind = SyntaxKind.ParenthesizedExpression, (ParenthesizedArrowFunctionExpressionSyntax).prototype.__kind = SyntaxKind.ParenthesizedArrowFunctionExpression, (SimpleArrowFunctionExpressionSyntax).prototype.__kind = SyntaxKind.SimpleArrowFunctionExpression, (CastExpressionSyntax).prototype.__kind = SyntaxKind.CastExpression, (ElementAccessExpressionSyntax).prototype.__kind = SyntaxKind.ElementAccessExpression, (FunctionExpressionSyntax).prototype.__kind = SyntaxKind.FunctionExpression, (OmittedExpressionSyntax).prototype.__kind = SyntaxKind.OmittedExpression, (VariableDeclarationSyntax).prototype.__kind = SyntaxKind.VariableDeclaration, (VariableDeclaratorSyntax).prototype.__kind = SyntaxKind.VariableDeclarator, (ArgumentListSyntax).prototype.__kind = SyntaxKind.ArgumentList, (ParameterListSyntax).prototype.__kind = SyntaxKind.ParameterList, (TypeArgumentListSyntax).prototype.__kind = SyntaxKind.TypeArgumentList, (TypeParameterListSyntax).prototype.__kind = SyntaxKind.TypeParameterList, (EqualsValueClauseSyntax).prototype.__kind = SyntaxKind.EqualsValueClause, (CaseSwitchClauseSyntax).prototype.__kind = SyntaxKind.CaseSwitchClause, (DefaultSwitchClauseSyntax).prototype.__kind = SyntaxKind.DefaultSwitchClause, (ElseClauseSyntax).prototype.__kind = SyntaxKind.ElseClause, (CatchClauseSyntax).prototype.__kind = SyntaxKind.CatchClause, (FinallyClauseSyntax).prototype.__kind = SyntaxKind.FinallyClause, (TypeParameterSyntax).prototype.__kind = SyntaxKind.TypeParameter, (ConstraintSyntax).prototype.__kind = SyntaxKind.Constraint, (SimplePropertyAssignmentSyntax).prototype.__kind = SyntaxKind.SimplePropertyAssignment, (FunctionPropertyAssignmentSyntax).prototype.__kind = SyntaxKind.FunctionPropertyAssignment, (ParameterSyntax).prototype.__kind = SyntaxKind.Parameter, (EnumElementSyntax).prototype.__kind = SyntaxKind.EnumElement, (TypeAnnotationSyntax).prototype.__kind = SyntaxKind.TypeAnnotation, (ExternalModuleReferenceSyntax).prototype.__kind = SyntaxKind.ExternalModuleReference, (ModuleNameModuleReferenceSyntax).prototype.__kind = SyntaxKind.ModuleNameModuleReference; +} \ No newline at end of file diff --git a/src/services/syntax/syntaxNodes.concrete.generated.ts b/src/services/syntax/syntaxNodes.concrete.generated.ts new file mode 100644 index 00000000000..cc493aa53a6 --- /dev/null +++ b/src/services/syntax/syntaxNodes.concrete.generated.ts @@ -0,0 +1,1424 @@ +/// + +module TypeScript.Syntax.Concrete { + // Inject this module as the factory for producing syntax nodes in the parser. + Parser.syntaxFactory = Concrete; + export var isConcrete: boolean = true; + + export class SourceUnitSyntax extends SyntaxNode { + public syntaxTree: SyntaxTree = null; + public moduleElements: IModuleElementSyntax[]; + public endOfFileToken: ISyntaxToken; + constructor(data: number, moduleElements: IModuleElementSyntax[], endOfFileToken: ISyntaxToken) { + super(data); + this.parent = null, + this.moduleElements = moduleElements, + this.endOfFileToken = endOfFileToken, + !isShared(moduleElements) && (moduleElements.parent = this), + endOfFileToken.parent = this; + } + } + export class QualifiedNameSyntax extends SyntaxNode implements INameSyntax { + public left: INameSyntax; + public dotToken: ISyntaxToken; + public right: ISyntaxToken; + public _nameBrand: any; public _typeBrand: any; + constructor(data: number, left: INameSyntax, dotToken: ISyntaxToken, right: ISyntaxToken) { + super(data); + this.left = left, + this.dotToken = dotToken, + this.right = right, + left.parent = this, + dotToken.parent = this, + right.parent = this; + } + } + export class ObjectTypeSyntax extends SyntaxNode implements ITypeSyntax { + public openBraceToken: ISyntaxToken; + public typeMembers: ITypeMemberSyntax[]; + public closeBraceToken: ISyntaxToken; + public _typeBrand: any; + constructor(data: number, openBraceToken: ISyntaxToken, typeMembers: ITypeMemberSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.openBraceToken = openBraceToken, + this.typeMembers = typeMembers, + this.closeBraceToken = closeBraceToken, + openBraceToken.parent = this, + !isShared(typeMembers) && (typeMembers.parent = this), + closeBraceToken.parent = this; + } + } + export class FunctionTypeSyntax extends SyntaxNode implements ITypeSyntax { + public typeParameterList: TypeParameterListSyntax; + public parameterList: ParameterListSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public type: ITypeSyntax; + public _typeBrand: any; + constructor(data: number, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, equalsGreaterThanToken: ISyntaxToken, type: ITypeSyntax) { + super(data); + this.typeParameterList = typeParameterList, + this.parameterList = parameterList, + this.equalsGreaterThanToken = equalsGreaterThanToken, + this.type = type, + typeParameterList && (typeParameterList.parent = this), + parameterList.parent = this, + equalsGreaterThanToken.parent = this, + type.parent = this; + } + } + export class ArrayTypeSyntax extends SyntaxNode implements ITypeSyntax { + public type: ITypeSyntax; + public openBracketToken: ISyntaxToken; + public closeBracketToken: ISyntaxToken; + public _typeBrand: any; + constructor(data: number, type: ITypeSyntax, openBracketToken: ISyntaxToken, closeBracketToken: ISyntaxToken) { + super(data); + this.type = type, + this.openBracketToken = openBracketToken, + this.closeBracketToken = closeBracketToken, + type.parent = this, + openBracketToken.parent = this, + closeBracketToken.parent = this; + } + } + export class ConstructorTypeSyntax extends SyntaxNode implements ITypeSyntax { + public newKeyword: ISyntaxToken; + public typeParameterList: TypeParameterListSyntax; + public parameterList: ParameterListSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public type: ITypeSyntax; + public _typeBrand: any; + constructor(data: number, newKeyword: ISyntaxToken, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, equalsGreaterThanToken: ISyntaxToken, type: ITypeSyntax) { + super(data); + this.newKeyword = newKeyword, + this.typeParameterList = typeParameterList, + this.parameterList = parameterList, + this.equalsGreaterThanToken = equalsGreaterThanToken, + this.type = type, + newKeyword.parent = this, + typeParameterList && (typeParameterList.parent = this), + parameterList.parent = this, + equalsGreaterThanToken.parent = this, + type.parent = this; + } + } + export class GenericTypeSyntax extends SyntaxNode implements ITypeSyntax { + public name: INameSyntax; + public typeArgumentList: TypeArgumentListSyntax; + public _typeBrand: any; + constructor(data: number, name: INameSyntax, typeArgumentList: TypeArgumentListSyntax) { + super(data); + this.name = name, + this.typeArgumentList = typeArgumentList, + name.parent = this, + typeArgumentList.parent = this; + } + } + export class TypeQuerySyntax extends SyntaxNode implements ITypeSyntax { + public typeOfKeyword: ISyntaxToken; + public name: INameSyntax; + public _typeBrand: any; + constructor(data: number, typeOfKeyword: ISyntaxToken, name: INameSyntax) { + super(data); + this.typeOfKeyword = typeOfKeyword, + this.name = name, + typeOfKeyword.parent = this, + name.parent = this; + } + } + export class InterfaceDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public interfaceKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public typeParameterList: TypeParameterListSyntax; + public heritageClauses: HeritageClauseSyntax[]; + public body: ObjectTypeSyntax; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], interfaceKeyword: ISyntaxToken, identifier: ISyntaxToken, typeParameterList: TypeParameterListSyntax, heritageClauses: HeritageClauseSyntax[], body: ObjectTypeSyntax) { + super(data); + this.modifiers = modifiers, + this.interfaceKeyword = interfaceKeyword, + this.identifier = identifier, + this.typeParameterList = typeParameterList, + this.heritageClauses = heritageClauses, + this.body = body, + !isShared(modifiers) && (modifiers.parent = this), + interfaceKeyword.parent = this, + identifier.parent = this, + typeParameterList && (typeParameterList.parent = this), + !isShared(heritageClauses) && (heritageClauses.parent = this), + body.parent = this; + } + } + export class FunctionDeclarationSyntax extends SyntaxNode implements IStatementSyntax { + public modifiers: ISyntaxToken[]; + public functionKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.functionKeyword = functionKeyword, + this.identifier = identifier, + this.callSignature = callSignature, + this.block = block, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + functionKeyword.parent = this, + identifier.parent = this, + callSignature.parent = this, + block && (block.parent = this), + semicolonToken && (semicolonToken.parent = this); + } + } + export class ModuleDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public moduleKeyword: ISyntaxToken; + public name: INameSyntax; + public stringLiteral: ISyntaxToken; + public openBraceToken: ISyntaxToken; + public moduleElements: IModuleElementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], moduleKeyword: ISyntaxToken, name: INameSyntax, stringLiteral: ISyntaxToken, openBraceToken: ISyntaxToken, moduleElements: IModuleElementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.moduleKeyword = moduleKeyword, + this.name = name, + this.stringLiteral = stringLiteral, + this.openBraceToken = openBraceToken, + this.moduleElements = moduleElements, + this.closeBraceToken = closeBraceToken, + !isShared(modifiers) && (modifiers.parent = this), + moduleKeyword.parent = this, + name && (name.parent = this), + stringLiteral && (stringLiteral.parent = this), + openBraceToken.parent = this, + !isShared(moduleElements) && (moduleElements.parent = this), + closeBraceToken.parent = this; + } + } + export class ClassDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public classKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public typeParameterList: TypeParameterListSyntax; + public heritageClauses: HeritageClauseSyntax[]; + public openBraceToken: ISyntaxToken; + public classElements: IClassElementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], classKeyword: ISyntaxToken, identifier: ISyntaxToken, typeParameterList: TypeParameterListSyntax, heritageClauses: HeritageClauseSyntax[], openBraceToken: ISyntaxToken, classElements: IClassElementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.classKeyword = classKeyword, + this.identifier = identifier, + this.typeParameterList = typeParameterList, + this.heritageClauses = heritageClauses, + this.openBraceToken = openBraceToken, + this.classElements = classElements, + this.closeBraceToken = closeBraceToken, + !isShared(modifiers) && (modifiers.parent = this), + classKeyword.parent = this, + identifier.parent = this, + typeParameterList && (typeParameterList.parent = this), + !isShared(heritageClauses) && (heritageClauses.parent = this), + openBraceToken.parent = this, + !isShared(classElements) && (classElements.parent = this), + closeBraceToken.parent = this; + } + } + export class EnumDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public enumKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public openBraceToken: ISyntaxToken; + public enumElements: EnumElementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], enumKeyword: ISyntaxToken, identifier: ISyntaxToken, openBraceToken: ISyntaxToken, enumElements: EnumElementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.enumKeyword = enumKeyword, + this.identifier = identifier, + this.openBraceToken = openBraceToken, + this.enumElements = enumElements, + this.closeBraceToken = closeBraceToken, + !isShared(modifiers) && (modifiers.parent = this), + enumKeyword.parent = this, + identifier.parent = this, + openBraceToken.parent = this, + !isShared(enumElements) && (enumElements.parent = this), + closeBraceToken.parent = this; + } + } + export class ImportDeclarationSyntax extends SyntaxNode implements IModuleElementSyntax { + public modifiers: ISyntaxToken[]; + public importKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public equalsToken: ISyntaxToken; + public moduleReference: IModuleReferenceSyntax; + public semicolonToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], importKeyword: ISyntaxToken, identifier: ISyntaxToken, equalsToken: ISyntaxToken, moduleReference: IModuleReferenceSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.importKeyword = importKeyword, + this.identifier = identifier, + this.equalsToken = equalsToken, + this.moduleReference = moduleReference, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + importKeyword.parent = this, + identifier.parent = this, + equalsToken.parent = this, + moduleReference.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class ExportAssignmentSyntax extends SyntaxNode implements IModuleElementSyntax { + public exportKeyword: ISyntaxToken; + public equalsToken: ISyntaxToken; + public identifier: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _moduleElementBrand: any; + constructor(data: number, exportKeyword: ISyntaxToken, equalsToken: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.exportKeyword = exportKeyword, + this.equalsToken = equalsToken, + this.identifier = identifier, + this.semicolonToken = semicolonToken, + exportKeyword.parent = this, + equalsToken.parent = this, + identifier.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class MemberFunctionDeclarationSyntax extends SyntaxNode implements IMemberDeclarationSyntax { + public modifiers: ISyntaxToken[]; + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public semicolonToken: ISyntaxToken; + public _memberDeclarationBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + propertyName.parent = this, + callSignature.parent = this, + block && (block.parent = this), + semicolonToken && (semicolonToken.parent = this); + } + } + export class MemberVariableDeclarationSyntax extends SyntaxNode implements IMemberDeclarationSyntax { + public modifiers: ISyntaxToken[]; + public variableDeclarator: VariableDeclaratorSyntax; + public semicolonToken: ISyntaxToken; + public _memberDeclarationBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], variableDeclarator: VariableDeclaratorSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.variableDeclarator = variableDeclarator, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + variableDeclarator.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class ConstructorDeclarationSyntax extends SyntaxNode implements IClassElementSyntax { + public modifiers: ISyntaxToken[]; + public constructorKeyword: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public semicolonToken: ISyntaxToken; + public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], constructorKeyword: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.constructorKeyword = constructorKeyword, + this.callSignature = callSignature, + this.block = block, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + constructorKeyword.parent = this, + callSignature.parent = this, + block && (block.parent = this), + semicolonToken && (semicolonToken.parent = this); + } + } + export class IndexMemberDeclarationSyntax extends SyntaxNode implements IClassElementSyntax { + public modifiers: ISyntaxToken[]; + public indexSignature: IndexSignatureSyntax; + public semicolonToken: ISyntaxToken; + public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], indexSignature: IndexSignatureSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.indexSignature = indexSignature, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + indexSignature.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class GetAccessorSyntax extends SyntaxNode implements IMemberDeclarationSyntax, IPropertyAssignmentSyntax { + public modifiers: ISyntaxToken[]; + public getKeyword: ISyntaxToken; + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _memberDeclarationBrand: any; public _propertyAssignmentBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], getKeyword: ISyntaxToken, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.modifiers = modifiers, + this.getKeyword = getKeyword, + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + getKeyword.parent = this, + propertyName.parent = this, + callSignature.parent = this, + block.parent = this; + } + } + export class SetAccessorSyntax extends SyntaxNode implements IMemberDeclarationSyntax, IPropertyAssignmentSyntax { + public modifiers: ISyntaxToken[]; + public setKeyword: ISyntaxToken; + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _memberDeclarationBrand: any; public _propertyAssignmentBrand: any; public _classElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], setKeyword: ISyntaxToken, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.modifiers = modifiers, + this.setKeyword = setKeyword, + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + !isShared(modifiers) && (modifiers.parent = this), + setKeyword.parent = this, + propertyName.parent = this, + callSignature.parent = this, + block.parent = this; + } + } + export class PropertySignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public propertyName: ISyntaxToken; + public questionToken: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public _typeMemberBrand: any; + constructor(data: number, propertyName: ISyntaxToken, questionToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax) { + super(data); + this.propertyName = propertyName, + this.questionToken = questionToken, + this.typeAnnotation = typeAnnotation, + propertyName.parent = this, + questionToken && (questionToken.parent = this), + typeAnnotation && (typeAnnotation.parent = this); + } + } + export class CallSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public typeParameterList: TypeParameterListSyntax; + public parameterList: ParameterListSyntax; + public typeAnnotation: TypeAnnotationSyntax; + public _typeMemberBrand: any; + constructor(data: number, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, typeAnnotation: TypeAnnotationSyntax) { + super(data); + this.typeParameterList = typeParameterList, + this.parameterList = parameterList, + this.typeAnnotation = typeAnnotation, + typeParameterList && (typeParameterList.parent = this), + parameterList.parent = this, + typeAnnotation && (typeAnnotation.parent = this); + } + } + export class ConstructSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public newKeyword: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public _typeMemberBrand: any; + constructor(data: number, newKeyword: ISyntaxToken, callSignature: CallSignatureSyntax) { + super(data); + this.newKeyword = newKeyword, + this.callSignature = callSignature, + newKeyword.parent = this, + callSignature.parent = this; + } + } + export class IndexSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public openBracketToken: ISyntaxToken; + public parameters: ParameterSyntax[]; + public closeBracketToken: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public _typeMemberBrand: any; + constructor(data: number, openBracketToken: ISyntaxToken, parameters: ParameterSyntax[], closeBracketToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax) { + super(data); + this.openBracketToken = openBracketToken, + this.parameters = parameters, + this.closeBracketToken = closeBracketToken, + this.typeAnnotation = typeAnnotation, + openBracketToken.parent = this, + !isShared(parameters) && (parameters.parent = this), + closeBracketToken.parent = this, + typeAnnotation && (typeAnnotation.parent = this); + } + } + export class MethodSignatureSyntax extends SyntaxNode implements ITypeMemberSyntax { + public propertyName: ISyntaxToken; + public questionToken: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public _typeMemberBrand: any; + constructor(data: number, propertyName: ISyntaxToken, questionToken: ISyntaxToken, callSignature: CallSignatureSyntax) { + super(data); + this.propertyName = propertyName, + this.questionToken = questionToken, + this.callSignature = callSignature, + propertyName.parent = this, + questionToken && (questionToken.parent = this), + callSignature.parent = this; + } + } + export class BlockSyntax extends SyntaxNode implements IStatementSyntax { + public openBraceToken: ISyntaxToken; + public statements: IStatementSyntax[]; + public closeBraceToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, openBraceToken: ISyntaxToken, statements: IStatementSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.openBraceToken = openBraceToken, + this.statements = statements, + this.closeBraceToken = closeBraceToken, + openBraceToken.parent = this, + !isShared(statements) && (statements.parent = this), + closeBraceToken.parent = this; + } + } + export class IfStatementSyntax extends SyntaxNode implements IStatementSyntax { + public ifKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public elseClause: ElseClauseSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, ifKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax, elseClause: ElseClauseSyntax) { + super(data); + this.ifKeyword = ifKeyword, + this.openParenToken = openParenToken, + this.condition = condition, + this.closeParenToken = closeParenToken, + this.statement = statement, + this.elseClause = elseClause, + ifKeyword.parent = this, + openParenToken.parent = this, + condition.parent = this, + closeParenToken.parent = this, + statement.parent = this, + elseClause && (elseClause.parent = this); + } + } + export class VariableStatementSyntax extends SyntaxNode implements IStatementSyntax { + public modifiers: ISyntaxToken[]; + public variableDeclaration: VariableDeclarationSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, modifiers: ISyntaxToken[], variableDeclaration: VariableDeclarationSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.modifiers = modifiers, + this.variableDeclaration = variableDeclaration, + this.semicolonToken = semicolonToken, + !isShared(modifiers) && (modifiers.parent = this), + variableDeclaration.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class ExpressionStatementSyntax extends SyntaxNode implements IStatementSyntax { + public expression: IExpressionSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, expression: IExpressionSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.expression = expression, + this.semicolonToken = semicolonToken, + expression.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class ReturnStatementSyntax extends SyntaxNode implements IStatementSyntax { + public returnKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, returnKeyword: ISyntaxToken, expression: IExpressionSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.returnKeyword = returnKeyword, + this.expression = expression, + this.semicolonToken = semicolonToken, + returnKeyword.parent = this, + expression && (expression.parent = this), + semicolonToken && (semicolonToken.parent = this); + } + } + export class SwitchStatementSyntax extends SyntaxNode implements IStatementSyntax { + public switchKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public expression: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public openBraceToken: ISyntaxToken; + public switchClauses: ISwitchClauseSyntax[]; + public closeBraceToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, switchKeyword: ISyntaxToken, openParenToken: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, openBraceToken: ISyntaxToken, switchClauses: ISwitchClauseSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.switchKeyword = switchKeyword, + this.openParenToken = openParenToken, + this.expression = expression, + this.closeParenToken = closeParenToken, + this.openBraceToken = openBraceToken, + this.switchClauses = switchClauses, + this.closeBraceToken = closeBraceToken, + switchKeyword.parent = this, + openParenToken.parent = this, + expression.parent = this, + closeParenToken.parent = this, + openBraceToken.parent = this, + !isShared(switchClauses) && (switchClauses.parent = this), + closeBraceToken.parent = this; + } + } + export class BreakStatementSyntax extends SyntaxNode implements IStatementSyntax { + public breakKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, breakKeyword: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.breakKeyword = breakKeyword, + this.identifier = identifier, + this.semicolonToken = semicolonToken, + breakKeyword.parent = this, + identifier && (identifier.parent = this), + semicolonToken && (semicolonToken.parent = this); + } + } + export class ContinueStatementSyntax extends SyntaxNode implements IStatementSyntax { + public continueKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, continueKeyword: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.continueKeyword = continueKeyword, + this.identifier = identifier, + this.semicolonToken = semicolonToken, + continueKeyword.parent = this, + identifier && (identifier.parent = this), + semicolonToken && (semicolonToken.parent = this); + } + } + export class ForStatementSyntax extends SyntaxNode implements IStatementSyntax { + public forKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public variableDeclaration: VariableDeclarationSyntax; + public initializer: IExpressionSyntax; + public firstSemicolonToken: ISyntaxToken; + public condition: IExpressionSyntax; + public secondSemicolonToken: ISyntaxToken; + public incrementor: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.forKeyword = forKeyword, + this.openParenToken = openParenToken, + this.variableDeclaration = variableDeclaration, + this.initializer = initializer, + this.firstSemicolonToken = firstSemicolonToken, + this.condition = condition, + this.secondSemicolonToken = secondSemicolonToken, + this.incrementor = incrementor, + this.closeParenToken = closeParenToken, + this.statement = statement, + forKeyword.parent = this, + openParenToken.parent = this, + variableDeclaration && (variableDeclaration.parent = this), + initializer && (initializer.parent = this), + firstSemicolonToken.parent = this, + condition && (condition.parent = this), + secondSemicolonToken.parent = this, + incrementor && (incrementor.parent = this), + closeParenToken.parent = this, + statement.parent = this; + } + } + export class ForInStatementSyntax extends SyntaxNode implements IStatementSyntax { + public forKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public variableDeclaration: VariableDeclarationSyntax; + public left: IExpressionSyntax; + public inKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, left: IExpressionSyntax, inKeyword: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.forKeyword = forKeyword, + this.openParenToken = openParenToken, + this.variableDeclaration = variableDeclaration, + this.left = left, + this.inKeyword = inKeyword, + this.expression = expression, + this.closeParenToken = closeParenToken, + this.statement = statement, + forKeyword.parent = this, + openParenToken.parent = this, + variableDeclaration && (variableDeclaration.parent = this), + left && (left.parent = this), + inKeyword.parent = this, + expression.parent = this, + closeParenToken.parent = this, + statement.parent = this; + } + } + export class EmptyStatementSyntax extends SyntaxNode implements IStatementSyntax { + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, semicolonToken: ISyntaxToken) { + super(data); + this.semicolonToken = semicolonToken, + semicolonToken.parent = this; + } + } + export class ThrowStatementSyntax extends SyntaxNode implements IStatementSyntax { + public throwKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, throwKeyword: ISyntaxToken, expression: IExpressionSyntax, semicolonToken: ISyntaxToken) { + super(data); + this.throwKeyword = throwKeyword, + this.expression = expression, + this.semicolonToken = semicolonToken, + throwKeyword.parent = this, + expression.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class WhileStatementSyntax extends SyntaxNode implements IStatementSyntax { + public whileKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, whileKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.whileKeyword = whileKeyword, + this.openParenToken = openParenToken, + this.condition = condition, + this.closeParenToken = closeParenToken, + this.statement = statement, + whileKeyword.parent = this, + openParenToken.parent = this, + condition.parent = this, + closeParenToken.parent = this, + statement.parent = this; + } + } + export class TryStatementSyntax extends SyntaxNode implements IStatementSyntax { + public tryKeyword: ISyntaxToken; + public block: BlockSyntax; + public catchClause: CatchClauseSyntax; + public finallyClause: FinallyClauseSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, tryKeyword: ISyntaxToken, block: BlockSyntax, catchClause: CatchClauseSyntax, finallyClause: FinallyClauseSyntax) { + super(data); + this.tryKeyword = tryKeyword, + this.block = block, + this.catchClause = catchClause, + this.finallyClause = finallyClause, + tryKeyword.parent = this, + block.parent = this, + catchClause && (catchClause.parent = this), + finallyClause && (finallyClause.parent = this); + } + } + export class LabeledStatementSyntax extends SyntaxNode implements IStatementSyntax { + public identifier: ISyntaxToken; + public colonToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, identifier: ISyntaxToken, colonToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.identifier = identifier, + this.colonToken = colonToken, + this.statement = statement, + identifier.parent = this, + colonToken.parent = this, + statement.parent = this; + } + } + export class DoStatementSyntax extends SyntaxNode implements IStatementSyntax { + public doKeyword: ISyntaxToken; + public statement: IStatementSyntax; + public whileKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, doKeyword: ISyntaxToken, statement: IStatementSyntax, whileKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.doKeyword = doKeyword, + this.statement = statement, + this.whileKeyword = whileKeyword, + this.openParenToken = openParenToken, + this.condition = condition, + this.closeParenToken = closeParenToken, + this.semicolonToken = semicolonToken, + doKeyword.parent = this, + statement.parent = this, + whileKeyword.parent = this, + openParenToken.parent = this, + condition.parent = this, + closeParenToken.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class DebuggerStatementSyntax extends SyntaxNode implements IStatementSyntax { + public debuggerKeyword: ISyntaxToken; + public semicolonToken: ISyntaxToken; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, debuggerKeyword: ISyntaxToken, semicolonToken: ISyntaxToken) { + super(data); + this.debuggerKeyword = debuggerKeyword, + this.semicolonToken = semicolonToken, + debuggerKeyword.parent = this, + semicolonToken && (semicolonToken.parent = this); + } + } + export class WithStatementSyntax extends SyntaxNode implements IStatementSyntax { + public withKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public condition: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public statement: IStatementSyntax; + public _statementBrand: any; public _moduleElementBrand: any; + constructor(data: number, withKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.withKeyword = withKeyword, + this.openParenToken = openParenToken, + this.condition = condition, + this.closeParenToken = closeParenToken, + this.statement = statement, + withKeyword.parent = this, + openParenToken.parent = this, + condition.parent = this, + closeParenToken.parent = this, + statement.parent = this; + } + } + export class PrefixUnaryExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public operatorToken: ISyntaxToken; + public operand: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, operatorToken: ISyntaxToken, operand: IUnaryExpressionSyntax) { + super(data); + this.operatorToken = operatorToken, + this.operand = operand, + operatorToken.parent = this, + operand.parent = this; + } + public kind(): SyntaxKind { return SyntaxFacts.getPrefixUnaryExpressionFromOperatorToken(this.operatorToken.kind()); } + } + export class DeleteExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public deleteKeyword: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, deleteKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.deleteKeyword = deleteKeyword, + this.expression = expression, + deleteKeyword.parent = this, + expression.parent = this; + } + } + export class TypeOfExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public typeOfKeyword: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, typeOfKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.typeOfKeyword = typeOfKeyword, + this.expression = expression, + typeOfKeyword.parent = this, + expression.parent = this; + } + } + export class VoidExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public voidKeyword: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, voidKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.voidKeyword = voidKeyword, + this.expression = expression, + voidKeyword.parent = this, + expression.parent = this; + } + } + export class ConditionalExpressionSyntax extends SyntaxNode implements IExpressionSyntax { + public condition: IExpressionSyntax; + public questionToken: ISyntaxToken; + public whenTrue: IExpressionSyntax; + public colonToken: ISyntaxToken; + public whenFalse: IExpressionSyntax; + public _expressionBrand: any; + constructor(data: number, condition: IExpressionSyntax, questionToken: ISyntaxToken, whenTrue: IExpressionSyntax, colonToken: ISyntaxToken, whenFalse: IExpressionSyntax) { + super(data); + this.condition = condition, + this.questionToken = questionToken, + this.whenTrue = whenTrue, + this.colonToken = colonToken, + this.whenFalse = whenFalse, + condition.parent = this, + questionToken.parent = this, + whenTrue.parent = this, + colonToken.parent = this, + whenFalse.parent = this; + } + } + export class BinaryExpressionSyntax extends SyntaxNode implements IExpressionSyntax { + public left: IExpressionSyntax; + public operatorToken: ISyntaxToken; + public right: IExpressionSyntax; + public _expressionBrand: any; + constructor(data: number, left: IExpressionSyntax, operatorToken: ISyntaxToken, right: IExpressionSyntax) { + super(data); + this.left = left, + this.operatorToken = operatorToken, + this.right = right, + left.parent = this, + operatorToken.parent = this, + right.parent = this; + } + public kind(): SyntaxKind { return SyntaxFacts.getBinaryExpressionFromOperatorToken(this.operatorToken.kind()); } + } + export class PostfixUnaryExpressionSyntax extends SyntaxNode implements IPostfixExpressionSyntax { + public operand: ILeftHandSideExpressionSyntax; + public operatorToken: ISyntaxToken; + public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, operand: ILeftHandSideExpressionSyntax, operatorToken: ISyntaxToken) { + super(data); + this.operand = operand, + this.operatorToken = operatorToken, + operand.parent = this, + operatorToken.parent = this; + } + public kind(): SyntaxKind { return SyntaxFacts.getPostfixUnaryExpressionFromOperatorToken(this.operatorToken.kind()); } + } + export class MemberAccessExpressionSyntax extends SyntaxNode implements IMemberExpressionSyntax, ICallExpressionSyntax { + public expression: ILeftHandSideExpressionSyntax; + public dotToken: ISyntaxToken; + public name: ISyntaxToken; + public _memberExpressionBrand: any; public _callExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, expression: ILeftHandSideExpressionSyntax, dotToken: ISyntaxToken, name: ISyntaxToken) { + super(data); + this.expression = expression, + this.dotToken = dotToken, + this.name = name, + expression.parent = this, + dotToken.parent = this, + name.parent = this; + } + } + export class InvocationExpressionSyntax extends SyntaxNode implements ICallExpressionSyntax { + public expression: ILeftHandSideExpressionSyntax; + public argumentList: ArgumentListSyntax; + public _callExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, expression: ILeftHandSideExpressionSyntax, argumentList: ArgumentListSyntax) { + super(data); + this.expression = expression, + this.argumentList = argumentList, + expression.parent = this, + argumentList.parent = this; + } + } + export class ArrayLiteralExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public openBracketToken: ISyntaxToken; + public expressions: IExpressionSyntax[]; + public closeBracketToken: ISyntaxToken; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, openBracketToken: ISyntaxToken, expressions: IExpressionSyntax[], closeBracketToken: ISyntaxToken) { + super(data); + this.openBracketToken = openBracketToken, + this.expressions = expressions, + this.closeBracketToken = closeBracketToken, + openBracketToken.parent = this, + !isShared(expressions) && (expressions.parent = this), + closeBracketToken.parent = this; + } + } + export class ObjectLiteralExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public openBraceToken: ISyntaxToken; + public propertyAssignments: IPropertyAssignmentSyntax[]; + public closeBraceToken: ISyntaxToken; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, openBraceToken: ISyntaxToken, propertyAssignments: IPropertyAssignmentSyntax[], closeBraceToken: ISyntaxToken) { + super(data); + this.openBraceToken = openBraceToken, + this.propertyAssignments = propertyAssignments, + this.closeBraceToken = closeBraceToken, + openBraceToken.parent = this, + !isShared(propertyAssignments) && (propertyAssignments.parent = this), + closeBraceToken.parent = this; + } + } + export class ObjectCreationExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public newKeyword: ISyntaxToken; + public expression: IMemberExpressionSyntax; + public argumentList: ArgumentListSyntax; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, newKeyword: ISyntaxToken, expression: IMemberExpressionSyntax, argumentList: ArgumentListSyntax) { + super(data); + this.newKeyword = newKeyword, + this.expression = expression, + this.argumentList = argumentList, + newKeyword.parent = this, + expression.parent = this, + argumentList && (argumentList.parent = this); + } + } + export class ParenthesizedExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public openParenToken: ISyntaxToken; + public expression: IExpressionSyntax; + public closeParenToken: ISyntaxToken; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, openParenToken: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken) { + super(data); + this.openParenToken = openParenToken, + this.expression = expression, + this.closeParenToken = closeParenToken, + openParenToken.parent = this, + expression.parent = this, + closeParenToken.parent = this; + } + } + export class ParenthesizedArrowFunctionExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public callSignature: CallSignatureSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public block: BlockSyntax; + public expression: IExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, callSignature: CallSignatureSyntax, equalsGreaterThanToken: ISyntaxToken, block: BlockSyntax, expression: IExpressionSyntax) { + super(data); + this.callSignature = callSignature, + this.equalsGreaterThanToken = equalsGreaterThanToken, + this.block = block, + this.expression = expression, + callSignature.parent = this, + equalsGreaterThanToken.parent = this, + block && (block.parent = this), + expression && (expression.parent = this); + } + } + export class SimpleArrowFunctionExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public parameter: ParameterSyntax; + public equalsGreaterThanToken: ISyntaxToken; + public block: BlockSyntax; + public expression: IExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, parameter: ParameterSyntax, equalsGreaterThanToken: ISyntaxToken, block: BlockSyntax, expression: IExpressionSyntax) { + super(data); + this.parameter = parameter, + this.equalsGreaterThanToken = equalsGreaterThanToken, + this.block = block, + this.expression = expression, + parameter.parent = this, + equalsGreaterThanToken.parent = this, + block && (block.parent = this), + expression && (expression.parent = this); + } + } + export class CastExpressionSyntax extends SyntaxNode implements IUnaryExpressionSyntax { + public lessThanToken: ISyntaxToken; + public type: ITypeSyntax; + public greaterThanToken: ISyntaxToken; + public expression: IUnaryExpressionSyntax; + public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, lessThanToken: ISyntaxToken, type: ITypeSyntax, greaterThanToken: ISyntaxToken, expression: IUnaryExpressionSyntax) { + super(data); + this.lessThanToken = lessThanToken, + this.type = type, + this.greaterThanToken = greaterThanToken, + this.expression = expression, + lessThanToken.parent = this, + type.parent = this, + greaterThanToken.parent = this, + expression.parent = this; + } + } + export class ElementAccessExpressionSyntax extends SyntaxNode implements IMemberExpressionSyntax, ICallExpressionSyntax { + public expression: ILeftHandSideExpressionSyntax; + public openBracketToken: ISyntaxToken; + public argumentExpression: IExpressionSyntax; + public closeBracketToken: ISyntaxToken; + public _memberExpressionBrand: any; public _callExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, expression: ILeftHandSideExpressionSyntax, openBracketToken: ISyntaxToken, argumentExpression: IExpressionSyntax, closeBracketToken: ISyntaxToken) { + super(data); + this.expression = expression, + this.openBracketToken = openBracketToken, + this.argumentExpression = argumentExpression, + this.closeBracketToken = closeBracketToken, + expression.parent = this, + openBracketToken.parent = this, + argumentExpression.parent = this, + closeBracketToken.parent = this; + } + } + export class FunctionExpressionSyntax extends SyntaxNode implements IPrimaryExpressionSyntax { + public functionKeyword: ISyntaxToken; + public identifier: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; + constructor(data: number, functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.functionKeyword = functionKeyword, + this.identifier = identifier, + this.callSignature = callSignature, + this.block = block, + functionKeyword.parent = this, + identifier && (identifier.parent = this), + callSignature.parent = this, + block.parent = this; + } + } + export class OmittedExpressionSyntax extends SyntaxNode implements IExpressionSyntax { + public _expressionBrand: any; + constructor(data: number) { + super(data); + } + } + export class VariableDeclarationSyntax extends SyntaxNode { + public varKeyword: ISyntaxToken; + public variableDeclarators: VariableDeclaratorSyntax[]; + constructor(data: number, varKeyword: ISyntaxToken, variableDeclarators: VariableDeclaratorSyntax[]) { + super(data); + this.varKeyword = varKeyword, + this.variableDeclarators = variableDeclarators, + varKeyword.parent = this, + !isShared(variableDeclarators) && (variableDeclarators.parent = this); + } + } + export class VariableDeclaratorSyntax extends SyntaxNode { + public propertyName: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public equalsValueClause: EqualsValueClauseSyntax; + constructor(data: number, propertyName: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, equalsValueClause: EqualsValueClauseSyntax) { + super(data); + this.propertyName = propertyName, + this.typeAnnotation = typeAnnotation, + this.equalsValueClause = equalsValueClause, + propertyName.parent = this, + typeAnnotation && (typeAnnotation.parent = this), + equalsValueClause && (equalsValueClause.parent = this); + } + } + export class ArgumentListSyntax extends SyntaxNode { + public typeArgumentList: TypeArgumentListSyntax; + public openParenToken: ISyntaxToken; + public arguments: IExpressionSyntax[]; + public closeParenToken: ISyntaxToken; + constructor(data: number, typeArgumentList: TypeArgumentListSyntax, openParenToken: ISyntaxToken, _arguments: IExpressionSyntax[], closeParenToken: ISyntaxToken) { + super(data); + this.typeArgumentList = typeArgumentList, + this.openParenToken = openParenToken, + this.arguments = _arguments, + this.closeParenToken = closeParenToken, + typeArgumentList && (typeArgumentList.parent = this), + openParenToken.parent = this, + !isShared(_arguments) && (_arguments.parent = this), + closeParenToken.parent = this; + } + } + export class ParameterListSyntax extends SyntaxNode { + public openParenToken: ISyntaxToken; + public parameters: ParameterSyntax[]; + public closeParenToken: ISyntaxToken; + constructor(data: number, openParenToken: ISyntaxToken, parameters: ParameterSyntax[], closeParenToken: ISyntaxToken) { + super(data); + this.openParenToken = openParenToken, + this.parameters = parameters, + this.closeParenToken = closeParenToken, + openParenToken.parent = this, + !isShared(parameters) && (parameters.parent = this), + closeParenToken.parent = this; + } + } + export class TypeArgumentListSyntax extends SyntaxNode { + public lessThanToken: ISyntaxToken; + public typeArguments: ITypeSyntax[]; + public greaterThanToken: ISyntaxToken; + constructor(data: number, lessThanToken: ISyntaxToken, typeArguments: ITypeSyntax[], greaterThanToken: ISyntaxToken) { + super(data); + this.lessThanToken = lessThanToken, + this.typeArguments = typeArguments, + this.greaterThanToken = greaterThanToken, + lessThanToken.parent = this, + !isShared(typeArguments) && (typeArguments.parent = this), + greaterThanToken.parent = this; + } + } + export class TypeParameterListSyntax extends SyntaxNode { + public lessThanToken: ISyntaxToken; + public typeParameters: TypeParameterSyntax[]; + public greaterThanToken: ISyntaxToken; + constructor(data: number, lessThanToken: ISyntaxToken, typeParameters: TypeParameterSyntax[], greaterThanToken: ISyntaxToken) { + super(data); + this.lessThanToken = lessThanToken, + this.typeParameters = typeParameters, + this.greaterThanToken = greaterThanToken, + lessThanToken.parent = this, + !isShared(typeParameters) && (typeParameters.parent = this), + greaterThanToken.parent = this; + } + } + export class HeritageClauseSyntax extends SyntaxNode { + public extendsOrImplementsKeyword: ISyntaxToken; + public typeNames: INameSyntax[]; + constructor(data: number, extendsOrImplementsKeyword: ISyntaxToken, typeNames: INameSyntax[]) { + super(data); + this.extendsOrImplementsKeyword = extendsOrImplementsKeyword, + this.typeNames = typeNames, + extendsOrImplementsKeyword.parent = this, + !isShared(typeNames) && (typeNames.parent = this); + } + public kind(): SyntaxKind { return this.extendsOrImplementsKeyword.kind() === SyntaxKind.ExtendsKeyword ? SyntaxKind.ExtendsHeritageClause : SyntaxKind.ImplementsHeritageClause; } + } + export class EqualsValueClauseSyntax extends SyntaxNode { + public equalsToken: ISyntaxToken; + public value: IExpressionSyntax; + constructor(data: number, equalsToken: ISyntaxToken, value: IExpressionSyntax) { + super(data); + this.equalsToken = equalsToken, + this.value = value, + equalsToken.parent = this, + value.parent = this; + } + } + export class CaseSwitchClauseSyntax extends SyntaxNode implements ISwitchClauseSyntax { + public caseKeyword: ISyntaxToken; + public expression: IExpressionSyntax; + public colonToken: ISyntaxToken; + public statements: IStatementSyntax[]; + public _switchClauseBrand: any; + constructor(data: number, caseKeyword: ISyntaxToken, expression: IExpressionSyntax, colonToken: ISyntaxToken, statements: IStatementSyntax[]) { + super(data); + this.caseKeyword = caseKeyword, + this.expression = expression, + this.colonToken = colonToken, + this.statements = statements, + caseKeyword.parent = this, + expression.parent = this, + colonToken.parent = this, + !isShared(statements) && (statements.parent = this); + } + } + export class DefaultSwitchClauseSyntax extends SyntaxNode implements ISwitchClauseSyntax { + public defaultKeyword: ISyntaxToken; + public colonToken: ISyntaxToken; + public statements: IStatementSyntax[]; + public _switchClauseBrand: any; + constructor(data: number, defaultKeyword: ISyntaxToken, colonToken: ISyntaxToken, statements: IStatementSyntax[]) { + super(data); + this.defaultKeyword = defaultKeyword, + this.colonToken = colonToken, + this.statements = statements, + defaultKeyword.parent = this, + colonToken.parent = this, + !isShared(statements) && (statements.parent = this); + } + } + export class ElseClauseSyntax extends SyntaxNode { + public elseKeyword: ISyntaxToken; + public statement: IStatementSyntax; + constructor(data: number, elseKeyword: ISyntaxToken, statement: IStatementSyntax) { + super(data); + this.elseKeyword = elseKeyword, + this.statement = statement, + elseKeyword.parent = this, + statement.parent = this; + } + } + export class CatchClauseSyntax extends SyntaxNode { + public catchKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public identifier: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public closeParenToken: ISyntaxToken; + public block: BlockSyntax; + constructor(data: number, catchKeyword: ISyntaxToken, openParenToken: ISyntaxToken, identifier: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, closeParenToken: ISyntaxToken, block: BlockSyntax) { + super(data); + this.catchKeyword = catchKeyword, + this.openParenToken = openParenToken, + this.identifier = identifier, + this.typeAnnotation = typeAnnotation, + this.closeParenToken = closeParenToken, + this.block = block, + catchKeyword.parent = this, + openParenToken.parent = this, + identifier.parent = this, + typeAnnotation && (typeAnnotation.parent = this), + closeParenToken.parent = this, + block.parent = this; + } + } + export class FinallyClauseSyntax extends SyntaxNode { + public finallyKeyword: ISyntaxToken; + public block: BlockSyntax; + constructor(data: number, finallyKeyword: ISyntaxToken, block: BlockSyntax) { + super(data); + this.finallyKeyword = finallyKeyword, + this.block = block, + finallyKeyword.parent = this, + block.parent = this; + } + } + export class TypeParameterSyntax extends SyntaxNode { + public identifier: ISyntaxToken; + public constraint: ConstraintSyntax; + constructor(data: number, identifier: ISyntaxToken, constraint: ConstraintSyntax) { + super(data); + this.identifier = identifier, + this.constraint = constraint, + identifier.parent = this, + constraint && (constraint.parent = this); + } + } + export class ConstraintSyntax extends SyntaxNode { + public extendsKeyword: ISyntaxToken; + public typeOrExpression: ISyntaxNodeOrToken; + constructor(data: number, extendsKeyword: ISyntaxToken, typeOrExpression: ISyntaxNodeOrToken) { + super(data); + this.extendsKeyword = extendsKeyword, + this.typeOrExpression = typeOrExpression, + extendsKeyword.parent = this, + typeOrExpression.parent = this; + } + } + export class SimplePropertyAssignmentSyntax extends SyntaxNode implements IPropertyAssignmentSyntax { + public propertyName: ISyntaxToken; + public colonToken: ISyntaxToken; + public expression: IExpressionSyntax; + public _propertyAssignmentBrand: any; + constructor(data: number, propertyName: ISyntaxToken, colonToken: ISyntaxToken, expression: IExpressionSyntax) { + super(data); + this.propertyName = propertyName, + this.colonToken = colonToken, + this.expression = expression, + propertyName.parent = this, + colonToken.parent = this, + expression.parent = this; + } + } + export class FunctionPropertyAssignmentSyntax extends SyntaxNode implements IPropertyAssignmentSyntax { + public propertyName: ISyntaxToken; + public callSignature: CallSignatureSyntax; + public block: BlockSyntax; + public _propertyAssignmentBrand: any; + constructor(data: number, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax) { + super(data); + this.propertyName = propertyName, + this.callSignature = callSignature, + this.block = block, + propertyName.parent = this, + callSignature.parent = this, + block.parent = this; + } + } + export class ParameterSyntax extends SyntaxNode { + public dotDotDotToken: ISyntaxToken; + public modifiers: ISyntaxToken[]; + public identifier: ISyntaxToken; + public questionToken: ISyntaxToken; + public typeAnnotation: TypeAnnotationSyntax; + public equalsValueClause: EqualsValueClauseSyntax; + constructor(data: number, dotDotDotToken: ISyntaxToken, modifiers: ISyntaxToken[], identifier: ISyntaxToken, questionToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, equalsValueClause: EqualsValueClauseSyntax) { + super(data); + this.dotDotDotToken = dotDotDotToken, + this.modifiers = modifiers, + this.identifier = identifier, + this.questionToken = questionToken, + this.typeAnnotation = typeAnnotation, + this.equalsValueClause = equalsValueClause, + dotDotDotToken && (dotDotDotToken.parent = this), + !isShared(modifiers) && (modifiers.parent = this), + identifier.parent = this, + questionToken && (questionToken.parent = this), + typeAnnotation && (typeAnnotation.parent = this), + equalsValueClause && (equalsValueClause.parent = this); + } + } + export class EnumElementSyntax extends SyntaxNode { + public propertyName: ISyntaxToken; + public equalsValueClause: EqualsValueClauseSyntax; + constructor(data: number, propertyName: ISyntaxToken, equalsValueClause: EqualsValueClauseSyntax) { + super(data); + this.propertyName = propertyName, + this.equalsValueClause = equalsValueClause, + propertyName.parent = this, + equalsValueClause && (equalsValueClause.parent = this); + } + } + export class TypeAnnotationSyntax extends SyntaxNode { + public colonToken: ISyntaxToken; + public type: ITypeSyntax; + constructor(data: number, colonToken: ISyntaxToken, type: ITypeSyntax) { + super(data); + this.colonToken = colonToken, + this.type = type, + colonToken.parent = this, + type.parent = this; + } + } + export class ExternalModuleReferenceSyntax extends SyntaxNode implements IModuleReferenceSyntax { + public requireKeyword: ISyntaxToken; + public openParenToken: ISyntaxToken; + public stringLiteral: ISyntaxToken; + public closeParenToken: ISyntaxToken; + public _moduleReferenceBrand: any; + constructor(data: number, requireKeyword: ISyntaxToken, openParenToken: ISyntaxToken, stringLiteral: ISyntaxToken, closeParenToken: ISyntaxToken) { + super(data); + this.requireKeyword = requireKeyword, + this.openParenToken = openParenToken, + this.stringLiteral = stringLiteral, + this.closeParenToken = closeParenToken, + requireKeyword.parent = this, + openParenToken.parent = this, + stringLiteral.parent = this, + closeParenToken.parent = this; + } + } + export class ModuleNameModuleReferenceSyntax extends SyntaxNode implements IModuleReferenceSyntax { + public moduleName: INameSyntax; + public _moduleReferenceBrand: any; + constructor(data: number, moduleName: INameSyntax) { + super(data); + this.moduleName = moduleName, + moduleName.parent = this; + } + } + + (SourceUnitSyntax).prototype.__kind = SyntaxKind.SourceUnit, (QualifiedNameSyntax).prototype.__kind = SyntaxKind.QualifiedName, (ObjectTypeSyntax).prototype.__kind = SyntaxKind.ObjectType, (FunctionTypeSyntax).prototype.__kind = SyntaxKind.FunctionType, (ArrayTypeSyntax).prototype.__kind = SyntaxKind.ArrayType, (ConstructorTypeSyntax).prototype.__kind = SyntaxKind.ConstructorType, (GenericTypeSyntax).prototype.__kind = SyntaxKind.GenericType, (TypeQuerySyntax).prototype.__kind = SyntaxKind.TypeQuery, (InterfaceDeclarationSyntax).prototype.__kind = SyntaxKind.InterfaceDeclaration, (FunctionDeclarationSyntax).prototype.__kind = SyntaxKind.FunctionDeclaration, (ModuleDeclarationSyntax).prototype.__kind = SyntaxKind.ModuleDeclaration, (ClassDeclarationSyntax).prototype.__kind = SyntaxKind.ClassDeclaration, (EnumDeclarationSyntax).prototype.__kind = SyntaxKind.EnumDeclaration, (ImportDeclarationSyntax).prototype.__kind = SyntaxKind.ImportDeclaration, (ExportAssignmentSyntax).prototype.__kind = SyntaxKind.ExportAssignment, (MemberFunctionDeclarationSyntax).prototype.__kind = SyntaxKind.MemberFunctionDeclaration, (MemberVariableDeclarationSyntax).prototype.__kind = SyntaxKind.MemberVariableDeclaration, (ConstructorDeclarationSyntax).prototype.__kind = SyntaxKind.ConstructorDeclaration, (IndexMemberDeclarationSyntax).prototype.__kind = SyntaxKind.IndexMemberDeclaration, (GetAccessorSyntax).prototype.__kind = SyntaxKind.GetAccessor, (SetAccessorSyntax).prototype.__kind = SyntaxKind.SetAccessor, (PropertySignatureSyntax).prototype.__kind = SyntaxKind.PropertySignature, (CallSignatureSyntax).prototype.__kind = SyntaxKind.CallSignature, (ConstructSignatureSyntax).prototype.__kind = SyntaxKind.ConstructSignature, (IndexSignatureSyntax).prototype.__kind = SyntaxKind.IndexSignature, (MethodSignatureSyntax).prototype.__kind = SyntaxKind.MethodSignature, (BlockSyntax).prototype.__kind = SyntaxKind.Block, (IfStatementSyntax).prototype.__kind = SyntaxKind.IfStatement, (VariableStatementSyntax).prototype.__kind = SyntaxKind.VariableStatement, (ExpressionStatementSyntax).prototype.__kind = SyntaxKind.ExpressionStatement, (ReturnStatementSyntax).prototype.__kind = SyntaxKind.ReturnStatement, (SwitchStatementSyntax).prototype.__kind = SyntaxKind.SwitchStatement, (BreakStatementSyntax).prototype.__kind = SyntaxKind.BreakStatement, (ContinueStatementSyntax).prototype.__kind = SyntaxKind.ContinueStatement, (ForStatementSyntax).prototype.__kind = SyntaxKind.ForStatement, (ForInStatementSyntax).prototype.__kind = SyntaxKind.ForInStatement, (EmptyStatementSyntax).prototype.__kind = SyntaxKind.EmptyStatement, (ThrowStatementSyntax).prototype.__kind = SyntaxKind.ThrowStatement, (WhileStatementSyntax).prototype.__kind = SyntaxKind.WhileStatement, (TryStatementSyntax).prototype.__kind = SyntaxKind.TryStatement, (LabeledStatementSyntax).prototype.__kind = SyntaxKind.LabeledStatement, (DoStatementSyntax).prototype.__kind = SyntaxKind.DoStatement, (DebuggerStatementSyntax).prototype.__kind = SyntaxKind.DebuggerStatement, (WithStatementSyntax).prototype.__kind = SyntaxKind.WithStatement, (DeleteExpressionSyntax).prototype.__kind = SyntaxKind.DeleteExpression, (TypeOfExpressionSyntax).prototype.__kind = SyntaxKind.TypeOfExpression, (VoidExpressionSyntax).prototype.__kind = SyntaxKind.VoidExpression, (ConditionalExpressionSyntax).prototype.__kind = SyntaxKind.ConditionalExpression, (MemberAccessExpressionSyntax).prototype.__kind = SyntaxKind.MemberAccessExpression, (InvocationExpressionSyntax).prototype.__kind = SyntaxKind.InvocationExpression, (ArrayLiteralExpressionSyntax).prototype.__kind = SyntaxKind.ArrayLiteralExpression, (ObjectLiteralExpressionSyntax).prototype.__kind = SyntaxKind.ObjectLiteralExpression, (ObjectCreationExpressionSyntax).prototype.__kind = SyntaxKind.ObjectCreationExpression, (ParenthesizedExpressionSyntax).prototype.__kind = SyntaxKind.ParenthesizedExpression, (ParenthesizedArrowFunctionExpressionSyntax).prototype.__kind = SyntaxKind.ParenthesizedArrowFunctionExpression, (SimpleArrowFunctionExpressionSyntax).prototype.__kind = SyntaxKind.SimpleArrowFunctionExpression, (CastExpressionSyntax).prototype.__kind = SyntaxKind.CastExpression, (ElementAccessExpressionSyntax).prototype.__kind = SyntaxKind.ElementAccessExpression, (FunctionExpressionSyntax).prototype.__kind = SyntaxKind.FunctionExpression, (OmittedExpressionSyntax).prototype.__kind = SyntaxKind.OmittedExpression, (VariableDeclarationSyntax).prototype.__kind = SyntaxKind.VariableDeclaration, (VariableDeclaratorSyntax).prototype.__kind = SyntaxKind.VariableDeclarator, (ArgumentListSyntax).prototype.__kind = SyntaxKind.ArgumentList, (ParameterListSyntax).prototype.__kind = SyntaxKind.ParameterList, (TypeArgumentListSyntax).prototype.__kind = SyntaxKind.TypeArgumentList, (TypeParameterListSyntax).prototype.__kind = SyntaxKind.TypeParameterList, (EqualsValueClauseSyntax).prototype.__kind = SyntaxKind.EqualsValueClause, (CaseSwitchClauseSyntax).prototype.__kind = SyntaxKind.CaseSwitchClause, (DefaultSwitchClauseSyntax).prototype.__kind = SyntaxKind.DefaultSwitchClause, (ElseClauseSyntax).prototype.__kind = SyntaxKind.ElseClause, (CatchClauseSyntax).prototype.__kind = SyntaxKind.CatchClause, (FinallyClauseSyntax).prototype.__kind = SyntaxKind.FinallyClause, (TypeParameterSyntax).prototype.__kind = SyntaxKind.TypeParameter, (ConstraintSyntax).prototype.__kind = SyntaxKind.Constraint, (SimplePropertyAssignmentSyntax).prototype.__kind = SyntaxKind.SimplePropertyAssignment, (FunctionPropertyAssignmentSyntax).prototype.__kind = SyntaxKind.FunctionPropertyAssignment, (ParameterSyntax).prototype.__kind = SyntaxKind.Parameter, (EnumElementSyntax).prototype.__kind = SyntaxKind.EnumElement, (TypeAnnotationSyntax).prototype.__kind = SyntaxKind.TypeAnnotation, (ExternalModuleReferenceSyntax).prototype.__kind = SyntaxKind.ExternalModuleReference, (ModuleNameModuleReferenceSyntax).prototype.__kind = SyntaxKind.ModuleNameModuleReference; +} \ No newline at end of file diff --git a/src/services/syntax/syntaxNodes.interfaces.generated.ts b/src/services/syntax/syntaxNodes.interfaces.generated.ts new file mode 100644 index 00000000000..0ecc9cbf9c6 --- /dev/null +++ b/src/services/syntax/syntaxNodes.interfaces.generated.ts @@ -0,0 +1,573 @@ +/// + +module TypeScript { + export interface SourceUnitSyntax extends ISyntaxNode { + syntaxTree: SyntaxTree; + moduleElements: IModuleElementSyntax[]; + endOfFileToken: ISyntaxToken; + } + export interface QualifiedNameSyntax extends ISyntaxNode, INameSyntax { + left: INameSyntax; + dotToken: ISyntaxToken; + right: ISyntaxToken; + } + export interface ObjectTypeSyntax extends ISyntaxNode, ITypeSyntax { + openBraceToken: ISyntaxToken; + typeMembers: ITypeMemberSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface FunctionTypeSyntax extends ISyntaxNode, ITypeSyntax { + typeParameterList: TypeParameterListSyntax; + parameterList: ParameterListSyntax; + equalsGreaterThanToken: ISyntaxToken; + type: ITypeSyntax; + } + export interface ArrayTypeSyntax extends ISyntaxNode, ITypeSyntax { + type: ITypeSyntax; + openBracketToken: ISyntaxToken; + closeBracketToken: ISyntaxToken; + } + export interface ConstructorTypeSyntax extends ISyntaxNode, ITypeSyntax { + newKeyword: ISyntaxToken; + typeParameterList: TypeParameterListSyntax; + parameterList: ParameterListSyntax; + equalsGreaterThanToken: ISyntaxToken; + type: ITypeSyntax; + } + export interface GenericTypeSyntax extends ISyntaxNode, ITypeSyntax { + name: INameSyntax; + typeArgumentList: TypeArgumentListSyntax; + } + export interface TypeQuerySyntax extends ISyntaxNode, ITypeSyntax { + typeOfKeyword: ISyntaxToken; + name: INameSyntax; + } + export interface InterfaceDeclarationSyntax extends ISyntaxNode, IModuleElementSyntax { + modifiers: ISyntaxToken[]; + interfaceKeyword: ISyntaxToken; + identifier: ISyntaxToken; + typeParameterList: TypeParameterListSyntax; + heritageClauses: HeritageClauseSyntax[]; + body: ObjectTypeSyntax; + } + export interface FunctionDeclarationSyntax extends ISyntaxNode, IStatementSyntax { + modifiers: ISyntaxToken[]; + functionKeyword: ISyntaxToken; + identifier: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + semicolonToken: ISyntaxToken; + } + export interface ModuleDeclarationSyntax extends ISyntaxNode, IModuleElementSyntax { + modifiers: ISyntaxToken[]; + moduleKeyword: ISyntaxToken; + name: INameSyntax; + stringLiteral: ISyntaxToken; + openBraceToken: ISyntaxToken; + moduleElements: IModuleElementSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface ClassDeclarationSyntax extends ISyntaxNode, IModuleElementSyntax { + modifiers: ISyntaxToken[]; + classKeyword: ISyntaxToken; + identifier: ISyntaxToken; + typeParameterList: TypeParameterListSyntax; + heritageClauses: HeritageClauseSyntax[]; + openBraceToken: ISyntaxToken; + classElements: IClassElementSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface EnumDeclarationSyntax extends ISyntaxNode, IModuleElementSyntax { + modifiers: ISyntaxToken[]; + enumKeyword: ISyntaxToken; + identifier: ISyntaxToken; + openBraceToken: ISyntaxToken; + enumElements: EnumElementSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface ImportDeclarationSyntax extends ISyntaxNode, IModuleElementSyntax { + modifiers: ISyntaxToken[]; + importKeyword: ISyntaxToken; + identifier: ISyntaxToken; + equalsToken: ISyntaxToken; + moduleReference: IModuleReferenceSyntax; + semicolonToken: ISyntaxToken; + } + export interface ExportAssignmentSyntax extends ISyntaxNode, IModuleElementSyntax { + exportKeyword: ISyntaxToken; + equalsToken: ISyntaxToken; + identifier: ISyntaxToken; + semicolonToken: ISyntaxToken; + } + export interface MemberFunctionDeclarationSyntax extends ISyntaxNode, IMemberDeclarationSyntax { + modifiers: ISyntaxToken[]; + propertyName: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + semicolonToken: ISyntaxToken; + } + export interface MemberVariableDeclarationSyntax extends ISyntaxNode, IMemberDeclarationSyntax { + modifiers: ISyntaxToken[]; + variableDeclarator: VariableDeclaratorSyntax; + semicolonToken: ISyntaxToken; + } + export interface ConstructorDeclarationSyntax extends ISyntaxNode, IClassElementSyntax { + modifiers: ISyntaxToken[]; + constructorKeyword: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + semicolonToken: ISyntaxToken; + } + export interface IndexMemberDeclarationSyntax extends ISyntaxNode, IClassElementSyntax { + modifiers: ISyntaxToken[]; + indexSignature: IndexSignatureSyntax; + semicolonToken: ISyntaxToken; + } + export interface GetAccessorSyntax extends ISyntaxNode, IMemberDeclarationSyntax, IPropertyAssignmentSyntax { + modifiers: ISyntaxToken[]; + getKeyword: ISyntaxToken; + propertyName: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + } + export interface SetAccessorSyntax extends ISyntaxNode, IMemberDeclarationSyntax, IPropertyAssignmentSyntax { + modifiers: ISyntaxToken[]; + setKeyword: ISyntaxToken; + propertyName: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + } + export interface PropertySignatureSyntax extends ISyntaxNode, ITypeMemberSyntax { + propertyName: ISyntaxToken; + questionToken: ISyntaxToken; + typeAnnotation: TypeAnnotationSyntax; + } + export interface CallSignatureSyntax extends ISyntaxNode, ITypeMemberSyntax { + typeParameterList: TypeParameterListSyntax; + parameterList: ParameterListSyntax; + typeAnnotation: TypeAnnotationSyntax; + } + export interface ConstructSignatureSyntax extends ISyntaxNode, ITypeMemberSyntax { + newKeyword: ISyntaxToken; + callSignature: CallSignatureSyntax; + } + export interface IndexSignatureSyntax extends ISyntaxNode, ITypeMemberSyntax { + openBracketToken: ISyntaxToken; + parameters: ParameterSyntax[]; + closeBracketToken: ISyntaxToken; + typeAnnotation: TypeAnnotationSyntax; + } + export interface MethodSignatureSyntax extends ISyntaxNode, ITypeMemberSyntax { + propertyName: ISyntaxToken; + questionToken: ISyntaxToken; + callSignature: CallSignatureSyntax; + } + export interface BlockSyntax extends ISyntaxNode, IStatementSyntax { + openBraceToken: ISyntaxToken; + statements: IStatementSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface IfStatementSyntax extends ISyntaxNode, IStatementSyntax { + ifKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + condition: IExpressionSyntax; + closeParenToken: ISyntaxToken; + statement: IStatementSyntax; + elseClause: ElseClauseSyntax; + } + export interface VariableStatementSyntax extends ISyntaxNode, IStatementSyntax { + modifiers: ISyntaxToken[]; + variableDeclaration: VariableDeclarationSyntax; + semicolonToken: ISyntaxToken; + } + export interface ExpressionStatementSyntax extends ISyntaxNode, IStatementSyntax { + expression: IExpressionSyntax; + semicolonToken: ISyntaxToken; + } + export interface ReturnStatementSyntax extends ISyntaxNode, IStatementSyntax { + returnKeyword: ISyntaxToken; + expression: IExpressionSyntax; + semicolonToken: ISyntaxToken; + } + export interface SwitchStatementSyntax extends ISyntaxNode, IStatementSyntax { + switchKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + expression: IExpressionSyntax; + closeParenToken: ISyntaxToken; + openBraceToken: ISyntaxToken; + switchClauses: ISwitchClauseSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface BreakStatementSyntax extends ISyntaxNode, IStatementSyntax { + breakKeyword: ISyntaxToken; + identifier: ISyntaxToken; + semicolonToken: ISyntaxToken; + } + export interface ContinueStatementSyntax extends ISyntaxNode, IStatementSyntax { + continueKeyword: ISyntaxToken; + identifier: ISyntaxToken; + semicolonToken: ISyntaxToken; + } + export interface ForStatementSyntax extends ISyntaxNode, IStatementSyntax { + forKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + variableDeclaration: VariableDeclarationSyntax; + initializer: IExpressionSyntax; + firstSemicolonToken: ISyntaxToken; + condition: IExpressionSyntax; + secondSemicolonToken: ISyntaxToken; + incrementor: IExpressionSyntax; + closeParenToken: ISyntaxToken; + statement: IStatementSyntax; + } + export interface ForInStatementSyntax extends ISyntaxNode, IStatementSyntax { + forKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + variableDeclaration: VariableDeclarationSyntax; + left: IExpressionSyntax; + inKeyword: ISyntaxToken; + expression: IExpressionSyntax; + closeParenToken: ISyntaxToken; + statement: IStatementSyntax; + } + export interface EmptyStatementSyntax extends ISyntaxNode, IStatementSyntax { + semicolonToken: ISyntaxToken; + } + export interface ThrowStatementSyntax extends ISyntaxNode, IStatementSyntax { + throwKeyword: ISyntaxToken; + expression: IExpressionSyntax; + semicolonToken: ISyntaxToken; + } + export interface WhileStatementSyntax extends ISyntaxNode, IStatementSyntax { + whileKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + condition: IExpressionSyntax; + closeParenToken: ISyntaxToken; + statement: IStatementSyntax; + } + export interface TryStatementSyntax extends ISyntaxNode, IStatementSyntax { + tryKeyword: ISyntaxToken; + block: BlockSyntax; + catchClause: CatchClauseSyntax; + finallyClause: FinallyClauseSyntax; + } + export interface LabeledStatementSyntax extends ISyntaxNode, IStatementSyntax { + identifier: ISyntaxToken; + colonToken: ISyntaxToken; + statement: IStatementSyntax; + } + export interface DoStatementSyntax extends ISyntaxNode, IStatementSyntax { + doKeyword: ISyntaxToken; + statement: IStatementSyntax; + whileKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + condition: IExpressionSyntax; + closeParenToken: ISyntaxToken; + semicolonToken: ISyntaxToken; + } + export interface DebuggerStatementSyntax extends ISyntaxNode, IStatementSyntax { + debuggerKeyword: ISyntaxToken; + semicolonToken: ISyntaxToken; + } + export interface WithStatementSyntax extends ISyntaxNode, IStatementSyntax { + withKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + condition: IExpressionSyntax; + closeParenToken: ISyntaxToken; + statement: IStatementSyntax; + } + export interface PrefixUnaryExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + operatorToken: ISyntaxToken; + operand: IUnaryExpressionSyntax; + } + export interface DeleteExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + deleteKeyword: ISyntaxToken; + expression: IUnaryExpressionSyntax; + } + export interface TypeOfExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + typeOfKeyword: ISyntaxToken; + expression: IUnaryExpressionSyntax; + } + export interface VoidExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + voidKeyword: ISyntaxToken; + expression: IUnaryExpressionSyntax; + } + export interface ConditionalExpressionSyntax extends ISyntaxNode, IExpressionSyntax { + condition: IExpressionSyntax; + questionToken: ISyntaxToken; + whenTrue: IExpressionSyntax; + colonToken: ISyntaxToken; + whenFalse: IExpressionSyntax; + } + export interface BinaryExpressionSyntax extends ISyntaxNode, IExpressionSyntax { + left: IExpressionSyntax; + operatorToken: ISyntaxToken; + right: IExpressionSyntax; + } + export interface PostfixUnaryExpressionSyntax extends ISyntaxNode, IPostfixExpressionSyntax { + operand: ILeftHandSideExpressionSyntax; + operatorToken: ISyntaxToken; + } + export interface MemberAccessExpressionSyntax extends ISyntaxNode, IMemberExpressionSyntax, ICallExpressionSyntax { + expression: ILeftHandSideExpressionSyntax; + dotToken: ISyntaxToken; + name: ISyntaxToken; + } + export interface InvocationExpressionSyntax extends ISyntaxNode, ICallExpressionSyntax { + expression: ILeftHandSideExpressionSyntax; + argumentList: ArgumentListSyntax; + } + export interface ArrayLiteralExpressionSyntax extends ISyntaxNode, IPrimaryExpressionSyntax { + openBracketToken: ISyntaxToken; + expressions: IExpressionSyntax[]; + closeBracketToken: ISyntaxToken; + } + export interface ObjectLiteralExpressionSyntax extends ISyntaxNode, IPrimaryExpressionSyntax { + openBraceToken: ISyntaxToken; + propertyAssignments: IPropertyAssignmentSyntax[]; + closeBraceToken: ISyntaxToken; + } + export interface ObjectCreationExpressionSyntax extends ISyntaxNode, IPrimaryExpressionSyntax { + newKeyword: ISyntaxToken; + expression: IMemberExpressionSyntax; + argumentList: ArgumentListSyntax; + } + export interface ParenthesizedExpressionSyntax extends ISyntaxNode, IPrimaryExpressionSyntax { + openParenToken: ISyntaxToken; + expression: IExpressionSyntax; + closeParenToken: ISyntaxToken; + } + export interface ParenthesizedArrowFunctionExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + callSignature: CallSignatureSyntax; + equalsGreaterThanToken: ISyntaxToken; + block: BlockSyntax; + expression: IExpressionSyntax; + } + export interface SimpleArrowFunctionExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + parameter: ParameterSyntax; + equalsGreaterThanToken: ISyntaxToken; + block: BlockSyntax; + expression: IExpressionSyntax; + } + export interface CastExpressionSyntax extends ISyntaxNode, IUnaryExpressionSyntax { + lessThanToken: ISyntaxToken; + type: ITypeSyntax; + greaterThanToken: ISyntaxToken; + expression: IUnaryExpressionSyntax; + } + export interface ElementAccessExpressionSyntax extends ISyntaxNode, IMemberExpressionSyntax, ICallExpressionSyntax { + expression: ILeftHandSideExpressionSyntax; + openBracketToken: ISyntaxToken; + argumentExpression: IExpressionSyntax; + closeBracketToken: ISyntaxToken; + } + export interface FunctionExpressionSyntax extends ISyntaxNode, IPrimaryExpressionSyntax { + functionKeyword: ISyntaxToken; + identifier: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + } + export interface OmittedExpressionSyntax extends ISyntaxNode, IExpressionSyntax { + } + export interface VariableDeclarationSyntax extends ISyntaxNode { + varKeyword: ISyntaxToken; + variableDeclarators: VariableDeclaratorSyntax[]; + } + export interface VariableDeclaratorSyntax extends ISyntaxNode { + propertyName: ISyntaxToken; + typeAnnotation: TypeAnnotationSyntax; + equalsValueClause: EqualsValueClauseSyntax; + } + export interface ArgumentListSyntax extends ISyntaxNode { + typeArgumentList: TypeArgumentListSyntax; + openParenToken: ISyntaxToken; + arguments: IExpressionSyntax[]; + closeParenToken: ISyntaxToken; + } + export interface ParameterListSyntax extends ISyntaxNode { + openParenToken: ISyntaxToken; + parameters: ParameterSyntax[]; + closeParenToken: ISyntaxToken; + } + export interface TypeArgumentListSyntax extends ISyntaxNode { + lessThanToken: ISyntaxToken; + typeArguments: ITypeSyntax[]; + greaterThanToken: ISyntaxToken; + } + export interface TypeParameterListSyntax extends ISyntaxNode { + lessThanToken: ISyntaxToken; + typeParameters: TypeParameterSyntax[]; + greaterThanToken: ISyntaxToken; + } + export interface HeritageClauseSyntax extends ISyntaxNode { + extendsOrImplementsKeyword: ISyntaxToken; + typeNames: INameSyntax[]; + } + export interface EqualsValueClauseSyntax extends ISyntaxNode { + equalsToken: ISyntaxToken; + value: IExpressionSyntax; + } + export interface CaseSwitchClauseSyntax extends ISyntaxNode, ISwitchClauseSyntax { + caseKeyword: ISyntaxToken; + expression: IExpressionSyntax; + colonToken: ISyntaxToken; + statements: IStatementSyntax[]; + } + export interface DefaultSwitchClauseSyntax extends ISyntaxNode, ISwitchClauseSyntax { + defaultKeyword: ISyntaxToken; + colonToken: ISyntaxToken; + statements: IStatementSyntax[]; + } + export interface ElseClauseSyntax extends ISyntaxNode { + elseKeyword: ISyntaxToken; + statement: IStatementSyntax; + } + export interface CatchClauseSyntax extends ISyntaxNode { + catchKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + identifier: ISyntaxToken; + typeAnnotation: TypeAnnotationSyntax; + closeParenToken: ISyntaxToken; + block: BlockSyntax; + } + export interface FinallyClauseSyntax extends ISyntaxNode { + finallyKeyword: ISyntaxToken; + block: BlockSyntax; + } + export interface TypeParameterSyntax extends ISyntaxNode { + identifier: ISyntaxToken; + constraint: ConstraintSyntax; + } + export interface ConstraintSyntax extends ISyntaxNode { + extendsKeyword: ISyntaxToken; + typeOrExpression: ISyntaxNodeOrToken; + } + export interface SimplePropertyAssignmentSyntax extends ISyntaxNode, IPropertyAssignmentSyntax { + propertyName: ISyntaxToken; + colonToken: ISyntaxToken; + expression: IExpressionSyntax; + } + export interface FunctionPropertyAssignmentSyntax extends ISyntaxNode, IPropertyAssignmentSyntax { + propertyName: ISyntaxToken; + callSignature: CallSignatureSyntax; + block: BlockSyntax; + } + export interface ParameterSyntax extends ISyntaxNode { + dotDotDotToken: ISyntaxToken; + modifiers: ISyntaxToken[]; + identifier: ISyntaxToken; + questionToken: ISyntaxToken; + typeAnnotation: TypeAnnotationSyntax; + equalsValueClause: EqualsValueClauseSyntax; + } + export interface EnumElementSyntax extends ISyntaxNode { + propertyName: ISyntaxToken; + equalsValueClause: EqualsValueClauseSyntax; + } + export interface TypeAnnotationSyntax extends ISyntaxNode { + colonToken: ISyntaxToken; + type: ITypeSyntax; + } + export interface ExternalModuleReferenceSyntax extends ISyntaxNode, IModuleReferenceSyntax { + requireKeyword: ISyntaxToken; + openParenToken: ISyntaxToken; + stringLiteral: ISyntaxToken; + closeParenToken: ISyntaxToken; + } + export interface ModuleNameModuleReferenceSyntax extends ISyntaxNode, IModuleReferenceSyntax { + moduleName: INameSyntax; + } + + export var nodeMetadata: string[][] = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],["moduleElements","endOfFileToken"],["left","dotToken","right"],["openBraceToken","typeMembers","closeBraceToken"],["typeParameterList","parameterList","equalsGreaterThanToken","type"],["type","openBracketToken","closeBracketToken"],["newKeyword","typeParameterList","parameterList","equalsGreaterThanToken","type"],["name","typeArgumentList"],["typeOfKeyword","name"],["modifiers","interfaceKeyword","identifier","typeParameterList","heritageClauses","body"],["modifiers","functionKeyword","identifier","callSignature","block","semicolonToken"],["modifiers","moduleKeyword","name","stringLiteral","openBraceToken","moduleElements","closeBraceToken"],["modifiers","classKeyword","identifier","typeParameterList","heritageClauses","openBraceToken","classElements","closeBraceToken"],["modifiers","enumKeyword","identifier","openBraceToken","enumElements","closeBraceToken"],["modifiers","importKeyword","identifier","equalsToken","moduleReference","semicolonToken"],["exportKeyword","equalsToken","identifier","semicolonToken"],["modifiers","propertyName","callSignature","block","semicolonToken"],["modifiers","variableDeclarator","semicolonToken"],["modifiers","constructorKeyword","callSignature","block","semicolonToken"],["modifiers","indexSignature","semicolonToken"],["modifiers","getKeyword","propertyName","callSignature","block"],["modifiers","setKeyword","propertyName","callSignature","block"],["propertyName","questionToken","typeAnnotation"],["typeParameterList","parameterList","typeAnnotation"],["newKeyword","callSignature"],["openBracketToken","parameters","closeBracketToken","typeAnnotation"],["propertyName","questionToken","callSignature"],["openBraceToken","statements","closeBraceToken"],["ifKeyword","openParenToken","condition","closeParenToken","statement","elseClause"],["modifiers","variableDeclaration","semicolonToken"],["expression","semicolonToken"],["returnKeyword","expression","semicolonToken"],["switchKeyword","openParenToken","expression","closeParenToken","openBraceToken","switchClauses","closeBraceToken"],["breakKeyword","identifier","semicolonToken"],["continueKeyword","identifier","semicolonToken"],["forKeyword","openParenToken","variableDeclaration","initializer","firstSemicolonToken","condition","secondSemicolonToken","incrementor","closeParenToken","statement"],["forKeyword","openParenToken","variableDeclaration","left","inKeyword","expression","closeParenToken","statement"],["semicolonToken"],["throwKeyword","expression","semicolonToken"],["whileKeyword","openParenToken","condition","closeParenToken","statement"],["tryKeyword","block","catchClause","finallyClause"],["identifier","colonToken","statement"],["doKeyword","statement","whileKeyword","openParenToken","condition","closeParenToken","semicolonToken"],["debuggerKeyword","semicolonToken"],["withKeyword","openParenToken","condition","closeParenToken","statement"],["operatorToken","operand"],["operatorToken","operand"],["operatorToken","operand"],["operatorToken","operand"],["operatorToken","operand"],["operatorToken","operand"],["deleteKeyword","expression"],["typeOfKeyword","expression"],["voidKeyword","expression"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["condition","questionToken","whenTrue","colonToken","whenFalse"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["left","operatorToken","right"],["operand","operatorToken"],["operand","operatorToken"],["expression","dotToken","name"],["expression","argumentList"],["openBracketToken","expressions","closeBracketToken"],["openBraceToken","propertyAssignments","closeBraceToken"],["newKeyword","expression","argumentList"],["openParenToken","expression","closeParenToken"],["callSignature","equalsGreaterThanToken","block","expression"],["parameter","equalsGreaterThanToken","block","expression"],["lessThanToken","type","greaterThanToken","expression"],["expression","openBracketToken","argumentExpression","closeBracketToken"],["functionKeyword","identifier","callSignature","block"],[],["varKeyword","variableDeclarators"],["propertyName","typeAnnotation","equalsValueClause"],["typeArgumentList","openParenToken","arguments","closeParenToken"],["openParenToken","parameters","closeParenToken"],["lessThanToken","typeArguments","greaterThanToken"],["lessThanToken","typeParameters","greaterThanToken"],["extendsOrImplementsKeyword","typeNames"],["extendsOrImplementsKeyword","typeNames"],["equalsToken","value"],["caseKeyword","expression","colonToken","statements"],["defaultKeyword","colonToken","statements"],["elseKeyword","statement"],["catchKeyword","openParenToken","identifier","typeAnnotation","closeParenToken","block"],["finallyKeyword","block"],["identifier","constraint"],["extendsKeyword","typeOrExpression"],["propertyName","colonToken","expression"],["propertyName","callSignature","block"],["dotDotDotToken","modifiers","identifier","questionToken","typeAnnotation","equalsValueClause"],["propertyName","equalsValueClause"],["colonToken","type"],["requireKeyword","openParenToken","stringLiteral","closeParenToken"],["moduleName"],]; + + export module Syntax { + export interface ISyntaxFactory { + isConcrete: boolean; + SourceUnitSyntax: { new(data: number, moduleElements: IModuleElementSyntax[], endOfFileToken: ISyntaxToken): SourceUnitSyntax }; + QualifiedNameSyntax: { new(data: number, left: INameSyntax, dotToken: ISyntaxToken, right: ISyntaxToken): QualifiedNameSyntax }; + ObjectTypeSyntax: { new(data: number, openBraceToken: ISyntaxToken, typeMembers: ITypeMemberSyntax[], closeBraceToken: ISyntaxToken): ObjectTypeSyntax }; + FunctionTypeSyntax: { new(data: number, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, equalsGreaterThanToken: ISyntaxToken, type: ITypeSyntax): FunctionTypeSyntax }; + ArrayTypeSyntax: { new(data: number, type: ITypeSyntax, openBracketToken: ISyntaxToken, closeBracketToken: ISyntaxToken): ArrayTypeSyntax }; + ConstructorTypeSyntax: { new(data: number, newKeyword: ISyntaxToken, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, equalsGreaterThanToken: ISyntaxToken, type: ITypeSyntax): ConstructorTypeSyntax }; + GenericTypeSyntax: { new(data: number, name: INameSyntax, typeArgumentList: TypeArgumentListSyntax): GenericTypeSyntax }; + TypeQuerySyntax: { new(data: number, typeOfKeyword: ISyntaxToken, name: INameSyntax): TypeQuerySyntax }; + InterfaceDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], interfaceKeyword: ISyntaxToken, identifier: ISyntaxToken, typeParameterList: TypeParameterListSyntax, heritageClauses: HeritageClauseSyntax[], body: ObjectTypeSyntax): InterfaceDeclarationSyntax }; + FunctionDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken): FunctionDeclarationSyntax }; + ModuleDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], moduleKeyword: ISyntaxToken, name: INameSyntax, stringLiteral: ISyntaxToken, openBraceToken: ISyntaxToken, moduleElements: IModuleElementSyntax[], closeBraceToken: ISyntaxToken): ModuleDeclarationSyntax }; + ClassDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], classKeyword: ISyntaxToken, identifier: ISyntaxToken, typeParameterList: TypeParameterListSyntax, heritageClauses: HeritageClauseSyntax[], openBraceToken: ISyntaxToken, classElements: IClassElementSyntax[], closeBraceToken: ISyntaxToken): ClassDeclarationSyntax }; + EnumDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], enumKeyword: ISyntaxToken, identifier: ISyntaxToken, openBraceToken: ISyntaxToken, enumElements: EnumElementSyntax[], closeBraceToken: ISyntaxToken): EnumDeclarationSyntax }; + ImportDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], importKeyword: ISyntaxToken, identifier: ISyntaxToken, equalsToken: ISyntaxToken, moduleReference: IModuleReferenceSyntax, semicolonToken: ISyntaxToken): ImportDeclarationSyntax }; + ExportAssignmentSyntax: { new(data: number, exportKeyword: ISyntaxToken, equalsToken: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken): ExportAssignmentSyntax }; + MemberFunctionDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken): MemberFunctionDeclarationSyntax }; + MemberVariableDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], variableDeclarator: VariableDeclaratorSyntax, semicolonToken: ISyntaxToken): MemberVariableDeclarationSyntax }; + ConstructorDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], constructorKeyword: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax, semicolonToken: ISyntaxToken): ConstructorDeclarationSyntax }; + IndexMemberDeclarationSyntax: { new(data: number, modifiers: ISyntaxToken[], indexSignature: IndexSignatureSyntax, semicolonToken: ISyntaxToken): IndexMemberDeclarationSyntax }; + GetAccessorSyntax: { new(data: number, modifiers: ISyntaxToken[], getKeyword: ISyntaxToken, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax): GetAccessorSyntax }; + SetAccessorSyntax: { new(data: number, modifiers: ISyntaxToken[], setKeyword: ISyntaxToken, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax): SetAccessorSyntax }; + PropertySignatureSyntax: { new(data: number, propertyName: ISyntaxToken, questionToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax): PropertySignatureSyntax }; + CallSignatureSyntax: { new(data: number, typeParameterList: TypeParameterListSyntax, parameterList: ParameterListSyntax, typeAnnotation: TypeAnnotationSyntax): CallSignatureSyntax }; + ConstructSignatureSyntax: { new(data: number, newKeyword: ISyntaxToken, callSignature: CallSignatureSyntax): ConstructSignatureSyntax }; + IndexSignatureSyntax: { new(data: number, openBracketToken: ISyntaxToken, parameters: ParameterSyntax[], closeBracketToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax): IndexSignatureSyntax }; + MethodSignatureSyntax: { new(data: number, propertyName: ISyntaxToken, questionToken: ISyntaxToken, callSignature: CallSignatureSyntax): MethodSignatureSyntax }; + BlockSyntax: { new(data: number, openBraceToken: ISyntaxToken, statements: IStatementSyntax[], closeBraceToken: ISyntaxToken): BlockSyntax }; + IfStatementSyntax: { new(data: number, ifKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax, elseClause: ElseClauseSyntax): IfStatementSyntax }; + VariableStatementSyntax: { new(data: number, modifiers: ISyntaxToken[], variableDeclaration: VariableDeclarationSyntax, semicolonToken: ISyntaxToken): VariableStatementSyntax }; + ExpressionStatementSyntax: { new(data: number, expression: IExpressionSyntax, semicolonToken: ISyntaxToken): ExpressionStatementSyntax }; + ReturnStatementSyntax: { new(data: number, returnKeyword: ISyntaxToken, expression: IExpressionSyntax, semicolonToken: ISyntaxToken): ReturnStatementSyntax }; + SwitchStatementSyntax: { new(data: number, switchKeyword: ISyntaxToken, openParenToken: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, openBraceToken: ISyntaxToken, switchClauses: ISwitchClauseSyntax[], closeBraceToken: ISyntaxToken): SwitchStatementSyntax }; + BreakStatementSyntax: { new(data: number, breakKeyword: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken): BreakStatementSyntax }; + ContinueStatementSyntax: { new(data: number, continueKeyword: ISyntaxToken, identifier: ISyntaxToken, semicolonToken: ISyntaxToken): ContinueStatementSyntax }; + ForStatementSyntax: { new(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): ForStatementSyntax }; + ForInStatementSyntax: { new(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, left: IExpressionSyntax, inKeyword: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): ForInStatementSyntax }; + EmptyStatementSyntax: { new(data: number, semicolonToken: ISyntaxToken): EmptyStatementSyntax }; + ThrowStatementSyntax: { new(data: number, throwKeyword: ISyntaxToken, expression: IExpressionSyntax, semicolonToken: ISyntaxToken): ThrowStatementSyntax }; + WhileStatementSyntax: { new(data: number, whileKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): WhileStatementSyntax }; + TryStatementSyntax: { new(data: number, tryKeyword: ISyntaxToken, block: BlockSyntax, catchClause: CatchClauseSyntax, finallyClause: FinallyClauseSyntax): TryStatementSyntax }; + LabeledStatementSyntax: { new(data: number, identifier: ISyntaxToken, colonToken: ISyntaxToken, statement: IStatementSyntax): LabeledStatementSyntax }; + DoStatementSyntax: { new(data: number, doKeyword: ISyntaxToken, statement: IStatementSyntax, whileKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, semicolonToken: ISyntaxToken): DoStatementSyntax }; + DebuggerStatementSyntax: { new(data: number, debuggerKeyword: ISyntaxToken, semicolonToken: ISyntaxToken): DebuggerStatementSyntax }; + WithStatementSyntax: { new(data: number, withKeyword: ISyntaxToken, openParenToken: ISyntaxToken, condition: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): WithStatementSyntax }; + PrefixUnaryExpressionSyntax: { new(data: number, operatorToken: ISyntaxToken, operand: IUnaryExpressionSyntax): PrefixUnaryExpressionSyntax }; + DeleteExpressionSyntax: { new(data: number, deleteKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax): DeleteExpressionSyntax }; + TypeOfExpressionSyntax: { new(data: number, typeOfKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax): TypeOfExpressionSyntax }; + VoidExpressionSyntax: { new(data: number, voidKeyword: ISyntaxToken, expression: IUnaryExpressionSyntax): VoidExpressionSyntax }; + ConditionalExpressionSyntax: { new(data: number, condition: IExpressionSyntax, questionToken: ISyntaxToken, whenTrue: IExpressionSyntax, colonToken: ISyntaxToken, whenFalse: IExpressionSyntax): ConditionalExpressionSyntax }; + BinaryExpressionSyntax: { new(data: number, left: IExpressionSyntax, operatorToken: ISyntaxToken, right: IExpressionSyntax): BinaryExpressionSyntax }; + PostfixUnaryExpressionSyntax: { new(data: number, operand: ILeftHandSideExpressionSyntax, operatorToken: ISyntaxToken): PostfixUnaryExpressionSyntax }; + MemberAccessExpressionSyntax: { new(data: number, expression: ILeftHandSideExpressionSyntax, dotToken: ISyntaxToken, name: ISyntaxToken): MemberAccessExpressionSyntax }; + InvocationExpressionSyntax: { new(data: number, expression: ILeftHandSideExpressionSyntax, argumentList: ArgumentListSyntax): InvocationExpressionSyntax }; + ArrayLiteralExpressionSyntax: { new(data: number, openBracketToken: ISyntaxToken, expressions: IExpressionSyntax[], closeBracketToken: ISyntaxToken): ArrayLiteralExpressionSyntax }; + ObjectLiteralExpressionSyntax: { new(data: number, openBraceToken: ISyntaxToken, propertyAssignments: IPropertyAssignmentSyntax[], closeBraceToken: ISyntaxToken): ObjectLiteralExpressionSyntax }; + ObjectCreationExpressionSyntax: { new(data: number, newKeyword: ISyntaxToken, expression: IMemberExpressionSyntax, argumentList: ArgumentListSyntax): ObjectCreationExpressionSyntax }; + ParenthesizedExpressionSyntax: { new(data: number, openParenToken: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken): ParenthesizedExpressionSyntax }; + ParenthesizedArrowFunctionExpressionSyntax: { new(data: number, callSignature: CallSignatureSyntax, equalsGreaterThanToken: ISyntaxToken, block: BlockSyntax, expression: IExpressionSyntax): ParenthesizedArrowFunctionExpressionSyntax }; + SimpleArrowFunctionExpressionSyntax: { new(data: number, parameter: ParameterSyntax, equalsGreaterThanToken: ISyntaxToken, block: BlockSyntax, expression: IExpressionSyntax): SimpleArrowFunctionExpressionSyntax }; + CastExpressionSyntax: { new(data: number, lessThanToken: ISyntaxToken, type: ITypeSyntax, greaterThanToken: ISyntaxToken, expression: IUnaryExpressionSyntax): CastExpressionSyntax }; + ElementAccessExpressionSyntax: { new(data: number, expression: ILeftHandSideExpressionSyntax, openBracketToken: ISyntaxToken, argumentExpression: IExpressionSyntax, closeBracketToken: ISyntaxToken): ElementAccessExpressionSyntax }; + FunctionExpressionSyntax: { new(data: number, functionKeyword: ISyntaxToken, identifier: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax): FunctionExpressionSyntax }; + OmittedExpressionSyntax: { new(data: number): OmittedExpressionSyntax }; + VariableDeclarationSyntax: { new(data: number, varKeyword: ISyntaxToken, variableDeclarators: VariableDeclaratorSyntax[]): VariableDeclarationSyntax }; + VariableDeclaratorSyntax: { new(data: number, propertyName: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, equalsValueClause: EqualsValueClauseSyntax): VariableDeclaratorSyntax }; + ArgumentListSyntax: { new(data: number, typeArgumentList: TypeArgumentListSyntax, openParenToken: ISyntaxToken, arguments: IExpressionSyntax[], closeParenToken: ISyntaxToken): ArgumentListSyntax }; + ParameterListSyntax: { new(data: number, openParenToken: ISyntaxToken, parameters: ParameterSyntax[], closeParenToken: ISyntaxToken): ParameterListSyntax }; + TypeArgumentListSyntax: { new(data: number, lessThanToken: ISyntaxToken, typeArguments: ITypeSyntax[], greaterThanToken: ISyntaxToken): TypeArgumentListSyntax }; + TypeParameterListSyntax: { new(data: number, lessThanToken: ISyntaxToken, typeParameters: TypeParameterSyntax[], greaterThanToken: ISyntaxToken): TypeParameterListSyntax }; + HeritageClauseSyntax: { new(data: number, extendsOrImplementsKeyword: ISyntaxToken, typeNames: INameSyntax[]): HeritageClauseSyntax }; + EqualsValueClauseSyntax: { new(data: number, equalsToken: ISyntaxToken, value: IExpressionSyntax): EqualsValueClauseSyntax }; + CaseSwitchClauseSyntax: { new(data: number, caseKeyword: ISyntaxToken, expression: IExpressionSyntax, colonToken: ISyntaxToken, statements: IStatementSyntax[]): CaseSwitchClauseSyntax }; + DefaultSwitchClauseSyntax: { new(data: number, defaultKeyword: ISyntaxToken, colonToken: ISyntaxToken, statements: IStatementSyntax[]): DefaultSwitchClauseSyntax }; + ElseClauseSyntax: { new(data: number, elseKeyword: ISyntaxToken, statement: IStatementSyntax): ElseClauseSyntax }; + CatchClauseSyntax: { new(data: number, catchKeyword: ISyntaxToken, openParenToken: ISyntaxToken, identifier: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, closeParenToken: ISyntaxToken, block: BlockSyntax): CatchClauseSyntax }; + FinallyClauseSyntax: { new(data: number, finallyKeyword: ISyntaxToken, block: BlockSyntax): FinallyClauseSyntax }; + TypeParameterSyntax: { new(data: number, identifier: ISyntaxToken, constraint: ConstraintSyntax): TypeParameterSyntax }; + ConstraintSyntax: { new(data: number, extendsKeyword: ISyntaxToken, typeOrExpression: ISyntaxNodeOrToken): ConstraintSyntax }; + SimplePropertyAssignmentSyntax: { new(data: number, propertyName: ISyntaxToken, colonToken: ISyntaxToken, expression: IExpressionSyntax): SimplePropertyAssignmentSyntax }; + FunctionPropertyAssignmentSyntax: { new(data: number, propertyName: ISyntaxToken, callSignature: CallSignatureSyntax, block: BlockSyntax): FunctionPropertyAssignmentSyntax }; + ParameterSyntax: { new(data: number, dotDotDotToken: ISyntaxToken, modifiers: ISyntaxToken[], identifier: ISyntaxToken, questionToken: ISyntaxToken, typeAnnotation: TypeAnnotationSyntax, equalsValueClause: EqualsValueClauseSyntax): ParameterSyntax }; + EnumElementSyntax: { new(data: number, propertyName: ISyntaxToken, equalsValueClause: EqualsValueClauseSyntax): EnumElementSyntax }; + TypeAnnotationSyntax: { new(data: number, colonToken: ISyntaxToken, type: ITypeSyntax): TypeAnnotationSyntax }; + ExternalModuleReferenceSyntax: { new(data: number, requireKeyword: ISyntaxToken, openParenToken: ISyntaxToken, stringLiteral: ISyntaxToken, closeParenToken: ISyntaxToken): ExternalModuleReferenceSyntax }; + ModuleNameModuleReferenceSyntax: { new(data: number, moduleName: INameSyntax): ModuleNameModuleReferenceSyntax }; + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxToken.ts b/src/services/syntax/syntaxToken.ts new file mode 100644 index 00000000000..19c522aee18 --- /dev/null +++ b/src/services/syntax/syntaxToken.ts @@ -0,0 +1,547 @@ +/// + +module TypeScript { + export interface ISyntaxToken extends ISyntaxNodeOrToken, INameSyntax, IPrimaryExpressionSyntax { + // Adjusts the full start of this token. Should only be called by the parser. + setFullStart(fullStart: number): void; + + // The absolute start of this element, including the leading trivia. + fullStart(): number; + + // With of this element, including leading and trailing trivia. + fullWidth(): number; + + // Text for this token, not including leading or trailing trivia. + text(): string; + fullText(text?: ISimpleText): string; + + hasLeadingTrivia(): boolean; + hasTrailingTrivia(): boolean; + + hasSkippedToken(): boolean; + + leadingTrivia(text?: ISimpleText): ISyntaxTriviaList; + trailingTrivia(text?: ISimpleText): ISyntaxTriviaList; + + leadingTriviaWidth(text?: ISimpleText): number; + trailingTriviaWidth(text?: ISimpleText): number; + + // True if this was a keyword that the parser converted to an identifier. i.e. if you have + // x.public + // + // then 'public' will be converted to an identifier. These tokens should are parser + // generated and, as such, should not be returned when the incremental parser source + // hands out tokens. Note: If it is included in a node then *that* node may still + // be reusuable. i.e. if i have: private Foo() { x.public = 1; } + // + // Then that entire method node is reusable even if the 'public' identifier is not. + isKeywordConvertedToIdentifier(): boolean; + + // True if this element cannot be reused in incremental parsing. There are several situations + // in which an element can not be reused. They are: + // + // 1) The element contained skipped text. + // 2) The element contained zero width tokens. + // 3) The element contains tokens generated by the parser (like >> or a keyword -> identifier + // conversion). + // 4) The element contains a regex token somewhere under it. A regex token is either a + // regex itself (i.e. /foo/), or is a token which could start a regex (i.e. "/" or "/="). This + // data is used by the incremental parser to decide if a node can be reused. Due to the + // lookahead nature of regex tokens, a node containing a regex token cannot be reused. Normally, + // changes to text only affect the tokens directly intersected. However, because regex tokens + // have such unbounded lookahead (technically bounded at the end of a line, but htat's minor), + // we need to recheck them to see if they've changed due to the edit. For example, if you had: + // + // while (true) /3; return; + // + // And you changed it to: + // + // while (true) /3; return/; + // + // Then even though only the 'return' and ';' colons were touched, we'd want to rescan the '/' + // token which we would then realize was a regex. + isIncrementallyUnusable(): boolean; + + clone(): ISyntaxToken; + } +} + +module TypeScript { + export function tokenValue(token: ISyntaxToken): any { + if (token.fullWidth() === 0) { + return null; + } + + var kind = token.kind(); + var text = token.text(); + + if (kind === SyntaxKind.IdentifierName) { + return massageEscapes(text); + } + + switch (kind) { + case SyntaxKind.TrueKeyword: + return true; + case SyntaxKind.FalseKeyword: + return false; + case SyntaxKind.NullKeyword: + return null; + } + + if (SyntaxFacts.isAnyKeyword(kind) || SyntaxFacts.isAnyPunctuation(kind)) { + return SyntaxFacts.getText(kind); + } + + if (kind === SyntaxKind.NumericLiteral) { + return IntegerUtilities.isHexInteger(text) ? parseInt(text, /*radix:*/ 16) : parseFloat(text); + } + else if (kind === SyntaxKind.StringLiteral) { + if (text.length > 1 && text.charCodeAt(text.length - 1) === text.charCodeAt(0)) { + // Properly terminated. Remove the quotes, and massage any escape characters we see. + return massageEscapes(text.substr(1, text.length - 2)); + } + else { + // Not property terminated. Remove the first quote and massage any escape characters we see. + return massageEscapes(text.substr(1)); + + } + } + else if (kind === SyntaxKind.RegularExpressionLiteral) { + return regularExpressionValue(text); + } + else if (kind === SyntaxKind.EndOfFileToken || kind === SyntaxKind.ErrorToken) { + return null; + } + else { + throw Errors.invalidOperation(); + } + } + + export function tokenValueText(token: ISyntaxToken): string { + var value = tokenValue(token); + return value === null ? "" : massageDisallowedIdentifiers(value.toString()); + } + + export function massageEscapes(text: string): string { + return text.indexOf("\\") >= 0 ? convertEscapes(text) : text; + } + + function regularExpressionValue(text: string): RegExp { + try { + var lastSlash = text.lastIndexOf("/"); + var body = text.substring(1, lastSlash); + var flags = text.substring(lastSlash + 1); + return new RegExp(body, flags); + } + catch (e) { + return null; + } + } + + function massageDisallowedIdentifiers(text: string): string { + // We routinely store the 'valueText' for a token as keys in dictionaries. However, as those + // dictionaries are usually just a javascript object, we run into issues when teh keys collide + // with certain predefined keys they depend on (like __proto__). To workaround this + // we ensure that the valueText of any token is not __proto__ but is instead ___proto__. + // + // We also prepend a _ to any identifier starting with two __ . That allows us to carve + // out the entire namespace of identifiers starting with __ for ourselves. + if (text.charCodeAt(0) === CharacterCodes._ && text.charCodeAt(1) === CharacterCodes._) { + return "_" + text; + } + + return text; + } + + var characterArray: number[] = []; + + function convertEscapes(text: string): string { + characterArray.length = 0; + var result = ""; + + for (var i = 0, n = text.length; i < n; i++) { + var ch = text.charCodeAt(i); + + if (ch === CharacterCodes.backslash) { + i++; + if (i < n) { + ch = text.charCodeAt(i); + switch (ch) { + case CharacterCodes._0: + characterArray.push(CharacterCodes.nullCharacter); + continue; + + case CharacterCodes.b: + characterArray.push(CharacterCodes.backspace); + continue; + + case CharacterCodes.f: + characterArray.push(CharacterCodes.formFeed); + continue; + + case CharacterCodes.n: + characterArray.push(CharacterCodes.lineFeed); + continue; + + case CharacterCodes.r: + characterArray.push(CharacterCodes.carriageReturn); + continue; + + case CharacterCodes.t: + characterArray.push(CharacterCodes.tab); + continue; + + case CharacterCodes.v: + characterArray.push(CharacterCodes.verticalTab); + continue; + + case CharacterCodes.x: + characterArray.push(hexValue(text, /*start:*/ i + 1, /*length:*/ 2)); + i += 2; + continue; + + case CharacterCodes.u: + characterArray.push(hexValue(text, /*start:*/ i + 1, /*length:*/ 4)); + i += 4; + continue; + + case CharacterCodes.carriageReturn: + var nextIndex = i + 1; + if (nextIndex < text.length && text.charCodeAt(nextIndex) === CharacterCodes.lineFeed) { + // Skip the entire \r\n sequence. + i++; + } + continue; + + case CharacterCodes.lineFeed: + case CharacterCodes.paragraphSeparator: + case CharacterCodes.lineSeparator: + // From ES5: LineContinuation is the empty character sequence. + continue; + + default: + // Any other character is ok as well. As per rule: + // EscapeSequence :: CharacterEscapeSequence + // CharacterEscapeSequence :: NonEscapeCharacter + // NonEscapeCharacter :: SourceCharacter but notEscapeCharacter or LineTerminator + // + // Intentional fall through + } + } + } + + characterArray.push(ch); + + if (i && !(i % 1024)) { + result = result.concat(String.fromCharCode.apply(null, characterArray)); + characterArray.length = 0; + } + } + + if (characterArray.length) { + result = result.concat(String.fromCharCode.apply(null, characterArray)); + } + + return result; + } + + function hexValue(text: string, start: number, length: number): number { + var intChar = 0; + for (var i = 0; i < length; i++) { + var ch2 = text.charCodeAt(start + i); + if (!CharacterInfo.isHexDigit(ch2)) { + break; + } + + intChar = (intChar << 4) + CharacterInfo.hexValue(ch2); + } + + return intChar; + } +} + +module TypeScript.Syntax { + export function realizeToken(token: ISyntaxToken, text: ISimpleText): ISyntaxToken { + return new RealizedToken(token.fullStart(), token.kind(), token.isKeywordConvertedToIdentifier(), token.leadingTrivia(text), token.text(), token.trailingTrivia(text)); + } + + export function convertKeywordToIdentifier(token: ISyntaxToken): ISyntaxToken { + return new ConvertedKeywordToken(token); + } + + export function withLeadingTrivia(token: ISyntaxToken, leadingTrivia: ISyntaxTriviaList, text: ISimpleText): ISyntaxToken { + return new RealizedToken(token.fullStart(), token.kind(), token.isKeywordConvertedToIdentifier(), leadingTrivia, token.text(), token.trailingTrivia(text)); + } + + export function withTrailingTrivia(token: ISyntaxToken, trailingTrivia: ISyntaxTriviaList, text: ISimpleText): ISyntaxToken { + return new RealizedToken(token.fullStart(), token.kind(), token.isKeywordConvertedToIdentifier(), token.leadingTrivia(text), token.text(), trailingTrivia); + } + + export function emptyToken(kind: SyntaxKind): ISyntaxToken { + return new EmptyToken(kind); + } + + class EmptyToken implements ISyntaxToken { + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; public _typeBrand: any; + + constructor(private _kind: SyntaxKind) { + } + + public setFullStart(fullStart: number): void { + // An empty token is always at the -1 position. + } + + public kind(): SyntaxKind { + return this._kind; + } + + public clone(): ISyntaxToken { + return new EmptyToken(this.kind()); + } + + // Empty tokens are never incrementally reusable. + public isIncrementallyUnusable() { return true; } + + public isKeywordConvertedToIdentifier() { + return false; + } + + public fullWidth() { return 0; } + + private position(): number { + // It's hard for us to tell the position of an empty token at the eact time we create + // it. For example, we may have: + // + // a / finally + // + // There will be a missing token detected after the forward slash, so it would be + // tempting to set its position as the full-end of hte slash token. However, + // immediately after that, the 'finally' token will be skipped and will be attached + // as skipped text to the forward slash. This means the 'full-end' of the forward + // slash will change, and thus the empty token will now appear to be embedded inside + // another token. This violates are rule that all tokens must only touch at the end, + // and makes enforcing invariants much harder. + // + // To address this we create the empty token with no known position, and then we + // determine what it's position should be based on where it lies in the tree. + // Specifically, we find the previous non-zero-width syntax element, and we consider + // the full-start of this token to be at the full-end of that element. + + var previousElement = this.previousNonZeroWidthElement(); + return previousElement === null ? 0 : fullStart(previousElement) + fullWidth(previousElement); + } + + private previousNonZeroWidthElement(): ISyntaxElement { + var current: ISyntaxElement = this; + while (true) { + var parent = current.parent; + if (parent === null) { + Debug.assert(current.kind() === SyntaxKind.SourceUnit, "We had a node without a parent that was not the root node!"); + + // We walked all the way to the top, and never found a previous element. This + // can happen with code like: + // + // / b; + // + // We will have an empty identifier token as the first token in the tree. In + // this case, return null so that the position of the empty token will be + // considered to be 0. + return null; + } + + // Ok. We have a parent. First, find out which slot we're at in the parent. + for (var i = 0, n = childCount(parent); i < n; i++) { + if (childAt(parent, i) === current) { + break; + } + } + + Debug.assert(i !== n, "Could not find current element in parent's child list!"); + + // Walk backward from this element, looking for a non-zero-width sibling. + for (var j = i - 1; j >= 0; j--) { + var sibling = childAt(parent, j); + if (sibling && fullWidth(sibling) > 0) { + return sibling; + } + } + + // We couldn't find a non-zero-width sibling. We were either the first element, or + // all preceding elements are empty. So, move up to our parent so we we can find + // its preceding sibling. + current = current.parent; + } + } + + public fullStart(): number { + return this.position(); + } + + public text() { return ""; } + public fullText(): string { return ""; } + + public hasLeadingTrivia() { return false; } + public leadingTriviaWidth() { return 0; } + public hasTrailingTrivia() { return false; } + public hasSkippedToken() { return false; } + + public trailingTriviaWidth() { return 0; } + public leadingTrivia(): ISyntaxTriviaList { return Syntax.emptyTriviaList; } + public trailingTrivia(): ISyntaxTriviaList { return Syntax.emptyTriviaList; } + } + + class RealizedToken implements ISyntaxToken { + private _fullStart: number; + private _kind: SyntaxKind; + private _isKeywordConvertedToIdentifier: boolean; + private _leadingTrivia: ISyntaxTriviaList; + private _text: string; + private _trailingTrivia: ISyntaxTriviaList; + + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; public _typeBrand: any; + + constructor(fullStart: number, + kind: SyntaxKind, + isKeywordConvertedToIdentifier: boolean, + leadingTrivia: ISyntaxTriviaList, + text: string, + trailingTrivia: ISyntaxTriviaList) { + this._fullStart = fullStart; + this._kind = kind; + this._isKeywordConvertedToIdentifier = isKeywordConvertedToIdentifier; + this._text = text; + + this._leadingTrivia = leadingTrivia.clone(); + this._trailingTrivia = trailingTrivia.clone(); + + if (!this._leadingTrivia.isShared()) { + this._leadingTrivia.parent = this; + } + + if (!this._trailingTrivia.isShared()) { + this._trailingTrivia.parent = this; + } + } + + public setFullStart(fullStart: number): void { + this._fullStart = fullStart; + } + + public kind(): SyntaxKind { + return this._kind; + } + + public clone(): ISyntaxToken { + return new RealizedToken(this._fullStart, this.kind(), this._isKeywordConvertedToIdentifier, this._leadingTrivia, this._text, this._trailingTrivia); + } + + // Realized tokens are created from the parser. They are *never* incrementally reusable. + public isIncrementallyUnusable() { return true; } + + public isKeywordConvertedToIdentifier() { + return this._isKeywordConvertedToIdentifier; + } + + public fullStart(): number { return this._fullStart; } + public fullWidth(): number { return this._leadingTrivia.fullWidth() + this._text.length + this._trailingTrivia.fullWidth(); } + + public text(): string { return this._text; } + public fullText(): string { return this._leadingTrivia.fullText() + this.text() + this._trailingTrivia.fullText(); } + + public hasLeadingTrivia(): boolean { return this._leadingTrivia.count() > 0; } + public hasTrailingTrivia(): boolean { return this._trailingTrivia.count() > 0; } + + public leadingTriviaWidth(): number { return this._leadingTrivia.fullWidth(); } + public trailingTriviaWidth(): number { return this._trailingTrivia.fullWidth(); } + + public hasSkippedToken(): boolean { return this._leadingTrivia.hasSkippedToken() || this._trailingTrivia.hasSkippedToken(); } + + public leadingTrivia(): ISyntaxTriviaList { return this._leadingTrivia; } + public trailingTrivia(): ISyntaxTriviaList { return this._trailingTrivia; } + } + + class ConvertedKeywordToken implements ISyntaxToken { + public _primaryExpressionBrand: any; public _memberExpressionBrand: any; public _leftHandSideExpressionBrand: any; public _postfixExpressionBrand: any; public _unaryExpressionBrand: any; public _expressionBrand: any; public _typeBrand: any; + + constructor(private underlyingToken: ISyntaxToken) { + } + + public kind() { + return SyntaxKind.IdentifierName; + } + + public setFullStart(fullStart: number): void { + this.underlyingToken.setFullStart(fullStart); + } + + public fullStart(): number { + return this.underlyingToken.fullStart(); + } + + public fullWidth(): number { + return this.underlyingToken.fullWidth(); + } + + public text(): string { + return this.underlyingToken.text(); + } + + private syntaxTreeText(text: ISimpleText) { + var result = text || syntaxTree(this).text; + Debug.assert(result); + return result; + } + + public fullText(text?: ISimpleText): string { + return this.underlyingToken.fullText(this.syntaxTreeText(text)); + } + + public hasLeadingTrivia(): boolean { + return this.underlyingToken.hasLeadingTrivia(); + } + + public hasTrailingTrivia(): boolean { + return this.underlyingToken.hasTrailingTrivia(); + } + + public hasSkippedToken(): boolean { + return this.underlyingToken.hasSkippedToken(); + } + + public leadingTrivia(text?: ISimpleText): ISyntaxTriviaList { + var result = this.underlyingToken.leadingTrivia(this.syntaxTreeText(text)); + result.parent = this; + return result; + } + + public trailingTrivia(text?: ISimpleText): ISyntaxTriviaList { + var result = this.underlyingToken.trailingTrivia(this.syntaxTreeText(text)); + result.parent = this; + return result; + } + + public leadingTriviaWidth(text?: ISimpleText): number { + return this.underlyingToken.leadingTriviaWidth(this.syntaxTreeText(text)); + } + + public trailingTriviaWidth(text?: ISimpleText): number { + return this.underlyingToken.trailingTriviaWidth(this.syntaxTreeText(text)); + } + + public isKeywordConvertedToIdentifier(): boolean { + return true; + } + + public isIncrementallyUnusable(): boolean { + // We're incrementally unusable if our underlying token is unusable. + // For example, we may have: this.public \ + // In this case we will keyword converted to an identifier that is still unusable because + // it has a trailing skipped token. + return this.underlyingToken.isIncrementallyUnusable(); + } + + public clone(): ISyntaxToken { + return new ConvertedKeywordToken(this.underlyingToken); + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxTree.ts b/src/services/syntax/syntaxTree.ts new file mode 100644 index 00000000000..de4c4cf7add --- /dev/null +++ b/src/services/syntax/syntaxTree.ts @@ -0,0 +1,1615 @@ +/// + +module TypeScript { + export var syntaxDiagnosticsTime: number = 0; + + export class SyntaxTree { + private _isConcrete: boolean; + private _sourceUnit: SourceUnitSyntax; + private _isDeclaration: boolean; + private _parserDiagnostics: Diagnostic[]; + private _allDiagnostics: Diagnostic[] = null; + private _fileName: string; + private _lineMap: LineMap; + private _languageVersion: ts.ScriptTarget; + + // Computed on demand. + private _amdDependencies: string[]; + private _isExternalModule: boolean; + + constructor(isConcrete: boolean, + sourceUnit: SourceUnitSyntax, + isDeclaration: boolean, + diagnostics: Diagnostic[], + fileName: string, + public text: ISimpleText, + languageVersion: ts.ScriptTarget) { + this._isConcrete = isConcrete; + this._sourceUnit = sourceUnit; + this._isDeclaration = isDeclaration; + this._parserDiagnostics = diagnostics; + this._fileName = fileName; + this._lineMap = text.lineMap(); + this._languageVersion = languageVersion; + + sourceUnit.syntaxTree = this; + } + + public isConcrete(): boolean { + return this._isConcrete; + } + + public sourceUnit(): SourceUnitSyntax { + return this._sourceUnit; + } + + public isDeclaration(): boolean { + return this._isDeclaration; + } + + private computeDiagnostics(): Diagnostic[] { + if (this._parserDiagnostics.length > 0) { + return this._parserDiagnostics; + } + + // No parser reported diagnostics. Check for any additional grammar diagnostics. + var diagnostics: Diagnostic[] = []; + visitNodeOrToken(new GrammarCheckerWalker(this, diagnostics), this.sourceUnit()); + + return diagnostics; + } + + public diagnostics(): Diagnostic[] { + if (this._allDiagnostics === null) { + var start = new Date().getTime(); + this._allDiagnostics = this.computeDiagnostics(); + syntaxDiagnosticsTime += new Date().getTime() - start; + } + + return this._allDiagnostics; + } + + public fileName(): string { + return this._fileName; + } + + public lineMap(): LineMap { + return this._lineMap; + } + + public languageVersion(): ts.ScriptTarget { + return this._languageVersion; + } + + private cacheSyntaxTreeInfo(): void { + // If we're not keeping around the syntax tree, store the diagnostics and line + // map so they don't have to be recomputed. + var sourceUnit = this.sourceUnit(); + var firstToken = firstSyntaxTreeToken(this); + var leadingTrivia = firstToken.leadingTrivia(this.text); + + this._isExternalModule = externalModuleIndicatorSpanWorker(this, firstToken) !== null; + + var amdDependencies: string[] = []; + for (var i = 0, n = leadingTrivia.count(); i < n; i++) { + var trivia = leadingTrivia.syntaxTriviaAt(i); + if (trivia.isComment()) { + var amdDependency = this.getAmdDependency(trivia.fullText()); + if (amdDependency) { + amdDependencies.push(amdDependency); + } + } + } + + this._amdDependencies = amdDependencies; + } + + private getAmdDependency(comment: string): string { + var amdDependencyRegEx = /^\/\/\/\s* 0) { + var modifiers = parameter.modifiers; + + for (var i = 0, n = modifiers.length; i < n; i++) { + var modifier = modifiers[i]; + + if (this.checkParameterAccessibilityModifier(parameterList, modifier, i)) { + return true; + } + } + } + + return false; + } + + private checkParameterAccessibilityModifier(parameterList: ParameterListSyntax, modifier: ISyntaxToken, modifierIndex: number): boolean { + if (modifier.kind() !== SyntaxKind.PublicKeyword && modifier.kind() !== SyntaxKind.PrivateKeyword) { + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_cannot_appear_on_a_parameter, [modifier.text()]); + return true; + } + else { + if (modifierIndex > 0) { + this.pushDiagnostic(modifier, DiagnosticCode.Accessibility_modifier_already_seen); + return true; + } + } + + return false; + } + + private checkForTrailingComma(list: ISyntaxNodeOrToken[]): boolean { + // If we have at least one child, and we have an even number of children, then that + // means we have an illegal trailing separator. + if (childCount(list) === 0 || childCount(list) % 2 === 1) { + return false; + } + + var child = childAt(list, childCount(list) - 1); + this.pushDiagnostic(child, DiagnosticCode.Trailing_comma_not_allowed); + + return true; + } + + private checkForAtLeastOneElement(parent: ISyntaxElement, list: ISyntaxNodeOrToken[], reportToken: ISyntaxToken, listKind: string): boolean { + if (childCount(list) > 0) { + return false; + } + + this.pushDiagnostic(reportToken, DiagnosticCode._0_list_cannot_be_empty, [listKind]); + return true; + } + + public visitParameterList(node: ParameterListSyntax): void { + if (this.checkParameterListAcessibilityModifiers(node) || + this.checkParameterListOrder(node) || + this.checkForTrailingComma(node.parameters)) { + + return; + } + + super.visitParameterList(node); + } + + public visitHeritageClause(node: HeritageClauseSyntax): void { + if (this.checkForTrailingComma(node.typeNames) || + this.checkForAtLeastOneElement(node, node.typeNames, node.extendsOrImplementsKeyword, SyntaxFacts.getText(node.extendsOrImplementsKeyword.kind()))) { + return; + } + + super.visitHeritageClause(node); + } + + public visitArgumentList(node: ArgumentListSyntax): void { + if (this.checkForTrailingComma(node.arguments)) { + return; + } + + super.visitArgumentList(node); + } + + public visitVariableDeclaration(node: VariableDeclarationSyntax): void { + if (this.checkForAtLeastOneElement(node, node.variableDeclarators, node.varKeyword, getLocalizedText(DiagnosticCode.variable_declaration, null)) || + this.checkForTrailingComma(node.variableDeclarators)) { + return; + } + + super.visitVariableDeclaration(node); + } + + public visitTypeArgumentList(node: TypeArgumentListSyntax): void { + if (this.checkForTrailingComma(node.typeArguments) || + this.checkForAtLeastOneElement(node, node.typeArguments, node.lessThanToken, getLocalizedText(DiagnosticCode.type_argument, null))) { + return; + } + + super.visitTypeArgumentList(node); + } + + public visitTypeParameterList(node: TypeParameterListSyntax): void { + if (this.checkForTrailingComma(node.typeParameters) || + this.checkForAtLeastOneElement(node, node.typeParameters, node.lessThanToken, getLocalizedText(DiagnosticCode.type_parameter, null))) { + return; + } + + super.visitTypeParameterList(node); + } + + private checkIndexSignatureParameter(node: IndexSignatureSyntax): boolean { + if (node.parameters.length !== 1) { + this.pushDiagnostic(node.openBracketToken, DiagnosticCode.Index_signature_must_have_exactly_one_parameter); + return true; + } + + var parameter = node.parameters[0]; + + if (parameter.dotDotDotToken) { + this.pushDiagnostic(parameter, DiagnosticCode.Index_signatures_cannot_have_rest_parameters); + return true; + } + else if (parameter.modifiers.length > 0) { + this.pushDiagnostic(parameter, DiagnosticCode.Index_signature_parameter_cannot_have_accessibility_modifiers); + return true; + } + else if (parameter.questionToken) { + this.pushDiagnostic(parameter, DiagnosticCode.Index_signature_parameter_cannot_have_a_question_mark); + return true; + } + else if (parameter.equalsValueClause) { + this.pushDiagnostic(parameter, DiagnosticCode.Index_signature_parameter_cannot_have_an_initializer); + return true; + } + else if (!parameter.typeAnnotation) { + this.pushDiagnostic(parameter, DiagnosticCode.Index_signature_parameter_must_have_a_type_annotation); + return true; + } + else if (parameter.typeAnnotation.type.kind() !== SyntaxKind.StringKeyword && + parameter.typeAnnotation.type.kind() !== SyntaxKind.NumberKeyword) { + this.pushDiagnostic(parameter, DiagnosticCode.Index_signature_parameter_type_must_be_string_or_number); + return true; + } + + return false; + } + + public visitIndexSignature(node: IndexSignatureSyntax): void { + if (this.checkIndexSignatureParameter(node)) { + return; + } + + if (!node.typeAnnotation) { + this.pushDiagnostic(node, DiagnosticCode.Index_signature_must_have_a_type_annotation); + return; + } + + super.visitIndexSignature(node); + } + + private checkClassDeclarationHeritageClauses(node: ClassDeclarationSyntax): boolean { + var seenExtendsClause = false; + var seenImplementsClause = false; + + for (var i = 0, n = node.heritageClauses.length; i < n; i++) { + Debug.assert(i <= 2); + var heritageClause = node.heritageClauses[i]; + + if (heritageClause.extendsOrImplementsKeyword.kind() === SyntaxKind.ExtendsKeyword) { + if (seenExtendsClause) { + this.pushDiagnostic(heritageClause, DiagnosticCode.extends_clause_already_seen); + return true; + } + + if (seenImplementsClause) { + this.pushDiagnostic(heritageClause, DiagnosticCode.extends_clause_must_precede_implements_clause); + return true; + } + + if (heritageClause.typeNames.length > 1) { + this.pushDiagnostic(heritageClause, DiagnosticCode.Classes_can_only_extend_a_single_class); + return true; + } + + seenExtendsClause = true; + } + else { + Debug.assert(heritageClause.extendsOrImplementsKeyword.kind() === SyntaxKind.ImplementsKeyword); + if (seenImplementsClause) { + this.pushDiagnostic(heritageClause, DiagnosticCode.implements_clause_already_seen); + return true; + } + + seenImplementsClause = true; + } + } + + return false; + } + + private checkForDisallowedDeclareModifier(modifiers: ISyntaxToken[]): boolean { + if (this.inAmbientDeclaration) { + // If we're already in an ambient declaration, then 'declare' is not allowed. + var declareToken = SyntaxUtilities.getToken(modifiers, SyntaxKind.DeclareKeyword); + + if (declareToken) { + this.pushDiagnostic(declareToken, DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context); + return true; + } + } + + return false; + } + + private checkForRequiredDeclareModifier(moduleElement: IModuleElementSyntax, reportToken: ISyntaxToken, modifiers: ISyntaxToken[]): boolean { + if (!this.inAmbientDeclaration && this.syntaxTree.isDeclaration()) { + // We're at the top level in a declaration file, a 'declare' modifiers is required + // on most module elements. + if (!SyntaxUtilities.containsToken(modifiers, SyntaxKind.DeclareKeyword)) { + this.pushDiagnostic(reportToken, DiagnosticCode.A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file); + return true; + } + } + } + + public visitClassDeclaration(node: ClassDeclarationSyntax): void { + if (this.checkForDisallowedDeclareModifier(node.modifiers) || + this.checkForRequiredDeclareModifier(node, node.identifier, node.modifiers) || + this.checkModuleElementModifiers(node.modifiers) || + this.checkClassDeclarationHeritageClauses(node)) { + + return; + } + + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = this.inAmbientDeclaration || this.syntaxTree.isDeclaration() || SyntaxUtilities.containsToken(node.modifiers, SyntaxKind.DeclareKeyword); + super.visitClassDeclaration(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + private checkInterfaceDeclarationHeritageClauses(node: InterfaceDeclarationSyntax): boolean { + var seenExtendsClause = false; + + for (var i = 0, n = node.heritageClauses.length; i < n; i++) { + Debug.assert(i <= 1); + var heritageClause = node.heritageClauses[i]; + + if (heritageClause.extendsOrImplementsKeyword.kind() === SyntaxKind.ExtendsKeyword) { + if (seenExtendsClause) { + this.pushDiagnostic(heritageClause, DiagnosticCode.extends_clause_already_seen); + return true; + } + + seenExtendsClause = true; + } + else { + Debug.assert(heritageClause.extendsOrImplementsKeyword.kind() === SyntaxKind.ImplementsKeyword); + this.pushDiagnostic(heritageClause, DiagnosticCode.Interface_declaration_cannot_have_implements_clause); + return true; + } + } + + return false; + } + + private checkInterfaceModifiers(modifiers: ISyntaxToken[]): boolean { + for (var i = 0, n = modifiers.length; i < n; i++) { + var modifier = modifiers[i]; + if (modifier.kind() === SyntaxKind.DeclareKeyword) { + this.pushDiagnostic(modifier, + DiagnosticCode.A_declare_modifier_cannot_be_used_with_an_interface_declaration); + return true; + } + } + + return false; + } + + public visitInterfaceDeclaration(node: InterfaceDeclarationSyntax): void { + if (this.checkInterfaceModifiers(node.modifiers) || + this.checkModuleElementModifiers(node.modifiers) || + this.checkInterfaceDeclarationHeritageClauses(node)) { + + return; + } + + super.visitInterfaceDeclaration(node); + } + + private checkClassElementModifiers(list: ISyntaxToken[]): boolean { + var seenAccessibilityModifier = false; + var seenStaticModifier = false; + + for (var i = 0, n = list.length; i < n; i++) { + var modifier = list[i]; + if (modifier.kind() === SyntaxKind.PublicKeyword || + modifier.kind() === SyntaxKind.PrivateKeyword) { + + if (seenAccessibilityModifier) { + this.pushDiagnostic(modifier, DiagnosticCode.Accessibility_modifier_already_seen); + return true; + } + + if (seenStaticModifier) { + var previousToken = list[i - 1]; + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_must_precede_1_modifier, [modifier.text(), previousToken.text()]); + return true; + } + + seenAccessibilityModifier = true; + } + else if (modifier.kind() === SyntaxKind.StaticKeyword) { + if (seenStaticModifier) { + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_already_seen, [modifier.text()]); + return true; + } + + seenStaticModifier = true; + } + else { + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_cannot_appear_on_a_class_element, [modifier.text()]); + return true; + } + } + + return false; + } + + public visitMemberVariableDeclaration(node: MemberVariableDeclarationSyntax): void { + if (this.checkClassElementModifiers(node.modifiers)) { + return; + } + + super.visitMemberVariableDeclaration(node); + } + + public visitMemberFunctionDeclaration(node: MemberFunctionDeclarationSyntax): void { + if (this.checkClassElementModifiers(node.modifiers)) { + return; + } + + super.visitMemberFunctionDeclaration(node); + } + + private checkGetAccessorParameter(node: GetAccessorSyntax): boolean { + if (node.callSignature.parameterList.parameters.length !== 0) { + this.pushDiagnostic(node.propertyName, DiagnosticCode.get_accessor_cannot_have_parameters); + return true; + } + + return false; + } + + public visitIndexMemberDeclaration(node: IndexMemberDeclarationSyntax): void { + if (this.checkIndexMemberModifiers(node)) { + return; + } + + super.visitIndexMemberDeclaration(node); + } + + private checkIndexMemberModifiers(node: IndexMemberDeclarationSyntax): boolean { + if (node.modifiers.length > 0) { + this.pushDiagnostic(childAt(node.modifiers, 0), DiagnosticCode.Modifiers_cannot_appear_here); + return true; + } + + return false; + } + + private checkEcmaScriptVersionIsAtLeast(parent: ISyntaxElement, reportToken: ISyntaxToken, languageVersion: ts.ScriptTarget, diagnosticKey: string): boolean { + if (this.syntaxTree.languageVersion() < languageVersion) { + this.pushDiagnostic(reportToken, diagnosticKey); + return true; + } + + return false; + } + + public visitObjectLiteralExpression(node: ObjectLiteralExpressionSyntax): void { + var savedInObjectLiteralExpression = this.inObjectLiteralExpression; + this.inObjectLiteralExpression = true; + super.visitObjectLiteralExpression(node); + this.inObjectLiteralExpression = savedInObjectLiteralExpression; + } + + public visitGetAccessor(node: GetAccessorSyntax): void { + if (this.checkForAccessorDeclarationInAmbientContext(node) || + this.checkEcmaScriptVersionIsAtLeast(node, node.propertyName, ts.ScriptTarget.ES5, DiagnosticCode.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher) || + this.checkForDisallowedModifiers(node, node.modifiers) || + this.checkClassElementModifiers(node.modifiers) || + this.checkForDisallowedAccessorTypeParameters(node.callSignature) || + this.checkGetAccessorParameter(node)) { + return; + } + + super.visitGetAccessor(node); + } + + private checkForDisallowedSetAccessorTypeAnnotation(accessor: SetAccessorSyntax): boolean { + if (accessor.callSignature.typeAnnotation) { + this.pushDiagnostic(accessor.callSignature.typeAnnotation, DiagnosticCode.Type_annotation_cannot_appear_on_a_set_accessor); + return true; + } + + return false; + } + + private checkForDisallowedAccessorTypeParameters(callSignature: CallSignatureSyntax): boolean { + if (callSignature.typeParameterList !== null) { + this.pushDiagnostic(callSignature.typeParameterList, DiagnosticCode.Type_parameters_cannot_appear_on_an_accessor); + return true; + } + + return false; + } + + private checkForAccessorDeclarationInAmbientContext(accessor: ISyntaxNode): boolean { + if (this.inAmbientDeclaration) { + this.pushDiagnostic(accessor, DiagnosticCode.Accessors_are_not_allowed_in_ambient_contexts); + return true; + } + + return false; + } + + private checkSetAccessorParameter(node: SetAccessorSyntax): boolean { + var parameters = node.callSignature.parameterList.parameters; + if (childCount(parameters) !== 1) { + this.pushDiagnostic(node.propertyName, DiagnosticCode.set_accessor_must_have_exactly_one_parameter); + return true; + } + + var parameter = parameters[0]; + + if (parameter.questionToken) { + this.pushDiagnostic(parameter, DiagnosticCode.set_accessor_parameter_cannot_be_optional); + return true; + } + + if (parameter.equalsValueClause) { + this.pushDiagnostic(parameter, DiagnosticCode.set_accessor_parameter_cannot_have_an_initializer); + return true; + } + + if (parameter.dotDotDotToken) { + this.pushDiagnostic(parameter, DiagnosticCode.set_accessor_cannot_have_rest_parameter); + return true; + } + + return false; + } + + public visitSetAccessor(node: SetAccessorSyntax): void { + if (this.checkForAccessorDeclarationInAmbientContext(node) || + this.checkEcmaScriptVersionIsAtLeast(node, node.propertyName, ts.ScriptTarget.ES5, DiagnosticCode.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher) || + this.checkForDisallowedModifiers(node, node.modifiers) || + this.checkClassElementModifiers(node.modifiers) || + this.checkForDisallowedAccessorTypeParameters(node.callSignature) || + this.checkForDisallowedSetAccessorTypeAnnotation(node) || + this.checkSetAccessorParameter(node)) { + return; + } + + super.visitSetAccessor(node); + } + + public visitEnumDeclaration(node: EnumDeclarationSyntax): void { + if (this.checkForDisallowedDeclareModifier(node.modifiers) || + this.checkForRequiredDeclareModifier(node, node.identifier, node.modifiers) || + this.checkModuleElementModifiers(node.modifiers), + this.checkEnumElements(node)) { + + return; + } + + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = this.inAmbientDeclaration || this.syntaxTree.isDeclaration() || SyntaxUtilities.containsToken(node.modifiers, SyntaxKind.DeclareKeyword); + super.visitEnumDeclaration(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + private checkEnumElements(node: EnumDeclarationSyntax): boolean { + var previousValueWasComputed = false; + for (var i = 0, n = childCount(node.enumElements); i < n; i++) { + var child = childAt(node.enumElements, i); + + if (i % 2 === 0) { + var enumElement = child; + + if (!enumElement.equalsValueClause && previousValueWasComputed) { + this.pushDiagnostic(enumElement, DiagnosticCode.Enum_member_must_have_initializer); + return true; + } + + if (enumElement.equalsValueClause) { + var value = enumElement.equalsValueClause.value; + previousValueWasComputed = !Syntax.isIntegerLiteral(value); + } + } + } + + return false; + } + + public visitEnumElement(node: EnumElementSyntax): void { + if (this.inAmbientDeclaration && node.equalsValueClause) { + var expression = node.equalsValueClause.value; + if (!Syntax.isIntegerLiteral(expression)) { + this.pushDiagnostic(node.equalsValueClause.value, DiagnosticCode.Ambient_enum_elements_can_only_have_integer_literal_initializers); + return; + } + } + + super.visitEnumElement(node); + } + + public visitInvocationExpression(node: InvocationExpressionSyntax): void { + if (node.expression.kind() === SyntaxKind.SuperKeyword && + node.argumentList.typeArgumentList !== null) { + this.pushDiagnostic(node, DiagnosticCode.super_invocation_cannot_have_type_arguments); + } + + super.visitInvocationExpression(node); + } + + private checkModuleElementModifiers(modifiers: ISyntaxToken[]): boolean { + var seenExportModifier = false; + var seenDeclareModifier = false; + + for (var i = 0, n = modifiers.length; i < n; i++) { + var modifier = modifiers[i]; + if (modifier.kind() === SyntaxKind.PublicKeyword || + modifier.kind() === SyntaxKind.PrivateKeyword || + modifier.kind() === SyntaxKind.StaticKeyword) { + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_cannot_appear_on_a_module_element, [modifier.text()]); + return true; + } + + if (modifier.kind() === SyntaxKind.DeclareKeyword) { + if (seenDeclareModifier) { + this.pushDiagnostic(modifier, DiagnosticCode.Accessibility_modifier_already_seen); + return; + } + + seenDeclareModifier = true; + } + else if (modifier.kind() === SyntaxKind.ExportKeyword) { + if (seenExportModifier) { + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_already_seen, [modifier.text()]); + return; + } + + if (seenDeclareModifier) { + this.pushDiagnostic(modifier, DiagnosticCode._0_modifier_must_precede_1_modifier, + [SyntaxFacts.getText(SyntaxKind.ExportKeyword), SyntaxFacts.getText(SyntaxKind.DeclareKeyword)]); + return; + } + + seenExportModifier = true; + } + } + + return false; + } + + private checkForDisallowedImportDeclaration(node: ModuleDeclarationSyntax): boolean { + if (!node.stringLiteral) { + for (var i = 0, n = node.moduleElements.length; i < n; i++) { + var child = node.moduleElements[i]; + if (child.kind() === SyntaxKind.ImportDeclaration) { + var importDeclaration = child; + if (importDeclaration.moduleReference.kind() === SyntaxKind.ExternalModuleReference) { + this.pushDiagnostic(importDeclaration, DiagnosticCode.Import_declarations_in_an_internal_module_cannot_reference_an_external_module); + } + } + } + } + + return false; + } + + private checkForDisallowedDeclareModifierOnImportDeclaration(modifiers: ISyntaxToken[]): boolean { + var declareToken = SyntaxUtilities.getToken(modifiers, SyntaxKind.DeclareKeyword); + + if (declareToken) { + this.pushDiagnostic(declareToken, DiagnosticCode.A_declare_modifier_cannot_be_used_with_an_import_declaration); + return true; + } + } + + public visitImportDeclaration(node: ImportDeclarationSyntax): any { + if (this.checkForDisallowedDeclareModifierOnImportDeclaration(node.modifiers) || + this.checkModuleElementModifiers(node.modifiers)) { + return; + } + + super.visitImportDeclaration(node); + } + + public visitModuleDeclaration(node: ModuleDeclarationSyntax): void { + if (this.checkForDisallowedDeclareModifier(node.modifiers) || + this.checkForRequiredDeclareModifier(node, node.stringLiteral ? node.stringLiteral : firstToken(node.name), node.modifiers) || + this.checkModuleElementModifiers(node.modifiers) || + this.checkForDisallowedImportDeclaration(node)) { + + return; + } + + if (node.stringLiteral) { + if (!this.inAmbientDeclaration && !SyntaxUtilities.containsToken(node.modifiers, SyntaxKind.DeclareKeyword)) { + this.pushDiagnostic(node.stringLiteral, DiagnosticCode.Only_ambient_modules_can_use_quoted_names); + return; + } + } + + if (!node.stringLiteral && this.checkForDisallowedExportAssignment(node)) { + return; + } + + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = this.inAmbientDeclaration || this.syntaxTree.isDeclaration() || SyntaxUtilities.containsToken(node.modifiers, SyntaxKind.DeclareKeyword); + super.visitModuleDeclaration(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + private checkForDisallowedExportAssignment(node: ModuleDeclarationSyntax): boolean { + for (var i = 0, n = node.moduleElements.length; i < n; i++) { + var child = node.moduleElements[i]; + + if (child.kind() === SyntaxKind.ExportAssignment) { + this.pushDiagnostic(child, DiagnosticCode.Export_assignment_cannot_be_used_in_internal_modules); + return true; + } + } + + return false; + } + + public visitBlock(node: BlockSyntax): void { + if (this.checkForBlockInAmbientContext(node)) { + return; + } + + var savedInBlock = this.inBlock; + this.inBlock = true; + super.visitBlock(node); + this.inBlock = savedInBlock; + } + + private checkForBlockInAmbientContext(node: BlockSyntax): boolean { + if (this.inAmbientDeclaration || this.syntaxTree.isDeclaration()) { + // Provide a specialized message for a block as a statement versus the block as a + // function body. + if (node.parent.kind() === SyntaxKind.List) { + this.pushDiagnostic(firstToken(node), DiagnosticCode.Statements_are_not_allowed_in_ambient_contexts); + } + else { + this.pushDiagnostic(firstToken(node), DiagnosticCode.A_function_implementation_cannot_be_declared_in_an_ambient_context); + } + + return true; + } + + return false; + } + + private checkForStatementInAmbientContxt(node: IStatementSyntax): boolean { + if (this.inAmbientDeclaration || this.syntaxTree.isDeclaration()) { + this.pushDiagnostic(firstToken(node), DiagnosticCode.Statements_are_not_allowed_in_ambient_contexts); + return true; + } + + return false; + } + + public visitBreakStatement(node: BreakStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node) || + this.checkBreakStatementTarget(node)) { + return; + } + + super.visitBreakStatement(node); + } + + public visitContinueStatement(node: ContinueStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node) || + this.checkContinueStatementTarget(node)) { + return; + } + + super.visitContinueStatement(node); + } + + private checkBreakStatementTarget(node: BreakStatementSyntax): boolean { + // Invalid break statements are considered syntax errors in ES5. + + // Note: the order here is important. If the 'break' has a target, then it can jump to + // any enclosing laballed statment. If it has no target, it must be in an iteration or + // swtich statement. + if (node.identifier) { + var breakableLabels = this.getEnclosingLabels(node, /*breakable:*/ true, /*crossFunctions:*/ false); + + if (!ArrayUtilities.any(breakableLabels, s => tokenValueText(s.identifier) === tokenValueText(node.identifier))) { + // The target of the continue statement wasn't to a reachable label. + // + // Let hte user know, with a specialized message if the target was to an + // unreachable label (as opposed to a non-existed label) + var breakableLabels = this.getEnclosingLabels(node, /*breakable:*/ true, /*crossFunctions:*/ true); + if (ArrayUtilities.any(breakableLabels, s => tokenValueText(s.identifier) === tokenValueText(node.identifier))) { + this.pushDiagnostic(node, DiagnosticCode.Jump_target_cannot_cross_function_boundary); + } + else { + this.pushDiagnostic(node, DiagnosticCode.Jump_target_not_found); + } + + return true; + } + } + else if (!this.inIterationStatement(node, /*crossFunctions:*/ false) && !this.inSwitchStatement(node)) { + if (this.inIterationStatement(node, /*crossFunctions:*/ true)) { + this.pushDiagnostic(node, DiagnosticCode.Jump_target_cannot_cross_function_boundary); + } + else { + this.pushDiagnostic(node, DiagnosticCode.break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement); + } + + return true; + } + + return false; + } + + private inSwitchStatement(ast: ISyntaxElement): boolean { + while (ast) { + if (ast.kind() === SyntaxKind.SwitchStatement) { + return true; + } + + if (SyntaxUtilities.isAnyFunctionExpressionOrDeclaration(ast)) { + return false; + } + + ast = ast.parent; + } + + return false; + } + + private isIterationStatement(ast: ISyntaxElement): boolean { + switch (ast.kind()) { + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.DoStatement: + return true; + } + + return false; + } + + private inIterationStatement(element: ISyntaxElement, crossFunctions: boolean): boolean { + while (element) { + if (this.isIterationStatement(element)) { + return true; + } + + if (!crossFunctions && SyntaxUtilities.isAnyFunctionExpressionOrDeclaration(element)) { + return false; + } + + element = element.parent; + } + + return false; + } + + private getEnclosingLabels(element: ISyntaxElement, breakable: boolean, crossFunctions: boolean): LabeledStatementSyntax[] { + var result: LabeledStatementSyntax[] = []; + + element = element.parent; + while (element) { + if (element.kind() === SyntaxKind.LabeledStatement) { + var labeledStatement = element; + if (breakable) { + // Breakable labels can be placed on any construct + result.push(labeledStatement); + } + else { + // They're asking for continuable labels. Continuable labels must be on + // a loop construct. + if (this.labelIsOnContinuableConstruct(labeledStatement.statement)) { + result.push(labeledStatement); + } + } + } + + if (!crossFunctions && SyntaxUtilities.isAnyFunctionExpressionOrDeclaration(element)) { + break; + } + + element = element.parent; + } + + return result; + } + + private labelIsOnContinuableConstruct(statement: ISyntaxElement): boolean { + switch (statement.kind()) { + case SyntaxKind.LabeledStatement: + // Labels work transitively. i.e. if you have: + // foo: + // bar: + // while(...) + // + // Then both 'foo' and 'bar' are in the label set for 'while' and are thus + // continuable. + return this.labelIsOnContinuableConstruct((statement).statement); + + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.DoStatement: + return true; + + default: + return false; + } + } + + private checkContinueStatementTarget(node: ContinueStatementSyntax): boolean { + // Invalid continue statements are considered syntax errors in ES5. + + if (!this.inIterationStatement(node, /*crossFunctions:*/ false)) { + if (this.inIterationStatement(node, /*crossFunctions:*/ true)) { + this.pushDiagnostic(node, DiagnosticCode.Jump_target_cannot_cross_function_boundary); + } + else { + this.pushDiagnostic(node, DiagnosticCode.continue_statement_can_only_be_used_within_an_enclosing_iteration_statement); + } + + return true; + } + else if (node.identifier) { + var continuableLabels = this.getEnclosingLabels(node, /*breakable:*/ false, /*crossFunctions:*/ false); + + if (!ArrayUtilities.any(continuableLabels, s => tokenValueText(s.identifier) === tokenValueText(node.identifier))) { + // The target of the continue statement wasn't to a reachable label. + // + // Let hte user know, with a specialized message if the target was to an + // unreachable label (as opposed to a non-existed label) + var continuableLabels = this.getEnclosingLabels(node, /*breakable:*/ false, /*crossFunctions:*/ true); + + if (ArrayUtilities.any(continuableLabels, s => tokenValueText(s.identifier) === tokenValueText(node.identifier))) { + this.pushDiagnostic(node, DiagnosticCode.Jump_target_cannot_cross_function_boundary); + } + else { + this.pushDiagnostic(node, DiagnosticCode.Jump_target_not_found); + } + + return true; + } + } + + return false; + } + + public visitDebuggerStatement(node: DebuggerStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitDebuggerStatement(node); + } + + public visitDoStatement(node: DoStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitDoStatement(node); + } + + public visitEmptyStatement(node: EmptyStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitEmptyStatement(node); + } + + public visitExpressionStatement(node: ExpressionStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitExpressionStatement(node); + } + + public visitForInStatement(node: ForInStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node) || + this.checkForInStatementVariableDeclaration(node) || + this.checkForInLeftHandSideExpression(node)) { + + return; + } + + super.visitForInStatement(node); + } + + private checkForInLeftHandSideExpression(node: ForInStatementSyntax): boolean { + if (node.left && !SyntaxUtilities.isLeftHandSizeExpression(node.left)) { + this.pushDiagnostic(node.left, DiagnosticCode.Invalid_left_hand_side_in_for_in_statement); + return true; + } + + return false; + } + + private checkForInStatementVariableDeclaration(node: ForInStatementSyntax): boolean { + // The parser accepts a Variable Declaration in a ForInStatement, but the grammar only + // allows a very restricted form. Specifically, there must be only a single Variable + // Declarator in the Declaration. + if (node.variableDeclaration && node.variableDeclaration.variableDeclarators.length > 1) { + this.pushDiagnostic(node.variableDeclaration, DiagnosticCode.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement); + return true; + } + + return false; + } + + public visitForStatement(node: ForStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitForStatement(node); + } + + public visitIfStatement(node: IfStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitIfStatement(node); + } + + public visitLabeledStatement(node: LabeledStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node) || + this.checkForInvalidLabelIdentifier(node)) { + return; + } + + super.visitLabeledStatement(node); + } + + private checkForInvalidLabelIdentifier(node: LabeledStatementSyntax): boolean { + // Invalid break statements are considered syntax errors in ES5. + + // Note that break/continue are treated differently. ES5 says this about a break statement: + // A program is considered syntactically incorrect if ...: + // + // The program contains a break statement with the optional Identifier, where Identifier + // does not appear in the label set of an enclosing (but not crossing function boundaries) + // **Statement.** + // + // However, it says this about continue statements: + // + // The program contains a continue statement with the optional Identifier, where Identifier + // does not appear in the label set of an enclosing (but not crossing function boundaries) + // **IterationStatement.** + + // In other words, you can 'break' to any enclosing statement. But you can only 'continue' + // to an enclosing *iteration* statement. + var labelIdentifier = tokenValueText(node.identifier); + + var breakableLabels = this.getEnclosingLabels(node, /*breakable:*/ true, /*crossFunctions:*/ false); + + // It is invalid to have a label enclosed in a label of the same name. + var matchingLabel = ArrayUtilities.firstOrDefault(breakableLabels, s => tokenValueText(s.identifier) === labelIdentifier); + if (matchingLabel) { + this.pushDiagnostic(node.identifier, DiagnosticCode.Duplicate_identifier_0, [labelIdentifier]); + return true; + } + + return false; + } + + public visitReturnStatement(node: ReturnStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node) || + this.checkForReturnStatementNotInFunctionBody(node)) { + return; + } + + super.visitReturnStatement(node); + } + + public checkForReturnStatementNotInFunctionBody(node: ReturnStatementSyntax): boolean { + for (var element: ISyntaxElement = node; element; element = element.parent) { + if (SyntaxUtilities.isAnyFunctionExpressionOrDeclaration(element)) { + return false; + } + } + + this.pushDiagnostic(firstToken(node), DiagnosticCode.return_statement_must_be_contained_within_a_function_body); + return true; + } + + public visitSwitchStatement(node: SwitchStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitSwitchStatement(node); + } + + public visitThrowStatement(node: ThrowStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitThrowStatement(node); + } + + public visitTryStatement(node: TryStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitTryStatement(node); + } + + public visitWhileStatement(node: WhileStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node)) { + return; + } + + super.visitWhileStatement(node); + } + + public visitWithStatement(node: WithStatementSyntax): void { + if (this.checkForStatementInAmbientContxt(node) || + this.checkForWithInStrictMode(node)) { + return; + } + + super.visitWithStatement(node); + } + + private checkForWithInStrictMode(node: WithStatementSyntax): boolean { + if (parsedInStrictMode(node)) { + this.pushDiagnostic(firstToken(node), DiagnosticCode.with_statements_are_not_allowed_in_strict_mode); + return true; + } + + return false; + } + + private checkForDisallowedModifiers(parent: ISyntaxElement, modifiers: ISyntaxToken[]): boolean { + if (this.inBlock || this.inObjectLiteralExpression) { + if (modifiers.length > 0) { + this.pushDiagnostic(childAt(modifiers, 0), DiagnosticCode.Modifiers_cannot_appear_here); + return true; + } + } + + return false; + } + + public visitFunctionDeclaration(node: FunctionDeclarationSyntax): void { + if (this.checkForDisallowedDeclareModifier(node.modifiers) || + this.checkForDisallowedModifiers(node, node.modifiers) || + this.checkForRequiredDeclareModifier(node, node.identifier, node.modifiers) || + this.checkModuleElementModifiers(node.modifiers) || + this.checkForDisallowedEvalOrArguments(node, node.identifier)) { + + return; + } + + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = this.inAmbientDeclaration || this.syntaxTree.isDeclaration() || SyntaxUtilities.containsToken(node.modifiers, SyntaxKind.DeclareKeyword); + super.visitFunctionDeclaration(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + public visitFunctionExpression(node: FunctionExpressionSyntax): void { + if (this.checkForDisallowedEvalOrArguments(node, node.identifier)) { + return; + } + + super.visitFunctionExpression(node); + } + + public visitVariableStatement(node: VariableStatementSyntax): void { + if (this.checkForDisallowedDeclareModifier(node.modifiers) || + this.checkForDisallowedModifiers(node, node.modifiers) || + this.checkForRequiredDeclareModifier(node, node.variableDeclaration.varKeyword, node.modifiers) || + this.checkModuleElementModifiers(node.modifiers)) { + + return; + } + + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = this.inAmbientDeclaration || this.syntaxTree.isDeclaration() || SyntaxUtilities.containsToken(node.modifiers, SyntaxKind.DeclareKeyword); + super.visitVariableStatement(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + private checkListSeparators(parent: ISyntaxElement, list: T[], kind: SyntaxKind): boolean { + for (var i = 0, n = childCount(list); i < n; i++) { + var child = childAt(list, i); + if (i % 2 === 1 && child.kind() !== kind) { + this.pushDiagnostic(child, DiagnosticCode._0_expected, [SyntaxFacts.getText(kind)]); + } + } + + return false; + } + + public visitObjectType(node: ObjectTypeSyntax): void { + if (this.checkListSeparators(node, node.typeMembers, SyntaxKind.SemicolonToken)) { + return; + } + + // All code in an object type is implicitly ambient. (i.e. parameters can't have initializer, etc.) + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = true; + super.visitObjectType(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + public visitArrayType(node: ArrayTypeSyntax): void { + // All code in an object type is implicitly ambient. (i.e. parameters can't have initializer, etc.) + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = true; + super.visitArrayType(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + public visitFunctionType(node: FunctionTypeSyntax): void { + // All code in an object type is implicitly ambient. (i.e. parameters can't have initializer, etc.) + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = true; + super.visitFunctionType(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + public visitConstructorType(node: ConstructorTypeSyntax): void { + // All code in an object type is implicitly ambient. (i.e. parameters can't have initializer, etc.) + var savedInAmbientDeclaration = this.inAmbientDeclaration; + this.inAmbientDeclaration = true; + super.visitConstructorType(node); + this.inAmbientDeclaration = savedInAmbientDeclaration; + } + + public visitVariableDeclarator(node: VariableDeclaratorSyntax): void { + if (this.checkVariableDeclaratorInitializer(node) || + this.checkVariableDeclaratorIdentifier(node)) { + return; + } + + super.visitVariableDeclarator(node); + } + + private checkVariableDeclaratorIdentifier(node: VariableDeclaratorSyntax): boolean { + if (node.parent.kind() !== SyntaxKind.MemberVariableDeclaration) { + if (this.checkForDisallowedEvalOrArguments(node, node.propertyName)) { + return true; + } + } + + return false; + } + + private checkVariableDeclaratorInitializer(node: VariableDeclaratorSyntax): boolean { + if (this.inAmbientDeclaration && node.equalsValueClause) { + this.pushDiagnostic(firstToken(node.equalsValueClause.value), DiagnosticCode.Initializers_are_not_allowed_in_ambient_contexts); + return true; + } + + return false; + } + + public visitConstructorDeclaration(node: ConstructorDeclarationSyntax): void { + if (this.checkClassElementModifiers(node.modifiers) || + this.checkConstructorModifiers(node.modifiers) || + this.checkConstructorTypeParameterList(node) || + this.checkConstructorTypeAnnotation(node)) { + + return; + } + + super.visitConstructorDeclaration(node); + } + + private checkConstructorModifiers(modifiers: ISyntaxToken[]): boolean { + for (var i = 0, n = modifiers.length; i < n; i++) { + var child = modifiers[i]; + if (child.kind() !== SyntaxKind.PublicKeyword) { + this.pushDiagnostic(child, DiagnosticCode._0_modifier_cannot_appear_on_a_constructor_declaration, [SyntaxFacts.getText(child.kind())]); + return true; + } + } + + return false; + } + + private checkConstructorTypeParameterList(node: ConstructorDeclarationSyntax): boolean { + if (node.callSignature.typeParameterList) { + this.pushDiagnostic(node.callSignature.typeParameterList, DiagnosticCode.Type_parameters_cannot_appear_on_a_constructor_declaration); + return true; + } + + return false; + } + + private checkConstructorTypeAnnotation(node: ConstructorDeclarationSyntax): boolean { + if (node.callSignature.typeAnnotation) { + this.pushDiagnostic(node.callSignature.typeAnnotation, DiagnosticCode.Type_annotation_cannot_appear_on_a_constructor_declaration); + return true; + } + + return false; + } + + public visitBinaryExpression(node: BinaryExpressionSyntax): void { + if (this.checkIllegalAssignment(node)) { + return; + } + + super.visitBinaryExpression(node); + } + + public visitPrefixUnaryExpression(node: PrefixUnaryExpressionSyntax): void { + if (parsedInStrictMode(node) && this.isPreIncrementOrDecrementExpression(node) && this.isEvalOrArguments(node.operand)) { + this.pushDiagnostic(node.operatorToken, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(node.operand)]); + } + + super.visitPrefixUnaryExpression(node); + } + + public visitPostfixUnaryExpression(node: PostfixUnaryExpressionSyntax): void { + if (parsedInStrictMode(node) && this.isEvalOrArguments(node.operand)) { + this.pushDiagnostic(node.operatorToken, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(node.operand)]); + } + + super.visitPostfixUnaryExpression(node); + } + + public visitParameter(node: ParameterSyntax): void { + if (this.checkForDisallowedEvalOrArguments(node, node.identifier)) { + return; + } + + super.visitParameter(node); + } + + private checkForDisallowedEvalOrArguments(node: ISyntaxNode, token: ISyntaxToken): boolean { + if (token) { + if (parsedInStrictMode(node) && this.isEvalOrArguments(token)) { + this.pushDiagnostic(token, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(token)]); + return true; + } + } + + return false; + } + + private isPreIncrementOrDecrementExpression(node: PrefixUnaryExpressionSyntax) { + switch (node.kind()) { + case SyntaxKind.PreDecrementExpression: + case SyntaxKind.PreIncrementExpression: + return true; + } + + return false; + } + + public visitDeleteExpression(node: DeleteExpressionSyntax): void { + if (parsedInStrictMode(node) && node.expression.kind() === SyntaxKind.IdentifierName) { + this.pushDiagnostic(firstToken(node), DiagnosticCode.delete_cannot_be_called_on_an_identifier_in_strict_mode); + return; + } + + super.visitDeleteExpression(node); + } + + private checkIllegalAssignment(node: BinaryExpressionSyntax): boolean { + if (parsedInStrictMode(node) && SyntaxFacts.isAssignmentOperatorToken(node.operatorToken.kind()) && this.isEvalOrArguments(node.left)) { + this.pushDiagnostic(node.operatorToken, DiagnosticCode.Invalid_use_of_0_in_strict_mode, [this.getEvalOrArguments(node.left)]); + return true; + } + + return false; + } + + private getEvalOrArguments(expr: IExpressionSyntax): string { + if (expr.kind() === SyntaxKind.IdentifierName) { + var text = tokenValueText(expr); + if (text === "eval" || text === "arguments") { + return text; + } + } + + return null; + } + + private isEvalOrArguments(expr: IExpressionSyntax): boolean { + return this.getEvalOrArguments(expr) !== null; + } + + public visitConstraint(node: ConstraintSyntax): void { + if (this.checkConstraintType(node)) { + return; + } + + super.visitConstraint(node); + } + + private checkConstraintType(node: ConstraintSyntax): boolean { + if (!SyntaxFacts.isType(node.typeOrExpression.kind())) { + this.pushDiagnostic(node.typeOrExpression, DiagnosticCode.Type_expected); + return true; + } + + return false; + } + } + + function firstSyntaxTreeToken(syntaxTree: SyntaxTree) { + // We don't just access the firstToken of the tree here as the tree may be abstract and may + // not have a firstToken in it. + var scanner = Scanner.createScanner(syntaxTree.languageVersion(), syntaxTree.text, () => { }); + return scanner.scan(/*allowContextualToken:*/ false); + } + + export function externalModuleIndicatorSpan(syntaxTree: SyntaxTree): TextSpan { + var firstToken = firstSyntaxTreeToken(syntaxTree); + return externalModuleIndicatorSpanWorker(syntaxTree, firstToken); + } + + export function externalModuleIndicatorSpanWorker(syntaxTree: SyntaxTree, firstToken: ISyntaxToken) { + var leadingTrivia = firstToken.leadingTrivia(syntaxTree.text); + return implicitImportSpan(leadingTrivia) || topLevelImportOrExportSpan(syntaxTree.sourceUnit()); + } + + function implicitImportSpan(sourceUnitLeadingTrivia: ISyntaxTriviaList): TextSpan { + for (var i = 0, n = sourceUnitLeadingTrivia.count(); i < n; i++) { + var trivia = sourceUnitLeadingTrivia.syntaxTriviaAt(i); + + if (trivia.isComment()) { + var span = implicitImportSpanWorker(trivia); + if (span) { + return span; + } + } + } + + return null; + } + + function implicitImportSpanWorker(trivia: ISyntaxTrivia): TextSpan { + var implicitImportRegEx = /^(\/\/\/\s*/gim; + var match = implicitImportRegEx.exec(trivia.fullText()); + + if (match) { + return new TextSpan(trivia.fullStart(), trivia.fullWidth()); + } + + return null; + } + + function topLevelImportOrExportSpan(node: SourceUnitSyntax): TextSpan { + for (var i = 0, n = node.moduleElements.length; i < n; i++) { + var moduleElement = node.moduleElements[i]; + + var _firstToken = firstToken(moduleElement); + if (_firstToken !== null && _firstToken.kind() === SyntaxKind.ExportKeyword) { + return new TextSpan(start(_firstToken), width(_firstToken)); + } + + if (moduleElement.kind() === SyntaxKind.ImportDeclaration) { + var importDecl = moduleElement; + if (importDecl.moduleReference.kind() === SyntaxKind.ExternalModuleReference) { + var literal = (importDecl.moduleReference).stringLiteral; + return new TextSpan(start(literal), width(literal)); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxTrivia.ts b/src/services/syntax/syntaxTrivia.ts new file mode 100644 index 00000000000..1c8d79cf6bf --- /dev/null +++ b/src/services/syntax/syntaxTrivia.ts @@ -0,0 +1,178 @@ +/// + +module TypeScript { + export interface ISyntaxTrivia { + parent?: ISyntaxTriviaList; + kind(): SyntaxKind; + + isWhitespace(): boolean; + isComment(): boolean; + isNewLine(): boolean; + isSkippedToken(): boolean; + + fullStart(): number; + fullWidth(): number; + + // Text for this trivia. + fullText(): string; + + // If this is a skipped token trivia, then this was the token that was skipped. + skippedToken(): ISyntaxToken; + + clone(): ISyntaxTrivia; + } +} + +module TypeScript.Syntax { + class AbstractTrivia implements ISyntaxTrivia { + constructor(private _kind: SyntaxKind) { + } + + public kind(): SyntaxKind { + return this._kind; + } + + public clone(): ISyntaxTrivia { + throw Errors.abstract(); + } + + public fullStart(): number { + throw Errors.abstract(); + } + + public fullWidth(): number { + throw Errors.abstract(); + } + + public fullText(): string { + throw Errors.abstract(); + } + + public skippedToken(): ISyntaxToken { + throw Errors.abstract(); + } + + public isWhitespace(): boolean { + return this.kind() === SyntaxKind.WhitespaceTrivia; + } + + public isComment(): boolean { + return this.kind() === SyntaxKind.SingleLineCommentTrivia || this.kind() === SyntaxKind.MultiLineCommentTrivia; + } + + public isNewLine(): boolean { + return this.kind() === SyntaxKind.NewLineTrivia; + } + + public isSkippedToken(): boolean { + return this.kind() === SyntaxKind.SkippedTokenTrivia; + } + } + + class SkippedTokenTrivia extends AbstractTrivia { + constructor(private _skippedToken: ISyntaxToken, private _fullText: string) { + super(SyntaxKind.SkippedTokenTrivia); + + _skippedToken.parent = this; + } + + public clone(): ISyntaxTrivia { + return new SkippedTokenTrivia(this._skippedToken.clone(), this._fullText); + } + + public fullStart(): number { + return this._skippedToken.fullStart(); + } + + public fullWidth(): number { + return this.fullText().length; + } + + public fullText(): string { + return this._fullText; + } + + public skippedToken(): ISyntaxToken { + return this._skippedToken; + } + } + + class DeferredTrivia extends AbstractTrivia { + constructor(kind: SyntaxKind, private _text: ISimpleText, private _fullStart: number, private _fullWidth: number) { + super(kind); + } + + public clone(): ISyntaxTrivia { + return new DeferredTrivia(this.kind(), this._text, this._fullStart, this._fullWidth); + } + + public fullStart(): number { + return this._fullStart; + } + + public fullWidth(): number { + return this._fullWidth; + } + + public fullText(): string { + return this._text.substr(this._fullStart, this._fullWidth); + } + + public skippedToken(): ISyntaxToken { + throw Errors.invalidOperation(); + } + } + + export function deferredTrivia(kind: SyntaxKind, text: ISimpleText, fullStart: number, fullWidth: number): ISyntaxTrivia { + return new DeferredTrivia(kind, text, fullStart, fullWidth); + } + + export function skippedTokenTrivia(token: ISyntaxToken, text: ISimpleText): ISyntaxTrivia { + Debug.assert(!token.hasLeadingTrivia()); + Debug.assert(!token.hasTrailingTrivia()); + Debug.assert(token.fullWidth() > 0); + return new SkippedTokenTrivia(token, token.fullText(text)); + } + + // Breaks a multiline trivia up into individual line components. If the trivia doesn't span + // any lines, then the result will be a single string with the entire text of the trivia. + // Otherwise, there will be one entry in the array for each line spanned by the trivia. Each + // entry will contain the line separator at the end of the string. + export function splitMultiLineCommentTriviaIntoMultipleLines(trivia: ISyntaxTrivia): string[] { + // Debug.assert(trivia.kind === SyntaxKind.MultiLineCommentTrivia); + var result: string[] = []; + + var triviaText = trivia.fullText(); + var currentIndex = 0; + + for (var i = 0; i < triviaText.length; i++) { + var ch = triviaText.charCodeAt(i); + + // When we run into a newline for the first time, create the string builder and copy + // all the values up to this newline into it. + var isCarriageReturnLineFeed = false; + switch (ch) { + case CharacterCodes.carriageReturn: + if (i < triviaText.length - 1 && triviaText.charCodeAt(i + 1) === CharacterCodes.lineFeed) { + // Consume the \r + i++; + } + + // Fall through. + + case CharacterCodes.lineFeed: + case CharacterCodes.paragraphSeparator: + case CharacterCodes.lineSeparator: + // Eat from the last starting position through to the end of the newline. + result.push(triviaText.substring(currentIndex, i + 1)); + + // Set the current index to *after* the newline. + currentIndex = i + 1; + continue; + } + } + + result.push(triviaText.substring(currentIndex)); + return result; + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxTriviaList.ts b/src/services/syntax/syntaxTriviaList.ts new file mode 100644 index 00000000000..937b66b129f --- /dev/null +++ b/src/services/syntax/syntaxTriviaList.ts @@ -0,0 +1,246 @@ +/// + +module TypeScript { + export interface ISyntaxTriviaList { + parent?: ISyntaxToken; + + isShared(): boolean; + + count(): number; + syntaxTriviaAt(index: number): ISyntaxTrivia; + + // With of this trivia list. + fullWidth(): number; + + // Text for this trivia list. + fullText(): string; + + hasComment(): boolean; + hasNewLine(): boolean; + hasSkippedToken(): boolean; + + last(): ISyntaxTrivia; + toArray(): ISyntaxTrivia[]; + + clone(): ISyntaxTriviaList; + } +} + +module TypeScript.Syntax { + class EmptyTriviaList implements ISyntaxTriviaList { + public kind() { + return SyntaxKind.TriviaList; + } + + public isShared(): boolean { + return true; + } + + public count(): number { + return 0; + } + + public syntaxTriviaAt(index: number): ISyntaxTrivia { + throw Errors.argumentOutOfRange("index"); + } + + public last(): ISyntaxTrivia { + throw Errors.argumentOutOfRange("index"); + } + + public fullWidth(): number { + return 0; + } + + public fullText(): string { + return ""; + } + + public hasComment(): boolean { + return false; + } + + public hasNewLine(): boolean { + return false; + } + + public hasSkippedToken(): boolean { + return false; + } + + public toArray(): ISyntaxTrivia[] { + return []; + } + + public clone() { + return this; + } + }; + + export var emptyTriviaList: ISyntaxTriviaList = new EmptyTriviaList(); + + function isComment(trivia: ISyntaxTrivia): boolean { + return trivia.kind() === SyntaxKind.MultiLineCommentTrivia || trivia.kind() === SyntaxKind.SingleLineCommentTrivia; + } + + class SingletonSyntaxTriviaList implements ISyntaxTriviaList { + private item: ISyntaxTrivia; + + constructor(item: ISyntaxTrivia) { + this.item = item.clone(); + this.item.parent = this; + } + + public kind() { + return SyntaxKind.TriviaList; + } + + public isShared(): boolean { + return false; + } + + public count(): number { + return 1; + } + + public syntaxTriviaAt(index: number): ISyntaxTrivia { + if (index !== 0) { + throw Errors.argumentOutOfRange("index"); + } + + return this.item; + } + + public last(): ISyntaxTrivia { + return this.item; + } + + public fullWidth(): number { + return this.item.fullWidth(); + } + + public fullText(): string { + return this.item.fullText(); + } + + public hasComment(): boolean { + return isComment(this.item); + } + + public hasNewLine(): boolean { + return this.item.kind() === SyntaxKind.NewLineTrivia; + } + + public hasSkippedToken(): boolean { + return this.item.kind() === SyntaxKind.SkippedTokenTrivia; + } + + public toArray(): ISyntaxTrivia[] { + return [this.item]; + } + + public clone(): ISyntaxTriviaList { + return new SingletonSyntaxTriviaList(this.item.clone()); + } + } + + class NormalSyntaxTriviaList implements ISyntaxTriviaList { + private trivia: ISyntaxTrivia[]; + + constructor(trivia: ISyntaxTrivia[]) { + this.trivia = trivia.map(t => { + var cloned = t.clone(); + cloned.parent = this; + return cloned; + }); + } + + public kind() { + return SyntaxKind.TriviaList; + } + + public isShared(): boolean { + return false; + } + + public count() { + return this.trivia.length; + } + + public syntaxTriviaAt(index: number): ISyntaxTrivia { + if (index < 0 || index >= this.trivia.length) { + throw Errors.argumentOutOfRange("index"); + } + + return this.trivia[index]; + } + + public last(): ISyntaxTrivia { + return this.trivia[this.trivia.length - 1]; + } + + public fullWidth(): number { + return ArrayUtilities.sum(this.trivia, t => t.fullWidth()); + } + + public fullText(): string { + var result: string[] = []; + + for (var i = 0, n = this.trivia.length; i < n; i++) { + result.push(this.trivia[i].fullText()); + } + + return result.join(""); + } + + public hasComment(): boolean { + for (var i = 0; i < this.trivia.length; i++) { + if (isComment(this.trivia[i])) { + return true; + } + } + + return false; + } + + public hasNewLine(): boolean { + for (var i = 0; i < this.trivia.length; i++) { + if (this.trivia[i].kind() === SyntaxKind.NewLineTrivia) { + return true; + } + } + + return false; + } + + public hasSkippedToken(): boolean { + for (var i = 0; i < this.trivia.length; i++) { + if (this.trivia[i].kind() === SyntaxKind.SkippedTokenTrivia) { + return true; + } + } + + return false; + } + + public toArray(): ISyntaxTrivia[] { + return this.trivia.slice(0); + } + + public clone(): ISyntaxTriviaList { + return new NormalSyntaxTriviaList(this.trivia.map(t => t.clone())); + } + } + + export function triviaList(trivia: ISyntaxTrivia[]): ISyntaxTriviaList { + if (trivia === undefined || trivia === null || trivia.length === 0) { + return Syntax.emptyTriviaList; + } + + if (trivia.length === 1) { + return new SingletonSyntaxTriviaList(trivia[0]); + } + + return new NormalSyntaxTriviaList(trivia); + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxUtilities.ts b/src/services/syntax/syntaxUtilities.ts new file mode 100644 index 00000000000..c5e122e927b --- /dev/null +++ b/src/services/syntax/syntaxUtilities.ts @@ -0,0 +1,336 @@ +/// + +module TypeScript { + export class SyntaxUtilities { + public static isAnyFunctionExpressionOrDeclaration(ast: ISyntaxElement): boolean { + switch (ast.kind()) { + case SyntaxKind.SimpleArrowFunctionExpression: + case SyntaxKind.ParenthesizedArrowFunctionExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.FunctionPropertyAssignment: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return true; + } + + return false; + } + + public static isLastTokenOnLine(token: ISyntaxToken, text: ISimpleText): boolean { + var _nextToken = nextToken(token, text); + if (_nextToken === null) { + return true; + } + + var lineMap = text.lineMap(); + var tokenLine = lineMap.getLineNumberFromPosition(end(token, text)); + var nextTokenLine = lineMap.getLineNumberFromPosition(start(_nextToken, text)); + + return tokenLine !== nextTokenLine; + } + + public static isLeftHandSizeExpression(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.MemberAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.ObjectCreationExpression: + case SyntaxKind.InvocationExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.IdentifierName: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.SuperKeyword: + return true; + } + } + + return false; + } + + public static isExpression(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.IdentifierName: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.SuperKeyword: + + case SyntaxKind.PlusExpression: + case SyntaxKind.NegateExpression: + case SyntaxKind.BitwiseNotExpression: + case SyntaxKind.LogicalNotExpression: + case SyntaxKind.PreIncrementExpression: + case SyntaxKind.PreDecrementExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.CommaExpression: + case SyntaxKind.AssignmentExpression: + case SyntaxKind.AddAssignmentExpression: + case SyntaxKind.SubtractAssignmentExpression: + case SyntaxKind.MultiplyAssignmentExpression: + case SyntaxKind.DivideAssignmentExpression: + case SyntaxKind.ModuloAssignmentExpression: + case SyntaxKind.AndAssignmentExpression: + case SyntaxKind.ExclusiveOrAssignmentExpression: + case SyntaxKind.OrAssignmentExpression: + case SyntaxKind.LeftShiftAssignmentExpression: + case SyntaxKind.SignedRightShiftAssignmentExpression: + case SyntaxKind.UnsignedRightShiftAssignmentExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.LogicalOrExpression: + case SyntaxKind.LogicalAndExpression: + case SyntaxKind.BitwiseOrExpression: + case SyntaxKind.BitwiseExclusiveOrExpression: + case SyntaxKind.BitwiseAndExpression: + case SyntaxKind.EqualsWithTypeConversionExpression: + case SyntaxKind.NotEqualsWithTypeConversionExpression: + case SyntaxKind.EqualsExpression: + case SyntaxKind.NotEqualsExpression: + case SyntaxKind.LessThanExpression: + case SyntaxKind.GreaterThanExpression: + case SyntaxKind.LessThanOrEqualExpression: + case SyntaxKind.GreaterThanOrEqualExpression: + case SyntaxKind.InstanceOfExpression: + case SyntaxKind.InExpression: + case SyntaxKind.LeftShiftExpression: + case SyntaxKind.SignedRightShiftExpression: + case SyntaxKind.UnsignedRightShiftExpression: + case SyntaxKind.MultiplyExpression: + case SyntaxKind.DivideExpression: + case SyntaxKind.ModuloExpression: + case SyntaxKind.AddExpression: + case SyntaxKind.SubtractExpression: + case SyntaxKind.PostIncrementExpression: + case SyntaxKind.PostDecrementExpression: + case SyntaxKind.MemberAccessExpression: + case SyntaxKind.InvocationExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ObjectCreationExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ParenthesizedArrowFunctionExpression: + case SyntaxKind.SimpleArrowFunctionExpression: + case SyntaxKind.CastExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.OmittedExpression: + return true; + } + } + + return false; + } + + public static isSwitchClause(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.CaseSwitchClause: + case SyntaxKind.DefaultSwitchClause: + return true; + } + } + + return false; + } + + public static isTypeMember(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.ConstructSignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.PropertySignature: + case SyntaxKind.CallSignature: + return true; + } + } + + return false; + } + + public static isClassElement(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.IndexMemberDeclaration: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.MemberVariableDeclaration: + return true; + } + } + + return false; + } + + public static isModuleElement(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportAssignment: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + + // Keep in sync with isStatement: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.Block: + case SyntaxKind.IfStatement: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.BreakStatement: + case SyntaxKind.ContinueStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.EmptyStatement: + case SyntaxKind.TryStatement: + case SyntaxKind.LabeledStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.DebuggerStatement: + return true; + } + } + + return false; + } + + public static isStatement(element: ISyntaxElement) { + if (element) { + switch (element.kind()) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.Block: + case SyntaxKind.IfStatement: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.BreakStatement: + case SyntaxKind.ContinueStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.EmptyStatement: + case SyntaxKind.TryStatement: + case SyntaxKind.LabeledStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.DebuggerStatement: + return true; + } + } + + return false; + } + + public static isAngleBracket(positionedElement: ISyntaxElement): boolean { + var element = positionedElement; + var parent = positionedElement.parent; + if (parent !== null && (element.kind() === SyntaxKind.LessThanToken || element.kind() === SyntaxKind.GreaterThanToken)) { + switch (parent.kind()) { + case SyntaxKind.TypeArgumentList: + case SyntaxKind.TypeParameterList: + case SyntaxKind.CastExpression: + return true; + } + } + + return false; + } + + public static getToken(list: ISyntaxToken[], kind: SyntaxKind): ISyntaxToken { + for (var i = 0, n = list.length; i < n; i++) { + var token = list[i]; + if (token.kind() === kind) { + return token; + } + } + + return null; + } + + public static containsToken(list: ISyntaxToken[], kind: SyntaxKind): boolean { + return SyntaxUtilities.getToken(list, kind) !== null; + } + + public static hasExportKeyword(moduleElement: IModuleElementSyntax): boolean { + return SyntaxUtilities.getExportKeyword(moduleElement) !== null; + } + + public static getExportKeyword(moduleElement: IModuleElementSyntax): ISyntaxToken { + switch (moduleElement.kind()) { + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ImportDeclaration: + return SyntaxUtilities.getToken((moduleElement).modifiers, SyntaxKind.ExportKeyword); + default: + return null; + } + } + + public static isAmbientDeclarationSyntax(positionNode: ISyntaxNode): boolean { + if (!positionNode) { + return false; + } + + var node = positionNode; + switch (node.kind()) { + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.EnumDeclaration: + if (SyntaxUtilities.containsToken((node).modifiers, SyntaxKind.DeclareKeyword)) { + return true; + } + // Fall through to check if syntax container is ambient + + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.MemberFunctionDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MemberVariableDeclaration: + if (SyntaxUtilities.isClassElement(node) || SyntaxUtilities.isModuleElement(node)) { + return SyntaxUtilities.isAmbientDeclarationSyntax(Syntax.containingNode(positionNode)); + } + + case SyntaxKind.EnumElement: + return SyntaxUtilities.isAmbientDeclarationSyntax(Syntax.containingNode(Syntax.containingNode(positionNode))); + + default: + return SyntaxUtilities.isAmbientDeclarationSyntax(Syntax.containingNode(positionNode)); + } + } + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxVisitor.generated.ts b/src/services/syntax/syntaxVisitor.generated.ts new file mode 100644 index 00000000000..7081499105d --- /dev/null +++ b/src/services/syntax/syntaxVisitor.generated.ts @@ -0,0 +1,190 @@ +/// + +module TypeScript { + export function visitNodeOrToken(visitor: ISyntaxVisitor, element: ISyntaxNodeOrToken): any { + if (element === null) { return null; } + if (isToken(element)) { return visitor.visitToken(element); } + switch (element.kind()) { + case SyntaxKind.SourceUnit: return visitor.visitSourceUnit(element); + case SyntaxKind.QualifiedName: return visitor.visitQualifiedName(element); + case SyntaxKind.ObjectType: return visitor.visitObjectType(element); + case SyntaxKind.FunctionType: return visitor.visitFunctionType(element); + case SyntaxKind.ArrayType: return visitor.visitArrayType(element); + case SyntaxKind.ConstructorType: return visitor.visitConstructorType(element); + case SyntaxKind.GenericType: return visitor.visitGenericType(element); + case SyntaxKind.TypeQuery: return visitor.visitTypeQuery(element); + case SyntaxKind.InterfaceDeclaration: return visitor.visitInterfaceDeclaration(element); + case SyntaxKind.FunctionDeclaration: return visitor.visitFunctionDeclaration(element); + case SyntaxKind.ModuleDeclaration: return visitor.visitModuleDeclaration(element); + case SyntaxKind.ClassDeclaration: return visitor.visitClassDeclaration(element); + case SyntaxKind.EnumDeclaration: return visitor.visitEnumDeclaration(element); + case SyntaxKind.ImportDeclaration: return visitor.visitImportDeclaration(element); + case SyntaxKind.ExportAssignment: return visitor.visitExportAssignment(element); + case SyntaxKind.MemberFunctionDeclaration: return visitor.visitMemberFunctionDeclaration(element); + case SyntaxKind.MemberVariableDeclaration: return visitor.visitMemberVariableDeclaration(element); + case SyntaxKind.ConstructorDeclaration: return visitor.visitConstructorDeclaration(element); + case SyntaxKind.IndexMemberDeclaration: return visitor.visitIndexMemberDeclaration(element); + case SyntaxKind.GetAccessor: return visitor.visitGetAccessor(element); + case SyntaxKind.SetAccessor: return visitor.visitSetAccessor(element); + case SyntaxKind.PropertySignature: return visitor.visitPropertySignature(element); + case SyntaxKind.CallSignature: return visitor.visitCallSignature(element); + case SyntaxKind.ConstructSignature: return visitor.visitConstructSignature(element); + case SyntaxKind.IndexSignature: return visitor.visitIndexSignature(element); + case SyntaxKind.MethodSignature: return visitor.visitMethodSignature(element); + case SyntaxKind.Block: return visitor.visitBlock(element); + case SyntaxKind.IfStatement: return visitor.visitIfStatement(element); + case SyntaxKind.VariableStatement: return visitor.visitVariableStatement(element); + case SyntaxKind.ExpressionStatement: return visitor.visitExpressionStatement(element); + case SyntaxKind.ReturnStatement: return visitor.visitReturnStatement(element); + case SyntaxKind.SwitchStatement: return visitor.visitSwitchStatement(element); + case SyntaxKind.BreakStatement: return visitor.visitBreakStatement(element); + case SyntaxKind.ContinueStatement: return visitor.visitContinueStatement(element); + case SyntaxKind.ForStatement: return visitor.visitForStatement(element); + case SyntaxKind.ForInStatement: return visitor.visitForInStatement(element); + case SyntaxKind.EmptyStatement: return visitor.visitEmptyStatement(element); + case SyntaxKind.ThrowStatement: return visitor.visitThrowStatement(element); + case SyntaxKind.WhileStatement: return visitor.visitWhileStatement(element); + case SyntaxKind.TryStatement: return visitor.visitTryStatement(element); + case SyntaxKind.LabeledStatement: return visitor.visitLabeledStatement(element); + case SyntaxKind.DoStatement: return visitor.visitDoStatement(element); + case SyntaxKind.DebuggerStatement: return visitor.visitDebuggerStatement(element); + case SyntaxKind.WithStatement: return visitor.visitWithStatement(element); + case SyntaxKind.PreIncrementExpression: case SyntaxKind.PreDecrementExpression: case SyntaxKind.PlusExpression: case SyntaxKind.NegateExpression: case SyntaxKind.BitwiseNotExpression: case SyntaxKind.LogicalNotExpression: + return visitor.visitPrefixUnaryExpression(element); + case SyntaxKind.DeleteExpression: return visitor.visitDeleteExpression(element); + case SyntaxKind.TypeOfExpression: return visitor.visitTypeOfExpression(element); + case SyntaxKind.VoidExpression: return visitor.visitVoidExpression(element); + case SyntaxKind.ConditionalExpression: return visitor.visitConditionalExpression(element); + case SyntaxKind.MultiplyExpression: case SyntaxKind.DivideExpression: case SyntaxKind.ModuloExpression: case SyntaxKind.AddExpression: case SyntaxKind.SubtractExpression: case SyntaxKind.LeftShiftExpression: case SyntaxKind.SignedRightShiftExpression: case SyntaxKind.UnsignedRightShiftExpression: case SyntaxKind.LessThanExpression: case SyntaxKind.GreaterThanExpression: case SyntaxKind.LessThanOrEqualExpression: case SyntaxKind.GreaterThanOrEqualExpression: case SyntaxKind.InstanceOfExpression: case SyntaxKind.InExpression: case SyntaxKind.EqualsWithTypeConversionExpression: case SyntaxKind.NotEqualsWithTypeConversionExpression: case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: case SyntaxKind.BitwiseAndExpression: case SyntaxKind.BitwiseExclusiveOrExpression: case SyntaxKind.BitwiseOrExpression: case SyntaxKind.LogicalAndExpression: case SyntaxKind.LogicalOrExpression: case SyntaxKind.OrAssignmentExpression: case SyntaxKind.AndAssignmentExpression: case SyntaxKind.ExclusiveOrAssignmentExpression: case SyntaxKind.LeftShiftAssignmentExpression: case SyntaxKind.SignedRightShiftAssignmentExpression: case SyntaxKind.UnsignedRightShiftAssignmentExpression: case SyntaxKind.AddAssignmentExpression: case SyntaxKind.SubtractAssignmentExpression: case SyntaxKind.MultiplyAssignmentExpression: case SyntaxKind.DivideAssignmentExpression: case SyntaxKind.ModuloAssignmentExpression: case SyntaxKind.AssignmentExpression: case SyntaxKind.CommaExpression: + return visitor.visitBinaryExpression(element); + case SyntaxKind.PostIncrementExpression: case SyntaxKind.PostDecrementExpression: + return visitor.visitPostfixUnaryExpression(element); + case SyntaxKind.MemberAccessExpression: return visitor.visitMemberAccessExpression(element); + case SyntaxKind.InvocationExpression: return visitor.visitInvocationExpression(element); + case SyntaxKind.ArrayLiteralExpression: return visitor.visitArrayLiteralExpression(element); + case SyntaxKind.ObjectLiteralExpression: return visitor.visitObjectLiteralExpression(element); + case SyntaxKind.ObjectCreationExpression: return visitor.visitObjectCreationExpression(element); + case SyntaxKind.ParenthesizedExpression: return visitor.visitParenthesizedExpression(element); + case SyntaxKind.ParenthesizedArrowFunctionExpression: return visitor.visitParenthesizedArrowFunctionExpression(element); + case SyntaxKind.SimpleArrowFunctionExpression: return visitor.visitSimpleArrowFunctionExpression(element); + case SyntaxKind.CastExpression: return visitor.visitCastExpression(element); + case SyntaxKind.ElementAccessExpression: return visitor.visitElementAccessExpression(element); + case SyntaxKind.FunctionExpression: return visitor.visitFunctionExpression(element); + case SyntaxKind.OmittedExpression: return visitor.visitOmittedExpression(element); + case SyntaxKind.VariableDeclaration: return visitor.visitVariableDeclaration(element); + case SyntaxKind.VariableDeclarator: return visitor.visitVariableDeclarator(element); + case SyntaxKind.ArgumentList: return visitor.visitArgumentList(element); + case SyntaxKind.ParameterList: return visitor.visitParameterList(element); + case SyntaxKind.TypeArgumentList: return visitor.visitTypeArgumentList(element); + case SyntaxKind.TypeParameterList: return visitor.visitTypeParameterList(element); + case SyntaxKind.ExtendsHeritageClause: case SyntaxKind.ImplementsHeritageClause: + return visitor.visitHeritageClause(element); + case SyntaxKind.EqualsValueClause: return visitor.visitEqualsValueClause(element); + case SyntaxKind.CaseSwitchClause: return visitor.visitCaseSwitchClause(element); + case SyntaxKind.DefaultSwitchClause: return visitor.visitDefaultSwitchClause(element); + case SyntaxKind.ElseClause: return visitor.visitElseClause(element); + case SyntaxKind.CatchClause: return visitor.visitCatchClause(element); + case SyntaxKind.FinallyClause: return visitor.visitFinallyClause(element); + case SyntaxKind.TypeParameter: return visitor.visitTypeParameter(element); + case SyntaxKind.Constraint: return visitor.visitConstraint(element); + case SyntaxKind.SimplePropertyAssignment: return visitor.visitSimplePropertyAssignment(element); + case SyntaxKind.FunctionPropertyAssignment: return visitor.visitFunctionPropertyAssignment(element); + case SyntaxKind.Parameter: return visitor.visitParameter(element); + case SyntaxKind.EnumElement: return visitor.visitEnumElement(element); + case SyntaxKind.TypeAnnotation: return visitor.visitTypeAnnotation(element); + case SyntaxKind.ExternalModuleReference: return visitor.visitExternalModuleReference(element); + case SyntaxKind.ModuleNameModuleReference: return visitor.visitModuleNameModuleReference(element); + } + + throw Errors.invalidOperation(); + } + + export interface ISyntaxVisitor { + visitToken(token: ISyntaxToken): any; + visitSourceUnit(node: SourceUnitSyntax): any; + visitQualifiedName(node: QualifiedNameSyntax): any; + visitObjectType(node: ObjectTypeSyntax): any; + visitFunctionType(node: FunctionTypeSyntax): any; + visitArrayType(node: ArrayTypeSyntax): any; + visitConstructorType(node: ConstructorTypeSyntax): any; + visitGenericType(node: GenericTypeSyntax): any; + visitTypeQuery(node: TypeQuerySyntax): any; + visitInterfaceDeclaration(node: InterfaceDeclarationSyntax): any; + visitFunctionDeclaration(node: FunctionDeclarationSyntax): any; + visitModuleDeclaration(node: ModuleDeclarationSyntax): any; + visitClassDeclaration(node: ClassDeclarationSyntax): any; + visitEnumDeclaration(node: EnumDeclarationSyntax): any; + visitImportDeclaration(node: ImportDeclarationSyntax): any; + visitExportAssignment(node: ExportAssignmentSyntax): any; + visitMemberFunctionDeclaration(node: MemberFunctionDeclarationSyntax): any; + visitMemberVariableDeclaration(node: MemberVariableDeclarationSyntax): any; + visitConstructorDeclaration(node: ConstructorDeclarationSyntax): any; + visitIndexMemberDeclaration(node: IndexMemberDeclarationSyntax): any; + visitGetAccessor(node: GetAccessorSyntax): any; + visitSetAccessor(node: SetAccessorSyntax): any; + visitPropertySignature(node: PropertySignatureSyntax): any; + visitCallSignature(node: CallSignatureSyntax): any; + visitConstructSignature(node: ConstructSignatureSyntax): any; + visitIndexSignature(node: IndexSignatureSyntax): any; + visitMethodSignature(node: MethodSignatureSyntax): any; + visitBlock(node: BlockSyntax): any; + visitIfStatement(node: IfStatementSyntax): any; + visitVariableStatement(node: VariableStatementSyntax): any; + visitExpressionStatement(node: ExpressionStatementSyntax): any; + visitReturnStatement(node: ReturnStatementSyntax): any; + visitSwitchStatement(node: SwitchStatementSyntax): any; + visitBreakStatement(node: BreakStatementSyntax): any; + visitContinueStatement(node: ContinueStatementSyntax): any; + visitForStatement(node: ForStatementSyntax): any; + visitForInStatement(node: ForInStatementSyntax): any; + visitEmptyStatement(node: EmptyStatementSyntax): any; + visitThrowStatement(node: ThrowStatementSyntax): any; + visitWhileStatement(node: WhileStatementSyntax): any; + visitTryStatement(node: TryStatementSyntax): any; + visitLabeledStatement(node: LabeledStatementSyntax): any; + visitDoStatement(node: DoStatementSyntax): any; + visitDebuggerStatement(node: DebuggerStatementSyntax): any; + visitWithStatement(node: WithStatementSyntax): any; + visitPrefixUnaryExpression(node: PrefixUnaryExpressionSyntax): any; + visitDeleteExpression(node: DeleteExpressionSyntax): any; + visitTypeOfExpression(node: TypeOfExpressionSyntax): any; + visitVoidExpression(node: VoidExpressionSyntax): any; + visitConditionalExpression(node: ConditionalExpressionSyntax): any; + visitBinaryExpression(node: BinaryExpressionSyntax): any; + visitPostfixUnaryExpression(node: PostfixUnaryExpressionSyntax): any; + visitMemberAccessExpression(node: MemberAccessExpressionSyntax): any; + visitInvocationExpression(node: InvocationExpressionSyntax): any; + visitArrayLiteralExpression(node: ArrayLiteralExpressionSyntax): any; + visitObjectLiteralExpression(node: ObjectLiteralExpressionSyntax): any; + visitObjectCreationExpression(node: ObjectCreationExpressionSyntax): any; + visitParenthesizedExpression(node: ParenthesizedExpressionSyntax): any; + visitParenthesizedArrowFunctionExpression(node: ParenthesizedArrowFunctionExpressionSyntax): any; + visitSimpleArrowFunctionExpression(node: SimpleArrowFunctionExpressionSyntax): any; + visitCastExpression(node: CastExpressionSyntax): any; + visitElementAccessExpression(node: ElementAccessExpressionSyntax): any; + visitFunctionExpression(node: FunctionExpressionSyntax): any; + visitOmittedExpression(node: OmittedExpressionSyntax): any; + visitVariableDeclaration(node: VariableDeclarationSyntax): any; + visitVariableDeclarator(node: VariableDeclaratorSyntax): any; + visitArgumentList(node: ArgumentListSyntax): any; + visitParameterList(node: ParameterListSyntax): any; + visitTypeArgumentList(node: TypeArgumentListSyntax): any; + visitTypeParameterList(node: TypeParameterListSyntax): any; + visitHeritageClause(node: HeritageClauseSyntax): any; + visitEqualsValueClause(node: EqualsValueClauseSyntax): any; + visitCaseSwitchClause(node: CaseSwitchClauseSyntax): any; + visitDefaultSwitchClause(node: DefaultSwitchClauseSyntax): any; + visitElseClause(node: ElseClauseSyntax): any; + visitCatchClause(node: CatchClauseSyntax): any; + visitFinallyClause(node: FinallyClauseSyntax): any; + visitTypeParameter(node: TypeParameterSyntax): any; + visitConstraint(node: ConstraintSyntax): any; + visitSimplePropertyAssignment(node: SimplePropertyAssignmentSyntax): any; + visitFunctionPropertyAssignment(node: FunctionPropertyAssignmentSyntax): any; + visitParameter(node: ParameterSyntax): any; + visitEnumElement(node: EnumElementSyntax): any; + visitTypeAnnotation(node: TypeAnnotationSyntax): any; + visitExternalModuleReference(node: ExternalModuleReferenceSyntax): any; + visitModuleNameModuleReference(node: ModuleNameModuleReferenceSyntax): any; + } +} \ No newline at end of file diff --git a/src/services/syntax/syntaxWalker.generated.ts b/src/services/syntax/syntaxWalker.generated.ts new file mode 100644 index 00000000000..73f70f1834b --- /dev/null +++ b/src/services/syntax/syntaxWalker.generated.ts @@ -0,0 +1,618 @@ +/// + +module TypeScript { + export class SyntaxWalker implements ISyntaxVisitor { + public visitToken(token: ISyntaxToken): void { + } + + public visitNode(node: ISyntaxNode): void { + visitNodeOrToken(this, node); + } + + public visitNodeOrToken(nodeOrToken: ISyntaxNodeOrToken): void { + if (isToken(nodeOrToken)) { + this.visitToken(nodeOrToken); + } + else { + this.visitNode(nodeOrToken); + } + } + + private visitOptionalToken(token: ISyntaxToken): void { + if (token === null) { + return; + } + + this.visitToken(token); + } + + public visitOptionalNode(node: ISyntaxNode): void { + if (node === null) { + return; + } + + this.visitNode(node); + } + + public visitOptionalNodeOrToken(nodeOrToken: ISyntaxNodeOrToken): void { + if (nodeOrToken === null) { + return; + } + + this.visitNodeOrToken(nodeOrToken); + } + + public visitList(list: ISyntaxNodeOrToken[]): void { + for (var i = 0, n = list.length; i < n; i++) { + this.visitNodeOrToken(list[i]); + } + } + + public visitSeparatedList(list: ISyntaxNodeOrToken[]): void { + for (var i = 0, n = childCount(list); i < n; i++) { + var item = childAt(list, i); + this.visitNodeOrToken(item); + } + } + + public visitSourceUnit(node: SourceUnitSyntax): void { + this.visitList(node.moduleElements); + this.visitToken(node.endOfFileToken); + } + + public visitQualifiedName(node: QualifiedNameSyntax): void { + this.visitNodeOrToken(node.left); + this.visitToken(node.dotToken); + this.visitToken(node.right); + } + + public visitObjectType(node: ObjectTypeSyntax): void { + this.visitToken(node.openBraceToken); + this.visitSeparatedList(node.typeMembers); + this.visitToken(node.closeBraceToken); + } + + public visitFunctionType(node: FunctionTypeSyntax): void { + this.visitOptionalNode(node.typeParameterList); + this.visitNode(node.parameterList); + this.visitToken(node.equalsGreaterThanToken); + this.visitNodeOrToken(node.type); + } + + public visitArrayType(node: ArrayTypeSyntax): void { + this.visitNodeOrToken(node.type); + this.visitToken(node.openBracketToken); + this.visitToken(node.closeBracketToken); + } + + public visitConstructorType(node: ConstructorTypeSyntax): void { + this.visitToken(node.newKeyword); + this.visitOptionalNode(node.typeParameterList); + this.visitNode(node.parameterList); + this.visitToken(node.equalsGreaterThanToken); + this.visitNodeOrToken(node.type); + } + + public visitGenericType(node: GenericTypeSyntax): void { + this.visitNodeOrToken(node.name); + this.visitNode(node.typeArgumentList); + } + + public visitTypeQuery(node: TypeQuerySyntax): void { + this.visitToken(node.typeOfKeyword); + this.visitNodeOrToken(node.name); + } + + public visitInterfaceDeclaration(node: InterfaceDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.interfaceKeyword); + this.visitToken(node.identifier); + this.visitOptionalNode(node.typeParameterList); + this.visitList(node.heritageClauses); + this.visitNode(node.body); + } + + public visitFunctionDeclaration(node: FunctionDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.functionKeyword); + this.visitToken(node.identifier); + this.visitNode(node.callSignature); + this.visitOptionalNode(node.block); + this.visitOptionalToken(node.semicolonToken); + } + + public visitModuleDeclaration(node: ModuleDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.moduleKeyword); + this.visitOptionalNodeOrToken(node.name); + this.visitOptionalToken(node.stringLiteral); + this.visitToken(node.openBraceToken); + this.visitList(node.moduleElements); + this.visitToken(node.closeBraceToken); + } + + public visitClassDeclaration(node: ClassDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.classKeyword); + this.visitToken(node.identifier); + this.visitOptionalNode(node.typeParameterList); + this.visitList(node.heritageClauses); + this.visitToken(node.openBraceToken); + this.visitList(node.classElements); + this.visitToken(node.closeBraceToken); + } + + public visitEnumDeclaration(node: EnumDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.enumKeyword); + this.visitToken(node.identifier); + this.visitToken(node.openBraceToken); + this.visitSeparatedList(node.enumElements); + this.visitToken(node.closeBraceToken); + } + + public visitImportDeclaration(node: ImportDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.importKeyword); + this.visitToken(node.identifier); + this.visitToken(node.equalsToken); + this.visitNodeOrToken(node.moduleReference); + this.visitOptionalToken(node.semicolonToken); + } + + public visitExportAssignment(node: ExportAssignmentSyntax): void { + this.visitToken(node.exportKeyword); + this.visitToken(node.equalsToken); + this.visitToken(node.identifier); + this.visitOptionalToken(node.semicolonToken); + } + + public visitMemberFunctionDeclaration(node: MemberFunctionDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.propertyName); + this.visitNode(node.callSignature); + this.visitOptionalNode(node.block); + this.visitOptionalToken(node.semicolonToken); + } + + public visitMemberVariableDeclaration(node: MemberVariableDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitNode(node.variableDeclarator); + this.visitOptionalToken(node.semicolonToken); + } + + public visitConstructorDeclaration(node: ConstructorDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.constructorKeyword); + this.visitNode(node.callSignature); + this.visitOptionalNode(node.block); + this.visitOptionalToken(node.semicolonToken); + } + + public visitIndexMemberDeclaration(node: IndexMemberDeclarationSyntax): void { + this.visitList(node.modifiers); + this.visitNode(node.indexSignature); + this.visitOptionalToken(node.semicolonToken); + } + + public visitGetAccessor(node: GetAccessorSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.getKeyword); + this.visitToken(node.propertyName); + this.visitNode(node.callSignature); + this.visitNode(node.block); + } + + public visitSetAccessor(node: SetAccessorSyntax): void { + this.visitList(node.modifiers); + this.visitToken(node.setKeyword); + this.visitToken(node.propertyName); + this.visitNode(node.callSignature); + this.visitNode(node.block); + } + + public visitPropertySignature(node: PropertySignatureSyntax): void { + this.visitToken(node.propertyName); + this.visitOptionalToken(node.questionToken); + this.visitOptionalNode(node.typeAnnotation); + } + + public visitCallSignature(node: CallSignatureSyntax): void { + this.visitOptionalNode(node.typeParameterList); + this.visitNode(node.parameterList); + this.visitOptionalNode(node.typeAnnotation); + } + + public visitConstructSignature(node: ConstructSignatureSyntax): void { + this.visitToken(node.newKeyword); + this.visitNode(node.callSignature); + } + + public visitIndexSignature(node: IndexSignatureSyntax): void { + this.visitToken(node.openBracketToken); + this.visitSeparatedList(node.parameters); + this.visitToken(node.closeBracketToken); + this.visitOptionalNode(node.typeAnnotation); + } + + public visitMethodSignature(node: MethodSignatureSyntax): void { + this.visitToken(node.propertyName); + this.visitOptionalToken(node.questionToken); + this.visitNode(node.callSignature); + } + + public visitBlock(node: BlockSyntax): void { + this.visitToken(node.openBraceToken); + this.visitList(node.statements); + this.visitToken(node.closeBraceToken); + } + + public visitIfStatement(node: IfStatementSyntax): void { + this.visitToken(node.ifKeyword); + this.visitToken(node.openParenToken); + this.visitNodeOrToken(node.condition); + this.visitToken(node.closeParenToken); + this.visitNodeOrToken(node.statement); + this.visitOptionalNode(node.elseClause); + } + + public visitVariableStatement(node: VariableStatementSyntax): void { + this.visitList(node.modifiers); + this.visitNode(node.variableDeclaration); + this.visitOptionalToken(node.semicolonToken); + } + + public visitExpressionStatement(node: ExpressionStatementSyntax): void { + this.visitNodeOrToken(node.expression); + this.visitOptionalToken(node.semicolonToken); + } + + public visitReturnStatement(node: ReturnStatementSyntax): void { + this.visitToken(node.returnKeyword); + this.visitOptionalNodeOrToken(node.expression); + this.visitOptionalToken(node.semicolonToken); + } + + public visitSwitchStatement(node: SwitchStatementSyntax): void { + this.visitToken(node.switchKeyword); + this.visitToken(node.openParenToken); + this.visitNodeOrToken(node.expression); + this.visitToken(node.closeParenToken); + this.visitToken(node.openBraceToken); + this.visitList(node.switchClauses); + this.visitToken(node.closeBraceToken); + } + + public visitBreakStatement(node: BreakStatementSyntax): void { + this.visitToken(node.breakKeyword); + this.visitOptionalToken(node.identifier); + this.visitOptionalToken(node.semicolonToken); + } + + public visitContinueStatement(node: ContinueStatementSyntax): void { + this.visitToken(node.continueKeyword); + this.visitOptionalToken(node.identifier); + this.visitOptionalToken(node.semicolonToken); + } + + public visitForStatement(node: ForStatementSyntax): void { + this.visitToken(node.forKeyword); + this.visitToken(node.openParenToken); + this.visitOptionalNode(node.variableDeclaration); + this.visitOptionalNodeOrToken(node.initializer); + this.visitToken(node.firstSemicolonToken); + this.visitOptionalNodeOrToken(node.condition); + this.visitToken(node.secondSemicolonToken); + this.visitOptionalNodeOrToken(node.incrementor); + this.visitToken(node.closeParenToken); + this.visitNodeOrToken(node.statement); + } + + public visitForInStatement(node: ForInStatementSyntax): void { + this.visitToken(node.forKeyword); + this.visitToken(node.openParenToken); + this.visitOptionalNode(node.variableDeclaration); + this.visitOptionalNodeOrToken(node.left); + this.visitToken(node.inKeyword); + this.visitNodeOrToken(node.expression); + this.visitToken(node.closeParenToken); + this.visitNodeOrToken(node.statement); + } + + public visitEmptyStatement(node: EmptyStatementSyntax): void { + this.visitToken(node.semicolonToken); + } + + public visitThrowStatement(node: ThrowStatementSyntax): void { + this.visitToken(node.throwKeyword); + this.visitNodeOrToken(node.expression); + this.visitOptionalToken(node.semicolonToken); + } + + public visitWhileStatement(node: WhileStatementSyntax): void { + this.visitToken(node.whileKeyword); + this.visitToken(node.openParenToken); + this.visitNodeOrToken(node.condition); + this.visitToken(node.closeParenToken); + this.visitNodeOrToken(node.statement); + } + + public visitTryStatement(node: TryStatementSyntax): void { + this.visitToken(node.tryKeyword); + this.visitNode(node.block); + this.visitOptionalNode(node.catchClause); + this.visitOptionalNode(node.finallyClause); + } + + public visitLabeledStatement(node: LabeledStatementSyntax): void { + this.visitToken(node.identifier); + this.visitToken(node.colonToken); + this.visitNodeOrToken(node.statement); + } + + public visitDoStatement(node: DoStatementSyntax): void { + this.visitToken(node.doKeyword); + this.visitNodeOrToken(node.statement); + this.visitToken(node.whileKeyword); + this.visitToken(node.openParenToken); + this.visitNodeOrToken(node.condition); + this.visitToken(node.closeParenToken); + this.visitOptionalToken(node.semicolonToken); + } + + public visitDebuggerStatement(node: DebuggerStatementSyntax): void { + this.visitToken(node.debuggerKeyword); + this.visitOptionalToken(node.semicolonToken); + } + + public visitWithStatement(node: WithStatementSyntax): void { + this.visitToken(node.withKeyword); + this.visitToken(node.openParenToken); + this.visitNodeOrToken(node.condition); + this.visitToken(node.closeParenToken); + this.visitNodeOrToken(node.statement); + } + + public visitPrefixUnaryExpression(node: PrefixUnaryExpressionSyntax): void { + this.visitToken(node.operatorToken); + this.visitNodeOrToken(node.operand); + } + + public visitDeleteExpression(node: DeleteExpressionSyntax): void { + this.visitToken(node.deleteKeyword); + this.visitNodeOrToken(node.expression); + } + + public visitTypeOfExpression(node: TypeOfExpressionSyntax): void { + this.visitToken(node.typeOfKeyword); + this.visitNodeOrToken(node.expression); + } + + public visitVoidExpression(node: VoidExpressionSyntax): void { + this.visitToken(node.voidKeyword); + this.visitNodeOrToken(node.expression); + } + + public visitConditionalExpression(node: ConditionalExpressionSyntax): void { + this.visitNodeOrToken(node.condition); + this.visitToken(node.questionToken); + this.visitNodeOrToken(node.whenTrue); + this.visitToken(node.colonToken); + this.visitNodeOrToken(node.whenFalse); + } + + public visitBinaryExpression(node: BinaryExpressionSyntax): void { + this.visitNodeOrToken(node.left); + this.visitToken(node.operatorToken); + this.visitNodeOrToken(node.right); + } + + public visitPostfixUnaryExpression(node: PostfixUnaryExpressionSyntax): void { + this.visitNodeOrToken(node.operand); + this.visitToken(node.operatorToken); + } + + public visitMemberAccessExpression(node: MemberAccessExpressionSyntax): void { + this.visitNodeOrToken(node.expression); + this.visitToken(node.dotToken); + this.visitToken(node.name); + } + + public visitInvocationExpression(node: InvocationExpressionSyntax): void { + this.visitNodeOrToken(node.expression); + this.visitNode(node.argumentList); + } + + public visitArrayLiteralExpression(node: ArrayLiteralExpressionSyntax): void { + this.visitToken(node.openBracketToken); + this.visitSeparatedList(node.expressions); + this.visitToken(node.closeBracketToken); + } + + public visitObjectLiteralExpression(node: ObjectLiteralExpressionSyntax): void { + this.visitToken(node.openBraceToken); + this.visitSeparatedList(node.propertyAssignments); + this.visitToken(node.closeBraceToken); + } + + public visitObjectCreationExpression(node: ObjectCreationExpressionSyntax): void { + this.visitToken(node.newKeyword); + this.visitNodeOrToken(node.expression); + this.visitOptionalNode(node.argumentList); + } + + public visitParenthesizedExpression(node: ParenthesizedExpressionSyntax): void { + this.visitToken(node.openParenToken); + this.visitNodeOrToken(node.expression); + this.visitToken(node.closeParenToken); + } + + public visitParenthesizedArrowFunctionExpression(node: ParenthesizedArrowFunctionExpressionSyntax): void { + this.visitNode(node.callSignature); + this.visitToken(node.equalsGreaterThanToken); + this.visitOptionalNode(node.block); + this.visitOptionalNodeOrToken(node.expression); + } + + public visitSimpleArrowFunctionExpression(node: SimpleArrowFunctionExpressionSyntax): void { + this.visitNode(node.parameter); + this.visitToken(node.equalsGreaterThanToken); + this.visitOptionalNode(node.block); + this.visitOptionalNodeOrToken(node.expression); + } + + public visitCastExpression(node: CastExpressionSyntax): void { + this.visitToken(node.lessThanToken); + this.visitNodeOrToken(node.type); + this.visitToken(node.greaterThanToken); + this.visitNodeOrToken(node.expression); + } + + public visitElementAccessExpression(node: ElementAccessExpressionSyntax): void { + this.visitNodeOrToken(node.expression); + this.visitToken(node.openBracketToken); + this.visitNodeOrToken(node.argumentExpression); + this.visitToken(node.closeBracketToken); + } + + public visitFunctionExpression(node: FunctionExpressionSyntax): void { + this.visitToken(node.functionKeyword); + this.visitOptionalToken(node.identifier); + this.visitNode(node.callSignature); + this.visitNode(node.block); + } + + public visitOmittedExpression(node: OmittedExpressionSyntax): void { + } + + public visitVariableDeclaration(node: VariableDeclarationSyntax): void { + this.visitToken(node.varKeyword); + this.visitSeparatedList(node.variableDeclarators); + } + + public visitVariableDeclarator(node: VariableDeclaratorSyntax): void { + this.visitToken(node.propertyName); + this.visitOptionalNode(node.typeAnnotation); + this.visitOptionalNode(node.equalsValueClause); + } + + public visitArgumentList(node: ArgumentListSyntax): void { + this.visitOptionalNode(node.typeArgumentList); + this.visitToken(node.openParenToken); + this.visitSeparatedList(node.arguments); + this.visitToken(node.closeParenToken); + } + + public visitParameterList(node: ParameterListSyntax): void { + this.visitToken(node.openParenToken); + this.visitSeparatedList(node.parameters); + this.visitToken(node.closeParenToken); + } + + public visitTypeArgumentList(node: TypeArgumentListSyntax): void { + this.visitToken(node.lessThanToken); + this.visitSeparatedList(node.typeArguments); + this.visitToken(node.greaterThanToken); + } + + public visitTypeParameterList(node: TypeParameterListSyntax): void { + this.visitToken(node.lessThanToken); + this.visitSeparatedList(node.typeParameters); + this.visitToken(node.greaterThanToken); + } + + public visitHeritageClause(node: HeritageClauseSyntax): void { + this.visitToken(node.extendsOrImplementsKeyword); + this.visitSeparatedList(node.typeNames); + } + + public visitEqualsValueClause(node: EqualsValueClauseSyntax): void { + this.visitToken(node.equalsToken); + this.visitNodeOrToken(node.value); + } + + public visitCaseSwitchClause(node: CaseSwitchClauseSyntax): void { + this.visitToken(node.caseKeyword); + this.visitNodeOrToken(node.expression); + this.visitToken(node.colonToken); + this.visitList(node.statements); + } + + public visitDefaultSwitchClause(node: DefaultSwitchClauseSyntax): void { + this.visitToken(node.defaultKeyword); + this.visitToken(node.colonToken); + this.visitList(node.statements); + } + + public visitElseClause(node: ElseClauseSyntax): void { + this.visitToken(node.elseKeyword); + this.visitNodeOrToken(node.statement); + } + + public visitCatchClause(node: CatchClauseSyntax): void { + this.visitToken(node.catchKeyword); + this.visitToken(node.openParenToken); + this.visitToken(node.identifier); + this.visitOptionalNode(node.typeAnnotation); + this.visitToken(node.closeParenToken); + this.visitNode(node.block); + } + + public visitFinallyClause(node: FinallyClauseSyntax): void { + this.visitToken(node.finallyKeyword); + this.visitNode(node.block); + } + + public visitTypeParameter(node: TypeParameterSyntax): void { + this.visitToken(node.identifier); + this.visitOptionalNode(node.constraint); + } + + public visitConstraint(node: ConstraintSyntax): void { + this.visitToken(node.extendsKeyword); + this.visitNodeOrToken(node.typeOrExpression); + } + + public visitSimplePropertyAssignment(node: SimplePropertyAssignmentSyntax): void { + this.visitToken(node.propertyName); + this.visitToken(node.colonToken); + this.visitNodeOrToken(node.expression); + } + + public visitFunctionPropertyAssignment(node: FunctionPropertyAssignmentSyntax): void { + this.visitToken(node.propertyName); + this.visitNode(node.callSignature); + this.visitNode(node.block); + } + + public visitParameter(node: ParameterSyntax): void { + this.visitOptionalToken(node.dotDotDotToken); + this.visitList(node.modifiers); + this.visitToken(node.identifier); + this.visitOptionalToken(node.questionToken); + this.visitOptionalNode(node.typeAnnotation); + this.visitOptionalNode(node.equalsValueClause); + } + + public visitEnumElement(node: EnumElementSyntax): void { + this.visitToken(node.propertyName); + this.visitOptionalNode(node.equalsValueClause); + } + + public visitTypeAnnotation(node: TypeAnnotationSyntax): void { + this.visitToken(node.colonToken); + this.visitNodeOrToken(node.type); + } + + public visitExternalModuleReference(node: ExternalModuleReferenceSyntax): void { + this.visitToken(node.requireKeyword); + this.visitToken(node.openParenToken); + this.visitToken(node.stringLiteral); + this.visitToken(node.closeParenToken); + } + + public visitModuleNameModuleReference(node: ModuleNameModuleReferenceSyntax): void { + this.visitNodeOrToken(node.moduleName); + } + } +} \ No newline at end of file diff --git a/src/services/syntax/testUtilities.ts b/src/services/syntax/testUtilities.ts new file mode 100644 index 00000000000..2dd0055e068 --- /dev/null +++ b/src/services/syntax/testUtilities.ts @@ -0,0 +1,209 @@ +module TypeScript { + function assertParent(parent: ISyntaxElement, child: ISyntaxElement) { + if (child && !TypeScript.isShared(child)) { + return Debug.assert(parent === child.parent); + } + } + + export function nodeStructuralEquals(node1: TypeScript.ISyntaxNode, node2: TypeScript.ISyntaxNode, checkParents: boolean, text1: ISimpleText, text2: ISimpleText): boolean { + if (node1 === node2) { return true; } + if (node1 === null || node2 === null) { return false; } + + Debug.assert(node1.kind() === TypeScript.SyntaxKind.SourceUnit || node1.parent); + Debug.assert(node2.kind() === TypeScript.SyntaxKind.SourceUnit || node2.parent); + + if (node1.kind() !== node2.kind()) { return false; } + if (childCount(node1) !== childCount(node2)) { return false; } + + for (var i = 0, n = childCount(node1); i < n; i++) { + var element1 = childAt(node1, i); + var element2 = childAt(node2, i); + + if (checkParents) { + assertParent(node1, element1); + assertParent(node2, element2); + } + + if (!elementStructuralEquals(element1, element2, checkParents, text1, text2)) { + return false; + } + } + + return true; + } + + export function nodeOrTokenStructuralEquals(node1: TypeScript.ISyntaxNodeOrToken, node2: TypeScript.ISyntaxNodeOrToken, checkParents: boolean, text1: ISimpleText, text2: ISimpleText): boolean { + if (node1 === node2) { + return true; + } + + if (node1 === null || node2 === null) { + return false; + } + + Debug.assert(node1.kind() === TypeScript.SyntaxKind.SourceUnit || node1.parent); + Debug.assert(node2.kind() === TypeScript.SyntaxKind.SourceUnit || node2.parent); + + if (TypeScript.isToken(node1)) { + return TypeScript.isToken(node2) ? tokenStructuralEquals(node1, node2, text1, text2) : false; + } + + return TypeScript.isNode(node2) ? nodeStructuralEquals(node1, node2, checkParents, text1, text2) : false; + } + + export function tokenStructuralEquals(token1: TypeScript.ISyntaxToken, token2: TypeScript.ISyntaxToken, text1: ISimpleText, text2: ISimpleText): boolean { + if (token1 === token2) { + return true; + } + + if (token1 === null || token2 === null) { + return false; + } + + Debug.assert(token1.parent); + Debug.assert(token2.parent); + + return token1.kind() === token2.kind() && + TypeScript.width(token1) === TypeScript.width(token2) && + token1.fullWidth() === token2.fullWidth() && + token1.fullStart() === token2.fullStart() && + TypeScript.fullEnd(token1) === TypeScript.fullEnd(token2) && + TypeScript.start(token1, text1) === TypeScript.start(token2, text2) && + TypeScript.end(token1, text1) === TypeScript.end(token2, text2) && + token1.text() === token2.text() && + triviaListStructuralEquals(token1.leadingTrivia(text1), token2.leadingTrivia(text2)) && + triviaListStructuralEquals(token1.trailingTrivia(text1), token2.trailingTrivia(text2)); + } + + export function triviaListStructuralEquals(triviaList1: TypeScript.ISyntaxTriviaList, triviaList2: TypeScript.ISyntaxTriviaList): boolean { + Debug.assert(triviaList1.isShared() || triviaList1.parent); + Debug.assert(triviaList1.isShared() || triviaList2.parent); + + if (triviaList1.count() !== triviaList2.count()) { + return false; + } + + for (var i = 0, n = triviaList1.count(); i < n; i++) { + if (!triviaStructuralEquals(triviaList1.syntaxTriviaAt(i), triviaList2.syntaxTriviaAt(i))) { + return false; + } + } + + return true; + } + + export function triviaStructuralEquals(trivia1: TypeScript.ISyntaxTrivia, trivia2: TypeScript.ISyntaxTrivia): boolean { + Debug.assert(trivia1.parent); + Debug.assert(trivia2.parent); + + return trivia1.kind === trivia2.kind && + trivia1.fullWidth() === trivia2.fullWidth() && + trivia1.fullText() === trivia2.fullText(); + } + + function listStructuralEquals(list1: T[], list2: T[], checkParents: boolean, text1: ISimpleText, text2: ISimpleText): boolean { + Debug.assert(TypeScript.isShared(list1) || list1.parent); + Debug.assert(TypeScript.isShared(list2) || list2.parent); + + if (childCount(list1) !== childCount(list2)) { + return false; + } + + for (var i = 0, n = childCount(list1); i < n; i++) { + var child1 = childAt(list1, i); + var child2 = childAt(list2, i); + + if (checkParents) { + assertParent(list1, child1); + assertParent(list2, child2); + } + + if (!nodeOrTokenStructuralEquals(child1, child2, checkParents, text1, text2)) { + return false; + } + } + + return true; + } + + function separatedListStructuralEquals(list1: T[], list2: T[], checkParents: boolean, text1: ISimpleText, text2: ISimpleText): boolean { + Debug.assert(TypeScript.isShared(list1) || list1.parent); + Debug.assert(TypeScript.isShared(list2) || list2.parent); + + if (childCount(list1) !== childCount(list2)) { + return false; + } + + for (var i = 0, n = childCount(list1); i < n; i++) { + var element1 = childAt(list1, i); + var element2 = childAt(list2, i); + + if (checkParents) { + assertParent(list1, element1); + assertParent(list2, element2); + } + + if (!nodeOrTokenStructuralEquals(element1, element2, checkParents, text1, text2)) { + return false; + } + } + + return true; + } + + export function elementStructuralEquals(element1: TypeScript.ISyntaxElement, element2: TypeScript.ISyntaxElement, checkParents: boolean, text1: ISimpleText, text2: ISimpleText) { + if (element1 === element2) { + return true; + } + + if (element1 === null || element2 === null) { + return false; + } + + Debug.assert(element1.kind() === SyntaxKind.SourceUnit || element1.parent); + Debug.assert(element2.kind() === SyntaxKind.SourceUnit || element2.parent); + + if (element2.kind() !== element2.kind()) { + return false; + } + + if (TypeScript.fullStart(element1) !== TypeScript.fullStart(element2)) { + return false; + } + + if (TypeScript.start(element1) !== TypeScript.start(element2)) { + return false; + } + + if (TypeScript.end(element1) !== TypeScript.end(element2)) { + return false; + } + + if (TypeScript.fullEnd(element1) !== TypeScript.fullEnd(element2)) { + return false; + } + + if (TypeScript.isToken(element1)) { + return tokenStructuralEquals(element1, element2, text1, text2); + } + else if (TypeScript.isNode(element1)) { + return nodeStructuralEquals(element1, element2, checkParents, text1, text2); + } + else if (TypeScript.isList(element1)) { + return listStructuralEquals(element1, element2, checkParents, text1, text2); + } + else if (TypeScript.isSeparatedList(element1)) { + return separatedListStructuralEquals(element1, element2, checkParents, text1, text2); + } + + throw TypeScript.Errors.invalidOperation(); + } + + export function treeStructuralEquals(tree1: TypeScript.SyntaxTree, tree2: TypeScript.SyntaxTree, checkParents: boolean): boolean { + if (!TypeScript.ArrayUtilities.sequenceEquals(tree1.diagnostics(), tree2.diagnostics(), TypeScript.Diagnostic.equals)) { + return false; + } + + return nodeStructuralEquals(tree1.sourceUnit(), tree2.sourceUnit(), checkParents, tree1.text, tree2.text); + } +} \ No newline at end of file diff --git a/src/services/syntax/unicode.ts b/src/services/syntax/unicode.ts new file mode 100644 index 00000000000..df2f88b74c5 --- /dev/null +++ b/src/services/syntax/unicode.ts @@ -0,0 +1,107 @@ +/// + +module TypeScript { + export class Unicode { + /* + As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers + IdentifierStart :: + Can contain Unicode 3.0.0 categories: + “Uppercase letter (Lu)”, + “Lowercase letter (Ll)”, + “Titlecase letter (Lt)”, + “Modifier letter (Lm)”, + “Other letter (Lo)”, or + “Letter number (Nl)”. + IdentifierPart :: = + Can contain IdentifierStart + Unicode 3.0.0 categories: + “Non-spacing mark (Mn)”, + “Combining spacing mark (Mc)”, + “Decimal number (Nd)”, or + “Connector punctuation (Pc)”. + + Codepoint ranges for ES3 Identifiers are extracted from the Unicode 3.0.0 specification at: + http://www.unicode.org/Public/3.0-Update/UnicodeData-3.0.0.txt + */ + static unicodeES3IdentifierStart = [170,170, 181,181, 186,186, 192,214, 216,246, 248,543, 546,563, 592,685, 688,696, 699,705, 720,721, 736,740, 750,750, 890,890, 902,902, 904,906, 908,908, 910,929, 931,974, 976,983, 986,1011, 1024,1153, 1164,1220, 1223,1224, 1227,1228, 1232,1269, 1272,1273, 1329,1366, 1369,1369, 1377,1415, 1488,1514, 1520,1522, 1569,1594, 1600,1610, 1649,1747, 1749,1749, 1765,1766, 1786,1788, 1808,1808, 1810,1836, 1920,1957, 2309,2361, 2365,2365, 2384,2384, 2392,2401, 2437,2444, 2447,2448, 2451,2472, 2474,2480, 2482,2482, 2486,2489, 2524,2525, 2527,2529, 2544,2545, 2565,2570, 2575,2576, 2579,2600, 2602,2608, 2610,2611, 2613,2614, 2616,2617, 2649,2652, 2654,2654, 2674,2676, 2693,2699, 2701,2701, 2703,2705, 2707,2728, 2730,2736, 2738,2739, 2741,2745, 2749,2749, 2768,2768, 2784,2784, 2821,2828, 2831,2832, 2835,2856, 2858,2864, 2866,2867, 2870,2873, 2877,2877, 2908,2909, 2911,2913, 2949,2954, 2958,2960, 2962,2965, 2969,2970, 2972,2972, 2974,2975, 2979,2980, 2984,2986, 2990,2997, 2999,3001, 3077,3084, 3086,3088, 3090,3112, 3114,3123, 3125,3129, 3168,3169, 3205,3212, 3214,3216, 3218,3240, 3242,3251, 3253,3257, 3294,3294, 3296,3297, 3333,3340, 3342,3344, 3346,3368, 3370,3385, 3424,3425, 3461,3478, 3482,3505, 3507,3515, 3517,3517, 3520,3526, 3585,3632, 3634,3635, 3648,3654, 3713,3714, 3716,3716, 3719,3720, 3722,3722, 3725,3725, 3732,3735, 3737,3743, 3745,3747, 3749,3749, 3751,3751, 3754,3755, 3757,3760, 3762,3763, 3773,3773, 3776,3780, 3782,3782, 3804,3805, 3840,3840, 3904,3911, 3913,3946, 3976,3979, 4096,4129, 4131,4135, 4137,4138, 4176,4181, 4256,4293, 4304,4342, 4352,4441, 4447,4514, 4520,4601, 4608,4614, 4616,4678, 4680,4680, 4682,4685, 4688,4694, 4696,4696, 4698,4701, 4704,4742, 4744,4744, 4746,4749, 4752,4782, 4784,4784, 4786,4789, 4792,4798, 4800,4800, 4802,4805, 4808,4814, 4816,4822, 4824,4846, 4848,4878, 4880,4880, 4882,4885, 4888,4894, 4896,4934, 4936,4954, 5024,5108, 5121,5740, 5743,5750, 5761,5786, 5792,5866, 6016,6067, 6176,6263, 6272,6312, 7680,7835, 7840,7929, 7936,7957, 7960,7965, 7968,8005, 8008,8013, 8016,8023, 8025,8025, 8027,8027, 8029,8029, 8031,8061, 8064,8116, 8118,8124, 8126,8126, 8130,8132, 8134,8140, 8144,8147, 8150,8155, 8160,8172, 8178,8180, 8182,8188, 8319,8319, 8450,8450, 8455,8455, 8458,8467, 8469,8469, 8473,8477, 8484,8484, 8486,8486, 8488,8488, 8490,8493, 8495,8497, 8499,8505, 8544,8579, 12293,12295, 12321,12329, 12337,12341, 12344,12346, 12353,12436, 12445,12446, 12449,12538, 12540,12542, 12549,12588, 12593,12686, 12704,12727, 13312,19893, 19968,40869, 40960,42124, 44032,55203, 63744,64045, 64256,64262, 64275,64279, 64285,64285, 64287,64296, 64298,64310, 64312,64316, 64318,64318, 64320,64321, 64323,64324, 64326,64433, 64467,64829, 64848,64911, 64914,64967, 65008,65019, 65136,65138, 65140,65140, 65142,65276, 65313,65338, 65345,65370, 65382,65470, 65474,65479, 65482,65487, 65490,65495, 65498,65500, ]; + static unicodeES3IdentifierPart = [170,170, 181,181, 186,186, 192,214, 216,246, 248,543, 546,563, 592,685, 688,696, 699,705, 720,721, 736,740, 750,750, 768,846, 864,866, 890,890, 902,902, 904,906, 908,908, 910,929, 931,974, 976,983, 986,1011, 1024,1153, 1155,1158, 1164,1220, 1223,1224, 1227,1228, 1232,1269, 1272,1273, 1329,1366, 1369,1369, 1377,1415, 1425,1441, 1443,1465, 1467,1469, 1471,1471, 1473,1474, 1476,1476, 1488,1514, 1520,1522, 1569,1594, 1600,1621, 1632,1641, 1648,1747, 1749,1756, 1759,1768, 1770,1773, 1776,1788, 1808,1836, 1840,1866, 1920,1968, 2305,2307, 2309,2361, 2364,2381, 2384,2388, 2392,2403, 2406,2415, 2433,2435, 2437,2444, 2447,2448, 2451,2472, 2474,2480, 2482,2482, 2486,2489, 2492,2492, 2494,2500, 2503,2504, 2507,2509, 2519,2519, 2524,2525, 2527,2531, 2534,2545, 2562,2562, 2565,2570, 2575,2576, 2579,2600, 2602,2608, 2610,2611, 2613,2614, 2616,2617, 2620,2620, 2622,2626, 2631,2632, 2635,2637, 2649,2652, 2654,2654, 2662,2676, 2689,2691, 2693,2699, 2701,2701, 2703,2705, 2707,2728, 2730,2736, 2738,2739, 2741,2745, 2748,2757, 2759,2761, 2763,2765, 2768,2768, 2784,2784, 2790,2799, 2817,2819, 2821,2828, 2831,2832, 2835,2856, 2858,2864, 2866,2867, 2870,2873, 2876,2883, 2887,2888, 2891,2893, 2902,2903, 2908,2909, 2911,2913, 2918,2927, 2946,2947, 2949,2954, 2958,2960, 2962,2965, 2969,2970, 2972,2972, 2974,2975, 2979,2980, 2984,2986, 2990,2997, 2999,3001, 3006,3010, 3014,3016, 3018,3021, 3031,3031, 3047,3055, 3073,3075, 3077,3084, 3086,3088, 3090,3112, 3114,3123, 3125,3129, 3134,3140, 3142,3144, 3146,3149, 3157,3158, 3168,3169, 3174,3183, 3202,3203, 3205,3212, 3214,3216, 3218,3240, 3242,3251, 3253,3257, 3262,3268, 3270,3272, 3274,3277, 3285,3286, 3294,3294, 3296,3297, 3302,3311, 3330,3331, 3333,3340, 3342,3344, 3346,3368, 3370,3385, 3390,3395, 3398,3400, 3402,3405, 3415,3415, 3424,3425, 3430,3439, 3458,3459, 3461,3478, 3482,3505, 3507,3515, 3517,3517, 3520,3526, 3530,3530, 3535,3540, 3542,3542, 3544,3551, 3570,3571, 3585,3642, 3648,3662, 3664,3673, 3713,3714, 3716,3716, 3719,3720, 3722,3722, 3725,3725, 3732,3735, 3737,3743, 3745,3747, 3749,3749, 3751,3751, 3754,3755, 3757,3769, 3771,3773, 3776,3780, 3782,3782, 3784,3789, 3792,3801, 3804,3805, 3840,3840, 3864,3865, 3872,3881, 3893,3893, 3895,3895, 3897,3897, 3902,3911, 3913,3946, 3953,3972, 3974,3979, 3984,3991, 3993,4028, 4038,4038, 4096,4129, 4131,4135, 4137,4138, 4140,4146, 4150,4153, 4160,4169, 4176,4185, 4256,4293, 4304,4342, 4352,4441, 4447,4514, 4520,4601, 4608,4614, 4616,4678, 4680,4680, 4682,4685, 4688,4694, 4696,4696, 4698,4701, 4704,4742, 4744,4744, 4746,4749, 4752,4782, 4784,4784, 4786,4789, 4792,4798, 4800,4800, 4802,4805, 4808,4814, 4816,4822, 4824,4846, 4848,4878, 4880,4880, 4882,4885, 4888,4894, 4896,4934, 4936,4954, 4969,4977, 5024,5108, 5121,5740, 5743,5750, 5761,5786, 5792,5866, 6016,6099, 6112,6121, 6160,6169, 6176,6263, 6272,6313, 7680,7835, 7840,7929, 7936,7957, 7960,7965, 7968,8005, 8008,8013, 8016,8023, 8025,8025, 8027,8027, 8029,8029, 8031,8061, 8064,8116, 8118,8124, 8126,8126, 8130,8132, 8134,8140, 8144,8147, 8150,8155, 8160,8172, 8178,8180, 8182,8188, 8255,8256, 8319,8319, 8400,8412, 8417,8417, 8450,8450, 8455,8455, 8458,8467, 8469,8469, 8473,8477, 8484,8484, 8486,8486, 8488,8488, 8490,8493, 8495,8497, 8499,8505, 8544,8579, 12293,12295, 12321,12335, 12337,12341, 12344,12346, 12353,12436, 12441,12442, 12445,12446, 12449,12542, 12549,12588, 12593,12686, 12704,12727, 13312,19893, 19968,40869, 40960,42124, 44032,55203, 63744,64045, 64256,64262, 64275,64279, 64285,64296, 64298,64310, 64312,64316, 64318,64318, 64320,64321, 64323,64324, 64326,64433, 64467,64829, 64848,64911, 64914,64967, 65008,65019, 65056,65059, 65075,65076, 65101,65103, 65136,65138, 65140,65140, 65142,65276, 65296,65305, 65313,65338, 65343,65343, 65345,65370, 65381,65470, 65474,65479, 65482,65487, 65490,65495, 65498,65500, ]; + + /* + As per ECMAScript Language Specification 5th Edition, Section 7.6: ISyntaxToken Names and Identifiers + IdentifierStart :: + Can contain Unicode 6.2 categories: + “Uppercase letter (Lu)”, + “Lowercase letter (Ll)”, + “Titlecase letter (Lt)”, + “Modifier letter (Lm)”, + “Other letter (Lo)”, or + “Letter number (Nl)”. + IdentifierPart :: + Can contain IdentifierStart + Unicode 6.2 categories: + “Non-spacing mark (Mn)”, + “Combining spacing mark (Mc)”, + “Decimal number (Nd)”, + “Connector punctuation (Pc)”, + , or + . + + Codepoint ranges for ES5 Identifiers are extracted from the Unicode 6.2 specification at: + http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt + */ + static unicodeES5IdentifierStart = [170,170, 181,181, 186,186, 192,214, 216,246, 248,705, 710,721, 736,740, 748,748, 750,750, 880,884, 886,887, 890,893, 902,902, 904,906, 908,908, 910,929, 931,1013, 1015,1153, 1162,1319, 1329,1366, 1369,1369, 1377,1415, 1488,1514, 1520,1522, 1568,1610, 1646,1647, 1649,1747, 1749,1749, 1765,1766, 1774,1775, 1786,1788, 1791,1791, 1808,1808, 1810,1839, 1869,1957, 1969,1969, 1994,2026, 2036,2037, 2042,2042, 2048,2069, 2074,2074, 2084,2084, 2088,2088, 2112,2136, 2208,2208, 2210,2220, 2308,2361, 2365,2365, 2384,2384, 2392,2401, 2417,2423, 2425,2431, 2437,2444, 2447,2448, 2451,2472, 2474,2480, 2482,2482, 2486,2489, 2493,2493, 2510,2510, 2524,2525, 2527,2529, 2544,2545, 2565,2570, 2575,2576, 2579,2600, 2602,2608, 2610,2611, 2613,2614, 2616,2617, 2649,2652, 2654,2654, 2674,2676, 2693,2701, 2703,2705, 2707,2728, 2730,2736, 2738,2739, 2741,2745, 2749,2749, 2768,2768, 2784,2785, 2821,2828, 2831,2832, 2835,2856, 2858,2864, 2866,2867, 2869,2873, 2877,2877, 2908,2909, 2911,2913, 2929,2929, 2947,2947, 2949,2954, 2958,2960, 2962,2965, 2969,2970, 2972,2972, 2974,2975, 2979,2980, 2984,2986, 2990,3001, 3024,3024, 3077,3084, 3086,3088, 3090,3112, 3114,3123, 3125,3129, 3133,3133, 3160,3161, 3168,3169, 3205,3212, 3214,3216, 3218,3240, 3242,3251, 3253,3257, 3261,3261, 3294,3294, 3296,3297, 3313,3314, 3333,3340, 3342,3344, 3346,3386, 3389,3389, 3406,3406, 3424,3425, 3450,3455, 3461,3478, 3482,3505, 3507,3515, 3517,3517, 3520,3526, 3585,3632, 3634,3635, 3648,3654, 3713,3714, 3716,3716, 3719,3720, 3722,3722, 3725,3725, 3732,3735, 3737,3743, 3745,3747, 3749,3749, 3751,3751, 3754,3755, 3757,3760, 3762,3763, 3773,3773, 3776,3780, 3782,3782, 3804,3807, 3840,3840, 3904,3911, 3913,3948, 3976,3980, 4096,4138, 4159,4159, 4176,4181, 4186,4189, 4193,4193, 4197,4198, 4206,4208, 4213,4225, 4238,4238, 4256,4293, 4295,4295, 4301,4301, 4304,4346, 4348,4680, 4682,4685, 4688,4694, 4696,4696, 4698,4701, 4704,4744, 4746,4749, 4752,4784, 4786,4789, 4792,4798, 4800,4800, 4802,4805, 4808,4822, 4824,4880, 4882,4885, 4888,4954, 4992,5007, 5024,5108, 5121,5740, 5743,5759, 5761,5786, 5792,5866, 5870,5872, 5888,5900, 5902,5905, 5920,5937, 5952,5969, 5984,5996, 5998,6000, 6016,6067, 6103,6103, 6108,6108, 6176,6263, 6272,6312, 6314,6314, 6320,6389, 6400,6428, 6480,6509, 6512,6516, 6528,6571, 6593,6599, 6656,6678, 6688,6740, 6823,6823, 6917,6963, 6981,6987, 7043,7072, 7086,7087, 7098,7141, 7168,7203, 7245,7247, 7258,7293, 7401,7404, 7406,7409, 7413,7414, 7424,7615, 7680,7957, 7960,7965, 7968,8005, 8008,8013, 8016,8023, 8025,8025, 8027,8027, 8029,8029, 8031,8061, 8064,8116, 8118,8124, 8126,8126, 8130,8132, 8134,8140, 8144,8147, 8150,8155, 8160,8172, 8178,8180, 8182,8188, 8305,8305, 8319,8319, 8336,8348, 8450,8450, 8455,8455, 8458,8467, 8469,8469, 8473,8477, 8484,8484, 8486,8486, 8488,8488, 8490,8493, 8495,8505, 8508,8511, 8517,8521, 8526,8526, 8544,8584, 11264,11310, 11312,11358, 11360,11492, 11499,11502, 11506,11507, 11520,11557, 11559,11559, 11565,11565, 11568,11623, 11631,11631, 11648,11670, 11680,11686, 11688,11694, 11696,11702, 11704,11710, 11712,11718, 11720,11726, 11728,11734, 11736,11742, 11823,11823, 12293,12295, 12321,12329, 12337,12341, 12344,12348, 12353,12438, 12445,12447, 12449,12538, 12540,12543, 12549,12589, 12593,12686, 12704,12730, 12784,12799, 13312,19893, 19968,40908, 40960,42124, 42192,42237, 42240,42508, 42512,42527, 42538,42539, 42560,42606, 42623,42647, 42656,42735, 42775,42783, 42786,42888, 42891,42894, 42896,42899, 42912,42922, 43000,43009, 43011,43013, 43015,43018, 43020,43042, 43072,43123, 43138,43187, 43250,43255, 43259,43259, 43274,43301, 43312,43334, 43360,43388, 43396,43442, 43471,43471, 43520,43560, 43584,43586, 43588,43595, 43616,43638, 43642,43642, 43648,43695, 43697,43697, 43701,43702, 43705,43709, 43712,43712, 43714,43714, 43739,43741, 43744,43754, 43762,43764, 43777,43782, 43785,43790, 43793,43798, 43808,43814, 43816,43822, 43968,44002, 44032,55203, 55216,55238, 55243,55291, 63744,64109, 64112,64217, 64256,64262, 64275,64279, 64285,64285, 64287,64296, 64298,64310, 64312,64316, 64318,64318, 64320,64321, 64323,64324, 64326,64433, 64467,64829, 64848,64911, 64914,64967, 65008,65019, 65136,65140, 65142,65276, 65313,65338, 65345,65370, 65382,65470, 65474,65479, 65482,65487, 65490,65495, 65498,65500, ]; + static unicodeES5IdentifierPart = [170,170, 181,181, 186,186, 192,214, 216,246, 248,705, 710,721, 736,740, 748,748, 750,750, 768,884, 886,887, 890,893, 902,902, 904,906, 908,908, 910,929, 931,1013, 1015,1153, 1155,1159, 1162,1319, 1329,1366, 1369,1369, 1377,1415, 1425,1469, 1471,1471, 1473,1474, 1476,1477, 1479,1479, 1488,1514, 1520,1522, 1552,1562, 1568,1641, 1646,1747, 1749,1756, 1759,1768, 1770,1788, 1791,1791, 1808,1866, 1869,1969, 1984,2037, 2042,2042, 2048,2093, 2112,2139, 2208,2208, 2210,2220, 2276,2302, 2304,2403, 2406,2415, 2417,2423, 2425,2431, 2433,2435, 2437,2444, 2447,2448, 2451,2472, 2474,2480, 2482,2482, 2486,2489, 2492,2500, 2503,2504, 2507,2510, 2519,2519, 2524,2525, 2527,2531, 2534,2545, 2561,2563, 2565,2570, 2575,2576, 2579,2600, 2602,2608, 2610,2611, 2613,2614, 2616,2617, 2620,2620, 2622,2626, 2631,2632, 2635,2637, 2641,2641, 2649,2652, 2654,2654, 2662,2677, 2689,2691, 2693,2701, 2703,2705, 2707,2728, 2730,2736, 2738,2739, 2741,2745, 2748,2757, 2759,2761, 2763,2765, 2768,2768, 2784,2787, 2790,2799, 2817,2819, 2821,2828, 2831,2832, 2835,2856, 2858,2864, 2866,2867, 2869,2873, 2876,2884, 2887,2888, 2891,2893, 2902,2903, 2908,2909, 2911,2915, 2918,2927, 2929,2929, 2946,2947, 2949,2954, 2958,2960, 2962,2965, 2969,2970, 2972,2972, 2974,2975, 2979,2980, 2984,2986, 2990,3001, 3006,3010, 3014,3016, 3018,3021, 3024,3024, 3031,3031, 3046,3055, 3073,3075, 3077,3084, 3086,3088, 3090,3112, 3114,3123, 3125,3129, 3133,3140, 3142,3144, 3146,3149, 3157,3158, 3160,3161, 3168,3171, 3174,3183, 3202,3203, 3205,3212, 3214,3216, 3218,3240, 3242,3251, 3253,3257, 3260,3268, 3270,3272, 3274,3277, 3285,3286, 3294,3294, 3296,3299, 3302,3311, 3313,3314, 3330,3331, 3333,3340, 3342,3344, 3346,3386, 3389,3396, 3398,3400, 3402,3406, 3415,3415, 3424,3427, 3430,3439, 3450,3455, 3458,3459, 3461,3478, 3482,3505, 3507,3515, 3517,3517, 3520,3526, 3530,3530, 3535,3540, 3542,3542, 3544,3551, 3570,3571, 3585,3642, 3648,3662, 3664,3673, 3713,3714, 3716,3716, 3719,3720, 3722,3722, 3725,3725, 3732,3735, 3737,3743, 3745,3747, 3749,3749, 3751,3751, 3754,3755, 3757,3769, 3771,3773, 3776,3780, 3782,3782, 3784,3789, 3792,3801, 3804,3807, 3840,3840, 3864,3865, 3872,3881, 3893,3893, 3895,3895, 3897,3897, 3902,3911, 3913,3948, 3953,3972, 3974,3991, 3993,4028, 4038,4038, 4096,4169, 4176,4253, 4256,4293, 4295,4295, 4301,4301, 4304,4346, 4348,4680, 4682,4685, 4688,4694, 4696,4696, 4698,4701, 4704,4744, 4746,4749, 4752,4784, 4786,4789, 4792,4798, 4800,4800, 4802,4805, 4808,4822, 4824,4880, 4882,4885, 4888,4954, 4957,4959, 4992,5007, 5024,5108, 5121,5740, 5743,5759, 5761,5786, 5792,5866, 5870,5872, 5888,5900, 5902,5908, 5920,5940, 5952,5971, 5984,5996, 5998,6000, 6002,6003, 6016,6099, 6103,6103, 6108,6109, 6112,6121, 6155,6157, 6160,6169, 6176,6263, 6272,6314, 6320,6389, 6400,6428, 6432,6443, 6448,6459, 6470,6509, 6512,6516, 6528,6571, 6576,6601, 6608,6617, 6656,6683, 6688,6750, 6752,6780, 6783,6793, 6800,6809, 6823,6823, 6912,6987, 6992,7001, 7019,7027, 7040,7155, 7168,7223, 7232,7241, 7245,7293, 7376,7378, 7380,7414, 7424,7654, 7676,7957, 7960,7965, 7968,8005, 8008,8013, 8016,8023, 8025,8025, 8027,8027, 8029,8029, 8031,8061, 8064,8116, 8118,8124, 8126,8126, 8130,8132, 8134,8140, 8144,8147, 8150,8155, 8160,8172, 8178,8180, 8182,8188, 8204,8205, 8255,8256, 8276,8276, 8305,8305, 8319,8319, 8336,8348, 8400,8412, 8417,8417, 8421,8432, 8450,8450, 8455,8455, 8458,8467, 8469,8469, 8473,8477, 8484,8484, 8486,8486, 8488,8488, 8490,8493, 8495,8505, 8508,8511, 8517,8521, 8526,8526, 8544,8584, 11264,11310, 11312,11358, 11360,11492, 11499,11507, 11520,11557, 11559,11559, 11565,11565, 11568,11623, 11631,11631, 11647,11670, 11680,11686, 11688,11694, 11696,11702, 11704,11710, 11712,11718, 11720,11726, 11728,11734, 11736,11742, 11744,11775, 11823,11823, 12293,12295, 12321,12335, 12337,12341, 12344,12348, 12353,12438, 12441,12442, 12445,12447, 12449,12538, 12540,12543, 12549,12589, 12593,12686, 12704,12730, 12784,12799, 13312,19893, 19968,40908, 40960,42124, 42192,42237, 42240,42508, 42512,42539, 42560,42607, 42612,42621, 42623,42647, 42655,42737, 42775,42783, 42786,42888, 42891,42894, 42896,42899, 42912,42922, 43000,43047, 43072,43123, 43136,43204, 43216,43225, 43232,43255, 43259,43259, 43264,43309, 43312,43347, 43360,43388, 43392,43456, 43471,43481, 43520,43574, 43584,43597, 43600,43609, 43616,43638, 43642,43643, 43648,43714, 43739,43741, 43744,43759, 43762,43766, 43777,43782, 43785,43790, 43793,43798, 43808,43814, 43816,43822, 43968,44010, 44012,44013, 44016,44025, 44032,55203, 55216,55238, 55243,55291, 63744,64109, 64112,64217, 64256,64262, 64275,64279, 64285,64296, 64298,64310, 64312,64316, 64318,64318, 64320,64321, 64323,64324, 64326,64433, 64467,64829, 64848,64911, 64914,64967, 65008,65019, 65024,65039, 65056,65062, 65075,65076, 65101,65103, 65136,65140, 65142,65276, 65296,65305, 65313,65338, 65343,65343, 65345,65370, 65382,65470, 65474,65479, 65482,65487, 65490,65495, 65498,65500, ]; + + static lookupInUnicodeMap(code: number, map: number[]): boolean { + // Bail out quickly if it couldn't possibly be in the map. + if (code < map[0]) { + return false; + } + + // Perform binary search in one of the unicode range maps + var lo: number = 0; + var hi: number = map.length; + var mid: number; + + while (lo + 1 < hi) { + mid = lo + (hi - lo) / 2; + // mid has to be even to catch a range's beginning + mid -= mid % 2; + if (map[mid] <= code && code <= map[mid + 1]) { + return true; + } + + if (code < map[mid]) { + hi = mid; + } + else { + lo = mid + 2; + } + } + + return false; + } + + public static isIdentifierStart(code: number, languageVersion: ts.ScriptTarget) { + if (languageVersion === ts.ScriptTarget.ES3) { + return Unicode.lookupInUnicodeMap(code, Unicode.unicodeES3IdentifierStart); + } + else if (languageVersion === ts.ScriptTarget.ES5) { + return Unicode.lookupInUnicodeMap(code, Unicode.unicodeES5IdentifierStart); + } + else { + throw Errors.argumentOutOfRange("languageVersion"); + } + } + + public static isIdentifierPart(code: number, languageVersion: ts.ScriptTarget) { + if (languageVersion === ts.ScriptTarget.ES3) { + return Unicode.lookupInUnicodeMap(code, Unicode.unicodeES3IdentifierPart); + } + else if (languageVersion === ts.ScriptTarget.ES5) { + return Unicode.lookupInUnicodeMap(code, Unicode.unicodeES5IdentifierPart); + } + else { + throw Errors.argumentOutOfRange("languageVersion"); + } + } + } +} \ No newline at end of file diff --git a/src/services/text/characterCodes.ts b/src/services/text/characterCodes.ts new file mode 100644 index 00000000000..040e0c41c6b --- /dev/null +++ b/src/services/text/characterCodes.ts @@ -0,0 +1,138 @@ +/// + +module TypeScript { + export enum CharacterCodes { + nullCharacter = 0, + maxAsciiCharacter = 127, + + lineFeed = 10, // \n + carriageReturn = 13, // \r + lineSeparator = 0x2028, + paragraphSeparator = 0x2029, + + // REVIEW: do we need to support this? The scanner doesn't, but our IText does. This seems + // like an odd disparity? (Or maybe it's completely fine for them to be different). + nextLine = 0x0085, + + // Unicode 3.0 space characters + space = 0x0020, // " " + nonBreakingSpace = 0x00A0, // + enQuad = 0x2000, + emQuad = 0x2001, + enSpace = 0x2002, + emSpace = 0x2003, + threePerEmSpace = 0x2004, + fourPerEmSpace = 0x2005, + sixPerEmSpace = 0x2006, + figureSpace = 0x2007, + punctuationSpace = 0x2008, + thinSpace = 0x2009, + hairSpace = 0x200A, + zeroWidthSpace = 0x200B, + narrowNoBreakSpace = 0x202F, + ideographicSpace = 0x3000, + + _ = 95, + $ = 36, + + _0 = 48, + _1 = 49, + _2 = 50, + _3 = 51, + _4 = 52, + _5 = 53, + _6 = 54, + _7 = 55, + _8 = 56, + _9 = 57, + + a = 97, + b = 98, + c = 99, + d = 100, + e = 101, + f = 102, + g = 103, + h = 104, + i = 105, + j = 106, + k = 107, + l = 108, + m = 109, + n = 110, + o = 111, + p = 112, + q = 113, + r = 114, + s = 115, + t = 116, + u = 117, + v = 118, + w = 119, + x = 120, + y = 121, + z = 122, + + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + + ampersand = 38, // & + asterisk = 42, // * + at = 64, // @ + backslash = 92, // \ + bar = 124, // | + caret = 94, // ^ + closeBrace = 125, // } + closeBracket = 93, // ] + closeParen = 41, // ) + colon = 58, // : + comma = 44, // , + dot = 46, // . + doubleQuote = 34, // " + equals = 61, // = + exclamation = 33, // ! + greaterThan = 62, // > + lessThan = 60, // < + minus = 45, // - + openBrace = 123, // { + openBracket = 91, // [ + openParen = 40, // ( + percent = 37, // % + plus = 43, // + + question = 63, // ? + semicolon = 59, // ; + singleQuote = 39, // ' + slash = 47, // / + tilde = 126, // ~ + + backspace = 8, // \b + formFeed = 12, // \f + byteOrderMark = 0xFEFF, + tab = 9, // \t + verticalTab = 11, // \v + } +} \ No newline at end of file diff --git a/src/services/text/lineMap.ts b/src/services/text/lineMap.ts new file mode 100644 index 00000000000..15fbbe47e84 --- /dev/null +++ b/src/services/text/lineMap.ts @@ -0,0 +1,17 @@ +/// + +module TypeScript { + export module LineMap1 { + export function fromSimpleText(text: ISimpleText): LineMap { + return new LineMap(() => TextUtilities.parseLineStarts({ charCodeAt: index => text.charCodeAt(index), length: text.length() }), text.length()); + } + + export function fromScriptSnapshot(scriptSnapshot: IScriptSnapshot): LineMap { + return new LineMap(() => scriptSnapshot.getLineStartPositions(), scriptSnapshot.getLength()); + } + + export function fromString(text: string): LineMap { + return new LineMap(() => TextUtilities.parseLineStarts(text), text.length); + } + } +} \ No newline at end of file diff --git a/src/services/text/references.ts b/src/services/text/references.ts new file mode 100644 index 00000000000..e50975146b4 --- /dev/null +++ b/src/services/text/references.ts @@ -0,0 +1,12 @@ +/// + +/// +/// +/// +/// +/// +/// +/// + +// TextChangeRange depends on TextSpan. +/// \ No newline at end of file diff --git a/src/services/text/scriptSnapshot.ts b/src/services/text/scriptSnapshot.ts new file mode 100644 index 00000000000..41b5ed37c41 --- /dev/null +++ b/src/services/text/scriptSnapshot.ts @@ -0,0 +1,57 @@ +/// + +module TypeScript { + // Represents an immutable snapshot of a script at a specified time. Once acquired, the + // snapshot is observably immutable. i.e. the same calls with the same parameters will return + // the same values. + export interface IScriptSnapshot { + // Get's a portion of the script snapshot specified by [start, end). + getText(start: number, end: number): string; + + // Get's the length of this script snapshot. + getLength(): number; + + // This call returns the array containing the start position of every line. + // i.e."[0, 10, 55]". TODO: consider making this optional. The language service could + // always determine this (albeit in a more expensive manner). + getLineStartPositions(): number[]; + + // Returns a text change range representing what text has changed since the specified version. + // If the change cannot be determined (say, because a file was opened/closed), then 'null' + // should be returned. + getTextChangeRangeSinceVersion(scriptVersion: number): TextChangeRange; + } + + export module ScriptSnapshot { + class StringScriptSnapshot implements IScriptSnapshot { + private _lineStartPositions: number[] = null; + + constructor(private text: string) { + } + + public getText(start: number, end: number): string { + return this.text.substring(start, end); + } + + public getLength(): number { + return this.text.length; + } + + public getLineStartPositions(): number[]{ + if (!this._lineStartPositions) { + this._lineStartPositions = TextUtilities.parseLineStarts(this.text); + } + + return this._lineStartPositions; + } + + public getTextChangeRangeSinceVersion(scriptVersion: number): TypeScript.TextChangeRange { + throw Errors.notYetImplemented(); + } + } + + export function fromString(text: string): IScriptSnapshot { + return new StringScriptSnapshot(text); + } + } +} \ No newline at end of file diff --git a/src/services/text/text.ts b/src/services/text/text.ts new file mode 100644 index 00000000000..80756c301fd --- /dev/null +++ b/src/services/text/text.ts @@ -0,0 +1,21 @@ +/// + +/** + * Represents an immutable snapshot of text. + */ +module TypeScript { + /** + * Represents an immutable snapshot of text. + */ + export interface ISimpleText { + /** + * Total number of characters in the text source. + */ + length(): number; + + substr(start: number, length: number): string; + + charCodeAt(index: number): number; + lineMap(): LineMap; + } +} \ No newline at end of file diff --git a/src/services/text/textChangeRange.ts b/src/services/text/textChangeRange.ts new file mode 100644 index 00000000000..b45dc8a0318 --- /dev/null +++ b/src/services/text/textChangeRange.ts @@ -0,0 +1,168 @@ +/// + +module TypeScript { + export class TextChangeRange { + public static unchanged = new TextChangeRange(new TextSpan(0, 0), 0); + + private _span: TextSpan; + private _newLength: number; + + /** + * Initializes a new instance of TextChangeRange. + */ + constructor(span: TextSpan, newLength: number) { + if (newLength < 0) { + throw Errors.argumentOutOfRange("newLength"); + } + + this._span = span; + this._newLength = newLength; + } + + /** + * The span of text before the edit which is being changed + */ + public span(): TextSpan { + return this._span; + } + + /** + * Width of the span after the edit. A 0 here would represent a delete + */ + public newLength(): number { + return this._newLength; + } + + public newSpan(): TextSpan { + return new TextSpan(this.span().start(), this.newLength()); + } + + public isUnchanged(): boolean { + return this.span().isEmpty() && this.newLength() === 0; + } + + /** + * Called to merge all the changes that occurred across several versions of a script snapshot + * into a single change. i.e. if a user keeps making successive edits to a script we will + * have a text change from V1 to V2, V2 to V3, ..., Vn. + * + * This function will then merge those changes into a single change range valid between V1 and + * Vn. + */ + public static collapseChangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange { + if (changes.length === 0) { + return TextChangeRange.unchanged; + } + + if (changes.length === 1) { + return changes[0]; + } + + // We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd } + // as it makes things much easier to reason about. + var change0 = changes[0]; + + var oldStartN = change0.span().start(); + var oldEndN = change0.span().end(); + var newEndN = oldStartN + change0.newLength(); + + for (var i = 1; i < changes.length; i++) { + var nextChange = changes[i]; + + // Consider the following case: + // i.e. two edits. The first represents the text change range { { 10, 50 }, 30 }. i.e. The span starting + // at 10, with length 50 is reduced to length 30. The second represents the text change range { { 30, 30 }, 40 }. + // i.e. the span starting at 30 with length 30 is increased to length 40. + // + // 0 10 20 30 40 50 60 70 80 90 100 + // ------------------------------------------------------------------------------------------------------- + // | / + // | /---- + // T1 | /---- + // | /---- + // | /---- + // ------------------------------------------------------------------------------------------------------- + // | \ + // | \ + // T2 | \ + // | \ + // | \ + // ------------------------------------------------------------------------------------------------------- + // + // Merging these turns out to not be too difficult. First, determining the new start of the change is trivial + // it's just the min of the old and new starts. i.e.: + // + // 0 10 20 30 40 50 60 70 80 90 100 + // ------------------------------------------------------------*------------------------------------------ + // | / + // | /---- + // T1 | /---- + // | /---- + // | /---- + // ----------------------------------------$-------------------$------------------------------------------ + // . | \ + // . | \ + // T2 . | \ + // . | \ + // . | \ + // ----------------------------------------------------------------------*-------------------------------- + // + // (Note the dots represent the newly inferrred start. + // Determining the new and old end is also pretty simple. Basically it boils down to paying attention to the + // absolute positions at the asterixes, and the relative change between the dollar signs. Basically, we see + // which if the two $'s precedes the other, and we move that one forward until they line up. in this case that + // means: + // + // 0 10 20 30 40 50 60 70 80 90 100 + // --------------------------------------------------------------------------------*---------------------- + // | / + // | /---- + // T1 | /---- + // | /---- + // | /---- + // ------------------------------------------------------------$------------------------------------------ + // . | \ + // . | \ + // T2 . | \ + // . | \ + // . | \ + // ----------------------------------------------------------------------*-------------------------------- + // + // In other words (in this case), we're recognizing that the second edit happened after where the first edit + // ended with a delta of 20 characters (60 - 40). Thus, if we go back in time to where the first edit started + // that's the same as if we started at char 80 instead of 60. + // + // As it so happens, the same logic applies if the second edit precedes the first edit. In that case rahter + // than pusing the first edit forward to match the second, we'll push the second edit forward to match the + // first. + // + // In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange + // semantics: { { start: 10, length: 70 }, newLength: 60 } + // + // The math then works out as follows. + // If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the + // final result like so: + // + // { + // oldStart3: Min(oldStart1, oldStart2), + // oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)), + // newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)) + // } + + var oldStart1 = oldStartN; + var oldEnd1 = oldEndN; + var newEnd1 = newEndN; + + var oldStart2 = nextChange.span().start(); + var oldEnd2 = nextChange.span().end(); + var newEnd2 = oldStart2 + nextChange.newLength(); + + oldStartN = Math.min(oldStart1, oldStart2); + oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)); + newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)); + } + + return new TextChangeRange(TextSpan.fromBounds(oldStartN, oldEndN), /*newLength: */newEndN - oldStartN); + } + } +} \ No newline at end of file diff --git a/src/services/text/textFactory.ts b/src/services/text/textFactory.ts new file mode 100644 index 00000000000..21d215c291e --- /dev/null +++ b/src/services/text/textFactory.ts @@ -0,0 +1,66 @@ +/// + +module TypeScript.SimpleText { + class SimpleStringText implements ISimpleText { + private _lineMap: LineMap = null; + + constructor(private value: string) { + } + + public length(): number { + return this.value.length; + } + + public substr(start: number, length: number): string { + return this.value.substr(start, length); + } + + public charCodeAt(index: number): number { + return this.value.charCodeAt(index); + } + + public lineMap(): LineMap { + if (!this._lineMap) { + this._lineMap = LineMap1.fromString(this.value); + } + + return this._lineMap; + } + } + + // Class which wraps a host IScriptSnapshot and exposes an ISimpleText for newer compiler code. + class SimpleScriptSnapshotText implements ISimpleText { + private _lineMap: LineMap = null; + + constructor(public scriptSnapshot: IScriptSnapshot) { + } + + public charCodeAt(index: number): number { + return this.scriptSnapshot.getText(index, index + 1).charCodeAt(0); + } + + public length(): number { + return this.scriptSnapshot.getLength(); + } + + public substr(start: number, length: number): string { + return this.scriptSnapshot.getText(start, start + length); + } + + public lineMap(): LineMap { + if (this._lineMap === null) { + this._lineMap = new LineMap(() => this.scriptSnapshot.getLineStartPositions(), this.length()); + } + + return this._lineMap; + } + } + + export function fromString(value: string): ISimpleText { + return new SimpleStringText(value); + } + + export function fromScriptSnapshot(scriptSnapshot: IScriptSnapshot): ISimpleText { + return new SimpleScriptSnapshotText(scriptSnapshot); + } +} \ No newline at end of file diff --git a/src/services/text/textSpan.ts b/src/services/text/textSpan.ts new file mode 100644 index 00000000000..b0da8279841 --- /dev/null +++ b/src/services/text/textSpan.ts @@ -0,0 +1,141 @@ +/// + +module TypeScript { + export interface ISpan { + start(): number; + end(): number; + } + + export class TextSpan implements ISpan { + private _start: number; + private _length: number; + + /** + * Creates a TextSpan instance beginning with the position Start and having the Length + * specified with length. + */ + constructor(start: number, length: number) { + if (start < 0) { + Errors.argument("start"); + } + + if (length < 0) { + Errors.argument("length"); + } + + this._start = start; + this._length = length; + } + + public start(): number { + return this._start; + } + + public length(): number { + return this._length; + } + + public end(): number { + return this._start + this._length; + } + + public isEmpty(): boolean { + return this._length === 0; + } + + /** + * Determines whether the position lies within the span. Returns true if the position is greater than or equal to Start and strictly less + * than End, otherwise false. + * @param position The position to check. + */ + public containsPosition(position: number): boolean { + return position >= this._start && position < this.end(); + } + + /** + * Determines whether span falls completely within this span. Returns true if the specified span falls completely within this span, otherwise false. + * @param span The span to check. + */ + public containsTextSpan(span: TextSpan): boolean { + return span._start >= this._start && span.end() <= this.end(); + } + + /** + * Determines whether the given span overlaps this span. Two spans are considered to overlap + * if they have positions in common and neither is empty. Empty spans do not overlap with any + * other span. Returns true if the spans overlap, false otherwise. + * @param span The span to check. + */ + public overlapsWith(span: TextSpan): boolean { + var overlapStart = Math.max(this._start, span._start); + var overlapEnd = Math.min(this.end(), span.end()); + + return overlapStart < overlapEnd; + } + + /** + * Returns the overlap with the given span, or null if there is no overlap. + * @param span The span to check. + */ + public overlap(span: TextSpan): TextSpan { + var overlapStart = Math.max(this._start, span._start); + var overlapEnd = Math.min(this.end(), span.end()); + + if (overlapStart < overlapEnd) { + return TextSpan.fromBounds(overlapStart, overlapEnd); + } + + return null; + } + + /** + * Determines whether span intersects this span. Two spans are considered to + * intersect if they have positions in common or the end of one span + * coincides with the start of the other span. Returns true if the spans intersect, false otherwise. + * @param The span to check. + */ + public intersectsWithTextSpan(span: TextSpan): boolean { + return span._start <= this.end() && span.end() >= this._start; + } + + public intersectsWith(start: number, length: number): boolean { + var end = start + length; + return start <= this.end() && end >= this._start; + } + + /** + * Determines whether the given position intersects this span. + * A position is considered to intersect if it is between the start and + * end positions (inclusive) of this span. Returns true if the position intersects, false otherwise. + * @param position The position to check. + */ + public intersectsWithPosition(position: number): boolean { + return position <= this.end() && position >= this._start; + } + + /** + * Returns the intersection with the given span, or null if there is no intersection. + * @param span The span to check. + */ + public intersection(span: TextSpan): TextSpan { + var intersectStart = Math.max(this._start, span._start); + var intersectEnd = Math.min(this.end(), span.end()); + + if (intersectStart <= intersectEnd) { + return TextSpan.fromBounds(intersectStart, intersectEnd); + } + + return null; + } + + /** + * Creates a new TextSpan from the given start and end positions + * as opposed to a position and length. + */ + public static fromBounds(start: number, end: number): TextSpan { + Debug.assert(start >= 0); + Debug.assert(end - start >= 0); + return new TextSpan(start, end - start); + } + } +} \ No newline at end of file diff --git a/src/services/text/textUtilities.ts b/src/services/text/textUtilities.ts new file mode 100644 index 00000000000..a495f97e941 --- /dev/null +++ b/src/services/text/textUtilities.ts @@ -0,0 +1,94 @@ +/// + +module TypeScript.TextUtilities { + export interface ICharacterSequence { + charCodeAt(index: number): number; + length: number; + } + + export function parseLineStarts(text: ICharacterSequence): number[]{ + var length = text.length; + + // Corner case check + if (0 === length) { + var result = new Array(); + result.push(0); + return result; + } + + var position = 0; + var index = 0; + var arrayBuilder = new Array(); + var lineNumber = 0; + + // The following loop goes through every character in the text. It is highly + // performance critical, and thus inlines knowledge about common line breaks + // and non-line breaks. + while (index < length) { + var c = text.charCodeAt(index); + var lineBreakLength: number; + + // common case - ASCII & not a line break + if (c > CharacterCodes.carriageReturn && c <= 127) { + index++; + continue; + } + else if (c === CharacterCodes.carriageReturn && index + 1 < length && text.charCodeAt(index + 1) === CharacterCodes.lineFeed) { + lineBreakLength = 2; + } + else if (c === CharacterCodes.lineFeed) { + lineBreakLength = 1; + } + else { + lineBreakLength = TextUtilities.getLengthOfLineBreak(text, index); + } + + if (0 === lineBreakLength) { + index++; + } + else { + arrayBuilder.push(position); + index += lineBreakLength; + position = index; + lineNumber++; + } + } + + // Create a start for the final line. + arrayBuilder.push(position); + + return arrayBuilder; + } + + export function getLengthOfLineBreakSlow(text: ICharacterSequence, index: number, c: number): number { + if (c === CharacterCodes.carriageReturn) { + var next = index + 1; + return (next < text.length) && CharacterCodes.lineFeed === text.charCodeAt(next) ? 2 : 1; + } + else if (isAnyLineBreakCharacter(c)) { + return 1; + } + else { + return 0; + } + } + + export function getLengthOfLineBreak(text: ICharacterSequence, index: number): number { + var c = text.charCodeAt(index); + + // common case - ASCII & not a line break + if (c > CharacterCodes.carriageReturn && c <= 127) { + return 0; + } + + return getLengthOfLineBreakSlow(text, index, c); + } + + export function isAnyLineBreakCharacter(c: number): boolean { + return c === CharacterCodes.lineFeed || + c === CharacterCodes.carriageReturn || + c === CharacterCodes.nextLine || + c === CharacterCodes.lineSeparator || + c === CharacterCodes.paragraphSeparator; + } +} \ No newline at end of file diff --git a/tests/cases/fourslash_old/addDeclareToFunction.ts b/tests/cases/fourslash/addDeclareToFunction.ts similarity index 100% rename from tests/cases/fourslash_old/addDeclareToFunction.ts rename to tests/cases/fourslash/addDeclareToFunction.ts diff --git a/tests/cases/fourslash_old/addDeclareToModule.ts b/tests/cases/fourslash/addDeclareToModule.ts similarity index 100% rename from tests/cases/fourslash_old/addDeclareToModule.ts rename to tests/cases/fourslash/addDeclareToModule.ts diff --git a/tests/cases/fourslash_old/addDuplicateSetter.ts b/tests/cases/fourslash/addDuplicateSetter.ts similarity index 100% rename from tests/cases/fourslash_old/addDuplicateSetter.ts rename to tests/cases/fourslash/addDuplicateSetter.ts diff --git a/tests/cases/fourslash_old/addFunctionAboveMultiLineLambdaExpression.ts b/tests/cases/fourslash/addFunctionAboveMultiLineLambdaExpression.ts similarity index 100% rename from tests/cases/fourslash_old/addFunctionAboveMultiLineLambdaExpression.ts rename to tests/cases/fourslash/addFunctionAboveMultiLineLambdaExpression.ts diff --git a/tests/cases/fourslash_old/addFunctionInDuplicatedConstructorClassBody.ts b/tests/cases/fourslash/addFunctionInDuplicatedConstructorClassBody.ts similarity index 100% rename from tests/cases/fourslash_old/addFunctionInDuplicatedConstructorClassBody.ts rename to tests/cases/fourslash/addFunctionInDuplicatedConstructorClassBody.ts diff --git a/tests/cases/fourslash_old/addInterfaceMemberAboveClass.ts b/tests/cases/fourslash/addInterfaceMemberAboveClass.ts similarity index 100% rename from tests/cases/fourslash_old/addInterfaceMemberAboveClass.ts rename to tests/cases/fourslash/addInterfaceMemberAboveClass.ts diff --git a/tests/cases/fourslash_old/addInterfaceToNotSatisfyConstraint.ts b/tests/cases/fourslash/addInterfaceToNotSatisfyConstraint.ts similarity index 100% rename from tests/cases/fourslash_old/addInterfaceToNotSatisfyConstraint.ts rename to tests/cases/fourslash/addInterfaceToNotSatisfyConstraint.ts diff --git a/tests/cases/fourslash_old/addMemberToInterface.ts b/tests/cases/fourslash/addMemberToInterface.ts similarity index 100% rename from tests/cases/fourslash_old/addMemberToInterface.ts rename to tests/cases/fourslash/addMemberToInterface.ts diff --git a/tests/cases/fourslash_old/addMethodToInterface1.ts b/tests/cases/fourslash/addMethodToInterface1.ts similarity index 100% rename from tests/cases/fourslash_old/addMethodToInterface1.ts rename to tests/cases/fourslash/addMethodToInterface1.ts diff --git a/tests/cases/fourslash_old/addSignaturePartial.ts b/tests/cases/fourslash/addSignaturePartial.ts similarity index 100% rename from tests/cases/fourslash_old/addSignaturePartial.ts rename to tests/cases/fourslash/addSignaturePartial.ts diff --git a/tests/cases/fourslash_old/addVarToConstructor1.ts b/tests/cases/fourslash/addVarToConstructor1.ts similarity index 100% rename from tests/cases/fourslash_old/addVarToConstructor1.ts rename to tests/cases/fourslash/addVarToConstructor1.ts diff --git a/tests/cases/fourslash_old/aliasToVarUsedAsType.ts b/tests/cases/fourslash/aliasToVarUsedAsType.ts similarity index 100% rename from tests/cases/fourslash_old/aliasToVarUsedAsType.ts rename to tests/cases/fourslash/aliasToVarUsedAsType.ts diff --git a/tests/cases/fourslash_old/alignmentAfterFormattingOnMultilineExpressionAndParametersList.ts b/tests/cases/fourslash/alignmentAfterFormattingOnMultilineExpressionAndParametersList.ts similarity index 100% rename from tests/cases/fourslash_old/alignmentAfterFormattingOnMultilineExpressionAndParametersList.ts rename to tests/cases/fourslash/alignmentAfterFormattingOnMultilineExpressionAndParametersList.ts diff --git a/tests/cases/fourslash_old/ambientVariablesWithSameName.ts b/tests/cases/fourslash/ambientVariablesWithSameName.ts similarity index 100% rename from tests/cases/fourslash_old/ambientVariablesWithSameName.ts rename to tests/cases/fourslash/ambientVariablesWithSameName.ts diff --git a/tests/cases/fourslash_old/argumentsAreAvailableAfterEditsAtEndOfFunction.ts b/tests/cases/fourslash/argumentsAreAvailableAfterEditsAtEndOfFunction.ts similarity index 100% rename from tests/cases/fourslash_old/argumentsAreAvailableAfterEditsAtEndOfFunction.ts rename to tests/cases/fourslash/argumentsAreAvailableAfterEditsAtEndOfFunction.ts diff --git a/tests/cases/fourslash_old/argumentsIndexExpression.ts b/tests/cases/fourslash/argumentsIndexExpression.ts similarity index 100% rename from tests/cases/fourslash_old/argumentsIndexExpression.ts rename to tests/cases/fourslash/argumentsIndexExpression.ts diff --git a/tests/cases/fourslash_old/arrayCallAndConstructTypings.ts b/tests/cases/fourslash/arrayCallAndConstructTypings.ts similarity index 100% rename from tests/cases/fourslash_old/arrayCallAndConstructTypings.ts rename to tests/cases/fourslash/arrayCallAndConstructTypings.ts diff --git a/tests/cases/fourslash_old/arrayConcatTypeCheck0.ts b/tests/cases/fourslash/arrayConcatTypeCheck0.ts similarity index 100% rename from tests/cases/fourslash_old/arrayConcatTypeCheck0.ts rename to tests/cases/fourslash/arrayConcatTypeCheck0.ts diff --git a/tests/cases/fourslash_old/arrayConcatTypeCheck1.ts b/tests/cases/fourslash/arrayConcatTypeCheck1.ts similarity index 100% rename from tests/cases/fourslash_old/arrayConcatTypeCheck1.ts rename to tests/cases/fourslash/arrayConcatTypeCheck1.ts diff --git a/tests/cases/fourslash_old/arrayTypeMismatchIncrementalTypeCheck.ts b/tests/cases/fourslash/arrayTypeMismatchIncrementalTypeCheck.ts similarity index 100% rename from tests/cases/fourslash_old/arrayTypeMismatchIncrementalTypeCheck.ts rename to tests/cases/fourslash/arrayTypeMismatchIncrementalTypeCheck.ts diff --git a/tests/cases/fourslash_old/assertContextualType.ts b/tests/cases/fourslash/assertContextualType.ts similarity index 100% rename from tests/cases/fourslash_old/assertContextualType.ts rename to tests/cases/fourslash/assertContextualType.ts diff --git a/tests/cases/fourslash_old/assignToExistingClass.ts b/tests/cases/fourslash/assignToExistingClass.ts similarity index 100% rename from tests/cases/fourslash_old/assignToExistingClass.ts rename to tests/cases/fourslash/assignToExistingClass.ts diff --git a/tests/cases/fourslash_old/autoFormattingOnPasting.ts b/tests/cases/fourslash/autoFormattingOnPasting.ts similarity index 100% rename from tests/cases/fourslash_old/autoFormattingOnPasting.ts rename to tests/cases/fourslash/autoFormattingOnPasting.ts diff --git a/tests/cases/fourslash/breakpointValidationFunctionExpressions.ts b/tests/cases/fourslash/breakpointValidationFunctionExpressions.ts deleted file mode 100644 index e47a56cc08f..00000000000 --- a/tests/cases/fourslash/breakpointValidationFunctionExpressions.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// - -// @BaselineFile: bpSpan_functionExpressions.baseline -// @Filename: bpSpan_functionExpressions.ts -////var greetings = 0; -////var greet = (greeting: string): number => { -//// greetings++; -//// return greetings; -////} -////greet("Hello"); -////var incrGreetings = () => greetings++; -////var greetNewMsg = msg => greet(msg); -debugger; -verify.baselineCurrentFileBreakpointLocations(); \ No newline at end of file diff --git a/tests/cases/fourslash_old/brokenClassErrorRecovery.ts b/tests/cases/fourslash/brokenClassErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/brokenClassErrorRecovery.ts rename to tests/cases/fourslash/brokenClassErrorRecovery.ts diff --git a/tests/cases/fourslash_old/chainedFatArrowFormatting.ts b/tests/cases/fourslash/chainedFatArrowFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/chainedFatArrowFormatting.ts rename to tests/cases/fourslash/chainedFatArrowFormatting.ts diff --git a/tests/cases/fourslash_old/chainedFunctionFunctionArgIndent.ts b/tests/cases/fourslash/chainedFunctionFunctionArgIndent.ts similarity index 100% rename from tests/cases/fourslash_old/chainedFunctionFunctionArgIndent.ts rename to tests/cases/fourslash/chainedFunctionFunctionArgIndent.ts diff --git a/tests/cases/fourslash_old/chainedFunctionLambdaArgIndex.ts b/tests/cases/fourslash/chainedFunctionLambdaArgIndex.ts similarity index 100% rename from tests/cases/fourslash_old/chainedFunctionLambdaArgIndex.ts rename to tests/cases/fourslash/chainedFunctionLambdaArgIndex.ts diff --git a/tests/cases/fourslash_old/classInterfaceInsert.ts b/tests/cases/fourslash/classInterfaceInsert.ts similarity index 100% rename from tests/cases/fourslash_old/classInterfaceInsert.ts rename to tests/cases/fourslash/classInterfaceInsert.ts diff --git a/tests/cases/fourslash_old/classRenamingErrorRecovery.ts b/tests/cases/fourslash/classRenamingErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/classRenamingErrorRecovery.ts rename to tests/cases/fourslash/classRenamingErrorRecovery.ts diff --git a/tests/cases/fourslash_old/cloduleAsBaseClass.ts b/tests/cases/fourslash/cloduleAsBaseClass.ts similarity index 100% rename from tests/cases/fourslash_old/cloduleAsBaseClass.ts rename to tests/cases/fourslash/cloduleAsBaseClass.ts diff --git a/tests/cases/fourslash_old/closedCommentsInConstructor.ts b/tests/cases/fourslash/closedCommentsInConstructor.ts similarity index 100% rename from tests/cases/fourslash_old/closedCommentsInConstructor.ts rename to tests/cases/fourslash/closedCommentsInConstructor.ts diff --git a/tests/cases/fourslash_old/commentsBlocks.ts b/tests/cases/fourslash/commentsBlocks.ts similarity index 100% rename from tests/cases/fourslash_old/commentsBlocks.ts rename to tests/cases/fourslash/commentsBlocks.ts diff --git a/tests/cases/fourslash_old/completionInTypeOf2.ts b/tests/cases/fourslash/completionInTypeOf2.ts similarity index 100% rename from tests/cases/fourslash_old/completionInTypeOf2.ts rename to tests/cases/fourslash/completionInTypeOf2.ts diff --git a/tests/cases/fourslash_old/completionListAfterAnyType.ts b/tests/cases/fourslash/completionListAfterAnyType.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAfterAnyType.ts rename to tests/cases/fourslash/completionListAfterAnyType.ts diff --git a/tests/cases/fourslash_old/completionListAfterClassExtends.ts b/tests/cases/fourslash/completionListAfterClassExtends.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAfterClassExtends.ts rename to tests/cases/fourslash/completionListAfterClassExtends.ts diff --git a/tests/cases/fourslash_old/completionListAfterInvalidCharacter.ts b/tests/cases/fourslash/completionListAfterInvalidCharacter.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAfterInvalidCharacter.ts rename to tests/cases/fourslash/completionListAfterInvalidCharacter.ts diff --git a/tests/cases/fourslash_old/completionListAfterNumericLiteral1.ts b/tests/cases/fourslash/completionListAfterNumericLiteral1.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAfterNumericLiteral1.ts rename to tests/cases/fourslash/completionListAfterNumericLiteral1.ts diff --git a/tests/cases/fourslash_old/completionListAfterRegularExpressionLiteral1.ts b/tests/cases/fourslash/completionListAfterRegularExpressionLiteral1.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAfterRegularExpressionLiteral1.ts rename to tests/cases/fourslash/completionListAfterRegularExpressionLiteral1.ts diff --git a/tests/cases/fourslash_old/completionListAfterStringLiteral1.ts b/tests/cases/fourslash/completionListAfterStringLiteral1.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAfterStringLiteral1.ts rename to tests/cases/fourslash/completionListAfterStringLiteral1.ts diff --git a/tests/cases/fourslash_old/completionListAndMemberListOnCommentedDot.ts b/tests/cases/fourslash/completionListAndMemberListOnCommentedDot.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAndMemberListOnCommentedDot.ts rename to tests/cases/fourslash/completionListAndMemberListOnCommentedDot.ts diff --git a/tests/cases/fourslash_old/completionListAndMemberListOnCommentedLine.ts b/tests/cases/fourslash/completionListAndMemberListOnCommentedLine.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAndMemberListOnCommentedLine.ts rename to tests/cases/fourslash/completionListAndMemberListOnCommentedLine.ts diff --git a/tests/cases/fourslash_old/completionListAndMemberListOnCommentedWhiteSpace.ts b/tests/cases/fourslash/completionListAndMemberListOnCommentedWhiteSpace.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAndMemberListOnCommentedWhiteSpace.ts rename to tests/cases/fourslash/completionListAndMemberListOnCommentedWhiteSpace.ts diff --git a/tests/cases/fourslash_old/completionListAtDeclarationOfParameterType.ts b/tests/cases/fourslash/completionListAtDeclarationOfParameterType.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAtDeclarationOfParameterType.ts rename to tests/cases/fourslash/completionListAtDeclarationOfParameterType.ts diff --git a/tests/cases/fourslash_old/completionListAtEOF.ts b/tests/cases/fourslash/completionListAtEOF.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAtEOF.ts rename to tests/cases/fourslash/completionListAtEOF.ts diff --git a/tests/cases/fourslash_old/completionListAtEOF1.ts b/tests/cases/fourslash/completionListAtEOF1.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAtEOF1.ts rename to tests/cases/fourslash/completionListAtEOF1.ts diff --git a/tests/cases/fourslash_old/completionListAtIdentifierDefinitionLocations.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAtIdentifierDefinitionLocations.ts rename to tests/cases/fourslash/completionListAtIdentifierDefinitionLocations.ts diff --git a/tests/cases/fourslash_old/completionListAtInvalidLocations.ts b/tests/cases/fourslash/completionListAtInvalidLocations.ts similarity index 100% rename from tests/cases/fourslash_old/completionListAtInvalidLocations.ts rename to tests/cases/fourslash/completionListAtInvalidLocations.ts diff --git a/tests/cases/fourslash_old/completionListErrorRecovery.ts b/tests/cases/fourslash/completionListErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/completionListErrorRecovery.ts rename to tests/cases/fourslash/completionListErrorRecovery.ts diff --git a/tests/cases/fourslash_old/completionListErrorRecovery2.ts b/tests/cases/fourslash/completionListErrorRecovery2.ts similarity index 100% rename from tests/cases/fourslash_old/completionListErrorRecovery2.ts rename to tests/cases/fourslash/completionListErrorRecovery2.ts diff --git a/tests/cases/fourslash_old/completionListForGenericInstance1.ts b/tests/cases/fourslash/completionListForGenericInstance1.ts similarity index 100% rename from tests/cases/fourslash_old/completionListForGenericInstance1.ts rename to tests/cases/fourslash/completionListForGenericInstance1.ts diff --git a/tests/cases/fourslash_old/completionListFunctionMembers.ts b/tests/cases/fourslash/completionListFunctionMembers.ts similarity index 100% rename from tests/cases/fourslash_old/completionListFunctionMembers.ts rename to tests/cases/fourslash/completionListFunctionMembers.ts diff --git a/tests/cases/fourslash_old/completionListInComments.ts b/tests/cases/fourslash/completionListInComments.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInComments.ts rename to tests/cases/fourslash/completionListInComments.ts diff --git a/tests/cases/fourslash_old/completionListInEmptyFile.ts b/tests/cases/fourslash/completionListInEmptyFile.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInEmptyFile.ts rename to tests/cases/fourslash/completionListInEmptyFile.ts diff --git a/tests/cases/fourslash_old/completionListInFatArrow.ts b/tests/cases/fourslash/completionListInFatArrow.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInFatArrow.ts rename to tests/cases/fourslash/completionListInFatArrow.ts diff --git a/tests/cases/fourslash_old/completionListInFunctionDeclaration.ts b/tests/cases/fourslash/completionListInFunctionDeclaration.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInFunctionDeclaration.ts rename to tests/cases/fourslash/completionListInFunctionDeclaration.ts diff --git a/tests/cases/fourslash_old/completionListInFunctionExpression.ts b/tests/cases/fourslash/completionListInFunctionExpression.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInFunctionExpression.ts rename to tests/cases/fourslash/completionListInFunctionExpression.ts diff --git a/tests/cases/fourslash_old/completionListInObjectLiteral.ts b/tests/cases/fourslash/completionListInObjectLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInObjectLiteral.ts rename to tests/cases/fourslash/completionListInObjectLiteral.ts diff --git a/tests/cases/fourslash_old/completionListInObjectLiteral2.ts b/tests/cases/fourslash/completionListInObjectLiteral2.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInObjectLiteral2.ts rename to tests/cases/fourslash/completionListInObjectLiteral2.ts diff --git a/tests/cases/fourslash_old/completionListInObjectLiteralThatIsParameterOfFunctionCall.ts b/tests/cases/fourslash/completionListInObjectLiteralThatIsParameterOfFunctionCall.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInObjectLiteralThatIsParameterOfFunctionCall.ts rename to tests/cases/fourslash/completionListInObjectLiteralThatIsParameterOfFunctionCall.ts diff --git a/tests/cases/fourslash_old/completionListInScope.ts b/tests/cases/fourslash/completionListInScope.ts similarity index 100% rename from tests/cases/fourslash_old/completionListInScope.ts rename to tests/cases/fourslash/completionListInScope.ts diff --git a/tests/cases/fourslash_old/completionListKeywords.ts b/tests/cases/fourslash/completionListKeywords.ts similarity index 100% rename from tests/cases/fourslash_old/completionListKeywords.ts rename to tests/cases/fourslash/completionListKeywords.ts diff --git a/tests/cases/fourslash_old/completionListOfSplitInterface.ts b/tests/cases/fourslash/completionListOfSplitInterface.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOfSplitInterface.ts rename to tests/cases/fourslash/completionListOfSplitInterface.ts diff --git a/tests/cases/fourslash_old/completionListOnAliasedModule.ts b/tests/cases/fourslash/completionListOnAliasedModule.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOnAliasedModule.ts rename to tests/cases/fourslash/completionListOnAliasedModule.ts diff --git a/tests/cases/fourslash_old/completionListOnMethodParameterName.ts b/tests/cases/fourslash/completionListOnMethodParameterName.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOnMethodParameterName.ts rename to tests/cases/fourslash/completionListOnMethodParameterName.ts diff --git a/tests/cases/fourslash_old/completionListOnParamInClass.ts b/tests/cases/fourslash/completionListOnParamInClass.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOnParamInClass.ts rename to tests/cases/fourslash/completionListOnParamInClass.ts diff --git a/tests/cases/fourslash_old/completionListOnParamOfGenericType1.ts b/tests/cases/fourslash/completionListOnParamOfGenericType1.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOnParamOfGenericType1.ts rename to tests/cases/fourslash/completionListOnParamOfGenericType1.ts diff --git a/tests/cases/fourslash_old/completionListOnPrivateVariableInModule.ts b/tests/cases/fourslash/completionListOnPrivateVariableInModule.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOnPrivateVariableInModule.ts rename to tests/cases/fourslash/completionListOnPrivateVariableInModule.ts diff --git a/tests/cases/fourslash_old/completionListOnSuper.ts b/tests/cases/fourslash/completionListOnSuper.ts similarity index 100% rename from tests/cases/fourslash_old/completionListOnSuper.ts rename to tests/cases/fourslash/completionListOnSuper.ts diff --git a/tests/cases/fourslash_old/completionListsThroughTransitiveBaseClasses.ts b/tests/cases/fourslash/completionListsThroughTransitiveBaseClasses.ts similarity index 100% rename from tests/cases/fourslash_old/completionListsThroughTransitiveBaseClasses.ts rename to tests/cases/fourslash/completionListsThroughTransitiveBaseClasses.ts diff --git a/tests/cases/fourslash_old/completionListsThroughTransitiveBaseClasses2.ts b/tests/cases/fourslash/completionListsThroughTransitiveBaseClasses2.ts similarity index 100% rename from tests/cases/fourslash_old/completionListsThroughTransitiveBaseClasses2.ts rename to tests/cases/fourslash/completionListsThroughTransitiveBaseClasses2.ts diff --git a/tests/cases/fourslash_old/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts b/tests/cases/fourslash/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts similarity index 100% rename from tests/cases/fourslash_old/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts rename to tests/cases/fourslash/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts diff --git a/tests/cases/fourslash_old/consistenceOnIndentionsOfChainedFunctionCalls.ts b/tests/cases/fourslash/consistenceOnIndentionsOfChainedFunctionCalls.ts similarity index 100% rename from tests/cases/fourslash_old/consistenceOnIndentionsOfChainedFunctionCalls.ts rename to tests/cases/fourslash/consistenceOnIndentionsOfChainedFunctionCalls.ts diff --git a/tests/cases/fourslash_old/consistenceOnIndentionsOfObjectsInAListAfterFormatting.ts b/tests/cases/fourslash/consistenceOnIndentionsOfObjectsInAListAfterFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/consistenceOnIndentionsOfObjectsInAListAfterFormatting.ts rename to tests/cases/fourslash/consistenceOnIndentionsOfObjectsInAListAfterFormatting.ts diff --git a/tests/cases/fourslash_old/consistentContextualTypeErrorsAfterEdits.ts b/tests/cases/fourslash/consistentContextualTypeErrorsAfterEdits.ts similarity index 100% rename from tests/cases/fourslash_old/consistentContextualTypeErrorsAfterEdits.ts rename to tests/cases/fourslash/consistentContextualTypeErrorsAfterEdits.ts diff --git a/tests/cases/fourslash_old/constructorBraceFormatting.ts b/tests/cases/fourslash/constructorBraceFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/constructorBraceFormatting.ts rename to tests/cases/fourslash/constructorBraceFormatting.ts diff --git a/tests/cases/fourslash_old/constructorQuickInfo.ts b/tests/cases/fourslash/constructorQuickInfo.ts similarity index 100% rename from tests/cases/fourslash_old/constructorQuickInfo.ts rename to tests/cases/fourslash/constructorQuickInfo.ts diff --git a/tests/cases/fourslash_old/contextuallyTypedFunctionExpressionGeneric1.ts b/tests/cases/fourslash/contextuallyTypedFunctionExpressionGeneric1.ts similarity index 100% rename from tests/cases/fourslash_old/contextuallyTypedFunctionExpressionGeneric1.ts rename to tests/cases/fourslash/contextuallyTypedFunctionExpressionGeneric1.ts diff --git a/tests/cases/fourslash_old/debuggerStatementIndent.ts b/tests/cases/fourslash/debuggerStatementIndent.ts similarity index 100% rename from tests/cases/fourslash_old/debuggerStatementIndent.ts rename to tests/cases/fourslash/debuggerStatementIndent.ts diff --git a/tests/cases/fourslash_old/declareFunction.ts b/tests/cases/fourslash/declareFunction.ts similarity index 100% rename from tests/cases/fourslash_old/declareFunction.ts rename to tests/cases/fourslash/declareFunction.ts diff --git a/tests/cases/fourslash_old/deleteClassWithEnumPresent.ts b/tests/cases/fourslash/deleteClassWithEnumPresent.ts similarity index 100% rename from tests/cases/fourslash_old/deleteClassWithEnumPresent.ts rename to tests/cases/fourslash/deleteClassWithEnumPresent.ts diff --git a/tests/cases/fourslash_old/deleteExtensionInReopenedInterface.ts b/tests/cases/fourslash/deleteExtensionInReopenedInterface.ts similarity index 100% rename from tests/cases/fourslash_old/deleteExtensionInReopenedInterface.ts rename to tests/cases/fourslash/deleteExtensionInReopenedInterface.ts diff --git a/tests/cases/fourslash_old/deleteReopenedModule.ts b/tests/cases/fourslash/deleteReopenedModule.ts similarity index 100% rename from tests/cases/fourslash_old/deleteReopenedModule.ts rename to tests/cases/fourslash/deleteReopenedModule.ts diff --git a/tests/cases/fourslash_old/deleteTypeParameter.ts b/tests/cases/fourslash/deleteTypeParameter.ts similarity index 100% rename from tests/cases/fourslash_old/deleteTypeParameter.ts rename to tests/cases/fourslash/deleteTypeParameter.ts diff --git a/tests/cases/fourslash_old/duplicateClassModuleError0.ts b/tests/cases/fourslash/duplicateClassModuleError0.ts similarity index 100% rename from tests/cases/fourslash_old/duplicateClassModuleError0.ts rename to tests/cases/fourslash/duplicateClassModuleError0.ts diff --git a/tests/cases/fourslash_old/duplicateFunctionImplementation.ts b/tests/cases/fourslash/duplicateFunctionImplementation.ts similarity index 100% rename from tests/cases/fourslash_old/duplicateFunctionImplementation.ts rename to tests/cases/fourslash/duplicateFunctionImplementation.ts diff --git a/tests/cases/fourslash_old/editLambdaArgToTypeParameter1.ts b/tests/cases/fourslash/editLambdaArgToTypeParameter1.ts similarity index 100% rename from tests/cases/fourslash_old/editLambdaArgToTypeParameter1.ts rename to tests/cases/fourslash/editLambdaArgToTypeParameter1.ts diff --git a/tests/cases/fourslash_old/emptyTypeArgumentList.ts b/tests/cases/fourslash/emptyTypeArgumentList.ts similarity index 100% rename from tests/cases/fourslash_old/emptyTypeArgumentList.ts rename to tests/cases/fourslash/emptyTypeArgumentList.ts diff --git a/tests/cases/fourslash_old/enumAddition.ts b/tests/cases/fourslash/enumAddition.ts similarity index 100% rename from tests/cases/fourslash_old/enumAddition.ts rename to tests/cases/fourslash/enumAddition.ts diff --git a/tests/cases/fourslash_old/enumUpdate1.ts b/tests/cases/fourslash/enumUpdate1.ts similarity index 100% rename from tests/cases/fourslash_old/enumUpdate1.ts rename to tests/cases/fourslash/enumUpdate1.ts diff --git a/tests/cases/fourslash/errorConsistency.ts b/tests/cases/fourslash/errorConsistency.ts index ab910b4cf68..3d17870f3b4 100644 --- a/tests/cases/fourslash/errorConsistency.ts +++ b/tests/cases/fourslash/errorConsistency.ts @@ -4,7 +4,7 @@ ////val(f: (t: T) => U): Int; ////} ////declare var v1: Int; -////var /*1*/v2: Int = v1/*2*/; +////var /*1*/v2/*2*/: Int = v1; goTo.eof(); verify.errorExistsBetweenMarkers("1", "2"); diff --git a/tests/cases/fourslash_old/exportClauseErrorReporting0.ts b/tests/cases/fourslash/exportClauseErrorReporting0.ts similarity index 100% rename from tests/cases/fourslash_old/exportClauseErrorReporting0.ts rename to tests/cases/fourslash/exportClauseErrorReporting0.ts diff --git a/tests/cases/fourslash_old/exportEqualsInterfaceA.ts b/tests/cases/fourslash/exportEqualsInterfaceA.ts similarity index 100% rename from tests/cases/fourslash_old/exportEqualsInterfaceA.ts rename to tests/cases/fourslash/exportEqualsInterfaceA.ts diff --git a/tests/cases/fourslash_old/extendArrayInterface.ts b/tests/cases/fourslash/extendArrayInterface.ts similarity index 100% rename from tests/cases/fourslash_old/extendArrayInterface.ts rename to tests/cases/fourslash/extendArrayInterface.ts diff --git a/tests/cases/fourslash_old/failureToImplementClass.ts b/tests/cases/fourslash/failureToImplementClass.ts similarity index 100% rename from tests/cases/fourslash_old/failureToImplementClass.ts rename to tests/cases/fourslash/failureToImplementClass.ts diff --git a/tests/cases/fourslash_old/forIn.ts b/tests/cases/fourslash/forIn.ts similarity index 100% rename from tests/cases/fourslash_old/forIn.ts rename to tests/cases/fourslash/forIn.ts diff --git a/tests/cases/fourslash_old/forceIndentAfterNewLineInsert.ts b/tests/cases/fourslash/forceIndentAfterNewLineInsert.ts similarity index 100% rename from tests/cases/fourslash_old/forceIndentAfterNewLineInsert.ts rename to tests/cases/fourslash/forceIndentAfterNewLineInsert.ts diff --git a/tests/cases/fourslash_old/formatAfterObjectLiteral.ts b/tests/cases/fourslash/formatAfterObjectLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/formatAfterObjectLiteral.ts rename to tests/cases/fourslash/formatAfterObjectLiteral.ts diff --git a/tests/cases/fourslash_old/formatAnyTypeLiteral.ts b/tests/cases/fourslash/formatAnyTypeLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/formatAnyTypeLiteral.ts rename to tests/cases/fourslash/formatAnyTypeLiteral.ts diff --git a/tests/cases/fourslash_old/formatArrayOrObjectLiteralsInVariableList.ts b/tests/cases/fourslash/formatArrayOrObjectLiteralsInVariableList.ts similarity index 100% rename from tests/cases/fourslash_old/formatArrayOrObjectLiteralsInVariableList.ts rename to tests/cases/fourslash/formatArrayOrObjectLiteralsInVariableList.ts diff --git a/tests/cases/fourslash_old/formatColonAndQMark.ts b/tests/cases/fourslash/formatColonAndQMark.ts similarity index 100% rename from tests/cases/fourslash_old/formatColonAndQMark.ts rename to tests/cases/fourslash/formatColonAndQMark.ts diff --git a/tests/cases/fourslash_old/formatControlFlowConstructs.ts b/tests/cases/fourslash/formatControlFlowConstructs.ts similarity index 100% rename from tests/cases/fourslash_old/formatControlFlowConstructs.ts rename to tests/cases/fourslash/formatControlFlowConstructs.ts diff --git a/tests/cases/fourslash_old/formatDebuggerStatement.ts b/tests/cases/fourslash/formatDebuggerStatement.ts similarity index 100% rename from tests/cases/fourslash_old/formatDebuggerStatement.ts rename to tests/cases/fourslash/formatDebuggerStatement.ts diff --git a/tests/cases/fourslash_old/formatEmptyBlock.ts b/tests/cases/fourslash/formatEmptyBlock.ts similarity index 100% rename from tests/cases/fourslash_old/formatEmptyBlock.ts rename to tests/cases/fourslash/formatEmptyBlock.ts diff --git a/tests/cases/fourslash_old/formatImplicitModule.ts b/tests/cases/fourslash/formatImplicitModule.ts similarity index 100% rename from tests/cases/fourslash_old/formatImplicitModule.ts rename to tests/cases/fourslash/formatImplicitModule.ts diff --git a/tests/cases/fourslash_old/formatImportDeclaration.ts b/tests/cases/fourslash/formatImportDeclaration.ts similarity index 100% rename from tests/cases/fourslash_old/formatImportDeclaration.ts rename to tests/cases/fourslash/formatImportDeclaration.ts diff --git a/tests/cases/fourslash_old/formatMultilineComment.ts b/tests/cases/fourslash/formatMultilineComment.ts similarity index 100% rename from tests/cases/fourslash_old/formatMultilineComment.ts rename to tests/cases/fourslash/formatMultilineComment.ts diff --git a/tests/cases/fourslash_old/formatOnSemiColonAfterBreak.ts b/tests/cases/fourslash/formatOnSemiColonAfterBreak.ts similarity index 100% rename from tests/cases/fourslash_old/formatOnSemiColonAfterBreak.ts rename to tests/cases/fourslash/formatOnSemiColonAfterBreak.ts diff --git a/tests/cases/fourslash_old/formatSelectionWithTrivia.ts b/tests/cases/fourslash/formatSelectionWithTrivia.ts similarity index 100% rename from tests/cases/fourslash_old/formatSelectionWithTrivia.ts rename to tests/cases/fourslash/formatSelectionWithTrivia.ts diff --git a/tests/cases/fourslash_old/formatTryCatch.ts b/tests/cases/fourslash/formatTryCatch.ts similarity index 100% rename from tests/cases/fourslash_old/formatTryCatch.ts rename to tests/cases/fourslash/formatTryCatch.ts diff --git a/tests/cases/fourslash_old/formatVariableDeclarationList.ts b/tests/cases/fourslash/formatVariableDeclarationList.ts similarity index 100% rename from tests/cases/fourslash_old/formatVariableDeclarationList.ts rename to tests/cases/fourslash/formatVariableDeclarationList.ts diff --git a/tests/cases/fourslash_old/formatWithStatement.ts b/tests/cases/fourslash/formatWithStatement.ts similarity index 100% rename from tests/cases/fourslash_old/formatWithStatement.ts rename to tests/cases/fourslash/formatWithStatement.ts diff --git a/tests/cases/fourslash_old/formattingAfterChainedFatArrow.ts b/tests/cases/fourslash/formattingAfterChainedFatArrow.ts similarity index 100% rename from tests/cases/fourslash_old/formattingAfterChainedFatArrow.ts rename to tests/cases/fourslash/formattingAfterChainedFatArrow.ts diff --git a/tests/cases/fourslash_old/formattingAfterMultiLineIfCondition.ts b/tests/cases/fourslash/formattingAfterMultiLineIfCondition.ts similarity index 100% rename from tests/cases/fourslash_old/formattingAfterMultiLineIfCondition.ts rename to tests/cases/fourslash/formattingAfterMultiLineIfCondition.ts diff --git a/tests/cases/fourslash_old/formattingAfterMultiLineString.ts b/tests/cases/fourslash/formattingAfterMultiLineString.ts similarity index 100% rename from tests/cases/fourslash_old/formattingAfterMultiLineString.ts rename to tests/cases/fourslash/formattingAfterMultiLineString.ts diff --git a/tests/cases/fourslash_old/formattingArrayLiteral.ts b/tests/cases/fourslash/formattingArrayLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/formattingArrayLiteral.ts rename to tests/cases/fourslash/formattingArrayLiteral.ts diff --git a/tests/cases/fourslash_old/formattingComma.ts b/tests/cases/fourslash/formattingComma.ts similarity index 100% rename from tests/cases/fourslash_old/formattingComma.ts rename to tests/cases/fourslash/formattingComma.ts diff --git a/tests/cases/fourslash_old/formattingCrash.ts b/tests/cases/fourslash/formattingCrash.ts similarity index 100% rename from tests/cases/fourslash_old/formattingCrash.ts rename to tests/cases/fourslash/formattingCrash.ts diff --git a/tests/cases/fourslash_old/formattingElseInsideAFunction.ts b/tests/cases/fourslash/formattingElseInsideAFunction.ts similarity index 100% rename from tests/cases/fourslash_old/formattingElseInsideAFunction.ts rename to tests/cases/fourslash/formattingElseInsideAFunction.ts diff --git a/tests/cases/fourslash_old/formattingFatArrowFunctions.ts b/tests/cases/fourslash/formattingFatArrowFunctions.ts similarity index 100% rename from tests/cases/fourslash_old/formattingFatArrowFunctions.ts rename to tests/cases/fourslash/formattingFatArrowFunctions.ts diff --git a/tests/cases/fourslash_old/formattingForIn.ts b/tests/cases/fourslash/formattingForIn.ts similarity index 100% rename from tests/cases/fourslash_old/formattingForIn.ts rename to tests/cases/fourslash/formattingForIn.ts diff --git a/tests/cases/fourslash_old/formattingForLoopSemicolons.ts b/tests/cases/fourslash/formattingForLoopSemicolons.ts similarity index 100% rename from tests/cases/fourslash_old/formattingForLoopSemicolons.ts rename to tests/cases/fourslash/formattingForLoopSemicolons.ts diff --git a/tests/cases/fourslash_old/formattingObjectLiteral.ts b/tests/cases/fourslash/formattingObjectLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/formattingObjectLiteral.ts rename to tests/cases/fourslash/formattingObjectLiteral.ts diff --git a/tests/cases/fourslash_old/formattingOfChainedLambda.ts b/tests/cases/fourslash/formattingOfChainedLambda.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOfChainedLambda.ts rename to tests/cases/fourslash/formattingOfChainedLambda.ts diff --git a/tests/cases/fourslash_old/formattingOfModuleExport.ts b/tests/cases/fourslash/formattingOfModuleExport.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOfModuleExport.ts rename to tests/cases/fourslash/formattingOfModuleExport.ts diff --git a/tests/cases/fourslash_old/formattingOfMultilineBlockConstructs.ts b/tests/cases/fourslash/formattingOfMultilineBlockConstructs.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOfMultilineBlockConstructs.ts rename to tests/cases/fourslash/formattingOfMultilineBlockConstructs.ts diff --git a/tests/cases/fourslash_old/formattingOnClasses.ts b/tests/cases/fourslash/formattingOnClasses.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnClasses.ts rename to tests/cases/fourslash/formattingOnClasses.ts diff --git a/tests/cases/fourslash_old/formattingOnCloseBrace.ts b/tests/cases/fourslash/formattingOnCloseBrace.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnCloseBrace.ts rename to tests/cases/fourslash/formattingOnCloseBrace.ts diff --git a/tests/cases/fourslash_old/formattingOnClosingBracket.ts b/tests/cases/fourslash/formattingOnClosingBracket.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnClosingBracket.ts rename to tests/cases/fourslash/formattingOnClosingBracket.ts diff --git a/tests/cases/fourslash_old/formattingOnCommaOperator.ts b/tests/cases/fourslash/formattingOnCommaOperator.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnCommaOperator.ts rename to tests/cases/fourslash/formattingOnCommaOperator.ts diff --git a/tests/cases/fourslash_old/formattingOnDoWhileNoSemicolon.ts b/tests/cases/fourslash/formattingOnDoWhileNoSemicolon.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnDoWhileNoSemicolon.ts rename to tests/cases/fourslash/formattingOnDoWhileNoSemicolon.ts diff --git a/tests/cases/fourslash_old/formattingOnDocumentReadyFunction.ts b/tests/cases/fourslash/formattingOnDocumentReadyFunction.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnDocumentReadyFunction.ts rename to tests/cases/fourslash/formattingOnDocumentReadyFunction.ts diff --git a/tests/cases/fourslash_old/formattingOnEmptyInterfaceLiteral.ts b/tests/cases/fourslash/formattingOnEmptyInterfaceLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnEmptyInterfaceLiteral.ts rename to tests/cases/fourslash/formattingOnEmptyInterfaceLiteral.ts diff --git a/tests/cases/fourslash_old/formattingOnEnter.ts b/tests/cases/fourslash/formattingOnEnter.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnEnter.ts rename to tests/cases/fourslash/formattingOnEnter.ts diff --git a/tests/cases/fourslash_old/formattingOnEnterInComments.ts b/tests/cases/fourslash/formattingOnEnterInComments.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnEnterInComments.ts rename to tests/cases/fourslash/formattingOnEnterInComments.ts diff --git a/tests/cases/fourslash_old/formattingOnEnterInStrings.ts b/tests/cases/fourslash/formattingOnEnterInStrings.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnEnterInStrings.ts rename to tests/cases/fourslash/formattingOnEnterInStrings.ts diff --git a/tests/cases/fourslash_old/formattingOnInterfaces.ts b/tests/cases/fourslash/formattingOnInterfaces.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnInterfaces.ts rename to tests/cases/fourslash/formattingOnInterfaces.ts diff --git a/tests/cases/fourslash_old/formattingOnInvalidCodes.ts b/tests/cases/fourslash/formattingOnInvalidCodes.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnInvalidCodes.ts rename to tests/cases/fourslash/formattingOnInvalidCodes.ts diff --git a/tests/cases/fourslash_old/formattingOnModuleIndentation.ts b/tests/cases/fourslash/formattingOnModuleIndentation.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnModuleIndentation.ts rename to tests/cases/fourslash/formattingOnModuleIndentation.ts diff --git a/tests/cases/fourslash_old/formattingOnNestedDoWhileByEnter.ts b/tests/cases/fourslash/formattingOnNestedDoWhileByEnter.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnNestedDoWhileByEnter.ts rename to tests/cases/fourslash/formattingOnNestedDoWhileByEnter.ts diff --git a/tests/cases/fourslash_old/formattingOnNestedStatements.ts b/tests/cases/fourslash/formattingOnNestedStatements.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnNestedStatements.ts rename to tests/cases/fourslash/formattingOnNestedStatements.ts diff --git a/tests/cases/fourslash_old/formattingOnObjectLiteral.ts b/tests/cases/fourslash/formattingOnObjectLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnObjectLiteral.ts rename to tests/cases/fourslash/formattingOnObjectLiteral.ts diff --git a/tests/cases/fourslash_old/formattingOnOpenBraceOfFunctions.ts b/tests/cases/fourslash/formattingOnOpenBraceOfFunctions.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnOpenBraceOfFunctions.ts rename to tests/cases/fourslash/formattingOnOpenBraceOfFunctions.ts diff --git a/tests/cases/fourslash_old/formattingOnSemiColon.ts b/tests/cases/fourslash/formattingOnSemiColon.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnSemiColon.ts rename to tests/cases/fourslash/formattingOnSemiColon.ts diff --git a/tests/cases/fourslash_old/formattingOnSingleLineBlocks.ts b/tests/cases/fourslash/formattingOnSingleLineBlocks.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnSingleLineBlocks.ts rename to tests/cases/fourslash/formattingOnSingleLineBlocks.ts diff --git a/tests/cases/fourslash_old/formattingOnStatementsWithNoSemicolon.ts b/tests/cases/fourslash/formattingOnStatementsWithNoSemicolon.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnStatementsWithNoSemicolon.ts rename to tests/cases/fourslash/formattingOnStatementsWithNoSemicolon.ts diff --git a/tests/cases/fourslash_old/formattingOnTabAfterCloseCurly.ts b/tests/cases/fourslash/formattingOnTabAfterCloseCurly.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnTabAfterCloseCurly.ts rename to tests/cases/fourslash/formattingOnTabAfterCloseCurly.ts diff --git a/tests/cases/fourslash_old/formattingOnVariety.ts b/tests/cases/fourslash/formattingOnVariety.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOnVariety.ts rename to tests/cases/fourslash/formattingOnVariety.ts diff --git a/tests/cases/fourslash_old/formattingOptionsChange.ts b/tests/cases/fourslash/formattingOptionsChange.ts similarity index 100% rename from tests/cases/fourslash_old/formattingOptionsChange.ts rename to tests/cases/fourslash/formattingOptionsChange.ts diff --git a/tests/cases/fourslash_old/formattingSingleLineWithNewLineOptionSet.ts b/tests/cases/fourslash/formattingSingleLineWithNewLineOptionSet.ts similarity index 100% rename from tests/cases/fourslash_old/formattingSingleLineWithNewLineOptionSet.ts rename to tests/cases/fourslash/formattingSingleLineWithNewLineOptionSet.ts diff --git a/tests/cases/fourslash_old/formattingSkippedTokens.ts b/tests/cases/fourslash/formattingSkippedTokens.ts similarity index 100% rename from tests/cases/fourslash_old/formattingSkippedTokens.ts rename to tests/cases/fourslash/formattingSkippedTokens.ts diff --git a/tests/cases/fourslash_old/formattingSpacesAfterConstructor.ts b/tests/cases/fourslash/formattingSpacesAfterConstructor.ts similarity index 100% rename from tests/cases/fourslash_old/formattingSpacesAfterConstructor.ts rename to tests/cases/fourslash/formattingSpacesAfterConstructor.ts diff --git a/tests/cases/fourslash_old/formattingVoid.ts b/tests/cases/fourslash/formattingVoid.ts similarity index 100% rename from tests/cases/fourslash_old/formattingVoid.ts rename to tests/cases/fourslash/formattingVoid.ts diff --git a/tests/cases/fourslash_old/formattingWithEnterAfterMultilineString.ts b/tests/cases/fourslash/formattingWithEnterAfterMultilineString.ts similarity index 100% rename from tests/cases/fourslash_old/formattingWithEnterAfterMultilineString.ts rename to tests/cases/fourslash/formattingWithEnterAfterMultilineString.ts diff --git a/tests/cases/fourslash_old/formattingWithLimitedSpan.ts b/tests/cases/fourslash/formattingWithLimitedSpan.ts similarity index 100% rename from tests/cases/fourslash_old/formattingWithLimitedSpan.ts rename to tests/cases/fourslash/formattingWithLimitedSpan.ts diff --git a/tests/cases/fourslash_old/formattingofSingleLineBlockConstructs.ts b/tests/cases/fourslash/formattingofSingleLineBlockConstructs.ts similarity index 100% rename from tests/cases/fourslash_old/formattingofSingleLineBlockConstructs.ts rename to tests/cases/fourslash/formattingofSingleLineBlockConstructs.ts diff --git a/tests/cases/fourslash_old/forwardReference.ts b/tests/cases/fourslash/forwardReference.ts similarity index 100% rename from tests/cases/fourslash_old/forwardReference.ts rename to tests/cases/fourslash/forwardReference.ts diff --git a/tests/cases/fourslash_old/fsEditMarkerPositions.ts b/tests/cases/fourslash/fsEditMarkerPositions.ts similarity index 100% rename from tests/cases/fourslash_old/fsEditMarkerPositions.ts rename to tests/cases/fourslash/fsEditMarkerPositions.ts diff --git a/tests/cases/fourslash_old/functionFormatting.ts b/tests/cases/fourslash/functionFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/functionFormatting.ts rename to tests/cases/fourslash/functionFormatting.ts diff --git a/tests/cases/fourslash_old/functionIndentation.ts b/tests/cases/fourslash/functionIndentation.ts similarity index 100% rename from tests/cases/fourslash_old/functionIndentation.ts rename to tests/cases/fourslash/functionIndentation.ts diff --git a/tests/cases/fourslash_old/functionRenamingErrorRecovery.ts b/tests/cases/fourslash/functionRenamingErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/functionRenamingErrorRecovery.ts rename to tests/cases/fourslash/functionRenamingErrorRecovery.ts diff --git a/tests/cases/fourslash_old/functionTypeFormatting.ts b/tests/cases/fourslash/functionTypeFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/functionTypeFormatting.ts rename to tests/cases/fourslash/functionTypeFormatting.ts diff --git a/tests/cases/fourslash_old/functionTypes.ts b/tests/cases/fourslash/functionTypes.ts similarity index 100% rename from tests/cases/fourslash_old/functionTypes.ts rename to tests/cases/fourslash/functionTypes.ts diff --git a/tests/cases/fourslash_old/generated/dummy.txt b/tests/cases/fourslash/generated/dummy.txt similarity index 100% rename from tests/cases/fourslash_old/generated/dummy.txt rename to tests/cases/fourslash/generated/dummy.txt diff --git a/tests/cases/fourslash_old/genericArityEnforcementAfterEdit.ts b/tests/cases/fourslash/genericArityEnforcementAfterEdit.ts similarity index 100% rename from tests/cases/fourslash_old/genericArityEnforcementAfterEdit.ts rename to tests/cases/fourslash/genericArityEnforcementAfterEdit.ts diff --git a/tests/cases/fourslash_old/genericAssignmentCompat.ts b/tests/cases/fourslash/genericAssignmentCompat.ts similarity index 82% rename from tests/cases/fourslash_old/genericAssignmentCompat.ts rename to tests/cases/fourslash/genericAssignmentCompat.ts index f80870baf21..c9d547147b1 100644 --- a/tests/cases/fourslash_old/genericAssignmentCompat.ts +++ b/tests/cases/fourslash/genericAssignmentCompat.ts @@ -8,7 +8,7 @@ //// //// declare var v1: Int; //// -//// var /*1*/v2: Int = v1/*2*/; +//// var /*1*/v2/*2*/: Int = v1; verify.errorExistsBetweenMarkers("1", "2"); verify.numberOfErrorsInCurrentFile(1); \ No newline at end of file diff --git a/tests/cases/fourslash_old/genericCloduleCompletionList.ts b/tests/cases/fourslash/genericCloduleCompletionList.ts similarity index 100% rename from tests/cases/fourslash_old/genericCloduleCompletionList.ts rename to tests/cases/fourslash/genericCloduleCompletionList.ts diff --git a/tests/cases/fourslash_old/genericFunctionWithGenericParams1.ts b/tests/cases/fourslash/genericFunctionWithGenericParams1.ts similarity index 100% rename from tests/cases/fourslash_old/genericFunctionWithGenericParams1.ts rename to tests/cases/fourslash/genericFunctionWithGenericParams1.ts diff --git a/tests/cases/fourslash_old/genericInterfacePropertyInference1.ts b/tests/cases/fourslash/genericInterfacePropertyInference1.ts similarity index 100% rename from tests/cases/fourslash_old/genericInterfacePropertyInference1.ts rename to tests/cases/fourslash/genericInterfacePropertyInference1.ts diff --git a/tests/cases/fourslash_old/genericInterfacePropertyInference2.ts b/tests/cases/fourslash/genericInterfacePropertyInference2.ts similarity index 100% rename from tests/cases/fourslash_old/genericInterfacePropertyInference2.ts rename to tests/cases/fourslash/genericInterfacePropertyInference2.ts diff --git a/tests/cases/fourslash_old/genericInterfaceWithInheritanceEdit1.ts b/tests/cases/fourslash/genericInterfaceWithInheritanceEdit1.ts similarity index 100% rename from tests/cases/fourslash_old/genericInterfaceWithInheritanceEdit1.ts rename to tests/cases/fourslash/genericInterfaceWithInheritanceEdit1.ts diff --git a/tests/cases/fourslash_old/genericInterfacesWithConstraints1.ts b/tests/cases/fourslash/genericInterfacesWithConstraints1.ts similarity index 100% rename from tests/cases/fourslash_old/genericInterfacesWithConstraints1.ts rename to tests/cases/fourslash/genericInterfacesWithConstraints1.ts diff --git a/tests/cases/fourslash_old/genericMethodParam.ts b/tests/cases/fourslash/genericMethodParam.ts similarity index 100% rename from tests/cases/fourslash_old/genericMethodParam.ts rename to tests/cases/fourslash/genericMethodParam.ts diff --git a/tests/cases/fourslash_old/genericObjectBaseType.ts b/tests/cases/fourslash/genericObjectBaseType.ts similarity index 100% rename from tests/cases/fourslash_old/genericObjectBaseType.ts rename to tests/cases/fourslash/genericObjectBaseType.ts diff --git a/tests/cases/fourslash_old/genericParameterHelp.ts b/tests/cases/fourslash/genericParameterHelp.ts similarity index 100% rename from tests/cases/fourslash_old/genericParameterHelp.ts rename to tests/cases/fourslash/genericParameterHelp.ts diff --git a/tests/cases/fourslash_old/genericRespecialization1.ts b/tests/cases/fourslash/genericRespecialization1.ts similarity index 100% rename from tests/cases/fourslash_old/genericRespecialization1.ts rename to tests/cases/fourslash/genericRespecialization1.ts diff --git a/tests/cases/fourslash_old/genericSignaturesAreProperlyCleaned.ts b/tests/cases/fourslash/genericSignaturesAreProperlyCleaned.ts similarity index 100% rename from tests/cases/fourslash_old/genericSignaturesAreProperlyCleaned.ts rename to tests/cases/fourslash/genericSignaturesAreProperlyCleaned.ts diff --git a/tests/cases/fourslash_old/genericTypeWithMultipleBases1MultiFile.ts b/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts similarity index 89% rename from tests/cases/fourslash_old/genericTypeWithMultipleBases1MultiFile.ts rename to tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts index e28e1aa0fc7..a2b32a6f383 100644 --- a/tests/cases/fourslash_old/genericTypeWithMultipleBases1MultiFile.ts +++ b/tests/cases/fourslash/genericTypeWithMultipleBases1MultiFile.ts @@ -4,20 +4,20 @@ ////interface iBaseScope { //// watch: () => void; ////} -// @Filename: genericTypeWithMultipleBases_1.ts +// @Filename: genericTypeWithMultipleBases_1.ts ////interface iMover { //// moveUp: () => void; ////} -// @Filename: genericTypeWithMultipleBases_2.ts +// @Filename: genericTypeWithMultipleBases_2.ts ////interface iScope extends iBaseScope, iMover { //// family: TModel; ////} -// @Filename: genericTypeWithMultipleBases_3.ts +// @Filename: genericTypeWithMultipleBases_3.ts ////var x: iScope; -// @Filename: genericTypeWithMultipleBases_4.ts +// @Filename: genericTypeWithMultipleBases_4.ts ////x./**/ goTo.marker(); verify.completionListContains('watch', '() => void'); verify.completionListContains('moveUp', '() => void'); -verify.completionListContains('family', 'TModel'); \ No newline at end of file +verify.completionListContains('family', 'number'); \ No newline at end of file diff --git a/tests/cases/fourslash_old/genericsFormatting.ts b/tests/cases/fourslash/genericsFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/genericsFormatting.ts rename to tests/cases/fourslash/genericsFormatting.ts diff --git a/tests/cases/fourslash_old/getCompletionEntryDetails.ts b/tests/cases/fourslash/getCompletionEntryDetails.ts similarity index 100% rename from tests/cases/fourslash_old/getCompletionEntryDetails.ts rename to tests/cases/fourslash/getCompletionEntryDetails.ts diff --git a/tests/cases/fourslash_old/getCompletionEntryDetails2.ts b/tests/cases/fourslash/getCompletionEntryDetails2.ts similarity index 100% rename from tests/cases/fourslash_old/getCompletionEntryDetails2.ts rename to tests/cases/fourslash/getCompletionEntryDetails2.ts diff --git a/tests/cases/fourslash_old/getImplementors1.ts b/tests/cases/fourslash/getImplementors1.ts similarity index 100% rename from tests/cases/fourslash_old/getImplementors1.ts rename to tests/cases/fourslash/getImplementors1.ts diff --git a/tests/cases/fourslash_old/getImplementorsForFunction.ts b/tests/cases/fourslash/getImplementorsForFunction.ts similarity index 100% rename from tests/cases/fourslash_old/getImplementorsForFunction.ts rename to tests/cases/fourslash/getImplementorsForFunction.ts diff --git a/tests/cases/fourslash_old/getMatchingBracesNegativeCases.ts b/tests/cases/fourslash/getMatchingBracesNegativeCases.ts similarity index 100% rename from tests/cases/fourslash_old/getMatchingBracesNegativeCases.ts rename to tests/cases/fourslash/getMatchingBracesNegativeCases.ts diff --git a/tests/cases/fourslash_old/getNameOrDottedNameSpan.ts b/tests/cases/fourslash/getNameOrDottedNameSpan.ts similarity index 100% rename from tests/cases/fourslash_old/getNameOrDottedNameSpan.ts rename to tests/cases/fourslash/getNameOrDottedNameSpan.ts diff --git a/tests/cases/fourslash_old/getOccurrencesOfAny.ts b/tests/cases/fourslash/getOccurrencesOfAny.ts similarity index 100% rename from tests/cases/fourslash_old/getOccurrencesOfAny.ts rename to tests/cases/fourslash/getOccurrencesOfAny.ts diff --git a/tests/cases/fourslash_old/getTypeAtModuleExtends.ts b/tests/cases/fourslash/getTypeAtModuleExtends.ts similarity index 100% rename from tests/cases/fourslash_old/getTypeAtModuleExtends.ts rename to tests/cases/fourslash/getTypeAtModuleExtends.ts diff --git a/tests/cases/fourslash_old/globalCompletionListInsideObjectLiterals.ts b/tests/cases/fourslash/globalCompletionListInsideObjectLiterals.ts similarity index 100% rename from tests/cases/fourslash_old/globalCompletionListInsideObjectLiterals.ts rename to tests/cases/fourslash/globalCompletionListInsideObjectLiterals.ts diff --git a/tests/cases/fourslash_old/goToDefinitionPrimitives.ts b/tests/cases/fourslash/goToDefinitionPrimitives.ts similarity index 100% rename from tests/cases/fourslash_old/goToDefinitionPrimitives.ts rename to tests/cases/fourslash/goToDefinitionPrimitives.ts diff --git a/tests/cases/fourslash_old/goToDefinitionSourceUnit.ts b/tests/cases/fourslash/goToDefinitionSourceUnit.ts similarity index 100% rename from tests/cases/fourslash_old/goToDefinitionSourceUnit.ts rename to tests/cases/fourslash/goToDefinitionSourceUnit.ts diff --git a/tests/cases/fourslash_old/goToDefinitionUndefinedSymbols.ts b/tests/cases/fourslash/goToDefinitionUndefinedSymbols.ts similarity index 100% rename from tests/cases/fourslash_old/goToDefinitionUndefinedSymbols.ts rename to tests/cases/fourslash/goToDefinitionUndefinedSymbols.ts diff --git a/tests/cases/fourslash_old/identationAfterInterfaceCall.ts b/tests/cases/fourslash/identationAfterInterfaceCall.ts similarity index 100% rename from tests/cases/fourslash_old/identationAfterInterfaceCall.ts rename to tests/cases/fourslash/identationAfterInterfaceCall.ts diff --git a/tests/cases/fourslash_old/identifierErrorRecovery.ts b/tests/cases/fourslash/identifierErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/identifierErrorRecovery.ts rename to tests/cases/fourslash/identifierErrorRecovery.ts diff --git a/tests/cases/fourslash_old/importDeclPaste0.ts b/tests/cases/fourslash/importDeclPaste0.ts similarity index 100% rename from tests/cases/fourslash_old/importDeclPaste0.ts rename to tests/cases/fourslash/importDeclPaste0.ts diff --git a/tests/cases/fourslash_old/importValueUsedAsType.ts b/tests/cases/fourslash/importValueUsedAsType.ts similarity index 100% rename from tests/cases/fourslash_old/importValueUsedAsType.ts rename to tests/cases/fourslash/importValueUsedAsType.ts diff --git a/tests/cases/fourslash_old/incompatibleOverride.ts b/tests/cases/fourslash/incompatibleOverride.ts similarity index 100% rename from tests/cases/fourslash_old/incompatibleOverride.ts rename to tests/cases/fourslash/incompatibleOverride.ts diff --git a/tests/cases/fourslash_old/incrementalResolveConstructorDeclaration.ts b/tests/cases/fourslash/incrementalResolveConstructorDeclaration.ts similarity index 100% rename from tests/cases/fourslash_old/incrementalResolveConstructorDeclaration.ts rename to tests/cases/fourslash/incrementalResolveConstructorDeclaration.ts diff --git a/tests/cases/fourslash_old/incrementalResolveFunctionPropertyAssignment.ts b/tests/cases/fourslash/incrementalResolveFunctionPropertyAssignment.ts similarity index 100% rename from tests/cases/fourslash_old/incrementalResolveFunctionPropertyAssignment.ts rename to tests/cases/fourslash/incrementalResolveFunctionPropertyAssignment.ts diff --git a/tests/cases/fourslash_old/incrementalUpdateToClassImplementingGenericClass.ts b/tests/cases/fourslash/incrementalUpdateToClassImplementingGenericClass.ts similarity index 100% rename from tests/cases/fourslash_old/incrementalUpdateToClassImplementingGenericClass.ts rename to tests/cases/fourslash/incrementalUpdateToClassImplementingGenericClass.ts diff --git a/tests/cases/fourslash_old/indentAfterFunctionClosingBraces.ts b/tests/cases/fourslash/indentAfterFunctionClosingBraces.ts similarity index 100% rename from tests/cases/fourslash_old/indentAfterFunctionClosingBraces.ts rename to tests/cases/fourslash/indentAfterFunctionClosingBraces.ts diff --git a/tests/cases/fourslash_old/indentAfterImport.ts b/tests/cases/fourslash/indentAfterImport.ts similarity index 100% rename from tests/cases/fourslash_old/indentAfterImport.ts rename to tests/cases/fourslash/indentAfterImport.ts diff --git a/tests/cases/fourslash_old/indentation.ts b/tests/cases/fourslash/indentation.ts similarity index 100% rename from tests/cases/fourslash_old/indentation.ts rename to tests/cases/fourslash/indentation.ts diff --git a/tests/cases/fourslash_old/indentationAfterModuleImport.ts b/tests/cases/fourslash/indentationAfterModuleImport.ts similarity index 100% rename from tests/cases/fourslash_old/indentationAfterModuleImport.ts rename to tests/cases/fourslash/indentationAfterModuleImport.ts diff --git a/tests/cases/fourslash_old/indentionsOfCommentBlockAfterFormatting.ts b/tests/cases/fourslash/indentionsOfCommentBlockAfterFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/indentionsOfCommentBlockAfterFormatting.ts rename to tests/cases/fourslash/indentionsOfCommentBlockAfterFormatting.ts diff --git a/tests/cases/fourslash_old/indexSignatureWithoutAnnotation.ts b/tests/cases/fourslash/indexSignatureWithoutAnnotation.ts similarity index 100% rename from tests/cases/fourslash_old/indexSignatureWithoutAnnotation.ts rename to tests/cases/fourslash/indexSignatureWithoutAnnotation.ts diff --git a/tests/cases/fourslash_old/inheritedModuleMembersForClodule2.ts b/tests/cases/fourslash/inheritedModuleMembersForClodule2.ts similarity index 100% rename from tests/cases/fourslash_old/inheritedModuleMembersForClodule2.ts rename to tests/cases/fourslash/inheritedModuleMembersForClodule2.ts diff --git a/tests/cases/fourslash_old/insertArgumentBeforeOverloadedConstructor.ts b/tests/cases/fourslash/insertArgumentBeforeOverloadedConstructor.ts similarity index 100% rename from tests/cases/fourslash_old/insertArgumentBeforeOverloadedConstructor.ts rename to tests/cases/fourslash/insertArgumentBeforeOverloadedConstructor.ts diff --git a/tests/cases/fourslash_old/insertInterfaceAndCheckTypeLiteralField.ts b/tests/cases/fourslash/insertInterfaceAndCheckTypeLiteralField.ts similarity index 100% rename from tests/cases/fourslash_old/insertInterfaceAndCheckTypeLiteralField.ts rename to tests/cases/fourslash/insertInterfaceAndCheckTypeLiteralField.ts diff --git a/tests/cases/fourslash_old/insertMethodCallAboveOthers.ts b/tests/cases/fourslash/insertMethodCallAboveOthers.ts similarity index 100% rename from tests/cases/fourslash_old/insertMethodCallAboveOthers.ts rename to tests/cases/fourslash/insertMethodCallAboveOthers.ts diff --git a/tests/cases/fourslash_old/insertPublicBeforeSetter.ts b/tests/cases/fourslash/insertPublicBeforeSetter.ts similarity index 100% rename from tests/cases/fourslash_old/insertPublicBeforeSetter.ts rename to tests/cases/fourslash/insertPublicBeforeSetter.ts diff --git a/tests/cases/fourslash_old/insertReturnStatementInDuplicateIdentifierFunction.ts b/tests/cases/fourslash/insertReturnStatementInDuplicateIdentifierFunction.ts similarity index 100% rename from tests/cases/fourslash_old/insertReturnStatementInDuplicateIdentifierFunction.ts rename to tests/cases/fourslash/insertReturnStatementInDuplicateIdentifierFunction.ts diff --git a/tests/cases/fourslash_old/insertSecondTryCatchBlock.ts b/tests/cases/fourslash/insertSecondTryCatchBlock.ts similarity index 100% rename from tests/cases/fourslash_old/insertSecondTryCatchBlock.ts rename to tests/cases/fourslash/insertSecondTryCatchBlock.ts diff --git a/tests/cases/fourslash_old/insertVarAfterEmptyTypeParamList.ts b/tests/cases/fourslash/insertVarAfterEmptyTypeParamList.ts similarity index 100% rename from tests/cases/fourslash_old/insertVarAfterEmptyTypeParamList.ts rename to tests/cases/fourslash/insertVarAfterEmptyTypeParamList.ts diff --git a/tests/cases/fourslash_old/interfaceExtendsPrimitive.ts b/tests/cases/fourslash/interfaceExtendsPrimitive.ts similarity index 100% rename from tests/cases/fourslash_old/interfaceExtendsPrimitive.ts rename to tests/cases/fourslash/interfaceExtendsPrimitive.ts diff --git a/tests/cases/fourslash_old/interfaceIndent.ts b/tests/cases/fourslash/interfaceIndent.ts similarity index 100% rename from tests/cases/fourslash_old/interfaceIndent.ts rename to tests/cases/fourslash/interfaceIndent.ts diff --git a/tests/cases/fourslash_old/interfaceRecursiveInheritanceErrors0.ts b/tests/cases/fourslash/interfaceRecursiveInheritanceErrors0.ts similarity index 100% rename from tests/cases/fourslash_old/interfaceRecursiveInheritanceErrors0.ts rename to tests/cases/fourslash/interfaceRecursiveInheritanceErrors0.ts diff --git a/tests/cases/fourslash_old/interfaceRecursiveInheritanceErrors1.ts b/tests/cases/fourslash/interfaceRecursiveInheritanceErrors1.ts similarity index 100% rename from tests/cases/fourslash_old/interfaceRecursiveInheritanceErrors1.ts rename to tests/cases/fourslash/interfaceRecursiveInheritanceErrors1.ts diff --git a/tests/cases/fourslash_old/invalidRestArgError.ts b/tests/cases/fourslash/invalidRestArgError.ts similarity index 100% rename from tests/cases/fourslash_old/invalidRestArgError.ts rename to tests/cases/fourslash/invalidRestArgError.ts diff --git a/tests/cases/fourslash_old/invertedCloduleAfterQuickInfo.ts b/tests/cases/fourslash/invertedCloduleAfterQuickInfo.ts similarity index 100% rename from tests/cases/fourslash_old/invertedCloduleAfterQuickInfo.ts rename to tests/cases/fourslash/invertedCloduleAfterQuickInfo.ts diff --git a/tests/cases/fourslash_old/invertedFunduleAfterQuickInfo.ts b/tests/cases/fourslash/invertedFunduleAfterQuickInfo.ts similarity index 100% rename from tests/cases/fourslash_old/invertedFunduleAfterQuickInfo.ts rename to tests/cases/fourslash/invertedFunduleAfterQuickInfo.ts diff --git a/tests/cases/fourslash_old/lambdaThisMembers.ts b/tests/cases/fourslash/lambdaThisMembers.ts similarity index 100% rename from tests/cases/fourslash_old/lambdaThisMembers.ts rename to tests/cases/fourslash/lambdaThisMembers.ts diff --git a/tests/cases/fourslash_old/malformedObjectLiteral.ts b/tests/cases/fourslash/malformedObjectLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/malformedObjectLiteral.ts rename to tests/cases/fourslash/malformedObjectLiteral.ts diff --git a/tests/cases/fourslash_old/memberConstructorEdits.ts b/tests/cases/fourslash/memberConstructorEdits.ts similarity index 100% rename from tests/cases/fourslash_old/memberConstructorEdits.ts rename to tests/cases/fourslash/memberConstructorEdits.ts diff --git a/tests/cases/fourslash_old/memberListAfterSingleDot.ts b/tests/cases/fourslash/memberListAfterSingleDot.ts similarity index 100% rename from tests/cases/fourslash_old/memberListAfterSingleDot.ts rename to tests/cases/fourslash/memberListAfterSingleDot.ts diff --git a/tests/cases/fourslash_old/memberListErrorRecovery.ts b/tests/cases/fourslash/memberListErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/memberListErrorRecovery.ts rename to tests/cases/fourslash/memberListErrorRecovery.ts diff --git a/tests/cases/fourslash_old/memberListInFunctionCall.ts b/tests/cases/fourslash/memberListInFunctionCall.ts similarity index 100% rename from tests/cases/fourslash_old/memberListInFunctionCall.ts rename to tests/cases/fourslash/memberListInFunctionCall.ts diff --git a/tests/cases/fourslash_old/memberListOfEnumInModule.ts b/tests/cases/fourslash/memberListOfEnumInModule.ts similarity index 100% rename from tests/cases/fourslash_old/memberListOfEnumInModule.ts rename to tests/cases/fourslash/memberListOfEnumInModule.ts diff --git a/tests/cases/fourslash_old/memberListOfModuleAfterInvalidCharater.ts b/tests/cases/fourslash/memberListOfModuleAfterInvalidCharater.ts similarity index 100% rename from tests/cases/fourslash_old/memberListOfModuleAfterInvalidCharater.ts rename to tests/cases/fourslash/memberListOfModuleAfterInvalidCharater.ts diff --git a/tests/cases/fourslash_old/memberListOnConstructorType.ts b/tests/cases/fourslash/memberListOnConstructorType.ts similarity index 100% rename from tests/cases/fourslash_old/memberListOnConstructorType.ts rename to tests/cases/fourslash/memberListOnConstructorType.ts diff --git a/tests/cases/fourslash_old/memberOverloadEdits.ts b/tests/cases/fourslash/memberOverloadEdits.ts similarity index 100% rename from tests/cases/fourslash_old/memberOverloadEdits.ts rename to tests/cases/fourslash/memberOverloadEdits.ts diff --git a/tests/cases/fourslash_old/memberlistOnDDot.ts b/tests/cases/fourslash/memberlistOnDDot.ts similarity index 100% rename from tests/cases/fourslash_old/memberlistOnDDot.ts rename to tests/cases/fourslash/memberlistOnDDot.ts diff --git a/tests/cases/fourslash_old/mergedDeclarations1.ts b/tests/cases/fourslash/mergedDeclarations1.ts similarity index 100% rename from tests/cases/fourslash_old/mergedDeclarations1.ts rename to tests/cases/fourslash/mergedDeclarations1.ts diff --git a/tests/cases/fourslash_old/mergedDeclarations2.ts b/tests/cases/fourslash/mergedDeclarations2.ts similarity index 100% rename from tests/cases/fourslash_old/mergedDeclarations2.ts rename to tests/cases/fourslash/mergedDeclarations2.ts diff --git a/tests/cases/fourslash_old/mispeltVariableForInLoopErrorRecovery.ts b/tests/cases/fourslash/mispeltVariableForInLoopErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/mispeltVariableForInLoopErrorRecovery.ts rename to tests/cases/fourslash/mispeltVariableForInLoopErrorRecovery.ts diff --git a/tests/cases/fourslash_old/moduleEnumModule.ts b/tests/cases/fourslash/moduleEnumModule.ts similarity index 100% rename from tests/cases/fourslash_old/moduleEnumModule.ts rename to tests/cases/fourslash/moduleEnumModule.ts diff --git a/tests/cases/fourslash_old/moduleIndent.ts b/tests/cases/fourslash/moduleIndent.ts similarity index 100% rename from tests/cases/fourslash_old/moduleIndent.ts rename to tests/cases/fourslash/moduleIndent.ts diff --git a/tests/cases/fourslash_old/moduleMembersOfGenericType.ts b/tests/cases/fourslash/moduleMembersOfGenericType.ts similarity index 100% rename from tests/cases/fourslash_old/moduleMembersOfGenericType.ts rename to tests/cases/fourslash/moduleMembersOfGenericType.ts diff --git a/tests/cases/fourslash_old/moduleRenamingErrorRecovery.ts b/tests/cases/fourslash/moduleRenamingErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/moduleRenamingErrorRecovery.ts rename to tests/cases/fourslash/moduleRenamingErrorRecovery.ts diff --git a/tests/cases/fourslash_old/multiModuleClodule1.ts b/tests/cases/fourslash/multiModuleClodule1.ts similarity index 100% rename from tests/cases/fourslash_old/multiModuleClodule1.ts rename to tests/cases/fourslash/multiModuleClodule1.ts diff --git a/tests/cases/fourslash_old/multilineCommentBeforeOpenBrace.ts b/tests/cases/fourslash/multilineCommentBeforeOpenBrace.ts similarity index 100% rename from tests/cases/fourslash_old/multilineCommentBeforeOpenBrace.ts rename to tests/cases/fourslash/multilineCommentBeforeOpenBrace.ts diff --git a/tests/cases/fourslash_old/multipleExportAssignmentsErrorList0.ts b/tests/cases/fourslash/multipleExportAssignmentsErrorList0.ts similarity index 100% rename from tests/cases/fourslash_old/multipleExportAssignmentsErrorList0.ts rename to tests/cases/fourslash/multipleExportAssignmentsErrorList0.ts diff --git a/tests/cases/fourslash_old/navbar_contains-no-duplicates.ts b/tests/cases/fourslash/navbar_contains-no-duplicates.ts similarity index 100% rename from tests/cases/fourslash_old/navbar_contains-no-duplicates.ts rename to tests/cases/fourslash/navbar_contains-no-duplicates.ts diff --git a/tests/cases/fourslash_old/noCompletionListOnCommentsInsideObjectLiterals.ts b/tests/cases/fourslash/noCompletionListOnCommentsInsideObjectLiterals.ts similarity index 100% rename from tests/cases/fourslash_old/noCompletionListOnCommentsInsideObjectLiterals.ts rename to tests/cases/fourslash/noCompletionListOnCommentsInsideObjectLiterals.ts diff --git a/tests/cases/fourslash_old/noQuickInfoInWhitespace.ts b/tests/cases/fourslash/noQuickInfoInWhitespace.ts similarity index 100% rename from tests/cases/fourslash_old/noQuickInfoInWhitespace.ts rename to tests/cases/fourslash/noQuickInfoInWhitespace.ts diff --git a/tests/cases/fourslash_old/noSmartIndentInsideMultilineString.ts b/tests/cases/fourslash/noSmartIndentInsideMultilineString.ts similarity index 100% rename from tests/cases/fourslash_old/noSmartIndentInsideMultilineString.ts rename to tests/cases/fourslash/noSmartIndentInsideMultilineString.ts diff --git a/tests/cases/fourslash_old/nonExistingImport.ts b/tests/cases/fourslash/nonExistingImport.ts similarity index 100% rename from tests/cases/fourslash_old/nonExistingImport.ts rename to tests/cases/fourslash/nonExistingImport.ts diff --git a/tests/cases/fourslash_old/numberAssignement0.ts b/tests/cases/fourslash/numberAssignement0.ts similarity index 100% rename from tests/cases/fourslash_old/numberAssignement0.ts rename to tests/cases/fourslash/numberAssignement0.ts diff --git a/tests/cases/fourslash_old/outliningForNonCompleteInterfaceDeclaration.ts b/tests/cases/fourslash/outliningForNonCompleteInterfaceDeclaration.ts similarity index 100% rename from tests/cases/fourslash_old/outliningForNonCompleteInterfaceDeclaration.ts rename to tests/cases/fourslash/outliningForNonCompleteInterfaceDeclaration.ts diff --git a/tests/cases/fourslash_old/overloadObjectLiteralCrash.ts b/tests/cases/fourslash/overloadObjectLiteralCrash.ts similarity index 100% rename from tests/cases/fourslash_old/overloadObjectLiteralCrash.ts rename to tests/cases/fourslash/overloadObjectLiteralCrash.ts diff --git a/tests/cases/fourslash_old/parameterlessSetter.ts b/tests/cases/fourslash/parameterlessSetter.ts similarity index 100% rename from tests/cases/fourslash_old/parameterlessSetter.ts rename to tests/cases/fourslash/parameterlessSetter.ts diff --git a/tests/cases/fourslash_old/parenthesisFatArrows.ts b/tests/cases/fourslash/parenthesisFatArrows.ts similarity index 100% rename from tests/cases/fourslash_old/parenthesisFatArrows.ts rename to tests/cases/fourslash/parenthesisFatArrows.ts diff --git a/tests/cases/fourslash_old/paste.ts b/tests/cases/fourslash/paste.ts similarity index 100% rename from tests/cases/fourslash_old/paste.ts rename to tests/cases/fourslash/paste.ts diff --git a/tests/cases/fourslash_old/pasteLambdaOverModule.ts b/tests/cases/fourslash/pasteLambdaOverModule.ts similarity index 100% rename from tests/cases/fourslash_old/pasteLambdaOverModule.ts rename to tests/cases/fourslash/pasteLambdaOverModule.ts diff --git a/tests/cases/fourslash_old/propertyDuplicateIdentifierError.ts b/tests/cases/fourslash/propertyDuplicateIdentifierError.ts similarity index 100% rename from tests/cases/fourslash_old/propertyDuplicateIdentifierError.ts rename to tests/cases/fourslash/propertyDuplicateIdentifierError.ts diff --git a/tests/cases/fourslash_old/publicBreak.ts b/tests/cases/fourslash/publicBreak.ts similarity index 100% rename from tests/cases/fourslash_old/publicBreak.ts rename to tests/cases/fourslash/publicBreak.ts diff --git a/tests/cases/fourslash_old/pullFullDiffTypeParameterExtends0.ts b/tests/cases/fourslash/pullFullDiffTypeParameterExtends0.ts similarity index 100% rename from tests/cases/fourslash_old/pullFullDiffTypeParameterExtends0.ts rename to tests/cases/fourslash/pullFullDiffTypeParameterExtends0.ts diff --git a/tests/cases/fourslash_old/quickInfoFromEmptyBlockComment.ts b/tests/cases/fourslash/quickInfoFromEmptyBlockComment.ts similarity index 100% rename from tests/cases/fourslash_old/quickInfoFromEmptyBlockComment.ts rename to tests/cases/fourslash/quickInfoFromEmptyBlockComment.ts diff --git a/tests/cases/fourslash_old/quickInfoInvalidLocations.ts b/tests/cases/fourslash/quickInfoInvalidLocations.ts similarity index 100% rename from tests/cases/fourslash_old/quickInfoInvalidLocations.ts rename to tests/cases/fourslash/quickInfoInvalidLocations.ts diff --git a/tests/cases/fourslash_old/quickInfoOfLablledForStatementIterator.ts b/tests/cases/fourslash/quickInfoOfLablledForStatementIterator.ts similarity index 100% rename from tests/cases/fourslash_old/quickInfoOfLablledForStatementIterator.ts rename to tests/cases/fourslash/quickInfoOfLablledForStatementIterator.ts diff --git a/tests/cases/fourslash_old/quickInfoOnCircularTypes.ts b/tests/cases/fourslash/quickInfoOnCircularTypes.ts similarity index 100% rename from tests/cases/fourslash_old/quickInfoOnCircularTypes.ts rename to tests/cases/fourslash/quickInfoOnCircularTypes.ts diff --git a/tests/cases/fourslash_old/quickInfoOnMergedInterfaces.ts b/tests/cases/fourslash/quickInfoOnMergedInterfaces.ts similarity index 100% rename from tests/cases/fourslash_old/quickInfoOnMergedInterfaces.ts rename to tests/cases/fourslash/quickInfoOnMergedInterfaces.ts diff --git a/tests/cases/fourslash_old/quickInfoOnUnResolvedBaseConstructorSignature.ts b/tests/cases/fourslash/quickInfoOnUnResolvedBaseConstructorSignature.ts similarity index 100% rename from tests/cases/fourslash_old/quickInfoOnUnResolvedBaseConstructorSignature.ts rename to tests/cases/fourslash/quickInfoOnUnResolvedBaseConstructorSignature.ts diff --git a/tests/cases/fourslash_old/quickinfoIsConsistent.ts b/tests/cases/fourslash/quickinfoIsConsistent.ts similarity index 100% rename from tests/cases/fourslash_old/quickinfoIsConsistent.ts rename to tests/cases/fourslash/quickinfoIsConsistent.ts diff --git a/tests/cases/fourslash_old/recursiveGenerics2.ts b/tests/cases/fourslash/recursiveGenerics2.ts similarity index 100% rename from tests/cases/fourslash_old/recursiveGenerics2.ts rename to tests/cases/fourslash/recursiveGenerics2.ts diff --git a/tests/cases/fourslash_old/recursiveInternalModuleImport.ts b/tests/cases/fourslash/recursiveInternalModuleImport.ts similarity index 100% rename from tests/cases/fourslash_old/recursiveInternalModuleImport.ts rename to tests/cases/fourslash/recursiveInternalModuleImport.ts diff --git a/tests/cases/fourslash_old/recursiveWrappedTypeParameters1.ts b/tests/cases/fourslash/recursiveWrappedTypeParameters1.ts similarity index 100% rename from tests/cases/fourslash_old/recursiveWrappedTypeParameters1.ts rename to tests/cases/fourslash/recursiveWrappedTypeParameters1.ts diff --git a/tests/cases/fourslash_old/referencesForNoContext.ts b/tests/cases/fourslash/referencesForNoContext.ts similarity index 100% rename from tests/cases/fourslash_old/referencesForNoContext.ts rename to tests/cases/fourslash/referencesForNoContext.ts diff --git a/tests/cases/fourslash_old/referencesInComment.ts b/tests/cases/fourslash/referencesInComment.ts similarity index 100% rename from tests/cases/fourslash_old/referencesInComment.ts rename to tests/cases/fourslash/referencesInComment.ts diff --git a/tests/cases/fourslash_old/regexDetection.ts b/tests/cases/fourslash/regexDetection.ts similarity index 100% rename from tests/cases/fourslash_old/regexDetection.ts rename to tests/cases/fourslash/regexDetection.ts diff --git a/tests/cases/fourslash_old/regexErrorRecovery.ts b/tests/cases/fourslash/regexErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/regexErrorRecovery.ts rename to tests/cases/fourslash/regexErrorRecovery.ts diff --git a/tests/cases/fourslash_old/removeDeclareFunctionExports.ts b/tests/cases/fourslash/removeDeclareFunctionExports.ts similarity index 100% rename from tests/cases/fourslash_old/removeDeclareFunctionExports.ts rename to tests/cases/fourslash/removeDeclareFunctionExports.ts diff --git a/tests/cases/fourslash_old/removeDeclareInModule.ts b/tests/cases/fourslash/removeDeclareInModule.ts similarity index 100% rename from tests/cases/fourslash_old/removeDeclareInModule.ts rename to tests/cases/fourslash/removeDeclareInModule.ts diff --git a/tests/cases/fourslash_old/removeDeclareKeyword.ts b/tests/cases/fourslash/removeDeclareKeyword.ts similarity index 100% rename from tests/cases/fourslash_old/removeDeclareKeyword.ts rename to tests/cases/fourslash/removeDeclareKeyword.ts diff --git a/tests/cases/fourslash_old/removeDeclareParamTypeAnnotation.ts b/tests/cases/fourslash/removeDeclareParamTypeAnnotation.ts similarity index 100% rename from tests/cases/fourslash_old/removeDeclareParamTypeAnnotation.ts rename to tests/cases/fourslash/removeDeclareParamTypeAnnotation.ts diff --git a/tests/cases/fourslash_old/removeDuplicateIdentifier.ts b/tests/cases/fourslash/removeDuplicateIdentifier.ts similarity index 100% rename from tests/cases/fourslash_old/removeDuplicateIdentifier.ts rename to tests/cases/fourslash/removeDuplicateIdentifier.ts diff --git a/tests/cases/fourslash_old/removeExportFromInterfaceError0.ts b/tests/cases/fourslash/removeExportFromInterfaceError0.ts similarity index 100% rename from tests/cases/fourslash_old/removeExportFromInterfaceError0.ts rename to tests/cases/fourslash/removeExportFromInterfaceError0.ts diff --git a/tests/cases/fourslash_old/removeExportFromInterfaceError1.ts b/tests/cases/fourslash/removeExportFromInterfaceError1.ts similarity index 100% rename from tests/cases/fourslash_old/removeExportFromInterfaceError1.ts rename to tests/cases/fourslash/removeExportFromInterfaceError1.ts diff --git a/tests/cases/fourslash_old/removeExportedClassFromReopenedModule.ts b/tests/cases/fourslash/removeExportedClassFromReopenedModule.ts similarity index 100% rename from tests/cases/fourslash_old/removeExportedClassFromReopenedModule.ts rename to tests/cases/fourslash/removeExportedClassFromReopenedModule.ts diff --git a/tests/cases/fourslash_old/removeInterfaceExtendsClause.ts b/tests/cases/fourslash/removeInterfaceExtendsClause.ts similarity index 100% rename from tests/cases/fourslash_old/removeInterfaceExtendsClause.ts rename to tests/cases/fourslash/removeInterfaceExtendsClause.ts diff --git a/tests/cases/fourslash_old/removeInterfaceUsedAsGenericTypeArgument.ts b/tests/cases/fourslash/removeInterfaceUsedAsGenericTypeArgument.ts similarity index 100% rename from tests/cases/fourslash_old/removeInterfaceUsedAsGenericTypeArgument.ts rename to tests/cases/fourslash/removeInterfaceUsedAsGenericTypeArgument.ts diff --git a/tests/cases/fourslash_old/removeParameterBetweenCommentAndParameter.ts b/tests/cases/fourslash/removeParameterBetweenCommentAndParameter.ts similarity index 100% rename from tests/cases/fourslash_old/removeParameterBetweenCommentAndParameter.ts rename to tests/cases/fourslash/removeParameterBetweenCommentAndParameter.ts diff --git a/tests/cases/fourslash_old/removeVarFromModuleWithReopenedEnums.ts b/tests/cases/fourslash/removeVarFromModuleWithReopenedEnums.ts similarity index 100% rename from tests/cases/fourslash_old/removeVarFromModuleWithReopenedEnums.ts rename to tests/cases/fourslash/removeVarFromModuleWithReopenedEnums.ts diff --git a/tests/cases/fourslash_old/renameModuleToVar.ts b/tests/cases/fourslash/renameModuleToVar.ts similarity index 100% rename from tests/cases/fourslash_old/renameModuleToVar.ts rename to tests/cases/fourslash/renameModuleToVar.ts diff --git a/tests/cases/fourslash_old/restParametersTypeValidation1.ts b/tests/cases/fourslash/restParametersTypeValidation1.ts similarity index 100% rename from tests/cases/fourslash_old/restParametersTypeValidation1.ts rename to tests/cases/fourslash/restParametersTypeValidation1.ts diff --git a/tests/cases/fourslash_old/returnRecursiveType.ts b/tests/cases/fourslash/returnRecursiveType.ts similarity index 100% rename from tests/cases/fourslash_old/returnRecursiveType.ts rename to tests/cases/fourslash/returnRecursiveType.ts diff --git a/tests/cases/fourslash_old/scriptLexicalStructureEmptyConstructors.ts b/tests/cases/fourslash/scriptLexicalStructureEmptyConstructors.ts similarity index 100% rename from tests/cases/fourslash_old/scriptLexicalStructureEmptyConstructors.ts rename to tests/cases/fourslash/scriptLexicalStructureEmptyConstructors.ts diff --git a/tests/cases/fourslash_old/scriptLexicalStructureItems.ts b/tests/cases/fourslash/scriptLexicalStructureItems.ts similarity index 100% rename from tests/cases/fourslash_old/scriptLexicalStructureItems.ts rename to tests/cases/fourslash/scriptLexicalStructureItems.ts diff --git a/tests/cases/fourslash_old/scriptLexicalStructureItemsExternalModules.ts b/tests/cases/fourslash/scriptLexicalStructureItemsExternalModules.ts similarity index 100% rename from tests/cases/fourslash_old/scriptLexicalStructureItemsExternalModules.ts rename to tests/cases/fourslash/scriptLexicalStructureItemsExternalModules.ts diff --git a/tests/cases/fourslash_old/scriptLexicalStructurePropertiesDefinedInConstructors.ts b/tests/cases/fourslash/scriptLexicalStructurePropertiesDefinedInConstructors.ts similarity index 100% rename from tests/cases/fourslash_old/scriptLexicalStructurePropertiesDefinedInConstructors.ts rename to tests/cases/fourslash/scriptLexicalStructurePropertiesDefinedInConstructors.ts diff --git a/tests/cases/fourslash_old/selfReferencedExternalModule2.ts b/tests/cases/fourslash/selfReferencedExternalModule2.ts similarity index 100% rename from tests/cases/fourslash_old/selfReferencedExternalModule2.ts rename to tests/cases/fourslash/selfReferencedExternalModule2.ts diff --git a/tests/cases/fourslash_old/semicolonFormatting.ts b/tests/cases/fourslash/semicolonFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/semicolonFormatting.ts rename to tests/cases/fourslash/semicolonFormatting.ts diff --git a/tests/cases/fourslash_old/semicolonFormattingAfterArrayLiteral.ts b/tests/cases/fourslash/semicolonFormattingAfterArrayLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/semicolonFormattingAfterArrayLiteral.ts rename to tests/cases/fourslash/semicolonFormattingAfterArrayLiteral.ts diff --git a/tests/cases/fourslash_old/semicolonFormattingInsideAComment.ts b/tests/cases/fourslash/semicolonFormattingInsideAComment.ts similarity index 100% rename from tests/cases/fourslash_old/semicolonFormattingInsideAComment.ts rename to tests/cases/fourslash/semicolonFormattingInsideAComment.ts diff --git a/tests/cases/fourslash_old/semicolonFormattingInsideAStringLiteral.ts b/tests/cases/fourslash/semicolonFormattingInsideAStringLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/semicolonFormattingInsideAStringLiteral.ts rename to tests/cases/fourslash/semicolonFormattingInsideAStringLiteral.ts diff --git a/tests/cases/fourslash_old/semicolonFormattingNestedStatements.ts b/tests/cases/fourslash/semicolonFormattingNestedStatements.ts similarity index 100% rename from tests/cases/fourslash_old/semicolonFormattingNestedStatements.ts rename to tests/cases/fourslash/semicolonFormattingNestedStatements.ts diff --git a/tests/cases/fourslash_old/semicolonFormattingNewline.ts b/tests/cases/fourslash/semicolonFormattingNewline.ts similarity index 100% rename from tests/cases/fourslash_old/semicolonFormattingNewline.ts rename to tests/cases/fourslash/semicolonFormattingNewline.ts diff --git a/tests/cases/fourslash_old/signatureHelpInFunctionCall.ts b/tests/cases/fourslash/signatureHelpInFunctionCall.ts similarity index 100% rename from tests/cases/fourslash_old/signatureHelpInFunctionCall.ts rename to tests/cases/fourslash/signatureHelpInFunctionCall.ts diff --git a/tests/cases/fourslash_old/signatureHelpNegativeTests.ts b/tests/cases/fourslash/signatureHelpNegativeTests.ts similarity index 100% rename from tests/cases/fourslash_old/signatureHelpNegativeTests.ts rename to tests/cases/fourslash/signatureHelpNegativeTests.ts diff --git a/tests/cases/fourslash_old/signatureHelpNegativeTests2.ts b/tests/cases/fourslash/signatureHelpNegativeTests2.ts similarity index 100% rename from tests/cases/fourslash_old/signatureHelpNegativeTests2.ts rename to tests/cases/fourslash/signatureHelpNegativeTests2.ts diff --git a/tests/cases/fourslash_old/signatureHelpObjectCreationExpressionNoArgs_NotAvailable.ts b/tests/cases/fourslash/signatureHelpObjectCreationExpressionNoArgs_NotAvailable.ts similarity index 100% rename from tests/cases/fourslash_old/signatureHelpObjectCreationExpressionNoArgs_NotAvailable.ts rename to tests/cases/fourslash/signatureHelpObjectCreationExpressionNoArgs_NotAvailable.ts diff --git a/tests/cases/fourslash_old/signatureHelpWithInterfaceAsIdentifier.ts b/tests/cases/fourslash/signatureHelpWithInterfaceAsIdentifier.ts similarity index 100% rename from tests/cases/fourslash_old/signatureHelpWithInterfaceAsIdentifier.ts rename to tests/cases/fourslash/signatureHelpWithInterfaceAsIdentifier.ts diff --git a/tests/cases/fourslash_old/singleLineTypeLiteralFormatting.ts b/tests/cases/fourslash/singleLineTypeLiteralFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/singleLineTypeLiteralFormatting.ts rename to tests/cases/fourslash/singleLineTypeLiteralFormatting.ts diff --git a/tests/cases/fourslash_old/smartIndentAfterAlignedFunctionArgument.ts b/tests/cases/fourslash/smartIndentAfterAlignedFunctionArgument.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentAfterAlignedFunctionArgument.ts rename to tests/cases/fourslash/smartIndentAfterAlignedFunctionArgument.ts diff --git a/tests/cases/fourslash_old/smartIndentAfterFatArrowVar.ts b/tests/cases/fourslash/smartIndentAfterFatArrowVar.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentAfterFatArrowVar.ts rename to tests/cases/fourslash/smartIndentAfterFatArrowVar.ts diff --git a/tests/cases/fourslash_old/smartIndentClass.ts b/tests/cases/fourslash/smartIndentClass.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentClass.ts rename to tests/cases/fourslash/smartIndentClass.ts diff --git a/tests/cases/fourslash_old/smartIndentEnum.ts b/tests/cases/fourslash/smartIndentEnum.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentEnum.ts rename to tests/cases/fourslash/smartIndentEnum.ts diff --git a/tests/cases/fourslash_old/smartIndentInsideBlockInsideCase.ts b/tests/cases/fourslash/smartIndentInsideBlockInsideCase.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentInsideBlockInsideCase.ts rename to tests/cases/fourslash/smartIndentInsideBlockInsideCase.ts diff --git a/tests/cases/fourslash_old/smartIndentInsideMultilineString.ts b/tests/cases/fourslash/smartIndentInsideMultilineString.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentInsideMultilineString.ts rename to tests/cases/fourslash/smartIndentInsideMultilineString.ts diff --git a/tests/cases/fourslash_old/smartIndentInterface.ts b/tests/cases/fourslash/smartIndentInterface.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentInterface.ts rename to tests/cases/fourslash/smartIndentInterface.ts diff --git a/tests/cases/fourslash_old/smartIndentModule.ts b/tests/cases/fourslash/smartIndentModule.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentModule.ts rename to tests/cases/fourslash/smartIndentModule.ts diff --git a/tests/cases/fourslash_old/smartIndentNestedModule.ts b/tests/cases/fourslash/smartIndentNestedModule.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentNestedModule.ts rename to tests/cases/fourslash/smartIndentNestedModule.ts diff --git a/tests/cases/fourslash_old/smartIndentNonterminatedArgumentListAtEOF.ts b/tests/cases/fourslash/smartIndentNonterminatedArgumentListAtEOF.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentNonterminatedArgumentListAtEOF.ts rename to tests/cases/fourslash/smartIndentNonterminatedArgumentListAtEOF.ts diff --git a/tests/cases/fourslash_old/smartIndentNonterminatedIfStatementAtEOF.ts b/tests/cases/fourslash/smartIndentNonterminatedIfStatementAtEOF.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentNonterminatedIfStatementAtEOF.ts rename to tests/cases/fourslash/smartIndentNonterminatedIfStatementAtEOF.ts diff --git a/tests/cases/fourslash_old/smartIndentOnFunctionParameters.ts b/tests/cases/fourslash/smartIndentOnFunctionParameters.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentOnFunctionParameters.ts rename to tests/cases/fourslash/smartIndentOnFunctionParameters.ts diff --git a/tests/cases/fourslash_old/smartIndentStatementFor.ts b/tests/cases/fourslash/smartIndentStatementFor.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentStatementFor.ts rename to tests/cases/fourslash/smartIndentStatementFor.ts diff --git a/tests/cases/fourslash_old/smartIndentStatementForIn.ts b/tests/cases/fourslash/smartIndentStatementForIn.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentStatementForIn.ts rename to tests/cases/fourslash/smartIndentStatementForIn.ts diff --git a/tests/cases/fourslash_old/smartIndentStatementSwitch.ts b/tests/cases/fourslash/smartIndentStatementSwitch.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentStatementSwitch.ts rename to tests/cases/fourslash/smartIndentStatementSwitch.ts diff --git a/tests/cases/fourslash_old/smartIndentStatementTryCatchFinally.ts b/tests/cases/fourslash/smartIndentStatementTryCatchFinally.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentStatementTryCatchFinally.ts rename to tests/cases/fourslash/smartIndentStatementTryCatchFinally.ts diff --git a/tests/cases/fourslash_old/smartIndentStatementWith.ts b/tests/cases/fourslash/smartIndentStatementWith.ts similarity index 100% rename from tests/cases/fourslash_old/smartIndentStatementWith.ts rename to tests/cases/fourslash/smartIndentStatementWith.ts diff --git a/tests/cases/fourslash_old/spaceAfterConstructor.ts b/tests/cases/fourslash/spaceAfterConstructor.ts similarity index 100% rename from tests/cases/fourslash_old/spaceAfterConstructor.ts rename to tests/cases/fourslash/spaceAfterConstructor.ts diff --git a/tests/cases/fourslash_old/spaceAfterReturn.ts b/tests/cases/fourslash/spaceAfterReturn.ts similarity index 100% rename from tests/cases/fourslash_old/spaceAfterReturn.ts rename to tests/cases/fourslash/spaceAfterReturn.ts diff --git a/tests/cases/fourslash_old/squiggleFunctionExpression.ts b/tests/cases/fourslash/squiggleFunctionExpression.ts similarity index 78% rename from tests/cases/fourslash_old/squiggleFunctionExpression.ts rename to tests/cases/fourslash/squiggleFunctionExpression.ts index efd4b6bafec..3477f31db3a 100644 --- a/tests/cases/fourslash_old/squiggleFunctionExpression.ts +++ b/tests/cases/fourslash/squiggleFunctionExpression.ts @@ -1,7 +1,7 @@ /// ////function takesCallback(callback: (n) => any) { } -////takesCallback(function inner(n) { var /*1*/k: string = 10/*2*/; }); +////takesCallback(function inner(n) { var /*1*/k/*2*/: string = 10; }); verify.errorExistsBetweenMarkers("1", "2"); verify.not.errorExistsBeforeMarker("1"); diff --git a/tests/cases/fourslash_old/squiggleIllegalClassExtension.ts b/tests/cases/fourslash/squiggleIllegalClassExtension.ts similarity index 100% rename from tests/cases/fourslash_old/squiggleIllegalClassExtension.ts rename to tests/cases/fourslash/squiggleIllegalClassExtension.ts diff --git a/tests/cases/fourslash_old/squiggleIllegalInterfaceExtension.ts b/tests/cases/fourslash/squiggleIllegalInterfaceExtension.ts similarity index 100% rename from tests/cases/fourslash_old/squiggleIllegalInterfaceExtension.ts rename to tests/cases/fourslash/squiggleIllegalInterfaceExtension.ts diff --git a/tests/cases/fourslash_old/squiggleIllegalSubclassOverride.ts b/tests/cases/fourslash/squiggleIllegalSubclassOverride.ts similarity index 100% rename from tests/cases/fourslash_old/squiggleIllegalSubclassOverride.ts rename to tests/cases/fourslash/squiggleIllegalSubclassOverride.ts diff --git a/tests/cases/fourslash_old/squiggleUnclosedStringLiteral.ts b/tests/cases/fourslash/squiggleUnclosedStringLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/squiggleUnclosedStringLiteral.ts rename to tests/cases/fourslash/squiggleUnclosedStringLiteral.ts diff --git a/tests/cases/fourslash_old/superCallError0.ts b/tests/cases/fourslash/superCallError0.ts similarity index 100% rename from tests/cases/fourslash_old/superCallError0.ts rename to tests/cases/fourslash/superCallError0.ts diff --git a/tests/cases/fourslash_old/superInDerivedTypeOfGenericWithStatics.ts b/tests/cases/fourslash/superInDerivedTypeOfGenericWithStatics.ts similarity index 100% rename from tests/cases/fourslash_old/superInDerivedTypeOfGenericWithStatics.ts rename to tests/cases/fourslash/superInDerivedTypeOfGenericWithStatics.ts diff --git a/tests/cases/fourslash_old/switchIndenting.ts b/tests/cases/fourslash/switchIndenting.ts similarity index 100% rename from tests/cases/fourslash_old/switchIndenting.ts rename to tests/cases/fourslash/switchIndenting.ts diff --git a/tests/cases/fourslash_old/syntaxErrorAfterImport1.ts b/tests/cases/fourslash/syntaxErrorAfterImport1.ts similarity index 100% rename from tests/cases/fourslash_old/syntaxErrorAfterImport1.ts rename to tests/cases/fourslash/syntaxErrorAfterImport1.ts diff --git a/tests/cases/fourslash_old/tabbingAfterNewlineInsertedBeforeWhile.ts b/tests/cases/fourslash/tabbingAfterNewlineInsertedBeforeWhile.ts similarity index 100% rename from tests/cases/fourslash_old/tabbingAfterNewlineInsertedBeforeWhile.ts rename to tests/cases/fourslash/tabbingAfterNewlineInsertedBeforeWhile.ts diff --git a/tests/cases/fourslash_old/toggleDuplicateFunctionDeclaration.ts b/tests/cases/fourslash/toggleDuplicateFunctionDeclaration.ts similarity index 100% rename from tests/cases/fourslash_old/toggleDuplicateFunctionDeclaration.ts rename to tests/cases/fourslash/toggleDuplicateFunctionDeclaration.ts diff --git a/tests/cases/fourslash_old/tryCatchFinallyFormating.ts b/tests/cases/fourslash/tryCatchFinallyFormating.ts similarity index 100% rename from tests/cases/fourslash_old/tryCatchFinallyFormating.ts rename to tests/cases/fourslash/tryCatchFinallyFormating.ts diff --git a/tests/cases/fourslash_old/typeAboveNumberLiteralExpressionStatement.ts b/tests/cases/fourslash/typeAboveNumberLiteralExpressionStatement.ts similarity index 100% rename from tests/cases/fourslash_old/typeAboveNumberLiteralExpressionStatement.ts rename to tests/cases/fourslash/typeAboveNumberLiteralExpressionStatement.ts diff --git a/tests/cases/fourslash_old/typeArgCompletion.ts b/tests/cases/fourslash/typeArgCompletion.ts similarity index 100% rename from tests/cases/fourslash_old/typeArgCompletion.ts rename to tests/cases/fourslash/typeArgCompletion.ts diff --git a/tests/cases/fourslash_old/typeCheckAfterAddingGenericParameter.ts b/tests/cases/fourslash/typeCheckAfterAddingGenericParameter.ts similarity index 100% rename from tests/cases/fourslash_old/typeCheckAfterAddingGenericParameter.ts rename to tests/cases/fourslash/typeCheckAfterAddingGenericParameter.ts diff --git a/tests/cases/fourslash_old/typeCheckExpression0.ts b/tests/cases/fourslash/typeCheckExpression0.ts similarity index 100% rename from tests/cases/fourslash_old/typeCheckExpression0.ts rename to tests/cases/fourslash/typeCheckExpression0.ts diff --git a/tests/cases/fourslash_old/typeCheckGenericTypeLiteralArgument.ts b/tests/cases/fourslash/typeCheckGenericTypeLiteralArgument.ts similarity index 100% rename from tests/cases/fourslash_old/typeCheckGenericTypeLiteralArgument.ts rename to tests/cases/fourslash/typeCheckGenericTypeLiteralArgument.ts diff --git a/tests/cases/fourslash_old/typeCheckIndexSignature.ts b/tests/cases/fourslash/typeCheckIndexSignature.ts similarity index 100% rename from tests/cases/fourslash_old/typeCheckIndexSignature.ts rename to tests/cases/fourslash/typeCheckIndexSignature.ts diff --git a/tests/cases/fourslash_old/typeCheckIndexerAccess1.ts b/tests/cases/fourslash/typeCheckIndexerAccess1.ts similarity index 100% rename from tests/cases/fourslash_old/typeCheckIndexerAccess1.ts rename to tests/cases/fourslash/typeCheckIndexerAccess1.ts diff --git a/tests/cases/fourslash_old/typeCheckObjectInArrayLiteral.ts b/tests/cases/fourslash/typeCheckObjectInArrayLiteral.ts similarity index 100% rename from tests/cases/fourslash_old/typeCheckObjectInArrayLiteral.ts rename to tests/cases/fourslash/typeCheckObjectInArrayLiteral.ts diff --git a/tests/cases/fourslash_old/unclosedArrayErrorRecovery.ts b/tests/cases/fourslash/unclosedArrayErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedArrayErrorRecovery.ts rename to tests/cases/fourslash/unclosedArrayErrorRecovery.ts diff --git a/tests/cases/fourslash_old/unclosedCommentsInConstructor.ts b/tests/cases/fourslash/unclosedCommentsInConstructor.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedCommentsInConstructor.ts rename to tests/cases/fourslash/unclosedCommentsInConstructor.ts diff --git a/tests/cases/fourslash_old/unclosedFunctionErrorRecovery.ts b/tests/cases/fourslash/unclosedFunctionErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedFunctionErrorRecovery.ts rename to tests/cases/fourslash/unclosedFunctionErrorRecovery.ts diff --git a/tests/cases/fourslash_old/unclosedFunctionErrorRecovery2.ts b/tests/cases/fourslash/unclosedFunctionErrorRecovery2.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedFunctionErrorRecovery2.ts rename to tests/cases/fourslash/unclosedFunctionErrorRecovery2.ts diff --git a/tests/cases/fourslash_old/unclosedFunctionErrorRecovery3.ts b/tests/cases/fourslash/unclosedFunctionErrorRecovery3.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedFunctionErrorRecovery3.ts rename to tests/cases/fourslash/unclosedFunctionErrorRecovery3.ts diff --git a/tests/cases/fourslash_old/unclosedMultilineStringLiteralErrorRecovery.ts b/tests/cases/fourslash/unclosedMultilineStringLiteralErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedMultilineStringLiteralErrorRecovery.ts rename to tests/cases/fourslash/unclosedMultilineStringLiteralErrorRecovery.ts diff --git a/tests/cases/fourslash_old/unclosedStringLiteralAutoformating.ts b/tests/cases/fourslash/unclosedStringLiteralAutoformating.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedStringLiteralAutoformating.ts rename to tests/cases/fourslash/unclosedStringLiteralAutoformating.ts diff --git a/tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery.ts b/tests/cases/fourslash/unclosedStringLiteralErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery.ts rename to tests/cases/fourslash/unclosedStringLiteralErrorRecovery.ts diff --git a/tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery2.ts b/tests/cases/fourslash/unclosedStringLiteralErrorRecovery2.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery2.ts rename to tests/cases/fourslash/unclosedStringLiteralErrorRecovery2.ts diff --git a/tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery3.ts b/tests/cases/fourslash/unclosedStringLiteralErrorRecovery3.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery3.ts rename to tests/cases/fourslash/unclosedStringLiteralErrorRecovery3.ts diff --git a/tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery4.ts b/tests/cases/fourslash/unclosedStringLiteralErrorRecovery4.ts similarity index 100% rename from tests/cases/fourslash_old/unclosedStringLiteralErrorRecovery4.ts rename to tests/cases/fourslash/unclosedStringLiteralErrorRecovery4.ts diff --git a/tests/cases/fourslash_old/underscoreTyping1.ts b/tests/cases/fourslash/underscoreTyping1.ts similarity index 91% rename from tests/cases/fourslash_old/underscoreTyping1.ts rename to tests/cases/fourslash/underscoreTyping1.ts index 06e8b979079..d4556e07f74 100644 --- a/tests/cases/fourslash_old/underscoreTyping1.ts +++ b/tests/cases/fourslash/underscoreTyping1.ts @@ -18,4 +18,4 @@ //// } goTo.position(0); -verify.numberOfErrorsInCurrentFile(3); +verify.numberOfErrorsInCurrentFile(2); diff --git a/tests/cases/fourslash_old/unknownVariableErrorRecovery.ts b/tests/cases/fourslash/unknownVariableErrorRecovery.ts similarity index 100% rename from tests/cases/fourslash_old/unknownVariableErrorRecovery.ts rename to tests/cases/fourslash/unknownVariableErrorRecovery.ts diff --git a/tests/cases/fourslash_old/updateToClassStatics.ts b/tests/cases/fourslash/updateToClassStatics.ts similarity index 100% rename from tests/cases/fourslash_old/updateToClassStatics.ts rename to tests/cases/fourslash/updateToClassStatics.ts diff --git a/tests/cases/fourslash_old/whiteSpaceBeforeReturnTypeFormatting.ts b/tests/cases/fourslash/whiteSpaceBeforeReturnTypeFormatting.ts similarity index 100% rename from tests/cases/fourslash_old/whiteSpaceBeforeReturnTypeFormatting.ts rename to tests/cases/fourslash/whiteSpaceBeforeReturnTypeFormatting.ts diff --git a/tests/cases/fourslash_old/whiteSpaceTrimming.ts b/tests/cases/fourslash/whiteSpaceTrimming.ts similarity index 100% rename from tests/cases/fourslash_old/whiteSpaceTrimming.ts rename to tests/cases/fourslash/whiteSpaceTrimming.ts diff --git a/tests/cases/fourslash/errorsAfterResolvingVariableDeclOfMergedVariableAndClassDecl.ts b/tests/cases/fourslash_old/errorsAfterResolvingVariableDeclOfMergedVariableAndClassDecl.ts similarity index 100% rename from tests/cases/fourslash/errorsAfterResolvingVariableDeclOfMergedVariableAndClassDecl.ts rename to tests/cases/fourslash_old/errorsAfterResolvingVariableDeclOfMergedVariableAndClassDecl.ts diff --git a/tests/cases/fourslash/todoComments1.ts b/tests/cases/fourslash_old/todoComments1.ts similarity index 100% rename from tests/cases/fourslash/todoComments1.ts rename to tests/cases/fourslash_old/todoComments1.ts diff --git a/tests/cases/fourslash/todoComments10.ts b/tests/cases/fourslash_old/todoComments10.ts similarity index 100% rename from tests/cases/fourslash/todoComments10.ts rename to tests/cases/fourslash_old/todoComments10.ts diff --git a/tests/cases/fourslash/todoComments11.ts b/tests/cases/fourslash_old/todoComments11.ts similarity index 100% rename from tests/cases/fourslash/todoComments11.ts rename to tests/cases/fourslash_old/todoComments11.ts diff --git a/tests/cases/fourslash/todoComments12.ts b/tests/cases/fourslash_old/todoComments12.ts similarity index 100% rename from tests/cases/fourslash/todoComments12.ts rename to tests/cases/fourslash_old/todoComments12.ts diff --git a/tests/cases/fourslash/todoComments13.ts b/tests/cases/fourslash_old/todoComments13.ts similarity index 100% rename from tests/cases/fourslash/todoComments13.ts rename to tests/cases/fourslash_old/todoComments13.ts diff --git a/tests/cases/fourslash/todoComments14.ts b/tests/cases/fourslash_old/todoComments14.ts similarity index 100% rename from tests/cases/fourslash/todoComments14.ts rename to tests/cases/fourslash_old/todoComments14.ts diff --git a/tests/cases/fourslash/todoComments15.ts b/tests/cases/fourslash_old/todoComments15.ts similarity index 100% rename from tests/cases/fourslash/todoComments15.ts rename to tests/cases/fourslash_old/todoComments15.ts diff --git a/tests/cases/fourslash/todoComments16.ts b/tests/cases/fourslash_old/todoComments16.ts similarity index 100% rename from tests/cases/fourslash/todoComments16.ts rename to tests/cases/fourslash_old/todoComments16.ts diff --git a/tests/cases/fourslash/todoComments17.ts b/tests/cases/fourslash_old/todoComments17.ts similarity index 100% rename from tests/cases/fourslash/todoComments17.ts rename to tests/cases/fourslash_old/todoComments17.ts diff --git a/tests/cases/fourslash/todoComments2.ts b/tests/cases/fourslash_old/todoComments2.ts similarity index 100% rename from tests/cases/fourslash/todoComments2.ts rename to tests/cases/fourslash_old/todoComments2.ts diff --git a/tests/cases/fourslash/todoComments3.ts b/tests/cases/fourslash_old/todoComments3.ts similarity index 100% rename from tests/cases/fourslash/todoComments3.ts rename to tests/cases/fourslash_old/todoComments3.ts diff --git a/tests/cases/fourslash/todoComments4.ts b/tests/cases/fourslash_old/todoComments4.ts similarity index 100% rename from tests/cases/fourslash/todoComments4.ts rename to tests/cases/fourslash_old/todoComments4.ts diff --git a/tests/cases/fourslash/todoComments5.ts b/tests/cases/fourslash_old/todoComments5.ts similarity index 100% rename from tests/cases/fourslash/todoComments5.ts rename to tests/cases/fourslash_old/todoComments5.ts diff --git a/tests/cases/fourslash/todoComments6.ts b/tests/cases/fourslash_old/todoComments6.ts similarity index 100% rename from tests/cases/fourslash/todoComments6.ts rename to tests/cases/fourslash_old/todoComments6.ts diff --git a/tests/cases/fourslash/todoComments7.ts b/tests/cases/fourslash_old/todoComments7.ts similarity index 100% rename from tests/cases/fourslash/todoComments7.ts rename to tests/cases/fourslash_old/todoComments7.ts diff --git a/tests/cases/fourslash/todoComments8.ts b/tests/cases/fourslash_old/todoComments8.ts similarity index 100% rename from tests/cases/fourslash/todoComments8.ts rename to tests/cases/fourslash_old/todoComments8.ts diff --git a/tests/cases/fourslash/todoComments9.ts b/tests/cases/fourslash_old/todoComments9.ts similarity index 100% rename from tests/cases/fourslash/todoComments9.ts rename to tests/cases/fourslash_old/todoComments9.ts