Skip to main content

lsp_max/
lib.rs

1//! Language Server Protocol (LSP) server abstraction for [Tower].
2//!
3//! [Tower]: https://github.com/tower-rs/tower
4//!
5//! # Example
6//!
7//! ```rust,no_run
8//! use lsp_max::jsonrpc::Result;
9//! use lsp_max::lsp_types_max::*;
10//! use lsp_max::{Client, LanguageServer, LspService, Server};
11//!
12//! #[derive(Debug)]
13//! struct Backend {
14//!     client: Client,
15//! }
16//!
17//! #[lsp_max::async_trait]
18//! impl LanguageServer for Backend {
19//!     async fn initialize(&self, _: InitializeParams) -> Result<InitializeResult> {
20//!         Ok(InitializeResult {
21//!             capabilities: ServerCapabilities {
22//!                 hover_provider: Some(HoverProviderCapability::Simple(true)),
23//!                 completion_provider: Some(CompletionOptions::default()),
24//!                 ..Default::default()
25//!             },
26//!             ..Default::default()
27//!         })
28//!     }
29//!
30//!     async fn initialized(&self, _: InitializedParams) {
31//!         self.client
32//!             .log_message(MessageType::INFO, "server initialized!")
33//!             .await;
34//!     }
35//!
36//!     async fn shutdown(&self) -> Result<()> {
37//!         Ok(())
38//!     }
39//!
40//!     async fn completion(&self, _: CompletionParams) -> Result<Option<CompletionResponse>> {
41//!         Ok(Some(CompletionResponse::Array(vec![
42//!             CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()),
43//!             CompletionItem::new_simple("Bye".to_string(), "More detail".to_string())
44//!         ])))
45//!     }
46//!
47//!     async fn hover(&self, _: HoverParams) -> Result<Option<Hover>> {
48//!         Ok(Some(Hover {
49//!             contents: HoverContents::Scalar(
50//!                 MarkedString::String("You're hovering!".to_string())
51//!             ),
52//!             range: None
53//!         }))
54//!     }
55//! }
56//!
57//! #[tokio::main]
58//! async fn main() {
59//! #   tracing_subscriber::fmt().init();
60//! #
61//! #   #[cfg(all(feature = "runtime-agnostic", not(feature = "runtime-tokio")))]
62//! #   use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
63//! #   use std::io::Cursor;
64//!     let stdin = tokio::io::stdin();
65//!     let stdout = tokio::io::stdout();
66//! #   let message = r#"{"jsonrpc":"2.0","method":"exit"}"#;
67//! #   let (stdin, stdout) = (Cursor::new(format!("Content-Length: {}\r\n\r\n{}", message.len(), message).into_bytes()), Cursor::new(Vec::new()));
68//! #   #[cfg(all(feature = "runtime-agnostic", not(feature = "runtime-tokio")))]
69//! #   let (stdin, stdout) = (stdin.compat(), stdout.compat_write());
70//!
71//!     let (service, socket) = LspService::new(|client| Backend { client });
72//!     let _ = Server::new(stdin, stdout, socket).serve(service).await;
73//! }
74//! ```
75
76#![deny(missing_debug_implementations)]
77#![deny(missing_docs)]
78#![deny(unsafe_code)]
79// stdout is the LSP frame channel — a `print!`/`println!` anywhere in this
80// library interleaves log text ahead of the Content-Length header and corrupts
81// framing (the `Header must provide a Content-Length property` failure mode).
82// This is type-invisible: a write to stdout is type-identical whether it means
83// "log" or "protocol frame". Denying the lint makes the second writer to stdout
84// UNCONSTRUCTABLE — it cannot compile. Route all diagnostics through `tracing`
85// (stderr via the subscriber) or `eprintln!`; the frame writer uses the async
86// codec, not a print macro, so it is unaffected.
87#![deny(clippy::print_stdout)]
88#![allow(clippy::mutable_key_type)]
89#![allow(clippy::new_without_default)]
90#![allow(clippy::module_inception)]
91#![allow(deprecated)]
92#![allow(unused_imports)]
93
94pub extern crate lsp_types_max;
95pub use lsp_types_max as lsp_types;
96
97#[allow(missing_docs)]
98#[allow(missing_debug_implementations)]
99pub mod closure_channel;
100
101#[allow(missing_docs)]
102#[allow(missing_debug_implementations)]
103pub mod agent;
104
105#[allow(missing_docs)]
106#[allow(missing_debug_implementations)]
107pub mod runtime;
108
109#[allow(missing_docs)]
110#[allow(missing_debug_implementations)]
111pub mod andon;
112
113#[allow(missing_docs)]
114#[allow(missing_debug_implementations)]
115pub mod live;
116
117#[allow(missing_docs)]
118#[allow(missing_debug_implementations)]
119pub mod client;
120
121pub use agent as max_agent;
122pub use andon as max_andon;
123pub use live as max_live;
124pub use runtime as max_runtime;
125
126pub extern crate lsp_max_protocol as max_protocol;
127
128/// A re-export of [`async-trait`](https://docs.rs/async-trait) for convenience.
129pub use async_trait::async_trait;
130
131pub use self::service::progress::{
132    Bounded, Cancellable, NotCancellable, OngoingProgress, Progress, Unbounded,
133};
134pub use self::service::{Client, ClientSocket, ExitedError, LspService, LspServiceBuilder};
135pub use self::transport::{Loopback, Server};
136
137use lsp_types_max::*;
138
139use self::jsonrpc::{Error, Result};
140
141/// Semantic parsing and incremental AST generation via the lsp-max-ast layer.
142pub mod ast {
143    pub use lsp_max_ast::*;
144}
145
146pub mod jsonrpc;
147
148mod codec;
149/// Module containing the `LanguageServer` trait and its macro-generated router.
150pub mod language_server;
151pub mod service;
152mod transport;
153pub use language_server::LanguageServer;
154
155pub use lsp_max_lsif as lsif;
156
157mod composition;
158pub use composition::{ComposedServer, CompositionState, SharedCompositionState, SourceHealth};
159
160/// Module containing diagnostic update and management functions.
161pub mod diagnostics;
162/// Module containing validation gate logic.
163pub mod gate;
164/// Module containing helper functions to apply workspace and text edits.
165pub mod workspace_edit;
166
167/// Bridge trait eliminating hand-rolled regex-pattern LSP server boilerplate.
168pub mod rule_pack_server;
169pub use rule_pack_server::{
170    glob_matches, ClassifiedFindings, Finding, Rule, RulePack, RulePackServer,
171    ValidatedRulePackSet, WorkspaceIndex,
172};
173
174/// LSP 3.18 and LSIF protocol coverage matrices.
175pub mod coverage;
176
177/// First-class framework primitives: `DocumentStore`, `DiagnosticSink`, `debounce`.
178pub mod primitives;
179
180/// TPOT2-style breed pipeline type system for automated wasm4pm cognitive breed search.
181pub mod pipeline;
182
183pub(crate) use diagnostics::update_diagnostics;
184
185/// Returns current UTC time as an RFC-3339 string (seconds precision), using only std::time.
186pub(crate) fn rfc3339_now() -> String {
187    use std::time::{SystemTime, UNIX_EPOCH};
188    let mut s = SystemTime::now()
189        .duration_since(UNIX_EPOCH)
190        .unwrap_or_default()
191        .as_secs();
192    let sec = s % 60;
193    s /= 60;
194    let min = s % 60;
195    s /= 60;
196    let hour = s % 24;
197    s /= 24;
198    let mut year = 1970u64;
199    loop {
200        let leap = if (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
201            1
202        } else {
203            0
204        };
205        let days = 365 + leap;
206        if s < days {
207            break;
208        }
209        s -= days;
210        year += 1;
211    }
212    let leap = if (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
213        1
214    } else {
215        0
216    };
217    const MONTH_DAYS: [u64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
218    let mut month = 1u64;
219    for (i, &d) in MONTH_DAYS.iter().enumerate() {
220        let d = if i == 1 { d + leap } else { d };
221        if s < d {
222            month = i as u64 + 1;
223            break;
224        }
225        s -= d;
226    }
227    let day = s + 1;
228    format!("{year:04}-{month:02}-{day:02}T{hour:02}:{min:02}:{sec:02}Z")
229}
230
231fn _assert_object_safe() {
232    fn assert_impl<T: LanguageServer>() {}
233    assert_impl::<Box<dyn LanguageServer>>();
234}
235
236use std::collections::HashMap;
237use std::sync::{Mutex, OnceLock};
238
239/// Record representing a snapshot of the server state.
240#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
241#[allow(dead_code)]
242pub struct SnapshotRecord {
243    /// The unique identifier of this snapshot.
244    #[allow(dead_code)]
245    pub id: max_protocol::SnapshotId,
246    /// The capability vector at the time of snapshot.
247    pub capability_vector: max_protocol::MaxCapabilityVector,
248    /// Diagnostics present during the snapshot.
249    pub diagnostics: Vec<max_protocol::MaxDiagnostic>,
250    /// Actions generated/available for the snapshot.
251    pub actions: Vec<max_protocol::MaxCodeAction>,
252    /// The conformance vector of the server.
253    pub conformance_vector: max_protocol::ConformanceVector,
254    /// Receipts associated with the snapshot.
255    pub receipts: Vec<max_protocol::Receipt>,
256}
257
258/// Registry storing server diagnostic and capability state.
259#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
260pub struct ServerRegistry {
261    /// Client capabilities negotiated during initialization.
262    pub client_capabilities: Option<ClientCapabilities>,
263    /// Server capabilities returned during initialization.
264    pub server_capabilities: Option<ServerCapabilities>,
265    /// Table mapping diagnostic IDs to diagnostics.
266    pub diagnostics: HashMap<String, max_protocol::MaxDiagnostic>,
267    /// Table mapping file/resource paths to lists of repair code actions.
268    pub repair_plans: HashMap<String, Vec<max_protocol::MaxCodeAction>>,
269    /// Autonomic capability gates.
270    pub gates: HashMap<String, bool>,
271    /// Table mapping receipt IDs to receipt data.
272    pub receipts: HashMap<String, max_protocol::Receipt>,
273    /// Table mapping snapshot IDs to snapshot records.
274    pub snapshots: HashMap<String, SnapshotRecord>,
275    /// Table of cleared diagnostic IDs.
276    #[serde(default)]
277    pub cleared_diagnostics: std::collections::HashSet<String>,
278    /// Current server lifecycle phase state.
279    pub current_state: crate::service::State,
280    /// Table mapping document URIs to their current versions.
281    pub document_versions: HashMap<url::Url, i32>,
282    /// Root path for gate and diagnostic file resolution.
283    pub root_path: std::path::PathBuf,
284    /// Monotonically-increasing counter incremented on every release actuation.
285    /// Serves as a since-cursor for `max/conformanceDelta` polling.
286    #[serde(default)]
287    pub action_seq: u64,
288    /// Ring-buffer of recent conformance score changes keyed by sequence number.
289    /// The single authoritative conformance-delta store; replaces the former MESH global.
290    #[serde(default)]
291    pub conformance_delta_log:
292        std::collections::VecDeque<crate::max_runtime::ConformanceDeltaEntry>,
293}
294
295/// Global static instance of the server registry.
296pub static REGISTRY: OnceLock<Mutex<ServerRegistry>> = OnceLock::new();
297
298/// Global static instance of the autonomic mesh, used by RPC bridge methods.
299pub static MESH: OnceLock<Mutex<crate::max_runtime::AutonomicMesh>> = OnceLock::new();
300
301/// Retrieves a reference to the global server registry.
302pub fn get_registry() -> &'static Mutex<ServerRegistry> {
303    REGISTRY.get_or_init(|| {
304        let diagnostics = HashMap::new();
305        let repair_plans = HashMap::new();
306        let mut gates = HashMap::new();
307        gates.insert("gate-state-check".to_string(), false);
308
309        Mutex::new(ServerRegistry {
310            client_capabilities: None,
311            server_capabilities: None,
312            diagnostics,
313            repair_plans,
314            gates,
315            receipts: HashMap::new(),
316            snapshots: HashMap::new(),
317            cleared_diagnostics: std::collections::HashSet::new(),
318            current_state: crate::service::State::Uninitialized,
319            document_versions: HashMap::new(),
320            root_path: std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")),
321            action_seq: 0,
322            conformance_delta_log: std::collections::VecDeque::new(),
323        })
324    })
325}
326
327pub(crate) fn lock_registry() -> Result<std::sync::MutexGuard<'static, ServerRegistry>> {
328    get_registry().lock().map_err(|_| Error::internal_error())
329}
330
331fn build_standard_mesh() -> crate::max_runtime::AutonomicMesh {
332    let mut mesh = crate::max_runtime::AutonomicMesh::new();
333    mesh.register_hook(Box::new(crate::max_runtime::IntakeDiagnosticHook));
334    mesh.register_hook(Box::new(crate::max_runtime::IntakeClearHook));
335    mesh.register_hook(Box::new(
336        crate::max_runtime::CustomerRequestClassifierHook::new(),
337    ));
338    mesh.register_hook(Box::new(crate::max_runtime::PolicyEvaluationHook::new()));
339    mesh.register_hook(Box::new(crate::max_runtime::ReceiptRoutingHook::new()));
340    mesh
341}
342
343pub(crate) fn lock_mesh(
344) -> Result<std::sync::MutexGuard<'static, crate::max_runtime::AutonomicMesh>> {
345    MESH.get_or_init(|| Mutex::new(build_standard_mesh()))
346        .lock()
347        .map_err(|_| Error::internal_error())
348}
349
350/// Reset the global registry to a fresh state.
351/// Exposed as a public function for integration tests to prevent shared-state pollution.
352pub fn reset_registry_for_tests() {
353    if let Ok(mut reg) = get_registry().lock() {
354        reg.client_capabilities = None;
355        reg.server_capabilities = None;
356        reg.diagnostics.clear();
357        reg.repair_plans.clear();
358        reg.gates.clear();
359        reg.gates.insert("gate-state-check".to_string(), false);
360        reg.receipts.clear();
361        reg.snapshots.clear();
362        reg.cleared_diagnostics.clear();
363        reg.current_state = crate::service::State::Uninitialized;
364        reg.document_versions.clear();
365        reg.root_path = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
366        reg.action_seq = 0;
367        reg.conformance_delta_log.clear();
368    }
369}
370
371pub(crate) fn sha256(data: &[u8]) -> String {
372    let mut h = [
373        0x6a09e667u32,
374        0xbb67ae85u32,
375        0x3c6ef372u32,
376        0xa54ff53au32,
377        0x510e527fu32,
378        0x9b05688cu32,
379        0x1f83d9abu32,
380        0x5be0cd19u32,
381    ];
382    let k = [
383        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
384        0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
385        0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
386        0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
387        0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
388        0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
389        0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
390        0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
391        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
392        0xc67178f2,
393    ];
394
395    let mut padded = data.to_vec();
396    let bit_len = (data.len() as u64) * 8;
397    padded.push(0x80);
398    while (padded.len() + 8) % 64 != 0 {
399        padded.push(0);
400    }
401    padded.extend_from_slice(&bit_len.to_be_bytes());
402
403    for chunk in padded.chunks_exact(64) {
404        let mut w = [0u32; 64];
405        for i in 0..16 {
406            w[i] = u32::from_be_bytes([
407                chunk[i * 4],
408                chunk[i * 4 + 1],
409                chunk[i * 4 + 2],
410                chunk[i * 4 + 3],
411            ]);
412        }
413        for i in 16..64 {
414            let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
415            let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
416            w[i] = w[i - 16]
417                .wrapping_add(s0)
418                .wrapping_add(w[i - 7])
419                .wrapping_add(s1);
420        }
421
422        let mut a = h[0];
423        let mut b = h[1];
424        let mut c = h[2];
425        let mut d = h[3];
426        let mut e = h[4];
427        let mut f = h[5];
428        let mut g = h[6];
429        let mut h_val = h[7];
430
431        for i in 0..64 {
432            let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
433            let ch = (e & f) ^ ((!e) & g);
434            let temp1 = h_val
435                .wrapping_add(s1)
436                .wrapping_add(ch)
437                .wrapping_add(k[i])
438                .wrapping_add(w[i]);
439            let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
440            let maj = (a & b) ^ (a & c) ^ (b & c);
441            let temp2 = s0.wrapping_add(maj);
442
443            h_val = g;
444            g = f;
445            f = e;
446            e = d.wrapping_add(temp1);
447            d = c;
448            c = b;
449            b = a;
450            a = temp1.wrapping_add(temp2);
451        }
452
453        h[0] = h[0].wrapping_add(a);
454        h[1] = h[1].wrapping_add(b);
455        h[2] = h[2].wrapping_add(c);
456        h[3] = h[3].wrapping_add(d);
457        h[4] = h[4].wrapping_add(e);
458        h[5] = h[5].wrapping_add(f);
459        h[6] = h[6].wrapping_add(g);
460        h[7] = h[7].wrapping_add(h_val);
461    }
462
463    let mut result = String::new();
464    for &val in &h {
465        result.push_str(&format!("{:08x}", val));
466    }
467    result
468}