diff --git a/src/services/services.ts b/src/services/services.ts index 3100b99885a..1ef25e2cb0e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -38,6 +38,7 @@ module ts { getFullWidth(): number; getLeadingTriviaWidth(sourceFile?: SourceFile): number; getFullText(sourceFile?: SourceFile): string; + getText(sourceFile?: SourceFile): string; getFirstToken(sourceFile?: SourceFile): Node; getLastToken(sourceFile?: SourceFile): Node; } @@ -130,6 +131,10 @@ module ts { return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); } + public getText(sourceFile?: SourceFile): string { + return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); + } + private addSyntheticNodes(nodes: Node[], pos: number, end: number): number { scanner.setTextPos(pos); while (pos < end) { @@ -1287,7 +1292,6 @@ module ts { position: number; // position in the file where the completion was requested entries: CompletionEntry[]; // entries for this completion symbols: Map; // symbols by entry name map - location: Node; // the node where the completion was requested typeChecker: TypeChecker; // the typeChecker used to generate this completion } @@ -1957,21 +1961,21 @@ module ts { } function isRightSideOfPropertyAccess(node: Node) { - return node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).right === node; + return node && node.parent && node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).right === node; } function isCallExpressionTarget(node: Node): boolean { if (isRightSideOfPropertyAccess(node)) { node = node.parent; } - return node.parent.kind === SyntaxKind.CallExpression && (node.parent).func === node; + return node && node.parent && node.parent.kind === SyntaxKind.CallExpression && (node.parent).func === node; } function isNewExpressionTarget(node: Node): boolean { if (isRightSideOfPropertyAccess(node)) { node = node.parent; } - return node.parent.kind === SyntaxKind.NewExpression && (node.parent).func === node; + return node && node.parent && node.parent.kind === SyntaxKind.NewExpression && (node.parent).func === node; } function isNameOfModuleDeclaration(node: Node) { @@ -2014,6 +2018,37 @@ module ts { (node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).externalModuleName === node)); } + /** Returns true if the position is within a comment */ + function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean { + // The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment + return position <= token.getStart(sourceFile) && + (isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) || + isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart()))); + + function isInsideCommentRange(comments: CommentRange[]): boolean { + return forEach(comments, 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) === CharacterCodes.slash) { + return true; + } + else { + // is unterminated multi-line comment + return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash && + text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk); + } + } + return false; + }); + } + } + enum SemanticMeaning { None = 0x0, Value = 0x1, @@ -2317,6 +2352,135 @@ module ts { } function getCompletionsAtPosition(filename: string, position: number, isMemberCompletion: boolean) { + synchronizeHostData(); + + filename = TypeScript.switchToForwardSlashes(filename); + + var sourceFile = getSourceFile(filename); + + var currentToken = getTokenAtPosition(sourceFile, position); + + // Completion not allowed inside comments, bail out if this is the case + if (isInsideComment(sourceFile, currentToken, position)) { + host.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 previousToken = findPrecedingToken(position, sourceFile); + + // 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 === SyntaxKind.Identifier) { + previousToken = findPrecedingToken(previousToken.pos, sourceFile); + } + + // Check if this is a valid completion location + if (previousToken && isCompletionListBlocker(previousToken)) { + host.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: Node; + var isRightOfDot: boolean; + if (previousToken && previousToken.kind === SyntaxKind.DotToken && + (previousToken.parent.kind === SyntaxKind.PropertyAccess || previousToken.parent.kind === 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 + }; + + // Populate the completion list + if (isRightOfDot) { + // Right of dot member completion list + var symbols: Symbol[] = []; + isMemberCompletion = true; + + if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccess) { + var symbol = typeInfoResolver.getSymbolInfo(node); + + // This is an alias, follow what it aliases + if (symbol && symbol.flags & SymbolFlags.Import) { + symbol = typeInfoResolver.getAliasedSymbol(symbol); + } + + if (symbol && symbol.flags & SymbolFlags.HasExports) { + // Extract module or enum members + forEachValue(symbol.exports, symbol => { + if (typeInfoResolver.isValidPropertyAccess((node.parent), symbol.name)) { + symbols.push(symbol); + } + }); + } + } + + var type = typeInfoResolver.getTypeOfNode(node); + if (type) { + // Filter private properties + forEach(type.getApparentProperties(), 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 = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | 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); + } + + return { + isMemberCompletion: isMemberCompletion, + entries: activeCompletionSession.entries + }; + function getCompletionEntriesFromSymbols(symbols: Symbol[], session: CompletionSession): void { forEach(symbols, symbol => { var entry = createCompletionEntry(symbol, session.typeChecker); @@ -2327,40 +2491,47 @@ module ts { }); } - function isCompletionListBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { - // We shouldn't be getting a position that is outside the file because - // isEntirelyInsideComment can't handle when the position is out of bounds, - // callers should be fixed, however we should be resilient to bad inputs - // so we return true (this position is a blocker for getting completions) - if (position < 0 || position > TypeScript.fullWidth(sourceUnit)) { - return true; - } - - // This method uses Fidelity completely. Some information can be reached using the AST, but not everything. - return TypeScript.Syntax.isEntirelyInsideComment(sourceUnit, position) || - TypeScript.Syntax.isEntirelyInStringOrRegularExpressionLiteral(sourceUnit, position) || - isIdentifierDefinitionLocation(sourceUnit, position) || - isRightOfIllegalDot(sourceUnit, position); + function isCompletionListBlocker(previousToken: Node): boolean { + return isInStringOrRegularExpressionLiteral(previousToken) || + isIdentifierDefinitionLocation(previousToken) || + isRightOfIllegalDot(previousToken); } - function getContainingObjectLiteralApplicableForCompletion(sourceUnit: TypeScript.SourceUnitSyntax, position: number): TypeScript.ISyntaxElement { + function isInStringOrRegularExpressionLiteral(previousToken: Node): boolean { + if (previousToken.kind === SyntaxKind.StringLiteral) { + // The position has to be either: 1. entirely within the token text, or + // 2. at the end position, and the string literal is not terminated + var start = previousToken.getStart(); + var end = previousToken.getEnd(); + if (start < position && position < end) { + return true; + } + else if (position === end) { + var width = end - start; + var text = previousToken.getSourceFile().text; + return width <= 1 || + text.charCodeAt(start) !== text.charCodeAt(end - 1) || + text.charCodeAt(end - 2) === CharacterCodes.backslash; + } + } + else if (previousToken.kind === SyntaxKind.RegularExpressionLiteral) { + return previousToken.getStart() < position && position < previousToken.getEnd(); + } + return false; + } + + function getContainingObjectLiteralApplicableForCompletion(previousToken: Node): ObjectLiteral { // The locations in an object literal expression that are applicable for completion are property name definition locations. - var previousToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); if (previousToken) { var parent = previousToken.parent; - switch (previousToken.kind()) { - case TypeScript.SyntaxKind.OpenBraceToken: // var x = { | - case TypeScript.SyntaxKind.CommaToken: // var x = { a: 0, | - if (parent && parent.kind() === TypeScript.SyntaxKind.SeparatedList) { - parent = parent.parent; + switch (previousToken.kind) { + case SyntaxKind.OpenBraceToken: // var x = { | + case SyntaxKind.CommaToken: // var x = { a: 0, | + if (parent && parent.kind === SyntaxKind.ObjectLiteral) { + return parent; } - - if (parent && parent.kind() === TypeScript.SyntaxKind.ObjectLiteralExpression) { - return parent; - } - break; } } @@ -2368,47 +2539,71 @@ module ts { return undefined; } - function isIdentifierDefinitionLocation(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { - var positionedToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); + function isFunction(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.Method: + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + return true; + } + return false; + } - if (positionedToken) { - var containingNodeKind = TypeScript.Syntax.containingNode(positionedToken) && TypeScript.Syntax.containingNode(positionedToken).kind(); - switch (positionedToken.kind()) { - case TypeScript.SyntaxKind.CommaToken: - return containingNodeKind === TypeScript.SyntaxKind.ParameterList || - containingNodeKind === TypeScript.SyntaxKind.VariableDeclaration || - containingNodeKind === TypeScript.SyntaxKind.EnumDeclaration; // enum { foo, | + function isIdentifierDefinitionLocation(previousToken: Node): boolean { + if (previousToken) { + var containingNodeKind = previousToken.parent.kind; + switch (previousToken.kind) { + case SyntaxKind.CommaToken: + return containingNodeKind === SyntaxKind.VariableDeclaration || + containingNodeKind === SyntaxKind.VariableStatement || + containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { foo, | + isFunction(containingNodeKind); - case TypeScript.SyntaxKind.OpenParenToken: - return containingNodeKind === TypeScript.SyntaxKind.ParameterList || - containingNodeKind === TypeScript.SyntaxKind.CatchClause; + case SyntaxKind.OpenParenToken: + return containingNodeKind === SyntaxKind.CatchBlock || + isFunction(containingNodeKind); - case TypeScript.SyntaxKind.OpenBraceToken: - return containingNodeKind === TypeScript.SyntaxKind.EnumDeclaration; // enum { | + case SyntaxKind.OpenBraceToken: + return containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { | + containingNodeKind === SyntaxKind.InterfaceDeclaration; // interface a { | - case TypeScript.SyntaxKind.PublicKeyword: - case TypeScript.SyntaxKind.PrivateKeyword: - case TypeScript.SyntaxKind.StaticKeyword: - case TypeScript.SyntaxKind.DotDotDotToken: - return containingNodeKind === TypeScript.SyntaxKind.Parameter; + case SyntaxKind.SemicolonToken: + return containingNodeKind === SyntaxKind.Property && + previousToken.parent.parent.kind === SyntaxKind.InterfaceDeclaration; // interface a { f; | - case TypeScript.SyntaxKind.ClassKeyword: - case TypeScript.SyntaxKind.ModuleKeyword: - case TypeScript.SyntaxKind.EnumKeyword: - case TypeScript.SyntaxKind.InterfaceKeyword: - case TypeScript.SyntaxKind.FunctionKeyword: - case TypeScript.SyntaxKind.VarKeyword: - case TypeScript.SyntaxKind.GetKeyword: - case TypeScript.SyntaxKind.SetKeyword: + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.DotDotDotToken: + return containingNodeKind === SyntaxKind.Parameter; + + case SyntaxKind.ClassKeyword: + case SyntaxKind.ModuleKeyword: + case SyntaxKind.EnumKeyword: + case SyntaxKind.InterfaceKeyword: + case SyntaxKind.FunctionKeyword: + case SyntaxKind.VarKeyword: + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: return true; } // Previous token may have been a keyword that was converted to an identifier. - switch (positionedToken.text()) { + switch (previousToken.getText()) { case "class": case "interface": case "enum": case "module": + case "function": + case "var": + // TODO: add let and const return true; } } @@ -2416,44 +2611,15 @@ module ts { return false; } - function getNonIdentifierCompleteTokenOnLeft(sourceUnit: TypeScript.SourceUnitSyntax, position: number): TypeScript.ISyntaxToken { - var positionedToken = TypeScript.Syntax.findCompleteTokenOnLeft(sourceUnit, position, /*includeSkippedTokens*/true); - - if (positionedToken && position === TypeScript.end(positionedToken) && positionedToken.kind() == TypeScript.SyntaxKind.EndOfFileToken) { - // EndOfFile token is not interesting, get the one before it - positionedToken = TypeScript. previousToken(positionedToken, /*includeSkippedTokens*/true); - } - - if (positionedToken && position === TypeScript.end(positionedToken) && positionedToken.kind() === TypeScript.SyntaxKind.IdentifierName) { - // The caret is at the end of an identifier, the decision to provide completion depends on the previous token - positionedToken = TypeScript.previousToken(positionedToken, /*includeSkippedTokens*/true); - } - - return positionedToken; - } - - function isRightOfIllegalDot(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { - var positionedToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); - - if (positionedToken) { - switch (positionedToken.kind()) { - case TypeScript.SyntaxKind.DotToken: - var leftOfDotPositionedToken = TypeScript.previousToken(positionedToken, /*includeSkippedTokens*/true); - return leftOfDotPositionedToken && leftOfDotPositionedToken.kind() === TypeScript.SyntaxKind.NumericLiteral; - - case TypeScript.SyntaxKind.NumericLiteral: - var text = positionedToken.text(); - return text.charAt(text.length - 1) === "."; - } + function isRightOfIllegalDot(previousToken: Node): boolean { + if (previousToken && previousToken.kind === SyntaxKind.NumericLiteral) { + var text = previousToken.getFullText(); + return text.charAt(text.length - 1) === "."; } return false; } - function isPunctuation(kind: SyntaxKind) { - return (SyntaxKind.FirstPunctuation <= kind && kind <= SyntaxKind.LastPunctuation); - } - function filterContextualMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] { if (!existingMembers || existingMembers.length === 0) { return contextualMemberSymbols; @@ -2483,162 +2649,6 @@ module ts { return filteredMembers; } - - synchronizeHostData(); - - filename = TypeScript.switchToForwardSlashes(filename); - - var sourceFile = getSourceFile(filename); - var sourceUnit = sourceFile.getSourceUnit(); - - if (isCompletionListBlocker(sourceFile.getSyntaxTree().sourceUnit(), position)) { - host.log("Returning an empty list because completion was blocked."); - return null; - } - - var node = TypeScript.ASTHelpers.getAstAtPosition(sourceUnit, position, /*useTrailingTriviaAsLimChar*/ true, /*forceInclusive*/ true); - - if (node && node.kind() === TypeScript.SyntaxKind.IdentifierName && - TypeScript.start(node) === TypeScript.end(node)) { - // Ignore missing name nodes - node = node.parent; - } - - var isRightOfDot = false; - if (node && - node.kind() === TypeScript.SyntaxKind.MemberAccessExpression && - TypeScript.end((node).expression) < position) { - - isRightOfDot = true; - node = (node).expression; - } - else if (node && - node.kind() === TypeScript.SyntaxKind.QualifiedName && - TypeScript.end((node).left) < position) { - - isRightOfDot = true; - node = (node).left; - } - else if (node && node.parent && - node.kind() === TypeScript.SyntaxKind.IdentifierName && - node.parent.kind() === TypeScript.SyntaxKind.MemberAccessExpression && - (node.parent).name === node) { - - isRightOfDot = true; - node = (node.parent).expression; - } - else if (node && node.parent && - node.kind() === TypeScript.SyntaxKind.IdentifierName && - node.parent.kind() === TypeScript.SyntaxKind.QualifiedName && - (node.parent).right === node) { - - isRightOfDot = true; - node = (node.parent).left; - } - - // TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node - var precedingToken = findTokenOnLeftOfPosition(sourceFile, TypeScript.end(node)); - var mappedNode: Node; - if (!precedingToken) { - mappedNode = sourceFile; - } - else if (isPunctuation(precedingToken.kind)) { - mappedNode = precedingToken.parent; - } - else { - mappedNode = precedingToken; - } - - Debug.assert(mappedNode, "Could not map a Fidelity node to an AST node"); - - // Get the completions - activeCompletionSession = { - filename: filename, - position: position, - entries: [], - symbols: {}, - location: mappedNode, - typeChecker: typeInfoResolver - }; - - // Right of dot member completion list - if (isRightOfDot) { - var symbols: Symbol[] = []; - isMemberCompletion = true; - - if (mappedNode.kind === SyntaxKind.Identifier || mappedNode.kind === SyntaxKind.QualifiedName || mappedNode.kind === SyntaxKind.PropertyAccess) { - var symbol = typeInfoResolver.getSymbolInfo(mappedNode); - - // This is an alias, follow what it aliases - if (symbol && symbol.flags & SymbolFlags.Import) { - symbol = typeInfoResolver.getAliasedSymbol(symbol); - } - - if (symbol && symbol.flags & SymbolFlags.HasExports) { - // Extract module or enum members - forEachValue(symbol.exports, symbol => { - if (typeInfoResolver.isValidPropertyAccess((mappedNode.parent), symbol.name)) { - symbols.push(symbol); - } - }); - } - } - - var type = typeInfoResolver.getTypeOfNode(mappedNode); - if (type) { - // Filter private properties - forEach(type.getApparentProperties(), symbol => { - if (typeInfoResolver.isValidPropertyAccess((mappedNode.parent), symbol.name)) { - symbols.push(symbol); - } - }); - } - - getCompletionEntriesFromSymbols(symbols, activeCompletionSession); - } - else { - var containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(sourceFile.getSyntaxTree().sourceUnit(), position); - - // Object literal expression, look up possible property names from contextual type - if (containingObjectLiteral) { - var objectLiteral = (mappedNode.kind === SyntaxKind.ObjectLiteral ? mappedNode : getAncestor(mappedNode, SyntaxKind.ObjectLiteral)); - - Debug.assert(objectLiteral); - - isMemberCompletion = true; - - var contextualType = typeInfoResolver.getContextualType(objectLiteral); - 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, objectLiteral.properties); - getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession); - } - } - // Get scope members - else { - isMemberCompletion = false; - /// TODO filter meaning based on the current context - var symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Import; - var symbols = typeInfoResolver.getSymbolsInScope(mappedNode, symbolMeanings); - - getCompletionEntriesFromSymbols(symbols, activeCompletionSession); - } - } - - // Add keywords if this is not a member completion list - if (!isMemberCompletion) { - Array.prototype.push.apply(activeCompletionSession.entries, keywordCompletions); - } - - return { - isMemberCompletion: isMemberCompletion, - entries: activeCompletionSession.entries - }; } function getCompletionEntryDetails(filename: string, position: number, entryName: string): CompletionEntryDetails { @@ -2646,6 +2656,8 @@ module ts { // in the getCompletionsAtPosition earlier filename = TypeScript.switchToForwardSlashes(filename); + var sourceFile = getSourceFile(filename); + var session = activeCompletionSession; // Ensure that the current active completion session is still valid for this request @@ -2662,7 +2674,8 @@ module ts { // 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. - var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), session.location, session.typeChecker, session.location, SemanticMeaning.All); + var location = getTouchingPropertyName(sourceFile, position); + var displayPartsDocumentationsAndSymbolKind = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, getSourceFile(filename), location, session.typeChecker, location, SemanticMeaning.All); return { name: entryName, kind: displayPartsDocumentationsAndSymbolKind.symbolKind, @@ -2824,14 +2837,24 @@ module ts { var type = typeResolver.getTypeOfSymbol(symbol); if (type) { - if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { - // try get the call/construct signature from the type if it matches - var callExpression: CallExpression; - if (location.parent.kind === SyntaxKind.PropertyAccess && (location.parent).right === location) { + if (location.parent && location.parent.kind === SyntaxKind.PropertyAccess) { + var right = (location.parent).right; + // 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.kind === SyntaxKind.Missing)){ location = location.parent; } - callExpression = location.parent; + } + // try get the call/construct signature from the type if it matches + var callExpression: CallExpression; + if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) { + callExpression = location; + } + else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { + callExpression = location.parent; + } + + if (callExpression) { var candidateSignatures: Signature[] = []; signature = typeResolver.getResolvedSignature(callExpression, candidateSignatures); if (!signature && candidateSignatures.length) { @@ -5132,16 +5155,7 @@ module ts { // OK, we have found a match in the file. This is only an acceptable match if // it is contained within a comment. var token = getTokenAtPosition(sourceFile, matchPosition); - - if (token.getStart() <= matchPosition && matchPosition < token.getEnd()) { - // match was within the token itself. Not in the comment. Keep searching - // descriptor. - continue; - } - - // Looks to be within the trivia. See if we can find the comment containing it. - if (!getContainingComment(getTrailingCommentRanges(fileContents, token.getFullStart()), matchPosition) && - !getContainingComment(getLeadingCommentRanges(fileContents, token.getFullStart()), matchPosition)) { + if (!isInsideComment(sourceFile, token, matchPosition)) { continue; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index ca17b704d05..f6396bebf9b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -95,11 +95,12 @@ module ts { var child = current.getChildAt(i); var start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile); if (start <= position) { - if (position < child.getEnd()) { + var end = child.getEnd(); + if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) { current = child; continue outer; } - else if (includeItemAtEndPosition && child.getEnd() === position) { + else if (includeItemAtEndPosition && end === position) { var previousToken = findPrecedingToken(position, sourceFile, child); if (previousToken && includeItemAtEndPosition(previousToken)) { return previousToken; @@ -180,7 +181,7 @@ module ts { for (var i = 0, len = children.length; i < len; ++i) { var child = children[i]; if (nodeHasTokens(child)) { - if (position < child.end) { + if (position <= child.end) { if (child.getStart(sourceFile) >= position) { // actual start of the node is past the position - previous token should be at the end of previous child var candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i); diff --git a/tests/cases/fourslash/commentsExternalModules.ts b/tests/cases/fourslash/commentsExternalModules.ts index c0e27149202..ad0440a3218 100644 --- a/tests/cases/fourslash/commentsExternalModules.ts +++ b/tests/cases/fourslash/commentsExternalModules.ts @@ -64,7 +64,7 @@ goTo.marker('7'); verify.quickInfoIs("(var) myvar: m1.m2.c", ""); goTo.marker('8'); -verify.memberListContains("c", "class m1.m2.c", "class comment;"); +verify.memberListContains("c", "(constructor) m1.m2.c(): m1.m2.c", ""); verify.memberListContains("i", "(var) m1.m2.i: m1.m2.c", "i"); goTo.file("commentsExternalModules_file1.ts"); @@ -91,5 +91,5 @@ goTo.marker('14'); verify.quickInfoIs("(var) newVar: extMod.m1.m2.c", ""); goTo.marker('15'); -verify.memberListContains("c", "class extMod.m1.m2.c", "class comment;"); +verify.memberListContains("c", "(constructor) extMod.m1.m2.c(): extMod.m1.m2.c", ""); verify.memberListContains("i", "(var) extMod.m1.m2.i: extMod.m1.m2.c", "i"); diff --git a/tests/cases/fourslash/commentsFunction.ts b/tests/cases/fourslash/commentsFunction.ts index 3a4e4e4dcc2..3af718fc967 100644 --- a/tests/cases/fourslash/commentsFunction.ts +++ b/tests/cases/fourslash/commentsFunction.ts @@ -74,7 +74,7 @@ goTo.marker('12'); verify.quickInfoIs("(var) lambddaNoVarComment: (a: number, b: number) => number", ""); goTo.marker('13'); -verify.completionListContains('lambdaFoo', '(var) lambdaFoo: (a: number, b: number) => number', 'lamdaFoo var comment'); +verify.completionListContains('lambdaFoo', '(var) lambdaFoo: (a: number, b: number) => number', ''); verify.completionListContains('lambddaNoVarComment', '(var) lambddaNoVarComment: (a: number, b: number) => number', ''); goTo.marker('14'); diff --git a/tests/cases/fourslash/commentsImportDeclaration.ts b/tests/cases/fourslash/commentsImportDeclaration.ts index f115f3607b7..88abbed8618 100644 --- a/tests/cases/fourslash/commentsImportDeclaration.ts +++ b/tests/cases/fourslash/commentsImportDeclaration.ts @@ -47,5 +47,5 @@ goTo.marker('9'); verify.quickInfoIs("(var) newVar: extMod.m1.m2.c", ""); goTo.marker('10'); -verify.memberListContains("c", "class extMod.m1.m2.c", "class comment;"); +verify.memberListContains("c", "(constructor) extMod.m1.m2.c(): extMod.m1.m2.c", ""); verify.memberListContains("i", "(var) extMod.m1.m2.i: extMod.m1.m2.c", "i"); diff --git a/tests/cases/fourslash/commentsInheritance.ts b/tests/cases/fourslash/commentsInheritance.ts index eaca1d79ccb..e8c2084d38d 100644 --- a/tests/cases/fourslash/commentsInheritance.ts +++ b/tests/cases/fourslash/commentsInheritance.ts @@ -223,7 +223,7 @@ goTo.marker('1'); verify.memberListContains("i1_p1", "(property) i1.i1_p1: number", "i1_p1"); verify.memberListContains("i1_f1", "(method) i1.i1_f1(): void", "i1_f1"); -verify.memberListContains("i1_l1", "(property) i1.i1_l1: () => void", "i1_l1"); +verify.memberListContains("i1_l1", "(property) i1.i1_l1: () => void", ""); verify.memberListContains("i1_nc_p1", "(property) i1.i1_nc_p1: number", ""); verify.memberListContains("i1_nc_f1", "(method) i1.i1_nc_f1(): void", ""); verify.memberListContains("i1_nc_l1", "(property) i1.i1_nc_l1: () => void", ""); @@ -278,10 +278,10 @@ verify.memberListContains("i1_nc_f1", "(method) c1.i1_nc_f1(): void", ""); verify.memberListContains("i1_nc_l1", "(property) c1.i1_nc_l1: () => void", ""); verify.memberListContains("p1", "(property) c1.p1: number", "c1_p1"); verify.memberListContains("f1", "(method) c1.f1(): void", "c1_f1"); -verify.memberListContains("l1", "(property) c1.l1: () => void", "c1_l1"); +verify.memberListContains("l1", "(property) c1.l1: () => void", ""); verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "c1_nc_p1"); verify.memberListContains("nc_f1", "(method) c1.nc_f1(): void", "c1_nc_f1"); -verify.memberListContains("nc_l1", "(property) c1.nc_l1: () => void", "c1_nc_l1"); +verify.memberListContains("nc_l1", "(property) c1.nc_l1: () => void", ""); goTo.marker('7'); verify.currentSignatureHelpDocCommentIs(""); goTo.marker('8'); @@ -321,7 +321,7 @@ verify.quickInfoIs("(property) c1.nc_l1: () => void", ""); goTo.marker('11'); verify.memberListContains("i1_p1", "(property) i1.i1_p1: number", "i1_p1"); verify.memberListContains("i1_f1", "(method) i1.i1_f1(): void", "i1_f1"); -verify.memberListContains("i1_l1", "(property) i1.i1_l1: () => void", "i1_l1"); +verify.memberListContains("i1_l1", "(property) i1.i1_l1: () => void", ""); verify.memberListContains("i1_nc_p1", "(property) i1.i1_nc_p1: number", ""); verify.memberListContains("i1_nc_f1", "(method) i1.i1_nc_f1(): void", ""); verify.memberListContains("i1_nc_l1", "(property) i1.i1_nc_l1: () => void", ""); @@ -508,13 +508,13 @@ verify.completionListContains("c4_i", "(var) c4_i: c4", ""); goTo.marker('36'); verify.memberListContains("i2_p1", "(property) i2.i2_p1: number", "i2_p1"); verify.memberListContains("i2_f1", "(method) i2.i2_f1(): void", "i2_f1"); -verify.memberListContains("i2_l1", "(property) i2.i2_l1: () => void", "i2_l1"); +verify.memberListContains("i2_l1", "(property) i2.i2_l1: () => void", ""); verify.memberListContains("i2_nc_p1", "(property) i2.i2_nc_p1: number", ""); verify.memberListContains("i2_nc_f1", "(method) i2.i2_nc_f1(): void", ""); verify.memberListContains("i2_nc_l1", "(property) i2.i2_nc_l1: () => void", ""); verify.memberListContains("p1", "(property) i2.p1: number", "i2 p1"); verify.memberListContains("f1", "(method) i2.f1(): void", "i2 f1"); -verify.memberListContains("l1", "(property) i2.l1: () => void", "i2 l1"); +verify.memberListContains("l1", "(property) i2.l1: () => void", ""); verify.memberListContains("nc_p1", "(property) i2.nc_p1: number", ""); verify.memberListContains("nc_f1", "(method) i2.nc_f1(): void", ""); verify.memberListContains("nc_l1", "(property) i2.nc_l1: () => void", ""); @@ -559,13 +559,13 @@ verify.quickInfoIs("(property) i2.nc_l1: () => void", ""); goTo.marker('41'); verify.memberListContains("i2_p1", "(property) i2.i2_p1: number", "i2_p1"); verify.memberListContains("i2_f1", "(method) i2.i2_f1(): void", "i2_f1"); -verify.memberListContains("i2_l1", "(property) i2.i2_l1: () => void", "i2_l1"); +verify.memberListContains("i2_l1", "(property) i2.i2_l1: () => void", ""); verify.memberListContains("i2_nc_p1", "(property) i2.i2_nc_p1: number", ""); verify.memberListContains("i2_nc_f1", "(method) i2.i2_nc_f1(): void", ""); verify.memberListContains("i2_nc_l1", "(property) i2.i2_nc_l1: () => void", ""); verify.memberListContains("p1", "(property) i3.p1: number", "i3 p1"); verify.memberListContains("f1", "(method) i3.f1(): void", "i3 f1"); -verify.memberListContains("l1", "(property) i3.l1: () => void", "i3 l1"); +verify.memberListContains("l1", "(property) i3.l1: () => void", ""); verify.memberListContains("nc_p1", "(property) i3.nc_p1: number", ""); verify.memberListContains("nc_f1", "(method) i3.nc_f1(): void", ""); verify.memberListContains("nc_l1", "(property) i3.nc_l1: () => void", ""); @@ -606,13 +606,13 @@ verify.quickInfoIs("(property) i3.nc_l1: () => void", ""); goTo.marker('46'); verify.memberListContains("i2_p1", "(property) i2.i2_p1: number", "i2_p1"); verify.memberListContains("i2_f1", "(method) i2.i2_f1(): void", "i2_f1"); -verify.memberListContains("i2_l1", "(property) i2.i2_l1: () => void", "i2_l1"); +verify.memberListContains("i2_l1", "(property) i2.i2_l1: () => void", ""); verify.memberListContains("i2_nc_p1", "(property) i2.i2_nc_p1: number", ""); verify.memberListContains("i2_nc_f1", "(method) i2.i2_nc_f1(): void", ""); verify.memberListContains("i2_nc_l1", "(property) i2.i2_nc_l1: () => void", ""); verify.memberListContains("p1", "(property) i2.p1: number", "i2 p1"); verify.memberListContains("f1", "(method) i2.f1(): void", "i2 f1"); -verify.memberListContains("l1", "(property) i2.l1: () => void", "i2 l1"); +verify.memberListContains("l1", "(property) i2.l1: () => void", ""); verify.memberListContains("nc_p1", "(property) i2.nc_p1: number", ""); verify.memberListContains("nc_f1", "(method) i2.nc_f1(): void", ""); verify.memberListContains("nc_l1", "(property) i2.nc_l1: () => void", ""); diff --git a/tests/cases/fourslash/commentsInterface.ts b/tests/cases/fourslash/commentsInterface.ts index 02856242cee..d4a3d2dd924 100644 --- a/tests/cases/fourslash/commentsInterface.ts +++ b/tests/cases/fourslash/commentsInterface.ts @@ -235,7 +235,7 @@ verify.completionListContains("i3_i", "(var) i3_i: i3", ""); goTo.marker('41'); verify.quickInfoIs("(method) i3.f(a: number): string", "Function i3 f"); verify.memberListContains("f", "(method) i3.f(a: number): string", "Function i3 f"); -verify.memberListContains("l", "(property) i3.l: (b: number) => string", "i3 l"); +verify.memberListContains("l", "(property) i3.l: (b: number) => string", ""); verify.memberListContains("x", "(property) i3.x: number", "Comment i3 x"); verify.memberListContains("nc_f", "(method) i3.nc_f(a: number): string", ""); verify.memberListContains("nc_l", "(property) i3.nc_l: (b: number) => string", ""); diff --git a/tests/cases/fourslash/commentsModules.ts b/tests/cases/fourslash/commentsModules.ts index 06b45afe89e..90257079569 100644 --- a/tests/cases/fourslash/commentsModules.ts +++ b/tests/cases/fourslash/commentsModules.ts @@ -125,7 +125,7 @@ verify.quickInfoIs("(var) myvar: m1.m2.c", ""); goTo.marker('8'); verify.quickInfoIs("(constructor) m1.m2.c(): m1.m2.c", ""); -verify.memberListContains("c", "class m1.m2.c", "class comment;"); +verify.memberListContains("c", "(constructor) m1.m2.c(): m1.m2.c", ""); verify.memberListContains("i", "(var) m1.m2.i: m1.m2.c", "i"); goTo.marker('9'); @@ -138,7 +138,7 @@ verify.quickInfoIs("module m2.m3", "module comment of m2.m3"); goTo.marker('11'); verify.quickInfoIs("(constructor) m2.m3.c(): m2.m3.c", ""); -verify.memberListContains("c", "class m2.m3.c", "Exported class comment"); +verify.memberListContains("c", "(constructor) m2.m3.c(): m2.m3.c", ""); goTo.marker('12'); verify.completionListContains("m3", "module m3", ""); @@ -153,8 +153,8 @@ verify.memberListContains("m5", "module m3.m4.m5"); verify.quickInfoIs("module m3.m4.m5", "module comment of m3.m4.m5"); goTo.marker('15'); -verify.memberListContains("c", "class m3.m4.m5.c", "Exported class comment"); verify.quickInfoIs("(constructor) m3.m4.m5.c(): m3.m4.m5.c", ""); +verify.memberListContains("c", "(constructor) m3.m4.m5.c(): m3.m4.m5.c", ""); goTo.marker('16'); verify.completionListContains("m4", "module m4", ""); @@ -173,7 +173,7 @@ verify.memberListContains("m7", "module m4.m5.m6.m7"); verify.quickInfoIs("module m4.m5.m6.m7", ""); goTo.marker('20'); -verify.memberListContains("c", "class m4.m5.m6.m7.c", "Exported class comment"); +verify.memberListContains("c", "(constructor) m4.m5.m6.m7.c(): m4.m5.m6.m7.c", ""); verify.quickInfoIs("(constructor) m4.m5.m6.m7.c(): m4.m5.m6.m7.c", ""); goTo.marker('21'); @@ -193,7 +193,7 @@ verify.memberListContains("m8", "module m5.m6.m7.m8"); verify.quickInfoIs("module m5.m6.m7.m8", "module m8 comment"); goTo.marker('25'); -verify.memberListContains("c", "class m5.m6.m7.m8.c", "Exported class comment"); +verify.memberListContains("c", "(constructor) m5.m6.m7.m8.c(): m5.m6.m7.m8.c", ""); verify.quickInfoIs("(constructor) m5.m6.m7.m8.c(): m5.m6.m7.m8.c", ""); goTo.marker('26'); @@ -209,7 +209,7 @@ verify.memberListContains("m8", "module m6.m7.m8"); verify.quickInfoIs("module m6.m7.m8", ""); goTo.marker('29'); -verify.memberListContains("c", "class m6.m7.m8.c", "Exported class comment"); +verify.memberListContains("c", "(constructor) m6.m7.m8.c(): m6.m7.m8.c", ""); verify.quickInfoIs("(constructor) m6.m7.m8.c(): m6.m7.m8.c", ""); goTo.marker('30'); @@ -225,7 +225,7 @@ verify.memberListContains("m9", "module m7.m8.m9"); verify.quickInfoIs("module m7.m8.m9", "module m9 comment"); goTo.marker('33'); -verify.memberListContains("c", "class m7.m8.m9.c", "Exported class comment"); +verify.memberListContains("c", "(constructor) m7.m8.m9.c(): m7.m8.m9.c", ""); verify.quickInfoIs("(constructor) m7.m8.m9.c(): m7.m8.m9.c", ""); goTo.marker('34'); diff --git a/tests/cases/fourslash/commentsOverloads.ts b/tests/cases/fourslash/commentsOverloads.ts index 3976fdeae4a..1e85346a17a 100644 --- a/tests/cases/fourslash/commentsOverloads.ts +++ b/tests/cases/fourslash/commentsOverloads.ts @@ -594,11 +594,7 @@ goTo.marker('64q'); verify.quickInfoIs("(constructor) c5(b: string): c5 (+1 overload)", "c5 2"); goTo.marker('65'); -//verify.completionListContains("c", "class c", ""); -// the below check is wrong and it should show it as class but currently we have a bug for adding the parameters of ambient function in the symbol list -// eg declare function foo2(x: number); -// completion list here -verify.completionListContains("c", "(parameter) c: boolean", ""); +verify.completionListContains("c", "class c", ""); verify.completionListContains("c1", "class c1", ""); verify.completionListContains("c2", "class c2", ""); verify.completionListContains("c3", "class c3", ""); diff --git a/tests/cases/fourslash/completionListAfterFunction.ts b/tests/cases/fourslash/completionListAfterFunction.ts new file mode 100644 index 00000000000..140c5e2e363 --- /dev/null +++ b/tests/cases/fourslash/completionListAfterFunction.ts @@ -0,0 +1,25 @@ +/// + +////// Outside the function +////declare function f1(a: number);/*1*/ +//// +////// inside the function +////declare function f2(b: number, b2 = /*2*/ +//// +////// Outside the function +////function f3(c: number) { }/*3*/ +//// +////// inside the function +////function f4(d: number) { /*4*/} + +goTo.marker("1"); +verify.not.completionListContains("a"); + +goTo.marker("2"); +verify.completionListContains("b"); + +goTo.marker("3"); +verify.not.completionListContains("c"); + +goTo.marker("4"); +verify.completionListContains("d"); diff --git a/tests/cases/fourslash/completionListAfterFunction2.ts b/tests/cases/fourslash/completionListAfterFunction2.ts new file mode 100644 index 00000000000..c123e084972 --- /dev/null +++ b/tests/cases/fourslash/completionListAfterFunction2.ts @@ -0,0 +1,13 @@ +/// + +////// Outside the function expression +////declare var f1: (a: number) => void; /*1*/ +//// +////declare var f1: (b: number, b2: /*2*/) => void; + +goTo.marker("1"); +verify.not.completionListContains("a"); + +goTo.marker("2"); +verify.completionListContains("b"); + diff --git a/tests/cases/fourslash/completionListAfterFunction3.ts b/tests/cases/fourslash/completionListAfterFunction3.ts new file mode 100644 index 00000000000..575351ea648 --- /dev/null +++ b/tests/cases/fourslash/completionListAfterFunction3.ts @@ -0,0 +1,12 @@ +/// + +////// Outside the function expression +////var x1 = (a: number) => { }/*1*/; +//// +////var x2 = (b: number) => {/*2*/ }; + +goTo.marker("1"); +verify.not.completionListContains("a"); + +goTo.marker("2"); +verify.completionListContains("b"); diff --git a/tests/cases/fourslash/completionListAfterNumericLiteral.ts b/tests/cases/fourslash/completionListAfterNumericLiteral.ts index a8ea0335f70..549c048a635 100644 --- a/tests/cases/fourslash/completionListAfterNumericLiteral.ts +++ b/tests/cases/fourslash/completionListAfterNumericLiteral.ts @@ -25,10 +25,10 @@ goTo.marker("dotOnNumberExrpressions1"); verify.completionListIsEmpty(); goTo.marker("dotOnNumberExrpressions2"); -verify.completionListIsEmpty(); +verify.completionListContains("toExponential"); goTo.marker("dotOnNumberExrpressions3"); -verify.completionListIsEmpty(); +verify.completionListContains("toExponential"); goTo.marker("dotOnNumberExrpressions4"); verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/completionListAfterNumericLiteral1.ts b/tests/cases/fourslash/completionListAfterNumericLiteral1.ts index b2604531316..cf8b5d41e98 100644 --- a/tests/cases/fourslash/completionListAfterNumericLiteral1.ts +++ b/tests/cases/fourslash/completionListAfterNumericLiteral1.ts @@ -3,4 +3,4 @@ ////5../**/ goTo.marker(); -verify.completionListIsEmpty(); \ No newline at end of file +verify.completionListContains("toFixed"); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListAfterSlash.ts b/tests/cases/fourslash/completionListAfterSlash.ts new file mode 100644 index 00000000000..f9a0b69afdf --- /dev/null +++ b/tests/cases/fourslash/completionListAfterSlash.ts @@ -0,0 +1,8 @@ +/// + +////var a = 0; +////a/./**/ + +goTo.marker(); +// should not crash +verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_catch.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_catch.ts new file mode 100644 index 00000000000..bdf13c1d97f --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_catch.ts @@ -0,0 +1,13 @@ +/// + +////var aa = 1; + +//// try {} catch(/*catchVariable1*/ + +//// try {} catch(a/*catchVariable2*/ + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_classes.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_classes.ts new file mode 100644 index 00000000000..60a108cf1e6 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_classes.ts @@ -0,0 +1,12 @@ +/// + +////var aa = 1; + +////class /*className1*/ + +////class a/*className2*/ + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enumMembers.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enumMembers.ts new file mode 100644 index 00000000000..6c0472be546 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enumMembers.ts @@ -0,0 +1,11 @@ +/// + +////var aa = 1; + +////enum a { /*enumValueName1*/ + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enumMembers2.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enumMembers2.ts new file mode 100644 index 00000000000..ee2f3e71032 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enumMembers2.ts @@ -0,0 +1,9 @@ +/// + +////var aa = 1; +////enum a { foo, /*enumValueName3*/ + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enums.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enums.ts new file mode 100644 index 00000000000..183f8a22c63 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_enums.ts @@ -0,0 +1,14 @@ +/// + +////var aa = 1; + +////enum /*enumName1*/ + +////enum a/*enumName2*/ + +////var x = 0; enum /*enumName4*/ + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_functions.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_functions.ts new file mode 100644 index 00000000000..24231174727 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_functions.ts @@ -0,0 +1,13 @@ +/// + +////var aa = 1; + +////function /*functionName1*/ + +////function a/*functionName2*/ + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers.ts new file mode 100644 index 00000000000..266b0b78c9c --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers.ts @@ -0,0 +1,10 @@ +/// + +////var aa = 1; + +////interface a { /*interfaceValue1*/ + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers2.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers2.ts new file mode 100644 index 00000000000..82a30325948 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers2.ts @@ -0,0 +1,10 @@ +/// + +////var aa = 1; + +////interface a { f/*interfaceValue2*/ + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers3.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers3.ts new file mode 100644 index 00000000000..ed640dd3f1a --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaceMembers3.ts @@ -0,0 +1,10 @@ +/// + +////var aa = 1; + +////interface a { f; /*interfaceValue3*/ + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaces.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaces.ts new file mode 100644 index 00000000000..ec2732fe2fe --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_interfaces.ts @@ -0,0 +1,13 @@ +/// + +////var aa = 1; + +////interface /*interfaceName1*/ + +////interface a/*interfaceName2*/ + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_modules.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_modules.ts new file mode 100644 index 00000000000..4ebdf029a89 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_modules.ts @@ -0,0 +1,13 @@ +/// + +////var aa = 1; + +////module /*moduleName1*/ + +////module a/*moduleName2*/ + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_parameters.ts similarity index 54% rename from tests/cases/fourslash/completionListAtIdentifierDefinitionLocations.ts rename to tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_parameters.ts index e36d69c82f2..0fd66e9d091 100644 --- a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations.ts +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_parameters.ts @@ -1,40 +1,33 @@ /// ////var aa = 1; -////class /*className1*/ -////class a/*className2*/ -////interface /*interfaceName1*/ -////interface a/*interfaceName2*/ -////module /*moduleName1*/ -////module a/*moduleName2*/ -////enum /*enumName1*/ -////enum a/*enumName2*/ -////// fourslash is saying completion list is not empty on this line but editor disagrees -//////enum a { /*enumValueName1*/ -////enum a { f/*enumValueName2*/ -////enum a { foo, /*enumValueName3*/ -////var x = 0; enum /*enumName4*/ -////function /*functionName1*/ -////function a/*functionName2*/ -////var /*varName1*/ -////var a/*varName2*/ -////var a2,/*varName3*/ -////var a2, a/*varName4*/ + ////function testFunction(/*parameterName1*/ + ////function testFunction(a/*parameterName2*/ + ////function testFunction(a, /*parameterName3*/ + ////function testFunction(a, b/*parameterName4*/ + ////class bar1{ constructor(/*constructorParamter1*/ + ////class bar2{ constructor(a/*constructorParamter2*/ + ////class bar3{ constructor(a, /*constructorParamter3*/ + ////class bar4{ constructor(a, b/*constructorParamter4*/ + ////class bar5{ constructor(public /*constructorParamter5*/ + ////class bar6{ constructor(public a/*constructorParamter6*/ + ////class bar7{ constructor(private a/*constructorParamter7*/ + ////class bar8{ constructor(.../*constructorParamter8*/ + ////class bar9{ constructor(...a/*constructorParamter9*/ -//// try {} catch(/*catchVariable1*/ -//// try {} catch(a/*catchVariable2*/ + test.markers().forEach((m) => { goTo.position(m.position, m.fileName); diff --git a/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_varDeclarations.ts b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_varDeclarations.ts new file mode 100644 index 00000000000..50a112f4625 --- /dev/null +++ b/tests/cases/fourslash/completionListAtIdentifierDefinitionLocations_varDeclarations.ts @@ -0,0 +1,18 @@ +/// + +////var aa = 1; + + +////var /*varName1*/ + +////var a/*varName2*/ + +////var a2,/*varName3*/ + +////var a2, a/*varName4*/ + + +test.markers().forEach((m) => { + goTo.position(m.position, m.fileName); + verify.completionListIsEmpty(); +}); diff --git a/tests/cases/fourslash/completionListInComments2.ts b/tests/cases/fourslash/completionListInComments2.ts new file mode 100644 index 00000000000..d9855306fcf --- /dev/null +++ b/tests/cases/fourslash/completionListInComments2.ts @@ -0,0 +1,7 @@ +/// + +//// // */{| "name" : "1" |} + +goTo.marker("1"); +// Completion list should not be available within comments +verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/completionListInComments3.ts b/tests/cases/fourslash/completionListInComments3.ts new file mode 100644 index 00000000000..2f20a969ba1 --- /dev/null +++ b/tests/cases/fourslash/completionListInComments3.ts @@ -0,0 +1,31 @@ +/// + +//// /*{| "name": "1" |} + +//// /* {| "name": "2" |} + +//// /* *{| "name": "3" |} + +//// /* */{| "name": "4" |} + +//// {| "name": "5" |}/* */ + +/////* {| "name": "6" |} + +goTo.marker("1"); +verify.completionListIsEmpty(); + +goTo.marker("2"); +verify.completionListIsEmpty(); + +goTo.marker("3"); +verify.completionListIsEmpty(); + +goTo.marker("4"); +verify.not.completionListIsEmpty(); + +goTo.marker("5"); +verify.not.completionListIsEmpty(); + +goTo.marker("6"); +verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/completionListPrivateMembers3.ts b/tests/cases/fourslash/completionListPrivateMembers3.ts new file mode 100644 index 00000000000..69f7456d7b5 --- /dev/null +++ b/tests/cases/fourslash/completionListPrivateMembers3.ts @@ -0,0 +1,31 @@ +/// + +////class Other { +//// public p; +//// protected p2 +//// private p3; +////} +//// +////class Self { +//// private other: Other; +//// +//// method() { +//// this.other./*1*/; +//// +//// this.other.p/*2*/; +//// +//// this.other.p/*3*/.toString(); +//// } +////} + +goTo.marker("1"); +verify.memberListContains("p"); +verify.memberListCount(1); + +goTo.marker("2"); +verify.memberListContains("p"); +verify.memberListCount(1); + +goTo.marker("2"); +verify.memberListContains("p"); +verify.memberListCount(1); diff --git a/tests/cases/fourslash/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts b/tests/cases/fourslash/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts index e49dd0dea5d..d01856a54fb 100644 --- a/tests/cases/fourslash/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts +++ b/tests/cases/fourslash/completion_enum-members-with-invalid-identifiers-should-not-show-in-completion.ts @@ -10,6 +10,7 @@ //// //// e./**/ +goTo.marker(); verify.not.completionListContains('1'); verify.not.completionListContains('"1"'); verify.not.completionListContains('2'); diff --git a/tests/cases/fourslash/externalModuleWithExportAssignment.ts b/tests/cases/fourslash/externalModuleWithExportAssignment.ts index 1de1e55579b..4d5f600317d 100644 --- a/tests/cases/fourslash/externalModuleWithExportAssignment.ts +++ b/tests/cases/fourslash/externalModuleWithExportAssignment.ts @@ -37,7 +37,7 @@ verify.quickInfoIs("(var) a: {\n (): a1.connectExport;\n test1: a1.connect goTo.marker('3'); verify.quickInfoIs("(property) test1: a1.connectModule(res: any, req: any, next: any) => void", undefined); -verify.completionListContains("test1", "(property) test1: a1.connectModule", undefined); +verify.completionListContains("test1", "(property) test1: a1.connectModule(res: any, req: any, next: any) => void", undefined); verify.completionListContains("test2", "(method) test2(): a1.connectModule", undefined); verify.not.completionListContains("connectModule"); verify.not.completionListContains("connectExport"); @@ -59,7 +59,7 @@ verify.quickInfoIs("(var) r2: a1.connectExport", undefined); goTo.marker('9'); verify.quickInfoIs("(property) test1: a1.connectModule(res: any, req: any, next: any) => void", undefined); -verify.completionListContains("test1", "(property) test1: a1.connectModule", undefined); +verify.completionListContains("test1", "(property) test1: a1.connectModule(res: any, req: any, next: any) => void", undefined); verify.completionListContains("test2", "(method) test2(): a1.connectModule", undefined); verify.completionListContains("connectModule"); verify.completionListContains("connectExport");