This page documents the three navigation services exposed by the TypeScript language service: the navigation bar (file structure outline), outlining spans (code folding), and navigate-to (cross-file symbol search). These are distinct from find-references and rename, which are documented in 4.8, and from completions, which are documented in 4.1.
The navigation bar gives editors a hierarchical outline of the symbols defined in a source file. It is surfaced through two entry points:
| Function | Return type | Description |
|---|---|---|
getNavigationBarItems | NavigationBarItem[] | Flat list suitable for a two-level primary/secondary navbar menu |
getNavigationTree | NavigationTree | Full recursive tree |
Both are in src/services/navigationBar.ts
NavigationBarNodeBefore converting to the public NavigationBarItem / NavigationTree types, the service builds an intermediate tree of NavigationBarNode objects. This type is not exported.
interface NavigationBarNode {
node: Node;
name: DeclarationName | undefined;
additionalNodes: Node[] | undefined; // for merged declarations
parent: NavigationBarNode | undefined;
children: NavigationBarNode[] | undefined;
indent: number;
}
additionalNodes holds extra source nodes that were merged into a single logical entry (e.g., an interface that is declared in two places, or an ES5 class whose constructor function and prototype assignments are stitched together).
Sources: src/services/navigationBar.ts161-168
The following diagram shows how a SourceFile becomes a NavigationTree.
Navigation Bar Build Pipeline
Sources: src/services/navigationBar.ts171-229
A module-level stack (parentsStack, parent) tracks the current parent node rather than threading it through function arguments. startNode pushes to the stack; endNode pops. This is a deliberate performance choice noted in the source.
Sources: src/services/navigationBar.ts143-154
addChildrenRecursively visits each AST node and decides whether to create a sub-node, a leaf, or to recurse further. Key decisions:
| Node kind | Treatment |
|---|---|
ClassDeclaration, ClassExpression, InterfaceDeclaration | startNode + recurse members |
EnumDeclaration | startNode + leaf per non-computed member |
ModuleDeclaration | startNode + recurse getInteriorModule().body |
FunctionDeclaration, FunctionExpression, ArrowFunction | startNode + recurse body |
MethodDeclaration, GetAccessor, SetAccessor | addNodeWithRecursiveChild if has nav-bar name |
VariableDeclaration, PropertyAssignment, BindingElement | addNodeWithRecursiveInitializer |
ImportClause | Leaf per default name and named bindings |
TypeAliasDeclaration, ExportSpecifier, IndexSignature, etc. | addLeafNode |
BinaryExpression / CallExpression (assignment declarations) | ES5 class handling (see below) |
JSDocTypedefTag, JSDocCallbackTag | Leaf nodes |
Sources: src/services/navigationBar.ts326-578
JavaScript code that uses the ES5 prototype pattern produces multiple separate AST nodes (a FunctionDeclaration for the constructor, BinaryExpression nodes for prototype assignments). The navigation bar merges these into a single class-like entry.
ES5 Class Merging Logic
The trackedEs5Classes map (one per scope) records which function names are candidates for ES5 class treatment. The isEs5ClassMember record maps each AssignmentDeclarationKind to a boolean indicating whether it counts as an ES5 class member.
Sources: src/services/navigationBar.ts246-724
After a node's children are collected, endNode calls:
mergeChildren — groups children by name. For same-name children, calls tryMerge, which first tries tryMergeEs5Class and then shouldReallyMerge. Two declarations are merged if they have the same kind, the same parent, and (for property/accessor nodes) matching static-ness. For ModuleDeclaration, also checks that the chain of nested module names is identical (areSameModule).sortChildren — sorts by compareStringsCaseSensitiveUI on the display name, then by SyntaxKind as a tiebreaker.Sources: src/services/navigationBar.ts581-802
primaryNavBarMenuItems flattens the tree to a list for the primary (middle) navbar menu. An item appears in the primary menu if:
ClassDeclaration, ClassExpression, EnumDeclaration, InterfaceDeclaration, ModuleDeclaration, SourceFile, TypeAliasDeclaration, JSDocTypedefTag, or JSDocCallbackTag, orSourceFile, ModuleBlock, or a method body).Sources: src/services/navigationBar.ts877-930
getItemName computes the display string for a node:
| Node kind | Display |
|---|---|
SourceFile (external module) | "<filename>" |
SourceFile (script) | <global> |
ExportAssignment (export =) | export= |
ExportAssignment (export default) | default |
Constructor | constructor |
ConstructSignature | new() |
CallSignature | () |
IndexSignature | [] |
ArrowFunction / FunctionExpression / ClassExpression (unnamed) | Derived from the assignment target name via getFunctionOrClassName |
Sources: src/services/navigationBar.ts829-875
The outlining service computes the set of collapsible regions in a file for editor code-folding. It is exposed via collectElements in src/services/outliningElementsCollector.ts
An OutliningSpan has:
| Field | Type | Description |
|---|---|---|
textSpan | TextSpan | The collapsible range |
hintSpan | TextSpan | Shown to the user when the region is collapsed (typically the first line) |
bannerText | string | Collapsed placeholder text (default: "...") |
autoCollapse | boolean | Whether the editor should auto-collapse on load |
kind | OutliningSpanKind | One of Code, Comment, Imports, Region |
Sources: src/services/outliningElementsCollector.ts59-65
collectElements runs two independent passes then sorts results by start position:
Outlining Span Collection Pipeline
Sources: src/services/outliningElementsCollector.ts59-136
The visitor in addNodeOutliningSpans maintains a depthRemaining counter (starts at 40). Most nodes decrement it on entry and restore it on exit. CallExpression is a special case: the call expression itself gets one extra depth unit so that chained calls don't blow the limit.
Sources: src/services/outliningElementsCollector.ts68-135
getOutliningSpanForNode maps AST node kinds to span strategies:
| Node kind | Span strategy |
|---|---|
Block inside function-like | From open paren (if params are multi-line) or {, to } |
Block inside if/for/while/do/with/catch/try | Parent-node hint span |
ModuleBlock | Parent ModuleDeclaration hint span |
ClassDeclaration, ClassExpression, InterfaceDeclaration, EnumDeclaration, CaseBlock, TypeLiteral, ObjectBindingPattern | { to } |
CaseClause, DefaultClause | NodeArray of statements |
ObjectLiteralExpression | { to } |
ArrayLiteralExpression | [ to ] |
JsxElement | Opening tag to closing tag, banner <tag>...</tag> |
JsxFragment | <>...</> |
JsxSelfClosingElement, JsxOpeningElement | Attributes span |
TemplateExpression, NoSubstitutionTemplateLiteral | Full span |
ArrowFunction | Non-block body only (multi-line expression body) |
CallExpression | Argument list ( to ) (multi-line only) |
ParenthesizedExpression | Full span (multi-line only) |
NamedImports, NamedExports, ImportAttributes | { to } (multi-line only) |
Sources: src/services/outliningElementsCollector.ts236-398
addOutliningForLeadingCommentsForPos processes leading comment ranges before declarations, variable statements, return statements, call/new expressions, and the EOF token.
/* ... */): each one gets its own span, kind Comment.// ...): consecutive runs of two or more are combined into one span, kind Comment. Region delimiters (// #region) are excluded and reset the run counter.Sources: src/services/outliningElementsCollector.ts180-230
addRegionOutliningSpans scans every line of the source file for // #region [name] and // #endregion comments. It maintains a stack of open regions and pairs them up. The banner text for a region is the optional name given in the #region comment, defaulting to "#region".
Sources: src/services/outliningElementsCollector.ts138-162
tryGetFunctionOpenToken decides where the collapsible span of a function starts:
isNodeArrayMultiLine), the span starts at (.{.This gives editors a clean visual cue when long parameter lists are written vertically.
Sources: src/services/outliningElementsCollector.ts415-423
The navigate-to feature allows editors to search for any named declaration across all files in the program by name. It is implemented in src/services/navigateTo.ts and uses the pattern matcher in src/services/patternMatcher.ts
Entry point:
Sources: src/services/navigateTo.ts42-66
Navigate-To Search Flow
Sources: src/services/navigateTo.ts42-66
For ImportClause, ImportSpecifier, and ImportEqualsDeclaration, shouldKeepItem resolves the aliased symbol via checker.getAliasedSymbol. An import is excluded if the aliased symbol has the same name (the import is a re-export of itself) or if all of its declarations are in excluded library files.
Sources: src/services/navigateTo.ts110-128
When patternContainsDots is true (e.g., searching A.MyClass), the matcher needs the declaration's container chain. getContainers walks up the AST via getContainerNode and collects the names of each enclosing scope. Computed property names with simple dotted expressions (e.g., [X.Y.Z]) are also supported.
Sources: src/services/navigateTo.ts147-172
NavigateToItem ResultcreateNavigateToItem builds the final result from a RawNavigateToItem:
| Field | Source |
|---|---|
name | The declaration's text name |
kind | getNodeKind(declaration) |
kindModifiers | getNodeModifiers(declaration) |
matchKind | PatternMatchKind as a string key |
isCaseSensitive | From the pattern match |
fileName | sourceFile.fileName |
textSpan | createTextSpanFromNode(declaration) |
containerName | Name of the enclosing declaration |
containerKind | Kind of the enclosing declaration |
Sources: src/services/navigateTo.ts180-196
src/services/patternMatcher.ts implements the fuzzy matching engine used by navigate-to.
Results are ordered from strongest to weakest; callers receive the best-quality match.
Sources: src/services/patternMatcher.ts17-22
A pattern string is split on . into segments. Each segment is broken into sub-word text chunks using breakPatternIntoTextChunks (splitting on word boundaries, case transitions, punctuation, and digit-to-letter transitions). A TextChunk stores the text, its lowercase version, a flag for all-lowercase, and pre-computed characterSpans for camelCase matching.
Pattern Matching Architecture
Sources: src/services/patternMatcher.ts72-141
For a given candidate string and a Segment:
totalTextChunk as a whole (handles cases like @int).subWordTextChunk in turn:
tryCamelCaseMatch to align chunk character spans against candidate word spans.Sources: src/services/patternMatcher.ts175-282
getFullMatch first verifies that the last segment matches the declaration name, then works backwards through the remaining segments against the declaration's container chain. The weakest match across all segments is returned.
Sources: src/services/patternMatcher.ts143-165
All three features are wired into the LanguageService interface (see 4):
LanguageService method | Delegates to |
|---|---|
getNavigationBarItems(fileName) | getNavigationBarItems(sourceFile, cancellationToken) |
getNavigationTree(fileName) | getNavigationTree(sourceFile, cancellationToken) |
getOutliningSpans(fileName) | collectElements(sourceFile, cancellationToken) |
getNavigateToItems(searchValue, ...) | getNavigateToItems(sourceFiles, checker, ...) |
The navigation bar and outlining features operate entirely on the AST without the type checker. Navigate-to uses the type checker only for import alias resolution in shouldKeepItem.
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.