diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 63731417f48..694dcf6394e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -206,6 +206,24 @@ namespace FourSlash { private inputFiles = ts.createMap(); // Map between inputFile's fileName and its content for easily looking up when resolving references + private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) { + let result = ""; + ts.forEach(displayParts, part => { + if (result) { + result += ",\n "; + } + else { + result = "[\n "; + } + result += JSON.stringify(part); + }); + if (result) { + result += "\n]"; + } + + return result; + } + // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified private addMatchedInputFile(referenceFilePath: string, extensions: string[]) { @@ -777,6 +795,20 @@ namespace FourSlash { ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); } + public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) { + const referencedSymbols = this.findReferencesAtCaret(); + + if (referencedSymbols.length === 0) { + this.raiseError("No referenced symbols found at current caret position"); + } + else if (referencedSymbols.length > 1) { + this.raiseError("More than one referenced symbol found"); + } + + assert.equal(TestState.getDisplayPartsJson(referencedSymbols[0].definition.displayParts), + TestState.getDisplayPartsJson(expected), this.messageAtLastKnownMarker("referenced symbol definition display parts")); + } + private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { for (let i = 0; i < references.length; i++) { const reference = references[i]; @@ -811,6 +843,10 @@ namespace FourSlash { return this.languageService.getReferencesAtPosition(this.activeFile.fileName, this.currentCaretPosition); } + private findReferencesAtCaret() { + return this.languageService.findReferences(this.activeFile.fileName, this.currentCaretPosition); + } + public getSyntacticDiagnostics(expected: string) { const diagnostics = this.languageService.getSyntacticDiagnostics(this.activeFile.fileName); this.testDiagnostics(expected, diagnostics); @@ -856,30 +892,12 @@ namespace FourSlash { displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[]) { - function getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[]) { - let result = ""; - ts.forEach(displayParts, part => { - if (result) { - result += ",\n "; - } - else { - result = "[\n "; - } - result += JSON.stringify(part); - }); - if (result) { - result += "\n]"; - } - - return result; - } - const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind")); assert.equal(actualQuickInfo.kindModifiers, kindModifiers, this.messageAtLastKnownMarker("QuickInfo kindModifiers")); assert.equal(JSON.stringify(actualQuickInfo.textSpan), JSON.stringify(textSpan), this.messageAtLastKnownMarker("QuickInfo textSpan")); - assert.equal(getDisplayPartsJson(actualQuickInfo.displayParts), getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts")); - assert.equal(getDisplayPartsJson(actualQuickInfo.documentation), getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); + assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.displayParts), TestState.getDisplayPartsJson(displayParts), this.messageAtLastKnownMarker("QuickInfo displayParts")); + assert.equal(TestState.getDisplayPartsJson(actualQuickInfo.documentation), TestState.getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); } public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) { @@ -2947,6 +2965,10 @@ namespace FourSlashInterface { this.state.verifyRangesReferenceEachOther(ranges); } + public findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]) { + this.state.verifyDisplayPartsOfReferencedSymbol(expected); + } + public rangesWithSameTextReferenceEachOther() { this.state.verifyRangesWithSameTextReferenceEachOther(); } diff --git a/src/services/services.ts b/src/services/services.ts index 6da12696937..8794446e83e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6328,7 +6328,8 @@ namespace ts { fileName: targetLabel.getSourceFile().fileName, kind: ScriptElementKind.label, name: labelName, - textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()) + textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()), + displayParts: [displayPart(labelName, SymbolDisplayPartKind.text)] }; return [{ definition, references }]; @@ -6568,6 +6569,11 @@ namespace ts { getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references); } + const thisOrSuperSymbol = typeChecker.getSymbolAtLocation(thisOrSuperKeyword); + + const { displayParts } = getSymbolDisplayPartsDocumentationAndSymbolKind( + thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword); + return [{ definition: { containerKind: "", @@ -6575,7 +6581,8 @@ namespace ts { fileName: node.getSourceFile().fileName, kind: ScriptElementKind.variableElement, name: "this", - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + displayParts }, references: references }]; diff --git a/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts b/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts new file mode 100644 index 00000000000..d424bd0c500 --- /dev/null +++ b/tests/cases/fourslash/findReferencesDefinitionDisplayParts.ts @@ -0,0 +1,23 @@ +/// + +//// class Gre/*1*/eter { +//// someFunction() { th/*2*/is; } +//// } +//// +//// type Options = "opt/*3*/ion 1" | "option 2"; +//// let myOption: Options = "option 1"; +//// +//// some/*4*/Label: +//// break someLabel; + +goTo.marker("1"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "class", kind: "keyword" }, { text: " ", kind: "space" }, { text: "Greeter", kind: "className" }]); + +goTo.marker("2"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "this", kind: "keyword" }, { text: ":", kind: "punctuation" }, { text: " ", kind: "space" }, { text: "this", kind: "keyword" }]); + +goTo.marker("3"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "\"option 1\"", kind: "stringLiteral" }]); + +goTo.marker("4"); +verify.findReferencesDefinitionDisplayPartsAtCaretAre([{ text: "someLabel", kind: "text" }]); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9ce3197a46f..59037e0de67 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -171,6 +171,7 @@ declare namespace FourSlashInterface { * If `ranges` is omitted, this is `test.ranges()`. */ rangesReferenceEachOther(ranges?: Range[]): void; + findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void; rangesWithSameTextReferenceEachOther(): void; currentParameterHelpArgumentNameIs(name: string): void; currentParameterSpanIs(parameter: string): void;