diff options
author | Aleksey Kladov <[email protected]> | 2018-08-10 20:33:29 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-10 20:33:29 +0100 |
commit | 7c67612b8a894187fa3b64725531a5459f9211bf (patch) | |
tree | 9e2a536efa0c880d921fd8d4d74423afc9451fd4 /crates/server | |
parent | 26262aaf05983c5b7f41cc438e287523268fe1eb (diff) |
organizize
Diffstat (limited to 'crates/server')
-rw-r--r-- | crates/server/Cargo.toml | 18 | ||||
-rw-r--r-- | crates/server/src/caps.rs | 36 | ||||
-rw-r--r-- | crates/server/src/dispatch.rs | 174 | ||||
-rw-r--r-- | crates/server/src/handlers.rs | 61 | ||||
-rw-r--r-- | crates/server/src/io.rs | 202 | ||||
-rw-r--r-- | crates/server/src/main.rs | 249 | ||||
-rw-r--r-- | crates/server/src/req.rs | 41 |
7 files changed, 781 insertions, 0 deletions
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml new file mode 100644 index 000000000..e6d1b18c3 --- /dev/null +++ b/crates/server/Cargo.toml | |||
@@ -0,0 +1,18 @@ | |||
1 | [package] | ||
2 | name = "m" | ||
3 | version = "0.1.0" | ||
4 | authors = ["Aleksey Kladov <[email protected]>"] | ||
5 | |||
6 | [dependencies] | ||
7 | failure = "0.1.2" | ||
8 | languageserver-types = "0.48.0" | ||
9 | serde_json = "1.0.24" | ||
10 | serde = "1.0.71" | ||
11 | serde_derive = "1.0.71" | ||
12 | drop_bomb = "0.1.0" | ||
13 | crossbeam-channel = "0.2.4" | ||
14 | threadpool = "1.7.1" | ||
15 | flexi_logger = "0.9.0" | ||
16 | log = "0.4.3" | ||
17 | libeditor = { path = "../libeditor" } | ||
18 | libanalysis = { path = "../libanalysis" } | ||
diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs new file mode 100644 index 000000000..3d89c64a9 --- /dev/null +++ b/crates/server/src/caps.rs | |||
@@ -0,0 +1,36 @@ | |||
1 | use languageserver_types::{ | ||
2 | ServerCapabilities, | ||
3 | TextDocumentSyncCapability, | ||
4 | TextDocumentSyncOptions, | ||
5 | TextDocumentSyncKind, | ||
6 | }; | ||
7 | |||
8 | pub const SERVER_CAPABILITIES: ServerCapabilities = ServerCapabilities { | ||
9 | text_document_sync: Some(TextDocumentSyncCapability::Options( | ||
10 | TextDocumentSyncOptions { | ||
11 | open_close: Some(true), | ||
12 | change: Some(TextDocumentSyncKind::Full), | ||
13 | will_save: None, | ||
14 | will_save_wait_until: None, | ||
15 | save: None, | ||
16 | } | ||
17 | )), | ||
18 | hover_provider: None, | ||
19 | completion_provider: None, | ||
20 | signature_help_provider: None, | ||
21 | definition_provider: None, | ||
22 | type_definition_provider: None, | ||
23 | implementation_provider: None, | ||
24 | references_provider: None, | ||
25 | document_highlight_provider: None, | ||
26 | document_symbol_provider: None, | ||
27 | workspace_symbol_provider: None, | ||
28 | code_action_provider: None, | ||
29 | code_lens_provider: None, | ||
30 | document_formatting_provider: None, | ||
31 | document_range_formatting_provider: None, | ||
32 | document_on_type_formatting_provider: None, | ||
33 | rename_provider: None, | ||
34 | color_provider: None, | ||
35 | execute_command_provider: None, | ||
36 | }; | ||
diff --git a/crates/server/src/dispatch.rs b/crates/server/src/dispatch.rs new file mode 100644 index 000000000..2da0996e3 --- /dev/null +++ b/crates/server/src/dispatch.rs | |||
@@ -0,0 +1,174 @@ | |||
1 | use std::marker::PhantomData; | ||
2 | |||
3 | use serde::{ | ||
4 | ser::Serialize, | ||
5 | de::DeserializeOwned, | ||
6 | }; | ||
7 | use serde_json; | ||
8 | use drop_bomb::DropBomb; | ||
9 | |||
10 | use ::{ | ||
11 | Result, | ||
12 | req::{Request, Notification}, | ||
13 | io::{Io, RawMsg, RawResponse, RawRequest, RawNotification}, | ||
14 | }; | ||
15 | |||
16 | pub struct Responder<R: Request> { | ||
17 | id: u64, | ||
18 | bomb: DropBomb, | ||
19 | ph: PhantomData<R>, | ||
20 | } | ||
21 | |||
22 | impl<R: Request> Responder<R> | ||
23 | where | ||
24 | R::Params: DeserializeOwned, | ||
25 | R::Result: Serialize, | ||
26 | { | ||
27 | pub fn response(self, io: &mut Io, resp: Result<R::Result>) -> Result<()> { | ||
28 | match resp { | ||
29 | Ok(res) => self.result(io, res)?, | ||
30 | Err(e) => { | ||
31 | self.error(io)?; | ||
32 | return Err(e); | ||
33 | } | ||
34 | } | ||
35 | Ok(()) | ||
36 | } | ||
37 | |||
38 | pub fn result(mut self, io: &mut Io, result: R::Result) -> Result<()> { | ||
39 | self.bomb.defuse(); | ||
40 | io.send(RawMsg::Response(RawResponse { | ||
41 | id: Some(self.id), | ||
42 | result: serde_json::to_value(result)?, | ||
43 | error: serde_json::Value::Null, | ||
44 | })); | ||
45 | Ok(()) | ||
46 | } | ||
47 | |||
48 | pub fn error(mut self, io: &mut Io) -> Result<()> { | ||
49 | self.bomb.defuse(); | ||
50 | error(io, self.id, ErrorCode::InternalError, "internal error") | ||
51 | } | ||
52 | } | ||
53 | |||
54 | |||
55 | fn parse_request_as<R>(raw: RawRequest) -> Result<::std::result::Result<(R::Params, Responder<R>), RawRequest>> | ||
56 | where | ||
57 | R: Request, | ||
58 | R::Params: DeserializeOwned, | ||
59 | R::Result: Serialize, | ||
60 | { | ||
61 | if raw.method != R::METHOD { | ||
62 | return Ok(Err(raw)); | ||
63 | } | ||
64 | |||
65 | let params: R::Params = serde_json::from_value(raw.params)?; | ||
66 | let responder = Responder { | ||
67 | id: raw.id, | ||
68 | bomb: DropBomb::new("dropped request"), | ||
69 | ph: PhantomData, | ||
70 | }; | ||
71 | Ok(Ok((params, responder))) | ||
72 | } | ||
73 | |||
74 | pub fn handle_request<R, F>(req: &mut Option<RawRequest>, f: F) -> Result<()> | ||
75 | where | ||
76 | R: Request, | ||
77 | R::Params: DeserializeOwned, | ||
78 | R::Result: Serialize, | ||
79 | F: FnOnce(R::Params, Responder<R>) -> Result<()> | ||
80 | { | ||
81 | match req.take() { | ||
82 | None => Ok(()), | ||
83 | Some(r) => match parse_request_as::<R>(r)? { | ||
84 | Ok((params, responder)) => f(params, responder), | ||
85 | Err(r) => { | ||
86 | *req = Some(r); | ||
87 | Ok(()) | ||
88 | }, | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | pub fn expect_request<R>(io: &mut Io, raw: RawRequest) -> Result<Option<(R::Params, Responder<R>)>> | ||
94 | where | ||
95 | R: Request, | ||
96 | R::Params: DeserializeOwned, | ||
97 | R::Result: Serialize, | ||
98 | { | ||
99 | let ret = match parse_request_as::<R>(raw)? { | ||
100 | Ok(x) => Some(x), | ||
101 | Err(raw) => { | ||
102 | unknown_method(io, raw)?; | ||
103 | None | ||
104 | } | ||
105 | }; | ||
106 | Ok(ret) | ||
107 | } | ||
108 | |||
109 | fn parse_notification_as<N>(raw: RawNotification) -> Result<::std::result::Result<N::Params, RawNotification>> | ||
110 | where | ||
111 | N: Notification, | ||
112 | N::Params: DeserializeOwned, | ||
113 | { | ||
114 | if raw.method != N::METHOD { | ||
115 | return Ok(Err(raw)); | ||
116 | } | ||
117 | let params: N::Params = serde_json::from_value(raw.params)?; | ||
118 | Ok(Ok(params)) | ||
119 | } | ||
120 | |||
121 | pub fn handle_notification<N, F>(not: &mut Option<RawNotification>, f: F) -> Result<()> | ||
122 | where | ||
123 | N: Notification, | ||
124 | N::Params: DeserializeOwned, | ||
125 | F: FnOnce(N::Params) -> Result<()> | ||
126 | { | ||
127 | match not.take() { | ||
128 | None => Ok(()), | ||
129 | Some(n) => match parse_notification_as::<N>(n)? { | ||
130 | Ok(params) => f(params), | ||
131 | Err(n) => { | ||
132 | *not = Some(n); | ||
133 | Ok(()) | ||
134 | }, | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | |||
140 | pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> { | ||
141 | error(io, raw.id, ErrorCode::MethodNotFound, "unknown method") | ||
142 | } | ||
143 | |||
144 | fn error(io: &mut Io, id: u64, code: ErrorCode, message: &'static str) -> Result<()> { | ||
145 | #[derive(Serialize)] | ||
146 | struct Error { | ||
147 | code: i32, | ||
148 | message: &'static str, | ||
149 | } | ||
150 | io.send(RawMsg::Response(RawResponse { | ||
151 | id: Some(id), | ||
152 | result: serde_json::Value::Null, | ||
153 | error: serde_json::to_value(Error { | ||
154 | code: code as i32, | ||
155 | message, | ||
156 | })?, | ||
157 | })); | ||
158 | Ok(()) | ||
159 | } | ||
160 | |||
161 | |||
162 | #[allow(unused)] | ||
163 | enum ErrorCode { | ||
164 | ParseError = -32700, | ||
165 | InvalidRequest = -32600, | ||
166 | MethodNotFound = -32601, | ||
167 | InvalidParams = -32602, | ||
168 | InternalError = -32603, | ||
169 | ServerErrorStart = -32099, | ||
170 | ServerErrorEnd = -32000, | ||
171 | ServerNotInitialized = -32002, | ||
172 | UnknownErrorCode = -32001, | ||
173 | RequestCancelled = -32800, | ||
174 | } | ||
diff --git a/crates/server/src/handlers.rs b/crates/server/src/handlers.rs new file mode 100644 index 000000000..5ee87a4dd --- /dev/null +++ b/crates/server/src/handlers.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | use languageserver_types::{Range, Position}; | ||
2 | use libanalysis::World; | ||
3 | use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; | ||
4 | use {req, Result, FilePath}; | ||
5 | |||
6 | pub fn handle_syntax_tree( | ||
7 | world: World, | ||
8 | params: req::SyntaxTreeParams, | ||
9 | ) -> Result<String> { | ||
10 | let path = params.text_document.file_path()?; | ||
11 | let file = world.file_syntax(&path)?; | ||
12 | Ok(libeditor::syntax_tree(&file)) | ||
13 | } | ||
14 | |||
15 | pub fn handle_extend_selection( | ||
16 | world: World, | ||
17 | params: req::ExtendSelectionParams, | ||
18 | ) -> Result<req::ExtendSelectionResult> { | ||
19 | let path = params.text_document.file_path()?; | ||
20 | let file = world.file_syntax(&path)?; | ||
21 | let line_index = world.file_line_index(&path)?; | ||
22 | let selections = params.selections.into_iter() | ||
23 | .map(|r| { | ||
24 | let r = to_text_range(&line_index, r); | ||
25 | let r = libeditor::extend_selection(&file, r).unwrap_or(r); | ||
26 | to_vs_range(&line_index, r) | ||
27 | }) | ||
28 | .collect(); | ||
29 | Ok(req::ExtendSelectionResult { selections }) | ||
30 | } | ||
31 | |||
32 | |||
33 | fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange { | ||
34 | TextRange::from_to( | ||
35 | to_text_unit(line_index, range.start), | ||
36 | to_text_unit(line_index, range.end), | ||
37 | ) | ||
38 | } | ||
39 | |||
40 | fn to_text_unit(line_index: &LineIndex, position: Position) -> TextUnit { | ||
41 | // TODO: UTF-16 | ||
42 | let line_col = LineCol { | ||
43 | line: position.line as u32, | ||
44 | col: (position.character as u32).into(), | ||
45 | }; | ||
46 | line_index.offset(line_col) | ||
47 | } | ||
48 | |||
49 | |||
50 | fn to_vs_range(line_index: &LineIndex, range: TextRange) -> Range { | ||
51 | Range::new( | ||
52 | to_vs_position(line_index, range.start()), | ||
53 | to_vs_position(line_index, range.end()), | ||
54 | ) | ||
55 | } | ||
56 | |||
57 | fn to_vs_position(line_index: &LineIndex, offset: TextUnit) -> Position { | ||
58 | let line_col = line_index.line_col(offset); | ||
59 | // TODO: UTF-16 | ||
60 | Position::new(line_col.line as u64, u32::from(line_col.col) as u64) | ||
61 | } | ||
diff --git a/crates/server/src/io.rs b/crates/server/src/io.rs new file mode 100644 index 000000000..5eafc6942 --- /dev/null +++ b/crates/server/src/io.rs | |||
@@ -0,0 +1,202 @@ | |||
1 | use std::{ | ||
2 | thread, | ||
3 | io::{ | ||
4 | stdout, stdin, | ||
5 | BufRead, Write, | ||
6 | }, | ||
7 | }; | ||
8 | use serde_json::{Value, from_str, to_string}; | ||
9 | use crossbeam_channel::{Receiver, Sender, bounded}; | ||
10 | |||
11 | use Result; | ||
12 | |||
13 | |||
14 | #[derive(Debug, Serialize, Deserialize)] | ||
15 | #[serde(untagged)] | ||
16 | pub enum RawMsg { | ||
17 | Request(RawRequest), | ||
18 | Notification(RawNotification), | ||
19 | Response(RawResponse), | ||
20 | } | ||
21 | |||
22 | #[derive(Debug, Serialize, Deserialize)] | ||
23 | pub struct RawRequest { | ||
24 | pub id: u64, | ||
25 | pub method: String, | ||
26 | pub params: Value, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Serialize, Deserialize)] | ||
30 | pub struct RawNotification { | ||
31 | pub method: String, | ||
32 | pub params: Value, | ||
33 | } | ||
34 | |||
35 | #[derive(Debug, Serialize, Deserialize)] | ||
36 | pub struct RawResponse { | ||
37 | pub id: Option<u64>, | ||
38 | pub result: Value, | ||
39 | pub error: Value, | ||
40 | } | ||
41 | |||
42 | struct MsgReceiver { | ||
43 | chan: Receiver<RawMsg>, | ||
44 | thread: Option<thread::JoinHandle<Result<()>>>, | ||
45 | } | ||
46 | |||
47 | impl MsgReceiver { | ||
48 | fn recv(&mut self) -> Result<RawMsg> { | ||
49 | match self.chan.recv() { | ||
50 | Some(msg) => Ok(msg), | ||
51 | None => { | ||
52 | self.cleanup()?; | ||
53 | unreachable!() | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | fn cleanup(&mut self) -> Result<()> { | ||
59 | self.thread | ||
60 | .take() | ||
61 | .ok_or_else(|| format_err!("MsgReceiver thread panicked"))? | ||
62 | .join() | ||
63 | .map_err(|_| format_err!("MsgReceiver thread panicked"))??; | ||
64 | bail!("client disconnected") | ||
65 | } | ||
66 | |||
67 | fn stop(self) -> Result<()> { | ||
68 | // Can't really self.thread.join() here, b/c it might be | ||
69 | // blocking on read | ||
70 | Ok(()) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | struct MsgSender { | ||
75 | chan: Sender<RawMsg>, | ||
76 | thread: thread::JoinHandle<Result<()>>, | ||
77 | } | ||
78 | |||
79 | impl MsgSender { | ||
80 | fn send(&mut self, msg: RawMsg) { | ||
81 | self.chan.send(msg) | ||
82 | } | ||
83 | |||
84 | fn stop(self) -> Result<()> { | ||
85 | drop(self.chan); | ||
86 | self.thread.join() | ||
87 | .map_err(|_| format_err!("MsgSender thread panicked"))??; | ||
88 | Ok(()) | ||
89 | } | ||
90 | } | ||
91 | |||
92 | pub struct Io { | ||
93 | receiver: MsgReceiver, | ||
94 | sender: MsgSender, | ||
95 | } | ||
96 | |||
97 | impl Io { | ||
98 | pub fn from_stdio() -> Io { | ||
99 | let sender = { | ||
100 | let (tx, rx) = bounded(16); | ||
101 | MsgSender { | ||
102 | chan: tx, | ||
103 | thread: thread::spawn(move || { | ||
104 | let stdout = stdout(); | ||
105 | let mut stdout = stdout.lock(); | ||
106 | for msg in rx { | ||
107 | #[derive(Serialize)] | ||
108 | struct JsonRpc { | ||
109 | jsonrpc: &'static str, | ||
110 | #[serde(flatten)] | ||
111 | msg: RawMsg, | ||
112 | } | ||
113 | let text = to_string(&JsonRpc { | ||
114 | jsonrpc: "2.0", | ||
115 | msg, | ||
116 | })?; | ||
117 | write_msg_text(&mut stdout, &text)?; | ||
118 | } | ||
119 | Ok(()) | ||
120 | }), | ||
121 | } | ||
122 | }; | ||
123 | let receiver = { | ||
124 | let (tx, rx) = bounded(16); | ||
125 | MsgReceiver { | ||
126 | chan: rx, | ||
127 | thread: Some(thread::spawn(move || { | ||
128 | let stdin = stdin(); | ||
129 | let mut stdin = stdin.lock(); | ||
130 | while let Some(text) = read_msg_text(&mut stdin)? { | ||
131 | let msg: RawMsg = from_str(&text)?; | ||
132 | tx.send(msg); | ||
133 | } | ||
134 | Ok(()) | ||
135 | })), | ||
136 | } | ||
137 | }; | ||
138 | Io { receiver, sender } | ||
139 | } | ||
140 | |||
141 | pub fn send(&mut self, msg: RawMsg) { | ||
142 | self.sender.send(msg) | ||
143 | } | ||
144 | |||
145 | pub fn recv(&mut self) -> Result<RawMsg> { | ||
146 | self.receiver.recv() | ||
147 | } | ||
148 | |||
149 | pub fn receiver(&mut self) -> &mut Receiver<RawMsg> { | ||
150 | &mut self.receiver.chan | ||
151 | } | ||
152 | |||
153 | pub fn cleanup_receiver(&mut self) -> Result<()> { | ||
154 | self.receiver.cleanup() | ||
155 | } | ||
156 | |||
157 | pub fn stop(self) -> Result<()> { | ||
158 | self.receiver.stop()?; | ||
159 | self.sender.stop()?; | ||
160 | Ok(()) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | |||
165 | fn read_msg_text(inp: &mut impl BufRead) -> Result<Option<String>> { | ||
166 | let mut size = None; | ||
167 | let mut buf = String::new(); | ||
168 | loop { | ||
169 | buf.clear(); | ||
170 | if inp.read_line(&mut buf)? == 0 { | ||
171 | return Ok(None); | ||
172 | } | ||
173 | if !buf.ends_with("\r\n") { | ||
174 | bail!("malformed header: {:?}", buf); | ||
175 | } | ||
176 | let buf = &buf[..buf.len() - 2]; | ||
177 | if buf.is_empty() { | ||
178 | break; | ||
179 | } | ||
180 | let mut parts = buf.splitn(2, ": "); | ||
181 | let header_name = parts.next().unwrap(); | ||
182 | let header_value = parts.next().ok_or_else(|| format_err!("malformed header: {:?}", buf))?; | ||
183 | if header_name == "Content-Length" { | ||
184 | size = Some(header_value.parse::<usize>()?); | ||
185 | } | ||
186 | } | ||
187 | let size = size.ok_or_else(|| format_err!("no Content-Length"))?; | ||
188 | let mut buf = buf.into_bytes(); | ||
189 | buf.resize(size, 0); | ||
190 | inp.read_exact(&mut buf)?; | ||
191 | let buf = String::from_utf8(buf)?; | ||
192 | debug!("< {}", buf); | ||
193 | Ok(Some(buf)) | ||
194 | } | ||
195 | |||
196 | fn write_msg_text(out: &mut impl Write, msg: &str) -> Result<()> { | ||
197 | debug!("> {}", msg); | ||
198 | write!(out, "Content-Length: {}\r\n\r\n", msg.len())?; | ||
199 | out.write_all(msg.as_bytes())?; | ||
200 | out.flush()?; | ||
201 | Ok(()) | ||
202 | } | ||
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs new file mode 100644 index 000000000..116abce1c --- /dev/null +++ b/crates/server/src/main.rs | |||
@@ -0,0 +1,249 @@ | |||
1 | #[macro_use] | ||
2 | extern crate failure; | ||
3 | #[macro_use] | ||
4 | extern crate serde_derive; | ||
5 | extern crate serde; | ||
6 | extern crate serde_json; | ||
7 | extern crate languageserver_types; | ||
8 | extern crate drop_bomb; | ||
9 | #[macro_use] | ||
10 | extern crate crossbeam_channel; | ||
11 | extern crate threadpool; | ||
12 | #[macro_use] | ||
13 | extern crate log; | ||
14 | extern crate flexi_logger; | ||
15 | extern crate libeditor; | ||
16 | extern crate libanalysis; | ||
17 | |||
18 | mod io; | ||
19 | mod caps; | ||
20 | mod req; | ||
21 | mod dispatch; | ||
22 | mod handlers; | ||
23 | |||
24 | use std::path::PathBuf; | ||
25 | |||
26 | use threadpool::ThreadPool; | ||
27 | use crossbeam_channel::{bounded, Sender, Receiver}; | ||
28 | use flexi_logger::Logger; | ||
29 | use libanalysis::WorldState; | ||
30 | use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, TextDocumentIdentifier}; | ||
31 | |||
32 | use ::{ | ||
33 | io::{Io, RawMsg}, | ||
34 | handlers::{handle_syntax_tree, handle_extend_selection}, | ||
35 | }; | ||
36 | |||
37 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | ||
38 | |||
39 | fn main() -> Result<()> { | ||
40 | Logger::with_env_or_str("m=trace, libanalysis=trace") | ||
41 | .log_to_file() | ||
42 | .directory("log") | ||
43 | .start()?; | ||
44 | info!("starting server"); | ||
45 | match ::std::panic::catch_unwind(|| main_inner()) { | ||
46 | Ok(res) => { | ||
47 | info!("shutting down: {:?}", res); | ||
48 | res | ||
49 | } | ||
50 | Err(_) => { | ||
51 | error!("server panicked"); | ||
52 | bail!("server panicked") | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | |||
57 | fn main_inner() -> Result<()> { | ||
58 | let mut io = Io::from_stdio(); | ||
59 | let res = initialize(&mut io); | ||
60 | info!("shutting down IO..."); | ||
61 | let io_res = io.stop(); | ||
62 | info!("... IO is down"); | ||
63 | match (res, io_res) { | ||
64 | (Ok(()), Ok(())) => Ok(()), | ||
65 | (res, Ok(())) => res, | ||
66 | (Ok(()), io_res) => io_res, | ||
67 | (res, Err(io_err)) => { | ||
68 | error!("shutdown error: {:?}", io_err); | ||
69 | res | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | fn initialize(io: &mut Io) -> Result<()> { | ||
75 | loop { | ||
76 | match io.recv()? { | ||
77 | RawMsg::Request(req) => { | ||
78 | if let Some((_params, resp)) = dispatch::expect_request::<req::Initialize>(io, req)? { | ||
79 | resp.result(io, req::InitializeResult { | ||
80 | capabilities: caps::SERVER_CAPABILITIES | ||
81 | })?; | ||
82 | match io.recv()? { | ||
83 | RawMsg::Notification(n) => { | ||
84 | if n.method != "initialized" { | ||
85 | bail!("expected initialized notification"); | ||
86 | } | ||
87 | } | ||
88 | _ => { | ||
89 | bail!("expected initialized notification"); | ||
90 | } | ||
91 | } | ||
92 | return initialized(io); | ||
93 | } | ||
94 | } | ||
95 | RawMsg::Notification(n) => { | ||
96 | bail!("expected initialize request, got {:?}", n) | ||
97 | } | ||
98 | RawMsg::Response(res) => { | ||
99 | bail!("expected initialize request, got {:?}", res) | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | type Thunk = Box<for<'a> FnBox<&'a mut Io, Result<()>>>; | ||
106 | |||
107 | fn initialized(io: &mut Io) -> Result<()> { | ||
108 | let mut world = WorldState::new(); | ||
109 | let mut pool = ThreadPool::new(4); | ||
110 | let (sender, receiver) = bounded::<Thunk>(16); | ||
111 | let res = main_loop(io, &mut world, &mut pool, sender, receiver.clone()); | ||
112 | info!("waiting for background jobs to finish..."); | ||
113 | receiver.for_each(drop); | ||
114 | pool.join(); | ||
115 | info!("...background jobs have finished"); | ||
116 | res | ||
117 | } | ||
118 | |||
119 | fn main_loop( | ||
120 | io: &mut Io, | ||
121 | world: &mut WorldState, | ||
122 | pool: &mut ThreadPool, | ||
123 | sender: Sender<Thunk>, | ||
124 | receiver: Receiver<Thunk>, | ||
125 | ) -> Result<()> { | ||
126 | info!("server initialized, serving requests"); | ||
127 | loop { | ||
128 | enum Event { | ||
129 | Msg(RawMsg), | ||
130 | Thunk(Thunk), | ||
131 | ReceiverDead, | ||
132 | } | ||
133 | |||
134 | let event = select! { | ||
135 | recv(io.receiver(), msg) => match msg { | ||
136 | Some(msg) => Event::Msg(msg), | ||
137 | None => Event::ReceiverDead, | ||
138 | }, | ||
139 | recv(receiver, thunk) => Event::Thunk(thunk.unwrap()), | ||
140 | }; | ||
141 | |||
142 | let msg = match event { | ||
143 | Event::ReceiverDead => { | ||
144 | io.cleanup_receiver()?; | ||
145 | unreachable!(); | ||
146 | } | ||
147 | Event::Thunk(thunk) => { | ||
148 | thunk.call_box(io)?; | ||
149 | continue; | ||
150 | } | ||
151 | Event::Msg(msg) => msg, | ||
152 | }; | ||
153 | |||
154 | match msg { | ||
155 | RawMsg::Request(req) => { | ||
156 | let mut req = Some(req); | ||
157 | dispatch::handle_request::<req::SyntaxTree, _>(&mut req, |params, resp| { | ||
158 | let world = world.snapshot(); | ||
159 | let sender = sender.clone(); | ||
160 | pool.execute(move || { | ||
161 | let res = handle_syntax_tree(world, params); | ||
162 | sender.send(Box::new(|io: &mut Io| resp.response(io, res))) | ||
163 | }); | ||
164 | Ok(()) | ||
165 | })?; | ||
166 | dispatch::handle_request::<req::ExtendSelection, _>(&mut req, |params, resp| { | ||
167 | let world = world.snapshot(); | ||
168 | let sender = sender.clone(); | ||
169 | pool.execute(move || { | ||
170 | let res = handle_extend_selection(world, params); | ||
171 | sender.send(Box::new(|io: &mut Io| resp.response(io, res))) | ||
172 | }); | ||
173 | Ok(()) | ||
174 | })?; | ||
175 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | ||
176 | resp.result(io, ())?; | ||
177 | Ok(()) | ||
178 | })?; | ||
179 | if let Some(req) = req { | ||
180 | error!("unknown method: {:?}", req); | ||
181 | dispatch::unknown_method(io, req)?; | ||
182 | } | ||
183 | } | ||
184 | RawMsg::Notification(not) => { | ||
185 | let mut not = Some(not); | ||
186 | dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { | ||
187 | let path = params.text_document.file_path()?; | ||
188 | world.change_overlay(path, Some(params.text_document.text)); | ||
189 | Ok(()) | ||
190 | })?; | ||
191 | dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { | ||
192 | let path = params.text_document.file_path()?; | ||
193 | let text = params.content_changes.pop() | ||
194 | .ok_or_else(|| format_err!("empty changes"))? | ||
195 | .text; | ||
196 | world.change_overlay(path, Some(text)); | ||
197 | Ok(()) | ||
198 | })?; | ||
199 | dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { | ||
200 | let path = params.text_document.file_path()?; | ||
201 | world.change_overlay(path, None); | ||
202 | Ok(()) | ||
203 | })?; | ||
204 | |||
205 | if let Some(not) = not { | ||
206 | error!("unhandled notification: {:?}", not) | ||
207 | } | ||
208 | } | ||
209 | msg => { | ||
210 | eprintln!("msg = {:?}", msg); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | |||
216 | trait FnBox<A, R>: Send { | ||
217 | fn call_box(self: Box<Self>, a: A) -> R; | ||
218 | } | ||
219 | |||
220 | impl<A, R, F: FnOnce(A) -> R + Send> FnBox<A, R> for F { | ||
221 | fn call_box(self: Box<F>, a: A) -> R { | ||
222 | (*self)(a) | ||
223 | } | ||
224 | } | ||
225 | |||
226 | trait FilePath { | ||
227 | fn file_path(&self) -> Result<PathBuf>; | ||
228 | } | ||
229 | |||
230 | impl FilePath for TextDocumentItem { | ||
231 | fn file_path(&self) -> Result<PathBuf> { | ||
232 | self.uri.to_file_path() | ||
233 | .map_err(|()| format_err!("invalid uri: {}", self.uri)) | ||
234 | } | ||
235 | } | ||
236 | |||
237 | impl FilePath for VersionedTextDocumentIdentifier { | ||
238 | fn file_path(&self) -> Result<PathBuf> { | ||
239 | self.uri.to_file_path() | ||
240 | .map_err(|()| format_err!("invalid uri: {}", self.uri)) | ||
241 | } | ||
242 | } | ||
243 | |||
244 | impl FilePath for TextDocumentIdentifier { | ||
245 | fn file_path(&self) -> Result<PathBuf> { | ||
246 | self.uri.to_file_path() | ||
247 | .map_err(|()| format_err!("invalid uri: {}", self.uri)) | ||
248 | } | ||
249 | } | ||
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs new file mode 100644 index 000000000..4e588159b --- /dev/null +++ b/crates/server/src/req.rs | |||
@@ -0,0 +1,41 @@ | |||
1 | use languageserver_types::{TextDocumentIdentifier, Range}; | ||
2 | |||
3 | pub use languageserver_types::{ | ||
4 | request::*, notification::*, | ||
5 | InitializeResult, | ||
6 | }; | ||
7 | |||
8 | pub enum SyntaxTree {} | ||
9 | |||
10 | impl Request for SyntaxTree { | ||
11 | type Params = SyntaxTreeParams; | ||
12 | type Result = String; | ||
13 | const METHOD: &'static str = "m/syntaxTree"; | ||
14 | } | ||
15 | |||
16 | #[derive(Deserialize, Debug)] | ||
17 | #[serde(rename_all = "camelCase")] | ||
18 | pub struct SyntaxTreeParams { | ||
19 | pub text_document: TextDocumentIdentifier | ||
20 | } | ||
21 | |||
22 | pub enum ExtendSelection {} | ||
23 | |||
24 | impl Request for ExtendSelection { | ||
25 | type Params = ExtendSelectionParams; | ||
26 | type Result = ExtendSelectionResult; | ||
27 | const METHOD: &'static str = "m/extendSelection"; | ||
28 | } | ||
29 | |||
30 | #[derive(Deserialize, Debug)] | ||
31 | #[serde(rename_all = "camelCase")] | ||
32 | pub struct ExtendSelectionParams { | ||
33 | pub text_document: TextDocumentIdentifier, | ||
34 | pub selections: Vec<Range>, | ||
35 | } | ||
36 | |||
37 | #[derive(Serialize, Debug)] | ||
38 | #[serde(rename_all = "camelCase")] | ||
39 | pub struct ExtendSelectionResult { | ||
40 | pub selections: Vec<Range>, | ||
41 | } | ||