From fe96258920dc5b4baf44425c0e29dd013e5f1b29 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 20 Jan 2015 11:06:21 -0800 Subject: [PATCH] revert serviceVersion change as the API is still compatible --- src/services/services.js | 4423 ++++++++++++++++++++++++++++++++++++++ src/services/services.ts | 2 +- 2 files changed, 4424 insertions(+), 1 deletion(-) create mode 100644 src/services/services.js diff --git a/src/services/services.js b/src/services/services.js new file mode 100644 index 00000000000..2f3a191d5d6 --- /dev/null +++ b/src/services/services.js @@ -0,0 +1,4423 @@ +/// +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +/// +/// +/// +/// +/// +/// +/// +var ts; +(function (ts) { + ts.servicesVersion = "0.4"; + var ScriptSnapshot; + (function (ScriptSnapshot) { + var StringScriptSnapshot = (function () { + function StringScriptSnapshot(text) { + this.text = text; + this._lineStartPositions = undefined; + } + StringScriptSnapshot.prototype.getText = function (start, end) { + return this.text.substring(start, end); + }; + StringScriptSnapshot.prototype.getLength = function () { + return this.text.length; + }; + StringScriptSnapshot.prototype.getLineStartPositions = function () { + if (!this._lineStartPositions) { + this._lineStartPositions = ts.computeLineStarts(this.text); + } + return this._lineStartPositions; + }; + StringScriptSnapshot.prototype.getChangeRange = function (oldSnapshot) { + // Text-based snapshots do not support incremental parsing. Return undefined + // to signal that to the caller. + return undefined; + }; + return StringScriptSnapshot; + })(); + function fromString(text) { + return new StringScriptSnapshot(text); + } + ScriptSnapshot.fromString = fromString; + })(ScriptSnapshot = ts.ScriptSnapshot || (ts.ScriptSnapshot = {})); + var scanner = ts.createScanner(ts.ScriptTarget.Latest, true); + var emptyArray = []; + function createNode(kind, pos, end, flags, parent) { + var node = new (ts.getNodeConstructor(kind))(); + node.pos = pos; + node.end = end; + node.flags = flags; + node.parent = parent; + return node; + } + var NodeObject = (function () { + function NodeObject() { + } + NodeObject.prototype.getSourceFile = function () { + return ts.getSourceFileOfNode(this); + }; + NodeObject.prototype.getStart = function (sourceFile) { + return ts.getTokenPosOfNode(this, sourceFile); + }; + NodeObject.prototype.getFullStart = function () { + return this.pos; + }; + NodeObject.prototype.getEnd = function () { + return this.end; + }; + NodeObject.prototype.getWidth = function (sourceFile) { + return this.getEnd() - this.getStart(sourceFile); + }; + NodeObject.prototype.getFullWidth = function () { + return this.end - this.getFullStart(); + }; + NodeObject.prototype.getLeadingTriviaWidth = function (sourceFile) { + return this.getStart(sourceFile) - this.pos; + }; + NodeObject.prototype.getFullText = function (sourceFile) { + return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); + }; + NodeObject.prototype.getText = function (sourceFile) { + return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); + }; + NodeObject.prototype.addSyntheticNodes = function (nodes, pos, end) { + scanner.setTextPos(pos); + while (pos < end) { + var token = scanner.scan(); + var textPos = scanner.getTextPos(); + nodes.push(createNode(token, pos, textPos, ts.NodeFlags.Synthetic, this)); + pos = textPos; + } + return pos; + }; + NodeObject.prototype.createSyntaxList = function (nodes) { + var list = createNode(ts.SyntaxKind.SyntaxList, nodes.pos, nodes.end, ts.NodeFlags.Synthetic, this); + list._children = []; + var pos = nodes.pos; + for (var i = 0, len = nodes.length; i < len; i++) { + var node = nodes[i]; + if (pos < node.pos) { + pos = this.addSyntheticNodes(list._children, pos, node.pos); + } + list._children.push(node); + pos = node.end; + } + if (pos < nodes.end) { + this.addSyntheticNodes(list._children, pos, nodes.end); + } + return list; + }; + NodeObject.prototype.createChildren = function (sourceFile) { + var _this = this; + if (this.kind >= ts.SyntaxKind.FirstNode) { + scanner.setText((sourceFile || this.getSourceFile()).text); + var children = []; + var pos = this.pos; + var processNode = function (node) { + if (pos < node.pos) { + pos = _this.addSyntheticNodes(children, pos, node.pos); + } + children.push(node); + pos = node.end; + }; + var processNodes = function (nodes) { + if (pos < nodes.pos) { + pos = _this.addSyntheticNodes(children, pos, nodes.pos); + } + children.push(_this.createSyntaxList(nodes)); + pos = nodes.end; + }; + ts.forEachChild(this, processNode, processNodes); + if (pos < this.end) { + this.addSyntheticNodes(children, pos, this.end); + } + scanner.setText(undefined); + } + this._children = children || emptyArray; + }; + NodeObject.prototype.getChildCount = function (sourceFile) { + if (!this._children) + this.createChildren(sourceFile); + return this._children.length; + }; + NodeObject.prototype.getChildAt = function (index, sourceFile) { + if (!this._children) + this.createChildren(sourceFile); + return this._children[index]; + }; + NodeObject.prototype.getChildren = function (sourceFile) { + if (!this._children) + this.createChildren(sourceFile); + return this._children; + }; + NodeObject.prototype.getFirstToken = function (sourceFile) { + var children = this.getChildren(); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.kind < ts.SyntaxKind.FirstNode) { + return child; + } + return child.getFirstToken(sourceFile); + } + }; + NodeObject.prototype.getLastToken = function (sourceFile) { + var children = this.getChildren(sourceFile); + for (var i = children.length - 1; i >= 0; i--) { + var child = children[i]; + if (child.kind < ts.SyntaxKind.FirstNode) { + return child; + } + return child.getLastToken(sourceFile); + } + }; + return NodeObject; + })(); + var SymbolObject = (function () { + function SymbolObject(flags, name) { + this.flags = flags; + this.name = name; + } + SymbolObject.prototype.getFlags = function () { + return this.flags; + }; + SymbolObject.prototype.getName = function () { + return this.name; + }; + SymbolObject.prototype.getDeclarations = function () { + return this.declarations; + }; + SymbolObject.prototype.getDocumentationComment = function () { + if (this.documentationComment === undefined) { + this.documentationComment = getJsDocCommentsFromDeclarations(this.declarations, this.name, !(this.flags & ts.SymbolFlags.Property)); + } + return this.documentationComment; + }; + return SymbolObject; + })(); + function getJsDocCommentsFromDeclarations(declarations, name, canUseParsedParamTagComments) { + var documentationComment = []; + var docComments = getJsDocCommentsSeparatedByNewLines(); + ts.forEach(docComments, function (docComment) { + if (documentationComment.length) { + documentationComment.push(ts.lineBreakPart()); + } + documentationComment.push(docComment); + }); + return documentationComment; + function getJsDocCommentsSeparatedByNewLines() { + var paramTag = "@param"; + var jsDocCommentParts = []; + ts.forEach(declarations, function (declaration) { + var sourceFileOfDeclaration = ts.getSourceFileOfNode(declaration); + // If it is parameter - try and get the jsDoc comment with @param tag from function declaration's jsDoc comments + if (canUseParsedParamTagComments && declaration.kind === ts.SyntaxKind.Parameter) { + ts.forEach(getJsDocCommentTextRange(declaration.parent, sourceFileOfDeclaration), function (jsDocCommentTextRange) { + var cleanedParamJsDocComment = getCleanedParamJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration); + if (cleanedParamJsDocComment) { + jsDocCommentParts.push.apply(jsDocCommentParts, cleanedParamJsDocComment); + } + }); + } + // If this is left side of dotted module declaration, there is no doc comments associated with this node + if (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.body.kind === ts.SyntaxKind.ModuleDeclaration) { + return; + } + while (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.parent.kind === ts.SyntaxKind.ModuleDeclaration) { + declaration = declaration.parent; + } + // Get the cleaned js doc comment text from the declaration + ts.forEach(getJsDocCommentTextRange(declaration.kind === ts.SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration, sourceFileOfDeclaration), function (jsDocCommentTextRange) { + var cleanedJsDocComment = getCleanedJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration); + if (cleanedJsDocComment) { + jsDocCommentParts.push.apply(jsDocCommentParts, cleanedJsDocComment); + } + }); + }); + return jsDocCommentParts; + function getJsDocCommentTextRange(node, sourceFile) { + return ts.map(ts.getJsDocComments(node, sourceFile), function (jsDocComment) { + return { + pos: jsDocComment.pos + "/*".length, + end: jsDocComment.end - "*/".length // Trim off comment end indicator + }; + }); + } + function consumeWhiteSpacesOnTheLine(pos, end, sourceFile, maxSpacesToRemove) { + if (maxSpacesToRemove !== undefined) { + end = Math.min(end, pos + maxSpacesToRemove); + } + for (; pos < end; pos++) { + var ch = sourceFile.text.charCodeAt(pos); + if (!ts.isWhiteSpace(ch) || ts.isLineBreak(ch)) { + // Either found lineBreak or non whiteSpace + return pos; + } + } + return end; + } + function consumeLineBreaks(pos, end, sourceFile) { + while (pos < end && ts.isLineBreak(sourceFile.text.charCodeAt(pos))) { + pos++; + } + return pos; + } + function isName(pos, end, sourceFile, name) { + return pos + name.length < end && sourceFile.text.substr(pos, name.length) === name && (ts.isWhiteSpace(sourceFile.text.charCodeAt(pos + name.length)) || ts.isLineBreak(sourceFile.text.charCodeAt(pos + name.length))); + } + function isParamTag(pos, end, sourceFile) { + // If it is @param tag + return isName(pos, end, sourceFile, paramTag); + } + function pushDocCommentLineText(docComments, text, blankLineCount) { + while (blankLineCount--) + docComments.push(ts.textPart("")); + docComments.push(ts.textPart(text)); + } + function getCleanedJsDocComment(pos, end, sourceFile) { + var spacesToRemoveAfterAsterisk; + var docComments = []; + var blankLineCount = 0; + var isInParamTag = false; + while (pos < end) { + var docCommentTextOfLine = ""; + // First consume leading white space + pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile); + // If the comment starts with '*' consume the spaces on this line + if (pos < end && sourceFile.text.charCodeAt(pos) === ts.CharacterCodes.asterisk) { + var lineStartPos = pos + 1; + pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, spacesToRemoveAfterAsterisk); + // Set the spaces to remove after asterisk as margin if not already set + if (spacesToRemoveAfterAsterisk === undefined && pos < end && !ts.isLineBreak(sourceFile.text.charCodeAt(pos))) { + spacesToRemoveAfterAsterisk = pos - lineStartPos; + } + } + else if (spacesToRemoveAfterAsterisk === undefined) { + spacesToRemoveAfterAsterisk = 0; + } + while (pos < end && !ts.isLineBreak(sourceFile.text.charCodeAt(pos))) { + var ch = sourceFile.text.charAt(pos); + if (ch === "@") { + // If it is @param tag + if (isParamTag(pos, end, sourceFile)) { + isInParamTag = true; + pos += paramTag.length; + continue; + } + else { + isInParamTag = false; + } + } + // Add the ch to doc text if we arent in param tag + if (!isInParamTag) { + docCommentTextOfLine += ch; + } + // Scan next character + pos++; + } + // Continue with next line + pos = consumeLineBreaks(pos, end, sourceFile); + if (docCommentTextOfLine) { + pushDocCommentLineText(docComments, docCommentTextOfLine, blankLineCount); + blankLineCount = 0; + } + else if (!isInParamTag && docComments.length) { + // This is blank line when there is text already parsed + blankLineCount++; + } + } + return docComments; + } + function getCleanedParamJsDocComment(pos, end, sourceFile) { + var paramHelpStringMargin; + var paramDocComments = []; + while (pos < end) { + if (isParamTag(pos, end, sourceFile)) { + var blankLineCount = 0; + var recordedParamTag = false; + // Consume leading spaces + pos = consumeWhiteSpaces(pos + paramTag.length); + if (pos >= end) { + break; + } + // Ignore type expression + if (sourceFile.text.charCodeAt(pos) === ts.CharacterCodes.openBrace) { + pos++; + for (var curlies = 1; pos < end; pos++) { + var charCode = sourceFile.text.charCodeAt(pos); + // { character means we need to find another } to match the found one + if (charCode === ts.CharacterCodes.openBrace) { + curlies++; + continue; + } + // } char + if (charCode === ts.CharacterCodes.closeBrace) { + curlies--; + if (curlies === 0) { + // We do not have any more } to match the type expression is ignored completely + pos++; + break; + } + else { + continue; + } + } + // Found start of another tag + if (charCode === ts.CharacterCodes.at) { + break; + } + } + // Consume white spaces + pos = consumeWhiteSpaces(pos); + if (pos >= end) { + break; + } + } + // Parameter name + if (isName(pos, end, sourceFile, name)) { + // Found the parameter we are looking for consume white spaces + pos = consumeWhiteSpaces(pos + name.length); + if (pos >= end) { + break; + } + var paramHelpString = ""; + var firstLineParamHelpStringPos = pos; + while (pos < end) { + var ch = sourceFile.text.charCodeAt(pos); + // at line break, set this comment line text and go to next line + if (ts.isLineBreak(ch)) { + if (paramHelpString) { + pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); + paramHelpString = ""; + blankLineCount = 0; + recordedParamTag = true; + } + else if (recordedParamTag) { + blankLineCount++; + } + // Get the pos after cleaning start of the line + setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos); + continue; + } + // Done scanning param help string - next tag found + if (ch === ts.CharacterCodes.at) { + break; + } + paramHelpString += sourceFile.text.charAt(pos); + // Go to next character + pos++; + } + // If there is param help text, add it top the doc comments + if (paramHelpString) { + pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); + } + paramHelpStringMargin = undefined; + } + // If this is the start of another tag, continue with the loop in seach of param tag with symbol name + if (sourceFile.text.charCodeAt(pos) === ts.CharacterCodes.at) { + continue; + } + } + // Next character + pos++; + } + return paramDocComments; + function consumeWhiteSpaces(pos) { + while (pos < end && ts.isWhiteSpace(sourceFile.text.charCodeAt(pos))) { + pos++; + } + return pos; + } + function setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos) { + // Get the pos after consuming line breaks + pos = consumeLineBreaks(pos, end, sourceFile); + if (pos >= end) { + return; + } + if (paramHelpStringMargin === undefined) { + paramHelpStringMargin = sourceFile.getLineAndCharacterFromPosition(firstLineParamHelpStringPos).character - 1; + } + // Now consume white spaces max + var startOfLinePos = pos; + pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile, paramHelpStringMargin); + if (pos >= end) { + return; + } + var consumedSpaces = pos - startOfLinePos; + if (consumedSpaces < paramHelpStringMargin) { + var ch = sourceFile.text.charCodeAt(pos); + if (ch === ts.CharacterCodes.asterisk) { + // Consume more spaces after asterisk + pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, paramHelpStringMargin - consumedSpaces - 1); + } + } + } + } + } + } + var TypeObject = (function () { + function TypeObject(checker, flags) { + this.checker = checker; + this.flags = flags; + } + TypeObject.prototype.getFlags = function () { + return this.flags; + }; + TypeObject.prototype.getSymbol = function () { + return this.symbol; + }; + TypeObject.prototype.getProperties = function () { + return this.checker.getPropertiesOfType(this); + }; + TypeObject.prototype.getProperty = function (propertyName) { + return this.checker.getPropertyOfType(this, propertyName); + }; + TypeObject.prototype.getApparentProperties = function () { + return this.checker.getAugmentedPropertiesOfType(this); + }; + TypeObject.prototype.getCallSignatures = function () { + return this.checker.getSignaturesOfType(this, ts.SignatureKind.Call); + }; + TypeObject.prototype.getConstructSignatures = function () { + return this.checker.getSignaturesOfType(this, ts.SignatureKind.Construct); + }; + TypeObject.prototype.getStringIndexType = function () { + return this.checker.getIndexTypeOfType(this, ts.IndexKind.String); + }; + TypeObject.prototype.getNumberIndexType = function () { + return this.checker.getIndexTypeOfType(this, ts.IndexKind.Number); + }; + return TypeObject; + })(); + var SignatureObject = (function () { + function SignatureObject(checker) { + this.checker = checker; + } + SignatureObject.prototype.getDeclaration = function () { + return this.declaration; + }; + SignatureObject.prototype.getTypeParameters = function () { + return this.typeParameters; + }; + SignatureObject.prototype.getParameters = function () { + return this.parameters; + }; + SignatureObject.prototype.getReturnType = function () { + return this.checker.getReturnTypeOfSignature(this); + }; + SignatureObject.prototype.getDocumentationComment = function () { + if (this.documentationComment === undefined) { + this.documentationComment = this.declaration ? getJsDocCommentsFromDeclarations([this.declaration], undefined, false) : []; + } + return this.documentationComment; + }; + return SignatureObject; + })(); + var SourceFileObject = (function (_super) { + __extends(SourceFileObject, _super); + function SourceFileObject() { + _super.apply(this, arguments); + } + SourceFileObject.prototype.getNamedDeclarations = function () { + if (!this.namedDeclarations) { + var sourceFile = this; + var namedDeclarations = []; + ts.forEachChild(sourceFile, function visit(node) { + switch (node.kind) { + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + var functionDeclaration = node; + if (functionDeclaration.name && functionDeclaration.name.getFullWidth() > 0) { + var lastDeclaration = namedDeclarations.length > 0 ? namedDeclarations[namedDeclarations.length - 1] : undefined; + // Check whether this declaration belongs to an "overload group". + if (lastDeclaration && functionDeclaration.symbol === lastDeclaration.symbol) { + // Overwrite the last declaration if it was an overload + // and this one is an implementation. + if (functionDeclaration.body && !lastDeclaration.body) { + namedDeclarations[namedDeclarations.length - 1] = functionDeclaration; + } + } + else { + namedDeclarations.push(functionDeclaration); + } + ts.forEachChild(node, visit); + } + break; + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ModuleDeclaration: + case ts.SyntaxKind.ImportDeclaration: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.TypeLiteral: + if (node.name) { + namedDeclarations.push(node); + } + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.VariableStatement: + case ts.SyntaxKind.VariableDeclarationList: + case ts.SyntaxKind.ObjectBindingPattern: + case ts.SyntaxKind.ArrayBindingPattern: + case ts.SyntaxKind.ModuleBlock: + ts.forEachChild(node, visit); + break; + case ts.SyntaxKind.Block: + if (ts.isFunctionBlock(node)) { + ts.forEachChild(node, visit); + } + break; + case ts.SyntaxKind.Parameter: + // Only consider properties defined as constructor parameters + if (!(node.flags & ts.NodeFlags.AccessibilityModifier)) { + break; + } + case ts.SyntaxKind.VariableDeclaration: + case ts.SyntaxKind.BindingElement: + if (ts.isBindingPattern(node.name)) { + ts.forEachChild(node.name, visit); + break; + } + case ts.SyntaxKind.EnumMember: + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + namedDeclarations.push(node); + break; + } + }); + this.namedDeclarations = namedDeclarations; + } + return this.namedDeclarations; + }; + return SourceFileObject; + })(NodeObject); + var TextChange = (function () { + function TextChange() { + } + return TextChange; + })(); + ts.TextChange = TextChange; + (function (SymbolDisplayPartKind) { + SymbolDisplayPartKind[SymbolDisplayPartKind["aliasName"] = 0] = "aliasName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["className"] = 1] = "className"; + SymbolDisplayPartKind[SymbolDisplayPartKind["enumName"] = 2] = "enumName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["fieldName"] = 3] = "fieldName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["interfaceName"] = 4] = "interfaceName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["keyword"] = 5] = "keyword"; + SymbolDisplayPartKind[SymbolDisplayPartKind["lineBreak"] = 6] = "lineBreak"; + SymbolDisplayPartKind[SymbolDisplayPartKind["numericLiteral"] = 7] = "numericLiteral"; + SymbolDisplayPartKind[SymbolDisplayPartKind["stringLiteral"] = 8] = "stringLiteral"; + SymbolDisplayPartKind[SymbolDisplayPartKind["localName"] = 9] = "localName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["methodName"] = 10] = "methodName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["moduleName"] = 11] = "moduleName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["operator"] = 12] = "operator"; + SymbolDisplayPartKind[SymbolDisplayPartKind["parameterName"] = 13] = "parameterName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["propertyName"] = 14] = "propertyName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["punctuation"] = 15] = "punctuation"; + SymbolDisplayPartKind[SymbolDisplayPartKind["space"] = 16] = "space"; + SymbolDisplayPartKind[SymbolDisplayPartKind["text"] = 17] = "text"; + SymbolDisplayPartKind[SymbolDisplayPartKind["typeParameterName"] = 18] = "typeParameterName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["enumMemberName"] = 19] = "enumMemberName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["functionName"] = 20] = "functionName"; + SymbolDisplayPartKind[SymbolDisplayPartKind["regularExpressionLiteral"] = 21] = "regularExpressionLiteral"; + })(ts.SymbolDisplayPartKind || (ts.SymbolDisplayPartKind = {})); + var SymbolDisplayPartKind = ts.SymbolDisplayPartKind; + (function (TokenClass) { + TokenClass[TokenClass["Punctuation"] = 0] = "Punctuation"; + TokenClass[TokenClass["Keyword"] = 1] = "Keyword"; + TokenClass[TokenClass["Operator"] = 2] = "Operator"; + TokenClass[TokenClass["Comment"] = 3] = "Comment"; + TokenClass[TokenClass["Whitespace"] = 4] = "Whitespace"; + TokenClass[TokenClass["Identifier"] = 5] = "Identifier"; + TokenClass[TokenClass["NumberLiteral"] = 6] = "NumberLiteral"; + TokenClass[TokenClass["StringLiteral"] = 7] = "StringLiteral"; + TokenClass[TokenClass["RegExpLiteral"] = 8] = "RegExpLiteral"; + })(ts.TokenClass || (ts.TokenClass = {})); + var TokenClass = ts.TokenClass; + // TODO: move these to enums + var ScriptElementKind = (function () { + function ScriptElementKind() { + } + ScriptElementKind.unknown = ""; + // predefined type (void) or keyword (class) + ScriptElementKind.keyword = "keyword"; + // top level script node + ScriptElementKind.scriptElement = "script"; + // module foo {} + ScriptElementKind.moduleElement = "module"; + // class X {} + ScriptElementKind.classElement = "class"; + // interface Y {} + ScriptElementKind.interfaceElement = "interface"; + // type T = ... + ScriptElementKind.typeElement = "type"; + // enum E + ScriptElementKind.enumElement = "enum"; + // Inside module and script only + // var v = .. + ScriptElementKind.variableElement = "var"; + // Inside function + ScriptElementKind.localVariableElement = "local var"; + // Inside module and script only + // function f() { } + ScriptElementKind.functionElement = "function"; + // Inside function + ScriptElementKind.localFunctionElement = "local function"; + // class X { [public|private]* foo() {} } + ScriptElementKind.memberFunctionElement = "method"; + // class X { [public|private]* [get|set] foo:number; } + ScriptElementKind.memberGetAccessorElement = "getter"; + ScriptElementKind.memberSetAccessorElement = "setter"; + // class X { [public|private]* foo:number; } + // interface Y { foo:number; } + ScriptElementKind.memberVariableElement = "property"; + // class X { constructor() { } } + ScriptElementKind.constructorImplementationElement = "constructor"; + // interface Y { ():number; } + ScriptElementKind.callSignatureElement = "call"; + // interface Y { []:number; } + ScriptElementKind.indexSignatureElement = "index"; + // interface Y { new():Y; } + ScriptElementKind.constructSignatureElement = "construct"; + // function foo(*Y*: string) + ScriptElementKind.parameterElement = "parameter"; + ScriptElementKind.typeParameterElement = "type parameter"; + ScriptElementKind.primitiveType = "primitive type"; + ScriptElementKind.label = "label"; + ScriptElementKind.alias = "alias"; + ScriptElementKind.constElement = "const"; + ScriptElementKind.letElement = "let"; + return ScriptElementKind; + })(); + ts.ScriptElementKind = ScriptElementKind; + var ScriptElementKindModifier = (function () { + function ScriptElementKindModifier() { + } + ScriptElementKindModifier.none = ""; + ScriptElementKindModifier.publicMemberModifier = "public"; + ScriptElementKindModifier.privateMemberModifier = "private"; + ScriptElementKindModifier.protectedMemberModifier = "protected"; + ScriptElementKindModifier.exportedModifier = "export"; + ScriptElementKindModifier.ambientModifier = "declare"; + ScriptElementKindModifier.staticModifier = "static"; + return ScriptElementKindModifier; + })(); + ts.ScriptElementKindModifier = ScriptElementKindModifier; + var ClassificationTypeNames = (function () { + function ClassificationTypeNames() { + } + ClassificationTypeNames.comment = "comment"; + ClassificationTypeNames.identifier = "identifier"; + ClassificationTypeNames.keyword = "keyword"; + ClassificationTypeNames.numericLiteral = "number"; + ClassificationTypeNames.operator = "operator"; + ClassificationTypeNames.stringLiteral = "string"; + ClassificationTypeNames.whiteSpace = "whitespace"; + ClassificationTypeNames.text = "text"; + ClassificationTypeNames.punctuation = "punctuation"; + ClassificationTypeNames.className = "class name"; + ClassificationTypeNames.enumName = "enum name"; + ClassificationTypeNames.interfaceName = "interface name"; + ClassificationTypeNames.moduleName = "module name"; + ClassificationTypeNames.typeParameterName = "type parameter name"; + ClassificationTypeNames.typeAlias = "type alias name"; + return ClassificationTypeNames; + })(); + ts.ClassificationTypeNames = ClassificationTypeNames; + var MatchKind; + (function (MatchKind) { + MatchKind[MatchKind["none"] = 0] = "none"; + MatchKind[MatchKind["exact"] = 1] = "exact"; + MatchKind[MatchKind["substring"] = 2] = "substring"; + MatchKind[MatchKind["prefix"] = 3] = "prefix"; + })(MatchKind || (MatchKind = {})); + function displayPartsToString(displayParts) { + if (displayParts) { + return ts.map(displayParts, function (displayPart) { return displayPart.text; }).join(""); + } + return ""; + } + ts.displayPartsToString = displayPartsToString; + function isLocalVariableOrFunction(symbol) { + if (symbol.parent) { + return false; // This is exported symbol + } + return ts.forEach(symbol.declarations, function (declaration) { + // Function expressions are local + if (declaration.kind === ts.SyntaxKind.FunctionExpression) { + return true; + } + if (declaration.kind !== ts.SyntaxKind.VariableDeclaration && declaration.kind !== ts.SyntaxKind.FunctionDeclaration) { + return false; + } + for (var parent = declaration.parent; !ts.isFunctionBlock(parent); parent = parent.parent) { + // Reached source file or module block + if (parent.kind === ts.SyntaxKind.SourceFile || parent.kind === ts.SyntaxKind.ModuleBlock) { + return false; + } + } + // parent is in function block + return true; + }); + } + function getDefaultCompilerOptions() { + // Set "ScriptTarget.Latest" target by default for language service + return { + target: ts.ScriptTarget.Latest, + module: ts.ModuleKind.None, + }; + } + ts.getDefaultCompilerOptions = getDefaultCompilerOptions; + var OperationCanceledException = (function () { + function OperationCanceledException() { + } + return OperationCanceledException; + })(); + ts.OperationCanceledException = OperationCanceledException; + var CancellationTokenObject = (function () { + function CancellationTokenObject(cancellationToken) { + this.cancellationToken = cancellationToken; + } + CancellationTokenObject.prototype.isCancellationRequested = function () { + return this.cancellationToken && this.cancellationToken.isCancellationRequested(); + }; + CancellationTokenObject.prototype.throwIfCancellationRequested = function () { + if (this.isCancellationRequested()) { + throw new OperationCanceledException(); + } + }; + CancellationTokenObject.None = new CancellationTokenObject(null); + return CancellationTokenObject; + })(); + ts.CancellationTokenObject = CancellationTokenObject; + // 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. + var HostCache = (function () { + function HostCache(host) { + this.host = host; + // 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[ts.normalizeSlashes(filename)] = { + filename: filename, + version: host.getScriptVersion(filename) + }; + } + this._compilationSettings = host.getCompilationSettings() || getDefaultCompilerOptions(); + } + HostCache.prototype.compilationSettings = function () { + return this._compilationSettings; + }; + HostCache.prototype.getEntry = function (filename) { + filename = ts.normalizeSlashes(filename); + return ts.lookUp(this.filenameToEntry, filename); + }; + HostCache.prototype.contains = function (filename) { + return !!this.getEntry(filename); + }; + HostCache.prototype.getHostfilename = function (filename) { + var hostCacheEntry = this.getEntry(filename); + if (hostCacheEntry) { + return hostCacheEntry.filename; + } + return filename; + }; + HostCache.prototype.getFilenames = function () { + var _this = this; + var fileNames = []; + ts.forEachKey(this.filenameToEntry, function (key) { + if (ts.hasProperty(_this.filenameToEntry, key)) + fileNames.push(key); + }); + return fileNames; + }; + HostCache.prototype.getVersion = function (filename) { + return this.getEntry(filename).version; + }; + HostCache.prototype.getScriptSnapshot = function (filename) { + var file = this.getEntry(filename); + if (!file.sourceText) { + file.sourceText = this.host.getScriptSnapshot(file.filename); + } + return file.sourceText; + }; + HostCache.prototype.getChangeRange = function (filename, lastKnownVersion, oldScriptSnapshot) { + var currentVersion = this.getVersion(filename); + if (lastKnownVersion === currentVersion) { + return ts.unchangedTextChangeRange; // "No changes" + } + var scriptSnapshot = this.getScriptSnapshot(filename); + return scriptSnapshot.getChangeRange(oldScriptSnapshot); + }; + return HostCache; + })(); + var SyntaxTreeCache = (function () { + function SyntaxTreeCache(host) { + this.host = host; + // For our syntactic only features, we also keep a cache of the syntax tree for the + // currently edited file. + this.currentFilename = ""; + this.currentFileVersion = null; + this.currentSourceFile = null; + } + SyntaxTreeCache.prototype.log = function (message) { + if (this.host.log) { + this.host.log(message); + } + }; + SyntaxTreeCache.prototype.initialize = function (filename) { + // ensure that both source file and syntax tree are either initialized or not initialized + var start = new Date().getTime(); + this.hostCache = new HostCache(this.host); + this.log("SyntaxTreeCache.Initialize: new HostCache: " + (new Date().getTime() - start)); + var version = this.hostCache.getVersion(filename); + var sourceFile; + if (this.currentFilename !== filename) { + var scriptSnapshot = this.hostCache.getScriptSnapshot(filename); + var start = new Date().getTime(); + sourceFile = createLanguageServiceSourceFile(filename, scriptSnapshot, ts.ScriptTarget.Latest, version, true); + this.log("SyntaxTreeCache.Initialize: createSourceFile: " + (new Date().getTime() - start)); + } + else if (this.currentFileVersion !== version) { + var scriptSnapshot = this.hostCache.getScriptSnapshot(filename); + var editRange = this.hostCache.getChangeRange(filename, this.currentFileVersion, this.currentSourceFile.scriptSnapshot); + var start = new Date().getTime(); + sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange); + this.log("SyntaxTreeCache.Initialize: updateSourceFile: " + (new Date().getTime() - start)); + } + if (sourceFile) { + // All done, ensure state is up to date + this.currentFileVersion = version; + this.currentFilename = filename; + this.currentSourceFile = sourceFile; + } + }; + SyntaxTreeCache.prototype.getCurrentSourceFile = function (filename) { + this.initialize(filename); + return this.currentSourceFile; + }; + SyntaxTreeCache.prototype.getCurrentScriptSnapshot = function (filename) { + return this.getCurrentSourceFile(filename).scriptSnapshot; + }; + return SyntaxTreeCache; + })(); + function setSourceFileFields(sourceFile, scriptSnapshot, version) { + sourceFile.version = version; + sourceFile.scriptSnapshot = scriptSnapshot; + } + function createLanguageServiceSourceFile(filename, scriptSnapshot, scriptTarget, version, setNodeParents) { + var sourceFile = ts.createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), scriptTarget, setNodeParents); + setSourceFileFields(sourceFile, scriptSnapshot, version); + return sourceFile; + } + ts.createLanguageServiceSourceFile = createLanguageServiceSourceFile; + ts.disableIncrementalParsing = false; + function updateLanguageServiceSourceFile(sourceFile, scriptSnapshot, version, textChangeRange) { + if (textChangeRange && ts.Debug.shouldAssert(ts.AssertionLevel.Normal)) { + var oldText = sourceFile.scriptSnapshot; + var newText = scriptSnapshot; + ts.Debug.assert((oldText.getLength() - textChangeRange.span.length + textChangeRange.newLength) === newText.getLength()); + if (ts.Debug.shouldAssert(ts.AssertionLevel.VeryAggressive)) { + var oldTextPrefix = oldText.getText(0, textChangeRange.span.start); + var newTextPrefix = newText.getText(0, textChangeRange.span.start); + ts.Debug.assert(oldTextPrefix === newTextPrefix); + var oldTextSuffix = oldText.getText(ts.textSpanEnd(textChangeRange.span), oldText.getLength()); + var newTextSuffix = newText.getText(ts.textSpanEnd(ts.textChangeRangeNewSpan(textChangeRange)), newText.getLength()); + ts.Debug.assert(oldTextSuffix === newTextSuffix); + } + } + // If we were given a text change range, and our version or open-ness changed, then + // incrementally parse this file. + if (textChangeRange) { + if (version !== sourceFile.version) { + // Once incremental parsing is ready, then just call into this function. + if (!ts.disableIncrementalParsing) { + var newSourceFile = sourceFile.update(scriptSnapshot.getText(0, scriptSnapshot.getLength()), textChangeRange); + setSourceFileFields(newSourceFile, scriptSnapshot, version); + return newSourceFile; + } + } + } + // Otherwise, just create a new source file. + return createLanguageServiceSourceFile(sourceFile.filename, scriptSnapshot, sourceFile.languageVersion, version, true); + } + ts.updateLanguageServiceSourceFile = updateLanguageServiceSourceFile; + function createDocumentRegistry() { + var buckets = {}; + function getKeyFromCompilationSettings(settings) { + return "_" + settings.target; // + "|" + settings.propagateEnumConstantoString() + } + function getBucketForCompilationSettings(settings, createIfMissing) { + var key = getKeyFromCompilationSettings(settings); + var bucket = ts.lookUp(buckets, key); + if (!bucket && createIfMissing) { + buckets[key] = bucket = {}; + } + return bucket; + } + function reportStats() { + var bucketInfoArray = Object.keys(buckets).filter(function (name) { return name && name.charAt(0) === '_'; }).map(function (name) { + var entries = ts.lookUp(buckets, name); + var sourceFiles = []; + for (var i in entries) { + var entry = entries[i]; + sourceFiles.push({ + name: i, + refCount: entry.refCount, + references: entry.owners.slice(0) + }); + } + sourceFiles.sort(function (x, y) { return y.refCount - x.refCount; }); + return { + bucket: name, + sourceFiles + }; + }); + return JSON.stringify(bucketInfoArray, null, 2); + } + function acquireDocument(filename, compilationSettings, scriptSnapshot, version) { + var bucket = getBucketForCompilationSettings(compilationSettings, true); + var entry = ts.lookUp(bucket, filename); + if (!entry) { + var sourceFile = createLanguageServiceSourceFile(filename, scriptSnapshot, compilationSettings.target, version, false); + bucket[filename] = entry = { + sourceFile: sourceFile, + refCount: 0, + owners: [] + }; + } + entry.refCount++; + return entry.sourceFile; + } + function updateDocument(sourceFile, filename, compilationSettings, scriptSnapshot, version, textChangeRange) { + var bucket = getBucketForCompilationSettings(compilationSettings, false); + ts.Debug.assert(bucket !== undefined); + var entry = ts.lookUp(bucket, filename); + ts.Debug.assert(entry !== undefined); + entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, textChangeRange); + return entry.sourceFile; + } + function releaseDocument(filename, compilationSettings) { + var bucket = getBucketForCompilationSettings(compilationSettings, false); + ts.Debug.assert(bucket !== undefined); + var entry = ts.lookUp(bucket, filename); + entry.refCount--; + ts.Debug.assert(entry.refCount >= 0); + if (entry.refCount === 0) { + delete bucket[filename]; + } + } + return { + acquireDocument, + updateDocument, + releaseDocument, + reportStats + }; + } + ts.createDocumentRegistry = createDocumentRegistry; + function preProcessFile(sourceText, readImportFiles = true) { + var referencedFiles = []; + var importedFiles = []; + var isNoDefaultLib = false; + function processTripleSlashDirectives() { + var commentRanges = ts.getLeadingCommentRanges(sourceText, 0); + ts.forEach(commentRanges, function (commentRange) { + var comment = sourceText.substring(commentRange.pos, commentRange.end); + var referencePathMatchResult = ts.getFileReferenceFromReferencePath(comment, commentRange); + if (referencePathMatchResult) { + isNoDefaultLib = referencePathMatchResult.isNoDefaultLib; + var fileReference = referencePathMatchResult.fileReference; + if (fileReference) { + referencedFiles.push(fileReference); + } + } + }); + } + function processImport() { + scanner.setText(sourceText); + var token = scanner.scan(); + while (token !== ts.SyntaxKind.EndOfFileToken) { + if (token === ts.SyntaxKind.ImportKeyword) { + token = scanner.scan(); + if (token === ts.SyntaxKind.Identifier) { + token = scanner.scan(); + if (token === ts.SyntaxKind.EqualsToken) { + token = scanner.scan(); + if (token === ts.SyntaxKind.RequireKeyword) { + token = scanner.scan(); + if (token === ts.SyntaxKind.OpenParenToken) { + token = scanner.scan(); + if (token === ts.SyntaxKind.StringLiteral) { + var importPath = scanner.getTokenValue(); + var pos = scanner.getTokenPos(); + importedFiles.push({ + filename: importPath, + pos: pos, + end: pos + importPath.length + }); + } + } + } + } + } + } + token = scanner.scan(); + } + scanner.setText(undefined); + } + if (readImportFiles) { + processImport(); + } + processTripleSlashDirectives(); + return { referencedFiles, importedFiles, isLibFile: isNoDefaultLib }; + } + ts.preProcessFile = preProcessFile; + /// Helpers + function getTargetLabel(referenceNode, labelName) { + while (referenceNode) { + if (referenceNode.kind === ts.SyntaxKind.LabeledStatement && referenceNode.label.text === labelName) { + return referenceNode.label; + } + referenceNode = referenceNode.parent; + } + return undefined; + } + function isJumpStatementTarget(node) { + return node.kind === ts.SyntaxKind.Identifier && (node.parent.kind === ts.SyntaxKind.BreakStatement || node.parent.kind === ts.SyntaxKind.ContinueStatement) && node.parent.label === node; + } + function isLabelOfLabeledStatement(node) { + return node.kind === ts.SyntaxKind.Identifier && node.parent.kind === ts.SyntaxKind.LabeledStatement && node.parent.label === node; + } + /** + * Whether or not a 'node' is preceded by a label of the given string. + * Note: 'node' cannot be a SourceFile. + */ + function isLabeledBy(node, labelName) { + for (var owner = node.parent; owner.kind === ts.SyntaxKind.LabeledStatement; owner = owner.parent) { + if (owner.label.text === labelName) { + return true; + } + } + return false; + } + function isLabelName(node) { + return isLabelOfLabeledStatement(node) || isJumpStatementTarget(node); + } + function isRightSideOfQualifiedName(node) { + return node.parent.kind === ts.SyntaxKind.QualifiedName && node.parent.right === node; + } + function isRightSideOfPropertyAccess(node) { + return node && node.parent && node.parent.kind === ts.SyntaxKind.PropertyAccessExpression && node.parent.name === node; + } + function isCallExpressionTarget(node) { + if (isRightSideOfPropertyAccess(node)) { + node = node.parent; + } + return node && node.parent && node.parent.kind === ts.SyntaxKind.CallExpression && node.parent.expression === node; + } + function isNewExpressionTarget(node) { + if (isRightSideOfPropertyAccess(node)) { + node = node.parent; + } + return node && node.parent && node.parent.kind === ts.SyntaxKind.NewExpression && node.parent.expression === node; + } + function isNameOfModuleDeclaration(node) { + return node.parent.kind === ts.SyntaxKind.ModuleDeclaration && node.parent.name === node; + } + function isNameOfFunctionDeclaration(node) { + return node.kind === ts.SyntaxKind.Identifier && ts.isAnyFunction(node.parent) && node.parent.name === node; + } + /** Returns true if node is a name of an object literal property, e.g. "a" in x = { "a": 1 } */ + function isNameOfPropertyAssignment(node) { + return (node.kind === ts.SyntaxKind.Identifier || node.kind === ts.SyntaxKind.StringLiteral || node.kind === ts.SyntaxKind.NumericLiteral) && (node.parent.kind === ts.SyntaxKind.PropertyAssignment || node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) && node.parent.name === node; + } + function isLiteralNameOfPropertyDeclarationOrIndexAccess(node) { + if (node.kind === ts.SyntaxKind.StringLiteral || node.kind === ts.SyntaxKind.NumericLiteral) { + switch (node.parent.kind) { + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + case ts.SyntaxKind.PropertyAssignment: + case ts.SyntaxKind.EnumMember: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.ModuleDeclaration: + return node.parent.name === node; + case ts.SyntaxKind.ElementAccessExpression: + return node.parent.argumentExpression === node; + } + } + return false; + } + function isNameOfExternalModuleImportOrDeclaration(node) { + if (node.kind === ts.SyntaxKind.StringLiteral) { + return isNameOfModuleDeclaration(node) || (ts.isExternalModuleImportDeclaration(node.parent.parent) && ts.getExternalModuleImportDeclarationExpression(node.parent.parent) === node); + } + return false; + } + /** Returns true if the position is within a comment */ + function isInsideComment(sourceFile, token, position) { + // The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment + return position <= token.getStart(sourceFile) && (isInsideCommentRange(ts.getTrailingCommentRanges(sourceFile.text, token.getFullStart())) || isInsideCommentRange(ts.getLeadingCommentRanges(sourceFile.text, token.getFullStart()))); + function isInsideCommentRange(comments) { + return ts.forEach(comments, function (comment) { + // either we are 1. completely inside the comment, or 2. at the end of the comment + if (comment.pos < position && position < comment.end) { + return true; + } + else if (position === comment.end) { + var text = sourceFile.text; + var width = comment.end - comment.pos; + // is single line comment or just /* + if (width <= 2 || text.charCodeAt(comment.pos + 1) === ts.CharacterCodes.slash) { + return true; + } + else { + // is unterminated multi-line comment + return !(text.charCodeAt(comment.end - 1) === ts.CharacterCodes.slash && text.charCodeAt(comment.end - 2) === ts.CharacterCodes.asterisk); + } + } + return false; + }); + } + } + // A cache of completion entries for keywords, these do not change between sessions + var keywordCompletions = []; + for (var i = ts.SyntaxKind.FirstKeyword; i <= ts.SyntaxKind.LastKeyword; i++) { + keywordCompletions.push({ + name: ts.tokenToString(i), + kind: ScriptElementKind.keyword, + kindModifiers: ScriptElementKindModifier.none + }); + } + function createLanguageService(host, documentRegistry) { + var syntaxTreeCache = new SyntaxTreeCache(host); + var ruleProvider; + var hostCache; // A cache of all the information about the files on the host side. + var program; + // this checker is used to answer all LS questions except errors + var typeInfoResolver; + var useCaseSensitivefilenames = false; + var sourceFilesByName = {}; + var documentRegistry = documentRegistry; + var cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken()); + var activeCompletionSession; // 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 (!ts.localizedDiagnosticMessages && host.getLocalizedDiagnosticMessages) { + ts.localizedDiagnosticMessages = host.getLocalizedDiagnosticMessages(); + } + function log(message) { + if (host.log) { + host.log(message); + } + } + function getCanonicalFileName(filename) { + return useCaseSensitivefilenames ? filename : filename.toLowerCase(); + } + function getSourceFile(filename) { + return ts.lookUp(sourceFilesByName, getCanonicalFileName(filename)); + } + function getDiagnosticsProducingTypeChecker() { + return program.getTypeChecker(true); + } + function getRuleProvider(options) { + // Ensure rules are initialized and up to date wrt to formatting options + if (!ruleProvider) { + ruleProvider = new ts.formatting.RulesProvider(); + } + ruleProvider.ensureUpToDate(options); + return ruleProvider; + } + function sourceFileUpToDate(sourceFile) { + return sourceFile && sourceFile.version === hostCache.getVersion(sourceFile.filename); + } + function programUpToDate() { + // If we haven't create a program yet, then it is not up-to-date + if (!program) { + return false; + } + // If number of files in the program do not match, it is not up-to-date + var hostFilenames = hostCache.getFilenames(); + if (program.getSourceFiles().length !== hostFilenames.length) { + return false; + } + for (var i = 0, n = hostFilenames.length; i < n; i++) { + if (!sourceFileUpToDate(program.getSourceFile(hostFilenames[i]))) { + return false; + } + } + // If the compilation settings do no match, then the program is not up-to-date + return ts.compareDataObjects(program.getCompilerOptions(), hostCache.compilationSettings()); + } + function synchronizeHostData() { + // Reset the cache at start of every refresh + hostCache = new HostCache(host); + // If the program is already up-to-date, we can reuse it + if (programUpToDate()) { + return; + } + var compilationSettings = hostCache.compilationSettings(); + // Now, remove any files from the compiler that are no longer in the host. + var oldProgram = program; + if (oldProgram) { + var oldSettings = program.getCompilerOptions(); + // 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 && !ts.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 sourceFilesByName[getCanonicalFileName(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 scriptSnapshot = hostCache.getScriptSnapshot(filename); + var sourceFile = getSourceFile(filename); + if (sourceFile) { + // + // If the sourceFile is the same, assume no update + // + if (sourceFileUpToDate(sourceFile)) { + continue; + } + var textChangeRange = null; + textChangeRange = hostCache.getChangeRange(filename, sourceFile.version, sourceFile.scriptSnapshot); + sourceFile = documentRegistry.updateDocument(sourceFile, filename, compilationSettings, scriptSnapshot, version, textChangeRange); + } + else { + sourceFile = documentRegistry.acquireDocument(filename, compilationSettings, scriptSnapshot, version); + } + // Remember the new sourceFile + sourceFilesByName[getCanonicalFileName(filename)] = sourceFile; + } + // Now create a new compiler + program = ts.createProgram(hostfilenames, compilationSettings, { + getSourceFile: getSourceFile, + getCancellationToken: function () { return cancellationToken; }, + getCanonicalFileName: function (filename) { return useCaseSensitivefilenames ? filename : filename.toLowerCase(); }, + useCaseSensitiveFileNames: function () { return useCaseSensitivefilenames; }, + getNewLine: function () { return host.getNewLine ? host.getNewLine() : "\r\n"; }, + getDefaultLibFilename: ts.getDefaultLibraryFilename, + writeFile: function (filename, data, writeByteOrderMark) { + }, + getCurrentDirectory: function () { return host.getCurrentDirectory(); } + }); + typeInfoResolver = program.getTypeChecker(false); + } + /** + * Clean up any semantic caches that are not needed. + * The host can call this method if it wants to jettison unused memory. + * We will just dump the typeChecker and recreate a new one. this should have the effect of destroying all the semantic caches. + */ + function cleanupSemanticCache() { + if (program) { + typeInfoResolver = program.getTypeChecker(false); + } + } + function dispose() { + if (program) { + ts.forEach(program.getSourceFiles(), function (f) { + documentRegistry.releaseDocument(f.filename, program.getCompilerOptions()); + }); + } + } + /// Diagnostics + function getSyntacticDiagnostics(filename) { + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + return program.getDiagnostics(getSourceFile(filename)); + } + /** + * getSemanticDiagnostiscs return array of Diagnostics. If '-d' is not enabled, only report semantic errors + * If '-d' enabled, report both semantic and emitter errors + */ + function getSemanticDiagnostics(filename) { + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + var compilerOptions = program.getCompilerOptions(); + var checker = getDiagnosticsProducingTypeChecker(); + var targetSourceFile = getSourceFile(filename); + // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file. + // Therefore only get diagnostics for given file. + var allDiagnostics = checker.getDiagnostics(targetSourceFile); + if (compilerOptions.declaration) { + // If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface + allDiagnostics = allDiagnostics.concat(program.getDeclarationDiagnostics(targetSourceFile)); + } + return allDiagnostics; + } + function getCompilerOptionsDiagnostics() { + synchronizeHostData(); + return program.getGlobalDiagnostics(); + } + /// Completion + function getValidCompletionEntryDisplayName(symbol, target) { + var displayName = symbol.getName(); + if (displayName && displayName.length > 0) { + var firstCharCode = displayName.charCodeAt(0); + // First check of the displayName is not external module; if it is an external module, it is not valid entry + if ((symbol.flags & ts.SymbolFlags.Namespace) && (firstCharCode === ts.CharacterCodes.singleQuote || firstCharCode === ts.CharacterCodes.doubleQuote)) { + // If the symbol is external module, don't show it in the completion list + // (i.e declare module "http" { var x; } | // <= request completion here, "http" should not be there) + return undefined; + } + if (displayName && displayName.length >= 2 && firstCharCode === displayName.charCodeAt(displayName.length - 1) && (firstCharCode === ts.CharacterCodes.singleQuote || firstCharCode === ts.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 identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name. + displayName = displayName.substring(1, displayName.length - 1); + } + var isValid = ts.isIdentifierStart(displayName.charCodeAt(0), target); + for (var i = 1, n = displayName.length; isValid && i < n; i++) { + isValid = ts.isIdentifierPart(displayName.charCodeAt(i), target); + } + if (isValid) { + return ts.unescapeIdentifier(displayName); + } + } + return undefined; + } + function createCompletionEntry(symbol, typeChecker, location) { + // 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, program.getCompilerOptions().target); + if (!displayName) { + return undefined; + } + // TODO(drosen): Right now we just permit *all* semantic meanings when calling 'getSymbolKind' + // which is permissible given that it is backwards compatible; but really we should consider + // passing the meaning for the node so that we don't report that a suggestion for a value is an interface. + // We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration. + return { + name: displayName, + kind: getSymbolKind(symbol, typeChecker, location), + kindModifiers: getSymbolModifiers(symbol) + }; + } + function getCompletionsAtPosition(filename, position) { + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + var syntacticStart = new Date().getTime(); + var sourceFile = getSourceFile(filename); + var start = new Date().getTime(); + var currentToken = ts.getTokenAtPosition(sourceFile, position); + log("getCompletionsAtPosition: Get current token: " + (new Date().getTime() - start)); + var start = new Date().getTime(); + // Completion not allowed inside comments, bail out if this is the case + var insideComment = isInsideComment(sourceFile, currentToken, position); + log("getCompletionsAtPosition: Is inside comment: " + (new Date().getTime() - start)); + if (insideComment) { + log("Returning an empty list because completion was inside a comment."); + return undefined; + } + // The decision to provide completion depends on the previous token, so find it + // Note: previousToken can be undefined if we are the beginning of the file + var start = new Date().getTime(); + var previousToken = ts.findPrecedingToken(position, sourceFile); + log("getCompletionsAtPosition: Get previous token 1: " + (new Date().getTime() - start)); + // The caret is at the end of an identifier; this is a partial identifier that we want to complete: e.g. a.toS| + // Skip this partial identifier to the previous token + if (previousToken && position <= previousToken.end && previousToken.kind === ts.SyntaxKind.Identifier) { + var start = new Date().getTime(); + previousToken = ts.findPrecedingToken(previousToken.pos, sourceFile); + log("getCompletionsAtPosition: Get previous token 2: " + (new Date().getTime() - start)); + } + // Check if this is a valid completion location + if (previousToken && isCompletionListBlocker(previousToken)) { + log("Returning an empty list because completion was requested in an invalid position."); + return undefined; + } + // Find the node where completion is requested on, in the case of a completion after a dot, it is the member access expression + // other wise, it is a request for all visible symbols in the scope, and the node is the current location + var node; + var isRightOfDot; + if (previousToken && previousToken.kind === ts.SyntaxKind.DotToken && previousToken.parent.kind === ts.SyntaxKind.PropertyAccessExpression) { + node = previousToken.parent.expression; + isRightOfDot = true; + } + else if (previousToken && previousToken.kind === ts.SyntaxKind.DotToken && previousToken.parent.kind === ts.SyntaxKind.QualifiedName) { + node = previousToken.parent.left; + isRightOfDot = true; + } + else { + node = currentToken; + isRightOfDot = false; + } + // Clear the current activeCompletionSession for this session + activeCompletionSession = { + filename: filename, + position: position, + entries: [], + symbols: {}, + typeChecker: typeInfoResolver + }; + log("getCompletionsAtPosition: Syntactic work: " + (new Date().getTime() - syntacticStart)); + var location = ts.getTouchingPropertyName(sourceFile, position); + // Populate the completion list + var semanticStart = new Date().getTime(); + if (isRightOfDot) { + // Right of dot member completion list + var symbols = []; + var isMemberCompletion = true; + if (node.kind === ts.SyntaxKind.Identifier || node.kind === ts.SyntaxKind.QualifiedName || node.kind === ts.SyntaxKind.PropertyAccessExpression) { + var symbol = typeInfoResolver.getSymbolAtLocation(node); + // This is an alias, follow what it aliases + if (symbol && symbol.flags & ts.SymbolFlags.Import) { + symbol = typeInfoResolver.getAliasedSymbol(symbol); + } + if (symbol && symbol.flags & ts.SymbolFlags.HasExports) { + // Extract module or enum members + ts.forEachValue(symbol.exports, function (symbol) { + if (typeInfoResolver.isValidPropertyAccess((node.parent), symbol.name)) { + symbols.push(symbol); + } + }); + } + } + var type = typeInfoResolver.getTypeAtLocation(node); + if (type) { + // Filter private properties + ts.forEach(type.getApparentProperties(), function (symbol) { + if (typeInfoResolver.isValidPropertyAccess((node.parent), symbol.name)) { + symbols.push(symbol); + } + }); + } + getCompletionEntriesFromSymbols(symbols, activeCompletionSession); + } + else { + var containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(previousToken); + if (containingObjectLiteral) { + // Object literal expression, look up possible property names from contextual type + isMemberCompletion = true; + var contextualType = typeInfoResolver.getContextualType(containingObjectLiteral); + if (!contextualType) { + return undefined; + } + var contextualTypeMembers = typeInfoResolver.getPropertiesOfType(contextualType); + if (contextualTypeMembers && contextualTypeMembers.length > 0) { + // Add filtered items to the completion list + var filteredMembers = filterContextualMembersList(contextualTypeMembers, containingObjectLiteral.properties); + getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession); + } + } + else { + // Get scope members + isMemberCompletion = false; + /// TODO filter meaning based on the current context + var symbolMeanings = ts.SymbolFlags.Type | ts.SymbolFlags.Value | ts.SymbolFlags.Namespace | ts.SymbolFlags.Import; + var symbols = typeInfoResolver.getSymbolsInScope(node, symbolMeanings); + getCompletionEntriesFromSymbols(symbols, activeCompletionSession); + } + } + // Add keywords if this is not a member completion list + if (!isMemberCompletion) { + Array.prototype.push.apply(activeCompletionSession.entries, keywordCompletions); + } + log("getCompletionsAtPosition: Semantic work: " + (new Date().getTime() - semanticStart)); + return { + isMemberCompletion, + entries: activeCompletionSession.entries + }; + function getCompletionEntriesFromSymbols(symbols, session) { + var start = new Date().getTime(); + ts.forEach(symbols, function (symbol) { + var entry = createCompletionEntry(symbol, session.typeChecker, location); + if (entry) { + var id = ts.escapeIdentifier(entry.name); + if (!ts.lookUp(session.symbols, id)) { + session.entries.push(entry); + session.symbols[id] = symbol; + } + } + }); + log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start)); + } + function isCompletionListBlocker(previousToken) { + var start = new Date().getTime(); + var result = isInStringOrRegularExpressionOrTemplateLiteral(previousToken) || isIdentifierDefinitionLocation(previousToken) || isRightOfIllegalDot(previousToken); + log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start)); + return result; + } + function isInStringOrRegularExpressionOrTemplateLiteral(previousToken) { + if (previousToken.kind === ts.SyntaxKind.StringLiteral || previousToken.kind === ts.SyntaxKind.RegularExpressionLiteral || ts.isTemplateLiteralKind(previousToken.kind)) { + // The position has to be either: 1. entirely within the token text, or + // 2. at the end position of an unterminated token. + var start = previousToken.getStart(); + var end = previousToken.getEnd(); + if (start < position && position < end) { + return true; + } + else if (position === end) { + return !!previousToken.isUnterminated; + } + } + return false; + } + function getContainingObjectLiteralApplicableForCompletion(previousToken) { + // The locations in an object literal expression that are applicable for completion are property name definition locations. + if (previousToken) { + var parent = previousToken.parent; + switch (previousToken.kind) { + case ts.SyntaxKind.OpenBraceToken: + case ts.SyntaxKind.CommaToken: + if (parent && parent.kind === ts.SyntaxKind.ObjectLiteralExpression) { + return parent; + } + break; + } + } + return undefined; + } + function isFunction(kind) { + switch (kind) { + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.CallSignature: + case ts.SyntaxKind.ConstructSignature: + case ts.SyntaxKind.IndexSignature: + return true; + } + return false; + } + function isIdentifierDefinitionLocation(previousToken) { + if (previousToken) { + var containingNodeKind = previousToken.parent.kind; + switch (previousToken.kind) { + case ts.SyntaxKind.CommaToken: + return containingNodeKind === ts.SyntaxKind.VariableDeclaration || containingNodeKind === ts.SyntaxKind.VariableDeclarationList || containingNodeKind === ts.SyntaxKind.VariableStatement || containingNodeKind === ts.SyntaxKind.EnumDeclaration || isFunction(containingNodeKind); + case ts.SyntaxKind.OpenParenToken: + return containingNodeKind === ts.SyntaxKind.CatchClause || isFunction(containingNodeKind); + case ts.SyntaxKind.OpenBraceToken: + return containingNodeKind === ts.SyntaxKind.EnumDeclaration || containingNodeKind === ts.SyntaxKind.InterfaceDeclaration; + case ts.SyntaxKind.SemicolonToken: + return containingNodeKind === ts.SyntaxKind.PropertySignature && previousToken.parent.parent.kind === ts.SyntaxKind.InterfaceDeclaration; + case ts.SyntaxKind.PublicKeyword: + case ts.SyntaxKind.PrivateKeyword: + case ts.SyntaxKind.StaticKeyword: + case ts.SyntaxKind.DotDotDotToken: + return containingNodeKind === ts.SyntaxKind.Parameter; + case ts.SyntaxKind.ClassKeyword: + case ts.SyntaxKind.ModuleKeyword: + case ts.SyntaxKind.EnumKeyword: + case ts.SyntaxKind.InterfaceKeyword: + case ts.SyntaxKind.FunctionKeyword: + case ts.SyntaxKind.VarKeyword: + case ts.SyntaxKind.GetKeyword: + case ts.SyntaxKind.SetKeyword: + case ts.SyntaxKind.ImportKeyword: + return true; + } + switch (previousToken.getText()) { + case "class": + case "interface": + case "enum": + case "module": + case "function": + case "var": + // TODO: add let and const + return true; + } + } + return false; + } + function isRightOfIllegalDot(previousToken) { + if (previousToken && previousToken.kind === ts.SyntaxKind.NumericLiteral) { + var text = previousToken.getFullText(); + return text.charAt(text.length - 1) === "."; + } + return false; + } + function filterContextualMembersList(contextualMemberSymbols, existingMembers) { + if (!existingMembers || existingMembers.length === 0) { + return contextualMemberSymbols; + } + var existingMemberNames = {}; + ts.forEach(existingMembers, function (m) { + if (m.kind !== ts.SyntaxKind.PropertyAssignment && m.kind !== ts.SyntaxKind.ShorthandPropertyAssignment) { + // Ignore omitted expressions for missing members in the object literal + return; + } + if (m.getStart() <= position && position <= m.getEnd()) { + // If this is the current item we are editing right now, do not filter it out + return; + } + // TODO(jfreeman): Account for computed property name + existingMemberNames[m.name.text] = true; + }); + var filteredMembers = []; + ts.forEach(contextualMemberSymbols, function (s) { + if (!existingMemberNames[s.name]) { + filteredMembers.push(s); + } + }); + return filteredMembers; + } + } + function getCompletionEntryDetails(filename, position, entryName) { + // Note: No need to call synchronizeHostData, as we have captured all the data we need + // in the getCompletionsAtPosition earlier + filename = ts.normalizeSlashes(filename); + var sourceFile = getSourceFile(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 = ts.lookUp(activeCompletionSession.symbols, ts.escapeIdentifier(entryName)); + if (symbol) { + var location = ts.getTouchingPropertyName(sourceFile, position); + var completionEntry = createCompletionEntry(symbol, session.typeChecker, location); + // TODO(drosen): Right now we just permit *all* semantic meanings when calling 'getSymbolKind' + // which is permissible given that it is backwards compatible; but really we should consider + // passing the meaning for the node so that we don't report that a suggestion for a value is an interface. + // We COULD also just do what 'getSymbolModifiers' does, which is to use the first declaration. + ts.Debug.assert(session.typeChecker.getTypeOfSymbolAtLocation(symbol, location) !== undefined, "Could not find type for symbol"); + var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, 7 /* All */); + return { + name: entryName, + kind: displayPartsDocumentationsAndSymbolKind.symbolKind, + kindModifiers: completionEntry.kindModifiers, + displayParts: displayPartsDocumentationsAndSymbolKind.displayParts, + documentation: displayPartsDocumentationsAndSymbolKind.documentation + }; + } + else { + // No symbol, it is a keyword + return { + name: entryName, + kind: ScriptElementKind.keyword, + kindModifiers: ScriptElementKindModifier.none, + displayParts: [ts.displayPart(entryName, 5 /* keyword */)], + documentation: undefined + }; + } + } + function getContainerNode(node) { + while (true) { + node = node.parent; + if (!node) { + return undefined; + } + switch (node.kind) { + case ts.SyntaxKind.SourceFile: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ModuleDeclaration: + return node; + } + } + } + // TODO(drosen): use contextual SemanticMeaning. + function getSymbolKind(symbol, typeResolver, location) { + var flags = symbol.getFlags(); + if (flags & ts.SymbolFlags.Class) + return ScriptElementKind.classElement; + if (flags & ts.SymbolFlags.Enum) + return ScriptElementKind.enumElement; + if (flags & ts.SymbolFlags.TypeAlias) + return ScriptElementKind.typeElement; + if (flags & ts.SymbolFlags.Interface) + return ScriptElementKind.interfaceElement; + if (flags & ts.SymbolFlags.TypeParameter) + return ScriptElementKind.typeParameterElement; + var result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, flags, typeResolver, location); + if (result === ScriptElementKind.unknown) { + if (flags & ts.SymbolFlags.TypeParameter) + return ScriptElementKind.typeParameterElement; + if (flags & ts.SymbolFlags.EnumMember) + return ScriptElementKind.variableElement; + if (flags & ts.SymbolFlags.Import) + return ScriptElementKind.alias; + if (flags & ts.SymbolFlags.Module) + return ScriptElementKind.moduleElement; + } + return result; + } + function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, flags, typeResolver, location) { + if (typeResolver.isUndefinedSymbol(symbol)) { + return ScriptElementKind.variableElement; + } + if (typeResolver.isArgumentsSymbol(symbol)) { + return ScriptElementKind.localVariableElement; + } + if (flags & ts.SymbolFlags.Variable) { + if (ts.isFirstDeclarationOfSymbolParameter(symbol)) { + return ScriptElementKind.parameterElement; + } + else if (symbol.valueDeclaration && ts.isConst(symbol.valueDeclaration)) { + return ScriptElementKind.constElement; + } + else if (ts.forEach(symbol.declarations, ts.isLet)) { + return ScriptElementKind.letElement; + } + return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localVariableElement : ScriptElementKind.variableElement; + } + if (flags & ts.SymbolFlags.Function) + return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localFunctionElement : ScriptElementKind.functionElement; + if (flags & ts.SymbolFlags.GetAccessor) + return ScriptElementKind.memberGetAccessorElement; + if (flags & ts.SymbolFlags.SetAccessor) + return ScriptElementKind.memberSetAccessorElement; + if (flags & ts.SymbolFlags.Method) + return ScriptElementKind.memberFunctionElement; + if (flags & ts.SymbolFlags.Constructor) + return ScriptElementKind.constructorImplementationElement; + if (flags & ts.SymbolFlags.Property) { + if (flags & ts.SymbolFlags.UnionProperty) { + // If union property is result of union of non method (property/accessors/variables), it is labeled as property + var unionPropertyKind = ts.forEach(typeInfoResolver.getRootSymbols(symbol), function (rootSymbol) { + var rootSymbolFlags = rootSymbol.getFlags(); + if (rootSymbolFlags & (ts.SymbolFlags.PropertyOrAccessor | ts.SymbolFlags.Variable)) { + return ScriptElementKind.memberVariableElement; + } + ts.Debug.assert(!!(rootSymbolFlags & ts.SymbolFlags.Method)); + }); + if (!unionPropertyKind) { + // If this was union of all methods, + //make sure it has call signatures before we can label it as method + var typeOfUnionProperty = typeInfoResolver.getTypeOfSymbolAtLocation(symbol, location); + if (typeOfUnionProperty.getCallSignatures().length) { + return ScriptElementKind.memberFunctionElement; + } + return ScriptElementKind.memberVariableElement; + } + return unionPropertyKind; + } + return ScriptElementKind.memberVariableElement; + } + return ScriptElementKind.unknown; + } + function getTypeKind(type) { + var flags = type.getFlags(); + if (flags & ts.TypeFlags.Enum) + return ScriptElementKind.enumElement; + if (flags & ts.TypeFlags.Class) + return ScriptElementKind.classElement; + if (flags & ts.TypeFlags.Interface) + return ScriptElementKind.interfaceElement; + if (flags & ts.TypeFlags.TypeParameter) + return ScriptElementKind.typeParameterElement; + if (flags & ts.TypeFlags.Intrinsic) + return ScriptElementKind.primitiveType; + if (flags & ts.TypeFlags.StringLiteral) + return ScriptElementKind.primitiveType; + return ScriptElementKind.unknown; + } + function getNodeKind(node) { + switch (node.kind) { + case ts.SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement; + case ts.SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement; + case ts.SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement; + case ts.SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement; + case ts.SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement; + case ts.SyntaxKind.VariableDeclaration: + return ts.isConst(node) ? ScriptElementKind.constElement : ts.isLet(node) ? ScriptElementKind.letElement : ScriptElementKind.variableElement; + case ts.SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement; + case ts.SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement; + case ts.SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement; + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + return ScriptElementKind.memberFunctionElement; + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + return ScriptElementKind.memberVariableElement; + case ts.SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement; + case ts.SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement; + case ts.SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement; + case ts.SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement; + case ts.SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement; + case ts.SyntaxKind.EnumMember: return ScriptElementKind.variableElement; + case ts.SyntaxKind.Parameter: return (node.flags & ts.NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement; + } + return ScriptElementKind.unknown; + } + function getSymbolModifiers(symbol) { + return symbol && symbol.declarations && symbol.declarations.length > 0 ? ts.getNodeModifiers(symbol.declarations[0]) : ScriptElementKindModifier.none; + } + function getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, sourceFile, enclosingDeclaration, typeResolver, location, + // TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location + semanticMeaning = getMeaningFromLocation(location)) { + var displayParts = []; + var documentation; + var symbolFlags = symbol.flags; + var symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, symbolFlags, typeResolver, location); + var hasAddedSymbolInfo; + // Class at constructor site need to be shown as constructor apart from property,method, vars + if (symbolKind !== ScriptElementKind.unknown || symbolFlags & ts.SymbolFlags.Class || symbolFlags & ts.SymbolFlags.Import) { + // If it is accessor they are allowed only if location is at name of the accessor + if (symbolKind === ScriptElementKind.memberGetAccessorElement || symbolKind === ScriptElementKind.memberSetAccessorElement) { + symbolKind = ScriptElementKind.memberVariableElement; + } + var type = typeResolver.getTypeOfSymbolAtLocation(symbol, location); + if (type) { + if (location.parent && location.parent.kind === ts.SyntaxKind.PropertyAccessExpression) { + var right = location.parent.name; + // Either the location is on the right of a property access, or on the left and the right is missing + if (right === location || (right && right.getFullWidth() === 0)) { + location = location.parent; + } + } + // try get the call/construct signature from the type if it matches + var callExpression; + if (location.kind === ts.SyntaxKind.CallExpression || location.kind === ts.SyntaxKind.NewExpression) { + callExpression = location; + } + else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { + callExpression = location.parent; + } + if (callExpression) { + var candidateSignatures = []; + signature = typeResolver.getResolvedSignature(callExpression, candidateSignatures); + if (!signature && candidateSignatures.length) { + // Use the first candidate: + signature = candidateSignatures[0]; + } + var useConstructSignatures = callExpression.kind === ts.SyntaxKind.NewExpression || callExpression.expression.kind === ts.SyntaxKind.SuperKeyword; + var allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); + if (!ts.contains(allSignatures, signature.target || signature)) { + // Get the first signature if there + signature = allSignatures.length ? allSignatures[0] : undefined; + } + if (signature) { + if (useConstructSignatures && (symbolFlags & ts.SymbolFlags.Class)) { + // Constructor + symbolKind = ScriptElementKind.constructorImplementationElement; + addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); + } + else if (symbolFlags & ts.SymbolFlags.Import) { + symbolKind = ScriptElementKind.alias; + displayParts.push(ts.punctuationPart(ts.SyntaxKind.OpenParenToken)); + displayParts.push(ts.textPart(symbolKind)); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.CloseParenToken)); + displayParts.push(ts.spacePart()); + if (useConstructSignatures) { + displayParts.push(ts.keywordPart(ts.SyntaxKind.NewKeyword)); + displayParts.push(ts.spacePart()); + } + addFullSymbolName(symbol); + } + else { + addPrefixForAnyFunctionOrVar(symbol, symbolKind); + } + switch (symbolKind) { + case ScriptElementKind.memberVariableElement: + case ScriptElementKind.variableElement: + case ScriptElementKind.constElement: + case ScriptElementKind.letElement: + case ScriptElementKind.parameterElement: + case ScriptElementKind.localVariableElement: + // If it is call or construct signature of lambda's write type name + displayParts.push(ts.punctuationPart(ts.SyntaxKind.ColonToken)); + displayParts.push(ts.spacePart()); + if (useConstructSignatures) { + displayParts.push(ts.keywordPart(ts.SyntaxKind.NewKeyword)); + displayParts.push(ts.spacePart()); + } + if (!(type.flags & ts.TypeFlags.Anonymous)) { + displayParts.push.apply(displayParts, ts.symbolToDisplayParts(typeResolver, type.symbol, enclosingDeclaration, undefined, ts.SymbolFormatFlags.WriteTypeParametersOrArguments)); + } + addSignatureDisplayParts(signature, allSignatures, ts.TypeFormatFlags.WriteArrowStyleSignature); + break; + default: + // Just signature + addSignatureDisplayParts(signature, allSignatures); + } + hasAddedSymbolInfo = true; + } + } + else if ((isNameOfFunctionDeclaration(location) && !(symbol.flags & ts.SymbolFlags.Accessor)) || (location.kind === ts.SyntaxKind.ConstructorKeyword && location.parent.kind === ts.SyntaxKind.Constructor)) { + // get the signature from the declaration and write it + var signature; + var functionDeclaration = location.parent; + var allSignatures = functionDeclaration.kind === ts.SyntaxKind.Constructor ? type.getConstructSignatures() : type.getCallSignatures(); + if (!typeResolver.isImplementationOfOverload(functionDeclaration)) { + signature = typeResolver.getSignatureFromDeclaration(functionDeclaration); + } + else { + signature = allSignatures[0]; + } + if (functionDeclaration.kind === ts.SyntaxKind.Constructor) { + // show (constructor) Type(...) signature + symbolKind = ScriptElementKind.constructorImplementationElement; + addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); + } + else { + // (function/method) symbol(..signature) + addPrefixForAnyFunctionOrVar(functionDeclaration.kind === ts.SyntaxKind.CallSignature && !(type.symbol.flags & ts.SymbolFlags.TypeLiteral || type.symbol.flags & ts.SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); + } + addSignatureDisplayParts(signature, allSignatures); + hasAddedSymbolInfo = true; + } + } + } + if (symbolFlags & ts.SymbolFlags.Class && !hasAddedSymbolInfo) { + displayParts.push(ts.keywordPart(ts.SyntaxKind.ClassKeyword)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + writeTypeParametersOfSymbol(symbol, sourceFile); + } + if ((symbolFlags & ts.SymbolFlags.Interface) && (semanticMeaning & 2 /* Type */)) { + addNewLineIfDisplayPartsExist(); + displayParts.push(ts.keywordPart(ts.SyntaxKind.InterfaceKeyword)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + writeTypeParametersOfSymbol(symbol, sourceFile); + } + if (symbolFlags & ts.SymbolFlags.TypeAlias) { + addNewLineIfDisplayPartsExist(); + displayParts.push(ts.keywordPart(ts.SyntaxKind.TypeKeyword)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + displayParts.push(ts.spacePart()); + displayParts.push(ts.operatorPart(ts.SyntaxKind.EqualsToken)); + displayParts.push(ts.spacePart()); + displayParts.push.apply(displayParts, ts.typeToDisplayParts(typeResolver, typeResolver.getDeclaredTypeOfSymbol(symbol), enclosingDeclaration)); + } + if (symbolFlags & ts.SymbolFlags.Enum) { + addNewLineIfDisplayPartsExist(); + if (ts.forEach(symbol.declarations, ts.isConstEnumDeclaration)) { + displayParts.push(ts.keywordPart(ts.SyntaxKind.ConstKeyword)); + displayParts.push(ts.spacePart()); + } + displayParts.push(ts.keywordPart(ts.SyntaxKind.EnumKeyword)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + } + if (symbolFlags & ts.SymbolFlags.Module) { + addNewLineIfDisplayPartsExist(); + displayParts.push(ts.keywordPart(ts.SyntaxKind.ModuleKeyword)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + } + if ((symbolFlags & ts.SymbolFlags.TypeParameter) && (semanticMeaning & 2 /* Type */)) { + addNewLineIfDisplayPartsExist(); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.OpenParenToken)); + displayParts.push(ts.textPart("type parameter")); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.CloseParenToken)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + displayParts.push(ts.spacePart()); + displayParts.push(ts.keywordPart(ts.SyntaxKind.InKeyword)); + displayParts.push(ts.spacePart()); + if (symbol.parent) { + // Class/Interface type parameter + addFullSymbolName(symbol.parent, enclosingDeclaration); + writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration); + } + else { + // Method/function type parameter + var signatureDeclaration = ts.getDeclarationOfKind(symbol, ts.SyntaxKind.TypeParameter).parent; + var signature = typeResolver.getSignatureFromDeclaration(signatureDeclaration); + if (signatureDeclaration.kind === ts.SyntaxKind.ConstructSignature) { + displayParts.push(ts.keywordPart(ts.SyntaxKind.NewKeyword)); + displayParts.push(ts.spacePart()); + } + else if (signatureDeclaration.kind !== ts.SyntaxKind.CallSignature && signatureDeclaration.name) { + addFullSymbolName(signatureDeclaration.symbol); + } + displayParts.push.apply(displayParts, ts.signatureToDisplayParts(typeResolver, signature, sourceFile, ts.TypeFormatFlags.WriteTypeArgumentsOfSignature)); + } + } + if (symbolFlags & ts.SymbolFlags.EnumMember) { + addPrefixForAnyFunctionOrVar(symbol, "enum member"); + var declaration = symbol.declarations[0]; + if (declaration.kind === ts.SyntaxKind.EnumMember) { + var constantValue = typeResolver.getEnumMemberValue(declaration); + if (constantValue !== undefined) { + displayParts.push(ts.spacePart()); + displayParts.push(ts.operatorPart(ts.SyntaxKind.EqualsToken)); + displayParts.push(ts.spacePart()); + displayParts.push(ts.displayPart(constantValue.toString(), 7 /* numericLiteral */)); + } + } + } + if (symbolFlags & ts.SymbolFlags.Import) { + addNewLineIfDisplayPartsExist(); + displayParts.push(ts.keywordPart(ts.SyntaxKind.ImportKeyword)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + ts.forEach(symbol.declarations, function (declaration) { + if (declaration.kind === ts.SyntaxKind.ImportDeclaration) { + var importDeclaration = declaration; + if (ts.isExternalModuleImportDeclaration(importDeclaration)) { + displayParts.push(ts.spacePart()); + displayParts.push(ts.operatorPart(ts.SyntaxKind.EqualsToken)); + displayParts.push(ts.spacePart()); + displayParts.push(ts.keywordPart(ts.SyntaxKind.RequireKeyword)); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.OpenParenToken)); + displayParts.push(ts.displayPart(ts.getTextOfNode(ts.getExternalModuleImportDeclarationExpression(importDeclaration)), 8 /* stringLiteral */)); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.CloseParenToken)); + } + else { + var internalAliasSymbol = typeResolver.getSymbolAtLocation(importDeclaration.moduleReference); + if (internalAliasSymbol) { + displayParts.push(ts.spacePart()); + displayParts.push(ts.operatorPart(ts.SyntaxKind.EqualsToken)); + displayParts.push(ts.spacePart()); + addFullSymbolName(internalAliasSymbol, enclosingDeclaration); + } + } + return true; + } + }); + } + if (!hasAddedSymbolInfo) { + if (symbolKind !== ScriptElementKind.unknown) { + if (type) { + addPrefixForAnyFunctionOrVar(symbol, symbolKind); + // For properties, variables and local vars: show the type + if (symbolKind === ScriptElementKind.memberVariableElement || symbolFlags & ts.SymbolFlags.Variable || symbolKind === ScriptElementKind.localVariableElement) { + displayParts.push(ts.punctuationPart(ts.SyntaxKind.ColonToken)); + displayParts.push(ts.spacePart()); + // If the type is type parameter, format it specially + if (type.symbol && type.symbol.flags & ts.SymbolFlags.TypeParameter) { + var typeParameterParts = ts.mapToDisplayParts(function (writer) { + typeResolver.getSymbolDisplayBuilder().buildTypeParameterDisplay(type, writer, enclosingDeclaration); + }); + displayParts.push.apply(displayParts, typeParameterParts); + } + else { + displayParts.push.apply(displayParts, ts.typeToDisplayParts(typeResolver, type, enclosingDeclaration)); + } + } + else if (symbolFlags & ts.SymbolFlags.Function || symbolFlags & ts.SymbolFlags.Method || symbolFlags & ts.SymbolFlags.Constructor || symbolFlags & ts.SymbolFlags.Signature || symbolFlags & ts.SymbolFlags.Accessor || symbolKind === ScriptElementKind.memberFunctionElement) { + var allSignatures = type.getCallSignatures(); + addSignatureDisplayParts(allSignatures[0], allSignatures); + } + } + } + else { + symbolKind = getSymbolKind(symbol, typeResolver, location); + } + } + if (!documentation) { + documentation = symbol.getDocumentationComment(); + } + return { displayParts, documentation, symbolKind }; + function addNewLineIfDisplayPartsExist() { + if (displayParts.length) { + displayParts.push(ts.lineBreakPart()); + } + } + function addFullSymbolName(symbol, enclosingDeclaration) { + var fullSymbolDisplayParts = ts.symbolToDisplayParts(typeResolver, symbol, enclosingDeclaration || sourceFile, undefined, ts.SymbolFormatFlags.WriteTypeParametersOrArguments | ts.SymbolFormatFlags.UseOnlyExternalAliasing); + displayParts.push.apply(displayParts, fullSymbolDisplayParts); + } + function addPrefixForAnyFunctionOrVar(symbol, symbolKind) { + addNewLineIfDisplayPartsExist(); + if (symbolKind) { + displayParts.push(ts.punctuationPart(ts.SyntaxKind.OpenParenToken)); + displayParts.push(ts.textPart(symbolKind)); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.CloseParenToken)); + displayParts.push(ts.spacePart()); + addFullSymbolName(symbol); + } + } + function addSignatureDisplayParts(signature, allSignatures, flags) { + displayParts.push.apply(displayParts, ts.signatureToDisplayParts(typeResolver, signature, enclosingDeclaration, flags | ts.TypeFormatFlags.WriteTypeArgumentsOfSignature)); + if (allSignatures.length > 1) { + displayParts.push(ts.spacePart()); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.OpenParenToken)); + displayParts.push(ts.operatorPart(ts.SyntaxKind.PlusToken)); + displayParts.push(ts.displayPart((allSignatures.length - 1).toString(), 7 /* numericLiteral */)); + displayParts.push(ts.spacePart()); + displayParts.push(ts.textPart(allSignatures.length === 2 ? "overload" : "overloads")); + displayParts.push(ts.punctuationPart(ts.SyntaxKind.CloseParenToken)); + } + documentation = signature.getDocumentationComment(); + } + function writeTypeParametersOfSymbol(symbol, enclosingDeclaration) { + var typeParameterParts = ts.mapToDisplayParts(function (writer) { + typeResolver.getSymbolDisplayBuilder().buildTypeParameterDisplayFromSymbol(symbol, writer, enclosingDeclaration); + }); + displayParts.push.apply(displayParts, typeParameterParts); + } + } + function getQuickInfoAtPosition(fileName, position) { + synchronizeHostData(); + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getSourceFile(fileName); + var node = ts.getTouchingPropertyName(sourceFile, position); + if (!node) { + return undefined; + } + var symbol = typeInfoResolver.getSymbolAtLocation(node); + if (!symbol) { + switch (node.kind) { + case ts.SyntaxKind.Identifier: + case ts.SyntaxKind.PropertyAccessExpression: + case ts.SyntaxKind.QualifiedName: + case ts.SyntaxKind.ThisKeyword: + case ts.SyntaxKind.SuperKeyword: + // For the identifiers/this/super etc get the type at position + var type = typeInfoResolver.getTypeAtLocation(node); + if (type) { + return { + kind: ScriptElementKind.unknown, + kindModifiers: ScriptElementKindModifier.none, + textSpan: ts.createTextSpan(node.getStart(), node.getWidth()), + displayParts: ts.typeToDisplayParts(typeInfoResolver, type, getContainerNode(node)), + documentation: type.symbol ? type.symbol.getDocumentationComment() : undefined + }; + } + } + return undefined; + } + var displayPartsDocumentationsAndKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, sourceFile, getContainerNode(node), typeInfoResolver, node); + return { + kind: displayPartsDocumentationsAndKind.symbolKind, + kindModifiers: getSymbolModifiers(symbol), + textSpan: ts.createTextSpan(node.getStart(), node.getWidth()), + displayParts: displayPartsDocumentationsAndKind.displayParts, + documentation: displayPartsDocumentationsAndKind.documentation + }; + } + /// Goto definition + function getDefinitionAtPosition(filename, position) { + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + var sourceFile = getSourceFile(filename); + var node = ts.getTouchingPropertyName(sourceFile, position); + if (!node) { + return undefined; + } + // Labels + if (isJumpStatementTarget(node)) { + var labelName = node.text; + var label = getTargetLabel(node.parent, node.text); + return label ? [getDefinitionInfo(label, ScriptElementKind.label, labelName, undefined)] : undefined; + } + /// Triple slash reference comments + var comment = ts.forEach(sourceFile.referencedFiles, function (r) { return (r.pos <= position && position < r.end) ? r : undefined; }); + if (comment) { + var referenceFile = ts.tryResolveScriptReference(program, sourceFile, comment); + if (referenceFile) { + return [{ + fileName: referenceFile.filename, + textSpan: ts.createTextSpanFromBounds(0, 0), + kind: ScriptElementKind.scriptElement, + name: comment.filename, + containerName: undefined, + containerKind: undefined + }]; + } + return undefined; + } + var symbol = typeInfoResolver.getSymbolAtLocation(node); + // Could not find a symbol e.g. node is string or number keyword, + // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol + if (!symbol) { + return undefined; + } + var result = []; + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { + var shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + var shorthandDeclarations = shorthandSymbol.getDeclarations(); + var shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver, node); + var shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); + var shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); + ts.forEach(shorthandDeclarations, function (declaration) { + result.push(getDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); + }); + return result; + } + var declarations = symbol.getDeclarations(); + var symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol + var symbolKind = getSymbolKind(symbol, typeInfoResolver, node); + var containerSymbol = symbol.parent; + var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; + if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { + // Just add all the declarations. + ts.forEach(declarations, function (declaration) { + result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); + }); + } + return result; + function getDefinitionInfo(node, symbolKind, symbolName, containerName) { + return { + fileName: node.getSourceFile().filename, + textSpan: ts.createTextSpanFromBounds(node.getStart(), node.getEnd()), + kind: symbolKind, + name: symbolName, + containerKind: undefined, + containerName + }; + } + function tryAddSignature(signatureDeclarations, selectConstructors, symbolKind, symbolName, containerName, result) { + var declarations = []; + var definition; + ts.forEach(signatureDeclarations, function (d) { + if ((selectConstructors && d.kind === ts.SyntaxKind.Constructor) || (!selectConstructors && (d.kind === ts.SyntaxKind.FunctionDeclaration || d.kind === ts.SyntaxKind.MethodDeclaration || d.kind === ts.SyntaxKind.MethodSignature))) { + declarations.push(d); + if (d.body) + definition = d; + } + }); + if (definition) { + result.push(getDefinitionInfo(definition, symbolKind, symbolName, containerName)); + return true; + } + else if (declarations.length) { + result.push(getDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); + return true; + } + return false; + } + function tryAddConstructSignature(symbol, location, symbolKind, symbolName, containerName, result) { + // Applicable only if we are in a new expression, or we are on a constructor declaration + // and in either case the symbol has a construct signature definition, i.e. class + if (isNewExpressionTarget(location) || location.kind === ts.SyntaxKind.ConstructorKeyword) { + if (symbol.flags & ts.SymbolFlags.Class) { + var classDeclaration = symbol.getDeclarations()[0]; + ts.Debug.assert(classDeclaration && classDeclaration.kind === ts.SyntaxKind.ClassDeclaration); + return tryAddSignature(classDeclaration.members, true, symbolKind, symbolName, containerName, result); + } + } + return false; + } + function tryAddCallSignature(symbol, location, symbolKind, symbolName, containerName, result) { + if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) { + return tryAddSignature(symbol.declarations, false, symbolKind, symbolName, containerName, result); + } + return false; + } + } + /// References and Occurrences + function getOccurrencesAtPosition(filename, position) { + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + var sourceFile = getSourceFile(filename); + var node = ts.getTouchingWord(sourceFile, position); + if (!node) { + return undefined; + } + if (node.kind === ts.SyntaxKind.Identifier || node.kind === ts.SyntaxKind.ThisKeyword || node.kind === ts.SyntaxKind.SuperKeyword || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) { + return getReferencesForNode(node, [sourceFile], false, false); + } + switch (node.kind) { + case ts.SyntaxKind.IfKeyword: + case ts.SyntaxKind.ElseKeyword: + if (hasKind(node.parent, ts.SyntaxKind.IfStatement)) { + return getIfElseOccurrences(node.parent); + } + break; + case ts.SyntaxKind.ReturnKeyword: + if (hasKind(node.parent, ts.SyntaxKind.ReturnStatement)) { + return getReturnOccurrences(node.parent); + } + break; + case ts.SyntaxKind.ThrowKeyword: + if (hasKind(node.parent, ts.SyntaxKind.ThrowStatement)) { + return getThrowOccurrences(node.parent); + } + break; + case ts.SyntaxKind.CatchKeyword: + if (hasKind(parent(parent(node)), ts.SyntaxKind.TryStatement)) { + return getTryCatchFinallyOccurrences(node.parent.parent); + } + break; + case ts.SyntaxKind.TryKeyword: + case ts.SyntaxKind.FinallyKeyword: + if (hasKind(parent(node), ts.SyntaxKind.TryStatement)) { + return getTryCatchFinallyOccurrences(node.parent); + } + break; + case ts.SyntaxKind.SwitchKeyword: + if (hasKind(node.parent, ts.SyntaxKind.SwitchStatement)) { + return getSwitchCaseDefaultOccurrences(node.parent); + } + break; + case ts.SyntaxKind.CaseKeyword: + case ts.SyntaxKind.DefaultKeyword: + if (hasKind(parent(parent(node)), ts.SyntaxKind.SwitchStatement)) { + return getSwitchCaseDefaultOccurrences(node.parent.parent); + } + break; + case ts.SyntaxKind.BreakKeyword: + case ts.SyntaxKind.ContinueKeyword: + if (hasKind(node.parent, ts.SyntaxKind.BreakStatement) || hasKind(node.parent, ts.SyntaxKind.ContinueStatement)) { + return getBreakOrContinueStatementOccurences(node.parent); + } + break; + case ts.SyntaxKind.ForKeyword: + if (hasKind(node.parent, ts.SyntaxKind.ForStatement) || hasKind(node.parent, ts.SyntaxKind.ForInStatement)) { + return getLoopBreakContinueOccurrences(node.parent); + } + break; + case ts.SyntaxKind.WhileKeyword: + case ts.SyntaxKind.DoKeyword: + if (hasKind(node.parent, ts.SyntaxKind.WhileStatement) || hasKind(node.parent, ts.SyntaxKind.DoStatement)) { + return getLoopBreakContinueOccurrences(node.parent); + } + break; + case ts.SyntaxKind.ConstructorKeyword: + if (hasKind(node.parent, ts.SyntaxKind.Constructor)) { + return getConstructorOccurrences(node.parent); + } + break; + case ts.SyntaxKind.GetKeyword: + case ts.SyntaxKind.SetKeyword: + if (hasKind(node.parent, ts.SyntaxKind.GetAccessor) || hasKind(node.parent, ts.SyntaxKind.SetAccessor)) { + return getGetAndSetOccurrences(node.parent); + } + default: + if (ts.isModifier(node.kind) && node.parent && (ts.isDeclaration(node.parent) || node.parent.kind === ts.SyntaxKind.VariableStatement)) { + return getModifierOccurrences(node.kind, node.parent); + } + } + return undefined; + function getIfElseOccurrences(ifStatement) { + var keywords = []; + while (hasKind(ifStatement.parent, ts.SyntaxKind.IfStatement) && ifStatement.parent.elseStatement === ifStatement) { + ifStatement = ifStatement.parent; + } + while (ifStatement) { + var children = ifStatement.getChildren(); + pushKeywordIf(keywords, children[0], ts.SyntaxKind.IfKeyword); + for (var i = children.length - 1; i >= 0; i--) { + if (pushKeywordIf(keywords, children[i], ts.SyntaxKind.ElseKeyword)) { + break; + } + } + if (!hasKind(ifStatement.elseStatement, ts.SyntaxKind.IfStatement)) { + break; + } + ifStatement = ifStatement.elseStatement; + } + var result = []; + for (var i = 0; i < keywords.length; i++) { + if (keywords[i].kind === ts.SyntaxKind.ElseKeyword && i < keywords.length - 1) { + var elseKeyword = keywords[i]; + var ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword. + var shouldHighlightNextKeyword = true; + for (var j = ifKeyword.getStart() - 1; j >= elseKeyword.end; j--) { + if (!ts.isWhiteSpace(sourceFile.text.charCodeAt(j))) { + shouldHighlightNextKeyword = false; + break; + } + } + if (shouldHighlightNextKeyword) { + result.push({ + fileName: filename, + textSpan: ts.createTextSpanFromBounds(elseKeyword.getStart(), ifKeyword.end), + isWriteAccess: false + }); + i++; // skip the next keyword + continue; + } + } + // Ordinary case: just highlight the keyword. + result.push(getReferenceEntryFromNode(keywords[i])); + } + return result; + } + function getReturnOccurrences(returnStatement) { + var func = ts.getContainingFunction(returnStatement); + // If we didn't find a containing function with a block body, bail out. + if (!(func && hasKind(func.body, ts.SyntaxKind.Block))) { + return undefined; + } + var keywords = []; + ts.forEachReturnStatement(func.body, function (returnStatement) { + pushKeywordIf(keywords, returnStatement.getFirstToken(), ts.SyntaxKind.ReturnKeyword); + }); + // Include 'throw' statements that do not occur within a try block. + ts.forEach(aggregateOwnedThrowStatements(func.body), function (throwStatement) { + pushKeywordIf(keywords, throwStatement.getFirstToken(), ts.SyntaxKind.ThrowKeyword); + }); + return ts.map(keywords, getReferenceEntryFromNode); + } + function getThrowOccurrences(throwStatement) { + var owner = getThrowStatementOwner(throwStatement); + if (!owner) { + return undefined; + } + var keywords = []; + ts.forEach(aggregateOwnedThrowStatements(owner), function (throwStatement) { + pushKeywordIf(keywords, throwStatement.getFirstToken(), ts.SyntaxKind.ThrowKeyword); + }); + // If the "owner" is a function, then we equate 'return' and 'throw' statements in their + // ability to "jump out" of the function, and include occurrences for both. + if (ts.isFunctionBlock(owner)) { + ts.forEachReturnStatement(owner, function (returnStatement) { + pushKeywordIf(keywords, returnStatement.getFirstToken(), ts.SyntaxKind.ReturnKeyword); + }); + } + return ts.map(keywords, getReferenceEntryFromNode); + } + /** + * Aggregates all throw-statements within this node *without* crossing + * into function boundaries and try-blocks with catch-clauses. + */ + function aggregateOwnedThrowStatements(node) { + var statementAccumulator = []; + aggregate(node); + return statementAccumulator; + function aggregate(node) { + if (node.kind === ts.SyntaxKind.ThrowStatement) { + statementAccumulator.push(node); + } + else if (node.kind === ts.SyntaxKind.TryStatement) { + var tryStatement = node; + if (tryStatement.catchClause) { + aggregate(tryStatement.catchClause); + } + else { + // Exceptions thrown within a try block lacking a catch clause + // are "owned" in the current context. + aggregate(tryStatement.tryBlock); + } + if (tryStatement.finallyBlock) { + aggregate(tryStatement.finallyBlock); + } + } + else if (!ts.isAnyFunction(node)) { + ts.forEachChild(node, aggregate); + } + } + ; + } + /** + * For lack of a better name, this function takes a throw statement and returns the + * nearest ancestor that is a try-block (whose try statement has a catch clause), + * function-block, or source file. + */ + function getThrowStatementOwner(throwStatement) { + var child = throwStatement; + while (child.parent) { + var parent = child.parent; + if (ts.isFunctionBlock(parent) || parent.kind === ts.SyntaxKind.SourceFile) { + return parent; + } + // A throw-statement is only owned by a try-statement if the try-statement has + // a catch clause, and if the throw-statement occurs within the try block. + if (parent.kind === ts.SyntaxKind.TryStatement) { + var tryStatement = parent; + if (tryStatement.tryBlock === child && tryStatement.catchClause) { + return child; + } + } + child = parent; + } + return undefined; + } + function getTryCatchFinallyOccurrences(tryStatement) { + var keywords = []; + pushKeywordIf(keywords, tryStatement.getFirstToken(), ts.SyntaxKind.TryKeyword); + if (tryStatement.catchClause) { + pushKeywordIf(keywords, tryStatement.catchClause.getFirstToken(), ts.SyntaxKind.CatchKeyword); + } + if (tryStatement.finallyBlock) { + var finallyKeyword = ts.findChildOfKind(tryStatement, ts.SyntaxKind.FinallyKeyword, sourceFile); + pushKeywordIf(keywords, finallyKeyword, ts.SyntaxKind.FinallyKeyword); + } + return ts.map(keywords, getReferenceEntryFromNode); + } + function getLoopBreakContinueOccurrences(loopNode) { + var keywords = []; + if (pushKeywordIf(keywords, loopNode.getFirstToken(), ts.SyntaxKind.ForKeyword, ts.SyntaxKind.WhileKeyword, ts.SyntaxKind.DoKeyword)) { + // If we succeeded and got a do-while loop, then start looking for a 'while' keyword. + if (loopNode.kind === ts.SyntaxKind.DoStatement) { + var loopTokens = loopNode.getChildren(); + for (var i = loopTokens.length - 1; i >= 0; i--) { + if (pushKeywordIf(keywords, loopTokens[i], ts.SyntaxKind.WhileKeyword)) { + break; + } + } + } + } + var breaksAndContinues = aggregateAllBreakAndContinueStatements(loopNode.statement); + ts.forEach(breaksAndContinues, function (statement) { + if (ownsBreakOrContinueStatement(loopNode, statement)) { + pushKeywordIf(keywords, statement.getFirstToken(), ts.SyntaxKind.BreakKeyword, ts.SyntaxKind.ContinueKeyword); + } + }); + return ts.map(keywords, getReferenceEntryFromNode); + } + function getSwitchCaseDefaultOccurrences(switchStatement) { + var keywords = []; + pushKeywordIf(keywords, switchStatement.getFirstToken(), ts.SyntaxKind.SwitchKeyword); + // Go through each clause in the switch statement, collecting the 'case'/'default' keywords. + ts.forEach(switchStatement.clauses, function (clause) { + pushKeywordIf(keywords, clause.getFirstToken(), ts.SyntaxKind.CaseKeyword, ts.SyntaxKind.DefaultKeyword); + var breaksAndContinues = aggregateAllBreakAndContinueStatements(clause); + ts.forEach(breaksAndContinues, function (statement) { + if (ownsBreakOrContinueStatement(switchStatement, statement)) { + pushKeywordIf(keywords, statement.getFirstToken(), ts.SyntaxKind.BreakKeyword); + } + }); + }); + return ts.map(keywords, getReferenceEntryFromNode); + } + function getBreakOrContinueStatementOccurences(breakOrContinueStatement) { + var owner = getBreakOrContinueOwner(breakOrContinueStatement); + if (owner) { + switch (owner.kind) { + case ts.SyntaxKind.ForStatement: + case ts.SyntaxKind.ForInStatement: + case ts.SyntaxKind.DoStatement: + case ts.SyntaxKind.WhileStatement: + return getLoopBreakContinueOccurrences(owner); + case ts.SyntaxKind.SwitchStatement: + return getSwitchCaseDefaultOccurrences(owner); + } + } + return undefined; + } + function aggregateAllBreakAndContinueStatements(node) { + var statementAccumulator = []; + aggregate(node); + return statementAccumulator; + function aggregate(node) { + if (node.kind === ts.SyntaxKind.BreakStatement || node.kind === ts.SyntaxKind.ContinueStatement) { + statementAccumulator.push(node); + } + else if (!ts.isAnyFunction(node)) { + ts.forEachChild(node, aggregate); + } + } + ; + } + function ownsBreakOrContinueStatement(owner, statement) { + var actualOwner = getBreakOrContinueOwner(statement); + return actualOwner && actualOwner === owner; + } + function getBreakOrContinueOwner(statement) { + for (var node = statement.parent; node; node = node.parent) { + switch (node.kind) { + case ts.SyntaxKind.SwitchStatement: + if (statement.kind === ts.SyntaxKind.ContinueStatement) { + continue; + } + case ts.SyntaxKind.ForStatement: + case ts.SyntaxKind.ForInStatement: + case ts.SyntaxKind.WhileStatement: + case ts.SyntaxKind.DoStatement: + if (!statement.label || isLabeledBy(node, statement.label.text)) { + return node; + } + break; + default: + // Don't cross function boundaries. + if (ts.isAnyFunction(node)) { + return undefined; + } + break; + } + } + return undefined; + } + function getConstructorOccurrences(constructorDeclaration) { + var declarations = constructorDeclaration.symbol.getDeclarations(); + var keywords = []; + ts.forEach(declarations, function (declaration) { + ts.forEach(declaration.getChildren(), function (token) { + return pushKeywordIf(keywords, token, ts.SyntaxKind.ConstructorKeyword); + }); + }); + return ts.map(keywords, getReferenceEntryFromNode); + } + function getGetAndSetOccurrences(accessorDeclaration) { + var keywords = []; + tryPushAccessorKeyword(accessorDeclaration.symbol, ts.SyntaxKind.GetAccessor); + tryPushAccessorKeyword(accessorDeclaration.symbol, ts.SyntaxKind.SetAccessor); + return ts.map(keywords, getReferenceEntryFromNode); + function tryPushAccessorKeyword(accessorSymbol, accessorKind) { + var accessor = ts.getDeclarationOfKind(accessorSymbol, accessorKind); + if (accessor) { + ts.forEach(accessor.getChildren(), function (child) { return pushKeywordIf(keywords, child, ts.SyntaxKind.GetKeyword, ts.SyntaxKind.SetKeyword); }); + } + } + } + function getModifierOccurrences(modifier, declaration) { + var container = declaration.parent; + // Make sure we only highlight the keyword when it makes sense to do so. + if (declaration.flags & ts.NodeFlags.AccessibilityModifier) { + if (!(container.kind === ts.SyntaxKind.ClassDeclaration || (declaration.kind === ts.SyntaxKind.Parameter && hasKind(container, ts.SyntaxKind.Constructor)))) { + return undefined; + } + } + else if (declaration.flags & ts.NodeFlags.Static) { + if (container.kind !== ts.SyntaxKind.ClassDeclaration) { + return undefined; + } + } + else if (declaration.flags & (ts.NodeFlags.Export | ts.NodeFlags.Ambient)) { + if (!(container.kind === ts.SyntaxKind.ModuleBlock || container.kind === ts.SyntaxKind.SourceFile)) { + return undefined; + } + } + else { + // unsupported modifier + return undefined; + } + var keywords = []; + var modifierFlag = getFlagFromModifier(modifier); + var nodes; + switch (container.kind) { + case ts.SyntaxKind.ModuleBlock: + case ts.SyntaxKind.SourceFile: + nodes = container.statements; + break; + case ts.SyntaxKind.Constructor: + nodes = container.parameters.concat(container.parent.members); + break; + case ts.SyntaxKind.ClassDeclaration: + nodes = container.members; + // If we're an accessibility modifier, we're in an instance member and should search + // the constructor's parameter list for instance members as well. + if (modifierFlag & ts.NodeFlags.AccessibilityModifier) { + var constructor = ts.forEach(container.members, function (member) { + return member.kind === ts.SyntaxKind.Constructor && member; + }); + if (constructor) { + nodes = nodes.concat(constructor.parameters); + } + } + break; + default: + ts.Debug.fail("Invalid container kind."); + } + ts.forEach(nodes, function (node) { + if (node.modifiers && node.flags & modifierFlag) { + ts.forEach(node.modifiers, function (child) { return pushKeywordIf(keywords, child, modifier); }); + } + }); + return ts.map(keywords, getReferenceEntryFromNode); + function getFlagFromModifier(modifier) { + switch (modifier) { + case ts.SyntaxKind.PublicKeyword: + return ts.NodeFlags.Public; + case ts.SyntaxKind.PrivateKeyword: + return ts.NodeFlags.Private; + case ts.SyntaxKind.ProtectedKeyword: + return ts.NodeFlags.Protected; + case ts.SyntaxKind.StaticKeyword: + return ts.NodeFlags.Static; + case ts.SyntaxKind.ExportKeyword: + return ts.NodeFlags.Export; + case ts.SyntaxKind.DeclareKeyword: + return ts.NodeFlags.Ambient; + default: + ts.Debug.fail(); + } + } + } + // returns true if 'node' is defined and has a matching 'kind'. + function hasKind(node, kind) { + return node !== undefined && node.kind === kind; + } + // Null-propagating 'parent' function. + function parent(node) { + return node && node.parent; + } + function pushKeywordIf(keywordList, token, ...expected) { + if (token && ts.contains(expected, token.kind)) { + keywordList.push(token); + return true; + } + return false; + } + } + function findRenameLocations(fileName, position, findInStrings, findInComments) { + return findReferences(fileName, position, findInStrings, findInComments); + } + function getReferencesAtPosition(fileName, position) { + return findReferences(fileName, position, false, false); + } + function findReferences(fileName, position, findInStrings, findInComments) { + synchronizeHostData(); + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getSourceFile(fileName); + var node = ts.getTouchingPropertyName(sourceFile, position); + if (!node) { + return undefined; + } + if (node.kind !== ts.SyntaxKind.Identifier && !isLiteralNameOfPropertyDeclarationOrIndexAccess(node) && !isNameOfExternalModuleImportOrDeclaration(node)) { + return undefined; + } + ts.Debug.assert(node.kind === ts.SyntaxKind.Identifier || node.kind === ts.SyntaxKind.NumericLiteral || node.kind === ts.SyntaxKind.StringLiteral); + return getReferencesForNode(node, program.getSourceFiles(), findInStrings, findInComments); + } + function getReferencesForNode(node, sourceFiles, findInStrings, findInComments) { + // Labels + if (isLabelName(node)) { + if (isJumpStatementTarget(node)) { + var labelDefinition = getTargetLabel(node.parent, node.text); + // if we have a label definition, look within its statement for references, if not, then + // the label is undefined, just return a set of one for the current node. + return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : [getReferenceEntryFromNode(node)]; + } + else { + // it is a label definition and not a target, search within the parent labeledStatement + return getLabelReferencesInNode(node.parent, node); + } + } + if (node.kind === ts.SyntaxKind.ThisKeyword) { + return getReferencesForThisKeyword(node, sourceFiles); + } + if (node.kind === ts.SyntaxKind.SuperKeyword) { + return getReferencesForSuperKeyword(node); + } + var symbol = typeInfoResolver.getSymbolAtLocation(node); + // Could not find a symbol e.g. unknown identifier + if (!symbol) { + // Even if we did not find a symbol, we have an identifier, so there is at least + // one reference that we know of. return that instead of undefined. + return [getReferenceEntryFromNode(node)]; + } + var declarations = symbol.declarations; + // The symbol was an internal symbol and does not have a declaration e.g.undefined symbol + if (!declarations || !declarations.length) { + return undefined; + } + var result; + // Compute the meaning from the location and the symbol it references + var searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations); + // Get the text to search for, we need to normalize it as external module names will have quote + var declaredName = getDeclaredName(symbol); + // Try to get the smallest valid scope that we can limit our search to; + // otherwise we'll need to search globally (i.e. include each file). + var scope = getSymbolScope(symbol); + if (scope) { + result = []; + getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + } + else { + var internedName = getInternedName(symbol, declarations); + ts.forEach(sourceFiles, function (sourceFile) { + cancellationToken.throwIfCancellationRequested(); + if (ts.lookUp(sourceFile.identifiers, internedName)) { + result = result || []; + getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + } + }); + } + return result; + function getDeclaredName(symbol) { + var name = typeInfoResolver.symbolToString(symbol); + return stripQuotes(name); + } + function getInternedName(symbol, declarations) { + // Special case for function expressions, whose names are solely local to their bodies. + var functionExpression = ts.forEach(declarations, function (d) { return d.kind === ts.SyntaxKind.FunctionExpression ? d : undefined; }); + // When a name gets interned into a SourceFile's 'identifiers' Map, + // its name is escaped and stored in the same way its symbol name/identifier + // name should be stored. Function expressions, however, are a special case, + // because despite sometimes having a name, the binder unconditionally binds them + // to a symbol with the name "__function". + if (functionExpression && functionExpression.name) { + var name = functionExpression.name.text; + } + else { + var name = symbol.name; + } + return stripQuotes(name); + } + function stripQuotes(name) { + var length = name.length; + if (length >= 2 && name.charCodeAt(0) === ts.CharacterCodes.doubleQuote && name.charCodeAt(length - 1) === ts.CharacterCodes.doubleQuote) { + return name.substring(1, length - 1); + } + ; + return name; + } + function getSymbolScope(symbol) { + // If this is private property or method, the scope is the containing class + if (symbol.getFlags() && (ts.SymbolFlags.Property | ts.SymbolFlags.Method)) { + var privateDeclaration = ts.forEach(symbol.getDeclarations(), function (d) { return (d.flags & ts.NodeFlags.Private) ? d : undefined; }); + if (privateDeclaration) { + return ts.getAncestor(privateDeclaration, ts.SyntaxKind.ClassDeclaration); + } + } + // if this symbol is visible from its parent container, e.g. exported, then bail out + if (symbol.parent) { + return undefined; + } + var scope = undefined; + var declarations = symbol.getDeclarations(); + if (declarations) { + for (var i = 0, n = declarations.length; i < n; i++) { + var container = getContainerNode(declarations[i]); + if (!container) { + return undefined; + } + if (scope && scope !== container) { + // Different declarations have different containers, bail out + return undefined; + } + if (container.kind === ts.SyntaxKind.SourceFile && !ts.isExternalModule(container)) { + // This is a global variable and not an external module, any declaration defined + // within this scope is visible outside the file + return undefined; + } + // The search scope is the container node + scope = container; + } + } + return scope; + } + function getPossibleSymbolReferencePositions(sourceFile, symbolName, start, end) { + var positions = []; + /// TODO: Cache symbol existence for files to save text search + // Also, need to make this work for unicode escapes. + // Be resilient in the face of a symbol with no name or zero length name + if (!symbolName || !symbolName.length) { + return positions; + } + var text = sourceFile.text; + var sourceLength = text.length; + var symbolNameLength = symbolName.length; + var position = text.indexOf(symbolName, start); + while (position >= 0) { + cancellationToken.throwIfCancellationRequested(); + // If we are past the end, stop looking + if (position > end) + break; + // We found a match. Make sure it's not part of a larger word (i.e. the char + // before and after it have to be a non-identifier char). + var endPosition = position + symbolNameLength; + if ((position === 0 || !ts.isIdentifierPart(text.charCodeAt(position - 1), ts.ScriptTarget.Latest)) && (endPosition === sourceLength || !ts.isIdentifierPart(text.charCodeAt(endPosition), ts.ScriptTarget.Latest))) { + // Found a real match. Keep searching. + positions.push(position); + } + position = text.indexOf(symbolName, position + symbolNameLength + 1); + } + return positions; + } + function getLabelReferencesInNode(container, targetLabel) { + var result = []; + var sourceFile = container.getSourceFile(); + var labelName = targetLabel.text; + var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd()); + ts.forEach(possiblePositions, function (position) { + cancellationToken.throwIfCancellationRequested(); + var node = ts.getTouchingWord(sourceFile, position); + if (!node || node.getWidth() !== labelName.length) { + return; + } + // Only pick labels that are either the target label, or have a target that is the target label + if (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) { + result.push(getReferenceEntryFromNode(node)); + } + }); + return result; + } + function isValidReferencePosition(node, searchSymbolName) { + if (node) { + switch (node.kind) { + case ts.SyntaxKind.Identifier: + return node.getWidth() === searchSymbolName.length; + case ts.SyntaxKind.StringLiteral: + if (isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) { + // For string literals we have two additional chars for the quotes + return node.getWidth() === searchSymbolName.length + 2; + } + break; + case ts.SyntaxKind.NumericLiteral: + if (isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) { + return node.getWidth() === searchSymbolName.length; + } + break; + } + } + return false; + } + /** Search within node "container" for references for a search value, where the search value is defined as a + * tuple of(searchSymbol, searchText, searchLocation, and searchMeaning). + * searchLocation: a node where the search value + */ + function getReferencesInNode(container, searchSymbol, searchText, searchLocation, searchMeaning, findInStrings, findInComments, result) { + var sourceFile = container.getSourceFile(); + var tripleSlashDirectivePrefixRegex = /^\/\/\/\s*= 0) { + result.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); + } + } + }); + } + function isInString(position) { + var token = ts.getTokenAtPosition(sourceFile, position); + return token && token.kind === ts.SyntaxKind.StringLiteral && position > token.getStart(); + } + function isInComment(position) { + var token = ts.getTokenAtPosition(sourceFile, position); + if (token && position < token.getStart()) { + // First, we have to see if this position actually landed in a comment. + var commentRanges = ts.getLeadingCommentRanges(sourceFile.text, token.pos); + // Then we want to make sure that it wasn't in a "///<" directive comment + // We don't want to unintentionally update a file name. + return ts.forEach(commentRanges, function (c) { + if (c.pos < position && position < c.end) { + var commentText = sourceFile.text.substring(c.pos, c.end); + if (!tripleSlashDirectivePrefixRegex.test(commentText)) { + return true; + } + } + }); + } + return false; + } + } + function getReferencesForSuperKeyword(superKeyword) { + var searchSpaceNode = ts.getSuperContainer(superKeyword); + if (!searchSpaceNode) { + return undefined; + } + // Whether 'super' occurs in a static context within a class. + var staticFlag = ts.NodeFlags.Static; + switch (searchSpaceNode.kind) { + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + staticFlag &= searchSpaceNode.flags; + searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class + break; + default: + return undefined; + } + var result = []; + var sourceFile = searchSpaceNode.getSourceFile(); + var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd()); + ts.forEach(possiblePositions, function (position) { + cancellationToken.throwIfCancellationRequested(); + var node = ts.getTouchingWord(sourceFile, position); + if (!node || node.kind !== ts.SyntaxKind.SuperKeyword) { + return; + } + var container = ts.getSuperContainer(node); + // If we have a 'super' container, we must have an enclosing class. + // Now make sure the owning class is the same as the search-space + // and has the same static qualifier as the original 'super's owner. + if (container && (ts.NodeFlags.Static & container.flags) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) { + result.push(getReferenceEntryFromNode(node)); + } + }); + return result; + } + function getReferencesForThisKeyword(thisOrSuperKeyword, sourceFiles) { + var searchSpaceNode = ts.getThisContainer(thisOrSuperKeyword, false); + // Whether 'this' occurs in a static context within a class. + var staticFlag = ts.NodeFlags.Static; + switch (searchSpaceNode.kind) { + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + if (ts.isObjectLiteralMethod(searchSpaceNode)) { + break; + } + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + staticFlag &= searchSpaceNode.flags; + searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class + break; + case ts.SyntaxKind.SourceFile: + if (ts.isExternalModule(searchSpaceNode)) { + return undefined; + } + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionExpression: + break; + default: + return undefined; + } + var result = []; + if (searchSpaceNode.kind === ts.SyntaxKind.SourceFile) { + ts.forEach(sourceFiles, function (sourceFile) { + var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", sourceFile.getStart(), sourceFile.getEnd()); + getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, result); + }); + } + else { + var sourceFile = searchSpaceNode.getSourceFile(); + var possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode.getStart(), searchSpaceNode.getEnd()); + getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, result); + } + return result; + function getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, result) { + ts.forEach(possiblePositions, function (position) { + cancellationToken.throwIfCancellationRequested(); + var node = ts.getTouchingWord(sourceFile, position); + if (!node || node.kind !== ts.SyntaxKind.ThisKeyword) { + return; + } + var container = ts.getThisContainer(node, false); + switch (searchSpaceNode.kind) { + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.FunctionDeclaration: + if (searchSpaceNode.symbol === container.symbol) { + result.push(getReferenceEntryFromNode(node)); + } + break; + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + if (ts.isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) { + result.push(getReferenceEntryFromNode(node)); + } + break; + case ts.SyntaxKind.ClassDeclaration: + // Make sure the container belongs to the same class + // and has the appropriate static modifier from the original container. + if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (container.flags & ts.NodeFlags.Static) === staticFlag) { + result.push(getReferenceEntryFromNode(node)); + } + break; + case ts.SyntaxKind.SourceFile: + if (container.kind === ts.SyntaxKind.SourceFile && !ts.isExternalModule(container)) { + result.push(getReferenceEntryFromNode(node)); + } + break; + } + }); + } + } + function populateSearchSymbolSet(symbol, location) { + // The search set contains at least the current symbol + var result = [symbol]; + // If the location is in a context sensitive location (i.e. in an object literal) try + // to get a contextual type for it, and add the property symbol from the contextual + // type to the search set + if (isNameOfPropertyAssignment(location)) { + ts.forEach(getPropertySymbolsFromContextualType(location), function (contextualSymbol) { + result.push.apply(result, typeInfoResolver.getRootSymbols(contextualSymbol)); + }); + /* Because in short-hand property assignment, location has two meaning : property name and as value of the property + * When we do findAllReference at the position of the short-hand property assignment, we would want to have references to position of + * property name and variable declaration of the identifier. + * Like in below example, when querying for all references for an identifier 'name', of the property assignment, the language service + * should show both 'name' in 'obj' and 'name' in variable declaration + * var name = "Foo"; + * var obj = { name }; + * In order to do that, we will populate the search set with the value symbol of the identifier as a value of the property assignment + * so that when matching with potential reference symbol, both symbols from property declaration and variable declaration + * will be included correctly. + */ + var shorthandValueSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(location.parent); + if (shorthandValueSymbol) { + result.push(shorthandValueSymbol); + } + } + // If this is a union property, add all the symbols from all its source symbols in all unioned types. + // If the symbol is an instantiation from a another symbol (e.g. widened symbol) , add the root the list + ts.forEach(typeInfoResolver.getRootSymbols(symbol), function (rootSymbol) { + if (rootSymbol !== symbol) { + result.push(rootSymbol); + } + // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions + if (rootSymbol.parent && rootSymbol.parent.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) { + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result); + } + }); + return result; + } + function getPropertySymbolsFromBaseTypes(symbol, propertyName, result) { + if (symbol && symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) { + ts.forEach(symbol.getDeclarations(), function (declaration) { + if (declaration.kind === ts.SyntaxKind.ClassDeclaration) { + getPropertySymbolFromTypeReference(ts.getClassBaseTypeNode(declaration)); + ts.forEach(ts.getClassImplementedTypeNodes(declaration), getPropertySymbolFromTypeReference); + } + else if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration) { + ts.forEach(ts.getInterfaceBaseTypeNodes(declaration), getPropertySymbolFromTypeReference); + } + }); + } + return; + function getPropertySymbolFromTypeReference(typeReference) { + if (typeReference) { + var type = typeInfoResolver.getTypeAtLocation(typeReference); + if (type) { + var propertySymbol = typeInfoResolver.getPropertyOfType(type, propertyName); + if (propertySymbol) { + result.push(propertySymbol); + } + // Visit the typeReference as well to see if it directly or indirectly use that property + getPropertySymbolsFromBaseTypes(type.symbol, propertyName, result); + } + } + } + } + function isRelatableToSearchSet(searchSymbols, referenceSymbol, referenceLocation) { + if (searchSymbols.indexOf(referenceSymbol) >= 0) { + return true; + } + // If the reference location is in an object literal, try to get the contextual type for the + // object literal, lookup the property symbol in the contextual type, and use this symbol to + // compare to our searchSymbol + if (isNameOfPropertyAssignment(referenceLocation)) { + return ts.forEach(getPropertySymbolsFromContextualType(referenceLocation), function (contextualSymbol) { + return ts.forEach(typeInfoResolver.getRootSymbols(contextualSymbol), function (s) { return searchSymbols.indexOf(s) >= 0; }); + }); + } + // Unwrap symbols to get to the root (e.g. transient symbols as a result of widening) + // Or a union property, use its underlying unioned symbols + return ts.forEach(typeInfoResolver.getRootSymbols(referenceSymbol), function (rootSymbol) { + // if it is in the list, then we are done + if (searchSymbols.indexOf(rootSymbol) >= 0) { + return true; + } + // Finally, try all properties with the same name in any type the containing type extended or implemented, and + // see if any is in the list + if (rootSymbol.parent && rootSymbol.parent.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) { + var result = []; + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result); + return ts.forEach(result, function (s) { return searchSymbols.indexOf(s) >= 0; }); + } + return false; + }); + } + function getPropertySymbolsFromContextualType(node) { + if (isNameOfPropertyAssignment(node)) { + var objectLiteral = node.parent.parent; + var contextualType = typeInfoResolver.getContextualType(objectLiteral); + var name = node.text; + if (contextualType) { + if (contextualType.flags & ts.TypeFlags.Union) { + // This is a union type, first see if the property we are looking for is a union property (i.e. exists in all types) + // if not, search the constituent types for the property + var unionProperty = contextualType.getProperty(name); + if (unionProperty) { + return [unionProperty]; + } + else { + var result = []; + ts.forEach(contextualType.types, function (t) { + var symbol = t.getProperty(name); + if (symbol) { + result.push(symbol); + } + }); + return result; + } + } + else { + var symbol = contextualType.getProperty(name); + if (symbol) { + return [symbol]; + } + } + } + } + return undefined; + } + /** Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations + * of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class + * then we need to widen the search to include type positions as well. + * On the contrary, if we are searching for "Bar" in type position and we trace bar to an interface, and an uninstantiated + * module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module) + * do not intersect in any of the three spaces. + */ + function getIntersectingMeaningFromDeclarations(meaning, declarations) { + if (declarations) { + do { + // The result is order-sensitive, for instance if initialMeaning === Namespace, and declarations = [class, instantiated module] + // we need to consider both as they initialMeaning intersects with the module in the namespace space, and the module + // intersects with the class in the value space. + // To achieve that we will keep iterating until the result stabilizes. + // Remember the last meaning + var lastIterationMeaning = meaning; + for (var i = 0, n = declarations.length; i < n; i++) { + var declarationMeaning = getMeaningFromDeclaration(declarations[i]); + if (declarationMeaning & meaning) { + meaning |= declarationMeaning; + } + } + } while (meaning !== lastIterationMeaning); + } + return meaning; + } + } + function getReferenceEntryFromNode(node) { + var start = node.getStart(); + var end = node.getEnd(); + if (node.kind === ts.SyntaxKind.StringLiteral) { + start += 1; + end -= 1; + } + return { + fileName: node.getSourceFile().filename, + textSpan: ts.createTextSpanFromBounds(start, end), + isWriteAccess: isWriteAccess(node) + }; + } + /** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */ + function isWriteAccess(node) { + if (node.kind === ts.SyntaxKind.Identifier && ts.isDeclarationOrFunctionExpressionOrCatchVariableName(node)) { + return true; + } + var parent = node.parent; + if (parent) { + if (parent.kind === ts.SyntaxKind.PostfixUnaryExpression || parent.kind === ts.SyntaxKind.PrefixUnaryExpression) { + return true; + } + else if (parent.kind === ts.SyntaxKind.BinaryExpression && parent.left === node) { + var operator = parent.operator; + return ts.SyntaxKind.FirstAssignment <= operator && operator <= ts.SyntaxKind.LastAssignment; + } + } + return false; + } + /// NavigateTo + function getNavigateToItems(searchValue) { + synchronizeHostData(); + // Split search value in terms array + var terms = searchValue.split(" "); + // default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version + var searchTerms = ts.map(terms, function (t) { return ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }); }); + var items = []; + // Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[] + ts.forEach(program.getSourceFiles(), function (sourceFile) { + cancellationToken.throwIfCancellationRequested(); + var filename = sourceFile.filename; + var declarations = sourceFile.getNamedDeclarations(); + for (var i = 0, n = declarations.length; i < n; i++) { + var declaration = declarations[i]; + // TODO(jfreeman): Skip this declaration if it has a computed name + var name = declaration.name.text; + var matchKind = getMatchKind(searchTerms, name); + if (matchKind !== 0 /* none */) { + var container = getContainerNode(declaration); + items.push({ + name: name, + kind: getNodeKind(declaration), + kindModifiers: ts.getNodeModifiers(declaration), + matchKind: MatchKind[matchKind], + fileName: filename, + textSpan: ts.createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()), + // TODO(jfreeman): What should be the containerName when the container has a computed name? + containerName: container && container.name ? container.name.text : "", + containerKind: container && container.name ? getNodeKind(container) : "" + }); + } + } + }); + return items; + function hasAnyUpperCaseCharacter(s) { + for (var i = 0, n = s.length; i < n; i++) { + var c = s.charCodeAt(i); + if ((ts.CharacterCodes.A <= c && c <= ts.CharacterCodes.Z) || (c >= ts.CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) { + return true; + } + } + return false; + } + function getMatchKind(searchTerms, name) { + var matchKind = 0 /* none */; + if (name) { + for (var j = 0, n = searchTerms.length; j < n; j++) { + var searchTerm = searchTerms[j]; + var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase(); + // in case of case-insensitive search searchTerm.term will already be lower-cased + var index = nameToSearch.indexOf(searchTerm.term); + if (index < 0) { + // Didn't match. + return 0 /* none */; + } + var termKind = 2 /* substring */; + if (index === 0) { + // here we know that match occur at the beginning of the string. + // if search term and declName has the same length - we have an exact match, otherwise declName have longer length and this will be prefix match + termKind = name.length === searchTerm.term.length ? 1 /* exact */ : 3 /* prefix */; + } + // Update our match kind if we don't have one, or if this match is better. + if (matchKind === 0 /* none */ || termKind < matchKind) { + matchKind = termKind; + } + } + } + return matchKind; + } + } + function containErrors(diagnostics) { + return ts.forEach(diagnostics, function (diagnostic) { return diagnostic.category === ts.DiagnosticCategory.Error; }); + } + function getEmitOutput(filename) { + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + var sourceFile = getSourceFile(filename); + var outputFiles = []; + function writeFile(filename, data, writeByteOrderMark) { + outputFiles.push({ + name: filename, + writeByteOrderMark: writeByteOrderMark, + text: data + }); + } + // Get an emit host from our program, but override the writeFile functionality to + // call our local writer function. + var emitHost = ts.createEmitHostFromProgram(program); + emitHost.writeFile = writeFile; + var emitOutput = ts.emitFiles(getDiagnosticsProducingTypeChecker().getEmitResolver(), emitHost, sourceFile); + return { + outputFiles, + emitOutputStatus: emitOutput.emitResultStatus + }; + } + function getMeaningFromDeclaration(node) { + switch (node.kind) { + case ts.SyntaxKind.Parameter: + case ts.SyntaxKind.VariableDeclaration: + case ts.SyntaxKind.BindingElement: + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + case ts.SyntaxKind.PropertyAssignment: + case ts.SyntaxKind.ShorthandPropertyAssignment: + case ts.SyntaxKind.EnumMember: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.CatchClause: + return 1 /* Value */; + case ts.SyntaxKind.TypeParameter: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.TypeLiteral: + return 2 /* Type */; + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.EnumDeclaration: + return 1 /* Value */ | 2 /* Type */; + case ts.SyntaxKind.ModuleDeclaration: + if (node.name.kind === ts.SyntaxKind.StringLiteral) { + return 4 /* Namespace */ | 1 /* Value */; + } + else if (ts.getModuleInstanceState(node) === ts.ModuleInstanceState.Instantiated) { + return 4 /* Namespace */ | 1 /* Value */; + } + else { + return 4 /* Namespace */; + } + case ts.SyntaxKind.ImportDeclaration: + return 1 /* Value */ | 2 /* Type */ | 4 /* Namespace */; + case ts.SyntaxKind.SourceFile: + return 4 /* Namespace */ | 1 /* Value */; + } + ts.Debug.fail("Unknown declaration type"); + } + function isTypeReference(node) { + if (isRightSideOfQualifiedName(node)) { + node = node.parent; + } + return node.parent.kind === ts.SyntaxKind.TypeReference; + } + function isNamespaceReference(node) { + var root = node; + var isLastClause = true; + if (root.parent.kind === ts.SyntaxKind.QualifiedName) { + while (root.parent && root.parent.kind === ts.SyntaxKind.QualifiedName) + root = root.parent; + isLastClause = root.right === node; + } + return root.parent.kind === ts.SyntaxKind.TypeReference && !isLastClause; + } + function isInRightSideOfImport(node) { + while (node.parent.kind === ts.SyntaxKind.QualifiedName) { + node = node.parent; + } + return ts.isInternalModuleImportDeclaration(node.parent) && node.parent.moduleReference === node; + } + function getMeaningFromRightHandSideOfImport(node) { + ts.Debug.assert(node.kind === ts.SyntaxKind.Identifier); + // import a = |b|; // Namespace + // import a = |b.c|; // Value, type, namespace + // import a = |b.c|.d; // Namespace + if (node.parent.kind === ts.SyntaxKind.QualifiedName && node.parent.right === node && node.parent.parent.kind === ts.SyntaxKind.ImportDeclaration) { + return 1 /* Value */ | 2 /* Type */ | 4 /* Namespace */; + } + return 4 /* Namespace */; + } + function getMeaningFromLocation(node) { + if (node.parent.kind === ts.SyntaxKind.ExportAssignment) { + return 1 /* Value */ | 2 /* Type */ | 4 /* Namespace */; + } + else if (isInRightSideOfImport(node)) { + return getMeaningFromRightHandSideOfImport(node); + } + else if (ts.isDeclarationOrFunctionExpressionOrCatchVariableName(node)) { + return getMeaningFromDeclaration(node.parent); + } + else if (isTypeReference(node)) { + return 2 /* Type */; + } + else if (isNamespaceReference(node)) { + return 4 /* Namespace */; + } + else { + return 1 /* Value */; + } + } + // Signature help + /** + * This is a semantic operation. + */ + function getSignatureHelpItems(fileName, position) { + synchronizeHostData(); + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getSourceFile(fileName); + return ts.SignatureHelp.getSignatureHelpItems(sourceFile, position, typeInfoResolver, cancellationToken); + } + /// Syntactic features + function getCurrentSourceFile(filename) { + filename = ts.normalizeSlashes(filename); + var currentSourceFile = syntaxTreeCache.getCurrentSourceFile(filename); + return currentSourceFile; + } + function getNameOrDottedNameSpan(filename, startPos, endPos) { + filename = ts.normalizeSlashes(filename); + // Get node at the location + var node = ts.getTouchingPropertyName(getCurrentSourceFile(filename), startPos); + if (!node) { + return; + } + switch (node.kind) { + case ts.SyntaxKind.PropertyAccessExpression: + case ts.SyntaxKind.QualifiedName: + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.FalseKeyword: + case ts.SyntaxKind.TrueKeyword: + case ts.SyntaxKind.NullKeyword: + case ts.SyntaxKind.SuperKeyword: + case ts.SyntaxKind.ThisKeyword: + case ts.SyntaxKind.Identifier: + break; + default: + return; + } + var nodeForStartPos = node; + while (true) { + if (isRightSideOfPropertyAccess(nodeForStartPos) || isRightSideOfQualifiedName(nodeForStartPos)) { + // If on the span is in right side of the the property or qualified name, return the span from the qualified name pos to end of this node + nodeForStartPos = nodeForStartPos.parent; + } + else if (isNameOfModuleDeclaration(nodeForStartPos)) { + // If this is name of a module declarations, check if this is right side of dotted module name + // If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of + // Then this name is name from dotted module + if (nodeForStartPos.parent.parent.kind === ts.SyntaxKind.ModuleDeclaration && nodeForStartPos.parent.parent.body === nodeForStartPos.parent) { + // Use parent module declarations name for start pos + nodeForStartPos = nodeForStartPos.parent.parent.name; + } + else { + break; + } + } + else { + break; + } + } + return ts.createTextSpanFromBounds(nodeForStartPos.getStart(), node.getEnd()); + } + function getBreakpointStatementAtPosition(filename, position) { + // doesn't use compiler - no need to synchronize with host + filename = ts.normalizeSlashes(filename); + return ts.BreakpointResolver.spanInSourceFileAtLocation(getCurrentSourceFile(filename), position); + } + function getNavigationBarItems(filename) { + filename = ts.normalizeSlashes(filename); + return ts.NavigationBar.getNavigationBarItems(getCurrentSourceFile(filename)); + } + function getSemanticClassifications(fileName, span) { + synchronizeHostData(); + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getSourceFile(fileName); + var result = []; + processNode(sourceFile); + return result; + function classifySymbol(symbol, meaningAtPosition) { + var flags = symbol.getFlags(); + if (flags & ts.SymbolFlags.Class) { + return ClassificationTypeNames.className; + } + else if (flags & ts.SymbolFlags.Enum) { + return ClassificationTypeNames.enumName; + } + else if (flags & ts.SymbolFlags.TypeAlias) { + return ClassificationTypeNames.typeAlias; + } + else if (meaningAtPosition & 2 /* Type */) { + if (flags & ts.SymbolFlags.Interface) { + return ClassificationTypeNames.interfaceName; + } + else if (flags & ts.SymbolFlags.TypeParameter) { + return ClassificationTypeNames.typeParameterName; + } + } + else if (flags & ts.SymbolFlags.Module) { + // Only classify a module as such if + // - It appears in a namespace context. + // - There exists a module declaration which actually impacts the value side. + if (meaningAtPosition & 4 /* Namespace */ || (meaningAtPosition & 1 /* Value */ && hasValueSideModule(symbol))) { + return ClassificationTypeNames.moduleName; + } + } + return undefined; + /** + * Returns true if there exists a module that introduces entities on the value side. + */ + function hasValueSideModule(symbol) { + return ts.forEach(symbol.declarations, function (declaration) { + return declaration.kind === ts.SyntaxKind.ModuleDeclaration && ts.getModuleInstanceState(declaration) == ts.ModuleInstanceState.Instantiated; + }); + } + } + function processNode(node) { + // Only walk into nodes that intersect the requested span. + if (node && ts.textSpanIntersectsWith(span, node.getStart(), node.getWidth())) { + if (node.kind === ts.SyntaxKind.Identifier && node.getWidth() > 0) { + var symbol = typeInfoResolver.getSymbolAtLocation(node); + if (symbol) { + var type = classifySymbol(symbol, getMeaningFromLocation(node)); + if (type) { + result.push({ + textSpan: ts.createTextSpan(node.getStart(), node.getWidth()), + classificationType: type + }); + } + } + } + ts.forEachChild(node, processNode); + } + } + } + function getSyntacticClassifications(fileName, span) { + // doesn't use compiler - no need to synchronize with host + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getCurrentSourceFile(fileName); + // Make a scanner we can get trivia from. + var triviaScanner = ts.createScanner(ts.ScriptTarget.Latest, false, sourceFile.text); + var mergeConflictScanner = ts.createScanner(ts.ScriptTarget.Latest, false, sourceFile.text); + var result = []; + processElement(sourceFile); + return result; + function classifyLeadingTrivia(token) { + var tokenStart = ts.skipTrivia(sourceFile.text, token.pos, false); + if (tokenStart === token.pos) { + return; + } + // token has trivia. Classify them appropriately. + triviaScanner.setTextPos(token.pos); + while (true) { + var start = triviaScanner.getTextPos(); + var kind = triviaScanner.scan(); + var end = triviaScanner.getTextPos(); + var width = end - start; + if (ts.textSpanIntersectsWith(span, start, width)) { + if (!ts.isTrivia(kind)) { + return; + } + if (ts.isComment(kind)) { + // Simple comment. Just add as is. + result.push({ + textSpan: ts.createTextSpan(start, width), + classificationType: ClassificationTypeNames.comment + }); + continue; + } + if (kind === ts.SyntaxKind.ConflictMarkerTrivia) { + var text = sourceFile.text; + var ch = text.charCodeAt(start); + // for the <<<<<<< and >>>>>>> markers, we just add them in as comments + // in the classification stream. + if (ch === ts.CharacterCodes.lessThan || ch === ts.CharacterCodes.greaterThan) { + result.push({ + textSpan: ts.createTextSpan(start, width), + classificationType: ClassificationTypeNames.comment + }); + continue; + } + // for the ======== add a comment for the first line, and then lex all + // subsequent lines up until the end of the conflict marker. + ts.Debug.assert(ch === ts.CharacterCodes.equals); + classifyDisabledMergeCode(text, start, end); + } + } + } + } + function classifyDisabledMergeCode(text, start, end) { + for (var i = start; i < end; i++) { + if (ts.isLineBreak(text.charCodeAt(i))) { + break; + } + } + result.push({ + textSpan: ts.createTextSpanFromBounds(start, i), + classificationType: ClassificationTypeNames.comment + }); + mergeConflictScanner.setTextPos(i); + while (mergeConflictScanner.getTextPos() < end) { + classifyDisabledCodeToken(); + } + } + function classifyDisabledCodeToken() { + var start = mergeConflictScanner.getTextPos(); + var tokenKind = mergeConflictScanner.scan(); + var end = mergeConflictScanner.getTextPos(); + var type = classifyTokenType(tokenKind); + if (type) { + result.push({ + textSpan: ts.createTextSpanFromBounds(start, end), + classificationType: type + }); + } + } + function classifyToken(token) { + classifyLeadingTrivia(token); + if (token.getWidth() > 0) { + var type = classifyTokenType(token.kind, token); + if (type) { + result.push({ + textSpan: ts.createTextSpan(token.getStart(), token.getWidth()), + classificationType: type + }); + } + } + } + // for accurate classification, the actual token should be passed in. however, for + // cases like 'disabled merge code' classification, we just get the token kind and + // classify based on that instead. + function classifyTokenType(tokenKind, token) { + if (ts.isKeyword(tokenKind)) { + return ClassificationTypeNames.keyword; + } + // Special case < and > If they appear in a generic context they are punctuation, + // not operators. + if (tokenKind === ts.SyntaxKind.LessThanToken || tokenKind === ts.SyntaxKind.GreaterThanToken) { + // If the node owning the token has a type argument list or type parameter list, then + // we can effectively assume that a '<' and '>' belong to those lists. + if (token && ts.getTypeArgumentOrTypeParameterList(token.parent)) { + return ClassificationTypeNames.punctuation; + } + } + if (ts.isPunctuation(tokenKind)) { + if (token) { + if (tokenKind === ts.SyntaxKind.EqualsToken) { + // the '=' in a variable declaration is special cased here. + if (token.parent.kind === ts.SyntaxKind.VariableDeclaration || token.parent.kind === ts.SyntaxKind.PropertyDeclaration || token.parent.kind === ts.SyntaxKind.Parameter) { + return ClassificationTypeNames.operator; + } + } + if (token.parent.kind === ts.SyntaxKind.BinaryExpression || token.parent.kind === ts.SyntaxKind.PrefixUnaryExpression || token.parent.kind === ts.SyntaxKind.PostfixUnaryExpression || token.parent.kind === ts.SyntaxKind.ConditionalExpression) { + return ClassificationTypeNames.operator; + } + } + return ClassificationTypeNames.punctuation; + } + else if (tokenKind === ts.SyntaxKind.NumericLiteral) { + return ClassificationTypeNames.numericLiteral; + } + else if (tokenKind === ts.SyntaxKind.StringLiteral) { + return ClassificationTypeNames.stringLiteral; + } + else if (tokenKind === ts.SyntaxKind.RegularExpressionLiteral) { + // TODO: we should get another classification type for these literals. + return ClassificationTypeNames.stringLiteral; + } + else if (ts.isTemplateLiteralKind(tokenKind)) { + // TODO (drosen): we should *also* get another classification type for these literals. + return ClassificationTypeNames.stringLiteral; + } + else if (tokenKind === ts.SyntaxKind.Identifier) { + if (token) { + switch (token.parent.kind) { + case ts.SyntaxKind.ClassDeclaration: + if (token.parent.name === token) { + return ClassificationTypeNames.className; + } + return; + case ts.SyntaxKind.TypeParameter: + if (token.parent.name === token) { + return ClassificationTypeNames.typeParameterName; + } + return; + case ts.SyntaxKind.InterfaceDeclaration: + if (token.parent.name === token) { + return ClassificationTypeNames.interfaceName; + } + return; + case ts.SyntaxKind.EnumDeclaration: + if (token.parent.name === token) { + return ClassificationTypeNames.enumName; + } + return; + case ts.SyntaxKind.ModuleDeclaration: + if (token.parent.name === token) { + return ClassificationTypeNames.moduleName; + } + return; + } + } + return ClassificationTypeNames.text; + } + } + function processElement(element) { + // Ignore nodes that don't intersect the original span to classify. + if (ts.textSpanIntersectsWith(span, element.getFullStart(), element.getFullWidth())) { + var children = element.getChildren(); + for (var i = 0, n = children.length; i < n; i++) { + var child = children[i]; + if (ts.isToken(child)) { + classifyToken(child); + } + else { + // Recurse into our child nodes. + processElement(child); + } + } + } + } + } + function getOutliningSpans(filename) { + // doesn't use compiler - no need to synchronize with host + filename = ts.normalizeSlashes(filename); + var sourceFile = getCurrentSourceFile(filename); + return ts.OutliningElementsCollector.collectElements(sourceFile); + } + function getBraceMatchingAtPosition(filename, position) { + var sourceFile = getCurrentSourceFile(filename); + var result = []; + var token = ts.getTouchingToken(sourceFile, position); + if (token.getStart(sourceFile) === position) { + var matchKind = getMatchingTokenKind(token); + // Ensure that there is a corresponding token to match ours. + if (matchKind) { + var parentElement = token.parent; + var childNodes = parentElement.getChildren(sourceFile); + for (var i = 0, n = childNodes.length; i < n; i++) { + 33; + var current = childNodes[i]; + if (current.kind === matchKind) { + var range1 = ts.createTextSpan(token.getStart(sourceFile), token.getWidth(sourceFile)); + var range2 = ts.createTextSpan(current.getStart(sourceFile), current.getWidth(sourceFile)); + // We want to order the braces when we return the result. + if (range1.start < range2.start) { + result.push(range1, range2); + } + else { + result.push(range2, range1); + } + break; + } + } + } + } + return result; + function getMatchingTokenKind(token) { + switch (token.kind) { + case ts.SyntaxKind.OpenBraceToken: return ts.SyntaxKind.CloseBraceToken; + case ts.SyntaxKind.OpenParenToken: return ts.SyntaxKind.CloseParenToken; + case ts.SyntaxKind.OpenBracketToken: return ts.SyntaxKind.CloseBracketToken; + case ts.SyntaxKind.LessThanToken: return ts.SyntaxKind.GreaterThanToken; + case ts.SyntaxKind.CloseBraceToken: return ts.SyntaxKind.OpenBraceToken; + case ts.SyntaxKind.CloseParenToken: return ts.SyntaxKind.OpenParenToken; + case ts.SyntaxKind.CloseBracketToken: return ts.SyntaxKind.OpenBracketToken; + case ts.SyntaxKind.GreaterThanToken: return ts.SyntaxKind.LessThanToken; + } + return undefined; + } + } + function getIndentationAtPosition(filename, position, editorOptions) { + filename = ts.normalizeSlashes(filename); + var start = new Date().getTime(); + var sourceFile = getCurrentSourceFile(filename); + log("getIndentationAtPosition: getCurrentSourceFile: " + (new Date().getTime() - start)); + var start = new Date().getTime(); + var result = ts.formatting.SmartIndenter.getIndentation(position, sourceFile, editorOptions); + log("getIndentationAtPosition: computeIndentation : " + (new Date().getTime() - start)); + return result; + } + function getFormattingEditsForRange(fileName, start, end, options) { + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getCurrentSourceFile(fileName); + return ts.formatting.formatSelection(start, end, sourceFile, getRuleProvider(options), options); + } + function getFormattingEditsForDocument(fileName, options) { + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getCurrentSourceFile(fileName); + return ts.formatting.formatDocument(sourceFile, getRuleProvider(options), options); + } + function getFormattingEditsAfterKeystroke(fileName, position, key, options) { + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getCurrentSourceFile(fileName); + if (key === "}") { + return ts.formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(options), options); + } + else if (key === ";") { + return ts.formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(options), options); + } + else if (key === "\n") { + return ts.formatting.formatOnEnter(position, sourceFile, getRuleProvider(options), options); + } + return []; + } + function getTodoComments(filename, descriptors) { + // Note: while getting todo comments seems like a syntactic operation, we actually + // treat it as a semantic operation here. This is because we expect our host to call + // this on every single file. If we treat this syntactically, then that will cause + // us to populate and throw away the tree in our syntax tree cache for each file. By + // treating this as a semantic operation, we can access any tree without throwing + // anything away. + synchronizeHostData(); + filename = ts.normalizeSlashes(filename); + var sourceFile = getSourceFile(filename); + cancellationToken.throwIfCancellationRequested(); + var fileContents = sourceFile.text; + cancellationToken.throwIfCancellationRequested(); + var result = []; + if (descriptors.length > 0) { + var regExp = getTodoCommentsRegExp(); + var matchArray; + while (matchArray = regExp.exec(fileContents)) { + cancellationToken.throwIfCancellationRequested(); + // If we got a match, here is what the match array will look like. Say the source text is: + // + // " // hack 1" + // + // The result array with the regexp: will be: + // + // ["// hack 1", "// ", "hack 1", undefined, "hack"] + // + // Here are the relevant capture groups: + // 0) The full match for the entire regexp. + // 1) The preamble to the message portion. + // 2) The message portion. + // 3...N) The descriptor that was matched - by index. 'undefined' for each + // descriptor that didn't match. an actual value if it did match. + // + // i.e. 'undefined' in position 3 above means TODO(jason) didn't match. + // "hack" in position 4 means HACK did match. + var firstDescriptorCaptureIndex = 3; + ts.Debug.assert(matchArray.length === descriptors.length + firstDescriptorCaptureIndex); + var preamble = matchArray[1]; + var matchPosition = matchArray.index + preamble.length; + // OK, we have found a match in the file. This is only an acceptable match if + // it is contained within a comment. + var token = ts.getTokenAtPosition(sourceFile, matchPosition); + if (!isInsideComment(sourceFile, token, matchPosition)) { + continue; + } + var descriptor = undefined; + for (var i = 0, n = descriptors.length; i < n; i++) { + if (matchArray[i + firstDescriptorCaptureIndex]) { + descriptor = descriptors[i]; + } + } + ts.Debug.assert(descriptor !== undefined); + // We don't want to match something like 'TODOBY', so we make sure a non + // letter/digit follows the match. + if (isLetterOrDigit(fileContents.charCodeAt(matchPosition + descriptor.text.length))) { + continue; + } + var message = matchArray[2]; + result.push({ + descriptor: descriptor, + message: message, + position: matchPosition + }); + } + } + return result; + function escapeRegExp(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + } + function getTodoCommentsRegExp() { + // NOTE: ?: means 'non-capture group'. It allows us to have groups without having to + // filter them out later in the final result array. + // TODO comments can appear in one of the following forms: + // + // 1) // TODO or /////////// TODO + // + // 2) /* TODO or /********** TODO + // + // 3) /* + // * TODO + // */ + // + // The following three regexps are used to match the start of the text up to the TODO + // comment portion. + var singleLineCommentStart = /(?:\/\/+\s*)/.source; + var multiLineCommentStart = /(?:\/\*+\s*)/.source; + var anyNumberOfSpacesAndAsterixesAtStartOfLine = /(?:^(?:\s|\*)*)/.source; + // Match any of the above three TODO comment start regexps. + // Note that the outermost group *is* a capture group. We want to capture the preamble + // so that we can determine the starting position of the TODO comment match. + var preamble = "(" + anyNumberOfSpacesAndAsterixesAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")"; + // Takes the descriptors and forms a regexp that matches them as if they were literals. + // For example, if the descriptors are "TODO(jason)" and "HACK", then this will be: + // + // (?:(TODO\(jason\))|(HACK)) + // + // Note that the outermost group is *not* a capture group, but the innermost groups + // *are* capture groups. By capturing the inner literals we can determine after + // matching which descriptor we are dealing with. + var literals = "(?:" + ts.map(descriptors, function (d) { return "(" + escapeRegExp(d.text) + ")"; }).join("|") + ")"; + // After matching a descriptor literal, the following regexp matches the rest of the + // text up to the end of the line (or */). + var endOfLineOrEndOfComment = /(?:$|\*\/)/.source; + var messageRemainder = /(?:.*?)/.source; + // This is the portion of the match we'll return as part of the TODO comment result. We + // match the literal portion up to the end of the line or end of comment. + var messagePortion = "(" + literals + messageRemainder + ")"; + var regExpString = preamble + messagePortion + endOfLineOrEndOfComment; + // The final regexp will look like this: + // /((?:\/\/+\s*)|(?:\/\*+\s*)|(?:^(?:\s|\*)*))((?:(TODO\(jason\))|(HACK))(?:.*?))(?:$|\*\/)/gim + // The flags of the regexp are important here. + // 'g' is so that we are doing a global search and can find matches several times + // in the input. + // + // 'i' is for case insensitivity (We do this to match C# TODO comment code). + // + // 'm' is so we can find matches in a multi-line input. + return new RegExp(regExpString, "gim"); + } + function isLetterOrDigit(char) { + return (char >= ts.CharacterCodes.a && char <= ts.CharacterCodes.z) || (char >= ts.CharacterCodes.A && char <= ts.CharacterCodes.Z) || (char >= ts.CharacterCodes._0 && char <= ts.CharacterCodes._9); + } + } + function getRenameInfo(fileName, position) { + synchronizeHostData(); + fileName = ts.normalizeSlashes(fileName); + var sourceFile = getSourceFile(fileName); + var node = ts.getTouchingWord(sourceFile, position); + // Can only rename an identifier. + if (node && node.kind === ts.SyntaxKind.Identifier) { + var symbol = typeInfoResolver.getSymbolAtLocation(node); + // Only allow a symbol to be renamed if it actually has at least one declaration. + if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { + var kind = getSymbolKind(symbol, typeInfoResolver, node); + if (kind) { + return getRenameInfo(symbol.name, typeInfoResolver.getFullyQualifiedName(symbol), kind, getSymbolModifiers(symbol), ts.createTextSpan(node.getStart(), node.getWidth())); + } + } + } + return getRenameInfoError(ts.getLocaleSpecificMessage(ts.Diagnostics.You_cannot_rename_this_element.key)); + function getRenameInfoError(localizedErrorMessage) { + return { + canRename: false, + localizedErrorMessage: ts.getLocaleSpecificMessage(ts.Diagnostics.You_cannot_rename_this_element.key), + displayName: undefined, + fullDisplayName: undefined, + kind: undefined, + kindModifiers: undefined, + triggerSpan: undefined + }; + } + function getRenameInfo(displayName, fullDisplayName, kind, kindModifiers, triggerSpan) { + return { + canRename: true, + localizedErrorMessage: undefined, + displayName, + fullDisplayName, + kind, + kindModifiers, + triggerSpan + }; + } + } + return { + dispose, + cleanupSemanticCache, + getSyntacticDiagnostics, + getSemanticDiagnostics, + getCompilerOptionsDiagnostics, + getSyntacticClassifications, + getSemanticClassifications, + getCompletionsAtPosition, + getCompletionEntryDetails, + getSignatureHelpItems, + getQuickInfoAtPosition, + getDefinitionAtPosition, + getReferencesAtPosition, + getOccurrencesAtPosition, + getNameOrDottedNameSpan, + getBreakpointStatementAtPosition, + getNavigateToItems, + getRenameInfo, + findRenameLocations, + getNavigationBarItems, + getOutliningSpans, + getTodoComments, + getBraceMatchingAtPosition, + getIndentationAtPosition, + getFormattingEditsForRange, + getFormattingEditsForDocument, + getFormattingEditsAfterKeystroke, + getEmitOutput, + getSourceFile: getCurrentSourceFile, + }; + } + ts.createLanguageService = createLanguageService; + /// Classifier + function createClassifier() { + var scanner = ts.createScanner(ts.ScriptTarget.Latest, false); + /// We do not have a full parser support to know when we should parse a regex or not + /// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where + /// we have a series of divide operator. this list allows us to be more accurate by ruling out + /// locations where a regexp cannot exist. + var noRegexTable = []; + noRegexTable[ts.SyntaxKind.Identifier] = true; + noRegexTable[ts.SyntaxKind.StringLiteral] = true; + noRegexTable[ts.SyntaxKind.NumericLiteral] = true; + noRegexTable[ts.SyntaxKind.RegularExpressionLiteral] = true; + noRegexTable[ts.SyntaxKind.ThisKeyword] = true; + noRegexTable[ts.SyntaxKind.PlusPlusToken] = true; + noRegexTable[ts.SyntaxKind.MinusMinusToken] = true; + noRegexTable[ts.SyntaxKind.CloseParenToken] = true; + noRegexTable[ts.SyntaxKind.CloseBracketToken] = true; + noRegexTable[ts.SyntaxKind.CloseBraceToken] = true; + noRegexTable[ts.SyntaxKind.TrueKeyword] = true; + noRegexTable[ts.SyntaxKind.FalseKeyword] = true; + function isAccessibilityModifier(kind) { + switch (kind) { + case ts.SyntaxKind.PublicKeyword: + case ts.SyntaxKind.PrivateKeyword: + case ts.SyntaxKind.ProtectedKeyword: + return true; + } + return false; + } + /** Returns true if 'keyword2' can legally follow 'keyword1' in any language construct. */ + function canFollow(keyword1, keyword2) { + if (isAccessibilityModifier(keyword1)) { + if (keyword2 === ts.SyntaxKind.GetKeyword || keyword2 === ts.SyntaxKind.SetKeyword || keyword2 === ts.SyntaxKind.ConstructorKeyword || keyword2 === ts.SyntaxKind.StaticKeyword) { + // Allow things like "public get", "public constructor" and "public static". + // These are all legal. + return true; + } + // Any other keyword following "public" is actually an identifier an not a real + // keyword. + return false; + } + // Assume any other keyword combination is legal. This can be refined in the future + // if there are more cases we want the classifier to be better at. + return true; + } + // 'classifyKeywordsInGenerics' should be 'true' when a syntactic classifier is not present. + function getClassificationsForLine(text, lexState, classifyKeywordsInGenerics) { + var offset = 0; + var token = ts.SyntaxKind.Unknown; + var lastNonTriviaToken = ts.SyntaxKind.Unknown; + switch (lexState) { + case 3 /* InDoubleQuoteStringLiteral */: + text = '"\\\n' + text; + offset = 3; + break; + case 2 /* InSingleQuoteStringLiteral */: + text = "'\\\n" + text; + offset = 3; + break; + case 1 /* InMultiLineCommentTrivia */: + text = "/*\n" + text; + offset = 3; + break; + } + scanner.setText(text); + var result = { + finalLexState: 0 /* Start */, + entries: [] + }; + // We can run into an unfortunate interaction between the lexical and syntactic classifier + // when the user is typing something generic. Consider the case where the user types: + // + // Foo tokens. It's a weak heuristic, but should + // work well enough in practice. + var angleBracketStack = 0; + do { + token = scanner.scan(); + if (!ts.isTrivia(token)) { + if ((token === ts.SyntaxKind.SlashToken || token === ts.SyntaxKind.SlashEqualsToken) && !noRegexTable[lastNonTriviaToken]) { + if (scanner.reScanSlashToken() === ts.SyntaxKind.RegularExpressionLiteral) { + token = ts.SyntaxKind.RegularExpressionLiteral; + } + } + else if (lastNonTriviaToken === ts.SyntaxKind.DotToken && isKeyword(token)) { + token = ts.SyntaxKind.Identifier; + } + else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) { + // We have two keywords in a row. Only treat the second as a keyword if + // it's a sequence that could legally occur in the language. Otherwise + // treat it as an identifier. This way, if someone writes "private var" + // we recognize that 'var' is actually an identifier here. + token = ts.SyntaxKind.Identifier; + } + else if (lastNonTriviaToken === ts.SyntaxKind.Identifier && token === ts.SyntaxKind.LessThanToken) { + // Could be the start of something generic. Keep track of that by bumping + // up the current count of generic contexts we may be in. + angleBracketStack++; + } + else if (token === ts.SyntaxKind.GreaterThanToken && angleBracketStack > 0) { + // If we think we're currently in something generic, then mark that that + // generic entity is complete. + angleBracketStack--; + } + else if (token === ts.SyntaxKind.AnyKeyword || token === ts.SyntaxKind.StringKeyword || token === ts.SyntaxKind.NumberKeyword || token === ts.SyntaxKind.BooleanKeyword) { + if (angleBracketStack > 0 && !classifyKeywordsInGenerics) { + // If it looks like we're could be in something generic, don't classify this + // as a keyword. We may just get overwritten by the syntactic classifier, + // causing a noisy experience for the user. + token = ts.SyntaxKind.Identifier; + } + } + lastNonTriviaToken = token; + } + processToken(); + } while (token !== ts.SyntaxKind.EndOfFileToken); + return result; + function processToken() { + var start = scanner.getTokenPos(); + var end = scanner.getTextPos(); + addResult(end - start, classFromKind(token)); + if (end >= text.length) { + if (token === ts.SyntaxKind.StringLiteral) { + // Check to see if we finished up on a multiline string literal. + var tokenText = scanner.getTokenText(); + if (scanner.isUnterminated()) { + var lastCharIndex = tokenText.length - 1; + var numBackslashes = 0; + while (tokenText.charCodeAt(lastCharIndex - numBackslashes) === ts.CharacterCodes.backslash) { + numBackslashes++; + } + // If we have an odd number of backslashes, then the multiline string is unclosed + if (numBackslashes & 1) { + var quoteChar = tokenText.charCodeAt(0); + result.finalLexState = quoteChar === ts.CharacterCodes.doubleQuote ? 3 /* InDoubleQuoteStringLiteral */ : 2 /* InSingleQuoteStringLiteral */; + } + } + } + else if (token === ts.SyntaxKind.MultiLineCommentTrivia) { + // Check to see if the multiline comment was unclosed. + if (scanner.isUnterminated()) { + result.finalLexState = 1 /* InMultiLineCommentTrivia */; + } + } + } + } + function addResult(length, classification) { + 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: classification }); + } + } + } + function isBinaryExpressionOperatorToken(token) { + switch (token) { + case ts.SyntaxKind.AsteriskToken: + case ts.SyntaxKind.SlashToken: + case ts.SyntaxKind.PercentToken: + case ts.SyntaxKind.PlusToken: + case ts.SyntaxKind.MinusToken: + case ts.SyntaxKind.LessThanLessThanToken: + case ts.SyntaxKind.GreaterThanGreaterThanToken: + case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + case ts.SyntaxKind.LessThanToken: + case ts.SyntaxKind.GreaterThanToken: + case ts.SyntaxKind.LessThanEqualsToken: + case ts.SyntaxKind.GreaterThanEqualsToken: + case ts.SyntaxKind.InstanceOfKeyword: + case ts.SyntaxKind.InKeyword: + case ts.SyntaxKind.EqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsToken: + case ts.SyntaxKind.EqualsEqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsEqualsToken: + case ts.SyntaxKind.AmpersandToken: + case ts.SyntaxKind.CaretToken: + case ts.SyntaxKind.BarToken: + case ts.SyntaxKind.AmpersandAmpersandToken: + case ts.SyntaxKind.BarBarToken: + case ts.SyntaxKind.BarEqualsToken: + case ts.SyntaxKind.AmpersandEqualsToken: + case ts.SyntaxKind.CaretEqualsToken: + case ts.SyntaxKind.LessThanLessThanEqualsToken: + case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: + case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + case ts.SyntaxKind.PlusEqualsToken: + case ts.SyntaxKind.MinusEqualsToken: + case ts.SyntaxKind.AsteriskEqualsToken: + case ts.SyntaxKind.SlashEqualsToken: + case ts.SyntaxKind.PercentEqualsToken: + case ts.SyntaxKind.EqualsToken: + case ts.SyntaxKind.CommaToken: + return true; + default: return false; + } + } + function isPrefixUnaryExpressionOperatorToken(token) { + switch (token) { + case ts.SyntaxKind.PlusToken: + case ts.SyntaxKind.MinusToken: + case ts.SyntaxKind.TildeToken: + case ts.SyntaxKind.ExclamationToken: + case ts.SyntaxKind.PlusPlusToken: + case ts.SyntaxKind.MinusMinusToken: + return true; + default: + return false; + } + } + function isKeyword(token) { + return token >= ts.SyntaxKind.FirstKeyword && token <= ts.SyntaxKind.LastKeyword; + } + function classFromKind(token) { + if (isKeyword(token)) { + return 1 /* Keyword */; + } + else if (isBinaryExpressionOperatorToken(token) || isPrefixUnaryExpressionOperatorToken(token)) { + return 2 /* Operator */; + } + else if (token >= ts.SyntaxKind.FirstPunctuation && token <= ts.SyntaxKind.LastPunctuation) { + return 0 /* Punctuation */; + } + switch (token) { + case ts.SyntaxKind.NumericLiteral: + return 6 /* NumberLiteral */; + case ts.SyntaxKind.StringLiteral: + return 7 /* StringLiteral */; + case ts.SyntaxKind.RegularExpressionLiteral: + return 8 /* RegExpLiteral */; + case ts.SyntaxKind.ConflictMarkerTrivia: + case ts.SyntaxKind.MultiLineCommentTrivia: + case ts.SyntaxKind.SingleLineCommentTrivia: + return 3 /* Comment */; + case ts.SyntaxKind.WhitespaceTrivia: + return 4 /* Whitespace */; + case ts.SyntaxKind.Identifier: + default: + return 5 /* Identifier */; + } + } + return { getClassificationsForLine }; + } + ts.createClassifier = createClassifier; + /** + * Get the path of the default library file (lib.d.ts) as distributed with the typescript + * node package. + * The functionality is not supported if the ts module is consumed outside of a node module. + */ + function getDefaultLibraryFilePath(options) { + if (typeof module !== "undefined" && module.exports) { + return __dirname + ts.directorySeparator + ts.getDefaultLibraryFilename(options); + } + throw new Error("getDefaultLibraryFilename is only supported when consumed as a node module. "); + } + ts.getDefaultLibraryFilePath = getDefaultLibraryFilePath; + function initializeServices() { + ts.objectAllocator = { + getNodeConstructor: function (kind) { + function Node() { + } + var proto = kind === ts.SyntaxKind.SourceFile ? new SourceFileObject() : new NodeObject(); + proto.kind = kind; + proto.pos = 0; + proto.end = 0; + proto.flags = 0; + proto.parent = undefined; + Node.prototype = proto; + return Node; + }, + getSymbolConstructor: function () { return SymbolObject; }, + getTypeConstructor: function () { return TypeObject; }, + getSignatureConstructor: function () { return SignatureObject; }, + }; + } + initializeServices(); +})(ts || (ts = {})); diff --git a/src/services/services.ts b/src/services/services.ts index 253435f0d39..875b67aba0f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -9,7 +9,7 @@ /// module ts { - export var servicesVersion = "0.4.1" + export var servicesVersion = "0.4"; export interface Node { getSourceFile(): SourceFile;