1use std::io::{BufReader, BufWriter, Read, Write};
16use std::path::PathBuf;
17
18use serde_json::Value;
19
20use super::protocol::{
21 read_message, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
22};
23
24use super::diagnostic::{start_diagnostics_thread, DiagnosticsThread};
25use super::messagesender::{start_message_sender_thread, MessageSender};
26
27#[derive(PartialEq)]
28enum State {
29 Initialized,
30 ShuttingDown,
31}
32
33struct ServerContext {
34 state: State,
35 msg_sender: MessageSender,
37 diag: DiagnosticsThread,
39 exit_code: Option<i32>,
41}
42
43impl ServerContext {
44 fn new(msg_sender: MessageSender, diag: DiagnosticsThread) -> ServerContext {
45 ServerContext {
46 state: State::Initialized,
47 msg_sender: msg_sender,
48 diag: diag,
49 exit_code: None,
50 }
51 }
52}
53
54fn get_request_params<P: serde::de::DeserializeOwned>(
57 params: Value,
58) -> std::result::Result<P, ResponseError> {
59 serde_json::from_value::<P>(params)
60 .map_err(|err| ResponseError::new(ErrorCodes::InvalidRequest, err.to_string()))
61}
62
63fn handle_request(ctx: &mut ServerContext, msg: RequestMessage) -> anyhow::Result<()> {
64 let id = msg.id;
65 let method = msg.method.as_str();
66 log::debug!("[recv] Request: id = {}, method = {}", id, method);
67
68 if method == "exit" {
70 exit_notification(ctx);
71 return Ok(());
72 }
73
74 use lsp_types::request::*;
75 let res = match method {
76 Initialize::METHOD => initialize_request(),
77 Shutdown::METHOD => shutdown_request(ctx),
78 GotoDefinition::METHOD => get_request_params(msg.params)
79 .and_then(|params| goto_definition_request(&mut ctx.diag, params)),
80 _ => unimplemented_request(id, method),
81 };
82 match res {
83 Ok(res) => {
84 ctx.msg_sender.send_success_response(id, res);
85 }
86 Err(err) => ctx.msg_sender.send_error_response(id, err),
87 };
88 Ok(())
89}
90
91type RequestResult = std::result::Result<Value, ResponseError>;
92
93fn unimplemented_request(id: u64, method_name: &str) -> RequestResult {
94 let msg = format!(
95 "Unimplemented request: id = {} method = {}",
96 id, method_name
97 );
98 let err = ResponseError::new(ErrorCodes::InternalError, msg);
99 Err(err)
100}
101
102fn initialize_request() -> RequestResult {
103 let error_message = "Unexpected initialize message".to_owned();
105 Err(ResponseError::new(
106 ErrorCodes::ServerNotInitialized,
107 error_message,
108 ))
109}
110
111fn shutdown_request(ctx: &mut ServerContext) -> RequestResult {
112 ctx.state = State::ShuttingDown;
113 Ok(Value::Null)
114}
115
116fn goto_definition_request(
117 diag: &mut DiagnosticsThread,
118 params: lsp_types::TextDocumentPositionParams,
119) -> RequestResult {
120 if let Some(loc) = diag.goto_definition(params.text_document.uri, params.position) {
121 let res = serde_json::to_value(loc).unwrap();
122 return Ok(res);
123 }
124 return Ok(Value::Null);
125}
126
127fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> anyhow::Result<P> {
130 serde_json::from_value::<P>(params).map_err(|err| err.into())
131}
132
133fn handle_notification(ctx: &mut ServerContext, msg: NotificationMessage) -> anyhow::Result<()> {
134 log::debug!("[recv] Notification: method = {}", msg.method);
135
136 use lsp_types::notification::*;
137 match msg.method.as_str() {
138 Exit::METHOD => exit_notification(ctx),
139 DidOpenTextDocument::METHOD => {
140 get_params(msg.params).map(|params| did_open_text_document(ctx, params))?;
141 }
142 DidChangeTextDocument::METHOD => {
143 get_params(msg.params).map(|params| did_change_text_document(ctx, params))?;
144 }
145 DidChangeConfiguration::METHOD => (),
147 WillSaveTextDocument::METHOD => (),
148 DidSaveTextDocument::METHOD => (),
149 _ => {
150 log::warn!("Received unimplemented notification: {:#?}", msg);
151 }
152 }
153 Ok(())
154}
155
156fn exit_notification(ctx: &mut ServerContext) {
157 if ctx.state == State::ShuttingDown {
159 ctx.exit_code = Some(0);
160 } else {
161 ctx.exit_code = Some(1);
162 }
163}
164
165fn did_open_text_document(ctx: &mut ServerContext, params: lsp_types::DidOpenTextDocumentParams) {
166 ctx.diag
167 .check(params.text_document.uri, params.text_document.text);
168}
169
170fn did_change_text_document(
171 ctx: &mut ServerContext,
172 params: lsp_types::DidChangeTextDocumentParams,
173) {
174 let uri = params.text_document.uri.clone();
175 let content = params
176 .content_changes
177 .iter()
178 .map(|i| i.text.to_owned())
179 .collect::<Vec<_>>();
180 let text = content.join("");
181 ctx.diag.check(uri, text);
182}
183
184fn is_chromium_src_dir(path: &PathBuf) -> bool {
185 if !path.file_name().map(|name| name == "src").unwrap_or(false) {
187 return false;
188 }
189
190 match path.parent() {
192 Some(parent) => parent.join(".gclient").is_file(),
193 None => false,
194 }
195}
196
197fn find_chromium_src_dir(mut path: PathBuf) -> PathBuf {
198 if is_chromium_src_dir(&path) {
199 return path;
200 }
201
202 let original = path.clone();
203 while path.pop() {
204 if is_chromium_src_dir(&path) {
205 return path;
206 }
207 }
208 original
209}
210
211fn get_root_path(params: &lsp_types::InitializeParams) -> Option<PathBuf> {
212 let uri = match params.root_uri {
213 Some(ref uri) => uri,
214 None => return None,
215 };
216 let path = match uri.to_file_path() {
217 Ok(path) => path,
218 Err(_) => return None,
219 };
220
221 let path = find_chromium_src_dir(path);
223 Some(path)
224}
225
226pub fn start<R, W>(reader: R, writer: W) -> anyhow::Result<i32>
228where
229 R: Read,
230 W: Write + Send + 'static,
231{
232 let mut reader = BufReader::new(reader);
233 let mut writer = BufWriter::new(writer);
234
235 let params = super::initialization::initialize(&mut reader, &mut writer)?;
236
237 let root_path = get_root_path(¶ms).unwrap_or(PathBuf::new());
238
239 let msg_sender_thread = start_message_sender_thread(writer);
240 let diag = start_diagnostics_thread(root_path, msg_sender_thread.get_sender());
241
242 let mut ctx = ServerContext::new(msg_sender_thread.get_sender(), diag);
243 loop {
244 let message = read_message(&mut reader)?;
245 match message {
246 Message::Request(request) => handle_request(&mut ctx, request)?,
247 Message::Notofication(notification) => handle_notification(&mut ctx, notification)?,
248 _ => unreachable!(),
249 };
250
251 if let Some(exit_code) = ctx.exit_code {
252 return Ok(exit_code);
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::super::protocol::{self, read_message, write_notification, write_request};
260 use super::*;
261
262 use lsp_types::notification::*;
263 use lsp_types::request::*;
264 use pipe::pipe;
265
266 #[test]
267 fn test_server_init() {
268 let (reader, mut writer) = pipe();
269
270 let capabilities = lsp_types::ClientCapabilities {
271 workspace: None,
272 text_document: None,
273 window: None,
274 general: None,
275 experimental: None,
276 };
277 #[allow(deprecated)]
279 let params = lsp_types::InitializeParams {
280 process_id: None,
281 root_path: None,
282 root_uri: None,
283 initialization_options: None,
284 capabilities: capabilities,
285 trace: None,
286 workspace_folders: None,
287 client_info: None,
288 locale: None,
289 };
290 let params = serde_json::to_value(¶ms).unwrap();
291
292 let (r, w) = pipe();
293 let handle = std::thread::spawn(move || {
294 let status = start(reader, w);
295 status
296 });
297
298 write_request(
299 &mut writer,
300 1,
301 lsp_types::request::Initialize::METHOD,
302 params,
303 )
304 .unwrap();
305
306 let mut r = BufReader::new(r);
307 let msg = read_message(&mut r).unwrap();
308 match msg {
309 protocol::Message::Response(msg) => {
310 assert_eq!(1, msg.id);
311 }
312 _ => unreachable!(),
313 }
314
315 write_notification(
316 &mut writer,
317 lsp_types::notification::Initialized::METHOD,
318 serde_json::Value::Null,
319 )
320 .unwrap();
321
322 write_request(
323 &mut writer,
324 2,
325 lsp_types::request::Shutdown::METHOD,
326 serde_json::Value::Null,
327 )
328 .unwrap();
329
330 let msg = read_message(&mut r).unwrap();
331 match msg {
332 protocol::Message::Response(msg) => {
333 assert_eq!(2, msg.id);
334 }
335 _ => unreachable!(),
336 }
337
338 write_notification(
339 &mut writer,
340 lsp_types::notification::Exit::METHOD,
341 serde_json::Value::Null,
342 )
343 .unwrap();
344
345 drop(writer);
346 drop(r);
347
348 let status = handle.join().unwrap();
349 assert!(status.is_ok());
350 }
351}