Skip to main content

lsp_max/service/
state.rs

1//! Types representing the current state of the language server.
2
3use std::fmt::{self, Debug, Formatter};
4use std::sync::atomic::{AtomicI32, Ordering};
5use std::sync::Mutex;
6use std::task::{Context, Poll, Waker};
7
8use crate::max_runtime::{
9    AccessAdmissionLaw, EmptyData, Exited, Initialized, InitializedData, Initializing,
10    InitializingData, Machine, ShutDown, Uninitialized,
11};
12
13/// A list of possible states the language server can be in.
14#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
15#[repr(u8)]
16pub enum State {
17    /// Server has not received an `initialize` request.
18    Uninitialized = 0,
19    /// Server received an `initialize` request, but has not yet responded.
20    Initializing = 1,
21    /// Server received and responded success to an `initialize` request.
22    Initialized = 2,
23    /// Server received a `shutdown` request.
24    ShutDown = 3,
25    /// Server received an `exit` notification.
26    Exited = 4,
27}
28
29/// An enum representing the active typestate machine at runtime.
30#[derive(Debug)]
31pub enum StateMachine {
32    /// Uninitialized.
33    Uninitialized(Machine<AccessAdmissionLaw, Uninitialized, EmptyData>),
34    /// Initializing.
35    Initializing(Machine<AccessAdmissionLaw, Initializing, InitializingData>),
36    /// Initialized.
37    Initialized(Machine<AccessAdmissionLaw, Initialized, InitializedData>),
38    /// ShutDown.
39    ShutDown(Machine<AccessAdmissionLaw, ShutDown, EmptyData>),
40    /// Exited.
41    #[allow(dead_code)]
42    Exited(Machine<AccessAdmissionLaw, Exited, EmptyData>),
43}
44
45impl StateMachine {
46    /// Returns the corresponding State enum variant.
47    pub fn get_state(&self) -> State {
48        match self {
49            StateMachine::Uninitialized(_) => State::Uninitialized,
50            StateMachine::Initializing(_) => State::Initializing,
51            StateMachine::Initialized(_) => State::Initialized,
52            StateMachine::ShutDown(_) => State::ShutDown,
53            StateMachine::Exited(_) => State::Exited,
54        }
55    }
56}
57
58/// Thread-safe server state wrapper using a mutex.
59pub struct ServerState {
60    machine: Mutex<StateMachine>,
61    wakers: Mutex<Vec<Waker>>,
62    exit_code: AtomicI32,
63    /// The autonomic mesh manager for monitoring the workspace state.
64    pub mesh: Mutex<crate::max_runtime::AutonomicMesh>,
65    parent_pid: Mutex<Option<u32>>,
66}
67
68impl Default for ServerState {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74impl ServerState {
75    /// Creates a new `ServerState` initialized to `State::Uninitialized`.
76    pub fn new() -> Self {
77        let mut mesh = crate::max_runtime::AutonomicMesh::new();
78        let mut instance = crate::max_runtime::LspInstance::new("LSP_1");
79        instance.phase = crate::max_runtime::LspPhase::Uninitialized;
80        let r0 = crate::max_protocol::Receipt {
81            receipt_id: "rcpt-uninitialized".to_string(),
82            hash: crate::max_runtime::sha256(b"rcpt-uninitialized"),
83            prev_receipt_hash: None,
84        };
85        instance.receipts.push(r0.clone());
86        mesh.add_instance(instance);
87
88        if let Ok(mut reg) = crate::get_registry().lock() {
89            reg.receipts.insert(r0.receipt_id.clone(), r0);
90        }
91
92        ServerState {
93            machine: Mutex::new(StateMachine::Uninitialized(Machine {
94                _law: std::marker::PhantomData,
95                phase: Uninitialized,
96                data: EmptyData {
97                    client_capabilities: None,
98                    server_capabilities: None,
99                },
100            })),
101            wakers: Mutex::new(Vec::new()),
102            exit_code: AtomicI32::new(1),
103            mesh: Mutex::new(mesh),
104            parent_pid: Mutex::new(None),
105        }
106    }
107
108    /// Set the server state explicitly, discarding previous typestate data.
109    /// This is provided for compatibility and direct overrides.
110    #[allow(dead_code)]
111    pub fn set(&self, state: State) {
112        let mut lock = self.machine.lock().unwrap();
113        let next_machine = match state {
114            State::Uninitialized => {
115                StateMachine::Uninitialized(Machine::new(Uninitialized, EmptyData::default()))
116            }
117            State::Initializing => StateMachine::Initializing(Machine::new(
118                Initializing,
119                InitializingData {
120                    client_capabilities: serde_json::Value::Null,
121                },
122            )),
123            State::Initialized => StateMachine::Initialized(Machine::new(
124                Initialized,
125                InitializedData {
126                    client_capabilities: serde_json::Value::Null,
127                    server_capabilities: serde_json::Value::Null,
128                },
129            )),
130            State::ShutDown => StateMachine::ShutDown(Machine::new(ShutDown, EmptyData::default())),
131            State::Exited => StateMachine::Exited(Machine::new(Exited, EmptyData::default())),
132        };
133        *lock = next_machine;
134        if let Ok(mut reg) = crate::get_registry().lock() {
135            reg.current_state = state;
136        }
137        if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
138            instance.phase = match state {
139                crate::service::state::State::Uninitialized => {
140                    crate::max_runtime::LspPhase::Uninitialized
141                }
142                crate::service::state::State::Initializing => {
143                    crate::max_runtime::LspPhase::Initializing
144                }
145                crate::service::state::State::Initialized => {
146                    crate::max_runtime::LspPhase::Initialized
147                }
148                crate::service::state::State::ShutDown => crate::max_runtime::LspPhase::ShutDown,
149                crate::service::state::State::Exited => crate::max_runtime::LspPhase::Exited,
150            };
151        }
152
153        if state != State::Initializing {
154            let mut wakers = self.wakers.lock().unwrap();
155            let wakers_to_wake = std::mem::take(&mut *wakers);
156            for waker in wakers_to_wake {
157                waker.wake();
158            }
159        }
160    }
161
162    /// Returns the current state of the server.
163    pub fn get(&self) -> State {
164        self.machine.lock().unwrap().get_state()
165    }
166
167    /// Sets the exit code.
168    #[allow(dead_code)]
169    pub fn set_exit_code(&self, code: i32) {
170        self.exit_code.store(code, Ordering::SeqCst);
171    }
172
173    /// Gets the exit code.
174    pub fn get_exit_code(&self) -> i32 {
175        self.exit_code.load(Ordering::SeqCst)
176    }
177
178    /// Sets the parent process ID.
179    pub fn set_parent_pid(&self, pid: u32) {
180        *self.parent_pid.lock().unwrap() = Some(pid);
181    }
182
183    /// Gets the parent process ID.
184    pub fn get_parent_pid(&self) -> Option<u32> {
185        *self.parent_pid.lock().unwrap()
186    }
187
188    /// Attempts to transition from `Uninitialized` to `Initializing` phase.
189    pub fn try_initialize(&self, client_caps: serde_json::Value) -> bool {
190        let mut lock = self.machine.lock().unwrap();
191        match &*lock {
192            StateMachine::Uninitialized(_) => {
193                let old = std::mem::replace(
194                    &mut *lock,
195                    StateMachine::Exited(Machine::new(Exited, EmptyData::default())),
196                );
197                if let StateMachine::Uninitialized(m) = old {
198                    let next = m.admit_initialize(client_caps);
199                    let receipt = crate::max_runtime::TypestateKernel::receipt(&next);
200                    *lock = StateMachine::Initializing(next);
201                    if let Ok(mut reg) = crate::get_registry().lock() {
202                        reg.current_state = State::Initializing;
203                        reg.receipts
204                            .insert(receipt.receipt_id.clone(), receipt.clone());
205                    }
206                    if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
207                        instance.phase = crate::max_runtime::LspPhase::Initializing;
208                        instance.receipts.push(receipt.clone());
209                    }
210
211                    let mut mesh = self.mesh.lock().unwrap();
212                    mesh.dispatch_event(crate::max_protocol::HookEvent::StateTransition {
213                        instance_id: crate::runtime::InstanceId::from("LSP_1"),
214                        from_phase: "Uninitialized".to_string(),
215                        to_phase: "Initializing".to_string(),
216                    });
217                    mesh.dispatch_event(crate::max_protocol::HookEvent::ReceiptEmitted {
218                        instance_id: crate::runtime::InstanceId::from("LSP_1"),
219                        receipt,
220                    });
221                    let _ = mesh.verify_instance_ledger("LSP_1");
222                    true
223                } else {
224                    unreachable!()
225                }
226            }
227            _ => false,
228        }
229    }
230
231    /// Transitions from `Initializing` to `Initialized` phase.
232    pub fn transition_to_initialized(&self, server_caps: serde_json::Value) -> bool {
233        tracing::trace!("--- transition_to_initialized entered");
234        let mut lock = self.machine.lock().unwrap();
235        tracing::trace!(
236            "--- transition_to_initialized state machine before: {:?}",
237            lock.get_state()
238        );
239        let (success, receipt) = match &*lock {
240            StateMachine::Initializing(_) => {
241                let old = std::mem::replace(
242                    &mut *lock,
243                    StateMachine::Exited(Machine::new(Exited, EmptyData::default())),
244                );
245                if let StateMachine::Initializing(m) = old {
246                    let next = m.admit_initialized(server_caps);
247                    let receipt = crate::max_runtime::TypestateKernel::receipt(&next);
248                    *lock = StateMachine::Initialized(next);
249                    if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
250                        instance.phase = crate::max_runtime::LspPhase::Initialized;
251                        instance.receipts.push(receipt.clone());
252                    }
253                    (true, Some(receipt))
254                } else {
255                    unreachable!()
256                }
257            }
258            _ => (false, None),
259        };
260        tracing::trace!("--- transition_to_initialized success: {}", success);
261        if success {
262            if let Ok(mut reg) = crate::get_registry().lock() {
263                reg.current_state = State::Initialized;
264                if let Some(ref r) = receipt {
265                    reg.receipts.insert(r.receipt_id.clone(), r.clone());
266                }
267            }
268            if let Some(ref r) = receipt {
269                let mut mesh = self.mesh.lock().unwrap();
270                mesh.dispatch_event(crate::max_protocol::HookEvent::StateTransition {
271                    instance_id: crate::runtime::InstanceId::from("LSP_1"),
272                    from_phase: "Initializing".to_string(),
273                    to_phase: "Initialized".to_string(),
274                });
275                mesh.dispatch_event(crate::max_protocol::HookEvent::ReceiptEmitted {
276                    instance_id: crate::runtime::InstanceId::from("LSP_1"),
277                    receipt: r.clone(),
278                });
279                let _ = mesh.verify_instance_ledger("LSP_1");
280            }
281            let mut wakers = self.wakers.lock().unwrap();
282            let wakers_to_wake = std::mem::take(&mut *wakers);
283            for waker in wakers_to_wake {
284                waker.wake();
285            }
286        }
287        success
288    }
289
290    /// Reverts transition back to `Uninitialized` from `Initializing` phase.
291    pub fn transition_to_uninitialized(&self) -> bool {
292        let mut lock = self.machine.lock().unwrap();
293        let success = match &*lock {
294            StateMachine::Initializing(_) => {
295                *lock =
296                    StateMachine::Uninitialized(Machine::new(Uninitialized, EmptyData::default()));
297                if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
298                    instance.phase = crate::max_runtime::LspPhase::Uninitialized;
299                    instance.receipts.truncate(1);
300                }
301                true
302            }
303            _ => false,
304        };
305        if success {
306            if let Ok(mut reg) = crate::get_registry().lock() {
307                reg.current_state = State::Uninitialized;
308                reg.receipts.retain(|k, _| k == "rcpt-uninitialized");
309            }
310            let mut wakers = self.wakers.lock().unwrap();
311            let wakers_to_wake = std::mem::take(&mut *wakers);
312            for waker in wakers_to_wake {
313                waker.wake();
314            }
315        }
316        success
317    }
318
319    /// Transitions from `Initialized` to `ShutDown` phase.
320    pub fn transition_to_shutdown(&self) -> bool {
321        let mut lock = self.machine.lock().unwrap();
322        let (success, receipt) = match &*lock {
323            StateMachine::Initialized(_) => {
324                let old = std::mem::replace(
325                    &mut *lock,
326                    StateMachine::Exited(Machine::new(Exited, EmptyData::default())),
327                );
328                if let StateMachine::Initialized(m) = old {
329                    let next = m.admit_shutdown();
330                    let receipt = crate::max_runtime::TypestateKernel::receipt(&next);
331                    *lock = StateMachine::ShutDown(next);
332                    if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
333                        instance.phase = crate::max_runtime::LspPhase::ShutDown;
334                        instance.receipts.push(receipt.clone());
335                    }
336                    (true, Some(receipt))
337                } else {
338                    unreachable!()
339                }
340            }
341            _ => (false, None),
342        };
343        if success {
344            if let Ok(mut reg) = crate::get_registry().lock() {
345                reg.current_state = State::ShutDown;
346                if let Some(ref r) = receipt {
347                    reg.receipts.insert(r.receipt_id.clone(), r.clone());
348                }
349            }
350            if let Some(ref r) = receipt {
351                let mut mesh = self.mesh.lock().unwrap();
352                mesh.dispatch_event(crate::max_protocol::HookEvent::StateTransition {
353                    instance_id: crate::runtime::InstanceId::from("LSP_1"),
354                    from_phase: "Initialized".to_string(),
355                    to_phase: "ShutDown".to_string(),
356                });
357                mesh.dispatch_event(crate::max_protocol::HookEvent::ReceiptEmitted {
358                    instance_id: crate::runtime::InstanceId::from("LSP_1"),
359                    receipt: r.clone(),
360                });
361                let _ = mesh.verify_instance_ledger("LSP_1");
362            }
363        }
364        success
365    }
366
367    /// Transitions to `Exited` phase.
368    pub fn transition_to_exited(&self) -> bool {
369        let mut lock = self.machine.lock().unwrap();
370        let (res, receipt) = match &*lock {
371            StateMachine::ShutDown(_m) => {
372                let old = std::mem::replace(
373                    &mut *lock,
374                    StateMachine::Exited(Machine::new(Exited, EmptyData::default())),
375                );
376                if let StateMachine::ShutDown(m) = old {
377                    let next = m.admit_exit();
378                    let receipt = crate::max_runtime::TypestateKernel::receipt(&next);
379                    *lock = StateMachine::Exited(next);
380                    self.set_exit_code(0);
381                    if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
382                        instance.phase = crate::max_runtime::LspPhase::Exited;
383                        instance.receipts.push(receipt.clone());
384                    }
385                    (true, Some(receipt))
386                } else {
387                    unreachable!()
388                }
389            }
390            _ => {
391                *lock = StateMachine::Exited(Machine::new(Exited, EmptyData::default()));
392                self.set_exit_code(1);
393                if let Some(instance) = self.mesh.lock().unwrap().instances.get_mut("LSP_1") {
394                    instance.phase = crate::max_runtime::LspPhase::Exited;
395                }
396                (true, None)
397            }
398        };
399        if let Ok(mut reg) = crate::get_registry().lock() {
400            reg.current_state = State::Exited;
401            if let Some(ref r) = receipt {
402                reg.receipts.insert(r.receipt_id.clone(), r.clone());
403            }
404        }
405        if let Some(ref r) = receipt {
406            let mut mesh = self.mesh.lock().unwrap();
407            mesh.dispatch_event(crate::max_protocol::HookEvent::StateTransition {
408                instance_id: crate::runtime::InstanceId::from("LSP_1"),
409                from_phase: "ShutDown".to_string(),
410                to_phase: "Exited".to_string(),
411            });
412            mesh.dispatch_event(crate::max_protocol::HookEvent::ReceiptEmitted {
413                instance_id: crate::runtime::InstanceId::from("LSP_1"),
414                receipt: r.clone(),
415            });
416            let _ = mesh.verify_instance_ledger("LSP_1");
417        }
418        res
419    }
420
421    /// Polls whether the server state is currently `Initializing`.
422    /// If yes, registers the waker to be notified when state changes.
423    #[allow(dead_code)]
424    pub fn poll_initializing(&self, cx: &mut Context<'_>) -> Poll<()> {
425        let lock = self.machine.lock().unwrap();
426        match lock.get_state() {
427            State::Initializing => {
428                self.wakers.lock().unwrap().push(cx.waker().clone());
429                Poll::Pending
430            }
431            _ => Poll::Ready(()),
432        }
433    }
434}
435
436impl Debug for ServerState {
437    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
438        self.get().fmt(f)
439    }
440}