refactor(language-service): Separate reference and rename capabilities (#40523)

This commit separates the reference and rename functions into separate builders so it's easier
to locate functions specific to each

PR Close #40523
This commit is contained in:
Andrew Scott 2021-01-21 08:46:40 -08:00 committed by Alex Rickabaugh
parent 9bc8b343ea
commit c1bcbeb324
2 changed files with 66 additions and 61 deletions

View File

@ -26,7 +26,7 @@ import {CompilerFactory} from './compiler_factory';
import {CompletionBuilder, CompletionNodeContext} from './completions';
import {DefinitionBuilder} from './definitions';
import {QuickInfoBuilder} from './quick_info';
import {ReferencesAndRenameBuilder} from './references_and_rename';
import {ReferencesBuilder, RenameBuilder} from './references_and_rename';
import {getSignatureHelp} from './signature_help';
import {getTargetAtPosition, TargetContext, TargetNodeKind} from './template_target';
import {findTightestNode, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue} from './ts_utils';
@ -168,14 +168,14 @@ export class LanguageService {
getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[]|undefined {
return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
return new ReferencesAndRenameBuilder(this.programDriver, this.tsLS, compiler)
return new ReferencesBuilder(this.programDriver, this.tsLS, compiler)
.getReferencesAtPosition(fileName, position);
});
}
getRenameInfo(fileName: string, position: number): ts.RenameInfo {
return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
const renameInfo = new ReferencesAndRenameBuilder(this.programDriver, this.tsLS, compiler)
const renameInfo = new RenameBuilder(this.programDriver, this.tsLS, compiler)
.getRenameInfo(absoluteFrom(fileName), position);
if (!renameInfo.canRename) {
return renameInfo;
@ -191,7 +191,7 @@ export class LanguageService {
findRenameLocations(fileName: string, position: number): readonly ts.RenameLocation[]|undefined {
return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
return new ReferencesAndRenameBuilder(this.programDriver, this.tsLS, compiler)
return new RenameBuilder(this.programDriver, this.tsLS, compiler)
.findRenameLocations(fileName, position);
});
}

View File

@ -16,6 +16,61 @@ import {convertToTemplateDocumentSpan, createLocationKey, getRenameTextAndSpanAt
import {findTightestNode} from './ts_utils';
import {getTemplateInfoAtPosition, TemplateInfo} from './utils';
export class ReferencesBuilder {
private readonly ttc = this.compiler.getTemplateTypeChecker();
constructor(
private readonly driver: ProgramDriver, private readonly tsLS: ts.LanguageService,
private readonly compiler: NgCompiler) {}
getReferencesAtPosition(filePath: string, position: number): ts.ReferenceEntry[]|undefined {
this.ttc.generateAllTypeCheckBlocks();
const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
if (templateInfo === undefined) {
return this.getReferencesAtTypescriptPosition(filePath, position);
}
return this.getReferencesAtTemplatePosition(templateInfo, position);
}
private getReferencesAtTemplatePosition(templateInfo: TemplateInfo, position: number):
ts.ReferenceEntry[]|undefined {
const allTargetDetails = getTargetDetailsAtTemplatePosition(templateInfo, position, this.ttc);
if (allTargetDetails === null) {
return undefined;
}
const allReferences: ts.ReferenceEntry[] = [];
for (const targetDetails of allTargetDetails) {
for (const location of targetDetails.typescriptLocations) {
const refs = this.getReferencesAtTypescriptPosition(location.fileName, location.position);
if (refs !== undefined) {
allReferences.push(...refs);
}
}
}
return allReferences.length > 0 ? allReferences : undefined;
}
private getReferencesAtTypescriptPosition(fileName: string, position: number):
ts.ReferenceEntry[]|undefined {
const refs = this.tsLS.getReferencesAtPosition(fileName, position);
if (refs === undefined) {
return undefined;
}
const entries: Map<string, ts.ReferenceEntry> = new Map();
for (const ref of refs) {
if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(ref.fileName))) {
const entry = convertToTemplateDocumentSpan(ref, this.ttc, this.driver.getProgram());
if (entry !== null) {
entries.set(createLocationKey(entry), entry);
}
} else {
entries.set(createLocationKey(ref), ref);
}
}
return Array.from(entries.values());
}
}
enum RequestKind {
Template,
@ -35,7 +90,8 @@ interface TypeScriptRequest {
type RequestOrigin = TemplateRequest|TypeScriptRequest;
export class ReferencesAndRenameBuilder {
export class RenameBuilder {
private readonly ttc = this.compiler.getTemplateTypeChecker();
constructor(
@ -125,14 +181,6 @@ export class ReferencesAndRenameBuilder {
return allRenameLocations.length > 0 ? allRenameLocations : undefined;
}
private getTsNodeAtPosition(filePath: string, position: number): ts.Node|null {
const sf = this.driver.getProgram().getSourceFile(filePath);
if (!sf) {
return null;
}
return findTightestNode(sf, position) ?? null;
}
findRenameLocationsAtTypescriptPosition(
filePath: string, position: number,
requestOrigin: RequestOrigin): readonly ts.RenameLocation[]|undefined {
@ -181,54 +229,11 @@ export class ReferencesAndRenameBuilder {
});
}
getReferencesAtPosition(filePath: string, position: number): ts.ReferenceEntry[]|undefined {
this.ttc.generateAllTypeCheckBlocks();
return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
if (templateInfo === undefined) {
return this.getReferencesAtTypescriptPosition(filePath, position);
private getTsNodeAtPosition(filePath: string, position: number): ts.Node|null {
const sf = this.driver.getProgram().getSourceFile(filePath);
if (!sf) {
return null;
}
return this.getReferencesAtTemplatePosition(templateInfo, position);
});
}
private getReferencesAtTemplatePosition(templateInfo: TemplateInfo, position: number):
ts.ReferenceEntry[]|undefined {
const allTargetDetails = getTargetDetailsAtTemplatePosition(templateInfo, position, this.ttc);
if (allTargetDetails === null) {
return undefined;
}
const allReferences: ts.ReferenceEntry[] = [];
for (const targetDetails of allTargetDetails) {
for (const location of targetDetails.typescriptLocations) {
const refs = this.getReferencesAtTypescriptPosition(location.fileName, location.position);
if (refs !== undefined) {
allReferences.push(...refs);
}
}
}
return allReferences.length > 0 ? allReferences : undefined;
}
private getReferencesAtTypescriptPosition(fileName: string, position: number):
ts.ReferenceEntry[]|undefined {
const refs = this.tsLS.getReferencesAtPosition(fileName, position);
if (refs === undefined) {
return undefined;
}
const entries: Map<string, ts.ReferenceEntry> = new Map();
for (const ref of refs) {
if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(ref.fileName))) {
const entry = convertToTemplateDocumentSpan(ref, this.ttc, this.driver.getProgram());
if (entry !== null) {
entries.set(createLocationKey(entry), entry);
}
} else {
entries.set(createLocationKey(ref), ref);
}
}
return Array.from(entries.values());
return findTightestNode(sf, position) ?? null;
}
}