diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 3659d8844a9..4f9e6a3f0e9 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1515,6 +1515,14 @@ module FourSlash { } } + public verifyDefinitionsCount(negative: boolean, expectedCount: number) { + var assertFn = negative ? assert.notEqual : assert.equal; + + var definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition); + + assertFn(definitions.length, expectedCount, this.messageAtLastKnownMarker("Definitions Count")); + } + public verifyDefinitionsName(negative: boolean, expectedName: string, expectedContainerName: string) { this.taoInvalidReason = 'verifyDefinititionsInfo NYI'; @@ -1523,10 +1531,10 @@ module FourSlash { var actualDefinitionContainerName = definitions && definitions.length ? definitions[0].containerName : ""; if (negative) { assert.notEqual(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Name")); - assert.notEqual(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Container Name")); + assert.notEqual(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name")); } else { assert.equal(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Name")); - assert.equal(actualDefinitionName, expectedName, this.messageAtLastKnownMarker("Definition Info Container Name")); + assert.equal(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name")); } } diff --git a/src/services/services.ts b/src/services/services.ts index f8070fa0531..249270f187c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2598,7 +2598,7 @@ module ts { } // TODO(drosen): use contextual SemanticMeaning. - function getSymbolKind(symbol: Symbol, typeResolver: TypeChecker, location?: Node): string { + function getSymbolKind(symbol: Symbol, typeResolver: TypeChecker, location: Node): string { var flags = symbol.getFlags(); if (flags & SymbolFlags.Class) return ScriptElementKind.classElement; @@ -3097,6 +3097,83 @@ module ts { /// Goto definition function getDefinitionAtPosition(filename: string, position: number): DefinitionInfo[] { + synchronizeHostData(); + + filename = normalizeSlashes(filename); + var sourceFile = getSourceFile(filename); + + var node = getTouchingPropertyName(sourceFile, position); + if (!node) { + return undefined; + } + + // Labels + if (isJumpStatementTarget(node)) { + var labelName = (node).text; + var label = getTargetLabel((node.parent), (node).text); + return label ? [getDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; + } + + /// Triple slash reference comments + var comment = forEach(sourceFile.referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined); + if (comment) { + var referenceFile = tryResolveScriptReference(program, sourceFile, comment); + if (referenceFile) { + return [{ + fileName: referenceFile.filename, + textSpan: createTextSpanFromBounds(0, 0), + kind: ScriptElementKind.scriptElement, + name: comment.filename, + containerName: undefined, + containerKind: undefined + }]; + } + return undefined; + } + + var symbol = typeInfoResolver.getSymbolAtLocation(node); + + // Could not find a symbol e.g. node is string or number keyword, + // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol + if (!symbol) { + return undefined; + } + + var result: DefinitionInfo[] = []; + + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { + var shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + var shorthandDeclarations = shorthandSymbol.getDeclarations(); + var shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver, node); + var shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); + var shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); + forEach(shorthandDeclarations, declaration => { + result.push(getDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); + }); + return result + } + + var declarations = symbol.getDeclarations(); + var symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol + var symbolKind = getSymbolKind(symbol, typeInfoResolver, node); + var containerSymbol = symbol.parent; + var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; + + if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && + !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { + // Just add all the declarations. + forEach(declarations, declaration => { + result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); + }); + } + + return result; + function getDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo { return { fileName: node.getSourceFile().filename, @@ -3152,83 +3229,6 @@ module ts { } return false; } - - synchronizeHostData(); - - filename = normalizeSlashes(filename); - var sourceFile = getSourceFile(filename); - - var node = getTouchingPropertyName(sourceFile, position); - if (!node) { - return undefined; - } - - // Labels - if (isJumpStatementTarget(node)) { - var labelName = (node).text; - var label = getTargetLabel((node.parent), (node).text); - return label ? [getDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; - } - - /// Triple slash reference comments - var comment = forEach(sourceFile.referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined); - if (comment) { - var referenceFile = tryResolveScriptReference(program, sourceFile, comment); - if (referenceFile) { - return [{ - fileName: referenceFile.filename, - textSpan: createTextSpanFromBounds(0, 0), - kind: ScriptElementKind.scriptElement, - name: comment.filename, - containerName: undefined, - containerKind: undefined - }]; - } - return undefined; - } - - var symbol = typeInfoResolver.getSymbolAtLocation(node); - - // Could not find a symbol e.g. node is string or number keyword, - // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol - if (!symbol) { - return undefined; - } - - var result: DefinitionInfo[] = []; - - // Because name in short-hand property assignment has two different meanings: property name and property value, - // using go-to-definition at such position should go to the variable declaration of the property value rather than - // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition - // is performed at the location of property access, we would like to go to definition of the property in the short-hand - // assignment. This case and others are handled by the following code. - if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { - var shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - var shorthandDeclarations = shorthandSymbol.getDeclarations(); - var shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver); - var shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); - var shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); - forEach(shorthandDeclarations, declaration => { - result.push(getDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); - }); - return result - } - - var declarations = symbol.getDeclarations(); - var symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - var symbolKind = getSymbolKind(symbol, typeInfoResolver); - var containerSymbol = symbol.parent; - var containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; - - if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && - !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { - // Just add all the declarations. - forEach(declarations, declaration => { - result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); - }); - } - - return result; } /// References and Occurrences @@ -5273,7 +5273,7 @@ module ts { // Only allow a symbol to be renamed if it actually has at least one declaration. if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { - var kind = getSymbolKind(symbol, typeInfoResolver); + var kind = getSymbolKind(symbol, typeInfoResolver, node); if (kind) { return getRenameInfo(symbol.name, typeInfoResolver.getFullyQualifiedName(symbol), kind, getSymbolModifiers(symbol), diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9ad1734178d..0f458041521 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -217,6 +217,10 @@ module FourSlashInterface { FourSlash.currentTestState.verifyQuickInfoExists(this.negative); } + public definitionCountIs(expectedCount: number) { + FourSlash.currentTestState.verifyDefinitionsCount(this.negative, expectedCount); + } + public definitionLocationExists() { FourSlash.currentTestState.verifyDefinitionLocationExists(this.negative); } diff --git a/tests/cases/fourslash/goToDefinitionExternamModuleName4.ts b/tests/cases/fourslash/goToDefinitionExternamModuleName4.ts index 1a0fa46c072..c8bbaa458e4 100644 --- a/tests/cases/fourslash/goToDefinitionExternamModuleName4.ts +++ b/tests/cases/fourslash/goToDefinitionExternamModuleName4.ts @@ -1,4 +1,4 @@ -/// +/// // @Filename: b.ts ////import n = require('unknown/*1*/'); diff --git a/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts b/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts index 6dd4cc91f1f..488d788073a 100644 --- a/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts +++ b/tests/cases/fourslash/goToDefinitionFunctionOverloads.ts @@ -1,20 +1,20 @@ -/// - -/////*functionOverload1*/function /*functionOverload*/functionOverload(); -/////*functionOverload2*/function functionOverload(value: string); -/////*functionOverloadDefinition*/function functionOverload() {} -//// +/// + +/////*functionOverload1*/function /*functionOverload*/functionOverload(); +/////*functionOverload2*/function functionOverload(value: string); +/////*functionOverloadDefinition*/function functionOverload() {} +//// /////*functionOverloadReference1*/functionOverload(); /////*functionOverloadReference2*/functionOverload("123"); -goTo.marker('functionOverloadReference1'); -goTo.definition(); -verify.caretAtMarker('functionOverloadDefinition'); - -goTo.marker('functionOverloadReference2'); -goTo.definition(); -verify.caretAtMarker('functionOverloadDefinition'); - -goTo.marker('functionOverload'); -goTo.definition(); -verify.caretAtMarker('functionOverloadDefinition'); +goTo.marker('functionOverloadReference1'); +goTo.definition(); +verify.caretAtMarker('functionOverloadDefinition'); + +goTo.marker('functionOverloadReference2'); +goTo.definition(); +verify.caretAtMarker('functionOverloadDefinition'); + +goTo.marker('functionOverload'); +goTo.definition(); +verify.caretAtMarker('functionOverloadDefinition'); diff --git a/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts b/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts index 1db506f9686..f368120922d 100644 --- a/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts +++ b/tests/cases/fourslash/goToDefinitionFunctionOverloadsInClass.ts @@ -1,23 +1,23 @@ -/// - -////class clsInOverload { -//// static fnOverload(); -//// static /*staticFunctionOverload*/fnOverload(foo: string); -//// /*staticFunctionOverloadDefinition*/static fnOverload(foo: any) { } -//// public /*functionOverload*/fnOverload(): any; -//// public fnOverload(foo: string); -//// /*functionOverloadDefinition*/public fnOverload(foo: any) { return "foo" } -//// -//// constructor() { } -////} - +/// + +////class clsInOverload { +//// static fnOverload(); +//// static /*staticFunctionOverload*/fnOverload(foo: string); +//// /*staticFunctionOverloadDefinition*/static fnOverload(foo: any) { } +//// public /*functionOverload*/fnOverload(): any; +//// public fnOverload(foo: string); +//// /*functionOverloadDefinition*/public fnOverload(foo: any) { return "foo" } +//// +//// constructor() { } +////} + // this line triggers a semantic/syntactic error check, remove line when 788570 is fixed -edit.insert(''); - -goTo.marker('staticFunctionOverload'); -goTo.definition(); -verify.caretAtMarker('staticFunctionOverloadDefinition'); - -goTo.marker('functionOverload'); -goTo.definition(); +edit.insert(''); + +goTo.marker('staticFunctionOverload'); +goTo.definition(); +verify.caretAtMarker('staticFunctionOverloadDefinition'); + +goTo.marker('functionOverload'); +goTo.definition(); verify.caretAtMarker('functionOverloadDefinition'); \ No newline at end of file diff --git a/tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts b/tests/cases/fourslash/goToDefinitionUnionTypeProperty1.ts similarity index 91% rename from tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts rename to tests/cases/fourslash/goToDefinitionUnionTypeProperty1.ts index 74b5e2aad8a..65d7201aecf 100644 --- a/tests/cases/fourslash/goToDefinitionUnionTypeProperty.ts +++ b/tests/cases/fourslash/goToDefinitionUnionTypeProperty1.ts @@ -1,5 +1,5 @@ -/// - +/// + ////interface One { //// /*propertyDefinition1*/commonProperty: number; //// commonFunction(): number; @@ -13,12 +13,14 @@ ////var x : One | Two; //// ////x./*propertyReference*/commonProperty; -////x./*3*/commonFunction; - -goTo.marker("propertyReference"); -goTo.definition(0); -verify.caretAtMarker("propertyDefinition1"); - -goTo.marker("propertyReference"); -goTo.definition(1); -verify.caretAtMarker("propertyDefinition2"); +////x./*3*/commonFunction; + + +goTo.marker("propertyReference"); +verify.definitionCountIs(2); +goTo.definition(0); +verify.caretAtMarker("propertyDefinition1"); + +goTo.marker("propertyReference"); +goTo.definition(1); +verify.caretAtMarker("propertyDefinition2"); diff --git a/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts b/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts index 960a6b7a86e..674d5f4b623 100644 --- a/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts +++ b/tests/cases/fourslash/goToDefinitionUnionTypeProperty2.ts @@ -1,9 +1,9 @@ -/// -////interface HasAOrB { -//// /*propertyDefinition1*/a: string; -//// b: string; -////} -//// +/// +////interface HasAOrB { +//// /*propertyDefinition1*/a: string; +//// b: string; +////} +//// ////interface One { //// common: { /*propertyDefinition2*/a : number; }; ////} @@ -15,11 +15,12 @@ ////var x : One | Two; //// ////x.common./*propertyReference*/a; - -goTo.marker("propertyReference"); -goTo.definition(0); -verify.caretAtMarker("propertyDefinition2"); - -goTo.marker("propertyReference"); -goTo.definition(1); -verify.caretAtMarker("propertyDefinition1"); + +goTo.marker("propertyReference"); +verify.definitionCountIs(2); +goTo.definition(0); +verify.caretAtMarker("propertyDefinition2"); + +goTo.marker("propertyReference"); +goTo.definition(1); +verify.caretAtMarker("propertyDefinition1"); diff --git a/tests/cases/fourslash/goToDefinitionUnionTypeProperty3.ts b/tests/cases/fourslash/goToDefinitionUnionTypeProperty3.ts new file mode 100644 index 00000000000..337bae7d520 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionUnionTypeProperty3.ts @@ -0,0 +1,15 @@ +/// + +////interface Array { +//// /*definition*/specialPop(): T +////} +//// +////var strings: string[]; +////var numbers: number[]; +//// +////var x = (strings || numbers)./*usage*/specialPop() + +goTo.marker("usage"); +verify.definitionCountIs(1); +goTo.definition(); +verify.caretAtMarker("definition"); diff --git a/tests/cases/fourslash/goToDefinitionUnionTypeProperty4.ts b/tests/cases/fourslash/goToDefinitionUnionTypeProperty4.ts new file mode 100644 index 00000000000..eb17f9570c2 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionUnionTypeProperty4.ts @@ -0,0 +1,32 @@ +/// + +////interface SnapCrackle { +//// /*def1*/pop(): string; +////} +//// +////interface Magnitude { +//// /*def2*/pop(): number; +////} +//// +////interface Art { +//// /*def3*/pop(): boolean; +////} +//// +////var art: Art; +////var magnitude: Magnitude; +////var snapcrackle: SnapCrackle; +//// +////var x = (snapcrackle || magnitude || art)./*usage*/pop; + +goTo.marker("usage"); +verify.definitionCountIs(3); +goTo.definition(0); +verify.caretAtMarker("def1"); + +goTo.marker("usage"); +goTo.definition(1); +verify.caretAtMarker("def2"); + +goTo.marker("usage"); +goTo.definition(2); +verify.caretAtMarker("def3"); \ No newline at end of file