Merge pull request #16081 from Microsoft/findAllRefs-state
findAllRefs: Replace 'interface State' and 'createState' with just 'class State'
This commit is contained in:
commit
7cca4ba536
1 changed files with 48 additions and 67 deletions
|
@ -386,7 +386,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations);
|
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations);
|
||||||
|
|
||||||
const result: SymbolAndEntries[] = [];
|
const result: SymbolAndEntries[] = [];
|
||||||
const state = createState(sourceFiles, node, checker, cancellationToken, searchMeaning, options, result);
|
const state = new State(sourceFiles, /*isForConstructor*/ node.kind === SyntaxKind.ConstructorKeyword, checker, cancellationToken, searchMeaning, options, result);
|
||||||
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) });
|
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) });
|
||||||
|
|
||||||
// Try to get the smallest valid scope that we can limit our search to;
|
// Try to get the smallest valid scope that we can limit our search to;
|
||||||
|
@ -446,35 +446,9 @@ namespace ts.FindAllReferences.Core {
|
||||||
* Holds all state needed for the finding references.
|
* Holds all state needed for the finding references.
|
||||||
* Unlike `Search`, there is only one `State`.
|
* Unlike `Search`, there is only one `State`.
|
||||||
*/
|
*/
|
||||||
interface State extends Options {
|
class State {
|
||||||
/** True if we're searching for constructor references. */
|
|
||||||
readonly isForConstructor: boolean;
|
|
||||||
|
|
||||||
readonly sourceFiles: SourceFile[];
|
|
||||||
readonly checker: TypeChecker;
|
|
||||||
readonly cancellationToken: CancellationToken;
|
|
||||||
readonly searchMeaning: SemanticMeaning;
|
|
||||||
|
|
||||||
/** Cache for `explicitlyinheritsFrom`. */
|
/** Cache for `explicitlyinheritsFrom`. */
|
||||||
readonly inheritsFromCache: Map<boolean>;
|
readonly inheritsFromCache = createMap<boolean>();
|
||||||
|
|
||||||
/** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */
|
|
||||||
getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult;
|
|
||||||
|
|
||||||
/** @param allSearchSymbols set of additinal symbols for use by `includes`. */
|
|
||||||
createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions?: { text?: string, allSearchSymbols?: Symbol[] }): Search;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback to add references for a particular searched symbol.
|
|
||||||
* This initializes a reference group, so only call this if you will add at least one reference.
|
|
||||||
*/
|
|
||||||
referenceAdder(searchSymbol: Symbol, searchLocation: Node): (node: Node) => void;
|
|
||||||
|
|
||||||
/** Add a reference with no associated definition. */
|
|
||||||
addStringOrCommentReference(fileName: string, textSpan: TextSpan): void;
|
|
||||||
|
|
||||||
/** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */
|
|
||||||
markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type nodes can contain multiple references to the same type. For example:
|
* Type nodes can contain multiple references to the same type. For example:
|
||||||
|
@ -483,7 +457,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
* duplicate entries would be returned here as each of the type references is part of
|
* duplicate entries would be returned here as each of the type references is part of
|
||||||
* the same implementation. For that reason, check before we add a new entry.
|
* the same implementation. For that reason, check before we add a new entry.
|
||||||
*/
|
*/
|
||||||
markSeenContainingTypeReference(containingTypeReference: Node): boolean;
|
readonly markSeenContainingTypeReference = nodeSeenTracker();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It's possible that we will encounter the right side of `export { foo as bar } from "x";` more than once.
|
* It's possible that we will encounter the right side of `export { foo as bar } from "x";` more than once.
|
||||||
|
@ -496,33 +470,31 @@ namespace ts.FindAllReferences.Core {
|
||||||
* But another reference to it may appear in the same source file.
|
* But another reference to it may appear in the same source file.
|
||||||
* See `tests/cases/fourslash/transitiveExportImports3.ts`.
|
* See `tests/cases/fourslash/transitiveExportImports3.ts`.
|
||||||
*/
|
*/
|
||||||
markSeenReExportRHS(rhs: Identifier): boolean;
|
readonly markSeenReExportRHS = nodeSeenTracker();
|
||||||
}
|
|
||||||
|
|
||||||
function createState(sourceFiles: SourceFile[], originalLocation: Node, checker: TypeChecker, cancellationToken: CancellationToken, searchMeaning: SemanticMeaning, options: Options, result: Push<SymbolAndEntries>): State {
|
constructor(
|
||||||
const symbolIdToReferences: Entry[][] = [];
|
readonly sourceFiles: SourceFile[],
|
||||||
const inheritsFromCache = createMap<boolean>();
|
/** True if we're searching for constructor references. */
|
||||||
// Source file ID → symbol ID → Whether the symbol has been searched for in the source file.
|
readonly isForConstructor: boolean,
|
||||||
const sourceFileToSeenSymbols: Array<Array<true>> = [];
|
readonly checker: TypeChecker,
|
||||||
const isForConstructor = originalLocation.kind === SyntaxKind.ConstructorKeyword;
|
readonly cancellationToken: CancellationToken,
|
||||||
let importTracker: ImportTracker | undefined;
|
readonly searchMeaning: SemanticMeaning,
|
||||||
|
readonly options: Options,
|
||||||
|
private readonly result: Push<SymbolAndEntries>) {}
|
||||||
|
|
||||||
return {
|
private importTracker: ImportTracker | undefined;
|
||||||
...options,
|
/** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */
|
||||||
sourceFiles, isForConstructor, checker, cancellationToken, searchMeaning, inheritsFromCache, getImportSearches, createSearch, referenceAdder, addStringOrCommentReference,
|
getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult {
|
||||||
markSearchedSymbol, markSeenContainingTypeReference: nodeSeenTracker(), markSeenReExportRHS: nodeSeenTracker(),
|
if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.checker, this.cancellationToken);
|
||||||
};
|
return this.importTracker(exportSymbol, exportInfo, this.options.isForRename);
|
||||||
|
|
||||||
function getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult {
|
|
||||||
if (!importTracker) importTracker = createImportTracker(sourceFiles, checker, cancellationToken);
|
|
||||||
return importTracker(exportSymbol, exportInfo, options.isForRename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search {
|
/** @param allSearchSymbols set of additinal symbols for use by `includes`. */
|
||||||
|
createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search {
|
||||||
// Note: if this is an external module symbol, the name doesn't include quotes.
|
// Note: if this is an external module symbol, the name doesn't include quotes.
|
||||||
const { text = stripQuotes(getDeclaredName(checker, symbol, location)), allSearchSymbols = undefined } = searchOptions;
|
const { text = stripQuotes(getDeclaredName(this.checker, symbol, location)), allSearchSymbols = undefined } = searchOptions;
|
||||||
const escapedText = escapeIdentifier(text);
|
const escapedText = escapeIdentifier(text);
|
||||||
const parents = options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, checker);
|
const parents = this.options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker);
|
||||||
return { location, symbol, comingFrom, text, escapedText, parents, includes };
|
return { location, symbol, comingFrom, text, escapedText, parents, includes };
|
||||||
|
|
||||||
function includes(referenceSymbol: Symbol): boolean {
|
function includes(referenceSymbol: Symbol): boolean {
|
||||||
|
@ -530,27 +502,36 @@ namespace ts.FindAllReferences.Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function referenceAdder(referenceSymbol: Symbol, searchLocation: Node): (node: Node) => void {
|
private readonly symbolIdToReferences: Entry[][] = [];
|
||||||
const symbolId = getSymbolId(referenceSymbol);
|
/**
|
||||||
let references = symbolIdToReferences[symbolId];
|
* Callback to add references for a particular searched symbol.
|
||||||
|
* This initializes a reference group, so only call this if you will add at least one reference.
|
||||||
|
*/
|
||||||
|
referenceAdder(searchSymbol: Symbol, searchLocation: Node): (node: Node) => void {
|
||||||
|
const symbolId = getSymbolId(searchSymbol);
|
||||||
|
let references = this.symbolIdToReferences[symbolId];
|
||||||
if (!references) {
|
if (!references) {
|
||||||
references = symbolIdToReferences[symbolId] = [];
|
references = this.symbolIdToReferences[symbolId] = [];
|
||||||
result.push({ definition: { type: "symbol", symbol: referenceSymbol, node: searchLocation }, references });
|
this.result.push({ definition: { type: "symbol", symbol: searchSymbol, node: searchLocation }, references });
|
||||||
}
|
}
|
||||||
return node => references.push(nodeEntry(node));
|
return node => references.push(nodeEntry(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
function addStringOrCommentReference(fileName: string, textSpan: TextSpan): void {
|
/** Add a reference with no associated definition. */
|
||||||
result.push({
|
addStringOrCommentReference(fileName: string, textSpan: TextSpan): void {
|
||||||
|
this.result.push({
|
||||||
definition: undefined,
|
definition: undefined,
|
||||||
references: [{ type: "span", fileName, textSpan }]
|
references: [{ type: "span", fileName, textSpan }]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean {
|
// Source file ID → symbol ID → Whether the symbol has been searched for in the source file.
|
||||||
|
private readonly sourceFileToSeenSymbols: Array<Array<true>> = [];
|
||||||
|
/** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */
|
||||||
|
markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean {
|
||||||
const sourceId = getNodeId(sourceFile);
|
const sourceId = getNodeId(sourceFile);
|
||||||
const symbolId = getSymbolId(symbol);
|
const symbolId = getSymbolId(symbol);
|
||||||
const seenSymbols = sourceFileToSeenSymbols[sourceId] || (sourceFileToSeenSymbols[sourceId] = []);
|
const seenSymbols = this.sourceFileToSeenSymbols[sourceId] || (this.sourceFileToSeenSymbols[sourceId] = []);
|
||||||
return !seenSymbols[symbolId] && (seenSymbols[symbolId] = true);
|
return !seenSymbols[symbolId] && (seenSymbols[symbolId] = true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -580,7 +561,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
break;
|
break;
|
||||||
case ExportKind.Default:
|
case ExportKind.Default:
|
||||||
// Search for a property access to '.default'. This can't be renamed.
|
// Search for a property access to '.default'. This can't be renamed.
|
||||||
indirectSearch = state.isForRename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" });
|
indirectSearch = state.options.isForRename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" });
|
||||||
break;
|
break;
|
||||||
case ExportKind.ExportEquals:
|
case ExportKind.ExportEquals:
|
||||||
break;
|
break;
|
||||||
|
@ -803,7 +784,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.findInComments || container.jsDoc !== undefined)) {
|
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.options.findInComments || container.jsDoc !== undefined)) {
|
||||||
getReferencesAtLocation(sourceFile, position, search, state);
|
getReferencesAtLocation(sourceFile, position, search, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,7 +796,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
// This wasn't the start of a token. Check to see if it might be a
|
// This wasn't the start of a token. Check to see if it might be a
|
||||||
// match in a comment or string if that's what the caller is asking
|
// match in a comment or string if that's what the caller is asking
|
||||||
// for.
|
// for.
|
||||||
if (!state.implementations && (state.findInStrings && isInString(sourceFile, position) || state.findInComments && isInNonReferenceComment(sourceFile, position))) {
|
if (!state.options.implementations && (state.options.findInStrings && isInString(sourceFile, position) || state.options.findInComments && isInNonReferenceComment(sourceFile, position))) {
|
||||||
// In the case where we're looking inside comments/strings, we don't have
|
// In the case where we're looking inside comments/strings, we don't have
|
||||||
// an actual definition. So just use 'undefined' here. Features like
|
// an actual definition. So just use 'undefined' here. Features like
|
||||||
// 'Rename' won't care (as they ignore the definitions), and features like
|
// 'Rename' won't care (as they ignore the definitions), and features like
|
||||||
|
@ -881,7 +862,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
addRef();
|
addRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.isForRename && state.markSeenReExportRHS(name)) {
|
if (!state.options.isForRename && state.markSeenReExportRHS(name)) {
|
||||||
addReference(name, referenceSymbol, name, state);
|
addReference(name, referenceSymbol, name, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -892,7 +873,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For `export { foo as bar }`, rename `foo`, but not `bar`.
|
// For `export { foo as bar }`, rename `foo`, but not `bar`.
|
||||||
if (!(referenceLocation === propertyName && state.isForRename)) {
|
if (!(referenceLocation === propertyName && state.options.isForRename)) {
|
||||||
const exportKind = (referenceLocation as Identifier).originalKeywordKind === ts.SyntaxKind.DefaultKeyword ? ExportKind.Default : ExportKind.Named;
|
const exportKind = (referenceLocation as Identifier).originalKeywordKind === ts.SyntaxKind.DefaultKeyword ? ExportKind.Default : ExportKind.Named;
|
||||||
const exportInfo = getExportInfo(referenceSymbol, exportKind, state.checker);
|
const exportInfo = getExportInfo(referenceSymbol, exportKind, state.checker);
|
||||||
Debug.assert(!!exportInfo);
|
Debug.assert(!!exportInfo);
|
||||||
|
@ -934,7 +915,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
const { symbol } = importOrExport;
|
const { symbol } = importOrExport;
|
||||||
|
|
||||||
if (importOrExport.kind === ImportExport.Import) {
|
if (importOrExport.kind === ImportExport.Import) {
|
||||||
if (!state.isForRename || importOrExport.isNamedImport) {
|
if (!state.options.isForRename || importOrExport.isNamedImport) {
|
||||||
searchForImportedSymbol(symbol, state);
|
searchForImportedSymbol(symbol, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -960,7 +941,7 @@ namespace ts.FindAllReferences.Core {
|
||||||
|
|
||||||
function addReference(referenceLocation: Node, relatedSymbol: Symbol, searchLocation: Node, state: State): void {
|
function addReference(referenceLocation: Node, relatedSymbol: Symbol, searchLocation: Node, state: State): void {
|
||||||
const addRef = state.referenceAdder(relatedSymbol, searchLocation);
|
const addRef = state.referenceAdder(relatedSymbol, searchLocation);
|
||||||
if (state.implementations) {
|
if (state.options.implementations) {
|
||||||
addImplementationReferences(referenceLocation, addRef, state);
|
addImplementationReferences(referenceLocation, addRef, state);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in a new issue