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(¶ms.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!(¶ms.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, ¶ms)?;
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!(¶ms.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!(¶ms.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!(¶ms.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!(¶ms.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!(¶ms.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!(¶ms.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(¶ms.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(¶ms.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(°lob_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(°lob_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!(¶ms.text_document.uri, "code_action")?;
610
611 let mut cmds = vec![];
612 if ctx.build_ready() {
613 make_suggestion_fix_actions(¶ms, &file_path, &ctx, &mut cmds);
614 }
615 if ctx.analysis_ready() {
616 make_deglob_actions(¶ms, &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(¶ms.text_document, None, ¶ms.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(¶ms.text_document, Some(params.range), ¶ms.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!(¶ms.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