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/src/main.rs | |
parent | 26262aaf05983c5b7f41cc438e287523268fe1eb (diff) |
organizize
Diffstat (limited to 'crates/server/src/main.rs')
-rw-r--r-- | crates/server/src/main.rs | 249 |
1 files changed, 249 insertions, 0 deletions
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 | } | ||