mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -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:
@@ -37,6 +37,9 @@ namespace ts.server.protocol {
|
||||
/* @internal */
|
||||
EmitOutput = "emit-output",
|
||||
Exit = "exit",
|
||||
FileReferences = "fileReferences",
|
||||
/* @internal */
|
||||
FileReferencesFull = "fileReferences-full",
|
||||
Format = "format",
|
||||
Formatonkey = "formatonkey",
|
||||
/* @internal */
|
||||
@@ -1152,6 +1155,25 @@ namespace ts.server.protocol {
|
||||
body?: ReferencesResponseBody;
|
||||
}
|
||||
|
||||
export interface FileReferencesRequest extends FileRequest {
|
||||
command: CommandTypes.FileReferences;
|
||||
}
|
||||
|
||||
export interface FileReferencesResponseBody {
|
||||
/**
|
||||
* The file locations referencing the symbol.
|
||||
*/
|
||||
refs: readonly ReferencesResponseItem[];
|
||||
/**
|
||||
* The name of the symbol.
|
||||
*/
|
||||
symbolName: string;
|
||||
}
|
||||
|
||||
export interface FileReferencesResponse extends Response {
|
||||
body?: FileReferencesResponseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument for RenameRequest request.
|
||||
*/
|
||||
|
||||
@@ -409,6 +409,29 @@ namespace ts.server {
|
||||
return outputs.filter(o => o.references.length !== 0);
|
||||
}
|
||||
|
||||
function combineProjectOutputForFileReferences(
|
||||
projects: Projects,
|
||||
defaultProject: Project,
|
||||
fileName: string
|
||||
): readonly ReferenceEntry[] {
|
||||
const outputs: ReferenceEntry[] = [];
|
||||
|
||||
combineProjectOutputWorker(
|
||||
projects,
|
||||
defaultProject,
|
||||
/*initialLocation*/ undefined,
|
||||
project => {
|
||||
for (const referenceEntry of project.getLanguageService().getFileReferences(fileName) || emptyArray) {
|
||||
if (!contains(outputs, referenceEntry, documentSpansEqual)) {
|
||||
outputs.push(referenceEntry);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
interface ProjectAndLocation<TLocation extends DocumentPosition | undefined> {
|
||||
readonly project: Project;
|
||||
readonly location: TLocation;
|
||||
@@ -1509,7 +1532,7 @@ namespace ts.server {
|
||||
return arrayFrom(map.values());
|
||||
}
|
||||
|
||||
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | undefined | readonly ReferencedSymbol[] {
|
||||
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | readonly ReferencedSymbol[] {
|
||||
const file = toNormalizedPath(args.file);
|
||||
const projects = this.getProjects(args);
|
||||
const position = this.getPositionInFile(args, file);
|
||||
@@ -1528,22 +1551,28 @@ namespace ts.server {
|
||||
const nameSpan = nameInfo && nameInfo.textSpan;
|
||||
const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0;
|
||||
const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : "";
|
||||
const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol =>
|
||||
referencedSymbol.references.map(({ fileName, textSpan, contextSpan, isWriteAccess, isDefinition }): protocol.ReferencesResponseItem => {
|
||||
const scriptInfo = Debug.checkDefined(this.projectService.getScriptInfo(fileName));
|
||||
const span = toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo);
|
||||
const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1);
|
||||
const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: fileName,
|
||||
...span,
|
||||
lineText,
|
||||
isWriteAccess,
|
||||
isDefinition
|
||||
};
|
||||
}));
|
||||
const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol => {
|
||||
return referencedSymbol.references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry));
|
||||
});
|
||||
return { refs, symbolName, symbolStartOffset, symbolDisplayString };
|
||||
}
|
||||
|
||||
private getFileReferences(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.FileReferencesResponseBody | readonly ReferenceEntry[] {
|
||||
const projects = this.getProjects(args);
|
||||
const references = combineProjectOutputForFileReferences(
|
||||
projects,
|
||||
this.getDefaultProject(args),
|
||||
args.file,
|
||||
);
|
||||
|
||||
if (!simplifiedResult) return references;
|
||||
const refs = references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry));
|
||||
return {
|
||||
refs,
|
||||
symbolName: `"${args.file}"`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName is the name of the file to be opened
|
||||
* @param fileContent is a version of the file content that is known to be more up to date than the one on disk
|
||||
@@ -2639,6 +2668,12 @@ namespace ts.server {
|
||||
[CommandNames.GetSpanOfEnclosingComment]: (request: protocol.SpanOfEnclosingCommentRequest) => {
|
||||
return this.requiredResponse(this.getSpanOfEnclosingComment(request.arguments));
|
||||
},
|
||||
[CommandNames.FileReferences]: (request: protocol.FileReferencesRequest) => {
|
||||
return this.requiredResponse(this.getFileReferences(request.arguments, /*simplifiedResult*/ true));
|
||||
},
|
||||
[CommandNames.FileReferencesFull]: (request: protocol.FileReferencesRequest) => {
|
||||
return this.requiredResponse(this.getFileReferences(request.arguments, /*simplifiedResult*/ false));
|
||||
},
|
||||
[CommandNames.Format]: (request: protocol.FormatRequest) => {
|
||||
return this.requiredResponse(this.getFormattingEditsForRange(request.arguments));
|
||||
},
|
||||
@@ -3068,4 +3103,18 @@ namespace ts.server {
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function referenceEntryToReferencesResponseItem(projectService: ProjectService, { fileName, textSpan, contextSpan, isWriteAccess, isDefinition }: ReferenceEntry): protocol.ReferencesResponseItem {
|
||||
const scriptInfo = Debug.checkDefined(projectService.getScriptInfo(fileName));
|
||||
const span = toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo);
|
||||
const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1);
|
||||
const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, "");
|
||||
return {
|
||||
file: fileName,
|
||||
...span,
|
||||
lineText,
|
||||
isWriteAccess,
|
||||
isDefinition
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user