1pub use self::cancellation::{CancellationGuard, OnDrop};
4pub use self::client::{progress, Client, ClientSocket, RequestStream, ResponseSink};
5
6pub use self::pending::Pending;
7pub use self::state::{ServerState, State};
8
9use std::fmt::{self, Debug, Display, Formatter};
10use std::sync::Arc;
11use std::task::{Context, Poll};
12
13use futures::future::{self, BoxFuture, FutureExt};
14use serde_json::Value;
15use tower::Service;
16
17use crate::jsonrpc::{
18 Error, ErrorCode, FromParams, IntoResponse, Method, Request, Response, Router,
19};
20use crate::LanguageServer;
21
22pub(crate) mod layers;
23
24mod cancellation;
25mod client;
26mod pending;
27mod state;
28mod watchdog;
29
30#[derive(Clone, Debug, Eq, PartialEq)]
35#[repr(transparent)]
36pub struct ExitedError(pub i32);
37
38impl std::error::Error for ExitedError {}
39
40impl Display for ExitedError {
41 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
42 write!(f, "language server has exited with status: {}", self.0)
43 }
44}
45
46impl ExitedError {
47 pub fn code(&self) -> i32 {
49 self.0
50 }
51}
52
53#[derive(Debug)]
70pub struct LspService<S> {
71 inner: layers::CatchUnwindService<Router<S, ExitedError>>,
72 state: Arc<ServerState>,
73}
74
75impl<S: LanguageServer> LspService<S> {
76 pub fn new<F>(init: F) -> (Self, ClientSocket)
79 where
80 F: FnOnce(Client) -> S,
81 {
82 LspService::build(init).finish()
83 }
84
85 pub fn build<F>(init: F) -> LspServiceBuilder<S>
89 where
90 F: FnOnce(Client) -> S,
91 {
92 let state = Arc::new(ServerState::new());
93
94 let (client, socket) = Client::new(state.clone());
95 let inner = Router::new(init(client.clone()));
96 let pending = Arc::new(Pending::new());
97 let doc_sync = layers::DocumentSync::new();
98
99 LspServiceBuilder {
100 inner: crate::language_server::RegisterLspMethods::register_lsp_methods(
101 inner,
102 state.clone(),
103 pending.clone(),
104 client,
105 doc_sync.clone(),
106 ),
107 state,
108 pending,
109 socket,
110 doc_sync,
111 }
112 }
113
114 pub fn inner(&self) -> &S {
116 self.inner.inner().inner()
117 }
118}
119
120impl<S: LanguageServer> Service<Request> for LspService<S> {
121 type Response = Option<Response>;
122 type Error = ExitedError;
123 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
124
125 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
126 let cur_state = self.state.get();
129 if cur_state == State::Exited {
130 let code = self.state.get_exit_code();
131 return Poll::Ready(Err(ExitedError(code)));
132 }
133 if self.state.poll_initializing(cx).is_pending() {
134 return Poll::Pending;
135 }
136 tracing::trace!(
137 "LspService::poll_ready delegating to inner (state={:?})",
138 cur_state
139 );
140 self.inner.poll_ready(cx)
141 }
142
143 fn call(&mut self, req: Request) -> Self::Future {
144 if self.state.get() == State::Exited {
145 let code = self.state.get_exit_code();
146 return future::err(ExitedError(code)).boxed();
147 }
148
149 let fut = self.inner.call(req);
150
151 Box::pin(async move {
152 let response = fut.await?;
153
154 match response.as_ref().and_then(|res| res.error()) {
155 Some(Error {
156 code: ErrorCode::MethodNotFound,
157 data: Some(Value::String(m)),
158 ..
159 }) if m.starts_with("$/") => Ok(None),
160 _ => Ok(response),
161 }
162 })
163 }
164}
165
166pub struct LspServiceBuilder<S> {
170 inner: Router<S, ExitedError>,
171 state: Arc<ServerState>,
172 pending: Arc<Pending>,
173 socket: ClientSocket,
174 doc_sync: layers::DocumentSync,
175}
176
177impl<S: LanguageServer> LspServiceBuilder<S> {
178 pub fn custom_method<P, R, F>(mut self, name: &'static str, callback: F) -> Self
246 where
247 P: FromParams,
248 R: IntoResponse,
249 F: for<'a> Method<&'a S, P, R> + Clone + Send + Sync + 'static,
250 {
251 let layer = tower::ServiceBuilder::new()
252 .layer(layers::Normal::new(
253 self.state.clone(),
254 self.pending.clone(),
255 ))
256 .layer(self.doc_sync.clone())
257 .into_inner();
258 self.inner.method(name, callback, layer);
259 self
260 }
261
262 pub fn fallback_method<F>(mut self, f: F) -> Self
272 where
273 F: Fn(
274 crate::jsonrpc::Request,
275 ) -> futures::future::BoxFuture<
276 'static,
277 Result<Option<crate::jsonrpc::Response>, ExitedError>,
278 > + Send
279 + Sync
280 + 'static,
281 {
282 self.inner.set_fallback(f);
283 self
284 }
285
286 pub fn finish(self) -> (LspService<S>, ClientSocket) {
289 let LspServiceBuilder {
290 inner,
291 state,
292 socket,
293 ..
294 } = self;
295
296 let inner = tower::Layer::layer(&layers::CatchUnwind, inner);
297
298 (LspService { inner, state }, socket)
299 }
300}
301
302impl<S: Debug> Debug for LspServiceBuilder<S> {
303 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
304 f.debug_struct("LspServiceBuilder")
305 .field("inner", &self.inner)
306 .finish_non_exhaustive()
307 }
308}
309
310#[allow(dead_code)]
311fn handle_mesh_rpc(
312 state: &ServerState,
313 method: &str,
314 params: Option<Value>,
315) -> Result<Value, String> {
316 let mut registry = crate::get_registry()
317 .lock()
318 .unwrap_or_else(|p| p.into_inner());
319 crate::update_diagnostics(&mut registry);
320
321 let mut mesh = state.mesh.lock().unwrap_or_else(|p| p.into_inner());
322
323 let instance_id = "LSP_1";
325 if !mesh.instances.contains_key(instance_id) {
326 mesh.add_instance(crate::max_runtime::LspInstance::new(instance_id));
327 }
328 {
329 let instance = match mesh.instances.get_mut(instance_id) {
330 Some(inst) => inst,
331 None => return Err("LSP_1 instance not found".to_string()),
332 };
333 instance.phase = match registry.current_state {
334 State::Uninitialized => crate::max_runtime::LspPhase::Uninitialized,
335 State::Initializing => crate::max_runtime::LspPhase::Initializing,
336 State::Initialized => crate::max_runtime::LspPhase::Initialized,
337 State::ShutDown => crate::max_runtime::LspPhase::ShutDown,
338 State::Exited => crate::max_runtime::LspPhase::Exited,
339 };
340 instance.diagnostics = registry.diagnostics.values().cloned().collect();
341 let mut sorted_receipts: Vec<_> = registry.receipts.values().cloned().collect();
342 sorted_receipts.sort_by_key(|r| {
343 if r.receipt_id == "rcpt-uninitialized" {
344 0
345 } else if r
346 .receipt_id
347 .starts_with("rcpt-uninitialized-to-initializing:")
348 {
349 1
350 } else if r
351 .receipt_id
352 .starts_with("rcpt-initializing-to-initialized:")
353 {
354 2
355 } else if r.receipt_id == "rcpt-initialized-to-shutdown" {
356 3
357 } else if r.receipt_id == "rcpt-shutdown-to-exited" {
358 4
359 } else {
360 5
361 }
362 });
363 instance.receipts = sorted_receipts;
364 }
365
366 let result = mesh.dispatch_rpc(instance_id, method, params.unwrap_or(Value::Null))?;
368
369 if let Some(instance) = mesh.instances.get(instance_id) {
371 let mesh_diagnostic_ids: std::collections::HashSet<String> = instance
372 .diagnostics
373 .iter()
374 .map(|d| d.diagnostic_id.clone())
375 .collect();
376 for id in registry
377 .diagnostics
378 .keys()
379 .cloned()
380 .collect::<Vec<String>>()
381 {
382 if !mesh_diagnostic_ids.contains(&id) {
383 registry.diagnostics.remove(&id);
384 registry.cleared_diagnostics.insert(id);
385 }
386 }
387 for d in &instance.diagnostics {
388 registry
389 .diagnostics
390 .insert(d.diagnostic_id.clone(), d.clone());
391 }
392 for r in &instance.receipts {
393 registry.receipts.insert(r.receipt_id.clone(), r.clone());
394 }
395 }
396
397 if method == "max/snapshot" {
399 let snapshot_id: crate::max_protocol::SnapshotId =
400 serde_json::from_value(result.clone())
401 .map_err(|e| format!("Invalid snapshot ID: {}", e))?;
402
403 let capability_vector = crate::max_protocol::MaxCapabilityVector {
404 client: registry.client_capabilities.clone().unwrap_or_default(),
405 server: registry.server_capabilities.clone().unwrap_or_default(),
406 negotiated: serde_json::json!({
407 "conformance": "maximal",
408 "law_framework": "v1"
409 }),
410 experimental: serde_json::json!({}),
411 gaps: vec![],
412 };
413
414 let diagnostics = registry.diagnostics.values().cloned().collect();
415 let actions = registry.repair_plans.values().flatten().cloned().collect();
416
417 let score = if registry.diagnostics.is_empty() {
418 100.0
419 } else {
420 let severity_penalty: f64 = registry
421 .diagnostics
422 .values()
423 .map(|d| match d.lsp.severity {
424 Some(crate::lsp_types_max::DiagnosticSeverity::ERROR) => 30.0,
425 Some(crate::lsp_types_max::DiagnosticSeverity::WARNING) => 15.0,
426 _ => 5.0,
427 })
428 .sum();
429 (100.0 - severity_penalty).max(0.0)
430 };
431
432 let (refused_diags, admitted_diags): (Vec<_>, Vec<_>) =
433 registry.diagnostics.values().partition(|d| {
434 matches!(
435 d.lsp.severity,
436 Some(crate::lsp_types_max::DiagnosticSeverity::ERROR)
437 )
438 });
439 let refused: Vec<max_protocol::LawAxis> =
440 refused_diags.iter().map(|d| d.law_axis.clone()).collect();
441 let admitted: Vec<max_protocol::LawAxis> =
442 admitted_diags.iter().map(|d| d.law_axis.clone()).collect();
443 let derived_score = if admitted.is_empty() && refused.is_empty() {
444 None
445 } else {
446 let total = (admitted.len() + refused.len()) as f64;
447 Some(100.0 * admitted.len() as f64 / total)
448 };
449 let _ = score; let conformance_vector = crate::max_protocol::ConformanceVector {
451 admitted,
452 refused,
453 unknown: Vec::new(),
454 score: derived_score,
455 strict_mode: true,
456 process_quality: None,
457 ..Default::default()
458 };
459
460 let receipts = registry.receipts.values().cloned().collect();
461
462 let record = crate::SnapshotRecord {
463 id: snapshot_id.clone(),
464 capability_vector,
465 diagnostics,
466 actions,
467 conformance_vector,
468 receipts,
469 };
470
471 registry.snapshots.insert(snapshot_id.0.clone(), record);
472 }
473
474 Ok(result)
475}
476
477#[cfg(test)]
478mod tests;
479
480#[cfg(test)]
481mod unit_tests {
482 use super::*;
483
484 #[test]
485 fn exited_error_code() {
486 let e = ExitedError(42);
487 assert_eq!(e.code(), 42);
488 }
489
490 #[test]
491 fn exited_error_display() {
492 let e = ExitedError(1);
493 assert!(e.to_string().contains("1"));
494 }
495
496 #[test]
497 fn exited_error_ne() {
498 assert_ne!(ExitedError(0), ExitedError(1));
499 }
500}