1use std::sync::atomic::{AtomicBool, Ordering};
2
3use dada_util::Fallible;
4use dispatch::LspDispatch;
5use lsp_server::Connection;
6use lsp_types::{
7 InitializeParams, InitializeResult, PublishDiagnosticsParams, ServerCapabilities, ServerInfo,
8 notification, request,
9};
10
11mod dispatch;
12
13pub trait Lsp: Sized {
15 type Fork: LspFork;
18
19 fn run() -> Fallible<()> {
20 run_server::<Self>()
21 }
22
23 fn new(params: InitializeParams) -> Fallible<Self>;
24
25 fn server_capabilities(&mut self) -> Fallible<ServerCapabilities>;
27
28 fn server_info(&mut self) -> Fallible<Option<ServerInfo>>;
30
31 fn fork(&mut self) -> Self::Fork;
33
34 fn did_open(
36 &mut self,
37 editor: &mut dyn Editor<Self>,
38 item: lsp_types::DidOpenTextDocumentParams,
39 ) -> Fallible<()>;
40
41 fn did_change(
43 &mut self,
44 editor: &mut dyn Editor<Self>,
45 item: lsp_types::DidChangeTextDocumentParams,
46 ) -> Fallible<()>;
47
48 fn hover(
50 &mut self,
51 editor: &mut dyn Editor<Self>,
52 params: lsp_types::HoverParams,
53 ) -> Fallible<Option<lsp_types::Hover>>;
54}
55
56pub trait LspFork: Sized + Send {
57 #[expect(dead_code)]
58 fn fork(&self) -> Self;
59}
60
61pub trait Editor<L: Lsp> {
66 fn show_message(
68 &mut self,
69 message_type: lsp_types::MessageType,
70 message: String,
71 ) -> Fallible<()>;
72
73 fn publish_diagnostics(&mut self, params: PublishDiagnosticsParams) -> Fallible<()>;
74
75 #[allow(clippy::type_complexity)]
78 fn spawn(&mut self, task: Box<dyn FnOnce(&L::Fork, &mut dyn Editor<L>) -> Fallible<()> + Send>);
79}
80
81pub fn run_server<L: Lsp>() -> Fallible<()> {
82 let (connection, io_threads) = Connection::stdio();
85
86 let lsp = initialize_server::<L>(&connection)?;
87
88 LspDispatch::new(connection, lsp)
89 .on_notification::<notification::DidOpenTextDocument>(Lsp::did_open)
90 .on_notification::<notification::DidChangeTextDocument>(Lsp::did_change)
91 .on_request::<request::HoverRequest>(Lsp::hover)
92 .execute()?;
93
94 io_threads.join()?;
95
96 eprintln!("shutting down server");
98 Ok(())
99}
100
101static CANCEL: AtomicBool = AtomicBool::new(false);
102
103fn not_canceled() -> bool {
104 !CANCEL.load(Ordering::Relaxed)
105}
106
107fn initialize_server<L: Lsp>(connection: &Connection) -> Fallible<L> {
108 let (initialize_id, initialize_params) = connection.initialize_start_while(not_canceled)?;
109 let initialize_params: InitializeParams = serde_json::from_value(initialize_params)?;
110
111 let mut server = L::new(initialize_params)?;
112
113 let initialize_result = InitializeResult {
114 capabilities: server.server_capabilities()?,
115 server_info: server.server_info()?,
116 };
117
118 connection.initialize_finish_while(
119 initialize_id,
120 serde_json::to_value(initialize_result)?,
121 not_canceled,
122 )?;
123
124 Ok(server)
125}