diff options
Diffstat (limited to 'crates/server/src/main_loop/handlers.rs')
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 384 |
1 files changed, 131 insertions, 253 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 | } |