sql-cli 1.73.1

SQL query tool for CSV/JSON with both interactive TUI and non-interactive CLI modes - perfect for exploration and automation
Documentation
# Shadow State Manager Expansion Plan

## Current State
The shadow state manager is currently observing state transitions but not yet serving as the source of truth. State is scattered across multiple locations.

## Goal
Transform the shadow state manager from an observer to the central state authority, eliminating duplicate state storage throughout the TUI.

## Phase 1: State Inventory (Current Reality)

### State Currently Stored in Multiple Places:

1. **AppMode (Primary mode)**
   - Buffer: `self.buffer().get_mode()` / `set_mode()`
   - Shadow State: `self.shadow_state.borrow().get_state()`
   - Status: Duplicate storage, Buffer is authoritative

2. **Search State**
   - VimSearchManager: Tracks vim search pattern and matches
   - ColumnSearchState: In AppStateContainer
   - Buffer: Search pattern strings
   - Shadow State: Observes but doesn't control
   - Status: Highly fragmented

3. **Filter State**
   - Buffer: `filter_pattern`, `fuzzy_filter_pattern`, `filter_active`, `fuzzy_filter_active`
   - AppStateContainer: FilterState
   - DataView: Internal filter state
   - Status: Triple duplication

4. **Editor/Input State**
   - Buffer: `input_text`, cursor position
   - EditorWidget: Internal state
   - Status: Partially migrated to widgets

5. **Navigation State**
   - ViewportManager: cursor position, viewport, locks
   - Buffer: Some cursor state
   - Status: Mostly in ViewportManager

6. **Query State**
   - Buffer: `last_query`, `original_query`
   - Status: Only in Buffer

## Phase 2: Migration Strategy

### Step 1: Make Shadow State the Reader Interface
Instead of immediately removing state from Buffer, first make all reads go through shadow state:

```rust
// Instead of:
if self.buffer().get_mode() == AppMode::Results {
    // ...
}

// Use:
if self.shadow_state.borrow().is_in_results_mode() {
    // ...
}
```

### Step 2: Shadow State as Write-Through Cache
Shadow state becomes authoritative but still updates Buffer for compatibility:

```rust
impl ShadowStateManager {
    pub fn switch_to_mode(&mut self, mode: AppMode, buffer: &mut Buffer) {
        self.set_state(mode.clone());
        buffer.set_mode(mode); // Keep buffer in sync for now
    }
}
```

### Step 3: Remove Buffer State
Once all reads go through shadow state, remove the duplicate storage from Buffer.

## Phase 3: Implementation Order

### Priority 1: AppMode (Simplest, High Impact)
1. Add read methods to ShadowStateManager:
   - `is_in_results_mode()`
   - `is_in_command_mode()`
   - `is_in_search_mode()`
   - `get_current_mode()`

2. Replace all `buffer().get_mode()` calls with shadow state calls

3. Make shadow state authoritative for mode changes

4. Remove mode from Buffer

### Priority 2: Search State (Most Fragmented)
1. Consolidate search state in ShadowStateManager:
   ```rust
   pub struct UnifiedSearchState {
       vim_search: Option<VimSearchPattern>,
       column_search: Option<ColumnSearchPattern>,
       fuzzy_filter: Option<String>,
       text_filter: Option<String>,
       active_search: Option<SearchType>,
   }
   ```

2. Migrate VimSearchManager functionality to shadow state

3. Move column search from AppStateContainer to shadow state

4. Unify all search operations through shadow state

### Priority 3: Filter State
1. Move all filter state to shadow state
2. Remove from Buffer and AppStateContainer
3. Keep DataView as the executor but shadow state as the controller

## Phase 4: State Access Patterns

### Current (Scattered):
```
TUI -> Buffer -> get_mode()
TUI -> VimSearchManager -> get_pattern()
TUI -> AppStateContainer -> column_search()
TUI -> ViewportManager -> get_cursor()
```

### Target (Centralized):
```
TUI -> ShadowState -> mode()
TUI -> ShadowState -> search_state()
TUI -> ShadowState -> filter_state()
TUI -> ViewportManager -> get_cursor() // Keep navigation separate
```

## Phase 5: Benefits

1. **Single Source of Truth**: No more state synchronization bugs
2. **Easier Testing**: Mock one state manager instead of many
3. **Better Debugging**: All state transitions in one place
4. **Cleaner Architecture**: Clear separation of concerns
5. **Preparation for Undo/Redo**: State snapshots become trivial

## Implementation Checklist

### Week 1: AppMode Migration
- [ ] Add comprehensive read methods to ShadowStateManager
- [ ] Create compatibility layer for gradual migration
- [ ] Replace 50% of get_mode() calls
- [ ] Replace remaining get_mode() calls
- [ ] Make shadow state authoritative for mode
- [ ] Remove mode from Buffer

### Week 2: Search State Consolidation
- [ ] Design UnifiedSearchState structure
- [ ] Migrate VimSearchManager to shadow state
- [ ] Migrate column search from AppStateContainer
- [ ] Unify search interfaces
- [ ] Remove old search state storage

### Week 3: Filter State & Cleanup
- [ ] Migrate filter state to shadow
- [ ] Remove filter state from Buffer
- [ ] Clean up AppStateContainer
- [ ] Update all tests
- [ ] Documentation update

## Success Metrics

1. **Before**: 57+ scattered set_mode() calls
2. **After**: All state changes go through shadow state
3. **Before**: State bugs from synchronization issues
4. **After**: Single source of truth eliminates sync bugs
5. **Before**: Difficult to track state transitions
6. **After**: Complete state history in shadow state

## Next Immediate Steps

1. Create a new branch: `shadow_state_expansion`
2. Start with AppMode migration (simplest, highest impact)
3. Add comprehensive read methods to ShadowStateManager
4. Begin replacing `buffer().get_mode()` calls one file at a time