Skip to content

Commit 09e063c

Browse files
committed
feat: add configurable typeHierarchyMaxResults preference
- Add typeHierarchyMaxResults to UserPreferences (default: 1000) - Update type hierarchy API to accept preferences parameter - Pass preferences through services.ts and session.ts - Use configurable limit in getSubtypes for performance control - Document in PR_DESCRIPTION.md
1 parent 29f6b36 commit 09e063c

File tree

6 files changed

+35
-18
lines changed

6 files changed

+35
-18
lines changed

PR_DESCRIPTION.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,18 @@ Types are annotated with descriptive `kindModifiers` to help distinguish differe
6666
- `resolveTypeHierarchyDeclaration()` - Entry point for type hierarchy requests
6767
- `createTypeHierarchyItem()` - Creates hierarchy items with proper metadata
6868
- `getSupertypes()` - Collects base types, implemented interfaces, and type parameter constraints
69-
- `getSubtypes()` - Finds derived types using hybrid approach with performance limits
69+
- `getSubtypes()` - Finds derived types using hybrid approach with configurable limits
7070
- `getTypeHierarchyKindModifiers()` - Returns modifiers based on type pattern
7171
- `findMixinVariablesUsingSymbol()` - Reverse mixin lookup
7272
- `collectTypeParameterConstraints()` - Collects type parameter constraints as supertypes
7373

74-
2. **`src/harness/fourslashImpl.ts`** (modified)
74+
2. **`src/compiler/types.ts`** (modified)
75+
- Added `typeHierarchyMaxResults` to `UserPreferences` for configurable result limits
76+
77+
3. **`src/harness/fourslashImpl.ts`** (modified)
7578
- Added `kindModifiers` display in type hierarchy baselines
7679

77-
3. **Test Files** (27 new fourslash tests)
80+
4. **Test Files** (27 new fourslash tests)
7881
- Comprehensive coverage of all supported patterns
7982
- Multi-file tests for cross-file scenarios
8083
- Edge case and negative case testing
@@ -187,7 +190,8 @@ Conditional types like `ExtractDog<T> = T extends Dog ? T : never` are shown as
187190
- **AST Traversal**: Avoided when possible; uses targeted lookups
188191
- **Seen Sets**: Prevents duplicate processing
189192
- **Cancellation Tokens**: Supports responsiveness during long operations
190-
- **Results Limit**: Subtype collection is capped at 1000 results per level to prevent issues in very large codebases
193+
- **Results Limit**: Subtype collection is capped at a configurable limit (default: 1000) to prevent performance issues in very large codebases
194+
- **Configurable via `UserPreferences.typeHierarchyMaxResults`**: The limit can be adjusted per-request
191195

192196
## Known Limitations
193197

@@ -198,10 +202,10 @@ Conditional types like `ExtractDog<T> = T extends Dog ? T : never` are shown as
198202

199203
## Future Enhancements
200204

205+
- [ ] **Truncation indicator** - Show indicator when results exceed the configured limit
201206
- [ ] Handle more complex generic type inference
202207
- [ ] Show inferred type widening in hierarchy
203208
- [ ] Enhanced cross-project reference support
204-
- [ ] Add truncation indicator when results exceed limit
205209

206210
---
207211

src/compiler/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10564,6 +10564,14 @@ export interface UserPreferences {
1056410564
* Default: `500`
1056510565
*/
1056610566
readonly maximumHoverLength?: number;
10567+
/**
10568+
* Maximum number of results to return per level in type hierarchy requests.
10569+
* A positive integer limiting the number of supertypes/subtypes returned to prevent
10570+
* performance issues in very large codebases.
10571+
*
10572+
* Default: `1000`
10573+
*/
10574+
readonly typeHierarchyMaxResults?: number;
1056710575
}
1056810576

1056910577
export type OrganizeImportsTypeOrder = "last" | "inline" | "first";

src/server/session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3416,14 +3416,14 @@ export class Session<TMessage = string> implements EventSender {
34163416
private provideTypeHierarchySupertypes(args: protocol.FileLocationRequestArgs): protocol.TypeHierarchyItem[] {
34173417
const { file, project } = this.getFileAndProject(args);
34183418
const scriptInfo = this.getScriptInfoFromProjectService(file);
3419-
const supertypes = project.getLanguageService().provideTypeHierarchySupertypes(file, this.getPosition(args, scriptInfo));
3419+
const supertypes = project.getLanguageService().provideTypeHierarchySupertypes(file, this.getPosition(args, scriptInfo), this.getPreferences(file));
34203420
return supertypes.map(item => this.toProtocolTypeHierarchyItem(item));
34213421
}
34223422

34233423
private provideTypeHierarchySubtypes(args: protocol.FileLocationRequestArgs): protocol.TypeHierarchyItem[] {
34243424
const { file, project } = this.getFileAndProject(args);
34253425
const scriptInfo = this.getScriptInfoFromProjectService(file);
3426-
const subtypes = project.getLanguageService().provideTypeHierarchySubtypes(file, this.getPosition(args, scriptInfo));
3426+
const subtypes = project.getLanguageService().provideTypeHierarchySubtypes(file, this.getPosition(args, scriptInfo), this.getPreferences(file));
34273427
return subtypes.map(item => this.toProtocolTypeHierarchyItem(item));
34283428
}
34293429

src/services/services.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3375,18 +3375,18 @@ export function createLanguageService(
33753375
return position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position);
33763376
}
33773377

3378-
function provideTypeHierarchySupertypes(fileName: string, position: number): TypeHierarchyItem[] {
3378+
function provideTypeHierarchySupertypes(fileName: string, position: number, preferences: UserPreferences = emptyOptions): TypeHierarchyItem[] {
33793379
synchronizeHostData();
33803380
const sourceFile = getValidSourceFile(fileName);
33813381
const declaration = TypeHierarchy.resolveTypeHierarchyDeclaration(program, getTypeHierarchyNodeAtPosition(sourceFile, position));
3382-
return declaration ? TypeHierarchy.getSupertypes(program, declaration, cancellationToken) : [];
3382+
return declaration ? TypeHierarchy.getSupertypes(program, declaration, cancellationToken, preferences) : [];
33833383
}
33843384

3385-
function provideTypeHierarchySubtypes(fileName: string, position: number): TypeHierarchyItem[] {
3385+
function provideTypeHierarchySubtypes(fileName: string, position: number, preferences: UserPreferences = emptyOptions): TypeHierarchyItem[] {
33863386
synchronizeHostData();
33873387
const sourceFile = getValidSourceFile(fileName);
33883388
const declaration = TypeHierarchy.resolveTypeHierarchyDeclaration(program, getTypeHierarchyNodeAtPosition(sourceFile, position));
3389-
return declaration ? TypeHierarchy.getSubtypes(program, declaration, cancellationToken) : [];
3389+
return declaration ? TypeHierarchy.getSubtypes(program, declaration, cancellationToken, preferences) : [];
33903390
}
33913391

33923392
function provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences = emptyOptions): InlayHint[] {

src/services/typeHierarchy.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ import {
4343
TypeHierarchyItem,
4444
TypeReference,
4545
TypeReferenceNode,
46+
UserPreferences,
4647
VariableDeclaration,
4748
} from "./_namespaces/ts.js";
4849

50+
/** Default maximum results per level in type hierarchy */
51+
const DEFAULT_TYPE_HIERARCHY_MAX_RESULTS = 1000;
52+
4953
/** @internal */
5054
export type TypeHierarchyDeclaration =
5155
| ClassDeclaration
@@ -410,9 +414,12 @@ function getSymbolFromHeritageClauseType(typeNode: ExpressionWithTypeArguments,
410414
*
411415
* @internal
412416
*/
413-
export function getSupertypes(program: Program, declaration: TypeHierarchyDeclaration, _cancellationToken: CancellationToken): TypeHierarchyItem[] {
417+
export function getSupertypes(program: Program, declaration: TypeHierarchyDeclaration, _cancellationToken: CancellationToken, _preferences?: UserPreferences): TypeHierarchyItem[] {
414418
const typeChecker = program.getTypeChecker();
415419
const result: TypeHierarchyItem[] = [];
420+
// Note: maxResults is not used for supertypes as they are naturally limited
421+
// (a class has one base class plus some interfaces). The preference is accepted
422+
// for API consistency with getSubtypes.
416423

417424
if (isClassDeclaration(declaration) || isClassExpression(declaration)) {
418425
// Get base class
@@ -678,10 +685,11 @@ function addTypeReferenceToResult(
678685
*
679686
* @internal
680687
*/
681-
export function getSubtypes(program: Program, declaration: TypeHierarchyDeclaration, cancellationToken: CancellationToken): TypeHierarchyItem[] {
688+
export function getSubtypes(program: Program, declaration: TypeHierarchyDeclaration, cancellationToken: CancellationToken, preferences?: UserPreferences): TypeHierarchyItem[] {
682689
const typeChecker = program.getTypeChecker();
683690
const result: TypeHierarchyItem[] = [];
684691
const seen = new Set<TypeHierarchyDeclaration>();
692+
const maxResults = preferences?.typeHierarchyMaxResults ?? DEFAULT_TYPE_HIERARCHY_MAX_RESULTS;
685693

686694
// Get the name node - for assigned class expressions and mixin variables, use the variable name
687695
let locationNode: Node | undefined;
@@ -705,9 +713,6 @@ export function getSubtypes(program: Program, declaration: TypeHierarchyDeclarat
705713
return result;
706714
}
707715

708-
// Maximum number of subtypes to return for performance
709-
const maxResults = 1000;
710-
711716
// PART 1: Use FindAllReferences with { implementations: true } for heritage clauses
712717
// This efficiently finds classes/interfaces that extend/implement the target using the name index
713718
const entries = FindAllReferences.getReferenceEntriesForNode(

src/services/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,8 +627,8 @@ export interface LanguageService {
627627
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
628628

629629
prepareTypeHierarchy(fileName: string, position: number): TypeHierarchyItem | TypeHierarchyItem[] | undefined;
630-
provideTypeHierarchySupertypes(fileName: string, position: number): TypeHierarchyItem[];
631-
provideTypeHierarchySubtypes(fileName: string, position: number): TypeHierarchyItem[];
630+
provideTypeHierarchySupertypes(fileName: string, position: number, preferences?: UserPreferences): TypeHierarchyItem[];
631+
provideTypeHierarchySubtypes(fileName: string, position: number, preferences?: UserPreferences): TypeHierarchyItem[];
632632

633633
provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences | undefined): InlayHint[];
634634

0 commit comments

Comments
 (0)