sql_cli/state/
dispatcher.rs1use crate::buffer::Buffer;
4use crate::state::coordinator::StateCoordinator;
5use crate::state::events::{StateChange, StateEvent};
6use std::cell::RefCell;
7use std::rc::{Rc, Weak};
8use tracing::{debug, info, warn};
9
10pub trait StateSubscriber {
12 fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer);
14
15 fn name(&self) -> &str;
17}
18
19#[derive(Debug)]
21pub enum SubscriberCommand {
22 None,
23 ClearSearch,
24 UpdateViewport,
25 RefreshDisplay,
26}
27
28pub struct StateDispatcher {
30 buffer: Weak<RefCell<Buffer>>,
32
33 subscribers: Vec<Box<dyn StateSubscriber>>,
35
36 event_history: Vec<StateEvent>,
38
39 max_history: usize,
41}
42
43impl StateDispatcher {
44 pub fn new() -> Self {
45 Self {
46 buffer: Weak::new(),
47 subscribers: Vec::new(),
48 event_history: Vec::new(),
49 max_history: 100,
50 }
51 }
52
53 pub fn set_buffer(&mut self, buffer: Rc<RefCell<Buffer>>) {
55 self.buffer = Rc::downgrade(&buffer);
56 }
57
58 pub fn subscribe(&mut self, subscriber: Box<dyn StateSubscriber>) {
60 info!("StateDispatcher: Adding subscriber: {}", subscriber.name());
61 self.subscribers.push(subscriber);
62 }
63
64 pub fn dispatch(&mut self, event: StateEvent) {
66 debug!("StateDispatcher: Dispatching event: {:?}", event);
67
68 self.event_history.push(event.clone());
70 if self.event_history.len() > self.max_history {
71 self.event_history.remove(0);
72 }
73
74 let buffer_rc = match self.buffer.upgrade() {
76 Some(b) => b,
77 None => {
78 warn!("StateDispatcher: Buffer reference lost!");
79 return;
80 }
81 };
82
83 let change = {
85 let buffer = buffer_rc.borrow();
86 buffer.process_event(&event)
87 };
88
89 if !matches!(
91 change,
92 StateChange {
93 mode: None,
94 search_state: None,
95 filter_state: None,
96 fuzzy_filter_state: None,
97 clear_all_searches: false
98 }
99 ) {
100 info!("StateDispatcher: Applying state change: {:?}", change);
101 buffer_rc.borrow_mut().apply_change(change);
102 }
103
104 let buffer = buffer_rc.borrow();
106 for subscriber in &mut self.subscribers {
107 debug!(
108 "StateDispatcher: Notifying subscriber: {}",
109 subscriber.name()
110 );
111 subscriber.on_state_event(&event, &buffer);
112 }
113 }
114
115 pub fn dispatch_mode_change(
117 &mut self,
118 from: crate::buffer::AppMode,
119 to: crate::buffer::AppMode,
120 ) {
121 self.dispatch(StateEvent::ModeChanged { from, to });
122 }
123
124 pub fn dispatch_search_start(
126 &mut self,
127 search_type: crate::ui::state::shadow_state::SearchType,
128 ) {
129 self.dispatch(StateEvent::SearchStarted { search_type });
130 }
131
132 pub fn dispatch_search_end(&mut self, search_type: crate::ui::state::shadow_state::SearchType) {
134 self.dispatch(StateEvent::SearchEnded { search_type });
135 }
136
137 pub fn get_event_history(&self) -> &[StateEvent] {
139 &self.event_history
140 }
141}
142
143pub struct VimSearchSubscriber {
145 active: bool,
146}
147
148impl VimSearchSubscriber {
149 pub fn new() -> Self {
150 Self { active: false }
151 }
152}
153
154impl StateSubscriber for VimSearchSubscriber {
155 fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer) {
156 match event {
157 StateEvent::SearchStarted { search_type } => {
158 if matches!(search_type, crate::ui::state::shadow_state::SearchType::Vim) {
159 info!("VimSearchSubscriber: Activating for vim search");
160 self.active = true;
161 }
162 }
163 StateEvent::SearchEnded { search_type } => {
164 if matches!(search_type, crate::ui::state::shadow_state::SearchType::Vim) {
165 info!("VimSearchSubscriber: Deactivating - search ended");
166 self.active = false;
167 }
168 }
169 StateEvent::ModeChanged { from: _, to } => {
170 if *to == crate::buffer::AppMode::Results && buffer.search_state.pattern.is_empty()
172 {
173 if self.active {
174 info!("VimSearchSubscriber: Deactivating - mode changed to Results with empty search");
175 }
176 self.active = false;
177 }
178 }
179 _ => {}
180 }
181 }
182
183 fn name(&self) -> &str {
184 "VimSearchSubscriber"
185 }
186}