Skip to content

feat(language-service): add Document Symbols support for Angular templates#66690

Open
kbrilla wants to merge 4 commits intoangular:mainfrom
kbrilla:feat/document-symbols-templates
Open

feat(language-service): add Document Symbols support for Angular templates#66690
kbrilla wants to merge 4 commits intoangular:mainfrom
kbrilla:feat/document-symbols-templates

Conversation

@kbrilla
Copy link
Contributor

@kbrilla kbrilla commented Jan 21, 2026

PR: feat(language-service): add Document Symbols support for Angular templates

Description

Adds comprehensive Document Symbols support for Angular templates, enabling the Outline panel, breadcrumbs navigation, and "Go to Symbol" (Cmd+Shift+O / Ctrl+Shift+O) features to work with Angular template syntax.

Features

Block Syntax Support

  • @if, @else, @else if with expression and alias display
  • @for with track expression and context variables
  • @switch, @case, @default blocks
  • @defer, @placeholder, @loading, @error blocks with triggers
  • @let declarations

Structural Directive Support

  • *ngIf, *ngFor, *ngSwitch, *ngSwitchCase, *ngSwitchDefault
  • *ngTemplateOutlet, *ngComponentOutlet, *ngPlural, *ngPluralCase
  • Custom structural directives (shown with Class icon)

Template File Support

  • TypeScript files: Template symbols nested under (template) node in component class
  • External HTML templates: Template symbols at root level (works with templateUrl)

Multi-Component Support

  • Multiple components per file: Correctly merges template symbols into each component class
  • Non-standard naming: Works with components without "Component" suffix

Variables and References

  • Loop item variables (let item)
  • Template reference variables (#ref)
  • Context variable aliases (let i = $index)
  • Expression aliases (as alias)

SymbolKind Mappings

Template Element SymbolKind Icon
@if, @else, @switch, @case Struct 🔶
@for, @empty Array 📦
@defer, @placeholder, @loading, @error Event
HTML elements Object 🟡
Variables Variable
Structural directives Class

Configuration

angular.documentSymbols.enabled (default: true)

Enables Angular-specific document symbols.

angular.documentSymbols.showImplicitForVariables (default: false)

Shows all implicit @for loop variables ($index, $count, $first, $last, $even, $odd).

Example

Template:

@for (item of items; track item.id; let i = $index) {
  @if (item.visible; as isVisible) {
    <div #container (click)="onClick()">
      {{ item.name }}
    </div>
  }
}

Outline:

@for (item of items)  📦
├── let item
├── let i
├── @if (item.visible; as isVisible)  🔶
│   ├── as isVisible
│   └── <div>  🟡
│       └── #container

Breaking Changes

None

Related Issues

Closes #66691


AI Disclosure

This PR was developed using Claude Opus 4.6 and GPT 5.3 Max AI assistants under human orchestration and review by @kbrilla.

@pullapprove pullapprove bot requested a review from devversion January 21, 2026 19:57
@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: language-service Issues related to Angular's VS Code language service labels Jan 21, 2026
@ngbot ngbot bot modified the milestone: Backlog Jan 21, 2026
@JeanMeche JeanMeche requested a review from atscott January 21, 2026 20:40
Comment on lines 33 to 36
// Get the navigation tree from TypeScript's language service.
// This includes classes, functions, variables, and for Angular files,
// also includes template symbols via the Angular language service.
const navigationTree = languageService.getNavigationTree(scriptInfo.fileName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this only happen for TS files? What do we do about symbols for external templates (html files)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the code to make the two paths clearer:

// For HTML template files, we only need Angular template symbols (no TS symbols)
if (isHtmlFile) {
  // Returns template symbols at root level
  return convertTemplateSymbols(templateSymbols, scriptInfo);
}

// For TypeScript files, get navigation tree + merge template symbols into classes
// ... TS symbols with (template) node nested under class

Test at ivy_spec.ts:411 verifies external HTML templates work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see in the screenshots that the document symbols are separated by the provider. In that case, should we even include the TS document symbols in TS files?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, yes it is duplicating some positions, but so is html template one doing - should it also skip positions handled by native html? or make it configurable mayby?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For typescript, I think we should only include the parents of the inline template but should not provide other things from typescript (e.g. ngOnInit random various functions in the file, etc). For HTML, we can leave it as-is to keep it consistent for inline and external templates.

args.push('--suppressAngularDiagnosticCodes', suppressAngularDiagnosticCodes);
}

const documentSymbolsEnabled = config.get<boolean>('angular.documentSymbols.enabled', true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use middleware above to prevent the angular language server from processing files in projects that don't use Angular. Since the extension activates on all html and ts files, this is relatively important.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx for the hint! Added the provideDocumentSymbols middleware with isInAngularProject guard.

@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from 9b9c431 to 9e2edd7 Compare January 21, 2026 22:34
@kbrilla
Copy link
Contributor Author

kbrilla commented Jan 21, 2026

Screenshots demonstrating Document Symbols feature

TypeScript file with inline template (NxWelcome - no "Component" suffix)

Shows the component class with the (template) node containing Angular template symbols:

  • @if (title) control flow block with proper icon
  • Nested <span> and <div> elements
  • Class members (title property)
image

External HTML template (app.html)

Shows Angular symbols at root level for external templates:

  • @if (title) control flow block
  • <app-nx-welcome> component element
  • Both Angular Language Service and HTML Language Features sections working together
image

Key features visible in screenshots:

  1. ✅ Component without "Component" suffix works correctly
  2. ✅ Inline template symbols nested under class
  3. ✅ External template symbols at root level
  4. ✅ Control flow blocks (@if) with proper icons
  5. ✅ Breadcrumb navigation shows Angular hierarchy

@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch 2 times, most recently from 8b102aa to a0de8d6 Compare January 21, 2026 23:17
@kbrilla
Copy link
Contributor Author

kbrilla commented Jan 21, 2026

Additional Features/Fixes Added During Testing

While testing and addressing review feedback, I found and fixed two additional edge cases:

1. Components Without "Component" Suffix

Issue: Classes like NxWelcome (without "Component" suffix) didn't get template symbols merged correctly.

Root cause: The original merge logic relied on class name ending with "Component" as a heuristic.

Fix: Added className property to TemplateDocumentSymbol API. Template symbols are now explicitly tagged with their owning class name, enabling reliable merging regardless of naming conventions.

2. Multiple Components Per File

Issue: Files with multiple components (common in tests, storybooks, demo files) didn't work - all template symbols went into the first class.

Fix: Server now groups template symbols by className and merges each group into the correct component class.

Tests added:

  • 'provides document symbols for component without "Component" suffix'
  • 'provides document symbols for multiple components in one file'
image

Both scenarios are now documented in the PR description under "Multi-Component Support".

kbrilla added a commit to kbrilla/angular that referenced this pull request Jan 24, 2026
This commit adds infrastructure for features to request configuration from
the VS Code client using the LSP workspace/configuration protocol. This is
the preferred approach over CLI arguments because:

1. Configuration changes take effect immediately without restarting
2. Supports per-workspace and per-folder configuration
3. VS Code automatically merges settings from different scopes:
   - Default settings
   - User settings (global)
   - Workspace settings
   - Workspace folder settings
   - Language-specific settings

The new utilities include:
- getWorkspaceConfiguration: Request multiple config sections at once
- getConfigurationSection: Convenience wrapper for single sections
- flattenConfiguration: Flatten nested config to dot-notation keys

This infrastructure will be used by:
- Inlay hints feature (PR angular#66731)
- Document symbols feature (PR angular#66690)
@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from a0de8d6 to 4b96714 Compare January 24, 2026 09:08
@kbrilla
Copy link
Contributor Author

kbrilla commented Jan 24, 2026

🔗 Dependency Update

This PR now depends on #66734 (feat(language-server): add shared workspace/configuration utilities).

Changes in this update:

  • Removed CLI arguments: --disableDocumentSymbols and --showImplicitForVariables CLI args have been removed
  • Now uses workspace/configuration: Settings are fetched dynamically via LSP workspace/configuration request using the new shared utilities from feat(language-server): add shared workspace/configuration utilities #66734
  • Benefits: Users can now change document symbols settings without restarting the language server

Merge Order:

  1. First merge feat(language-server): add shared workspace/configuration utilities #66734 (workspace/configuration infrastructure)
  2. Then this PR can be merged

The PR has been rebased onto the feat/workspace-configuration branch.

@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from 4b96714 to 6fabcea Compare January 25, 2026 22:59
@kbrilla
Copy link
Contributor Author

kbrilla commented Jan 25, 2026

Update: Hybrid Approach Implemented

Following @atscott's feedback, I've implemented the hybrid approach (Option 1 + 2 from the analysis):

Changes:

  1. Default behavior (filtering): By default, only component classes with templates are shown in the outline - no TypeScript methods/properties
  2. New configuration: Added angular.documentSymbols.showTypescriptSymbols setting to opt-in to full TypeScript symbols

Default behavior (new):

MyComponent (class)
└── (template)
    ├── @if (condition)
    └── <button>

With showTypescriptSymbols: true:

MyComponent (class)
├── ngOnInit (method)
├── onClick (method)
├── someProperty (property)
└── (template)
    ├── @if (condition)
    └── <button>

Testing:

  • Updated existing test to verify default filtering behavior
  • Added test for showTypescriptSymbols: true configuration
  • Added test for multiple components in a single file
  • Added test for TypeScript files without Angular templates

All 53 specs pass.

Documentation:

  • Updated README with new setting and visual examples
  • Updated package.json with setting description

@atscott
Copy link
Contributor

atscott commented Jan 25, 2026

New configuration: Added angular.documentSymbols.showTypescriptSymbols setting to opt-in to full TypeScript symbols

sorry, that’s not really what I asked. Please remove all typescript symbols outside of the ancestors of the template property

* );
* ```
*/
export async function getConfigurationSection<T = unknown>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this used anywhere

@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from e57626d to c9b1442 Compare January 26, 2026 22:55
@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from c9b1442 to 5ca4d1d Compare February 23, 2026 22:35
@angular-robot angular-robot bot added the area: vscode-extension Issues related to the Angular Language Service VsCode extension label Feb 23, 2026
@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from 5ca4d1d to ac699af Compare February 24, 2026 12:56
@kbrilla
Copy link
Contributor Author

kbrilla commented Feb 24, 2026

I'm leaving out inlay styles support: styles: [`test{}`] as it requires more changes unrelated to document symbols, so I will post a separate PR with it not to bloat this PR #67266

…ettings grouping

Add shared workspace configuration helper utilities and move extension settings to grouped configuration sections, without inlay-hint feature options yet.
…lates

Adds comprehensive Document Symbols support for Angular templates, enabling the
Outline panel, breadcrumbs navigation, and 'Go to Symbol' (Cmd+Shift+O /
Ctrl+Shift+O) features to work with Angular template syntax.

Features:
- Block syntax: @if, @else, @for, @switch, @case, @defer, @Placeholder, etc.
- Structural directives: *ngIf, *ngFor, *ngSwitch, etc.
- HTML elements and components
- Template reference variables (#ref)
- Loop variables and aliases (let item, let i = $index)
- @let declarations

For TypeScript files with inline templates, shows component class containers
with template symbols nested inside (no TypeScript methods/properties shown).
For external HTML templates, shows template symbols at root level.

Configuration:
- angular.documentSymbols.enabled: Enable/disable feature (default: true)
- angular.documentSymbols.showImplicitForVariables: Show implicit @for variables

Depends on workspace/configuration utilities from the previous commit.

Closes angular#66691
@kbrilla kbrilla force-pushed the feat/document-symbols-templates branch from 975ac56 to 8ac4109 Compare February 24, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: language-service Issues related to Angular's VS Code language service area: vscode-extension Issues related to the Angular Language Service VsCode extension detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(language-service): add Document Symbols support for Angular templates

2 participants