Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions DANCE_INVESTIGATION_INDEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Dance VS Code Extension - Performance Investigation Report

## Overview
This directory contains a comprehensive analysis of performance issues in the Dance VS Code extension, including:
- Heavy lag on keystrokes after usage
- Cursor jumping back and forth
- Need to restart VS Code to fix issues

## Document Guide

### 1. **dance_performance_analysis.md** (14 KB)
**Comprehensive detailed analysis with full explanations**

Contents:
- Executive summary
- 8 detailed issues with explanations
- Root causes for each symptom
- Symptom-to-cause mapping
- Testing recommendations
- Fix priority list

Best for: Understanding the full context and all issues

### 2. **dance_issues_summary.txt** (5.2 KB)
**Quick reference with absolute file paths and line numbers**

Contents:
- Critical bugs with exact locations
- Race conditions identified
- Events that accumulate
- Disposal chain status
- Quick lookup of file paths

Best for: Quick reference while fixing

### 3. **dance_specific_code_issues.txt** (16 KB)
**Detailed code snippets with line-by-line analysis**

Contents:
- Issue #1: Extension.dispose() missing resource cleanup
- Issue #2: Array never cleared bug
- Issue #3: Selection update race condition
- Issue #4: Mode change setTimeout race condition
- Issue #5: Multiple decoration update pathways
- Issue #6: Stale document reference
- Issue #7: Disposal during async setup
- Issue #8: Error message subscriptions not cleaned
- Summary table: Disposal status for all components

Best for: Code review and understanding specific bugs

## Critical Issues Summary

### CRITICAL (Causes Immediate Symptoms)
1. **Extension.dispose() not disposing core resources**
- File: `/home/enrico/projects/dance/src/state/extension.ts`
- Lines: 213-222
- Missing: `this.editors.dispose()`, `this.recorder.dispose()`, `this._subscriptions cleanup`
- Impact: Event listener accumulation causing lag

2. **Array never cleared in Editors**
- File: `/home/enrico/projects/dance/src/state/editors.ts`
- Line: 690
- Bug: `this._lastRemovedEditorStates.length === 0;` (should be `=`)
- Impact: Memory leak from unreleased editor states

3. **Race condition in selection updates**
- File: `/home/enrico/projects/dance/src/api/selections.ts`
- Lines: 42-50
- Issue: Selection set triggers event → updates decorations → conflicts with reveal()
- Impact: Cursor jumping

### HIGH (Contributes to Issues)
4. Mode change setTimeout race conditions
5. Multiple decoration update pathways
6. Error message subscription cleanup

### MEDIUM (Potential Issues)
7. Stale document references
8. Async initialization race conditions

## Absolute File Paths (All Issues)

```
/home/enrico/projects/dance/src/state/extension.ts (Lines: 213-222, 362-389)
/home/enrico/projects/dance/src/state/editors.ts (Lines: 267-269, 277-279, 322-389, 545-554, 690)
/home/enrico/projects/dance/src/api/selections.ts (Lines: 42-50)
/home/enrico/projects/dance/src/api/modes.ts (Lines: 41-62)
/home/enrico/projects/dance/src/api/context.ts (Lines: 666-828)
/home/enrico/projects/dance/src/state/modes.ts (Lines: 244-248)
```

## Quick Fix Priority

### Immediate (CRITICAL)
1. Fix Extension.dispose() in `/home/enrico/projects/dance/src/state/extension.ts` lines 213-222
- Add disposal calls for all major components

2. Fix array clear bug in `/home/enrico/projects/dance/src/state/editors.ts` line 690
- Change `===` to `=`

### Short Term (HIGH)
3. Refactor selection updates to be async or debounced
4. Review setTimeout patterns in mode changes
5. Fix error message subscription cleanup

### Medium Term (MEDIUM)
6. Improve document reference stability in context conversions
7. Synchronize decoration update pathways

## Testing Recommendations

After fixes, verify:
1. Event listener count remains constant across reload cycles
2. Memory usage stable during extended usage
3. No cursor jumping during rapid selection changes
4. Mode switching doesn't cause selection conflicts
5. Error messages don't accumulate handlers

## How to Use These Documents

1. **Start with:** `dance_issues_summary.txt` for quick overview
2. **Then read:** `dance_performance_analysis.md` for full understanding
3. **When coding:** Reference `dance_specific_code_issues.txt` for exact locations

## Key Findings

### Root Cause #1: Memory Leaks (Event Listener Accumulation)
- **Why it happens:** Extension.dispose() doesn't call cleanup on major components
- **How it manifests:** Lag increases with each feature use
- **Why restart fixes it:** VS Code unloads all JS context

### Root Cause #2: Race Conditions (Cursor Jumping)
- **Why it happens:** Multiple independent pathways update selections/decorations
- **How it manifests:** Cursor appears to jump between keystrokes
- **Why restart fixes it:** New instance has fresh state

### Root Cause #3: Data Corruption (Array Not Cleared)
- **Why it happens:** Single character typo: `===` instead of `=`
- **How it manifests:** Memory grows unbounded, GC pressure
- **Why restart fixes it:** Old data discarded on reload

## Recommended Reading Order

### For Quick Fix
1. `dance_issues_summary.txt` - Get line numbers
2. `dance_specific_code_issues.txt` - See exact code to fix

### For Complete Understanding
1. `dance_performance_analysis.md` - Executive summary
2. `dance_issues_summary.txt` - Event accumulation details
3. `dance_specific_code_issues.txt` - Code-level analysis

### For Root Cause Analysis
1. Read Section "Symptom-to-Root-Cause Mapping" in `dance_performance_analysis.md`
2. Review CRITICAL issues in `dance_issues_summary.txt`
3. Deep dive with `dance_specific_code_issues.txt` for each issue

## Additional Notes

- All issues are reproducible
- All issues are fixable with code changes
- The main fix (Extension.dispose()) is relatively simple
- The cursor jumping requires race condition analysis/fixes
- Memory leak fix is trivial (one character change)

---

**Report Generated:** October 26, 2025
**Analysis Tool:** Claude Code - Anthropic
**Repository:** `/home/enrico/projects/dance`
**Status:** Thorough Investigation Complete
131 changes: 131 additions & 0 deletions dance_issues_summary.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
DANCE EXTENSION PERFORMANCE ISSUES - QUICK REFERENCE

================================================================================
ABSOLUTE PATH REFERENCES
================================================================================

1. CRITICAL BUG - Extension Not Disposing Resources
File: /home/enrico/projects/dance/src/state/extension.ts
Lines: 213-222 (dispose method)
Issue: Missing calls to this.editors.dispose(), this.recorder.dispose(),
and this._subscriptions.splice(0).forEach(d => d.dispose())

What should be disposed:
- Line 68: public readonly editors = new Editors(this);
- Line 53: public readonly registers = new Registers(this);
- Line 58: public readonly modes = new Modes(this);
- Line 63: public readonly recorder: Recorder;
- Line 27: private readonly _subscriptions: vscode.Disposable[] = [];

2. CRITICAL BUG - Array Never Cleared
File: /home/enrico/projects/dance/src/state/editors.ts
Line: 690
Issue: this._lastRemovedEditorStates.length === 0;
Fix: this._lastRemovedEditorStates.length = 0;

3. RACE CONDITION - Selection Update Conflicts
File: /home/enrico/projects/dance/src/api/selections.ts
Lines: 42-50 (set function)
Issue: Setting editor.selections triggers onDidChangeTextEditorSelection
which updates decorations before reveal() completes

Current flow:
- Line 45: context.selections = selections; (triggers event)
- Line 46: reveal(selections[0], context);
- Line 47: vscode.commands.executeCommand("editor.action.wordHighlight.trigger");

4. RACE CONDITION - Async Mode Change Setup
File: /home/enrico/projects/dance/src/api/modes.ts
Lines: 41-62
Issue: setTimeout(..., 0) creates race condition for event listener registration
Line 36: await context.switchToMode(mode);
Line 41-62: setTimeout setup of event listeners

5. MULTIPLE DECORATION UPDATE PATHWAYS
File: /home/enrico/projects/dance/src/state/editors.ts
Lines: 267-269, 277-279, 322-389
Issue: Three independent paths can trigger decoration updates:
- notifyDidChangeTextEditorSelection() at line 267
- notifyDidChangeTextEditorVisibleRanges() at line 277
- Mode change handlers at lines 174-196

6. INSUFFICIENT ERROR MESSAGE CLEANUP
File: /home/enrico/projects/dance/src/state/extension.ts
Lines: 362-389 (showDismissibleErrorMessage method)
Issue: Event subscriptions created for error display aren't cleaned
on extension deactivation

7. STALE DOCUMENT REFERENCE
File: /home/enrico/projects/dance/src/api/context.ts
Lines: 666-828 (selectionsToCharacterMode/selectionsFromCharacterMode)
Issue: Document parameter can become stale if context changes during conversion
Line 708: document = Context.current.document;

8. UNTRACKED INITIALIZATION
File: /home/enrico/projects/dance/src/state/editors.ts
Lines: 545-554
Issue: queueMicrotask initialization happens after constructor
Events can fire before setup completes

================================================================================
EVENTS THAT ACCUMULATE WITHOUT PROPER DISPOSAL
================================================================================

From Editors class (never disposed via Extension.dispose):
- onDidChangeActiveTextEditor (line 532)
- onDidChangeTextEditorSelection (line 534)
- onDidChangeTextEditorVisibleRanges (line 536)
- onDidChangeVisibleTextEditors (line 538)
- onDidOpenTextDocument (line 540)
- onDidCloseTextDocument (line 542)

From Recorder class (never disposed via Extension.dispose):
- onDidChangeActiveTextEditor (line 65)
- onDidChangeTextEditorSelection (line 66)
- onDidChangeTextDocument (line 67)
- onModeDidChange (line 68)

From Extension._subscriptions (never disposed):
- onDidChangeConfiguration (line 179)
- Multiple command descriptors (line 190)
- RegistersView (line 194)
- onDidLoadTreeSitter (line 197)

================================================================================
IMPACT SUMMARY
================================================================================

Symptom: Heavy lag on keystrokes after usage
- Each keystroke → onDidChangeTextEditorSelection fires
- Multiple handlers registered → compound processing
- Memory accumulation → GC pressure

Symptom: Cursor jumping back and forth
- Selection update → triggers event → updates decorations → event fires again
- Three separate decoration update pathways conflict
- setTimeout race conditions in mode changes

Symptom: Requires restart to fix
- Extension deactivation doesn't clean resources
- Reload gets clean state
- Indicates accumulation issue, not logic bug

================================================================================
DISPOSAL CHAIN STATUS
================================================================================

What IS disposed:
✓ this.statusBar (line 221)
✓ this._autoDisposables (line 217)
✓ this._cancellationTokenSource (lines 214-215)
✓ Editors._subscriptions (line 558)
✓ Recorder._subscriptions (line 77)

What IS NOT disposed:
✗ this.editors
✗ this.recorder
✗ this.registers
✗ this.modes
✗ this._subscriptions (Extension's array)

================================================================================
Loading
Loading