diff options
Diffstat (limited to 'crates/server/src/main_loop')
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 384 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 169 |
2 files changed, 179 insertions, 374 deletions
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 3ee0873f4..45083b084 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -2,16 +2,13 @@ use std::collections::HashMap; | |||
2 | 2 | ||
3 | use languageserver_types::{ | 3 | use languageserver_types::{ |
4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, | 4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, |
5 | Command, TextDocumentIdentifier, WorkspaceEdit, | 5 | Command, TextDocumentIdentifier, |
6 | SymbolInformation, Position, Location, TextEdit, | 6 | SymbolInformation, Position, Location, TextEdit, |
7 | CompletionItem, InsertTextFormat, CompletionItemKind, | 7 | CompletionItem, InsertTextFormat, CompletionItemKind, |
8 | }; | 8 | }; |
9 | use serde_json::{to_value, from_value}; | 9 | use serde_json::to_value; |
10 | use url_serde; | 10 | use libanalysis::{Query, FileId, RunnableKind}; |
11 | use libanalysis::{self, Query, FileId}; | ||
12 | use libeditor; | ||
13 | use libsyntax2::{ | 11 | use libsyntax2::{ |
14 | TextUnit, | ||
15 | text_utils::contains_offset_nonstrict, | 12 | text_utils::contains_offset_nonstrict, |
16 | }; | 13 | }; |
17 | 14 | ||
@@ -26,8 +23,8 @@ pub fn handle_syntax_tree( | |||
26 | params: req::SyntaxTreeParams, | 23 | params: req::SyntaxTreeParams, |
27 | ) -> Result<String> { | 24 | ) -> Result<String> { |
28 | let id = params.text_document.try_conv_with(&world)?; | 25 | let id = params.text_document.try_conv_with(&world)?; |
29 | let file = world.analysis().file_syntax(id)?; | 26 | let res = world.analysis().syntax_tree(id); |
30 | Ok(libeditor::syntax_tree(&file)) | 27 | Ok(res) |
31 | } | 28 | } |
32 | 29 | ||
33 | pub fn handle_extend_selection( | 30 | pub fn handle_extend_selection( |
@@ -35,11 +32,11 @@ pub fn handle_extend_selection( | |||
35 | params: req::ExtendSelectionParams, | 32 | params: req::ExtendSelectionParams, |
36 | ) -> Result<req::ExtendSelectionResult> { | 33 | ) -> Result<req::ExtendSelectionResult> { |
37 | let file_id = params.text_document.try_conv_with(&world)?; | 34 | let file_id = params.text_document.try_conv_with(&world)?; |
38 | let file = world.analysis().file_syntax(file_id)?; | 35 | let file = world.analysis().file_syntax(file_id); |
39 | let line_index = world.analysis().file_line_index(file_id)?; | 36 | let line_index = world.analysis().file_line_index(file_id); |
40 | let selections = params.selections.into_iter() | 37 | let selections = params.selections.into_iter() |
41 | .map_conv_with(&line_index) | 38 | .map_conv_with(&line_index) |
42 | .map(|r| libeditor::extend_selection(&file, r).unwrap_or(r)) | 39 | .map(|r| world.analysis().extend_selection(&file, r)) |
43 | .map_conv_with(&line_index) | 40 | .map_conv_with(&line_index) |
44 | .collect(); | 41 | .collect(); |
45 | Ok(req::ExtendSelectionResult { selections }) | 42 | Ok(req::ExtendSelectionResult { selections }) |
@@ -50,13 +47,13 @@ pub fn handle_find_matching_brace( | |||
50 | params: req::FindMatchingBraceParams, | 47 | params: req::FindMatchingBraceParams, |
51 | ) -> Result<Vec<Position>> { | 48 | ) -> Result<Vec<Position>> { |
52 | let file_id = params.text_document.try_conv_with(&world)?; | 49 | let file_id = params.text_document.try_conv_with(&world)?; |
53 | let file = world.analysis().file_syntax(file_id)?; | 50 | let file = world.analysis().file_syntax(file_id); |
54 | let line_index = world.analysis().file_line_index(file_id)?; | 51 | let line_index = world.analysis().file_line_index(file_id); |
55 | let res = params.offsets | 52 | let res = params.offsets |
56 | .into_iter() | 53 | .into_iter() |
57 | .map_conv_with(&line_index) | 54 | .map_conv_with(&line_index) |
58 | .map(|offset| { | 55 | .map(|offset| { |
59 | libeditor::matching_brace(&file, offset).unwrap_or(offset) | 56 | world.analysis().matching_brace(&file, offset).unwrap_or(offset) |
60 | }) | 57 | }) |
61 | .map_conv_with(&line_index) | 58 | .map_conv_with(&line_index) |
62 | .collect(); | 59 | .collect(); |
@@ -66,13 +63,31 @@ pub fn handle_find_matching_brace( | |||
66 | pub fn handle_join_lines( | 63 | pub fn handle_join_lines( |
67 | world: ServerWorld, | 64 | world: ServerWorld, |
68 | params: req::JoinLinesParams, | 65 | params: req::JoinLinesParams, |
69 | ) -> Result<Vec<TextEdit>> { | 66 | ) -> Result<req::SourceChange> { |
70 | let file_id = params.text_document.try_conv_with(&world)?; | 67 | let file_id = params.text_document.try_conv_with(&world)?; |
71 | let file = world.analysis().file_syntax(file_id)?; | 68 | let line_index = world.analysis().file_line_index(file_id); |
72 | let line_index = world.analysis().file_line_index(file_id)?; | ||
73 | let range = params.range.conv_with(&line_index); | 69 | let range = params.range.conv_with(&line_index); |
74 | let res = libeditor::join_lines(&file, range); | 70 | world.analysis().join_lines(file_id, range) |
75 | Ok(res.edit.conv_with(&line_index)) | 71 | .try_conv_with(&world) |
72 | } | ||
73 | |||
74 | pub fn handle_on_type_formatting( | ||
75 | world: ServerWorld, | ||
76 | params: req::DocumentOnTypeFormattingParams, | ||
77 | ) -> Result<Option<Vec<TextEdit>>> { | ||
78 | if params.ch != "=" { | ||
79 | return Ok(None); | ||
80 | } | ||
81 | |||
82 | let file_id = params.text_document.try_conv_with(&world)?; | ||
83 | let line_index = world.analysis().file_line_index(file_id); | ||
84 | let offset = params.position.conv_with(&line_index); | ||
85 | let edits = match world.analysis().on_eq_typed(file_id, offset) { | ||
86 | None => return Ok(None), | ||
87 | Some(mut action) => action.source_file_edits.pop().unwrap().edits, | ||
88 | }; | ||
89 | let edits = edits.into_iter().map_conv_with(&line_index).collect(); | ||
90 | Ok(Some(edits)) | ||
76 | } | 91 | } |
77 | 92 | ||
78 | pub fn handle_document_symbol( | 93 | pub fn handle_document_symbol( |
@@ -80,12 +95,11 @@ pub fn handle_document_symbol( | |||
80 | params: req::DocumentSymbolParams, | 95 | params: req::DocumentSymbolParams, |
81 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 96 | ) -> Result<Option<req::DocumentSymbolResponse>> { |
82 | let file_id = params.text_document.try_conv_with(&world)?; | 97 | let file_id = params.text_document.try_conv_with(&world)?; |
83 | let file = world.analysis().file_syntax(file_id)?; | 98 | let line_index = world.analysis().file_line_index(file_id); |
84 | let line_index = world.analysis().file_line_index(file_id)?; | ||
85 | 99 | ||
86 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 100 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
87 | 101 | ||
88 | for symbol in libeditor::file_structure(&file) { | 102 | for symbol in world.analysis().file_structure(file_id) { |
89 | let doc_symbol = DocumentSymbol { | 103 | let doc_symbol = DocumentSymbol { |
90 | name: symbol.label, | 104 | name: symbol.label, |
91 | detail: Some("".to_string()), | 105 | detail: Some("".to_string()), |
@@ -114,130 +128,6 @@ pub fn handle_document_symbol( | |||
114 | Ok(Some(req::DocumentSymbolResponse::Nested(res))) | 128 | Ok(Some(req::DocumentSymbolResponse::Nested(res))) |
115 | } | 129 | } |
116 | 130 | ||
117 | pub fn handle_code_action( | ||
118 | world: ServerWorld, | ||
119 | params: req::CodeActionParams, | ||
120 | ) -> Result<Option<Vec<Command>>> { | ||
121 | let file_id = params.text_document.try_conv_with(&world)?; | ||
122 | let file = world.analysis().file_syntax(file_id)?; | ||
123 | let line_index = world.analysis().file_line_index(file_id)?; | ||
124 | let offset = params.range.conv_with(&line_index).start(); | ||
125 | let mut res = Vec::new(); | ||
126 | |||
127 | let actions = &[ | ||
128 | (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), | ||
129 | (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), | ||
130 | (ActionId::AddImpl, libeditor::add_impl(&file, offset).is_some()), | ||
131 | ]; | ||
132 | |||
133 | for (id, edit) in actions { | ||
134 | if *edit { | ||
135 | let cmd = apply_code_action_cmd(*id, params.text_document.clone(), offset); | ||
136 | res.push(cmd); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | for (diag, quick_fix) in world.analysis().diagnostics(file_id)? { | ||
141 | let quick_fix = match quick_fix { | ||
142 | Some(quick_fix) => quick_fix, | ||
143 | None => continue, | ||
144 | }; | ||
145 | if !contains_offset_nonstrict(diag.range, offset) { | ||
146 | continue; | ||
147 | } | ||
148 | let mut ops = Vec::new(); | ||
149 | for op in quick_fix.fs_ops { | ||
150 | let op = match op { | ||
151 | libanalysis::FsOp::CreateFile { anchor, path } => { | ||
152 | let uri = world.file_id_to_uri(anchor)?; | ||
153 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
154 | let uri = uri.join(path)?; | ||
155 | FsOp::CreateFile { uri } | ||
156 | }, | ||
157 | libanalysis::FsOp::MoveFile { file, path } => { | ||
158 | let src = world.file_id_to_uri(file)?; | ||
159 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
160 | let dst = src.join(path)?; | ||
161 | FsOp::MoveFile { src, dst } | ||
162 | }, | ||
163 | }; | ||
164 | ops.push(op) | ||
165 | } | ||
166 | let cmd = Command { | ||
167 | title: "Create module".to_string(), | ||
168 | command: "libsyntax-rust.fsEdit".to_string(), | ||
169 | arguments: Some(vec![to_value(ops).unwrap()]), | ||
170 | }; | ||
171 | res.push(cmd) | ||
172 | } | ||
173 | return Ok(Some(res)); | ||
174 | } | ||
175 | |||
176 | #[derive(Serialize)] | ||
177 | #[serde(tag = "type", rename_all = "camelCase")] | ||
178 | enum FsOp { | ||
179 | CreateFile { | ||
180 | #[serde(with = "url_serde")] | ||
181 | uri: Url | ||
182 | }, | ||
183 | MoveFile { | ||
184 | #[serde(with = "url_serde")] | ||
185 | src: Url, | ||
186 | #[serde(with = "url_serde")] | ||
187 | dst: Url, | ||
188 | } | ||
189 | } | ||
190 | |||
191 | pub fn handle_runnables( | ||
192 | world: ServerWorld, | ||
193 | params: req::RunnablesParams, | ||
194 | ) -> Result<Vec<req::Runnable>> { | ||
195 | let file_id = params.text_document.try_conv_with(&world)?; | ||
196 | let file = world.analysis().file_syntax(file_id)?; | ||
197 | let line_index = world.analysis().file_line_index(file_id)?; | ||
198 | let offset = params.position.map(|it| it.conv_with(&line_index)); | ||
199 | let mut res = Vec::new(); | ||
200 | for runnable in libeditor::runnables(&file) { | ||
201 | if let Some(offset) = offset { | ||
202 | if !contains_offset_nonstrict(runnable.range, offset) { | ||
203 | continue; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | let r = req::Runnable { | ||
208 | range: runnable.range.conv_with(&line_index), | ||
209 | label: match &runnable.kind { | ||
210 | libeditor::RunnableKind::Test { name } => | ||
211 | format!("test {}", name), | ||
212 | libeditor::RunnableKind::Bin => | ||
213 | "run binary".to_string(), | ||
214 | }, | ||
215 | bin: "cargo".to_string(), | ||
216 | args: match runnable.kind { | ||
217 | libeditor::RunnableKind::Test { name } => { | ||
218 | vec![ | ||
219 | "test".to_string(), | ||
220 | "--".to_string(), | ||
221 | name, | ||
222 | "--nocapture".to_string(), | ||
223 | ] | ||
224 | } | ||
225 | libeditor::RunnableKind::Bin => vec!["run".to_string()] | ||
226 | }, | ||
227 | env: { | ||
228 | let mut m = HashMap::new(); | ||
229 | m.insert( | ||
230 | "RUST_BACKTRACE".to_string(), | ||
231 | "short".to_string(), | ||
232 | ); | ||
233 | m | ||
234 | } | ||
235 | }; | ||
236 | res.push(r); | ||
237 | } | ||
238 | return Ok(res); | ||
239 | } | ||
240 | |||
241 | pub fn handle_workspace_symbol( | 131 | pub fn handle_workspace_symbol( |
242 | world: ServerWorld, | 132 | world: ServerWorld, |
243 | params: req::WorkspaceSymbolParams, | 133 | params: req::WorkspaceSymbolParams, |
@@ -265,8 +155,8 @@ pub fn handle_workspace_symbol( | |||
265 | 155 | ||
266 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { | 156 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { |
267 | let mut res = Vec::new(); | 157 | let mut res = Vec::new(); |
268 | for (file_id, symbol) in world.analysis().world_symbols(query) { | 158 | for (file_id, symbol) in world.analysis().symbol_search(query) { |
269 | let line_index = world.analysis().file_line_index(file_id)?; | 159 | let line_index = world.analysis().file_line_index(file_id); |
270 | let info = SymbolInformation { | 160 | let info = SymbolInformation { |
271 | name: symbol.name.to_string(), | 161 | name: symbol.name.to_string(), |
272 | kind: symbol.kind.conv(), | 162 | kind: symbol.kind.conv(), |
@@ -287,11 +177,11 @@ pub fn handle_goto_definition( | |||
287 | params: req::TextDocumentPositionParams, | 177 | params: req::TextDocumentPositionParams, |
288 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 178 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
289 | let file_id = params.text_document.try_conv_with(&world)?; | 179 | let file_id = params.text_document.try_conv_with(&world)?; |
290 | let line_index = world.analysis().file_line_index(file_id)?; | 180 | let line_index = world.analysis().file_line_index(file_id); |
291 | let offset = params.position.conv_with(&line_index); | 181 | let offset = params.position.conv_with(&line_index); |
292 | let mut res = Vec::new(); | 182 | let mut res = Vec::new(); |
293 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset)? { | 183 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset) { |
294 | let line_index = world.analysis().file_line_index(file_id)?; | 184 | let line_index = world.analysis().file_line_index(file_id); |
295 | let location = to_location( | 185 | let location = to_location( |
296 | file_id, symbol.node_range, | 186 | file_id, symbol.node_range, |
297 | &world, &line_index, | 187 | &world, &line_index, |
@@ -308,7 +198,7 @@ pub fn handle_parent_module( | |||
308 | let file_id = params.try_conv_with(&world)?; | 198 | let file_id = params.try_conv_with(&world)?; |
309 | let mut res = Vec::new(); | 199 | let mut res = Vec::new(); |
310 | for (file_id, symbol) in world.analysis().parent_module(file_id) { | 200 | for (file_id, symbol) in world.analysis().parent_module(file_id) { |
311 | let line_index = world.analysis().file_line_index(file_id)?; | 201 | let line_index = world.analysis().file_line_index(file_id); |
312 | let location = to_location( | 202 | let location = to_location( |
313 | file_id, symbol.node_range, | 203 | file_id, symbol.node_range, |
314 | &world, &line_index | 204 | &world, &line_index |
@@ -318,15 +208,71 @@ pub fn handle_parent_module( | |||
318 | Ok(res) | 208 | Ok(res) |
319 | } | 209 | } |
320 | 210 | ||
211 | pub fn handle_runnables( | ||
212 | world: ServerWorld, | ||
213 | params: req::RunnablesParams, | ||
214 | ) -> Result<Vec<req::Runnable>> { | ||
215 | let file_id = params.text_document.try_conv_with(&world)?; | ||
216 | let line_index = world.analysis().file_line_index(file_id); | ||
217 | let offset = params.position.map(|it| it.conv_with(&line_index)); | ||
218 | let mut res = Vec::new(); | ||
219 | for runnable in world.analysis().runnables(file_id) { | ||
220 | if let Some(offset) = offset { | ||
221 | if !contains_offset_nonstrict(runnable.range, offset) { | ||
222 | continue; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | let r = req::Runnable { | ||
227 | range: runnable.range.conv_with(&line_index), | ||
228 | label: match &runnable.kind { | ||
229 | RunnableKind::Test { name } => | ||
230 | format!("test {}", name), | ||
231 | RunnableKind::Bin => | ||
232 | "run binary".to_string(), | ||
233 | }, | ||
234 | bin: "cargo".to_string(), | ||
235 | args: match runnable.kind { | ||
236 | RunnableKind::Test { name } => { | ||
237 | vec![ | ||
238 | "test".to_string(), | ||
239 | "--".to_string(), | ||
240 | name, | ||
241 | "--nocapture".to_string(), | ||
242 | ] | ||
243 | } | ||
244 | RunnableKind::Bin => vec!["run".to_string()] | ||
245 | }, | ||
246 | env: { | ||
247 | let mut m = HashMap::new(); | ||
248 | m.insert( | ||
249 | "RUST_BACKTRACE".to_string(), | ||
250 | "short".to_string(), | ||
251 | ); | ||
252 | m | ||
253 | } | ||
254 | }; | ||
255 | res.push(r); | ||
256 | } | ||
257 | return Ok(res); | ||
258 | } | ||
259 | |||
260 | pub fn handle_decorations( | ||
261 | world: ServerWorld, | ||
262 | params: TextDocumentIdentifier, | ||
263 | ) -> Result<Vec<Decoration>> { | ||
264 | let file_id = params.try_conv_with(&world)?; | ||
265 | Ok(highlight(&world, file_id)) | ||
266 | } | ||
267 | |||
321 | pub fn handle_completion( | 268 | pub fn handle_completion( |
322 | world: ServerWorld, | 269 | world: ServerWorld, |
323 | params: req::CompletionParams, | 270 | params: req::CompletionParams, |
324 | ) -> Result<Option<req::CompletionResponse>> { | 271 | ) -> Result<Option<req::CompletionResponse>> { |
325 | let file_id = params.text_document.try_conv_with(&world)?; | 272 | let file_id = params.text_document.try_conv_with(&world)?; |
326 | let file = world.analysis().file_syntax(file_id)?; | 273 | let line_index = world.analysis().file_line_index(file_id); |
327 | let line_index = world.analysis().file_line_index(file_id)?; | ||
328 | let offset = params.position.conv_with(&line_index); | 274 | let offset = params.position.conv_with(&line_index); |
329 | let items = match libeditor::scope_completion(&file, offset) { | 275 | let items = match world.analysis().completions(file_id, offset) { |
330 | None => return Ok(None), | 276 | None => return Ok(None), |
331 | Some(items) => items, | 277 | Some(items) => items, |
332 | }; | 278 | }; |
@@ -348,91 +294,33 @@ pub fn handle_completion( | |||
348 | Ok(Some(req::CompletionResponse::Array(items))) | 294 | Ok(Some(req::CompletionResponse::Array(items))) |
349 | } | 295 | } |
350 | 296 | ||
351 | pub fn handle_on_type_formatting( | 297 | pub fn handle_code_action( |
352 | world: ServerWorld, | 298 | world: ServerWorld, |
353 | params: req::DocumentOnTypeFormattingParams, | 299 | params: req::CodeActionParams, |
354 | ) -> Result<Option<Vec<TextEdit>>> { | 300 | ) -> Result<Option<Vec<Command>>> { |
355 | if params.ch != "=" { | ||
356 | return Ok(None); | ||
357 | } | ||
358 | |||
359 | let file_id = params.text_document.try_conv_with(&world)?; | 301 | let file_id = params.text_document.try_conv_with(&world)?; |
360 | let line_index = world.analysis().file_line_index(file_id)?; | 302 | let line_index = world.analysis().file_line_index(file_id); |
361 | let offset = params.position.conv_with(&line_index); | 303 | let offset = params.range.conv_with(&line_index).start(); |
362 | let file = world.analysis().file_syntax(file_id)?; | ||
363 | let action = match libeditor::on_eq_typed(&file, offset) { | ||
364 | None => return Ok(None), | ||
365 | Some(action) => action, | ||
366 | }; | ||
367 | Ok(Some(action.edit.conv_with(&line_index))) | ||
368 | } | ||
369 | |||
370 | pub fn handle_execute_command( | ||
371 | world: ServerWorld, | ||
372 | mut params: req::ExecuteCommandParams, | ||
373 | ) -> Result<(req::ApplyWorkspaceEditParams, Option<Position>)> { | ||
374 | if params.command.as_str() != "apply_code_action" { | ||
375 | bail!("unknown cmd: {:?}", params.command); | ||
376 | } | ||
377 | if params.arguments.len() != 1 { | ||
378 | bail!("expected single arg, got {}", params.arguments.len()); | ||
379 | } | ||
380 | let arg = params.arguments.pop().unwrap(); | ||
381 | let arg: ActionRequest = from_value(arg)?; | ||
382 | let file_id = arg.text_document.try_conv_with(&world)?; | ||
383 | let file = world.analysis().file_syntax(file_id)?; | ||
384 | let action_result = match arg.id { | ||
385 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()), | ||
386 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()), | ||
387 | ActionId::AddImpl => libeditor::add_impl(&file, arg.offset).map(|f| f()), | ||
388 | }.ok_or_else(|| format_err!("command not applicable"))?; | ||
389 | let line_index = world.analysis().file_line_index(file_id)?; | ||
390 | let mut changes = HashMap::new(); | ||
391 | changes.insert( | ||
392 | arg.text_document.uri, | ||
393 | action_result.edit.conv_with(&line_index), | ||
394 | ); | ||
395 | let edit = WorkspaceEdit { | ||
396 | changes: Some(changes), | ||
397 | document_changes: None, | ||
398 | }; | ||
399 | let edit = req::ApplyWorkspaceEditParams { edit }; | ||
400 | let cursor_pos = action_result.cursor_position | ||
401 | .map(|off| off.conv_with(&line_index)); | ||
402 | Ok((edit, cursor_pos)) | ||
403 | } | ||
404 | 304 | ||
405 | #[derive(Serialize, Deserialize)] | 305 | let assists = world.analysis().assists(file_id, offset).into_iter(); |
406 | struct ActionRequest { | 306 | let fixes = world.analysis().diagnostics(file_id).into_iter() |
407 | id: ActionId, | 307 | .filter_map(|d| Some((d.range, d.fix?))) |
408 | text_document: TextDocumentIdentifier, | 308 | .filter(|(range, _fix)| contains_offset_nonstrict(*range, offset)) |
409 | offset: TextUnit, | 309 | .map(|(_range, fix)| fix); |
410 | } | ||
411 | 310 | ||
412 | fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: TextUnit) -> Command { | 311 | let mut res = Vec::new(); |
413 | let action_request = ActionRequest { id, text_document: doc, offset }; | 312 | for source_edit in assists.chain(fixes) { |
414 | Command { | 313 | let title = source_edit.label.clone(); |
415 | title: id.title().to_string(), | 314 | let edit = source_edit.try_conv_with(&world)?; |
416 | command: "apply_code_action".to_string(), | 315 | let cmd = Command { |
417 | arguments: Some(vec![to_value(action_request).unwrap()]), | 316 | title, |
317 | command: "libsyntax-rust.applySourceChange".to_string(), | ||
318 | arguments: Some(vec![to_value(edit).unwrap()]), | ||
319 | }; | ||
320 | res.push(cmd); | ||
418 | } | 321 | } |
419 | } | ||
420 | 322 | ||
421 | #[derive(Serialize, Deserialize, Clone, Copy)] | 323 | Ok(Some(res)) |
422 | enum ActionId { | ||
423 | FlipComma, | ||
424 | AddDerive, | ||
425 | AddImpl, | ||
426 | } | ||
427 | |||
428 | impl ActionId { | ||
429 | fn title(&self) -> &'static str { | ||
430 | match *self { | ||
431 | ActionId::FlipComma => "Flip `,`", | ||
432 | ActionId::AddDerive => "Add `#[derive]`", | ||
433 | ActionId::AddImpl => "Add impl", | ||
434 | } | ||
435 | } | ||
436 | } | 324 | } |
437 | 325 | ||
438 | pub fn publish_diagnostics( | 326 | pub fn publish_diagnostics( |
@@ -440,28 +328,20 @@ pub fn publish_diagnostics( | |||
440 | uri: Url | 328 | uri: Url |
441 | ) -> Result<req::PublishDiagnosticsParams> { | 329 | ) -> Result<req::PublishDiagnosticsParams> { |
442 | let file_id = world.uri_to_file_id(&uri)?; | 330 | let file_id = world.uri_to_file_id(&uri)?; |
443 | let line_index = world.analysis().file_line_index(file_id)?; | 331 | let line_index = world.analysis().file_line_index(file_id); |
444 | let diagnostics = world.analysis().diagnostics(file_id)? | 332 | let diagnostics = world.analysis().diagnostics(file_id) |
445 | .into_iter() | 333 | .into_iter() |
446 | .map(|(d, _quick_fix)| Diagnostic { | 334 | .map(|d| Diagnostic { |
447 | range: d.range.conv_with(&line_index), | 335 | range: d.range.conv_with(&line_index), |
448 | severity: Some(DiagnosticSeverity::Error), | 336 | severity: Some(DiagnosticSeverity::Error), |
449 | code: None, | 337 | code: None, |
450 | source: Some("libsyntax2".to_string()), | 338 | source: Some("libsyntax2".to_string()), |
451 | message: d.msg, | 339 | message: d.message, |
452 | related_information: None, | 340 | related_information: None, |
453 | }).collect(); | 341 | }).collect(); |
454 | Ok(req::PublishDiagnosticsParams { uri, diagnostics }) | 342 | Ok(req::PublishDiagnosticsParams { uri, diagnostics }) |
455 | } | 343 | } |
456 | 344 | ||
457 | pub fn handle_decorations( | ||
458 | world: ServerWorld, | ||
459 | params: TextDocumentIdentifier, | ||
460 | ) -> Result<Vec<Decoration>> { | ||
461 | let file_id = params.try_conv_with(&world)?; | ||
462 | highlight(&world, file_id) | ||
463 | } | ||
464 | |||
465 | pub fn publish_decorations( | 345 | pub fn publish_decorations( |
466 | world: ServerWorld, | 346 | world: ServerWorld, |
467 | uri: Url | 347 | uri: Url |
@@ -469,18 +349,16 @@ pub fn publish_decorations( | |||
469 | let file_id = world.uri_to_file_id(&uri)?; | 349 | let file_id = world.uri_to_file_id(&uri)?; |
470 | Ok(req::PublishDecorationsParams { | 350 | Ok(req::PublishDecorationsParams { |
471 | uri, | 351 | uri, |
472 | decorations: highlight(&world, file_id)? | 352 | decorations: highlight(&world, file_id), |
473 | }) | 353 | }) |
474 | } | 354 | } |
475 | 355 | ||
476 | fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> { | 356 | fn highlight(world: &ServerWorld, file_id: FileId) -> Vec<Decoration> { |
477 | let file = world.analysis().file_syntax(file_id)?; | 357 | let line_index = world.analysis().file_line_index(file_id); |
478 | let line_index = world.analysis().file_line_index(file_id)?; | 358 | world.analysis().highlight(file_id) |
479 | let res = libeditor::highlight(&file) | ||
480 | .into_iter() | 359 | .into_iter() |
481 | .map(|h| Decoration { | 360 | .map(|h| Decoration { |
482 | range: h.range.conv_with(&line_index), | 361 | range: h.range.conv_with(&line_index), |
483 | tag: h.tag, | 362 | tag: h.tag, |
484 | }).collect(); | 363 | }).collect() |
485 | Ok(res) | ||
486 | } | 364 | } |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index accb13878..0f66248a5 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -7,7 +7,6 @@ use std::{ | |||
7 | use threadpool::ThreadPool; | 7 | use threadpool::ThreadPool; |
8 | use crossbeam_channel::{Sender, Receiver}; | 8 | use crossbeam_channel::{Sender, Receiver}; |
9 | use languageserver_types::Url; | 9 | use languageserver_types::Url; |
10 | use serde_json::to_value; | ||
11 | 10 | ||
12 | use { | 11 | use { |
13 | req, dispatch, | 12 | req, dispatch, |
@@ -15,24 +14,6 @@ use { | |||
15 | io::{Io, RawMsg, RawRequest, RawNotification}, | 14 | io::{Io, RawMsg, RawRequest, RawNotification}, |
16 | vfs::FileEvent, | 15 | vfs::FileEvent, |
17 | server_world::{ServerWorldState, ServerWorld}, | 16 | server_world::{ServerWorldState, ServerWorld}, |
18 | main_loop::handlers::{ | ||
19 | handle_syntax_tree, | ||
20 | handle_extend_selection, | ||
21 | publish_diagnostics, | ||
22 | publish_decorations, | ||
23 | handle_document_symbol, | ||
24 | handle_code_action, | ||
25 | handle_execute_command, | ||
26 | handle_workspace_symbol, | ||
27 | handle_goto_definition, | ||
28 | handle_find_matching_brace, | ||
29 | handle_parent_module, | ||
30 | handle_join_lines, | ||
31 | handle_completion, | ||
32 | handle_runnables, | ||
33 | handle_decorations, | ||
34 | handle_on_type_formatting, | ||
35 | }, | ||
36 | }; | 17 | }; |
37 | 18 | ||
38 | pub(super) fn main_loop( | 19 | pub(super) fn main_loop( |
@@ -45,7 +26,6 @@ pub(super) fn main_loop( | |||
45 | info!("server initialized, serving requests"); | 26 | info!("server initialized, serving requests"); |
46 | let mut state = ServerWorldState::new(); | 27 | let mut state = ServerWorldState::new(); |
47 | 28 | ||
48 | let mut next_request_id = 0; | ||
49 | let mut pending_requests: HashSet<u64> = HashSet::new(); | 29 | let mut pending_requests: HashSet<u64> = HashSet::new(); |
50 | let mut fs_events_receiver = Some(&fs_events_receiver); | 30 | let mut fs_events_receiver = Some(&fs_events_receiver); |
51 | loop { | 31 | loop { |
@@ -78,12 +58,6 @@ pub(super) fn main_loop( | |||
78 | } | 58 | } |
79 | Event::Task(task) => { | 59 | Event::Task(task) => { |
80 | match task { | 60 | match task { |
81 | Task::Request(mut request) => { | ||
82 | request.id = next_request_id; | ||
83 | pending_requests.insert(next_request_id); | ||
84 | next_request_id += 1; | ||
85 | io.send(RawMsg::Request(request)); | ||
86 | } | ||
87 | Task::Respond(response) => | 61 | Task::Respond(response) => |
88 | io.send(RawMsg::Response(response)), | 62 | io.send(RawMsg::Response(response)), |
89 | Task::Notify(n) => | 63 | Task::Notify(n) => |
@@ -125,79 +99,26 @@ fn on_request( | |||
125 | sender: &Sender<Task>, | 99 | sender: &Sender<Task>, |
126 | req: RawRequest, | 100 | req: RawRequest, |
127 | ) -> Result<bool> { | 101 | ) -> Result<bool> { |
128 | let mut req = Some(req); | 102 | let mut pool_dispatcher = PoolDispatcher { |
129 | handle_request_on_threadpool::<req::SyntaxTree>( | 103 | req: Some(req), |
130 | &mut req, pool, world, sender, handle_syntax_tree, | 104 | pool, world, sender |
131 | )?; | 105 | }; |
132 | handle_request_on_threadpool::<req::ExtendSelection>( | 106 | pool_dispatcher |
133 | &mut req, pool, world, sender, handle_extend_selection, | 107 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? |
134 | )?; | 108 | .on::<req::ExtendSelection>(handlers::handle_extend_selection)? |
135 | handle_request_on_threadpool::<req::FindMatchingBrace>( | 109 | .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)? |
136 | &mut req, pool, world, sender, handle_find_matching_brace, | 110 | .on::<req::JoinLines>(handlers::handle_join_lines)? |
137 | )?; | 111 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
138 | handle_request_on_threadpool::<req::DocumentSymbolRequest>( | 112 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
139 | &mut req, pool, world, sender, handle_document_symbol, | 113 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
140 | )?; | 114 | .on::<req::GotoDefinition>(handlers::handle_goto_definition)? |
141 | handle_request_on_threadpool::<req::CodeActionRequest>( | 115 | .on::<req::ParentModule>(handlers::handle_parent_module)? |
142 | &mut req, pool, world, sender, handle_code_action, | 116 | .on::<req::Runnables>(handlers::handle_runnables)? |
143 | )?; | 117 | .on::<req::DecorationsRequest>(handlers::handle_decorations)? |
144 | handle_request_on_threadpool::<req::Runnables>( | 118 | .on::<req::Completion>(handlers::handle_completion)? |
145 | &mut req, pool, world, sender, handle_runnables, | 119 | .on::<req::CodeActionRequest>(handlers::handle_code_action)?; |
146 | )?; | ||
147 | handle_request_on_threadpool::<req::WorkspaceSymbol>( | ||
148 | &mut req, pool, world, sender, handle_workspace_symbol, | ||
149 | )?; | ||
150 | handle_request_on_threadpool::<req::GotoDefinition>( | ||
151 | &mut req, pool, world, sender, handle_goto_definition, | ||
152 | )?; | ||
153 | handle_request_on_threadpool::<req::Completion>( | ||
154 | &mut req, pool, world, sender, handle_completion, | ||
155 | )?; | ||
156 | handle_request_on_threadpool::<req::ParentModule>( | ||
157 | &mut req, pool, world, sender, handle_parent_module, | ||
158 | )?; | ||
159 | handle_request_on_threadpool::<req::JoinLines>( | ||
160 | &mut req, pool, world, sender, handle_join_lines, | ||
161 | )?; | ||
162 | handle_request_on_threadpool::<req::DecorationsRequest>( | ||
163 | &mut req, pool, world, sender, handle_decorations, | ||
164 | )?; | ||
165 | handle_request_on_threadpool::<req::OnTypeFormatting>( | ||
166 | &mut req, pool, world, sender, handle_on_type_formatting, | ||
167 | )?; | ||
168 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { | ||
169 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); | ||
170 | |||
171 | let world = world.snapshot(); | ||
172 | let sender = sender.clone(); | ||
173 | pool.execute(move || { | ||
174 | let (edit, cursor) = match handle_execute_command(world, params) { | ||
175 | Ok(res) => res, | ||
176 | Err(e) => return sender.send(Task::Die(e)), | ||
177 | }; | ||
178 | match to_value(edit) { | ||
179 | Err(e) => return sender.send(Task::Die(e.into())), | ||
180 | Ok(params) => { | ||
181 | let request = RawRequest { | ||
182 | id: 0, | ||
183 | method: <req::ApplyWorkspaceEdit as req::ClientRequest>::METHOD.to_string(), | ||
184 | params, | ||
185 | }; | ||
186 | sender.send(Task::Request(request)) | ||
187 | } | ||
188 | } | ||
189 | if let Some(cursor) = cursor { | ||
190 | let request = RawRequest { | ||
191 | id: 0, | ||
192 | method: <req::MoveCursor as req::ClientRequest>::METHOD.to_string(), | ||
193 | params: to_value(cursor).unwrap(), | ||
194 | }; | ||
195 | sender.send(Task::Request(request)) | ||
196 | } | ||
197 | }); | ||
198 | Ok(()) | ||
199 | })?; | ||
200 | 120 | ||
121 | let mut req = pool_dispatcher.req; | ||
201 | let mut shutdown = false; | 122 | let mut shutdown = false; |
202 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { | 123 | dispatch::handle_request::<req::Shutdown, _>(&mut req, |(), resp| { |
203 | let resp = resp.into_response(Ok(()))?; | 124 | let resp = resp.into_response(Ok(()))?; |
@@ -273,27 +194,33 @@ fn on_notification( | |||
273 | Ok(()) | 194 | Ok(()) |
274 | } | 195 | } |
275 | 196 | ||
276 | fn handle_request_on_threadpool<R: req::ClientRequest>( | 197 | struct PoolDispatcher<'a> { |
277 | req: &mut Option<RawRequest>, | 198 | req: Option<RawRequest>, |
278 | pool: &ThreadPool, | 199 | pool: &'a ThreadPool, |
279 | world: &ServerWorldState, | 200 | world: &'a ServerWorldState, |
280 | sender: &Sender<Task>, | 201 | sender: &'a Sender<Task>, |
281 | f: fn(ServerWorld, R::Params) -> Result<R::Result>, | 202 | } |
282 | ) -> Result<()> | 203 | |
283 | { | 204 | impl<'a> PoolDispatcher<'a> { |
284 | dispatch::handle_request::<R, _>(req, |params, resp| { | 205 | fn on<'b, R: req::ClientRequest>(&'b mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&'b mut Self> { |
285 | let world = world.snapshot(); | 206 | let world = self.world; |
286 | let sender = sender.clone(); | 207 | let sender = self.sender; |
287 | pool.execute(move || { | 208 | let pool = self.pool; |
288 | let res = f(world, params); | 209 | dispatch::handle_request::<R, _>(&mut self.req, |params, resp| { |
289 | let task = match resp.into_response(res) { | 210 | let world = world.snapshot(); |
290 | Ok(resp) => Task::Respond(resp), | 211 | let sender = sender.clone(); |
291 | Err(e) => Task::Die(e), | 212 | pool.execute(move || { |
292 | }; | 213 | let res = f(world, params); |
293 | sender.send(task); | 214 | let task = match resp.into_response(res) { |
294 | }); | 215 | Ok(resp) => Task::Respond(resp), |
295 | Ok(()) | 216 | Err(e) => Task::Die(e), |
296 | }) | 217 | }; |
218 | sender.send(task); | ||
219 | }); | ||
220 | Ok(()) | ||
221 | })?; | ||
222 | Ok(self) | ||
223 | } | ||
297 | } | 224 | } |
298 | 225 | ||
299 | fn update_file_notifications_on_threadpool( | 226 | fn update_file_notifications_on_threadpool( |
@@ -303,7 +230,7 @@ fn update_file_notifications_on_threadpool( | |||
303 | uri: Url, | 230 | uri: Url, |
304 | ) { | 231 | ) { |
305 | pool.execute(move || { | 232 | pool.execute(move || { |
306 | match publish_diagnostics(world.clone(), uri.clone()) { | 233 | match handlers::publish_diagnostics(world.clone(), uri.clone()) { |
307 | Err(e) => { | 234 | Err(e) => { |
308 | error!("failed to compute diagnostics: {:?}", e) | 235 | error!("failed to compute diagnostics: {:?}", e) |
309 | } | 236 | } |
@@ -312,7 +239,7 @@ fn update_file_notifications_on_threadpool( | |||
312 | sender.send(Task::Notify(not)); | 239 | sender.send(Task::Notify(not)); |
313 | } | 240 | } |
314 | } | 241 | } |
315 | match publish_decorations(world, uri) { | 242 | match handlers::publish_decorations(world, uri) { |
316 | Err(e) => { | 243 | Err(e) => { |
317 | error!("failed to compute decorations: {:?}", e) | 244 | error!("failed to compute decorations: {:?}", e) |
318 | } | 245 | } |