diff --git a/src/services/services.ts b/src/services/services.ts index b0c69aa99fe..66d59a5f0a3 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) { @@ -1921,15 +1926,15 @@ module ts { } function isCallExpressionTarget(node: Node): boolean { - if (node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).right === node) + if (node && node.parent && node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).right === 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 (node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).right === node) + if (node && node.parent && node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).right === 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 isNameOfFunctionDeclaration(node: Node): boolean { @@ -1968,6 +1973,39 @@ module ts { (node.parent.kind === SyntaxKind.ImportDeclaration && (node.parent).externalModuleName === node)); } + /** Returns true if the position is within a comment */ + function isInsideComment(sourceFile: SourceFile, position: number): boolean { + var token = getTokenAtPosition(sourceFile, position); + + // The position has to be: 1. in the leading trivia (before tokek.getStart()), and 2. within a comment + return position <= token.getStart() && + (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 + 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 multiline comment + return text.charCodeAt(comment.end - 1) !== CharacterCodes.slash && + text.charCodeAt(comment.end - 2) !== CharacterCodes.asterisk; + } + } + return false; + }); + } + } + enum SemanticMeaning { None = 0x0, Value = 0x1, @@ -2281,40 +2319,58 @@ module ts { }); } - function isCompletionListBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { + function isCompletionListBlocker(sourceFile: SourceFile, 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)) { + if (position < 0 || position > sourceFile.end) { 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); + return isInsideComment(sourceFile, position) || + isEntirelyInStringOrRegularExpressionLiteral(sourceFile, position) || + isIdentifierDefinitionLocation(sourceFile, position) || + isRightOfIllegalDot(sourceFile, position); } - function getContainingObjectLiteralApplicableForCompletion(sourceUnit: TypeScript.SourceUnitSyntax, position: number): TypeScript.ISyntaxElement { + function isEntirelyInStringOrRegularExpressionLiteral(sourceFile: SourceFile, position: number): boolean { + var token = getTouchingPropertyName(sourceFile, position); + + // || token.kind === SyntaxKind.RegularExpressionLiteral + if (token.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 = token.getStart(); + var end = token.getEnd(); + if (start < position && position < end) { + return true; + } + else if (position === end) { + var width = end - start; + return width <= 1 || sourceFile.text.charCodeAt(start) !== sourceFile.text.charCodeAt(end - 1); + } + } + else if (token.kind === SyntaxKind.RegularExpressionLiteral) { + return token.getStart() < position && position < token.getEnd(); + } + return false; + } + + function getContainingObjectLiteralApplicableForCompletion(sourceFile: SourceFile, position: number): ObjectLiteral { // The locations in an object literal expression that are applicable for completion are property name definition locations. - var previousToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); + var previousToken = getNonIdentifierCompleteTokenOnLeft(sourceFile, 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; } } @@ -2322,47 +2378,67 @@ 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(sourceFile: SourceFile, position: number): boolean { + var previousToken = getNonIdentifierCompleteTokenOnLeft(sourceFile, position); + if (previousToken) { + var containingNodeKind = previousToken.parent.kind; + switch (previousToken.kind) { + case SyntaxKind.CommaToken: + return containingNodeKind === SyntaxKind.VariableDeclaration || + containingNodeKind === SyntaxKind.VariableStatement || + containingNodeKind === SyntaxKind.EnumDeclaration || // enum { 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 { | + // containingNodeKind === SyntaxKind.InterfaceDeclaration; - case TypeScript.SyntaxKind.PublicKeyword: - case TypeScript.SyntaxKind.PrivateKeyword: - case TypeScript.SyntaxKind.StaticKeyword: - case TypeScript.SyntaxKind.DotDotDotToken: - return containingNodeKind === TypeScript.SyntaxKind.Parameter; + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.DotDotDotToken: + return containingNodeKind === SyntaxKind.Parameter; - case TypeScript.SyntaxKind.ClassKeyword: - case TypeScript.SyntaxKind.ModuleKeyword: - case TypeScript.SyntaxKind.EnumKeyword: - case TypeScript.SyntaxKind.InterfaceKeyword: - case TypeScript.SyntaxKind.FunctionKeyword: - case TypeScript.SyntaxKind.VarKeyword: - case TypeScript.SyntaxKind.GetKeyword: - case TypeScript.SyntaxKind.SetKeyword: + 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": return true; } } @@ -2370,44 +2446,28 @@ module ts { return false; } - function getNonIdentifierCompleteTokenOnLeft(sourceUnit: TypeScript.SourceUnitSyntax, position: number): TypeScript.ISyntaxToken { - var positionedToken = TypeScript.Syntax.findCompleteTokenOnLeft(sourceUnit, position, /*includeSkippedTokens*/true); + function getNonIdentifierCompleteTokenOnLeft(sourceFile: SourceFile, position: number): Node { + var previousToken = findTokenOnLeftOfPosition(sourceFile, position); - 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) { + if (previousToken && position <= previousToken.end && previousToken.kind === SyntaxKind.Identifier) { // 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); + previousToken = findPrecedingToken(previousToken.pos, sourceFile); } - return positionedToken; + return previousToken; } - function isRightOfIllegalDot(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { - var positionedToken = getNonIdentifierCompleteTokenOnLeft(sourceUnit, position); + function isRightOfIllegalDot(sourceFile: SourceFile, position: number): boolean { + var previousToken = getNonIdentifierCompleteTokenOnLeft(sourceFile, 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) === "."; - } + 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; @@ -2445,65 +2505,28 @@ module ts { var sourceFile = getSourceFile(filename); var sourceUnit = sourceFile.getSourceUnit(); - if (isCompletionListBlocker(sourceFile.getSyntaxTree().sourceUnit(), position)) { + if (isCompletionListBlocker(sourceFile, 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) { - + var node: Node; + var isRightOfDot: boolean; + var token = getNonIdentifierCompleteTokenOnLeft(sourceFile, position); + if (token && token.kind === SyntaxKind.DotToken && + (token.parent.kind === SyntaxKind.PropertyAccess || token.parent.kind === SyntaxKind.QualifiedName)) { + node = (token.parent).left; 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; - } + node = !token ? sourceFile : token.parent; + isRightOfDot = false; - Debug.assert(mappedNode, "Could not map a Fidelity node to an AST node"); + // we are at the end of a container node, we do not want to be inside it, as that would affect our completion results + // e.g. function f(a) {}| <- 'a' should not be visible here + if (token && token.kind === SyntaxKind.CloseBraceToken && position === token.end) { + } + } // Get the completions activeCompletionSession = { @@ -2511,7 +2534,7 @@ module ts { position: position, entries: [], symbols: {}, - location: mappedNode, + location: node, typeChecker: typeInfoResolver }; @@ -2520,8 +2543,8 @@ module ts { var symbols: Symbol[] = []; isMemberCompletion = true; - if (mappedNode.kind === SyntaxKind.Identifier || mappedNode.kind === SyntaxKind.QualifiedName || mappedNode.kind === SyntaxKind.PropertyAccess) { - var symbol = typeInfoResolver.getSymbolInfo(mappedNode); + 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) { @@ -2531,19 +2554,19 @@ module ts { if (symbol && symbol.flags & SymbolFlags.HasExports) { // Extract module or enum members forEachValue(symbol.exports, symbol => { - if (typeInfoResolver.isValidPropertyAccess((mappedNode.parent), symbol.name)) { + if (typeInfoResolver.isValidPropertyAccess((node.parent), symbol.name)) { symbols.push(symbol); } }); } } - var type = typeInfoResolver.getTypeOfNode(mappedNode); + var type = typeInfoResolver.getTypeOfNode(node); var apparentType = type && typeInfoResolver.getApparentType(type); if (apparentType) { // Filter private properties forEach(apparentType.getApparentProperties(), symbol => { - if (typeInfoResolver.isValidPropertyAccess((mappedNode.parent), symbol.name)) { + if (typeInfoResolver.isValidPropertyAccess((node.parent), symbol.name)) { symbols.push(symbol); } }); @@ -2552,17 +2575,13 @@ module ts { getCompletionEntriesFromSymbols(symbols, activeCompletionSession); } else { - var containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(sourceFile.getSyntaxTree().sourceUnit(), position); + var containingObjectLiteral = getContainingObjectLiteralApplicableForCompletion(sourceFile, 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); + var contextualType = typeInfoResolver.getContextualType(containingObjectLiteral); if (!contextualType) { return undefined; } @@ -2570,7 +2589,7 @@ module ts { var contextualTypeMembers = typeInfoResolver.getPropertiesOfType(contextualType); if (contextualTypeMembers && contextualTypeMembers.length > 0) { // Add filtered items to the completion list - var filteredMembers = filterContextualMembersList(contextualTypeMembers, objectLiteral.properties); + var filteredMembers = filterContextualMembersList(contextualTypeMembers, containingObjectLiteral.properties); getCompletionEntriesFromSymbols(filteredMembers, activeCompletionSession); } } @@ -2579,7 +2598,7 @@ module ts { 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); + var symbols = typeInfoResolver.getSymbolsInScope(node, symbolMeanings); getCompletionEntriesFromSymbols(symbols, activeCompletionSession); } @@ -2767,14 +2786,19 @@ 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; + // 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)) { if (location.parent.kind === SyntaxKind.PropertyAccess && (location.parent).right === location) { location = location.parent; } callExpression = location.parent; + } + if (callExpression) { var candidateSignatures: Signature[] = []; signature = typeResolver.getResolvedSignature(callExpression, candidateSignatures); if (!signature && candidateSignatures.length) { @@ -5016,17 +5040,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, matchPosition)) { continue; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4ceb20fdcee..825c194bc73 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/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/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..3f57929c1f4 --- /dev/null +++ b/tests/cases/fourslash/completionListInComments3.ts @@ -0,0 +1,26 @@ +/// + +//// /*{| "name": "1" |} + +//// /* {| "name": "2" |} + +//// /* *{| "name": "3" |} + +//// /* */{| "name": "4" |} + +//// {| "name": "5" |}/* */ + +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(); 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');