mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-22 22:55:36 -05:00
Find references of a module by filename (#41805)
* Naive implementation enough to build and write a test * Add simple test * Add project references test * Add deduplication test, accept baselines * Add test for referencing a script (doesn’t do anything) * Update API baselines * Use refFileMap for non-module references * Fix find-all-refs on module specifier * Remove unused util * Don’t store text range on ts.RefFile * Ensure string literal could itself be a file reference * Remove unused utilities * Improve baseline format * Preserve old behavior of falling back to string literal references * Update baselines from master * Fix old RefFileMap code after merge * Add test for additional response info * Undo test change
This commit is contained in:
@@ -362,6 +362,18 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
getFileReferences(fileName: string): ReferenceEntry[] {
|
||||
const request = this.processRequest<protocol.FileReferencesRequest>(CommandNames.FileReferences, { file: fileName });
|
||||
const response = this.processResponse<protocol.FileReferencesResponse>(request);
|
||||
|
||||
return response.body!.refs.map(entry => ({ // TODO: GH#18217
|
||||
fileName: entry.file,
|
||||
textSpan: this.decodeSpan(entry),
|
||||
isWriteAccess: entry.isWriteAccess,
|
||||
isDefinition: entry.isDefinition,
|
||||
}));
|
||||
}
|
||||
|
||||
getEmitOutput(file: string): EmitOutput {
|
||||
const request = this.processRequest<protocol.EmitOutputRequest>(protocol.CommandTypes.EmitOutput, { file });
|
||||
const response = this.processResponse<protocol.EmitOutputResponse>(request);
|
||||
|
||||
@@ -1114,38 +1114,77 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyBaselineFindAllReferences(markerName: string) {
|
||||
const marker = this.getMarkerByName(markerName);
|
||||
const references = this.languageService.findReferences(marker.fileName, marker.position);
|
||||
public verifyBaselineFindAllReferences(...markerNames: string[]) {
|
||||
const baseline = markerNames.map(markerName => {
|
||||
const marker = this.getMarkerByName(markerName);
|
||||
const references = this.languageService.findReferences(marker.fileName, marker.position);
|
||||
const refsByFile = references
|
||||
? ts.group(ts.sort(ts.flatMap(references, r => r.references), (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName)
|
||||
: ts.emptyArray;
|
||||
|
||||
// Write input files
|
||||
const baselineContent = this.getBaselineContentForGroupedReferences(refsByFile, markerName);
|
||||
|
||||
// Write response JSON
|
||||
return baselineContent + JSON.stringify(references, undefined, 2);
|
||||
}).join("\n\n");
|
||||
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baseline);
|
||||
}
|
||||
|
||||
public verifyBaselineGetFileReferences(fileName: string) {
|
||||
const references = this.languageService.getFileReferences(fileName);
|
||||
const refsByFile = references
|
||||
? ts.group(ts.sort(ts.flatMap(references, r => r.references), (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName)
|
||||
? ts.group(ts.sort(references, (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName)
|
||||
: ts.emptyArray;
|
||||
|
||||
// Write input files
|
||||
let baselineContent = this.getBaselineContentForGroupedReferences(refsByFile);
|
||||
|
||||
// Write response JSON
|
||||
baselineContent += JSON.stringify(references, undefined, 2);
|
||||
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent);
|
||||
}
|
||||
|
||||
private getBaselineContentForGroupedReferences(refsByFile: readonly (readonly ts.ReferenceEntry[])[], markerName?: string) {
|
||||
const marker = markerName !== undefined ? this.getMarkerByName(markerName) : undefined;
|
||||
let baselineContent = "";
|
||||
for (const group of refsByFile) {
|
||||
baselineContent += getBaselineContentForFile(group[0].fileName, this.getFileContent(group[0].fileName));
|
||||
baselineContent += "\n\n";
|
||||
}
|
||||
|
||||
// Write response JSON
|
||||
baselineContent += JSON.stringify(references, undefined, 2);
|
||||
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent);
|
||||
return baselineContent;
|
||||
|
||||
function getBaselineContentForFile(fileName: string, content: string) {
|
||||
let newContent = `=== ${fileName} ===\n`;
|
||||
let pos = 0;
|
||||
for (const { textSpan } of refsByFile.find(refs => refs[0].fileName === fileName) ?? ts.emptyArray) {
|
||||
if (fileName === marker.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) {
|
||||
newContent += "/*FIND ALL REFS*/";
|
||||
}
|
||||
const end = textSpan.start + textSpan.length;
|
||||
newContent += content.slice(pos, textSpan.start);
|
||||
newContent += "[|";
|
||||
newContent += content.slice(textSpan.start, end);
|
||||
pos = textSpan.start;
|
||||
// It's easier to read if the /*FIND ALL REFS*/ comment is outside the range markers, which makes
|
||||
// this code a bit more verbose than it would be if I were less picky about the baseline format.
|
||||
if (fileName === marker?.fileName && marker.position === textSpan.start) {
|
||||
newContent += "/*FIND ALL REFS*/";
|
||||
newContent += "[|";
|
||||
}
|
||||
else if (fileName === marker?.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) {
|
||||
newContent += "[|";
|
||||
newContent += content.slice(pos, marker.position);
|
||||
newContent += "/*FIND ALL REFS*/";
|
||||
pos = marker.position;
|
||||
}
|
||||
else {
|
||||
newContent += "[|";
|
||||
}
|
||||
newContent += content.slice(pos, end);
|
||||
newContent += "|]";
|
||||
pos = end;
|
||||
}
|
||||
if (marker?.fileName === fileName && marker.position >= pos) {
|
||||
newContent += content.slice(pos, marker.position);
|
||||
newContent += "/*FIND ALL REFS*/";
|
||||
pos = marker.position;
|
||||
}
|
||||
newContent += content.slice(pos);
|
||||
return newContent.split(/\r?\n/).map(l => "// " + l).join("\n");
|
||||
}
|
||||
|
||||
@@ -332,8 +332,12 @@ namespace FourSlashInterface {
|
||||
this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected);
|
||||
}
|
||||
|
||||
public baselineFindAllReferences(markerName: string) {
|
||||
this.state.verifyBaselineFindAllReferences(markerName);
|
||||
public baselineFindAllReferences(...markerNames: string[]) {
|
||||
this.state.verifyBaselineFindAllReferences(...markerNames);
|
||||
}
|
||||
|
||||
public baselineGetFileReferences(fileName: string) {
|
||||
this.state.verifyBaselineGetFileReferences(fileName);
|
||||
}
|
||||
|
||||
public referenceGroups(starts: ArrayOrSingle<string> | ArrayOrSingle<FourSlash.Range>, parts: ReferenceGroup[]) {
|
||||
|
||||
@@ -519,6 +519,9 @@ namespace Harness.LanguageService {
|
||||
findReferences(fileName: string, position: number): ts.ReferencedSymbol[] {
|
||||
return unwrapJSONCallResult(this.shim.findReferences(fileName, position));
|
||||
}
|
||||
getFileReferences(fileName: string): ts.ReferenceEntry[] {
|
||||
return unwrapJSONCallResult(this.shim.getFileReferences(fileName));
|
||||
}
|
||||
getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
|
||||
return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user