aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src/main_loop/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/server/src/main_loop/mod.rs')
-rw-r--r--crates/server/src/main_loop/mod.rs201
1 files changed, 201 insertions, 0 deletions
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs
new file mode 100644
index 000000000..e7b24e53f
--- /dev/null
+++ b/crates/server/src/main_loop/mod.rs
@@ -0,0 +1,201 @@
1mod handlers;
2
3use threadpool::ThreadPool;
4use crossbeam_channel::{Sender, Receiver};
5use languageserver_types::Url;
6use libanalysis::{World, WorldState};
7use {
8 req, dispatch,
9 Task, Result,
10 io::{Io, RawMsg, RawRequest},
11 util::FilePath,
12 main_loop::handlers::{
13 handle_syntax_tree,
14 handle_extend_selection,
15 publish_diagnostics,
16 publish_decorations,
17 handle_document_symbol,
18 handle_code_action,
19 },
20};
21
22pub(super) fn main_loop(
23 io: &mut Io,
24 world: &mut WorldState,
25 pool: &mut ThreadPool,
26 sender: Sender<Task>,
27 receiver: Receiver<Task>,
28) -> Result<()> {
29 info!("server initialized, serving requests");
30 loop {
31 enum Event {
32 Msg(RawMsg),
33 Task(Task),
34 ReceiverDead,
35 }
36 let event = select! {
37 recv(io.receiver(), msg) => match msg {
38 Some(msg) => Event::Msg(msg),
39 None => Event::ReceiverDead,
40 },
41 recv(receiver, task) => Event::Task(task.unwrap()),
42 };
43
44 match event {
45 Event::ReceiverDead => {
46 io.cleanup_receiver()?;
47 unreachable!();
48 }
49 Event::Task(task) => {
50 match task {
51 Task::Respond(response) =>
52 io.send(RawMsg::Response(response)),
53 Task::Notify(n) =>
54 io.send(RawMsg::Notification(n)),
55 Task::Die(error) =>
56 return Err(error),
57 }
58 continue;
59 }
60 Event::Msg(msg) => {
61 if !on_msg(io, world, pool, &sender, msg)? {
62 return Ok(());
63 }
64 }
65 };
66 }
67}
68
69fn on_msg(
70 io: &mut Io,
71 world: &mut WorldState,
72 pool: &mut ThreadPool,
73 sender: &Sender<Task>,
74 msg: RawMsg,
75) -> Result<bool> {
76 match msg {
77 RawMsg::Request(req) => {
78 let mut req = Some(req);
79 handle_request_on_threadpool::<req::SyntaxTree>(
80 &mut req, pool, world, sender, handle_syntax_tree,
81 )?;
82 handle_request_on_threadpool::<req::ExtendSelection>(
83 &mut req, pool, world, sender, handle_extend_selection,
84 )?;
85 handle_request_on_threadpool::<req::DocumentSymbolRequest>(
86 &mut req, pool, world, sender, handle_document_symbol,
87 )?;
88 handle_request_on_threadpool::<req::CodeActionRequest>(
89 &mut req, pool, world, sender, handle_code_action,
90 )?;
91
92 let mut shutdown = false;
93 dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| {
94 let resp = resp.into_response(Ok(()))?;
95 io.send(RawMsg::Response(resp));
96 shutdown = true;
97 Ok(())
98 })?;
99 if shutdown {
100 info!("lifecycle: initiating shutdown");
101 return Ok(false);
102 }
103 if let Some(req) = req {
104 error!("unknown method: {:?}", req);
105 dispatch::unknown_method(io, req)?;
106 }
107 }
108 RawMsg::Notification(not) => {
109 let mut not = Some(not);
110 dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| {
111 let path = params.text_document.file_path()?;
112 world.change_overlay(path, Some(params.text_document.text));
113 update_file_notifications_on_threadpool(
114 pool, world.snapshot(), sender.clone(), params.text_document.uri,
115 );
116 Ok(())
117 })?;
118 dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| {
119 let path = params.text_document.file_path()?;
120 let text = params.content_changes.pop()
121 .ok_or_else(|| format_err!("empty changes"))?
122 .text;
123 world.change_overlay(path, Some(text));
124 update_file_notifications_on_threadpool(
125 pool, world.snapshot(), sender.clone(), params.text_document.uri,
126 );
127 Ok(())
128 })?;
129 dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| {
130 let path = params.text_document.file_path()?;
131 world.change_overlay(path, None);
132 let not = req::PublishDiagnosticsParams {
133 uri: params.text_document.uri,
134 diagnostics: Vec::new(),
135 };
136 let not = dispatch::send_notification::<req::PublishDiagnostics>(not);
137 io.send(RawMsg::Notification(not));
138 Ok(())
139 })?;
140
141 if let Some(not) = not {
142 error!("unhandled notification: {:?}", not)
143 }
144 }
145 msg => {
146 eprintln!("msg = {:?}", msg);
147 }
148 };
149 Ok(true)
150}
151
152fn handle_request_on_threadpool<R: req::ClientRequest>(
153 req: &mut Option<RawRequest>,
154 pool: &ThreadPool,
155 world: &WorldState,
156 sender: &Sender<Task>,
157 f: fn(World, R::Params) -> Result<R::Result>,
158) -> Result<()>
159{
160 dispatch::handle_request::<R, _>(req, |params, resp| {
161 let world = world.snapshot();
162 let sender = sender.clone();
163 pool.execute(move || {
164 let res = f(world, params);
165 let task = match resp.into_response(res) {
166 Ok(resp) => Task::Respond(resp),
167 Err(e) => Task::Die(e),
168 };
169 sender.send(task);
170 });
171 Ok(())
172 })
173}
174
175fn update_file_notifications_on_threadpool(
176 pool: &ThreadPool,
177 world: World,
178 sender: Sender<Task>,
179 uri: Url,
180) {
181 pool.execute(move || {
182 match publish_diagnostics(world.clone(), uri.clone()) {
183 Err(e) => {
184 error!("failed to compute diagnostics: {:?}", e)
185 }
186 Ok(params) => {
187 let not = dispatch::send_notification::<req::PublishDiagnostics>(params);
188 sender.send(Task::Notify(not));
189 }
190 }
191 match publish_decorations(world, uri) {
192 Err(e) => {
193 error!("failed to compute decorations: {:?}", e)
194 }
195 Ok(params) => {
196 let not = dispatch::send_notification::<req::PublishDecorations>(params);
197 sender.send(Task::Notify(not))
198 }
199 }
200 });
201}