diff --git a/Jakefile b/Jakefile index 0bdb573914f..04049837f6e 100644 --- a/Jakefile +++ b/Jakefile @@ -54,6 +54,8 @@ var servicesSources = [ }).concat([ "services.ts", "shims.ts", + "signatureHelp.ts", + "utilities.ts" ].map(function (f) { return path.join(servicesDirectory, f); })); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 227e99b712d..fdf25dadbeb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -91,6 +91,7 @@ module ts { getRootSymbol: getRootSymbol, getContextualType: getContextualType, getFullyQualifiedName: getFullyQualifiedName, + getResolvedSignature: getResolvedSignature, getEnumMemberValue: getEnumMemberValue }; @@ -4330,55 +4331,26 @@ module ts { return unknownSignature; } - function isCandidateSignature(node: CallExpression, signature: Signature) { + function signatureHasCorrectArity(node: CallExpression, signature: Signature): boolean { var args = node.arguments || emptyArray; - return args.length >= signature.minArgumentCount && + var isCorrect = args.length >= signature.minArgumentCount && (signature.hasRestParameter || args.length <= signature.parameters.length) && (!node.typeArguments || signature.typeParameters && node.typeArguments.length === signature.typeParameters.length); - } - // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order - // A nit here is that we reorder only signatures that belong to the same symbol, - // so order how inherited signatures are processed is still preserved. - // interface A { (x: string): void } - // interface B extends A { (x: 'foo'): string } - // var b: B; - // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] - function collectCandidates(node: CallExpression, signatures: Signature[]): Signature[]{ - var result: Signature[] = []; - var lastParent: Node; - var lastSymbol: Symbol; - var cutoffPos: number = 0; - var pos: number; - for (var i = 0; i < signatures.length; i++) { - var signature = signatures[i]; - if (isCandidateSignature(node, signature)) { - var symbol = signature.declaration && getSymbolOfNode(signature.declaration); - var parent = signature.declaration && signature.declaration.parent; - if (!lastSymbol || symbol === lastSymbol) { - if (lastParent && parent === lastParent) { - pos++; - } - else { - lastParent = parent; - pos = cutoffPos; - } - } - else { - // current declaration belongs to a different symbol - // set cutoffPos so re-orderings in the future won't change result set from 0 to cutoffPos - pos = cutoffPos = result.length; - lastParent = parent; - } - lastSymbol = symbol; - - for (var j = result.length; j > pos; j--) { - result[j] = result[j - 1]; - } - result[pos] = signature; + // For error recovery, since we have parsed OmittedExpressions for any extra commas + // in the argument list, if we see any OmittedExpressions, just return true. + // The reason this is ok is because omitted expressions here are syntactically + // illegal, and will cause a parse error. + // Note: It may be worth keeping the upper bound check on arity, but removing + // the lower bound check if there are omitted expressions. + if (!isCorrect) { + // Technically this type assertion is not safe because args could be initialized to emptyArray + // above. + if ((>args).hasTrailingComma || forEach(args, arg => arg.kind === SyntaxKind.OmittedExpression)) { + return true; } } - return result; + return isCorrect; } // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. @@ -4409,6 +4381,9 @@ module ts { var mapper = createInferenceMapper(context); // First infer from arguments that are not context sensitive for (var i = 0; i < args.length; i++) { + if (args[i].kind === SyntaxKind.OmittedExpression) { + continue; + } if (!excludeArgument || excludeArgument[i] === undefined) { var parameterType = getTypeAtPosition(signature, i); inferTypes(context, checkExpressionWithContextualType(args[i], parameterType, mapper), parameterType); @@ -4417,6 +4392,9 @@ module ts { // Next, infer from those context sensitive arguments that are no longer excluded if (excludeArgument) { for (var i = 0; i < args.length; i++) { + if (args[i].kind === SyntaxKind.OmittedExpression) { + continue; + } if (excludeArgument[i] === false) { var parameterType = getTypeAtPosition(signature, i); inferTypes(context, checkExpressionWithContextualType(args[i], parameterType, mapper), parameterType); @@ -4445,6 +4423,10 @@ module ts { if (node.arguments) { for (var i = 0; i < node.arguments.length; i++) { var arg = node.arguments[i]; + if (arg.kind === SyntaxKind.OmittedExpression) { + continue; + } + var paramType = getTypeAtPosition(signature, i); // String literals get string literal types unless we're reporting errors var argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors ? @@ -4462,9 +4444,11 @@ module ts { return true; } - function resolveCall(node: CallExpression, signatures: Signature[]): Signature { + function resolveCall(node: CallExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature { forEach(node.typeArguments, checkSourceElement); - var candidates = collectCandidates(node, signatures); + var candidates = candidatesOutArray || []; + // collectCandidates fills up the candidates array directly + collectCandidates(); if (!candidates.length) { error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target); return resolveErrorCall(node); @@ -4480,20 +4464,24 @@ module ts { var relation = candidates.length === 1 ? assignableRelation : subtypeRelation; while (true) { for (var i = 0; i < candidates.length; i++) { + if (!signatureHasCorrectArity(node, candidates[i])) { + continue; + } + while (true) { - var candidate = candidates[i]; - if (candidate.typeParameters) { + var candidateWithCorrectArity = candidates[i]; + if (candidateWithCorrectArity.typeParameters) { var typeArguments = node.typeArguments ? - checkTypeArguments(candidate, node.typeArguments) : - inferTypeArguments(candidate, args, excludeArgument); - candidate = getSignatureInstantiation(candidate, typeArguments); + checkTypeArguments(candidateWithCorrectArity, node.typeArguments) : + inferTypeArguments(candidateWithCorrectArity, args, excludeArgument); + candidateWithCorrectArity = getSignatureInstantiation(candidateWithCorrectArity, typeArguments); } - if (!checkApplicableSignature(node, candidate, relation, excludeArgument, /*reportErrors*/ false)) { + if (!checkApplicableSignature(node, candidateWithCorrectArity, relation, excludeArgument, /*reportErrors*/ false)) { break; } var index = excludeArgument ? indexOf(excludeArgument, true) : -1; if (index < 0) { - return candidate; + return candidateWithCorrectArity; } excludeArgument[index] = false; } @@ -4503,17 +4491,70 @@ module ts { } relation = assignableRelation; } + // No signatures were applicable. Now report errors based on the last applicable signature with // no arguments excluded from assignability checks. - checkApplicableSignature(node, candidate, relation, undefined, /*reportErrors*/ true); + // If candidate is undefined, it means that no candidates had a suitable arity. In that case, + // skip the checkApplicableSignature check. + if (candidateWithCorrectArity) { + checkApplicableSignature(node, candidateWithCorrectArity, relation, /*excludeArgument*/ undefined, /*reportErrors*/ true); + } + else { + error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target); + return resolveErrorCall(node); + } return resolveErrorCall(node); + + // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order + // A nit here is that we reorder only signatures that belong to the same symbol, + // so order how inherited signatures are processed is still preserved. + // interface A { (x: string): void } + // interface B extends A { (x: 'foo'): string } + // var b: B; + // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] + function collectCandidates(): void { + var result = candidates; + var lastParent: Node; + var lastSymbol: Symbol; + var cutoffPos: number = 0; + var pos: number; + Debug.assert(!result.length); + for (var i = 0; i < signatures.length; i++) { + var signature = signatures[i]; + if (true) { + var symbol = signature.declaration && getSymbolOfNode(signature.declaration); + var parent = signature.declaration && signature.declaration.parent; + if (!lastSymbol || symbol === lastSymbol) { + if (lastParent && parent === lastParent) { + pos++; + } + else { + lastParent = parent; + pos = cutoffPos; + } + } + else { + // current declaration belongs to a different symbol + // set cutoffPos so re-orderings in the future won't change result set from 0 to cutoffPos + pos = cutoffPos = result.length; + lastParent = parent; + } + lastSymbol = symbol; + + for (var j = result.length; j > pos; j--) { + result[j] = result[j - 1]; + } + result[pos] = signature; + } + } + } } - function resolveCallExpression(node: CallExpression): Signature { + function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature { if (node.func.kind === SyntaxKind.SuperKeyword) { var superType = checkSuperExpression(node.func); if (superType !== unknownType) { - return resolveCall(node, getSignaturesOfType(superType, SignatureKind.Construct)); + return resolveCall(node, getSignaturesOfType(superType, SignatureKind.Construct), candidatesOutArray); } return resolveUntypedCall(node); } @@ -4561,10 +4602,10 @@ module ts { } return resolveErrorCall(node); } - return resolveCall(node, callSignatures); + return resolveCall(node, callSignatures, candidatesOutArray); } - function resolveNewExpression(node: NewExpression): Signature { + function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature { var expressionType = checkExpression(node.func); if (expressionType === unknownType) { // Another error has already been reported @@ -4599,7 +4640,7 @@ module ts { // that the user will not add any. var constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct); if (constructSignatures.length) { - return resolveCall(node, constructSignatures); + return resolveCall(node, constructSignatures, candidatesOutArray); } // If ConstructExpr's apparent type is an object type with no construct signatures but @@ -4608,7 +4649,7 @@ module ts { // operation is Any. var callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); if (callSignatures.length) { - var signature = resolveCall(node, callSignatures); + var signature = resolveCall(node, callSignatures, candidatesOutArray); if (getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); } @@ -4619,11 +4660,19 @@ module ts { return resolveErrorCall(node); } - function getResolvedSignature(node: CallExpression): Signature { + // candidatesOutArray is passed by signature help in the language service, and collectCandidates + // must fill it up with the appropriate candidate signatures + function getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature { var links = getNodeLinks(node); - if (!links.resolvedSignature) { + // If getResolvedSignature has already been called, we will have cached the resolvedSignature. + // However, it is possible that either candidatesOutArray was not passed in the first time, + // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work + // to correctly fill the candidatesOutArray. + if (!links.resolvedSignature || candidatesOutArray) { links.resolvedSignature = anySignature; - links.resolvedSignature = node.kind === SyntaxKind.CallExpression ? resolveCallExpression(node) : resolveNewExpression(node); + links.resolvedSignature = node.kind === SyntaxKind.CallExpression + ? resolveCallExpression(node, candidatesOutArray) + : resolveNewExpression(node, candidatesOutArray); } return links.resolvedSignature; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6bfa537c9f2..fb2bde52453 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -11,7 +11,9 @@ module ts { var result: U; if (array) { for (var i = 0, len = array.length; i < len; i++) { - if (result = callback(array[i])) break; + if (result = callback(array[i])) { + break; + } } } return result; @@ -39,6 +41,18 @@ module ts { return -1; } + export function countWhere(array: T[], predicate: (x: T) => boolean): number { + var count = 0; + if (array) { + for (var i = 0, len = array.length; i < len; i++) { + if (predicate(array[i])) { + count++; + } + } + } + return count; + } + export function filter(array: T[], f: (x: T) => boolean): T[] { if (array) { var result: T[] = []; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 74b0533e5b4..4052350cf8c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -748,23 +748,46 @@ module ts { } } - function emitCommaList(nodes: Node[], count?: number) { - if (!(count >= 0)) count = nodes.length; - if (nodes) { - for (var i = 0; i < count; i++) { - if (i) write(", "); - emit(nodes[i]); + function emitTrailingCommaIfPresent(nodeList: NodeArray, isMultiline: boolean): void { + if (nodeList.hasTrailingComma) { + write(","); + if (isMultiline) { + writeLine(); } } } - function emitMultiLineList(nodes: Node[]) { + function emitCommaList(nodes: NodeArray, includeTrailingComma: boolean, count?: number) { + if (!(count >= 0)) { + count = nodes.length; + } + if (nodes) { + for (var i = 0; i < count; i++) { + if (i) { + write(", "); + } + emit(nodes[i]); + } + + if (includeTrailingComma) { + emitTrailingCommaIfPresent(nodes, /*isMultiline*/ false); + } + } + } + + function emitMultiLineList(nodes: NodeArray, includeTrailingComma: boolean) { if (nodes) { for (var i = 0; i < nodes.length; i++) { - if (i) write(","); + if (i) { + write(","); + } writeLine(); emit(nodes[i]); } + + if (includeTrailingComma) { + emitTrailingCommaIfPresent(nodes, /*isMultiline*/ true); + } } } @@ -876,14 +899,14 @@ module ts { if (node.flags & NodeFlags.MultiLine) { write("["); increaseIndent(); - emitMultiLineList(node.elements); + emitMultiLineList(node.elements, /*includeTrailingComma*/ true); decreaseIndent(); writeLine(); write("]"); } else { write("["); - emitCommaList(node.elements); + emitCommaList(node.elements, /*includeTrailingComma*/ true); write("]"); } } @@ -895,14 +918,14 @@ module ts { else if (node.flags & NodeFlags.MultiLine) { write("{"); increaseIndent(); - emitMultiLineList(node.properties); + emitMultiLineList(node.properties, /*includeTrailingComma*/ compilerOptions.target >= ScriptTarget.ES5); decreaseIndent(); writeLine(); write("}"); } else { write("{ "); - emitCommaList(node.properties); + emitCommaList(node.properties, /*includeTrailingComma*/ compilerOptions.target >= ScriptTarget.ES5); write(" }"); } } @@ -949,13 +972,13 @@ module ts { emitThis(node.func); if (node.arguments.length) { write(", "); - emitCommaList(node.arguments); + emitCommaList(node.arguments, /*includeTrailingComma*/ false); } write(")"); } else { write("("); - emitCommaList(node.arguments); + emitCommaList(node.arguments, /*includeTrailingComma*/ false); write(")"); } } @@ -965,7 +988,7 @@ module ts { emit(node.func); if (node.arguments) { write("("); - emitCommaList(node.arguments); + emitCommaList(node.arguments, /*includeTrailingComma*/ false); write(")"); } } @@ -1138,7 +1161,7 @@ module ts { if (node.declarations) { emitToken(SyntaxKind.VarKeyword, endPos); write(" "); - emitCommaList(node.declarations); + emitCommaList(node.declarations, /*includeTrailingComma*/ false); } if (node.initializer) { emit(node.initializer); @@ -1286,7 +1309,7 @@ module ts { function emitVariableStatement(node: VariableStatement) { emitLeadingComments(node); if (!(node.flags & NodeFlags.Export)) write("var "); - emitCommaList(node.declarations); + emitCommaList(node.declarations, /*includeTrailingComma*/ false); write(";"); emitTrailingComments(node); } @@ -1395,7 +1418,7 @@ module ts { increaseIndent(); write("("); if (node) { - emitCommaList(node.parameters, node.parameters.length - (hasRestParameters(node) ? 1 : 0)); + emitCommaList(node.parameters, /*includeTrailingComma*/ false, node.parameters.length - (hasRestParameters(node) ? 1 : 0)); } write(")"); decreaseIndent(); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7ea82597af0..af1851d6a75 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -628,12 +628,6 @@ module ts { Parameters, // Parameters in parameter list } - enum TrailingCommaBehavior { - Disallow, - Allow, - Preserve - } - // Tracks whether we nested (directly or indirectly) in a certain control block. // Used for validating break and continue statements. enum ControlBlockContext { @@ -1205,7 +1199,7 @@ module ts { } // Parses a comma-delimited list of elements - function parseDelimitedList(kind: ParsingContext, parseElement: () => T, trailingCommaBehavior: TrailingCommaBehavior): NodeArray { + function parseDelimitedList(kind: ParsingContext, parseElement: () => T, allowTrailingComma: boolean): NodeArray { var saveParsingContext = parsingContext; parsingContext |= 1 << kind; var result = >[]; @@ -1230,15 +1224,14 @@ module ts { else if (isListTerminator(kind)) { // Check if the last token was a comma. if (commaStart >= 0) { - if (trailingCommaBehavior === TrailingCommaBehavior.Disallow) { + if (!allowTrailingComma) { if (file.syntacticErrors.length === errorCountBeforeParsingList) { // Report a grammar error so we don't affect lookahead grammarErrorAtPos(commaStart, scanner.getStartPos() - commaStart, Diagnostics.Trailing_comma_not_allowed); } } - else if (trailingCommaBehavior === TrailingCommaBehavior.Preserve) { - result.push(createNode(SyntaxKind.OmittedExpression)); - } + // Always preserve a trailing comma by marking it on the NodeArray + result.hasTrailingComma = true; } break; @@ -1273,7 +1266,7 @@ module ts { function parseBracketedList(kind: ParsingContext, parseElement: () => T, startToken: SyntaxKind, endToken: SyntaxKind): NodeArray { if (parseExpected(startToken)) { - var result = parseDelimitedList(kind, parseElement, TrailingCommaBehavior.Disallow); + var result = parseDelimitedList(kind, parseElement, /*allowTrailingComma*/ false); parseExpected(endToken); return result; } @@ -2309,7 +2302,11 @@ module ts { else { parseExpected(SyntaxKind.OpenParenToken); } - callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseAssignmentExpression, TrailingCommaBehavior.Disallow); + // It is an error to have a trailing comma in an argument list. However, the checker + // needs evidence of a trailing comma in order to give good results for signature help. + // That is why we do not allow a trailing comma, but we "preserve" a trailing comma. + callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, + parseArgumentExpression, /*allowTrailingComma*/ false); parseExpected(SyntaxKind.CloseParenToken); expr = finishNode(callExpr); continue; @@ -2378,15 +2375,33 @@ module ts { return finishNode(node); } + function parseAssignmentExpressionOrOmittedExpression(omittedExpressionDiagnostic: DiagnosticMessage): Expression { + if (token === SyntaxKind.CommaToken) { + if (omittedExpressionDiagnostic) { + var errorStart = scanner.getTokenPos(); + var errorLength = scanner.getTextPos() - errorStart; + grammarErrorAtPos(errorStart, errorLength, omittedExpressionDiagnostic); + } + return createNode(SyntaxKind.OmittedExpression); + } + + return parseAssignmentExpression(); + } + function parseArrayLiteralElement(): Expression { - return token === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : parseAssignmentExpression(); + return parseAssignmentExpressionOrOmittedExpression(/*omittedExpressionDiagnostic*/ undefined); + } + + function parseArgumentExpression(): Expression { + return parseAssignmentExpressionOrOmittedExpression(Diagnostics.Argument_expression_expected); } function parseArrayLiteral(): ArrayLiteral { var node = createNode(SyntaxKind.ArrayLiteral); parseExpected(SyntaxKind.OpenBracketToken); if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine; - node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement, TrailingCommaBehavior.Preserve); + node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, + parseArrayLiteralElement, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } @@ -2428,10 +2443,7 @@ module ts { node.flags |= NodeFlags.MultiLine; } - // ES3 itself does not accept a trailing comma in an object literal, however, we'd like to preserve it in ES5. - var trailingCommaBehavior = languageVersion === ScriptTarget.ES3 ? TrailingCommaBehavior.Allow : TrailingCommaBehavior.Preserve; - - node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, trailingCommaBehavior); + node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBraceToken); var seen: Map = {}; @@ -2520,7 +2532,11 @@ module ts { parseExpected(SyntaxKind.NewKeyword); node.func = parseCallAndAccess(parsePrimaryExpression(), /* inNewExpression */ true); if (parseOptional(SyntaxKind.OpenParenToken) || token === SyntaxKind.LessThanToken && (node.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) { - node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseAssignmentExpression, TrailingCommaBehavior.Disallow); + // It is an error to have a trailing comma in an argument list. However, the checker + // needs evidence of a trailing comma in order to give good results for signature help. + // That is why we do not allow a trailing comma, but we "preserve" a trailing comma. + node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, + parseArgumentExpression, /*allowTrailingComma*/ false); parseExpected(SyntaxKind.CloseParenToken); } return finishNode(node); @@ -3089,7 +3105,8 @@ module ts { } function parseVariableDeclarationList(flags: NodeFlags, noIn?: boolean): NodeArray { - return parseDelimitedList(ParsingContext.VariableDeclarations, () => parseVariableDeclaration(flags, noIn), TrailingCommaBehavior.Disallow); + return parseDelimitedList(ParsingContext.VariableDeclarations, + () => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false); } function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement { @@ -3488,7 +3505,8 @@ module ts { var implementsKeywordLength: number; if (parseOptional(SyntaxKind.ImplementsKeyword)) { implementsKeywordLength = scanner.getStartPos() - implementsKeywordStart; - node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference, TrailingCommaBehavior.Disallow); + node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, + parseTypeReference, /*allowTrailingComma*/ false); } var errorCountBeforeClassBody = file.syntacticErrors.length; if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -3516,7 +3534,8 @@ module ts { var extendsKeywordLength: number; if (parseOptional(SyntaxKind.ExtendsKeyword)) { extendsKeywordLength = scanner.getStartPos() - extendsKeywordStart; - node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference, TrailingCommaBehavior.Disallow); + node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, + parseTypeReference, /*allowTrailingComma*/ false); } var errorCountBeforeInterfaceBody = file.syntacticErrors.length; node.members = parseTypeLiteral().members; @@ -3580,7 +3599,8 @@ module ts { parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { - node.members = parseDelimitedList(ParsingContext.EnumMembers, parseAndCheckEnumMember, TrailingCommaBehavior.Allow); + node.members = parseDelimitedList(ParsingContext.EnumMembers, + parseAndCheckEnumMember, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBraceToken); } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f029b072d44..991e3096daa 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -261,7 +261,9 @@ module ts { localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) } - export interface NodeArray extends Array, TextRange { } + export interface NodeArray extends Array, TextRange { + hasTrailingComma?: boolean; + } export interface Identifier extends Node { text: string; // Text of identifier (with escapes converted to characters) @@ -648,6 +650,7 @@ module ts { getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; getRootSymbol(symbol: Symbol): Symbol; getContextualType(node: Node): Type; + getResolvedSignature(node: CallExpression, candidatesOutArray?: Signature[]): Signature; // Returns the constant value of this enum member, or 'undefined' if the enum member has a // computed value. @@ -935,7 +938,7 @@ module ts { resolvedReturnType: Type; // Resolved return type minArgumentCount: number; // Number of non-optional parameters hasRestParameter: boolean; // True if last parameter is rest parameter - hasStringLiterals: boolean; // True if instantiated + hasStringLiterals: boolean; // True if specialized target?: Signature; // Instantiation target mapper?: TypeMapper; // Instantiation mapper erasedSignatureCache?: Signature; // Erased version of signature (deferred) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2e94e39e71d..0dfcb4ac513 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -819,14 +819,14 @@ module FourSlash { public verifyCurrentSignatureHelpIs(expected: string) { this.taoInvalidReason = 'verifyCurrentSignatureHelpIs NYI'; - var help = this.getActiveSignatureHelp(); + var help = this.getActiveSignatureHelpItem(); assert.equal(help.prefix + help.parameters.map(p => p.display).join(help.separator) + help.suffix, expected); } public verifyCurrentParameterIsVariable(isVariable: boolean) { this.taoInvalidReason = 'verifyCurrentParameterIsVariable NYI'; - var signature = this.getActiveSignatureHelp(); + var signature = this.getActiveSignatureHelpItem(); assert.isNotNull(signature); assert.equal(isVariable, signature.isVariadic); } @@ -842,7 +842,7 @@ module FourSlash { public verifyCurrentParameterSpanIs(parameter: string) { this.taoInvalidReason = 'verifyCurrentParameterSpanIs NYI'; - var activeSignature = this.getActiveSignatureHelp(); + var activeSignature = this.getActiveSignatureHelpItem(); var activeParameter = this.getActiveParameter(); assert.equal(activeParameter.display, parameter); } @@ -858,19 +858,19 @@ module FourSlash { public verifyCurrentSignatureHelpParameterCount(expectedCount: number) { this.taoInvalidReason = 'verifyCurrentSignatureHelpParameterCount NYI'; - assert.equal(this.getActiveSignatureHelp().parameters.length, expectedCount); + assert.equal(this.getActiveSignatureHelpItem().parameters.length, expectedCount); } public verifyCurrentSignatureHelpTypeParameterCount(expectedCount: number) { this.taoInvalidReason = 'verifyCurrentSignatureHelpTypeParameterCount NYI'; - // assert.equal(this.getActiveSignatureHelp().typeParameters.length, expectedCount); + // assert.equal(this.getActiveSignatureHelpItem().typeParameters.length, expectedCount); } public verifyCurrentSignatureHelpDocComment(docComment: string) { this.taoInvalidReason = 'verifyCurrentSignatureHelpDocComment NYI'; - var actualDocComment = this.getActiveSignatureHelp().documentation; + var actualDocComment = this.getActiveSignatureHelpItem().documentation; assert.equal(actualDocComment, docComment); } @@ -941,7 +941,7 @@ module FourSlash { // return help.formal; //} - private getActiveSignatureHelp() { + private getActiveSignatureHelpItem() { var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); // If the signature hasn't been narrowed down yet (e.g. no parameters have yet been entered), @@ -953,14 +953,13 @@ module FourSlash { } private getActiveParameter(): ts.SignatureHelpParameter { - var currentSig = this.getActiveSignatureHelp(); var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); var item = help.items[help.selectedItemIndex]; var state = this.languageService.getSignatureHelpCurrentArgumentState(this.activeFile.fileName, this.currentCaretPosition, help.applicableSpan.start()); // Same logic as in getActiveSignatureHelp - this value might be -1 until a parameter value actually gets typed - var currentParam = state === null ? 0 : state.argumentIndex; + var currentParam = state === undefined ? 0 : state.argumentIndex; return item.parameters[currentParam]; } @@ -1083,7 +1082,7 @@ module FourSlash { } public printCurrentSignatureHelp() { - var sigHelp = this.getActiveSignatureHelp(); + var sigHelp = this.getActiveSignatureHelpItem(); Harness.IO.log(JSON.stringify(sigHelp)); } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index ad07f884cc5..8021a47cee7 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -2,7 +2,6 @@ module ts.formatting { export module SmartIndenter { - export function getIndentation(position: number, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number { if (position > sourceFile.text.length) { return 0; // past EOF @@ -108,8 +107,10 @@ module ts.formatting { */ function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: TypeScript.FormattingOptions): number { // previous token is comma that separates items in list - find the previous item and try to derive indentation from it - var itemInfo = findPrecedingListItem(commaToken); - return deriveActualIndentationFromList(itemInfo.list.getChildren(), itemInfo.listItemIndex, sourceFile, options); + var commaItemInfo = findListItemInfo(commaToken); + Debug.assert(commaItemInfo.listItemIndex > 0); + // The item we're interested in is right before the comma + return deriveActualIndentationFromList(commaItemInfo.list.getChildren(), commaItemInfo.listItemIndex - 1, sourceFile, options); } /* @@ -167,27 +168,6 @@ module ts.formatting { return sourceFile.getLineAndCharacterFromPosition(n.getStart(sourceFile)); } - function findPrecedingListItem(commaToken: Node): { listItemIndex: number; list: Node } { - // CommaToken node is synthetic and thus will be stored in SyntaxList, however parent of the CommaToken points to the container of the SyntaxList skipping the list. - // In order to find the preceding list item we first need to locate SyntaxList itself and then search for the position of CommaToken - var syntaxList = forEach(commaToken.parent.getChildren(), c => { - // find syntax list that covers the span of CommaToken - if (c.kind == SyntaxKind.SyntaxList && c.pos <= commaToken.end && c.end >= commaToken.end) { - return c; - } - }); - Debug.assert(syntaxList); - - var children = syntaxList.getChildren(); - var commaIndex = indexOf(children, commaToken); - Debug.assert(commaIndex !== -1 && commaIndex !== 0); - - return { - listItemIndex: commaIndex - 1, - list: syntaxList - }; - } - function positionBelongsToNode(candidate: Node, position: number, sourceFile: SourceFile): boolean { return candidate.end > position || !isCompletedNode(candidate, sourceFile); } @@ -288,112 +268,6 @@ module ts.formatting { return column; } - function findNextToken(previousToken: Node, parent: Node): Node { - return find(parent); - - function find(n: Node): Node { - if (isToken(n) && n.pos === previousToken.end) { - // this is token that starts at the end of previous token - return it - return n; - } - - var children = n.getChildren(); - for (var i = 0, len = children.length; i < len; ++i) { - var child = children[i]; - var shouldDiveInChildNode = - // previous token is enclosed somewhere in the child - (child.pos <= previousToken.pos && child.end > previousToken.end) || - // previous token ends exactly at the beginning of child - (child.pos === previousToken.end); - - if (shouldDiveInChildNode && nodeHasTokens(child)) { - return find(child); - } - } - - return undefined; - } - } - - function findPrecedingToken(position: number, sourceFile: SourceFile): Node { - return find(sourceFile); - - function findRightmostToken(n: Node): Node { - if (isToken(n)) { - return n; - } - - var children = n.getChildren(); - var candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length); - return candidate && findRightmostToken(candidate); - - } - - function find(n: Node): Node { - if (isToken(n)) { - return n; - } - - var children = n.getChildren(); - for (var i = 0, len = children.length; i < len; ++i) { - var child = children[i]; - if (nodeHasTokens(child)) { - 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); - return candidate && findRightmostToken(candidate) - } - else { - // candidate should be in this node - return find(child); - } - } - } - } - - Debug.assert(n.kind === SyntaxKind.SourceFile); - - // Here we know that none of child token nodes embrace the position, - // the only known case is when position is at the end of the file. - // Try to find the rightmost token in the file without filtering. - // Namely we are skipping the check: 'position < node.end' - if (children.length) { - var candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length); - return candidate && findRightmostToken(candidate); - } - } - - /// finds last node that is considered as candidate for search (isCandidate(node) === true) starting from 'exclusiveStartPosition' - function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number): Node { - for (var i = exclusiveStartPosition - 1; i >= 0; --i) { - if (nodeHasTokens(children[i])) { - return children[i]; - } - } - } - } - - /* - * Checks if node is something that can contain tokens (except EOF) - filters out EOF tokens, Missing\Omitted expressions, empty SyntaxLists and expression statements that wrap any of listed nodes. - */ - function nodeHasTokens(n: Node): boolean { - if (n.kind === SyntaxKind.ExpressionStatement) { - return nodeHasTokens((n).expression); - } - - if (n.kind === SyntaxKind.EndOfFileToken || n.kind === SyntaxKind.OmittedExpression || n.kind === SyntaxKind.Missing) { - return false; - } - - // SyntaxList is already realized so getChildCount should be fast and non-expensive - return n.kind !== SyntaxKind.SyntaxList || n.getChildCount() !== 0; - } - - function isToken(n: Node): boolean { - return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken; - } - function nodeContentIsIndented(parent: Node, child: Node): boolean { switch (parent.kind) { case SyntaxKind.ClassDeclaration: diff --git a/src/services/services.ts b/src/services/services.ts index d84f4129ec8..449fab183ca 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -9,6 +9,8 @@ /// /// /// +/// +/// /// /// @@ -201,7 +203,7 @@ module ts { } public getFirstToken(sourceFile?: SourceFile): Node { - var children = this.getChildren(sourceFile); + var children = this.getChildren(); for (var i = 0; i < children.length; i++) { var child = children[i]; if (child.kind < SyntaxKind.Missing) return child; @@ -1129,7 +1131,7 @@ module ts { export class OperationCanceledException { } - class CancellationTokenObject { + export class CancellationTokenObject { public static None: CancellationTokenObject = new CancellationTokenObject(null) @@ -2250,40 +2252,6 @@ module ts { } } - /** Get the token whose text contains the position, or the containing node. */ - function getNodeAtPosition(sourceFile: SourceFile, position: number) { - var current: Node = sourceFile; - outer: while (true) { - // find the child that has this - for (var i = 0, n = current.getChildCount(); i < n; i++) { - var child = current.getChildAt(i); - if (child.getStart() <= position && position < child.getEnd()) { - current = child; - continue outer; - } - } - return current; - } - } - - /** Get a token that contains the position. This is guaranteed to return a token, the position can be in the - * leading trivia or within the token text. - */ - function getTokenAtPosition(sourceFile: SourceFile, position: number) { - var current: Node = sourceFile; - outer: while (true) { - // find the child that has this - for (var i = 0, n = current.getChildCount(); i < n; i++) { - var child = current.getChildAt(i); - if (child.getFullStart() <= position && position < child.getEnd()) { - current = child; - continue outer; - } - } - return current; - } - } - function getContainerNode(node: Node): Node { while (true) { node = node.parent; @@ -2546,7 +2514,7 @@ module ts { result.push(getDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); return true; } - + return false; } @@ -2747,7 +2715,7 @@ module ts { break; } } - + if (shouldHighlightNextKeyword) { result.push(new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(elseKeyword.getStart(), ifKeyword.end), /* isWriteAccess */ false)); i++; // skip the next keyword @@ -3754,7 +3722,30 @@ module ts { // Reset writer back to undefined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput writer = undefined; - return emitOutput; + return emitOutput; + } + + // Signature help + /** + * This is a semantic operation. + */ + function getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems { + synchronizeHostData(); + + fileName = TypeScript.switchToForwardSlashes(fileName); + var sourceFile = getSourceFile(fileName); + + return SignatureHelp.getSignatureHelpItems(sourceFile, position, typeInfoResolver, cancellationToken); + } + + /** + * This is a syntactic operation + */ + function getSignatureHelpCurrentArgumentState(fileName: string, position: number, applicableSpanStart: number): SignatureHelpState { + fileName = TypeScript.switchToForwardSlashes(fileName); + var sourceFile = getCurrentSourceFile(fileName); + + return SignatureHelp.getSignatureHelpCurrentArgumentState(sourceFile, position, applicableSpanStart); } /// Syntactic features @@ -4353,9 +4344,9 @@ module ts { getCompletionsAtPosition: getCompletionsAtPosition, getCompletionEntryDetails: getCompletionEntryDetails, getTypeAtPosition: getTypeAtPosition, + getSignatureHelpItems: getSignatureHelpItems, + getSignatureHelpCurrentArgumentState: getSignatureHelpCurrentArgumentState, getQuickInfoAtPosition: getQuickInfoAtPosition, - getSignatureHelpItems: (filename, position): SignatureHelpItems => null, - getSignatureHelpCurrentArgumentState: (fileName, position, applicableSpanStart): SignatureHelpState => null, getDefinitionAtPosition: getDefinitionAtPosition, getReferencesAtPosition: getReferencesAtPosition, getOccurrencesAtPosition: getOccurrencesAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index a1767bf0c34..0418a6da6cc 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -599,8 +599,8 @@ module ts { return this.forwardJSONCall( "getSignatureHelpCurrentArgumentState('" + fileName + "', " + position + ", " + applicableSpanStart + ")", () => { - var signatureInfo = this.languageService.getSignatureHelpItems(fileName, position); - return signatureInfo; + var signatureHelpState = this.languageService.getSignatureHelpCurrentArgumentState(fileName, position, applicableSpanStart); + return signatureHelpState; }); } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts new file mode 100644 index 00000000000..f129cabc00a --- /dev/null +++ b/src/services/signatureHelp.ts @@ -0,0 +1,349 @@ +/// + +module ts.SignatureHelp { + + // A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression + // or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference. + // To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it + // will return the generic identifier that started the expression (e.g. "foo" in "fooargumentList.parent; + var candidates = []; + var resolvedSignature = typeInfoResolver.getResolvedSignature(call, candidates); + cancellationToken.throwIfCancellationRequested(); + + if (!candidates.length) { + return undefined; + } + + return createSignatureHelpItems(candidates, resolvedSignature, argumentList); + + /** + * If node is an argument, returns its index in the argument list. + * If not, returns -1. + */ + function getImmediatelyContainingArgumentList(node: Node): Node { + if (node.parent.kind !== SyntaxKind.CallExpression && node.parent.kind !== SyntaxKind.NewExpression) { + return undefined; + } + + // There are 3 cases to handle: + // 1. The token introduces a list, and should begin a sig help session + // 2. The token is either not associated with a list, or ends a list, so the session should end + // 3. The token is buried inside a list, and should give sig help + // + // The following are examples of each: + // + // Case 1: + // foo<$T, U>($a, b) -> The token introduces a list, and should begin a sig help session + // Case 2: + // fo$o$(a, b)$ -> The token is either not associated with a list, or ends a list, so the session should end + // Case 3: + // foo(a$, $b$) -> The token is buried inside a list, and should give sig help + var parent = node.parent; + // Find out if 'node' is an argument, a type argument, or neither + if (node.kind === SyntaxKind.LessThanToken || node.kind === SyntaxKind.OpenParenToken) { + // Find the list that starts right *after* the < or ( token + var list = getChildListThatStartsWithOpenerToken(parent, node, sourceFile); + Debug.assert(list); + return list; + } + + if (node.kind === SyntaxKind.GreaterThanToken + || node.kind === SyntaxKind.CloseParenToken + || node === parent.func) { + return undefined; + } + + return findContainingList(node); + } + + function getContainingArgumentList(node: Node): Node { + for (var n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) { + if (n.kind === SyntaxKind.FunctionBlock) { + return undefined; + } + + var argumentList = getImmediatelyContainingArgumentList(n); + if (argumentList) { + return argumentList; + } + + + // TODO: Handle generic call with incomplete syntax + } + return undefined; + } + + function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListOrTypeArgumentList: Node): SignatureHelpItems { + var items = map(candidates, candidateSignature => { + var parameters = candidateSignature.parameters; + var parameterHelpItems = parameters.length === 0 ? emptyArray : map(parameters, p => { + var display = p.name; + if (candidateSignature.hasRestParameter && parameters[parameters.length - 1] === p) { + display = "..." + display; + } + var isOptional = !!(p.valueDeclaration.flags & NodeFlags.QuestionMark); + if (isOptional) { + display += "?"; + } + display += ": " + typeInfoResolver.typeToString(typeInfoResolver.getTypeOfSymbol(p), argumentListOrTypeArgumentList); + return new SignatureHelpParameter(p.name, "", display, isOptional); + }); + var callTargetNode = (argumentListOrTypeArgumentList.parent).func; + var callTargetSymbol = typeInfoResolver.getSymbolInfo(callTargetNode); + var signatureName = callTargetSymbol ? typeInfoResolver.symbolToString(callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : ""; + var prefix = signatureName; + // TODO(jfreeman): Constraints? + if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { + prefix += "<" + map(candidateSignature.typeParameters, tp => tp.symbol.name).join(", ") + ">"; + } + prefix += "("; + var suffix = "): " + typeInfoResolver.typeToString(candidateSignature.getReturnType(), argumentListOrTypeArgumentList); + return new SignatureHelpItem(candidateSignature.hasRestParameter, prefix, suffix, ", ", parameterHelpItems, ""); + }); + var selectedItemIndex = candidates.indexOf(bestSignature); + if (selectedItemIndex < 0) { + selectedItemIndex = 0; + } + + // We use full start and skip trivia on the end because we want to include trivia on + // both sides. For example, + // + // foo( /*comment */ a, b, c /*comment*/ ) + // | | + // + // The applicable span is from the first bar to the second bar (inclusive, + // but not including parentheses) + var applicableSpanStart = argumentListOrTypeArgumentList.getFullStart(); + var applicableSpanEnd = skipTrivia(sourceFile.text, argumentListOrTypeArgumentList.end, /*stopAfterLineBreak*/ false); + var applicableSpan = new TypeScript.TextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); + return new SignatureHelpItems(items, applicableSpan, selectedItemIndex); + } + } + + export function getSignatureHelpCurrentArgumentState(sourceFile: SourceFile, position: number, applicableSpanStart: number): SignatureHelpState { + var tokenPrecedingSpanStart = findPrecedingToken(applicableSpanStart, sourceFile); + if (!tokenPrecedingSpanStart) { + return undefined; + } + + if (tokenPrecedingSpanStart.kind !== SyntaxKind.OpenParenToken && tokenPrecedingSpanStart.kind !== SyntaxKind.LessThanToken) { + // The span start must have moved backward in the file (for example if the open paren was backspaced) + return undefined; + } + + var tokenPrecedingCurrentPosition = findPrecedingToken(position, sourceFile); + var call = tokenPrecedingSpanStart.parent; + Debug.assert(call.kind === SyntaxKind.CallExpression || call.kind === SyntaxKind.NewExpression, "wrong call kind " + SyntaxKind[call.kind]); + if (tokenPrecedingCurrentPosition.kind === SyntaxKind.CloseParenToken || tokenPrecedingCurrentPosition.kind === SyntaxKind.GreaterThanToken) { + if (tokenPrecedingCurrentPosition.parent === call) { + // This call expression is complete. Stop signature help. + return undefined; + } + } + + var argumentListOrTypeArgumentList = getChildListThatStartsWithOpenerToken(call, tokenPrecedingSpanStart, sourceFile); + // Debug.assert(argumentListOrTypeArgumentList.getChildCount() === 0 || argumentListOrTypeArgumentList.getChildCount() % 2 === 1, "Even number of children"); + + // The call might be finished, but incorrectly. Check if we are still within the bounds of the call + if (position > skipTrivia(sourceFile.text, argumentListOrTypeArgumentList.end, /*stopAfterLineBreak*/ false)) { + return undefined; + } + + var numberOfCommas = countWhere(argumentListOrTypeArgumentList.getChildren(), arg => arg.kind === SyntaxKind.CommaToken); + var argumentCount = numberOfCommas + 1; + if (argumentCount <= 1) { + return new SignatureHelpState(/*argumentIndex*/ 0, argumentCount); + } + + var indexOfNodeContainingPosition = findListItemIndexContainingPosition(argumentListOrTypeArgumentList, position); + + // indexOfNodeContainingPosition checks that position is between pos and end of each child, so it is + // possible that we are to the right of all children. Assume that we are still within + // the applicable span and that we are typing the last argument + // Alternatively, we could be in range of one of the arguments, in which case we need to divide + // by 2 to exclude commas. Use bit shifting in order to take the floor of the division. + var argumentIndex = indexOfNodeContainingPosition < 0 ? argumentCount - 1 : indexOfNodeContainingPosition >> 1; + return new SignatureHelpState(argumentIndex, argumentCount); + } + + function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { + var children = parent.getChildren(sourceFile); + var indexOfOpenerToken = children.indexOf(openerToken); + return children[indexOfOpenerToken + 1]; + } +} \ No newline at end of file diff --git a/src/services/signatureInfoHelpers.ts b/src/services/signatureInfoHelpers.ts deleted file mode 100644 index 8594a5dd320..00000000000 --- a/src/services/signatureInfoHelpers.ts +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. -// See LICENSE.txt in the project root for complete license information. - -/// - -module TypeScript.Services { - - export interface IPartiallyWrittenTypeArgumentListInformation { - genericIdentifer: TypeScript.ISyntaxToken; - lessThanToken: TypeScript.ISyntaxToken; - argumentIndex: number; - } - - export interface IExpressionWithArgumentListSyntax extends IExpressionSyntax { - expression: IExpressionSyntax; - argumentList: ArgumentListSyntax; - } - - export class SignatureInfoHelpers { - - // A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression - // or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference. - // To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it - // will return the generic identifier that started the expression (e.g. "foo" in "foo 1; - - for (var i = 0, n = signatures.length; i < n; i++) { - var signature = signatures[i]; - - // filter out the definition signature if there are overloads - if (hasOverloads && signature.isDefinition()) { - continue; - } - - var signatureGroupInfo = new FormalSignatureItemInfo(); - var paramIndexInfo: number[] = []; - var functionName = signature.getScopedNameEx(enclosingScopeSymbol).toString(); - if (!functionName && (!symbol.isType() || (symbol).isNamedTypeSymbol())) { - functionName = symbol.getScopedNameEx(enclosingScopeSymbol).toString(); - } - - var signatureMemberName = signature.getSignatureTypeNameEx(functionName, /*shortform*/ false, /*brackets*/ false, enclosingScopeSymbol, /*getParamMarkerInfo*/ true, /*getTypeParameterMarkerInfo*/ true); - signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(signatureMemberName, paramIndexInfo); - signatureGroupInfo.docComment = signature.docComments(); - - var parameterMarkerIndex = 0; - - if (signature.isGeneric()) { - var typeParameters = signature.getTypeParameters(); - for (var j = 0, m = typeParameters.length; j < m; j++) { - var typeParameter = typeParameters[j]; - var signatureTypeParameterInfo = new FormalTypeParameterInfo(); - signatureTypeParameterInfo.name = typeParameter.getDisplayName(); - signatureTypeParameterInfo.docComment = typeParameter.docComments(); - signatureTypeParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; - signatureTypeParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; - parameterMarkerIndex++; - signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); - } - } - - var parameters = signature.parameters; - for (var j = 0, m = parameters.length; j < m; j++) { - var parameter = parameters[j]; - var signatureParameterInfo = new FormalParameterInfo(); - signatureParameterInfo.isVariable = signature.hasVarArgs && (j === parameters.length - 1); - signatureParameterInfo.name = parameter.getDisplayName(); - signatureParameterInfo.docComment = parameter.docComments(); - signatureParameterInfo.minChar = paramIndexInfo[2 * parameterMarkerIndex]; - signatureParameterInfo.limChar = paramIndexInfo[2 * parameterMarkerIndex + 1]; - parameterMarkerIndex++; - signatureGroupInfo.parameters.push(signatureParameterInfo); - } - - signatureGroup.push(signatureGroupInfo); - } - - return signatureGroup; - } - - public static getSignatureInfoFromGenericSymbol(symbol: TypeScript.PullSymbol, enclosingScopeSymbol: TypeScript.PullSymbol, compilerState: LanguageServiceCompiler) { - var signatureGroupInfo = new FormalSignatureItemInfo(); - - var paramIndexInfo: number[] = []; - var symbolName = symbol.getScopedNameEx(enclosingScopeSymbol, /*skipTypeParametersInName*/ false, /*useConstaintInName*/ true, /*getPrettyTypeName*/ false, /*getTypeParamMarkerInfo*/ true); - - signatureGroupInfo.signatureInfo = TypeScript.MemberName.memberNameToString(symbolName, paramIndexInfo); - signatureGroupInfo.docComment = symbol.docComments(); - - var typeSymbol = symbol.type; - - var typeParameters = typeSymbol.getTypeParameters(); - for (var i = 0, n = typeParameters.length; i < n; i++) { - var typeParameter = typeParameters[i]; - var signatureTypeParameterInfo = new FormalTypeParameterInfo(); - signatureTypeParameterInfo.name = typeParameter.getDisplayName(); - signatureTypeParameterInfo.docComment = typeParameter.docComments(); - signatureTypeParameterInfo.minChar = paramIndexInfo[2 * i]; - signatureTypeParameterInfo.limChar = paramIndexInfo[2 * i + 1]; - signatureGroupInfo.typeParameters.push(signatureTypeParameterInfo); - } - - return [signatureGroupInfo]; - } - - public static getActualSignatureInfoFromCallExpression(ast: IExpressionWithArgumentListSyntax, caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { - if (!ast) { - return null; - } - - var result = new ActualSignatureInfo(); - - // The expression is not guaranteed to be complete, we need to populate the min and lim with the most accurate information we have about - // type argument and argument lists - var parameterMinChar = caretPosition; - var parameterLimChar = caretPosition; - - if (ast.argumentList.typeArgumentList) { - parameterMinChar = Math.min(start(ast.argumentList.typeArgumentList)); - parameterLimChar = Math.max(Math.max(start(ast.argumentList.typeArgumentList), end(ast.argumentList.typeArgumentList) + trailingTriviaWidth(ast.argumentList.typeArgumentList))); - } - - if (ast.argumentList.arguments) { - parameterMinChar = Math.min(parameterMinChar, end(ast.argumentList.openParenToken)); - parameterLimChar = Math.max(parameterLimChar, - ast.argumentList.closeParenToken.fullWidth() > 0 ? start(ast.argumentList.closeParenToken) : fullEnd(ast.argumentList)); - } - - result.parameterMinChar = parameterMinChar; - result.parameterLimChar = parameterLimChar; - result.currentParameterIsTypeParameter = false; - result.currentParameter = -1; - - if (typeParameterInformation) { - result.currentParameterIsTypeParameter = true; - result.currentParameter = typeParameterInformation.argumentIndex; - } - else if (ast.argumentList.arguments && ast.argumentList.arguments.length > 0) { - result.currentParameter = 0; - for (var index = 0; index < ast.argumentList.arguments.length; index++) { - if (caretPosition > end(ast.argumentList.arguments[index]) + lastToken(ast.argumentList.arguments[index]).trailingTriviaWidth()) { - result.currentParameter++; - } - } - } - - return result; - } - - public static getActualSignatureInfoFromPartiallyWritenGenericExpression(caretPosition: number, typeParameterInformation: IPartiallyWrittenTypeArgumentListInformation): ActualSignatureInfo { - var result = new ActualSignatureInfo(); - - result.parameterMinChar = start(typeParameterInformation.lessThanToken); - result.parameterLimChar = Math.max(fullEnd(typeParameterInformation.lessThanToken), caretPosition); - result.currentParameterIsTypeParameter = true; - result.currentParameter = typeParameterInformation.argumentIndex; - - return result; - } - - public static isSignatureHelpBlocker(sourceUnit: TypeScript.SourceUnitSyntax, position: number): boolean { - // We shouldn't be getting a possition that is outside the file because - // isEntirelyInsideComment can't handle when the position is out of bounds, - // callers should be fixed, however we should be resiliant to bad inputs - // so we return true (this position is a blocker for getting signature help) - if (position < 0 || position > fullWidth(sourceUnit)) { - return true; - } - - return TypeScript.Syntax.isEntirelyInsideComment(sourceUnit, position); - } - - public static isTargetOfObjectCreationExpression(positionedToken: TypeScript.ISyntaxToken): boolean { - var positionedParent = TypeScript.Syntax.getAncestorOfKind(positionedToken, TypeScript.SyntaxKind.ObjectCreationExpression); - if (positionedParent) { - var objectCreationExpression = positionedParent; - var expressionRelativeStart = objectCreationExpression.newKeyword.fullWidth(); - var tokenRelativeStart = positionedToken.fullStart() - fullStart(positionedParent); - return tokenRelativeStart >= expressionRelativeStart && - tokenRelativeStart <= (expressionRelativeStart + fullWidth(objectCreationExpression.expression)); - } - - return false; - } - - private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken { - if (!token || token.kind() !== tokenKind) { - throw TypeScript.Errors.invalidOperation(); - } - - // Skip the current token - token = previousToken(token, /*includeSkippedTokens*/ true); - - var stack = 0; - - while (token) { - if (token.kind() === matchingTokenKind) { - if (stack === 0) { - // Found the matching token, return - return token; - } - else if (stack < 0) { - // tokens overlapped.. bail out. - break; - } - else { - stack--; - } - } - else if (token.kind() === tokenKind) { - stack++; - } - - // Move back - token = previousToken(token, /*includeSkippedTokens*/ true); - } - - // Did not find matching token - return null; - } - } -} \ No newline at end of file diff --git a/src/services/utilities.ts b/src/services/utilities.ts new file mode 100644 index 00000000000..2824ab5d2da --- /dev/null +++ b/src/services/utilities.ts @@ -0,0 +1,204 @@ +// These utilities are common to multiple language service features. +module ts { + export interface ListItemInfo { + listItemIndex: number; + list: Node; + } + + export function findListItemInfo(node: Node): ListItemInfo { + var syntaxList = findContainingList(node); + var children = syntaxList.getChildren(); + var index = indexOf(children, node); + + return { + listItemIndex: index, + list: syntaxList + }; + } + + export function findContainingList(node: Node): Node { + // The node might be a list element (nonsynthetic) or a comma (synthetic). Either way, it will + // be parented by the container of the SyntaxList, not the SyntaxList itself. + // In order to find the list item index, we first need to locate SyntaxList itself and then search + // for the position of the relevant node (or comma). + var syntaxList = forEach(node.parent.getChildren(), c => { + // find syntax list that covers the span of the node + if (c.kind == SyntaxKind.SyntaxList && c.pos <= node.pos && c.end >= node.end) { + return c; + } + }); + + return syntaxList; + } + + /** + * Includes the start position of each child, but excludes the end. + */ + export function findListItemIndexContainingPosition(list: Node, position: number): number { + Debug.assert(list.kind === SyntaxKind.SyntaxList); + var children = list.getChildren(); + for (var i = 0; i < children.length; i++) { + if (children[i].pos <= position && children[i].end > position) { + return i; + } + } + + return -1; + } + + /** Get a token that contains the position. This is guaranteed to return a token, the position can be in the + * leading trivia or within the token text. + */ + export function getTokenAtPosition(sourceFile: SourceFile, position: number) { + var current: Node = sourceFile; + outer: while (true) { + // find the child that has this + for (var i = 0, n = current.getChildCount(); i < n; i++) { + var child = current.getChildAt(i); + if (child.getFullStart() <= position && position < child.getEnd()) { + current = child; + continue outer; + } + } + return current; + } + } + + /** Get the token whose text contains the position, or the containing node. */ + export function getNodeAtPosition(sourceFile: SourceFile, position: number) { + var current: Node = sourceFile; + outer: while (true) { + // find the child that has this + for (var i = 0, n = current.getChildCount(); i < n; i++) { + var child = current.getChildAt(i); + if (child.getStart() <= position && position < child.getEnd()) { + current = child; + continue outer; + } + } + return current; + } + } + + /** + * The token on the left of the position is the token that strictly includes the position + * or sits to the left of the cursor if it is on a boundary. For example + * + * fo|o -> will return foo + * foo |bar -> will return foo + * + */ + export function findTokenOnLeftOfPosition(file: SourceFile, position: number): Node { + // Ideally, getTokenAtPosition should return a token. However, it is currently + // broken, so we do a check to make sure the result was indeed a token. + var tokenAtPosition = getTokenAtPosition(file, position); + if (isToken(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) { + return tokenAtPosition; + } + + return findPrecedingToken(position, file); + } + + export function findNextToken(previousToken: Node, parent: Node): Node { + return find(parent); + + function find(n: Node): Node { + if (isToken(n) && n.pos === previousToken.end) { + // this is token that starts at the end of previous token - return it + return n; + } + + var children = n.getChildren(); + for (var i = 0, len = children.length; i < len; ++i) { + var child = children[i]; + var shouldDiveInChildNode = + // previous token is enclosed somewhere in the child + (child.pos <= previousToken.pos && child.end > previousToken.end) || + // previous token ends exactly at the beginning of child + (child.pos === previousToken.end); + + if (shouldDiveInChildNode && nodeHasTokens(child)) { + return find(child); + } + } + + return undefined; + } + } + + export function findPrecedingToken(position: number, sourceFile: SourceFile): Node { + return find(sourceFile); + + function findRightmostToken(n: Node): Node { + if (isToken(n)) { + return n; + } + + var children = n.getChildren(); + var candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length); + return candidate && findRightmostToken(candidate); + + } + + function find(n: Node): Node { + if (isToken(n)) { + return n; + } + + var children = n.getChildren(); + for (var i = 0, len = children.length; i < len; ++i) { + var child = children[i]; + if (nodeHasTokens(child)) { + 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); + return candidate && findRightmostToken(candidate) + } + else { + // candidate should be in this node + return find(child); + } + } + } + } + + Debug.assert(n.kind === SyntaxKind.SourceFile); + + // Here we know that none of child token nodes embrace the position, + // the only known case is when position is at the end of the file. + // Try to find the rightmost token in the file without filtering. + // Namely we are skipping the check: 'position < node.end' + if (children.length) { + var candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length); + return candidate && findRightmostToken(candidate); + } + } + + /// finds last node that is considered as candidate for search (isCandidate(node) === true) starting from 'exclusiveStartPosition' + function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number): Node { + for (var i = exclusiveStartPosition - 1; i >= 0; --i) { + if (nodeHasTokens(children[i])) { + return children[i]; + } + } + } + } + + function nodeHasTokens(n: Node): boolean { + if (n.kind === SyntaxKind.ExpressionStatement) { + return nodeHasTokens((n).expression); + } + + if (n.kind === SyntaxKind.EndOfFileToken || n.kind === SyntaxKind.OmittedExpression || n.kind === SyntaxKind.Missing) { + return false; + } + + // SyntaxList is already realized so getChildCount should be fast and non-expensive + return n.kind !== SyntaxKind.SyntaxList || n.getChildCount() !== 0; + } + + function isToken(n: Node): boolean { + return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/castExpressionParentheses.js b/tests/baselines/reference/castExpressionParentheses.js index 2f62d6e9447..2b518226c60 100644 --- a/tests/baselines/reference/castExpressionParentheses.js +++ b/tests/baselines/reference/castExpressionParentheses.js @@ -43,7 +43,7 @@ new (A()); // parentheses should be omitted // literals { a: 0 }; -[1, 3, ]; +[1, 3,]; "string"; 23.0; /regexp/g; diff --git a/tests/baselines/reference/emptyExpr.js b/tests/baselines/reference/emptyExpr.js index 36fd5ccee19..de3bddeba1e 100644 --- a/tests/baselines/reference/emptyExpr.js +++ b/tests/baselines/reference/emptyExpr.js @@ -2,4 +2,4 @@ [{},] //// [emptyExpr.js] -[{}, ]; +[{},]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression10.js b/tests/baselines/reference/parserArrayLiteralExpression10.js index 8cbbff49c9f..986d3045dc0 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression10.js +++ b/tests/baselines/reference/parserArrayLiteralExpression10.js @@ -2,4 +2,4 @@ var v = [1,1,]; //// [parserArrayLiteralExpression10.js] -var v = [1, 1, ]; +var v = [1, 1,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression15.js b/tests/baselines/reference/parserArrayLiteralExpression15.js index f31617a998b..84ab9dac240 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression15.js +++ b/tests/baselines/reference/parserArrayLiteralExpression15.js @@ -2,4 +2,4 @@ var v = [,,1,1,,1,,1,1,,1,]; //// [parserArrayLiteralExpression15.js] -var v = [, , 1, 1, , 1, , 1, 1, , 1, ]; +var v = [, , 1, 1, , 1, , 1, 1, , 1,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression2.js b/tests/baselines/reference/parserArrayLiteralExpression2.js index f6984016054..1fb26155eb5 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression2.js +++ b/tests/baselines/reference/parserArrayLiteralExpression2.js @@ -2,4 +2,4 @@ var v = [,]; //// [parserArrayLiteralExpression2.js] -var v = [, ]; +var v = [,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression3.js b/tests/baselines/reference/parserArrayLiteralExpression3.js index 6d70ebdb37f..2d7d19fc2c3 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression3.js +++ b/tests/baselines/reference/parserArrayLiteralExpression3.js @@ -2,4 +2,4 @@ var v = [,,]; //// [parserArrayLiteralExpression3.js] -var v = [, , ]; +var v = [, ,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression4.js b/tests/baselines/reference/parserArrayLiteralExpression4.js index dd75cb14fd6..2287897338e 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression4.js +++ b/tests/baselines/reference/parserArrayLiteralExpression4.js @@ -2,4 +2,4 @@ var v = [,,,]; //// [parserArrayLiteralExpression4.js] -var v = [, , , ]; +var v = [, , ,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression7.js b/tests/baselines/reference/parserArrayLiteralExpression7.js index 2ea0b298b4e..b302e87e352 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression7.js +++ b/tests/baselines/reference/parserArrayLiteralExpression7.js @@ -2,4 +2,4 @@ var v = [1,]; //// [parserArrayLiteralExpression7.js] -var v = [1, ]; +var v = [1,]; diff --git a/tests/baselines/reference/parserArrayLiteralExpression8.js b/tests/baselines/reference/parserArrayLiteralExpression8.js index 7ef65a8cc0d..223a86db229 100644 --- a/tests/baselines/reference/parserArrayLiteralExpression8.js +++ b/tests/baselines/reference/parserArrayLiteralExpression8.js @@ -2,4 +2,4 @@ var v = [,1,]; //// [parserArrayLiteralExpression8.js] -var v = [, 1, ]; +var v = [, 1,]; diff --git a/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js b/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js index 26dc61d189c..478c05f5e73 100644 --- a/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js +++ b/tests/baselines/reference/trailingCommaInHeterogenousArrayLiteral1.js @@ -17,7 +17,7 @@ var arrTest = (function () { }; arrTest.prototype.callTest = function () { // these two should give the same error - this.test([1, 2, "hi", 5, ]); + this.test([1, 2, "hi", 5,]); this.test([1, 2, "hi", 5]); }; return arrTest; diff --git a/tests/baselines/reference/trailingCommasES3.js b/tests/baselines/reference/trailingCommasES3.js index 56e30fe5c17..554390a83ea 100644 --- a/tests/baselines/reference/trailingCommasES3.js +++ b/tests/baselines/reference/trailingCommasES3.js @@ -18,8 +18,8 @@ var o2 = { a: 1, b: 2 }; var o3 = { a: 1 }; var o4 = {}; var a1 = [1, 2]; -var a2 = [1, 2, ]; -var a3 = [1, ]; +var a2 = [1, 2,]; +var a3 = [1,]; var a4 = []; -var a5 = [1, , ]; -var a6 = [, , ]; +var a5 = [1, ,]; +var a6 = [, ,]; diff --git a/tests/baselines/reference/trailingCommasES5.js b/tests/baselines/reference/trailingCommasES5.js index f342d9ac2d7..e54e911189a 100644 --- a/tests/baselines/reference/trailingCommasES5.js +++ b/tests/baselines/reference/trailingCommasES5.js @@ -14,12 +14,12 @@ var a6 = [, , ]; //// [trailingCommasES5.js] var o1 = { a: 1, b: 2 }; -var o2 = { a: 1, b: 2, }; -var o3 = { a: 1, }; +var o2 = { a: 1, b: 2, }; +var o3 = { a: 1, }; var o4 = {}; var a1 = [1, 2]; -var a2 = [1, 2, ]; -var a3 = [1, ]; +var a2 = [1, 2,]; +var a3 = [1,]; var a4 = []; -var a5 = [1, , ]; -var a6 = [, , ]; +var a5 = [1, ,]; +var a6 = [, ,]; diff --git a/tests/cases/fourslash/augmentedTypesModule2.ts b/tests/cases/fourslash/augmentedTypesModule2.ts new file mode 100644 index 00000000000..ca4d2db1423 --- /dev/null +++ b/tests/cases/fourslash/augmentedTypesModule2.ts @@ -0,0 +1,23 @@ +/// + +////function /*11*/m2f(x: number) { }; +////module m2f { export interface I { foo(): void } } +////var x: m2f./*1*/ +////var r/*2*/ = m2f/*3*/; + +//goTo.marker('11'); +//verify.quickInfoIs('(x: number): void'); + +//goTo.marker('1'); +//verify.completionListContains('I'); + +//edit.insert('I.'); +//verify.not.completionListContains('foo'); +//edit.backspace(1); + +//goTo.marker('2'); +//verify.quickInfoIs('typeof m2f'); + +goTo.marker('3'); +edit.insert('('); +verify.currentSignatureHelpIs('m2f(x: number): void'); diff --git a/tests/cases/fourslash/augmentedTypesModule3.ts b/tests/cases/fourslash/augmentedTypesModule3.ts new file mode 100644 index 00000000000..08d0b241ef1 --- /dev/null +++ b/tests/cases/fourslash/augmentedTypesModule3.ts @@ -0,0 +1,20 @@ +/// + +////function m2g() { }; +////module m2g { export class C { foo(x: number) { } } } +////var x: m2g./*1*/; +////var r/*2*/ = m2g/*3*/; + +//goTo.marker('1'); +//verify.completionListContains('C'); + +//edit.insert('C.'); +//verify.not.completionListContains('foo'); +//edit.backspace(1); + +//goTo.marker('2'); +//verify.quickInfoIs("typeof m2g", undefined, "r", "var"); + +goTo.marker('3'); +edit.insert('('); +verify.currentSignatureHelpIs('m2g(): void'); \ No newline at end of file diff --git a/tests/cases/fourslash/augmentedTypesModule6.ts b/tests/cases/fourslash/augmentedTypesModule6.ts new file mode 100644 index 00000000000..f7e71c4a685 --- /dev/null +++ b/tests/cases/fourslash/augmentedTypesModule6.ts @@ -0,0 +1,34 @@ +/// + +////declare class m3f { foo(x: number): void } +////module m3f { export interface I { foo(): void } } +////var x: m3f./*1*/ +////var r/*4*/ = new /*2*/m3f(/*3*/); +////r./*5*/ +////var r2: m3f.I = r; +////r2./*6*/ + +//goTo.marker('1'); +//verify.completionListContains('I'); + +//verify.not.completionListContains('foo'); +//edit.insert('I;'); + +//goTo.marker('2'); +//verify.completionListContains('m3f'); + +goTo.marker('3'); +verify.currentSignatureHelpIs('m3f(): m3f'); + +//goTo.marker('4'); +//verify.quickInfoIs('m3f'); + +//goTo.marker('5'); +//verify.completionListContains('foo'); +//edit.insert('foo(1)'); + +goTo.marker('6'); +//verify.completionListContains('foo'); +edit.insert('foo('); +// verify.currentSignatureHelpIs('foo(): void'); + diff --git a/tests/cases/fourslash/callSignatureHelp.ts b/tests/cases/fourslash/callSignatureHelp.ts new file mode 100644 index 00000000000..4e813dd17c4 --- /dev/null +++ b/tests/cases/fourslash/callSignatureHelp.ts @@ -0,0 +1,10 @@ +/// + +////interface C { +//// (): number; +////} +////var c: C; +////c(/**/ + +goTo.marker(); +verify.currentSignatureHelpIs('c(): number'); \ No newline at end of file diff --git a/tests/cases/fourslash/classExtendsInterfaceSigHelp1.ts b/tests/cases/fourslash/classExtendsInterfaceSigHelp1.ts new file mode 100644 index 00000000000..ebdccef485f --- /dev/null +++ b/tests/cases/fourslash/classExtendsInterfaceSigHelp1.ts @@ -0,0 +1,18 @@ +/// + +////class C { +//// public foo(x: string); +//// public foo(x: number); +//// public foo(x: any) { return x; } +////} + +////interface I extends C { +//// other(x: any): any; +////} + +////var i: I; +////i.foo(/**/ + +goTo.marker(); +verify.signatureHelpCountIs(2); +verify.currentParameterSpanIs('x: string'); \ No newline at end of file diff --git a/tests/cases/fourslash/externalModuleWithExportAssignment.ts b/tests/cases/fourslash/externalModuleWithExportAssignment.ts new file mode 100644 index 00000000000..0a5da79aad6 --- /dev/null +++ b/tests/cases/fourslash/externalModuleWithExportAssignment.ts @@ -0,0 +1,87 @@ +/// + +// @Filename: externalModuleWithExportAssignment_file0.ts +////module m2 { +//// export interface connectModule { +//// (res, req, next): void; +//// } +//// export interface connectExport { +//// use: (mod: connectModule) => connectExport; +//// listen: (port: number) => void; +//// } +////} +////var m2: { +//// (): m2.connectExport; +//// test1: m2.connectModule; +//// test2(): m2.connectModule; +////}; +////export = m2; + +// @Filename: externalModuleWithExportAssignment_file1.ts +////import /*1*/a1 = require("externalModuleWithExportAssignment_file0"); +////export var /*2*/a = a1; +////a./*3*/test1(/*4*/null, null, null); +////var /*6*/r1 = a.test2(/*5*/); +////var /*8*/r2 = a(/*7*/); +////a1./*9*/test1(/*10*/null, null, null); +////var /*12*/r3 = a1.test2(/*11*/); +////var /*14*/r4 = a1(/*13*/); +////var v1: a1./*15*/connectExport; + +//goTo.file("externalModuleWithExportAssignment_file1.ts"); +//goTo.marker('1'); +//verify.quickInfoIs("a1"); + +//goTo.marker('2'); +//verify.quickInfoIs("{ test1: a1.connectModule; test2(): a1.connectModule; (): a1.connectExport; }", undefined, "a", "var"); + +//goTo.marker('3'); +//verify.quickInfoIs("(res: any, req: any, next: any): void", undefined, "a1.connectModule", "function"); +//verify.completionListContains("test1", "a1.connectModule", undefined, "test1", "property"); +//verify.completionListContains("test2", "(): a1.connectModule", undefined, "test2", "method"); +//verify.not.completionListContains("connectModule"); +//verify.not.completionListContains("connectExport"); + +goTo.marker('4'); +verify.currentSignatureHelpIs("test1(res: any, req: any, next: any): void"); + +goTo.marker('5'); +verify.currentSignatureHelpIs("test2(): a1.connectModule"); + +//goTo.marker('6'); +//verify.quickInfoIs("a1.connectModule", undefined, "r1", "var"); + +goTo.marker('7'); +verify.currentSignatureHelpIs("a(): a1.connectExport"); + +//goTo.marker('8'); +//verify.quickInfoIs("a1.connectExport", undefined, "r2", "var"); + +//goTo.marker('9'); +//verify.quickInfoIs("(res: any, req: any, next: any): void", undefined, "a1.connectModule", "function"); +//verify.completionListContains("test1", "a1.connectModule", undefined, "test1", "property"); +//verify.completionListContains("test2", "(): a1.connectModule", undefined, "test2", "method"); +//verify.not.completionListContains("connectModule"); +//verify.not.completionListContains("connectExport"); + +goTo.marker('10'); +verify.currentSignatureHelpIs("test1(res: any, req: any, next: any): void"); + +goTo.marker('11'); +verify.currentSignatureHelpIs("test2(): a1.connectModule"); + +//goTo.marker('12'); +//verify.quickInfoIs("a1.connectModule", undefined, "r3", "var"); + +goTo.marker('13'); +verify.currentSignatureHelpIs("a1(): a1.connectExport"); + +//goTo.marker('14'); +//verify.quickInfoIs("a1.connectExport", undefined, "r4", "var"); + +//goTo.marker('15'); +//verify.not.completionListContains("test1", "a1.connectModule", undefined, "test1", "property"); +//verify.not.completionListContains("test2", "(): a1.connectModule", undefined, "test2", "method"); +//verify.completionListContains("connectModule", "a1.connectModule", undefined, "a1.connectModule", "interface"); +//verify.completionListContains("connectExport", "a1.connectExport", undefined, "a1.connectExport", "interface"); + diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index f9476ee4d92..35b6894b1ce 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -201,6 +201,7 @@ module FourSlashInterface { FourSlash.currentTestState.verifyImplementorsCountIs(count); } + // Add tests for this. public currentParameterIsVariable() { FourSlash.currentTestState.verifyCurrentParameterIsVariable(!this.negative); } @@ -282,11 +283,11 @@ module FourSlashInterface { } public currentParameterHelpArgumentDocCommentIs(docComment: string) { - FourSlash.currentTestState.verifyCurrentParameterHelpDocComment(docComment); + // FourSlash.currentTestState.verifyCurrentParameterHelpDocComment(docComment); } public currentSignatureHelpDocCommentIs(docComment: string) { - FourSlash.currentTestState.verifyCurrentSignatureHelpDocComment(docComment); + // FourSlash.currentTestState.verifyCurrentSignatureHelpDocComment(docComment); } public signatureHelpCountIs(expected: number) { diff --git a/tests/cases/fourslash/functionOverloadCount.ts b/tests/cases/fourslash/functionOverloadCount.ts new file mode 100644 index 00000000000..00856dcb24a --- /dev/null +++ b/tests/cases/fourslash/functionOverloadCount.ts @@ -0,0 +1,15 @@ +/// + +////class C1 { +//// public attr(): string; +//// public attr(i: number): string; +//// public attr(i: number, x: boolean): string; +//// public attr(i?: any, x?: any) { +//// return "hi"; +//// } +////} +////var i = new C1; +////i.attr(/*1*/ + +goTo.marker('1'); +verify.signatureHelpCountIs(3); \ No newline at end of file diff --git a/tests/cases/fourslash/functionProperty.ts b/tests/cases/fourslash/functionProperty.ts new file mode 100644 index 00000000000..4f089d95cf3 --- /dev/null +++ b/tests/cases/fourslash/functionProperty.ts @@ -0,0 +1,49 @@ +/// + +////var a = { +//// x(a: number) { } +////}; +//// +////var b = { +//// x: function (a: number) { } +////}; +//// +////var c = { +//// x: (a: number) => { } +////}; +////a.x(/*signatureA*/1); +////b.x(/*signatureB*/1); +////c.x(/*signatureC*/1); +////a./*completionA*/; +////b./*completionB*/; +////c./*completionC*/; +////a./*quickInfoA*/x; +////b./*quickInfoB*/x; +////c./*quickInfoC*/x; + +goTo.marker('signatureA'); +verify.currentSignatureHelpIs('x(a: number): void'); + +goTo.marker('signatureB'); +verify.currentSignatureHelpIs('x(a: number): void'); + +goTo.marker('signatureC'); +verify.currentSignatureHelpIs('x(a: number): void'); + +//goTo.marker('completionA'); +//verify.completionListContains("x", "(a: number): void"); + +//goTo.marker('completionB'); +//verify.completionListContains("x", "(a: number) => void"); + +//goTo.marker('completionC'); +//verify.completionListContains("x", "(a: number) => void"); + +//goTo.marker('quickInfoA'); +//verify.quickInfoIs("(a: number): void", undefined, "x", "local function"); + +//goTo.marker('quickInfoB'); +//verify.quickInfoIs("(a: number) => void", undefined, "x", "property"); + +//goTo.marker('quickInfoC'); +//verify.quickInfoIs("(a: number) => void", undefined, "x", "property"); \ No newline at end of file diff --git a/tests/cases/fourslash/genericFunctionReturnType.ts b/tests/cases/fourslash/genericFunctionReturnType.ts new file mode 100644 index 00000000000..3be60777b1f --- /dev/null +++ b/tests/cases/fourslash/genericFunctionReturnType.ts @@ -0,0 +1,21 @@ +/// + +////function foo(x: T, y: U): (a: U) => T { +//// var z = y; +//// return (z) => x; +////} + +////var r/*2*/ = foo(/*1*/1, ""); +////var r2/*4*/ = r(/*3*/""); + +// goTo.marker('1'); +// verify.currentSignatureHelpIs('foo(x: number, y: string): (a: string) => number'); + +//goTo.marker('2'); +//verify.quickInfoIs('(a: string) => number'); + +goTo.marker('3'); +verify.currentSignatureHelpIs('r(a: string): number'); + +//goTo.marker('4'); +//verify.quickInfoIs('number'); \ No newline at end of file diff --git a/tests/cases/fourslash/genericFunctionReturnType2.ts b/tests/cases/fourslash/genericFunctionReturnType2.ts new file mode 100644 index 00000000000..35b4b0af32d --- /dev/null +++ b/tests/cases/fourslash/genericFunctionReturnType2.ts @@ -0,0 +1,24 @@ +/// + +////class C { +//// constructor(x: T) { } +//// foo(x: T) { +//// return (a: T) => x; +//// } +////} + +////var x = new C(1); +////var r/*2*/ = x.foo(/*1*/3); +////var r2/*4*/ = r(/*3*/4); + +goTo.marker('1'); +verify.currentSignatureHelpIs('foo(x: number): (a: number) => number'); + +//goTo.marker('2'); +//verify.quickInfoIs('(a: number) => number'); + +goTo.marker('3'); +verify.currentSignatureHelpIs('r(a: number): number'); + +//goTo.marker('4'); +//verify.quickInfoIs('number'); \ No newline at end of file diff --git a/tests/cases/fourslash/genericFunctionSignatureHelp1.ts b/tests/cases/fourslash/genericFunctionSignatureHelp1.ts new file mode 100644 index 00000000000..fc1e45d7e6b --- /dev/null +++ b/tests/cases/fourslash/genericFunctionSignatureHelp1.ts @@ -0,0 +1,7 @@ +/// + +////function f(a: T): T { return null; } +////f(/**/ + +goTo.marker(); +verify.currentSignatureHelpIs('f(a: T): T'); diff --git a/tests/cases/fourslash/genericFunctionSignatureHelp2.ts b/tests/cases/fourslash/genericFunctionSignatureHelp2.ts new file mode 100644 index 00000000000..77511677588 --- /dev/null +++ b/tests/cases/fourslash/genericFunctionSignatureHelp2.ts @@ -0,0 +1,7 @@ +/// + +////var f = (a: T) => a; +////f(/**/ + +goTo.marker(); +verify.currentSignatureHelpIs('f(a: T): T'); diff --git a/tests/cases/fourslash/genericFunctionSignatureHelp3.ts b/tests/cases/fourslash/genericFunctionSignatureHelp3.ts new file mode 100644 index 00000000000..c5a5f62a0a9 --- /dev/null +++ b/tests/cases/fourslash/genericFunctionSignatureHelp3.ts @@ -0,0 +1,39 @@ +/// + +////function foo1(x: number, callback: (y1: T) => number) { } +////function foo2(x: number, callback: (y2: T) => number) { } +////function foo3(x: number, callback: (y3: T) => number) { } +////function foo4(x: number, callback: (y4: T) => number) { } +////function foo5(x: number, callback: (y5: T) => number) { } +////function foo6(x: number, callback: (y6: T) => number) { } +////function foo7(x: number, callback: (y7: T) => number) { } +//// IDE shows the results on the right of each line, fourslash says different +////foo1(/*1*/ // signature help shows y as T +////foo2(1,/*2*/ // signature help shows y as {} +////foo3(1, (/*3*/ // signature help shows y as T +////foo4(1,/*4*/ // signature help shows y as string +////foo5(1, (/*5*/ // signature help shows y as T +////foo6(1, (/*7*/ // signature help shows y as T + +goTo.marker('1'); +verify.currentSignatureHelpIs('foo1(x: number, callback: (y1: T) => number): void'); + +// goTo.marker('2'); +// verify.currentSignatureHelpIs('foo2(x: number, callback: (y2: {}) => number): void'); + +goTo.marker('3'); +verify.currentSignatureHelpIs('foo3(x: number, callback: (y3: T) => number): void'); + +// goTo.marker('4'); +// verify.currentSignatureHelpIs('foo4(x: number, callback: (y4: string) => number): void'); + +goTo.marker('5'); +verify.currentSignatureHelpIs('foo5(x: number, callback: (y5: T) => number): void'); + +goTo.marker('6'); +// verify.currentSignatureHelpIs('foo6(x: number, callback: (y6: {}) => number): void'); +edit.insert('string>(null,null);'); // need to make this line parse so we can get reasonable LS answers to later tests + +goTo.marker('7'); +verify.currentSignatureHelpIs('foo7(x: number, callback: (y7: T) => number): void'); diff --git a/tests/cases/fourslash/genericFunctionSignatureHelp3MultiFile.ts b/tests/cases/fourslash/genericFunctionSignatureHelp3MultiFile.ts new file mode 100644 index 00000000000..6eadb92f47d --- /dev/null +++ b/tests/cases/fourslash/genericFunctionSignatureHelp3MultiFile.ts @@ -0,0 +1,46 @@ +/// + +// @Filename: genericFunctionSignatureHelp_0.ts +////function foo1(x: number, callback: (y1: T) => number) { } +// @Filename: genericFunctionSignatureHelp_1.ts +////function foo2(x: number, callback: (y2: T) => number) { } +// @Filename: genericFunctionSignatureHelp_2.ts +////function foo3(x: number, callback: (y3: T) => number) { } +// @Filename: genericFunctionSignatureHelp_3.ts +////function foo4(x: number, callback: (y4: T) => number) { } +// @Filename: genericFunctionSignatureHelp_4.ts +////function foo5(x: number, callback: (y5: T) => number) { } +// @Filename: genericFunctionSignatureHelp_5.ts +////function foo6(x: number, callback: (y6: T) => number) { } +// @Filename: genericFunctionSignatureHelp_6.ts +////function foo7(x: number, callback: (y7: T) => number) { } +// @Filename: genericFunctionSignatureHelp_7.ts +////foo1(/*1*/ // signature help shows y as T +////foo2(1,/*2*/ // signature help shows y as {} +////foo3(1, (/*3*/ // signature help shows y as T +////foo4(1,/*4*/ // signature help shows y as string +////foo5(1, (/*5*/ // signature help shows y as T +////foo6(1, (/*7*/ // signature help shows y as T + +goTo.marker('1'); +verify.currentSignatureHelpIs('foo1(x: number, callback: (y1: T) => number): void'); + +// goTo.marker('2'); +// verify.currentSignatureHelpIs('foo2(x: number, callback: (y2: {}) => number): void'); + +goTo.marker('3'); +verify.currentSignatureHelpIs('foo3(x: number, callback: (y3: T) => number): void'); + +// goTo.marker('4'); +// verify.currentSignatureHelpIs('foo4(x: number, callback: (y4: string) => number): void'); + +goTo.marker('5'); +verify.currentSignatureHelpIs('foo5(x: number, callback: (y5: T) => number): void'); + +goTo.marker('6'); +// verify.currentSignatureHelpIs('foo6(x: number, callback: (y6: {}) => number): void'); +edit.insert('string>(null,null);'); // need to make this line parse so we can get reasonable LS answers to later tests + +goTo.marker('7'); +verify.currentSignatureHelpIs('foo7(x: number, callback: (y7: T) => number): void'); diff --git a/tests/cases/fourslash/genericParameterHelp.ts b/tests/cases/fourslash/genericParameterHelp.ts index e5a6385c179..a1df1cc2f32 100644 --- a/tests/cases/fourslash/genericParameterHelp.ts +++ b/tests/cases/fourslash/genericParameterHelp.ts @@ -27,67 +27,67 @@ ////class Bar extends testClass; -goTo.marker("1"); -// verify.currentSignatureParamterCountIs(3); -// verify.currentSignatureHelpIs("testFunction(a: T, b: U, c: M): M"); +// goTo.marker("1"); +// verify.currentSignatureParamterCountIs(3); +// verify.currentSignatureHelpIs("testFunction(a: T, b: U, c: M): M"); -// verify.currentParameterHelpArgumentNameIs("T"); -// verify.currentParameterSpanIs("T extends IFoo"); +// verify.currentParameterHelpArgumentNameIs("T"); +// verify.currentParameterSpanIs("T extends IFoo"); -// goTo.marker("2"); -// verify.currentParameterHelpArgumentNameIs("U"); -// verify.currentParameterSpanIs("U"); +// goTo.marker("2"); +// verify.currentParameterHelpArgumentNameIs("U"); +// verify.currentParameterSpanIs("U"); -// goTo.marker("3"); -// verify.currentParameterHelpArgumentNameIs("a"); -// verify.currentParameterSpanIs("a: T"); + goTo.marker("3"); + verify.currentParameterHelpArgumentNameIs("a"); + verify.currentParameterSpanIs("a: T"); -// goTo.marker("4"); -// verify.currentParameterHelpArgumentNameIs("M"); -// verify.currentParameterSpanIs("M extends IFoo"); + // goTo.marker("4"); + // verify.currentParameterHelpArgumentNameIs("M"); + // verify.currentParameterSpanIs("M extends IFoo"); -// goTo.marker("5"); -// verify.currentParameterHelpArgumentNameIs("M"); -// verify.currentParameterSpanIs("M extends IFoo"); + // goTo.marker("5"); + // verify.currentParameterHelpArgumentNameIs("M"); + // verify.currentParameterSpanIs("M extends IFoo"); -// goTo.marker("construcor1"); -// verify.currentSignatureHelpIs("testClass(a: T, b: U, c: M): testClass"); -// verify.currentParameterHelpArgumentNameIs("T"); -// verify.currentParameterSpanIs("T extends IFoo"); + // goTo.marker("construcor1"); + // verify.currentSignatureHelpIs("testClass(a: T, b: U, c: M): testClass"); + // verify.currentParameterHelpArgumentNameIs("T"); + // verify.currentParameterSpanIs("T extends IFoo"); -// goTo.marker("construcor2"); -// verify.currentParameterHelpArgumentNameIs("U"); -// verify.currentParameterSpanIs("U"); + // goTo.marker("construcor2"); + // verify.currentParameterHelpArgumentNameIs("U"); + // verify.currentParameterSpanIs("U"); -// goTo.marker("construcor3"); -// verify.currentParameterHelpArgumentNameIs("T"); -// verify.currentParameterSpanIs("T extends IFoo"); + //goTo.marker("construcor3"); + //verify.currentParameterHelpArgumentNameIs("T"); + //verify.currentParameterSpanIs("T extends IFoo"); -// goTo.marker("construcor4"); -// verify.currentParameterHelpArgumentNameIs("M"); -// verify.currentParameterSpanIs("M extends IFoo"); + // goTo.marker("construcor4"); + // verify.currentParameterHelpArgumentNameIs("M"); + // verify.currentParameterSpanIs("M extends IFoo"); -// goTo.marker("construcor5"); -// verify.currentParameterHelpArgumentNameIs("U"); -// verify.currentParameterSpanIs("U"); + // goTo.marker("construcor5"); + // verify.currentParameterHelpArgumentNameIs("U"); + // verify.currentParameterSpanIs("U"); -// goTo.marker("type1"); -// verify.signatureHelpCountIs(1); -// verify.currentSignatureHelpIs("testClass"); -// verify.currentParameterHelpArgumentNameIs("T"); -// verify.currentParameterSpanIs("T extends IFoo"); + // goTo.marker("type1"); + // verify.signatureHelpCountIs(1); + // verify.currentSignatureHelpIs("testClass"); + // verify.currentParameterHelpArgumentNameIs("T"); + // verify.currentParameterSpanIs("T extends IFoo"); -// goTo.marker("type2"); -// verify.signatureHelpCountIs(1); -// verify.currentParameterHelpArgumentNameIs("T"); -// verify.currentParameterSpanIs("T extends IFoo"); + // goTo.marker("type2"); + // verify.signatureHelpCountIs(1); + // verify.currentParameterHelpArgumentNameIs("T"); + // verify.currentParameterSpanIs("T extends IFoo"); -// goTo.marker("type3"); -// verify.signatureHelpCountIs(1); -// verify.currentParameterHelpArgumentNameIs("T"); -// verify.currentParameterSpanIs("T extends IFoo"); + // goTo.marker("type3"); + // verify.signatureHelpCountIs(1); + // verify.currentParameterHelpArgumentNameIs("T"); + // verify.currentParameterSpanIs("T extends IFoo"); -// goTo.marker("type4"); -// verify.signatureHelpCountIs(1); -// verify.currentParameterHelpArgumentNameIs("M"); -// verify.currentParameterSpanIs("M extends IFoo"); \ No newline at end of file + // goTo.marker("type4"); + // verify.signatureHelpCountIs(1); + // verify.currentParameterHelpArgumentNameIs("M"); + // verify.currentParameterSpanIs("M extends IFoo"); \ No newline at end of file diff --git a/tests/cases/fourslash/incrementalEditInvocationExpressionAboveInterfaceDeclaration.ts b/tests/cases/fourslash/incrementalEditInvocationExpressionAboveInterfaceDeclaration.ts new file mode 100644 index 00000000000..8a06efbaa44 --- /dev/null +++ b/tests/cases/fourslash/incrementalEditInvocationExpressionAboveInterfaceDeclaration.ts @@ -0,0 +1,17 @@ +/// + +////declare function alert(message?: any): void; +/////*1*/ +////interface Foo { +//// setISO8601(dString): Date; +////} + +diagnostics.setEditValidation(IncrementalEditValidation.None); + +// Do resolve without typeCheck +goTo.marker('1'); +edit.insert("alert("); +verify.currentSignatureHelpIs("alert(message?: any): void"); + +// TypeCheck +verify.errorExistsAfterMarker('1'); diff --git a/tests/cases/fourslash/overloadOnConstCallSignature.ts b/tests/cases/fourslash/overloadOnConstCallSignature.ts new file mode 100644 index 00000000000..757e96f162b --- /dev/null +++ b/tests/cases/fourslash/overloadOnConstCallSignature.ts @@ -0,0 +1,18 @@ +/// + +////var foo: { +//// (name: string): string; +//// (name: 'order'): string; +//// (name: 'content'): string; +//// (name: 'done'): string; +////} + +////var x/*2*/ = foo(/*1*/ + +goTo.marker('1'); +verify.signatureHelpCountIs(4); +verify.currentSignatureHelpIs('foo(name: string): string'); +edit.insert('"hi"'); + +//goTo.marker('2'); +//verify.quickInfoIs('string'); \ No newline at end of file diff --git a/tests/cases/fourslash/paramHelpOnCommaInString.ts b/tests/cases/fourslash/paramHelpOnCommaInString.ts new file mode 100644 index 00000000000..00271781582 --- /dev/null +++ b/tests/cases/fourslash/paramHelpOnCommaInString.ts @@ -0,0 +1,11 @@ +/// + +////function blah(foo: string, bar: number) { +////} +////blah('hola/*1*/,/*2*/') + +// making sure the comma in a string literal doesn't trigger param help on the second function param +goTo.marker('1'); +verify.currentParameterHelpArgumentNameIs('foo'); +goTo.marker('2'); +verify.currentParameterHelpArgumentNameIs('foo'); \ No newline at end of file diff --git a/tests/cases/fourslash/parameterInfoOnParameterType.ts b/tests/cases/fourslash/parameterInfoOnParameterType.ts new file mode 100644 index 00000000000..d28e675e24f --- /dev/null +++ b/tests/cases/fourslash/parameterInfoOnParameterType.ts @@ -0,0 +1,11 @@ +/// + +////function foo(a: string) { }; +////var b = "test"; +////foo("test"/*1*/); +////foo(b/*2*/); + +goTo.marker("1"); +verify.currentParameterHelpArgumentNameIs("a"); +goTo.marker("2"); +verify.currentParameterHelpArgumentNameIs("a"); \ No newline at end of file diff --git a/tests/cases/fourslash/qualifyModuleTypeNames.ts b/tests/cases/fourslash/qualifyModuleTypeNames.ts new file mode 100644 index 00000000000..50a9f6c85f0 --- /dev/null +++ b/tests/cases/fourslash/qualifyModuleTypeNames.ts @@ -0,0 +1,8 @@ +/// + +////module m { export class c { } }; +////function x(arg: m.c) { return arg; } +////x(/**/ + +goTo.marker(); +verify.currentSignatureHelpIs('x(arg: m.c): m.c'); diff --git a/tests/cases/fourslash/quickInfoInFunctionTypeReference2.ts b/tests/cases/fourslash/quickInfoInFunctionTypeReference2.ts new file mode 100644 index 00000000000..608dbba56c3 --- /dev/null +++ b/tests/cases/fourslash/quickInfoInFunctionTypeReference2.ts @@ -0,0 +1,18 @@ +/// + +////class C { +//// map(fn: (k/*1*/: string, value/*2*/: T, context: any) => void, context: any) { +//// } +////} + +////var c: C; +////c.map(/*3*/ + +//goTo.marker('1'); +//verify.quickInfoIs('string'); + +//goTo.marker('2'); +//verify.quickInfoIs('T'); + +goTo.marker('3'); +verify.currentSignatureHelpIs('map(fn: (k: string, value: number, context: any) => void, context: any): void'); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts b/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts new file mode 100644 index 00000000000..056ba5300c7 --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnConstructorWithGenericParameter.ts @@ -0,0 +1,29 @@ +/// + +////interface I { +//// x: number; +////} +////class Foo { +//// y: T; +////} +////class A { +//// foo() { } +////} +////class B extends A { +//// constructor(a: Foo, b: number) { +//// super(); +//// } +////} +////var x = new /*2*/B(/*1*/ + +// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed +edit.insert(''); + +goTo.marker("1"); +verify.currentSignatureHelpIs("B(a: Foo, b: number): B"); +edit.insert("null,"); +verify.currentSignatureHelpIs("B(a: Foo, b: number): B"); +edit.insert("10);"); + +//goTo.marker("2"); +//verify.quickInfoIs("(a: Foo, b: number): B", undefined, "B", "constructor"); \ No newline at end of file diff --git a/tests/cases/fourslash/restArgSignatureHelp.ts b/tests/cases/fourslash/restArgSignatureHelp.ts new file mode 100644 index 00000000000..baa8702a5f6 --- /dev/null +++ b/tests/cases/fourslash/restArgSignatureHelp.ts @@ -0,0 +1,7 @@ +/// + +////function f(...x: any[]) { } +////f(/**/); + +goTo.marker(); +verify.currentParameterHelpArgumentNameIs('x'); diff --git a/tests/cases/fourslash/signatureHelpAnonymousFunction.ts b/tests/cases/fourslash/signatureHelpAnonymousFunction.ts new file mode 100644 index 00000000000..cc723c713d6 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpAnonymousFunction.ts @@ -0,0 +1,17 @@ +/// + +////var anonymousFunctionTest = function(n: number, s: string): (a: number, b: string) => string { +//// return null; +////} +////anonymousFunctionTest(5, "")(/*anonymousFunction1*/1, /*anonymousFunction2*/""); + +goTo.marker('anonymousFunction1'); +verify.signatureHelpCountIs(1); +verify.currentSignatureParamterCountIs(2); +verify.currentSignatureHelpIs('(a: number, b: string): string'); +verify.currentParameterHelpArgumentNameIs("a"); +verify.currentParameterSpanIs("a: number"); + +goTo.marker('anonymousFunction2'); +verify.currentParameterHelpArgumentNameIs("b"); +verify.currentParameterSpanIs("b: string"); diff --git a/tests/cases/fourslash/signatureHelpAtEOF.ts b/tests/cases/fourslash/signatureHelpAtEOF.ts new file mode 100644 index 00000000000..64c4aaeeb2c --- /dev/null +++ b/tests/cases/fourslash/signatureHelpAtEOF.ts @@ -0,0 +1,15 @@ +/// + +////function Foo(arg1: string, arg2: string) { +////} +//// +////Foo(/**/ + +goTo.marker(); +verify.signatureHelpPresent(); +verify.signatureHelpCountIs(1); + +verify.currentSignatureHelpIs("Foo(arg1: string, arg2: string): void"); +verify.currentSignatureParamterCountIs(2); +verify.currentParameterHelpArgumentNameIs("arg1"); +verify.currentParameterSpanIs("arg1: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpBeforeSemicolon1.ts b/tests/cases/fourslash/signatureHelpBeforeSemicolon1.ts new file mode 100644 index 00000000000..2b0b07056bf --- /dev/null +++ b/tests/cases/fourslash/signatureHelpBeforeSemicolon1.ts @@ -0,0 +1,15 @@ +/// + +////function Foo(arg1: string, arg2: string) { +////} +//// +////Foo(/**/; + +goTo.marker(); +verify.signatureHelpPresent(); +verify.signatureHelpCountIs(1); + +verify.currentSignatureHelpIs("Foo(arg1: string, arg2: string): void"); +verify.currentSignatureParamterCountIs(2); +verify.currentParameterHelpArgumentNameIs("arg1"); +verify.currentParameterSpanIs("arg1: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpCallExpression.ts b/tests/cases/fourslash/signatureHelpCallExpression.ts new file mode 100644 index 00000000000..50d89aaae4e --- /dev/null +++ b/tests/cases/fourslash/signatureHelpCallExpression.ts @@ -0,0 +1,16 @@ +/// + +////function fnTest(str: string, num: number) { } +////fnTest(/*1*/'', /*2*/5); + +goTo.marker('1'); +verify.signatureHelpCountIs(1); +verify.currentSignatureParamterCountIs(2); +verify.currentSignatureHelpIs('fnTest(str: string, num: number): void'); + +verify.currentParameterHelpArgumentNameIs('str'); +verify.currentParameterSpanIs("str: string"); + +goTo.marker('2'); +verify.currentParameterHelpArgumentNameIs('num'); +verify.currentParameterSpanIs("num: number"); diff --git a/tests/cases/fourslash/signatureHelpConstructExpression.ts b/tests/cases/fourslash/signatureHelpConstructExpression.ts new file mode 100644 index 00000000000..a88cb3fce68 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpConstructExpression.ts @@ -0,0 +1,17 @@ +/// + +////class sampleCls { constructor(str: string, num: number) { } } +////var x = new sampleCls(/*1*/"", /*2*/5); + +goTo.marker('1'); +verify.signatureHelpCountIs(1); + +verify.currentSignatureParamterCountIs(2); +verify.currentSignatureHelpIs('sampleCls(str: string, num: number): sampleCls'); + +verify.currentParameterHelpArgumentNameIs('str'); +verify.currentParameterSpanIs("str: string"); + +goTo.marker('2'); +verify.currentParameterHelpArgumentNameIs('num'); +verify.currentParameterSpanIs("num: number"); diff --git a/tests/cases/fourslash/signatureHelpConstructorInheritance.ts b/tests/cases/fourslash/signatureHelpConstructorInheritance.ts new file mode 100644 index 00000000000..b066eab3fd4 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpConstructorInheritance.ts @@ -0,0 +1,22 @@ +/// + +////class base { +//// constructor(s: string); +//// constructor(n: number); +//// constructor(a: any) { } +////} +////class B1 extends base { } +////class B2 extends B1 { } +////class B3 extends B2 { +//// constructor() { +//// super(/*indirectSuperCall*/3); +//// } +////} + + +goTo.marker('indirectSuperCall'); +verify.signatureHelpCountIs(2); +verify.currentSignatureParamterCountIs(1); +verify.currentSignatureHelpIs('B2(n: number): B2'); +verify.currentParameterHelpArgumentNameIs("n"); +verify.currentParameterSpanIs("n: number"); diff --git a/tests/cases/fourslash/signatureHelpConstructorOverload.ts b/tests/cases/fourslash/signatureHelpConstructorOverload.ts new file mode 100644 index 00000000000..ce276e09d8f --- /dev/null +++ b/tests/cases/fourslash/signatureHelpConstructorOverload.ts @@ -0,0 +1,16 @@ +/// + +////class clsOverload { constructor(); constructor(test: string); constructor(test?: string) { } } +////var x = new clsOverload(/*1*/); +////var y = new clsOverload(/*2*/''); + +goTo.marker('1'); +verify.signatureHelpCountIs(2); +verify.currentSignatureParamterCountIs(0); +verify.currentSignatureHelpIs('clsOverload(): clsOverload'); + +goTo.marker('2'); +verify.currentSignatureParamterCountIs(1); +verify.currentSignatureHelpIs('clsOverload(test: string): clsOverload'); +verify.currentParameterHelpArgumentNameIs('test'); +verify.currentParameterSpanIs("test: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpEmptyList.ts b/tests/cases/fourslash/signatureHelpEmptyList.ts new file mode 100644 index 00000000000..6ea70159dcc --- /dev/null +++ b/tests/cases/fourslash/signatureHelpEmptyList.ts @@ -0,0 +1,20 @@ +/// + +////function Foo(arg1: string, arg2: string) { +////} +//// +////Foo(/*1*/); +////function Bar(arg1: string, arg2: string) { } +////Bar(); + +goTo.marker('1'); +verify.signatureHelpPresent(); +verify.signatureHelpCountIs(1); + +verify.currentSignatureHelpIs("Foo(arg1: string, arg2: string): void"); +verify.currentSignatureParamterCountIs(2); +verify.currentParameterHelpArgumentNameIs("arg1"); +verify.currentParameterSpanIs("arg1: string"); + +goTo.marker('2'); +verify.signatureHelpPresent(); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpForSuperCalls1.ts b/tests/cases/fourslash/signatureHelpForSuperCalls1.ts new file mode 100644 index 00000000000..58e083ea2ed --- /dev/null +++ b/tests/cases/fourslash/signatureHelpForSuperCalls1.ts @@ -0,0 +1,28 @@ +/// + +////class A { } +////class B extends A { } +////class C extends B { +//// constructor() { +//// super(/*1*/ // sig help here? +//// } +////} +////class A2 { } +////class B2 extends A2 { +//// constructor(x:number) {} +//// } +////class C2 extends B2 { +//// constructor() { +//// super(/*2*/ // sig help here? +//// } +////} + +// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed +edit.insert(''); + +goTo.marker('1'); +verify.signatureHelpPresent(); +verify.currentSignatureHelpIs('B(): B'); + +goTo.marker('2'); +verify.currentSignatureHelpIs('B2(x: number): B2'); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpFunctionOverload.ts b/tests/cases/fourslash/signatureHelpFunctionOverload.ts new file mode 100644 index 00000000000..2c1cdb51291 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpFunctionOverload.ts @@ -0,0 +1,18 @@ +/// + +////function functionOverload(); +////function functionOverload(test: string); +////function functionOverload(test?: string) { } +////functionOverload(/*functionOverload1*/); +////functionOverload(""/*functionOverload2*/); + +goTo.marker('functionOverload1'); +verify.signatureHelpCountIs(2); +verify.currentSignatureParamterCountIs(0); +verify.currentSignatureHelpIs('functionOverload(): any'); + +goTo.marker('functionOverload2'); +verify.currentSignatureParamterCountIs(1); +verify.currentSignatureHelpIs('functionOverload(test: string): any'); +verify.currentParameterHelpArgumentNameIs("test"); +verify.currentParameterSpanIs("test: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpFunctionParameter.ts b/tests/cases/fourslash/signatureHelpFunctionParameter.ts new file mode 100644 index 00000000000..cb2264f2c42 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpFunctionParameter.ts @@ -0,0 +1,17 @@ +/// + +////function parameterFunction(callback: (a: number, b: string) => void) { +//// callback(/*parameterFunction1*/5, /*parameterFunction2*/""); +////} + +goTo.marker('parameterFunction1'); +verify.signatureHelpCountIs(1); +verify.currentSignatureParamterCountIs(2); +verify.currentSignatureHelpIs('callback(a: number, b: string): void'); +verify.currentParameterHelpArgumentNameIs("a"); +verify.currentParameterSpanIs("a: number"); + +goTo.marker('parameterFunction2'); +verify.currentSignatureHelpIs('callback(a: number, b: string): void'); +verify.currentParameterHelpArgumentNameIs("b"); +verify.currentParameterSpanIs("b: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpImplicitConstructor.ts b/tests/cases/fourslash/signatureHelpImplicitConstructor.ts new file mode 100644 index 00000000000..9e42d25dbe9 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpImplicitConstructor.ts @@ -0,0 +1,10 @@ +/// + +////class ImplicitConstructor { +////} +////var implicitConstructor = new ImplicitConstructor(/**/); + +goTo.marker(); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("ImplicitConstructor(): ImplicitConstructor"); +verify.currentSignatureParamterCountIs(0); diff --git a/tests/cases/fourslash/signatureHelpInCallback.ts b/tests/cases/fourslash/signatureHelpInCallback.ts new file mode 100644 index 00000000000..dd4856b48ef --- /dev/null +++ b/tests/cases/fourslash/signatureHelpInCallback.ts @@ -0,0 +1,11 @@ +/// + +////declare function forEach(f: () => void); +////forEach(/*1*/() => { +//// /*2*/ +////}); + +goTo.marker('1'); +verify.signatureHelpPresent(); +goTo.marker('2'); +verify.not.signatureHelpPresent(); diff --git a/tests/cases/fourslash/signatureHelpInCompleteGenericsCall.ts b/tests/cases/fourslash/signatureHelpInCompleteGenericsCall.ts new file mode 100644 index 00000000000..12036cd6db6 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpInCompleteGenericsCall.ts @@ -0,0 +1,8 @@ +/// + +////function foo(x: number, callback: (x: T) => number) { +////} +////foo(/*1*/ + +goTo.marker('1'); +verify.currentSignatureHelpIs("foo(x: number, callback: (x: T) => number): void"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles.ts b/tests/cases/fourslash/signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles.ts new file mode 100644 index 00000000000..2339af02529 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles.ts @@ -0,0 +1,14 @@ +/// + +// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file0.ts +////declare function fn(x: string, y: number); + +// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file1.ts +////declare function fn(x: string); + +// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file2.ts +////fn(/*1*/ + +diagnostics.setEditValidation(IncrementalEditValidation.None); +goTo.marker('1'); +verify.signatureHelpCountIs(2); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpInIncompleteInvocationExpression.ts b/tests/cases/fourslash/signatureHelpInIncompleteInvocationExpression.ts new file mode 100644 index 00000000000..2e097b2a76a --- /dev/null +++ b/tests/cases/fourslash/signatureHelpInIncompleteInvocationExpression.ts @@ -0,0 +1,19 @@ +/// + +/////** +//// * Returns the substring at the specified location within a String object. +//// * @param start The zero-based index integer indicating the beginning of the substring. +//// * @param end Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end. +//// * If end is omitted, the characters from start through the end of the original string are returned. +//// */ +////function foo(start: number, end?: number) { +//// return ""; +////} +//// +////foo(/*1*/ +goTo.marker('1'); +verify.currentParameterHelpArgumentDocCommentIs("The zero-based index integer indicating the beginning of the substring."); +edit.insert("10,"); +verify.currentParameterHelpArgumentDocCommentIs("Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end.\nIf end is omitted, the characters from start through the end of the original string are returned."); +edit.insert(" "); +verify.currentParameterHelpArgumentDocCommentIs("Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end.\nIf end is omitted, the characters from start through the end of the original string are returned."); diff --git a/tests/cases/fourslash/signatureHelpInParenthetical.ts b/tests/cases/fourslash/signatureHelpInParenthetical.ts new file mode 100644 index 00000000000..0628d904fdc --- /dev/null +++ b/tests/cases/fourslash/signatureHelpInParenthetical.ts @@ -0,0 +1,9 @@ +/// + +//// class base { constructor (public n: number, public y: string) { } } +//// (new base(/**/ + +goTo.marker(); +verify.currentParameterHelpArgumentNameIs('n'); +edit.insert('0, '); +verify.currentParameterHelpArgumentNameIs('y'); diff --git a/tests/cases/fourslash/signatureHelpIncompleteCalls.ts b/tests/cases/fourslash/signatureHelpIncompleteCalls.ts new file mode 100644 index 00000000000..e73217b1d8c --- /dev/null +++ b/tests/cases/fourslash/signatureHelpIncompleteCalls.ts @@ -0,0 +1,31 @@ +/// + +////module IncompleteCalls { +//// class Foo { +//// public f1() { } +//// public f2(n: number): number { return 0; } +//// public f3(n: number, s: string) : string { return ""; } +//// } +//// var x = new Foo(); +//// x.f1(); +//// x.f2(5); +//// x.f3(5, ""); +//// x.f1(/*incompleteCalls1*/ +//// x.f2(5,/*incompleteCalls2*/ +//// x.f3(5,/*incompleteCalls3*/ +////} + +goTo.marker('incompleteCalls1'); +verify.currentSignatureHelpIs("f1(): void"); +verify.currentSignatureParamterCountIs(0); + +goTo.marker('incompleteCalls2'); +verify.currentSignatureParamterCountIs(1); +verify.currentSignatureHelpIs("f2(n: number): number"); +goTo.marker('incompleteCalls3'); +verify.currentSignatureParamterCountIs(2); +verify.currentSignatureHelpIs("f3(n: number, s: string): string"); + +verify.currentParameterHelpArgumentNameIs("s"); +verify.currentParameterSpanIs("s: string"); + diff --git a/tests/cases/fourslash/signatureHelpNegativeTests2.ts b/tests/cases/fourslash/signatureHelpNegativeTests2.ts index 0302736ee97..28f25063ddf 100644 --- a/tests/cases/fourslash/signatureHelpNegativeTests2.ts +++ b/tests/cases/fourslash/signatureHelpNegativeTests2.ts @@ -1,10 +1,10 @@ /// ////class clsOverload { constructor(); constructor(test: string); constructor(test?: string) { } } -////var x = new clsOverload/*beforeOpenParen*/()/*afterOpenParen*/; +////var x = new clsOverload/*beforeOpenParen*/()/*afterCloseParen*/; goTo.marker('beforeOpenParen'); verify.not.signatureHelpPresent(); -goTo.marker('afterOpenParen'); +goTo.marker('afterCloseParen'); verify.not.signatureHelpPresent(); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpNoArguments.ts b/tests/cases/fourslash/signatureHelpNoArguments.ts new file mode 100644 index 00000000000..16a1896a741 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpNoArguments.ts @@ -0,0 +1,12 @@ +/// + + +////function foo(n: number): string { +////} +//// +////foo(/**/ + +goTo.marker(); +verify.currentSignatureHelpIs("foo(n: number): string"); +verify.currentParameterHelpArgumentNameIs("n"); +verify.currentParameterSpanIs("n: number"); diff --git a/tests/cases/fourslash/signatureHelpObjectLiteral.ts b/tests/cases/fourslash/signatureHelpObjectLiteral.ts new file mode 100644 index 00000000000..cbc5df697b9 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpObjectLiteral.ts @@ -0,0 +1,17 @@ +/// + +////var objectLiteral = { n: 5, s: "", f: (a: number, b: string) => "" }; +////objectLiteral.f(/*objectLiteral1*/4, /*objectLiteral2*/""); + +goTo.marker('objectLiteral1'); +verify.signatureHelpCountIs(1); +verify.currentSignatureParamterCountIs(2); +verify.currentSignatureHelpIs('f(a: number, b: string): string'); + +verify.currentParameterHelpArgumentNameIs("a"); +verify.currentParameterSpanIs("a: number"); + +goTo.marker('objectLiteral2'); +verify.currentSignatureHelpIs('f(a: number, b: string): string'); +verify.currentParameterHelpArgumentNameIs("b"); +verify.currentParameterSpanIs("b: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpOnNestedOverloads.ts b/tests/cases/fourslash/signatureHelpOnNestedOverloads.ts new file mode 100644 index 00000000000..476d79a8243 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOnNestedOverloads.ts @@ -0,0 +1,20 @@ +/// + +////declare function fn(x: string); +////declare function fn(x: string, y: number); +////declare function fn2(x: string); +////declare function fn2(x: string, y: number); +////fn('', fn2(/*1*/ + +goTo.marker('1'); +verify.signatureHelpCountIs(2); +verify.currentSignatureHelpIs("fn2(x: string): any"); +verify.currentParameterHelpArgumentNameIs("x"); +verify.currentParameterSpanIs("x: string"); + +edit.insert("'',"); + +verify.signatureHelpCountIs(2); +// verify.currentSignatureHelpIs("fn2(x: string, y: number): any"); +// verify.currentParameterHelpArgumentNameIs("y"); +// verify.currentParameterSpanIs("y: number"); diff --git a/tests/cases/fourslash/signatureHelpOnOverloadOnConst.ts b/tests/cases/fourslash/signatureHelpOnOverloadOnConst.ts new file mode 100644 index 00000000000..8edd0617d70 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOnOverloadOnConst.ts @@ -0,0 +1,26 @@ +/// + +////function x1(x: 'hi'); +////function x1(y: 'bye'); +////function x1(z: string); +////function x1(a: any) { +////} +//// +////x1(''/*1*/); +////x1('hi'/*2*/); +////x1('bye'/*3*/); + +goTo.marker('1'); +verify.signatureHelpCountIs(3); +verify.currentParameterHelpArgumentNameIs("z"); +verify.currentParameterSpanIs("z: string"); + +goTo.marker('2'); +verify.signatureHelpCountIs(3); +verify.currentParameterHelpArgumentNameIs("x"); +verify.currentParameterSpanIs("x: 'hi'"); + +goTo.marker('3'); +verify.signatureHelpCountIs(3); +verify.currentParameterHelpArgumentNameIs("y"); +verify.currentParameterSpanIs("y: 'bye'"); diff --git a/tests/cases/fourslash/signatureHelpOnOverloads.ts b/tests/cases/fourslash/signatureHelpOnOverloads.ts new file mode 100644 index 00000000000..83d7b75a4cf --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOnOverloads.ts @@ -0,0 +1,18 @@ +/// + +////declare function fn(x: string); +////declare function fn(x: string, y: number); +////fn(/*1*/ + +goTo.marker('1'); +verify.signatureHelpCountIs(2); +verify.currentSignatureHelpIs("fn(x: string): any"); +verify.currentParameterHelpArgumentNameIs("x"); +verify.currentParameterSpanIs("x: string"); + +edit.insert("'',"); + +verify.signatureHelpCountIs(2); +// verify.currentSignatureHelpIs("fn(x: string, y: number): any"); +// verify.currentParameterHelpArgumentNameIs("y"); +// verify.currentParameterSpanIs("y: number"); diff --git a/tests/cases/fourslash/signatureHelpOnSuperWhenMembersAreNotResolved.ts b/tests/cases/fourslash/signatureHelpOnSuperWhenMembersAreNotResolved.ts new file mode 100644 index 00000000000..5613ca71a70 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOnSuperWhenMembersAreNotResolved.ts @@ -0,0 +1,14 @@ +/// + +////class A { } +////class B extends A { constructor(public x: string) { } } +////class C extends B { +//// constructor() { +//// /*1*/ +//// } +////} + +diagnostics.setEditValidation(IncrementalEditValidation.None); +goTo.marker("1"); +edit.insert("super("); +verify.currentSignatureHelpIs("B(x: string): B"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpSimpleConstructorCall.ts b/tests/cases/fourslash/signatureHelpSimpleConstructorCall.ts new file mode 100644 index 00000000000..81af0422f9a --- /dev/null +++ b/tests/cases/fourslash/signatureHelpSimpleConstructorCall.ts @@ -0,0 +1,17 @@ +/// + +////class ConstructorCall { +//// constructor(str: string, num: number) { +//// } +////} +////var x = new ConstructorCall(/*constructorCall1*/1,/*constructorCall2*/2); + +goTo.marker('constructorCall1'); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("ConstructorCall(str: string, num: number): ConstructorCall"); +verify.currentParameterHelpArgumentNameIs("str"); +verify.currentParameterSpanIs("str: string"); +goTo.marker('constructorCall2'); +verify.currentSignatureHelpIs("ConstructorCall(str: string, num: number): ConstructorCall"); +verify.currentParameterHelpArgumentNameIs("num"); +verify.currentParameterSpanIs("num: number"); diff --git a/tests/cases/fourslash/signatureHelpSimpleFunctionCall.ts b/tests/cases/fourslash/signatureHelpSimpleFunctionCall.ts new file mode 100644 index 00000000000..6e5817ad90d --- /dev/null +++ b/tests/cases/fourslash/signatureHelpSimpleFunctionCall.ts @@ -0,0 +1,19 @@ +/// + +////// Simple function test +////function functionCall(str: string, num: number) { +////} +////functionCall(/*functionCall1*/); +////functionCall("", /*functionCall2*/1); + + +goTo.marker('functionCall1'); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("functionCall(str: string, num: number): void"); +verify.currentParameterHelpArgumentNameIs("str"); +verify.currentParameterSpanIs("str: string"); +goTo.marker('functionCall2'); +verify.currentSignatureHelpIs("functionCall(str: string, num: number): void"); +verify.currentParameterHelpArgumentNameIs("num"); +verify.currentParameterSpanIs("num: number"); + diff --git a/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts b/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts new file mode 100644 index 00000000000..ff8913a6b04 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpSimpleSuperCall.ts @@ -0,0 +1,20 @@ +/// + +////class SuperCallBase { +//// constructor(b: boolean) { +//// } +////} +////class SuperCall extends SuperCallBase { +//// constructor() { +//// super(/*superCall*/); +//// } +////} + +// this line triggers a semantic/syntactic error check, remove line when 788570 is fixed +edit.insert(''); + +goTo.marker('superCall'); +verify.signatureHelpCountIs(1); +verify.currentSignatureHelpIs("SuperCallBase(b: boolean): SuperCallBase"); +verify.currentParameterHelpArgumentNameIs("b"); +verify.currentParameterSpanIs("b: boolean"); diff --git a/tests/cases/fourslash/signatureHelpSuperConstructorOverload.ts b/tests/cases/fourslash/signatureHelpSuperConstructorOverload.ts new file mode 100644 index 00000000000..1be8c3202a1 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpSuperConstructorOverload.ts @@ -0,0 +1,28 @@ +/// + +////class SuperOverloadlBase { +//// constructor(); +//// constructor(test: string); +//// constructor(test?: string) { +//// } +////} +////class SuperOverLoad1 extends SuperOverloadlBase { +//// constructor() { +//// super(/*superOverload1*/); +//// } +////} +////class SuperOverLoad2 extends SuperOverloadlBase { +//// constructor() { +//// super(""/*superOverload2*/); +//// } +////} + +goTo.marker('superOverload1'); +verify.signatureHelpCountIs(2); +verify.currentSignatureHelpIs("SuperOverloadlBase(): SuperOverloadlBase"); +verify.currentSignatureParamterCountIs(0); +goTo.marker('superOverload2'); +verify.currentSignatureParamterCountIs(1); +verify.currentSignatureHelpIs("SuperOverloadlBase(test: string): SuperOverloadlBase"); +verify.currentParameterHelpArgumentNameIs("test"); +verify.currentParameterSpanIs("test: string"); \ No newline at end of file diff --git a/tests/cases/fourslash/signatureHelpWhenEditingCallExpression.ts b/tests/cases/fourslash/signatureHelpWhenEditingCallExpression.ts new file mode 100644 index 00000000000..61e51d0a4e7 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpWhenEditingCallExpression.ts @@ -0,0 +1,30 @@ +/// + +/////** +//// * Returns the substring at the specified location within a String object. +//// * @param start The zero-based index integer indicating the beginning of the substring. +//// * @param end Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end. +//// * If end is omitted, the characters from start through the end of the original string are returned. +//// */ +////function foo(start: number, end?: number) { +//// return ""; +////} +//// +////fo/*1*/ +goTo.marker('1'); +verify.not.signatureHelpPresent(); +edit.insert("o"); +verify.not.signatureHelpPresent(); +edit.insert("("); +verify.currentParameterHelpArgumentDocCommentIs("The zero-based index integer indicating the beginning of the substring."); +edit.insert("10,"); +verify.currentParameterHelpArgumentDocCommentIs("Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end.\nIf end is omitted, the characters from start through the end of the original string are returned."); +edit.insert(" "); +verify.currentParameterHelpArgumentDocCommentIs("Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end.\nIf end is omitted, the characters from start through the end of the original string are returned."); +edit.insert(", "); +edit.backspace(3); +verify.currentParameterHelpArgumentDocCommentIs("Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end.\nIf end is omitted, the characters from start through the end of the original string are returned."); +edit.insert("12"); +verify.currentParameterHelpArgumentDocCommentIs("Zero-based index integer indicating the end of the substring. The substring includes the characters up to, but not including, the character indicated by end.\nIf end is omitted, the characters from start through the end of the original string are returned."); +edit.insert(")"); +verify.not.signatureHelpPresent(); diff --git a/tests/cases/fourslash/staticGenericOverloads1.ts b/tests/cases/fourslash/staticGenericOverloads1.ts new file mode 100644 index 00000000000..56f358dd951 --- /dev/null +++ b/tests/cases/fourslash/staticGenericOverloads1.ts @@ -0,0 +1,22 @@ +/// + +////class A { +//// static B(v: A): A; +//// static B(v: S): A; +//// static B(v: any): A { +//// return null; +//// } +////} + +////var a = new A(); +////A.B(/**/ + +goTo.marker(); +verify.signatureHelpCountIs(2); +edit.insert('a'); +verify.signatureHelpCountIs(2); +// verify.currentSignatureHelpIs('B(v: A): A') +edit.insert('); A.B('); +verify.currentSignatureHelpIs('B(v: A): A'); +edit.insert('a'); +// verify.currentSignatureHelpIs('B(v: A): A')