aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/server/src/main.rs')
-rw-r--r--crates/server/src/main.rs249
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]
2extern crate failure;
3#[macro_use]
4extern crate serde_derive;
5extern crate serde;
6extern crate serde_json;
7extern crate languageserver_types;
8extern crate drop_bomb;
9#[macro_use]
10extern crate crossbeam_channel;
11extern crate threadpool;
12#[macro_use]
13extern crate log;
14extern crate flexi_logger;
15extern crate libeditor;
16extern crate libanalysis;
17
18mod io;
19mod caps;
20mod req;
21mod dispatch;
22mod handlers;
23
24use std::path::PathBuf;
25
26use threadpool::ThreadPool;
27use crossbeam_channel::{bounded, Sender, Receiver};
28use flexi_logger::Logger;
29use libanalysis::WorldState;
30use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, TextDocumentIdentifier};
31
32use ::{
33 io::{Io, RawMsg},
34 handlers::{handle_syntax_tree, handle_extend_selection},
35};
36
37pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
38
39fn 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
57fn 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
74fn 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
105type Thunk = Box<for<'a> FnBox<&'a mut Io, Result<()>>>;
106
107fn 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
119fn 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
216trait FnBox<A, R>: Send {
217 fn call_box(self: Box<Self>, a: A) -> R;
218}
219
220impl<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
226trait FilePath {
227 fn file_path(&self) -> Result<PathBuf>;
228}
229
230impl 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
237impl 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
244impl 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}