1use lsp_server::{Connection, ErrorCode, ExtractError, Message, Notification, Request, Response};
4use lsp_types::notification::{
5 DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, LogMessage,
6 Notification as LspNotification, PublishDiagnostics,
7};
8use lsp_types::request::{
9 Completion, DocumentHighlightRequest, DocumentSymbolRequest, GotoDefinition, HoverRequest,
10 References, Request as LspRequest, SemanticTokensFullRequest, SignatureHelpRequest,
11};
12use lsp_types::{
13 CompletionParams, CompletionResponse, DidChangeTextDocumentParams, DidCloseTextDocumentParams,
14 DidOpenTextDocumentParams, DocumentHighlight, DocumentHighlightParams, DocumentSymbolParams,
15 DocumentSymbolResponse, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams,
16 Location, LogMessageParams, MessageType, ReferenceParams, SemanticTokens, SemanticTokensParams,
17 SemanticTokensResult, SignatureHelp, SignatureHelpParams, TextDocumentPositionParams,
18};
19
20use crate::Result;
21use crate::document::Encoding;
22use crate::features::{completion, hover, navigation, outline, semantic_tokens, signature_help};
23use crate::project::Cursor;
24use crate::workspace::{Refreshed, Workspace};
25
26pub(crate) struct Server<'conn> {
27 connection: &'conn Connection,
28 encoding: Encoding,
29 workspace: Workspace,
30}
31
32impl<'conn> Server<'conn> {
33 pub(crate) fn new(connection: &'conn Connection, encoding: Encoding) -> Self {
34 Self {
35 connection,
36 encoding,
37 workspace: Workspace::default(),
38 }
39 }
40
41 pub(crate) fn run(mut self) -> Result<()> {
43 for message in &self.connection.receiver {
44 match message {
45 Message::Request(request) => {
46 if self.connection.handle_shutdown(&request)? {
47 return Ok(());
48 }
49 let response = self.handle_request(request);
50 self.connection.sender.send(response.into())?;
51 }
52 Message::Notification(notification) => self.handle_notification(notification)?,
53 Message::Response(_) => {}
54 }
55 }
56 Ok(())
57 }
58
59 fn handle_request(&self, request: Request) -> Response {
60 RequestDispatch::new(self, request)
61 .on::<Completion>(Self::completion)
62 .on::<SignatureHelpRequest>(Self::signature_help)
63 .on::<HoverRequest>(Self::hover)
64 .on::<SemanticTokensFullRequest>(Self::semantic_tokens)
65 .on::<DocumentSymbolRequest>(Self::document_symbols)
66 .on::<GotoDefinition>(Self::definition)
67 .on::<References>(Self::references)
68 .on::<DocumentHighlightRequest>(Self::highlights)
69 .finish()
70 }
71
72 fn handle_notification(&mut self, notification: Notification) -> Result<()> {
73 let refreshed = match notification.method.as_str() {
74 DidOpenTextDocument::METHOD => {
75 let params: DidOpenTextDocumentParams = extract(notification)?;
76 let document = params.text_document;
77 self.workspace.update(
78 &document.uri,
79 document.text,
80 document.version,
81 self.encoding,
82 )
83 }
84 DidChangeTextDocument::METHOD => {
85 let mut params: DidChangeTextDocumentParams = extract(notification)?;
86 let Some(change) = params.content_changes.pop() else {
88 return Ok(());
89 };
90 let document = params.text_document;
91 self.workspace
92 .update(&document.uri, change.text, document.version, self.encoding)
93 }
94 DidCloseTextDocument::METHOD => {
95 let params: DidCloseTextDocumentParams = extract(notification)?;
96 self.workspace
97 .close(¶ms.text_document.uri, self.encoding)
98 }
99 _ => return Ok(()),
100 };
101 self.send_refreshed(refreshed)
102 }
103
104 fn send_refreshed(&self, refreshed: Refreshed) -> Result<()> {
105 for message in refreshed.warnings {
106 let params = LogMessageParams {
107 typ: MessageType::WARNING,
108 message,
109 };
110 self.notify::<LogMessage>(params)?;
111 }
112 for params in refreshed.diagnostics {
113 self.notify::<PublishDiagnostics>(params)?;
114 }
115 Ok(())
116 }
117
118 fn notify<N: LspNotification>(&self, params: N::Params) -> Result<()> {
119 let notification = Notification::new(N::METHOD.to_owned(), params);
120 self.connection.sender.send(notification.into())?;
121 Ok(())
122 }
123
124 fn completion(&self, params: &CompletionParams) -> Option<CompletionResponse> {
125 let cursor = self.locate(¶ms.text_document_position)?;
126 completion::completions(cursor).map(CompletionResponse::Array)
127 }
128
129 fn signature_help(&self, params: &SignatureHelpParams) -> Option<SignatureHelp> {
130 signature_help::signature_help(self.locate(¶ms.text_document_position_params)?)
131 }
132
133 fn hover(&self, params: &HoverParams) -> Option<Hover> {
134 let cursor = self.locate(¶ms.text_document_position_params)?;
135 hover::hover(cursor, self.encoding)
136 }
137
138 fn semantic_tokens(&self, params: &SemanticTokensParams) -> Option<SemanticTokensResult> {
139 let (project, file) = self.workspace.file(¶ms.text_document.uri)?;
140 let data = semantic_tokens::semantic_tokens(project, file, self.encoding);
141 Some(SemanticTokensResult::Tokens(SemanticTokens {
142 result_id: None,
143 data,
144 }))
145 }
146
147 fn document_symbols(&self, params: &DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
148 let (project, file) = self.workspace.file(¶ms.text_document.uri)?;
149 let symbols = outline::outline(project.document(file), self.encoding);
150 Some(DocumentSymbolResponse::Nested(symbols))
151 }
152
153 fn definition(&self, params: &GotoDefinitionParams) -> Option<GotoDefinitionResponse> {
154 let cursor = self.locate(¶ms.text_document_position_params)?;
155 let location = navigation::definition(cursor, self.encoding)?;
156 Some(GotoDefinitionResponse::Scalar(location))
157 }
158
159 fn references(&self, params: &ReferenceParams) -> Option<Vec<Location>> {
160 let cursor = self.locate(¶ms.text_document_position)?;
161 let include_declaration = params.context.include_declaration;
162 navigation::references(cursor, include_declaration, self.encoding)
163 }
164
165 fn highlights(&self, params: &DocumentHighlightParams) -> Option<Vec<DocumentHighlight>> {
166 let cursor = self.locate(¶ms.text_document_position_params)?;
167 navigation::highlights(cursor, self.encoding)
168 }
169
170 fn locate(&self, params: &TextDocumentPositionParams) -> Option<Cursor<'_>> {
172 self.workspace
173 .locate(¶ms.text_document.uri, params.position, self.encoding)
174 }
175}
176
177struct RequestDispatch<'a, 'conn> {
179 server: &'a Server<'conn>,
180 state: DispatchState,
181}
182
183enum DispatchState {
184 Pending(Request),
185 Answered(Response),
186}
187
188impl<'a, 'conn> RequestDispatch<'a, 'conn> {
189 fn new(server: &'a Server<'conn>, request: Request) -> Self {
190 Self {
191 server,
192 state: DispatchState::Pending(request),
193 }
194 }
195
196 fn on<R: LspRequest>(self, handler: fn(&Server<'conn>, &R::Params) -> R::Result) -> Self {
197 let DispatchState::Pending(request) = self.state else {
198 return self;
199 };
200 let id = request.id.clone();
201 let state = match request.extract::<R::Params>(R::METHOD) {
202 Ok((id, params)) => {
203 DispatchState::Answered(Response::new_ok(id, handler(self.server, ¶ms)))
204 }
205 Err(ExtractError::MethodMismatch(request)) => DispatchState::Pending(request),
206 Err(ExtractError::JsonError { method, error }) => {
207 DispatchState::Answered(Response::new_err(
208 id,
209 ErrorCode::InvalidParams as i32,
210 format!("invalid params for {method}: {error}"),
211 ))
212 }
213 };
214 Self {
215 server: self.server,
216 state,
217 }
218 }
219
220 fn finish(self) -> Response {
221 match self.state {
222 DispatchState::Answered(response) => response,
223 DispatchState::Pending(request) => Response::new_err(
224 request.id,
225 ErrorCode::MethodNotFound as i32,
226 format!("unsupported request: {}", request.method),
227 ),
228 }
229 }
230}
231
232fn extract<P: serde::de::DeserializeOwned>(notification: Notification) -> Result<P> {
235 let method = notification.method.clone();
236 notification
237 .extract(&method)
238 .map_err(|error| format!("invalid params for {method}: {error}").into())
239}