1 //! Requests that the RLS can respond to.
2 
3 use std::collections::HashMap;
4 use std::path::Path;
5 use std::sync::atomic::Ordering;
6 
7 use itertools::Itertools;
8 use jsonrpc_core::types::ErrorCode;
9 use log::{debug, trace, warn};
10 use rls_analysis::SymbolQuery;
11 use rls_data as data;
12 use rls_span as span;
13 use rls_vfs::FileContents;
14 use rustfmt_nightly::{Edition as RustfmtEdition, FileLines, FileName, Range as RustfmtRange};
15 use serde_derive::{Deserialize, Serialize};
16 use url::Url;
17 
18 use crate::actions::hover;
19 use crate::actions::run::collect_run_actions;
20 use crate::actions::InitActionContext;
21 use crate::build::Edition;
22 use crate::lsp_data;
23 use crate::lsp_data::request::ApplyWorkspaceEdit;
24 pub use crate::lsp_data::request::{
25     CodeActionRequest as CodeAction, CodeLensRequest, Completion,
26     DocumentHighlightRequest as DocumentHighlight, DocumentSymbolRequest as Symbols,
27     ExecuteCommand, Formatting, GotoDefinition as Definition, GotoImplementation as Implementation,
28     HoverRequest as Hover, RangeFormatting, References, Rename,
29     ResolveCompletionItem as ResolveCompletion, WorkspaceSymbol,
30 };
31 use crate::lsp_data::*;
32 use crate::server;
33 use crate::server::{Ack, Output, Request, RequestAction, ResponseError, ResponseWithMessage};
34 
35 /// The result of a deglob action for a single wildcard import.
36 ///
37 /// The `location` is the position of the wildcard.
38 /// `new_text` is the text which should replace the wildcard.
39 #[derive(Debug, Deserialize, Serialize)]
40 pub struct DeglobResult {
41     /// The `Location` of the "*" character in a wildcard import.
42     pub location: Location,
43     /// The replacement text.
44     pub new_text: String,
45 }
46 
47 impl RequestAction for WorkspaceSymbol {
48     type Response = Vec<SymbolInformation>;
49 
fallback_response() -> Result<Self::Response, ResponseError>50     fn fallback_response() -> Result<Self::Response, ResponseError> {
51         Ok(vec![])
52     }
53 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>54     fn handle(
55         ctx: InitActionContext,
56         params: Self::Params,
57     ) -> Result<Self::Response, ResponseError> {
58         let analysis = ctx.analysis;
59         let query = SymbolQuery::subsequence(&params.query).limit(512);
60         let defs = analysis.query_defs(query).unwrap_or_else(|_| vec![]);
61 
62         Ok(defs
63             .into_iter()
64             // Sometimes analysis will return duplicate symbols
65             // for the same location, fix that up.
66             .unique_by(|d| (d.span.clone(), d.name.clone()))
67             .map(|d| SymbolInformation {
68                 name: d.name,
69                 kind: source_kind_from_def_kind(d.kind),
70                 location: ls_util::rls_to_location(&d.span),
71                 container_name: d
72                     .parent
73                     .and_then(|id| analysis.get_def(id).ok())
74                     .map(|parent| parent.name),
75                 deprecated: None,
76             })
77             .collect())
78     }
79 }
80 
81 impl RequestAction for Symbols {
82     type Response = Vec<SymbolInformation>;
83 
fallback_response() -> Result<Self::Response, ResponseError>84     fn fallback_response() -> Result<Self::Response, ResponseError> {
85         Ok(vec![])
86     }
87 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>88     fn handle(
89         ctx: InitActionContext,
90         params: Self::Params,
91     ) -> Result<Self::Response, ResponseError> {
92         let analysis = ctx.analysis;
93 
94         let file_path = parse_file_path!(&params.text_document.uri, "symbols")?;
95 
96         let symbols = analysis.symbols(&file_path).unwrap_or_else(|_| vec![]);
97 
98         Ok(symbols
99             .into_iter()
100             .filter(|s| !s.name.is_empty()) // HACK: VS Code chokes on empty names
101             .filter(|s| {
102                 let range = ls_util::rls_to_range(s.span.range);
103                 range.start != range.end
104             })
105             .map(|s| SymbolInformation {
106                 name: s.name,
107                 kind: source_kind_from_def_kind(s.kind),
108                 location: ls_util::rls_to_location(&s.span),
109                 container_name: s
110                     .parent
111                     .and_then(|id| analysis.get_def(id).ok())
112                     .map(|parent| parent.name),
113                 deprecated: None,
114             })
115             .collect())
116     }
117 }
118 
119 impl RequestAction for Hover {
120     type Response = lsp_data::Hover;
121 
fallback_response() -> Result<Self::Response, ResponseError>122     fn fallback_response() -> Result<Self::Response, ResponseError> {
123         Ok(lsp_data::Hover { contents: HoverContents::Array(vec![]), range: None })
124     }
125 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>126     fn handle(
127         ctx: InitActionContext,
128         params: Self::Params,
129     ) -> Result<Self::Response, ResponseError> {
130         let tooltip = hover::tooltip(&ctx, &params)?;
131 
132         Ok(lsp_data::Hover {
133             contents: HoverContents::Array(tooltip.contents),
134             range: Some(ls_util::rls_to_range(tooltip.range)),
135         })
136     }
137 }
138 
139 impl RequestAction for Implementation {
140     type Response = Vec<Location>;
141 
fallback_response() -> Result<Self::Response, ResponseError>142     fn fallback_response() -> Result<Self::Response, ResponseError> {
143         Ok(vec![])
144     }
145 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>146     fn handle(
147         ctx: InitActionContext,
148         params: Self::Params,
149     ) -> Result<Self::Response, ResponseError> {
150         let file_path = parse_file_path!(&params.text_document.uri, "find_impls")?;
151         let span = ctx.convert_pos_to_span(file_path, params.position);
152         let analysis = ctx.analysis;
153 
154         let type_id = analysis.id(&span).map_err(|_| ResponseError::Empty)?;
155         let result = analysis
156             .find_impls(type_id)
157             .map(|spans| spans.into_iter().map(|x| ls_util::rls_to_location(&x)).collect());
158 
159         trace!("find_impls: {:?}", result);
160 
161         result.map_err(|_| {
162             ResponseError::Message(
163                 ErrorCode::InternalError,
164                 "Find Implementations failed to complete successfully".into(),
165             )
166         })
167     }
168 }
169 
170 impl RequestAction for Definition {
171     type Response = Vec<Location>;
172 
fallback_response() -> Result<Self::Response, ResponseError>173     fn fallback_response() -> Result<Self::Response, ResponseError> {
174         Ok(vec![])
175     }
176 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>177     fn handle(
178         ctx: InitActionContext,
179         params: Self::Params,
180     ) -> Result<Self::Response, ResponseError> {
181         // Save-analysis thread.
182         let file_path = parse_file_path!(&params.text_document.uri, "goto_def")?;
183         let span = ctx.convert_pos_to_span(file_path.clone(), params.position);
184 
185         if let Ok(out) = ctx.analysis.goto_def(&span) {
186             let result = vec![ls_util::rls_to_location(&out)];
187             trace!("goto_def (compiler): {:?}", result);
188             Ok(result)
189         } else {
190             let racer_enabled = {
191                 let config = ctx.config.lock().unwrap();
192                 config.racer_completion
193             };
194             if racer_enabled {
195                 let cache = ctx.racer_cache();
196                 let session = ctx.racer_session(&cache);
197                 let location = pos_to_racer_location(params.position);
198 
199                 let r = racer::find_definition(file_path, location, &session)
200                     .and_then(|rm| location_from_racer_match(&rm))
201                     .map(|l| vec![l])
202                     .unwrap_or_default();
203 
204                 trace!("goto_def (Racer): {:?}", r);
205                 Ok(r)
206             } else {
207                 Self::fallback_response()
208             }
209         }
210     }
211 }
212 
213 impl RequestAction for References {
214     type Response = Vec<Location>;
215 
fallback_response() -> Result<Self::Response, ResponseError>216     fn fallback_response() -> Result<Self::Response, ResponseError> {
217         Ok(vec![])
218     }
219 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>220     fn handle(
221         ctx: InitActionContext,
222         params: Self::Params,
223     ) -> Result<Self::Response, ResponseError> {
224         let file_path =
225             parse_file_path!(&params.text_document_position.text_document.uri, "find_all_refs")?;
226         let span = ctx.convert_pos_to_span(file_path, params.text_document_position.position);
227 
228         let result =
229             match ctx.analysis.find_all_refs(&span, params.context.include_declaration, false) {
230                 Ok(t) => t,
231                 _ => vec![],
232             };
233 
234         Ok(result.iter().map(|item| ls_util::rls_to_location(item)).collect())
235     }
236 }
237 
238 impl RequestAction for Completion {
239     type Response = Vec<CompletionItem>;
240 
fallback_response() -> Result<Self::Response, ResponseError>241     fn fallback_response() -> Result<Self::Response, ResponseError> {
242         Ok(vec![])
243     }
244 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>245     fn handle(
246         ctx: InitActionContext,
247         params: Self::Params,
248     ) -> Result<Self::Response, ResponseError> {
249         if !ctx.config.lock().unwrap().racer_completion {
250             return Self::fallback_response();
251         }
252 
253         let file_path =
254             parse_file_path!(&params.text_document_position.text_document.uri, "complete")?;
255 
256         let cache = ctx.racer_cache();
257         let session = ctx.racer_session(&cache);
258 
259         let location = pos_to_racer_location(params.text_document_position.position);
260         let results = racer::complete_from_file(&file_path, location, &session);
261         let is_use_stmt = racer::is_use_stmt(&file_path, location, &session);
262 
263         let code_completion_has_snippet_support =
264             ctx.client_capabilities.code_completion_has_snippet_support;
265 
266         Ok(results
267             .map(|comp| {
268                 let mut item = completion_item_from_racer_match(&comp);
269                 if is_use_stmt && comp.mtype.is_function() {
270                     item.insert_text = Some(comp.matchstr);
271                 } else if code_completion_has_snippet_support {
272                     let snippet = racer::snippet_for_match(&comp, &session);
273                     if !snippet.is_empty() {
274                         item.insert_text = Some(snippet);
275                         item.insert_text_format = Some(InsertTextFormat::Snippet);
276                     }
277                 }
278                 item
279             })
280             .collect())
281     }
282 }
283 
284 impl RequestAction for DocumentHighlight {
285     type Response = Vec<lsp_data::DocumentHighlight>;
286 
fallback_response() -> Result<Self::Response, ResponseError>287     fn fallback_response() -> Result<Self::Response, ResponseError> {
288         Ok(vec![])
289     }
290 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>291     fn handle(
292         ctx: InitActionContext,
293         params: Self::Params,
294     ) -> Result<Self::Response, ResponseError> {
295         let file_path = parse_file_path!(&params.text_document.uri, "highlight")?;
296         let span = ctx.convert_pos_to_span(file_path.clone(), params.position);
297 
298         let result = ctx.analysis.find_all_refs(&span, true, false).unwrap_or_else(|_| vec![]);
299 
300         Ok(result
301             .iter()
302             .filter_map(|span| {
303                 if span.file == file_path {
304                     Some(lsp_data::DocumentHighlight {
305                         range: ls_util::rls_to_range(span.range),
306                         kind: Some(DocumentHighlightKind::Text),
307                     })
308                 } else {
309                     None
310                 }
311             })
312             .collect())
313     }
314 }
315 
316 impl RequestAction for Rename {
317     type Response = ResponseWithMessage<WorkspaceEdit>;
318 
fallback_response() -> Result<Self::Response, ResponseError>319     fn fallback_response() -> Result<Self::Response, ResponseError> {
320         Ok(ResponseWithMessage::Response(WorkspaceEdit { changes: None, document_changes: None }))
321     }
322 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>323     fn handle(
324         ctx: InitActionContext,
325         params: Self::Params,
326     ) -> Result<Self::Response, ResponseError> {
327         ctx.quiescent.store(true, Ordering::SeqCst);
328         // We're going to mutate based on our data so we should block until the
329         // data is ready.
330         ctx.block_on_build();
331 
332         let file_path =
333             parse_file_path!(&params.text_document_position.text_document.uri, "rename")?;
334         let span = ctx.convert_pos_to_span(file_path, params.text_document_position.position);
335 
336         let analysis = ctx.analysis;
337 
338         macro_rules! unwrap_or_fallback {
339             ($e: expr, $msg: expr) => {
340                 match $e {
341                     Ok(e) => e,
342                     Err(_) => {
343                         return Ok(ResponseWithMessage::Warn($msg.to_owned()));
344                     }
345                 }
346             };
347         }
348 
349         let id = unwrap_or_fallback!(
350             analysis.crate_local_id(&span),
351             "Rename failed: no information for symbol"
352         );
353         let def =
354             unwrap_or_fallback!(analysis.get_def(id), "Rename failed: no definition for symbol");
355         if def.name == "self" || def.name == "Self"
356             // FIXME(#578)
357             || def.kind == data::DefKind::Mod
358         {
359             return Ok(ResponseWithMessage::Warn(format!(
360                 "Rename failed: cannot rename {}",
361                 if def.kind == data::DefKind::Mod { "modules" } else { &def.name }
362             )));
363         }
364 
365         let result = unwrap_or_fallback!(
366             analysis.find_all_refs(&span, true, true),
367             "Rename failed: error finding references"
368         );
369 
370         if result.is_empty() {
371             return Ok(ResponseWithMessage::Warn(
372                 "Rename failed: RLS found nothing to rename - possibly due to multiple defs"
373                     .to_owned(),
374             ));
375         }
376 
377         let mut edits: HashMap<Url, Vec<TextEdit>> = HashMap::new();
378 
379         for item in &result {
380             let loc = ls_util::rls_to_location(item);
381             edits
382                 .entry(loc.uri)
383                 .or_insert_with(Vec::new)
384                 .push(TextEdit { range: loc.range, new_text: params.new_name.clone() });
385         }
386 
387         if !ctx.quiescent.load(Ordering::SeqCst) {
388             return Ok(ResponseWithMessage::Warn(
389                 "Rename failed: RLS busy, please retry".to_owned(),
390             ));
391         }
392 
393         Ok(ResponseWithMessage::Response(WorkspaceEdit {
394             changes: Some(edits),
395             document_changes: None,
396         }))
397     }
398 }
399 
400 #[derive(Debug)]
401 pub enum ExecuteCommandResponse {
402     /// Response/client request containing workspace edits.
403     ApplyEdit(ApplyWorkspaceEditParams),
404 }
405 
406 impl server::Response for ExecuteCommandResponse {
send<O: Output>(self, id: server::RequestId, out: &O)407     fn send<O: Output>(self, id: server::RequestId, out: &O) {
408         // FIXME should handle the client's responses
409         match self {
410             ExecuteCommandResponse::ApplyEdit(ref params) => {
411                 let id = out.provide_id();
412                 let params = ApplyWorkspaceEditParams { edit: params.edit.clone() };
413 
414                 let request = Request::<ApplyWorkspaceEdit>::new(id, params);
415                 out.request(request);
416             }
417         }
418 
419         // The formal request response is a simple ACK, though the objective
420         // is the preceding client requests.
421         Ack.send(id, out);
422     }
423 }
424 
425 impl RequestAction for ExecuteCommand {
426     type Response = ExecuteCommandResponse;
427 
fallback_response() -> Result<Self::Response, ResponseError>428     fn fallback_response() -> Result<Self::Response, ResponseError> {
429         Err(ResponseError::Empty)
430     }
431 
432     /// Currently supports "rls.applySuggestion", "rls.deglobImports".
handle( ctx: InitActionContext, params: ExecuteCommandParams, ) -> Result<Self::Response, ResponseError>433     fn handle(
434         ctx: InitActionContext,
435         params: ExecuteCommandParams,
436     ) -> Result<Self::Response, ResponseError> {
437         if params.command.starts_with("rls.applySuggestion") {
438             apply_suggestion(&params.arguments).map(ExecuteCommandResponse::ApplyEdit)
439         } else if params.command.starts_with("rls.deglobImports") {
440             apply_deglobs(params.arguments, &ctx).map(ExecuteCommandResponse::ApplyEdit)
441         } else {
442             debug!("Unknown command: {}", params.command);
443             Err(ResponseError::Message(ErrorCode::MethodNotFound, "Unknown command".to_owned()))
444         }
445     }
446 }
447 
apply_suggestion(args: &[serde_json::Value]) -> Result<ApplyWorkspaceEditParams, ResponseError>448 fn apply_suggestion(args: &[serde_json::Value]) -> Result<ApplyWorkspaceEditParams, ResponseError> {
449     let location = serde_json::from_value(args[0].clone()).expect("Bad argument");
450     let new_text = serde_json::from_value(args[1].clone()).expect("Bad argument");
451 
452     trace!("apply_suggestion {:?} {}", location, new_text);
453     Ok(ApplyWorkspaceEditParams { edit: make_workspace_edit(location, new_text) })
454 }
455 
apply_deglobs( args: Vec<serde_json::Value>, ctx: &InitActionContext, ) -> Result<ApplyWorkspaceEditParams, ResponseError>456 fn apply_deglobs(
457     args: Vec<serde_json::Value>,
458     ctx: &InitActionContext,
459 ) -> Result<ApplyWorkspaceEditParams, ResponseError> {
460     ctx.quiescent.store(true, Ordering::SeqCst);
461     let deglob_results: Vec<DeglobResult> =
462         args.into_iter().map(|res| serde_json::from_value(res).expect("Bad argument")).collect();
463 
464     trace!("apply_deglobs {:?}", deglob_results);
465 
466     assert!(!deglob_results.is_empty());
467     let uri = deglob_results[0].location.uri.clone();
468 
469     let text_edits: Vec<_> = deglob_results
470         .into_iter()
471         .map(|res| TextEdit { range: res.location.range, new_text: res.new_text })
472         .collect();
473     // all deglob results will share the same URI
474     let changes: HashMap<_, _> = vec![(uri, text_edits)].into_iter().collect();
475 
476     let edit = WorkspaceEdit { changes: Some(changes), document_changes: None };
477 
478     if !ctx.quiescent.load(Ordering::SeqCst) {
479         return Err(ResponseError::Empty);
480     }
481     Ok(ApplyWorkspaceEditParams { edit })
482 }
483 
484 /// Creates `CodeAction`s for fixes suggested by the compiler.
485 /// The results are appended to `code_actions_result`.
make_suggestion_fix_actions( params: &<CodeAction as lsp_data::request::Request>::Params, file_path: &Path, ctx: &InitActionContext, code_actions_result: &mut <CodeAction as RequestAction>::Response, )486 fn make_suggestion_fix_actions(
487     params: &<CodeAction as lsp_data::request::Request>::Params,
488     file_path: &Path,
489     ctx: &InitActionContext,
490     code_actions_result: &mut <CodeAction as RequestAction>::Response,
491 ) {
492     // Search for compiler suggestions.
493     if let Some(results) = ctx.previous_build_results.lock().unwrap().get(file_path) {
494         let suggestions = results
495             .iter()
496             .filter(|(diag, _)| diag.range.overlaps(&params.range))
497             .flat_map(|(_, suggestions)| suggestions);
498         for s in suggestions {
499             let span = Location { uri: params.text_document.uri.clone(), range: s.range };
500             let span = serde_json::to_value(&span).unwrap();
501             let new_text = serde_json::to_value(&s.new_text).unwrap();
502             let cmd = Command {
503                 title: s.label.clone(),
504                 command: format!("rls.applySuggestion-{}", ctx.pid),
505                 arguments: Some(vec![span, new_text]),
506             };
507             code_actions_result.push(cmd);
508         }
509     }
510 }
511 
512 /// Creates `CodeAction`s for performing deglobbing when a wildcard import is found.
513 /// The results are appended to `code_actions_result`.
make_deglob_actions( params: &<CodeAction as lsp_data::request::Request>::Params, file_path: &Path, ctx: &InitActionContext, code_actions_result: &mut <CodeAction as RequestAction>::Response, )514 fn make_deglob_actions(
515     params: &<CodeAction as lsp_data::request::Request>::Params,
516     file_path: &Path,
517     ctx: &InitActionContext,
518     code_actions_result: &mut <CodeAction as RequestAction>::Response,
519 ) {
520     // Search for a glob in the line.
521     if let Ok(line) = ctx.vfs.load_line(file_path, ls_util::range_to_rls(params.range).row_start) {
522         let span = Location::new(params.text_document.uri.clone(), params.range);
523 
524         // For all indices that are a `*`, check if we can deglob them.
525         // This handles badly-formatted text containing multiple `use`s in one line.
526         let deglob_results: Vec<_> = line
527             .char_indices()
528             .filter(|&(_, chr)| chr == '*')
529             .filter_map(|(index, _)| {
530                 // Map the indices to `Span`s.
531                 let mut span = ls_util::location_to_rls(&span).unwrap();
532                 span.range.col_start = span::Column::new_zero_indexed(index as u32);
533                 span.range.col_end = span::Column::new_zero_indexed(index as u32 + 1);
534 
535                 // Load the deglob type information.
536                 ctx.analysis.show_type(&span).ok().map(|ty| (ty, span))
537             })
538             .map(|(mut deglob_str, span)| {
539                 // Handle multiple imports from one `*`.
540                 if deglob_str.contains(',') || deglob_str.is_empty() {
541                     deglob_str = format!("{{{}}}", sort_deglob_str(&deglob_str));
542                 }
543 
544                 // Build result.
545                 let deglob_result = DeglobResult {
546                     location: ls_util::rls_to_location(&span),
547                     new_text: deglob_str,
548                 };
549 
550                 // Convert to json
551                 serde_json::to_value(&deglob_result).unwrap()
552             })
553             .collect();
554 
555         if !deglob_results.is_empty() {
556             // extend result list
557             let cmd = Command {
558                 title: format!("Deglob import{}", if deglob_results.len() > 1 { "s" } else { "" }),
559                 command: format!("rls.deglobImports-{}", ctx.pid),
560                 arguments: Some(deglob_results),
561             };
562             code_actions_result.push(cmd);
563         }
564     };
565 }
566 
567 // Ideally we'd use Rustfmt for this, but reparsing is a bit of a pain.
sort_deglob_str(s: &str) -> String568 fn sort_deglob_str(s: &str) -> String {
569     let mut substrings = s.split(',').map(str::trim).collect::<Vec<_>>();
570     substrings.sort_by(|a, b| {
571         use std::cmp::Ordering;
572 
573         // Algorithm taken from rustfmt (`rustfmt/src/imports.rs`).
574 
575         let is_upper_snake_case =
576             |s: &str| s.chars().all(|c| c.is_uppercase() || c == '_' || c.is_numeric());
577 
578         // snake_case < CamelCase < UPPER_SNAKE_CASE
579         if a.starts_with(char::is_uppercase) && b.starts_with(char::is_lowercase) {
580             return Ordering::Greater;
581         }
582         if a.starts_with(char::is_lowercase) && b.starts_with(char::is_uppercase) {
583             return Ordering::Less;
584         }
585         if is_upper_snake_case(a) && !is_upper_snake_case(b) {
586             return Ordering::Greater;
587         }
588         if !is_upper_snake_case(a) && is_upper_snake_case(b) {
589             return Ordering::Less;
590         }
591         a.cmp(b)
592     });
593     substrings.join(", ")
594 }
595 
596 impl RequestAction for CodeAction {
597     type Response = Vec<Command>;
598 
fallback_response() -> Result<Self::Response, ResponseError>599     fn fallback_response() -> Result<Self::Response, ResponseError> {
600         Ok(vec![])
601     }
602 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>603     fn handle(
604         ctx: InitActionContext,
605         params: Self::Params,
606     ) -> Result<Self::Response, ResponseError> {
607         trace!("code_action {:?}", params);
608 
609         let file_path = parse_file_path!(&params.text_document.uri, "code_action")?;
610 
611         let mut cmds = vec![];
612         if ctx.build_ready() {
613             make_suggestion_fix_actions(&params, &file_path, &ctx, &mut cmds);
614         }
615         if ctx.analysis_ready() {
616             make_deglob_actions(&params, &file_path, &ctx, &mut cmds);
617         }
618         Ok(cmds)
619     }
620 }
621 
622 impl RequestAction for Formatting {
623     type Response = Vec<TextEdit>;
624 
fallback_response() -> Result<Self::Response, ResponseError>625     fn fallback_response() -> Result<Self::Response, ResponseError> {
626         Err(ResponseError::Message(
627             ErrorCode::InternalError,
628             "Reformat failed to complete successfully".into(),
629         ))
630     }
631 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>632     fn handle(
633         ctx: InitActionContext,
634         params: Self::Params,
635     ) -> Result<Self::Response, ResponseError> {
636         reformat(&params.text_document, None, &params.options, &ctx)
637     }
638 }
639 
640 impl RequestAction for RangeFormatting {
641     type Response = Vec<TextEdit>;
642 
fallback_response() -> Result<Self::Response, ResponseError>643     fn fallback_response() -> Result<Self::Response, ResponseError> {
644         Err(ResponseError::Message(
645             ErrorCode::InternalError,
646             "Reformat failed to complete successfully".into(),
647         ))
648     }
649 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>650     fn handle(
651         ctx: InitActionContext,
652         params: Self::Params,
653     ) -> Result<Self::Response, ResponseError> {
654         reformat(&params.text_document, Some(params.range), &params.options, &ctx)
655     }
656 }
657 
reformat( doc: &TextDocumentIdentifier, selection: Option<Range>, opts: &FormattingOptions, ctx: &InitActionContext, ) -> Result<Vec<TextEdit>, ResponseError>658 fn reformat(
659     doc: &TextDocumentIdentifier,
660     selection: Option<Range>,
661     opts: &FormattingOptions,
662     ctx: &InitActionContext,
663 ) -> Result<Vec<TextEdit>, ResponseError> {
664     ctx.quiescent.store(true, Ordering::SeqCst);
665     trace!("Reformat: {:?} {:?} {} {}", doc, selection, opts.tab_size, opts.insert_spaces);
666     let path = parse_file_path!(&doc.uri, "reformat")?;
667 
668     let input = match ctx.vfs.load_file(&path) {
669         Ok(FileContents::Text(s)) => s,
670         Ok(_) => {
671             debug!("Reformat failed, found binary file");
672             return Err(ResponseError::Message(
673                 ErrorCode::InternalError,
674                 "Reformat failed to complete successfully".into(),
675             ));
676         }
677         Err(e) => {
678             debug!("Reformat failed: {:?}", e);
679             return Err(ResponseError::Message(
680                 ErrorCode::InternalError,
681                 "Reformat failed to complete successfully".into(),
682             ));
683         }
684     };
685 
686     let mut config = ctx.fmt_config().get_rustfmt_config().clone();
687     if !config.was_set().hard_tabs() {
688         config.set().hard_tabs(!opts.insert_spaces);
689     }
690     if !config.was_set().tab_spaces() {
691         config.set().tab_spaces(opts.tab_size as usize);
692     }
693     if !config.was_set().edition() {
694         match ctx.file_edition(path.clone()) {
695             Some(edition) => {
696                 let edition = match edition {
697                     Edition::Edition2015 => RustfmtEdition::Edition2015,
698                     Edition::Edition2018 => RustfmtEdition::Edition2018,
699                     Edition::Edition2021 => RustfmtEdition::Edition2021,
700                 };
701                 config.set().edition(edition);
702                 trace!("Detected edition {:?} for file `{}`", edition, path.display());
703             }
704             None => {
705                 warn!("Reformat failed: ambiguous edition for `{}`", path.display());
706 
707                 return Err(ResponseError::Message(
708                     ErrorCode::InternalError,
709                     "Reformat failed to complete successfully".into(),
710                 ));
711             }
712         }
713     }
714 
715     if let Some(r) = selection {
716         let range_of_rls = ls_util::range_to_rls(r).one_indexed();
717         let range =
718             RustfmtRange::new(range_of_rls.row_start.0 as usize, range_of_rls.row_end.0 as usize);
719         let mut ranges = HashMap::new();
720         ranges.insert(FileName::Stdin, vec![range]);
721         let file_lines = FileLines::from_ranges(ranges);
722         config.set().file_lines(file_lines);
723     };
724 
725     let text_edits = ctx
726         .formatter()
727         .calc_text_edits(input, config)
728         .map_err(|msg| ResponseError::Message(ErrorCode::InternalError, msg.to_string()))?;
729 
730     // Note that we don't need to update the VFS, the client echos back the
731     // change to us when it applies the returned `TextEdit`.
732 
733     if !ctx.quiescent.load(Ordering::SeqCst) {
734         return Err(ResponseError::Message(
735             ErrorCode::InternalError,
736             "Reformat failed to complete successfully".into(),
737         ));
738     }
739 
740     Ok(text_edits)
741 }
742 
743 impl RequestAction for ResolveCompletion {
744     type Response = CompletionItem;
745 
fallback_response() -> Result<Self::Response, ResponseError>746     fn fallback_response() -> Result<Self::Response, ResponseError> {
747         Err(ResponseError::Empty)
748     }
749 
handle(_: InitActionContext, params: Self::Params) -> Result<Self::Response, ResponseError>750     fn handle(_: InitActionContext, params: Self::Params) -> Result<Self::Response, ResponseError> {
751         // Currently, we safely ignore this as a pass-through since we fully handle
752         // `textDocument/completion`. In the future, we may want to use this method as a
753         // way to more lazily fill out completion information.
754         Ok(params)
755     }
756 }
757 
racer_coord( row: span::Row<span::OneIndexed>, col: span::Column<span::ZeroIndexed>, ) -> racer::Coordinate758 pub(crate) fn racer_coord(
759     row: span::Row<span::OneIndexed>,
760     col: span::Column<span::ZeroIndexed>,
761 ) -> racer::Coordinate {
762     racer::Coordinate { row, col }
763 }
764 
from_racer_coord( coord: racer::Coordinate, ) -> (span::Row<span::OneIndexed>, span::Column<span::ZeroIndexed>)765 pub(crate) fn from_racer_coord(
766     coord: racer::Coordinate,
767 ) -> (span::Row<span::OneIndexed>, span::Column<span::ZeroIndexed>) {
768     (coord.row, coord.col)
769 }
770 
pos_to_racer_location(pos: Position) -> racer::Location771 fn pos_to_racer_location(pos: Position) -> racer::Location {
772     let pos = ls_util::position_to_rls(pos);
773     racer::Location::Coords(racer_coord(pos.row.one_indexed(), pos.col))
774 }
775 
location_from_racer_match(a_match: &racer::Match) -> Option<Location>776 fn location_from_racer_match(a_match: &racer::Match) -> Option<Location> {
777     let source_path = &a_match.filepath;
778 
779     a_match.coords.map(|coord| {
780         let (row, col) = from_racer_coord(coord);
781         let loc = span::Location::new(row.zero_indexed(), col, source_path);
782         ls_util::rls_location_to_location(&loc)
783     })
784 }
785 
786 impl RequestAction for CodeLensRequest {
787     type Response = Vec<CodeLens>;
788 
fallback_response() -> Result<Self::Response, ResponseError>789     fn fallback_response() -> Result<Self::Response, ResponseError> {
790         Err(ResponseError::Empty)
791     }
792 
handle( ctx: InitActionContext, params: Self::Params, ) -> Result<Self::Response, ResponseError>793     fn handle(
794         ctx: InitActionContext,
795         params: Self::Params,
796     ) -> Result<Self::Response, ResponseError> {
797         let mut ret = Vec::new();
798         if ctx.client_supports_cmd_run {
799             let file_path = parse_file_path!(&params.text_document.uri, "code_lens")?;
800             for action in collect_run_actions(&ctx, &file_path) {
801                 let command = Command {
802                     title: action.label,
803                     command: "rls.run".to_string(),
804                     arguments: Some(vec![serde_json::to_value(&action.cmd).unwrap()]),
805                 };
806                 let range = ls_util::rls_to_range(action.target_element);
807                 let lens = CodeLens { range, command: Some(command), data: None };
808                 ret.push(lens);
809             }
810         }
811         Ok(ret)
812     }
813 }
814 
815 #[cfg(test)]
816 mod test {
817     use super::*;
818 
819     #[test]
test_sort_deglob_str()820     fn test_sort_deglob_str() {
821         assert_eq!(sort_deglob_str(""), "");
822         assert_eq!(sort_deglob_str("foo"), "foo");
823         assert_eq!(sort_deglob_str("a, b"), "a, b");
824         assert_eq!(sort_deglob_str("b, a"), "a, b");
825         assert_eq!(sort_deglob_str("foo, bar, baz"), "bar, baz, foo");
826         assert_eq!(
827             sort_deglob_str("Curve, curve, ARC, bow, Bow, arc, Arc"),
828             "arc, bow, curve, Arc, Bow, Curve, ARC",
829         );
830     }
831 }
832