• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Conversion of rust-analyzer specific types to lsp_types equivalents.
2 use std::{
3     iter::once,
4     path,
5     sync::atomic::{AtomicU32, Ordering},
6 };
7 
8 use ide::{
9     Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
10     CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
11     Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
12     InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory,
13     RenameError, Runnable, Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind,
14     TextEdit, TextRange, TextSize,
15 };
16 use itertools::Itertools;
17 use serde_json::to_value;
18 use vfs::AbsPath;
19 
20 use crate::{
21     cargo_target_spec::CargoTargetSpec,
22     config::{CallInfoConfig, Config},
23     global_state::GlobalStateSnapshot,
24     line_index::{LineEndings, LineIndex, PositionEncoding},
25     lsp_ext,
26     lsp_utils::invalid_params_error,
27     semantic_tokens::{self, standard_fallback_type},
28 };
29 
position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position30 pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
31     let line_col = line_index.index.line_col(offset);
32     match line_index.encoding {
33         PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
34         PositionEncoding::Wide(enc) => {
35             let line_col = line_index.index.to_wide(enc, line_col).unwrap();
36             lsp_types::Position::new(line_col.line, line_col.col)
37         }
38     }
39 }
40 
range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range41 pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range {
42     let start = position(line_index, range.start());
43     let end = position(line_index, range.end());
44     lsp_types::Range::new(start, end)
45 }
46 
symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind47 pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
48     match symbol_kind {
49         SymbolKind::Function => lsp_types::SymbolKind::FUNCTION,
50         SymbolKind::Struct => lsp_types::SymbolKind::STRUCT,
51         SymbolKind::Enum => lsp_types::SymbolKind::ENUM,
52         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
53         SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE,
54         SymbolKind::Macro
55         | SymbolKind::BuiltinAttr
56         | SymbolKind::Attribute
57         | SymbolKind::Derive
58         | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION,
59         SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
60         SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
61             lsp_types::SymbolKind::TYPE_PARAMETER
62         }
63         SymbolKind::Field => lsp_types::SymbolKind::FIELD,
64         SymbolKind::Static => lsp_types::SymbolKind::CONSTANT,
65         SymbolKind::Const => lsp_types::SymbolKind::CONSTANT,
66         SymbolKind::ConstParam => lsp_types::SymbolKind::CONSTANT,
67         SymbolKind::Impl => lsp_types::SymbolKind::OBJECT,
68         SymbolKind::Local
69         | SymbolKind::SelfParam
70         | SymbolKind::LifetimeParam
71         | SymbolKind::ValueParam
72         | SymbolKind::Label => lsp_types::SymbolKind::VARIABLE,
73         SymbolKind::Union => lsp_types::SymbolKind::STRUCT,
74     }
75 }
76 
structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolKind77 pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolKind {
78     match kind {
79         StructureNodeKind::SymbolKind(symbol) => symbol_kind(symbol),
80         StructureNodeKind::Region => lsp_types::SymbolKind::NAMESPACE,
81     }
82 }
83 
document_highlight_kind( category: ReferenceCategory, ) -> Option<lsp_types::DocumentHighlightKind>84 pub(crate) fn document_highlight_kind(
85     category: ReferenceCategory,
86 ) -> Option<lsp_types::DocumentHighlightKind> {
87     match category {
88         ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ),
89         ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE),
90         ReferenceCategory::Import => None,
91     }
92 }
93 
diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity94 pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
95     match severity {
96         Severity::Error => lsp_types::DiagnosticSeverity::ERROR,
97         Severity::WeakWarning => lsp_types::DiagnosticSeverity::HINT,
98     }
99 }
100 
documentation(documentation: Documentation) -> lsp_types::Documentation101 pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
102     let value = crate::markdown::format_docs(documentation.as_str());
103     let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
104     lsp_types::Documentation::MarkupContent(markup_content)
105 }
106 
completion_item_kind( completion_item_kind: CompletionItemKind, ) -> lsp_types::CompletionItemKind107 pub(crate) fn completion_item_kind(
108     completion_item_kind: CompletionItemKind,
109 ) -> lsp_types::CompletionItemKind {
110     match completion_item_kind {
111         CompletionItemKind::Binding => lsp_types::CompletionItemKind::VARIABLE,
112         CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT,
113         CompletionItemKind::InferredType => lsp_types::CompletionItemKind::SNIPPET,
114         CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD,
115         CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD,
116         CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET,
117         CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE,
118         CompletionItemKind::SymbolKind(symbol) => match symbol {
119             SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION,
120             SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
121             SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
122             SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
123             SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION,
124             SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
125             SymbolKind::Field => lsp_types::CompletionItemKind::FIELD,
126             SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION,
127             SymbolKind::Impl => lsp_types::CompletionItemKind::TEXT,
128             SymbolKind::Label => lsp_types::CompletionItemKind::VARIABLE,
129             SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
130             SymbolKind::Local => lsp_types::CompletionItemKind::VARIABLE,
131             SymbolKind::Macro => lsp_types::CompletionItemKind::FUNCTION,
132             SymbolKind::Module => lsp_types::CompletionItemKind::MODULE,
133             SymbolKind::SelfParam => lsp_types::CompletionItemKind::VALUE,
134             SymbolKind::SelfType => lsp_types::CompletionItemKind::TYPE_PARAMETER,
135             SymbolKind::Static => lsp_types::CompletionItemKind::VALUE,
136             SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT,
137             SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE,
138             SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE,
139             SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT,
140             SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
141             SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT,
142             SymbolKind::ValueParam => lsp_types::CompletionItemKind::VALUE,
143             SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
144             SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
145             SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE,
146         },
147     }
148 }
149 
text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::TextEdit150 pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::TextEdit {
151     let range = range(line_index, indel.delete);
152     let new_text = match line_index.endings {
153         LineEndings::Unix => indel.insert,
154         LineEndings::Dos => indel.insert.replace('\n', "\r\n"),
155     };
156     lsp_types::TextEdit { range, new_text }
157 }
158 
completion_text_edit( line_index: &LineIndex, insert_replace_support: Option<lsp_types::Position>, indel: Indel, ) -> lsp_types::CompletionTextEdit159 pub(crate) fn completion_text_edit(
160     line_index: &LineIndex,
161     insert_replace_support: Option<lsp_types::Position>,
162     indel: Indel,
163 ) -> lsp_types::CompletionTextEdit {
164     let text_edit = text_edit(line_index, indel);
165     match insert_replace_support {
166         Some(cursor_pos) => lsp_types::InsertReplaceEdit {
167             new_text: text_edit.new_text,
168             insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
169             replace: text_edit.range,
170         }
171         .into(),
172         None => text_edit.into(),
173     }
174 }
175 
snippet_text_edit( line_index: &LineIndex, is_snippet: bool, indel: Indel, ) -> lsp_ext::SnippetTextEdit176 pub(crate) fn snippet_text_edit(
177     line_index: &LineIndex,
178     is_snippet: bool,
179     indel: Indel,
180 ) -> lsp_ext::SnippetTextEdit {
181     let text_edit = text_edit(line_index, indel);
182     let insert_text_format =
183         if is_snippet { Some(lsp_types::InsertTextFormat::SNIPPET) } else { None };
184     lsp_ext::SnippetTextEdit {
185         range: text_edit.range,
186         new_text: text_edit.new_text,
187         insert_text_format,
188         annotation_id: None,
189     }
190 }
191 
text_edit_vec( line_index: &LineIndex, text_edit: TextEdit, ) -> Vec<lsp_types::TextEdit>192 pub(crate) fn text_edit_vec(
193     line_index: &LineIndex,
194     text_edit: TextEdit,
195 ) -> Vec<lsp_types::TextEdit> {
196     text_edit.into_iter().map(|indel| self::text_edit(line_index, indel)).collect()
197 }
198 
snippet_text_edit_vec( line_index: &LineIndex, is_snippet: bool, text_edit: TextEdit, ) -> Vec<lsp_ext::SnippetTextEdit>199 pub(crate) fn snippet_text_edit_vec(
200     line_index: &LineIndex,
201     is_snippet: bool,
202     text_edit: TextEdit,
203 ) -> Vec<lsp_ext::SnippetTextEdit> {
204     text_edit
205         .into_iter()
206         .map(|indel| self::snippet_text_edit(line_index, is_snippet, indel))
207         .collect()
208 }
209 
completion_items( config: &Config, line_index: &LineIndex, tdpp: lsp_types::TextDocumentPositionParams, items: Vec<CompletionItem>, ) -> Vec<lsp_types::CompletionItem>210 pub(crate) fn completion_items(
211     config: &Config,
212     line_index: &LineIndex,
213     tdpp: lsp_types::TextDocumentPositionParams,
214     items: Vec<CompletionItem>,
215 ) -> Vec<lsp_types::CompletionItem> {
216     let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
217     let mut res = Vec::with_capacity(items.len());
218     for item in items {
219         completion_item(&mut res, config, line_index, &tdpp, max_relevance, item);
220     }
221 
222     if let Some(limit) = config.completion().limit {
223         res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text));
224         res.truncate(limit);
225     }
226 
227     res
228 }
229 
completion_item( acc: &mut Vec<lsp_types::CompletionItem>, config: &Config, line_index: &LineIndex, tdpp: &lsp_types::TextDocumentPositionParams, max_relevance: u32, item: CompletionItem, )230 fn completion_item(
231     acc: &mut Vec<lsp_types::CompletionItem>,
232     config: &Config,
233     line_index: &LineIndex,
234     tdpp: &lsp_types::TextDocumentPositionParams,
235     max_relevance: u32,
236     item: CompletionItem,
237 ) {
238     let insert_replace_support = config.insert_replace_support().then_some(tdpp.position);
239     let ref_match = item.ref_match();
240     let lookup = item.lookup().to_string();
241 
242     let mut additional_text_edits = Vec::new();
243 
244     // LSP does not allow arbitrary edits in completion, so we have to do a
245     // non-trivial mapping here.
246     let text_edit = {
247         let mut text_edit = None;
248         let source_range = item.source_range;
249         for indel in item.text_edit {
250             if indel.delete.contains_range(source_range) {
251                 // Extract this indel as the main edit
252                 text_edit = Some(if indel.delete == source_range {
253                     self::completion_text_edit(line_index, insert_replace_support, indel.clone())
254                 } else {
255                     assert!(source_range.end() == indel.delete.end());
256                     let range1 = TextRange::new(indel.delete.start(), source_range.start());
257                     let range2 = source_range;
258                     let indel1 = Indel::delete(range1);
259                     let indel2 = Indel::replace(range2, indel.insert.clone());
260                     additional_text_edits.push(self::text_edit(line_index, indel1));
261                     self::completion_text_edit(line_index, insert_replace_support, indel2)
262                 })
263             } else {
264                 assert!(source_range.intersect(indel.delete).is_none());
265                 let text_edit = self::text_edit(line_index, indel.clone());
266                 additional_text_edits.push(text_edit);
267             }
268         }
269         text_edit.unwrap()
270     };
271 
272     let insert_text_format = item.is_snippet.then_some(lsp_types::InsertTextFormat::SNIPPET);
273     let tags = item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
274     let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints {
275         Some(command::trigger_parameter_hints())
276     } else {
277         None
278     };
279 
280     let mut lsp_item = lsp_types::CompletionItem {
281         label: item.label.to_string(),
282         detail: item.detail,
283         filter_text: Some(lookup),
284         kind: Some(completion_item_kind(item.kind)),
285         text_edit: Some(text_edit),
286         additional_text_edits: Some(additional_text_edits),
287         documentation: item.documentation.map(documentation),
288         deprecated: Some(item.deprecated),
289         tags,
290         command,
291         insert_text_format,
292         ..Default::default()
293     };
294 
295     if config.completion_label_details_support() {
296         lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
297             detail: None,
298             description: lsp_item.detail.clone(),
299         });
300     }
301 
302     set_score(&mut lsp_item, max_relevance, item.relevance);
303 
304     if config.completion().enable_imports_on_the_fly {
305         if !item.import_to_add.is_empty() {
306             let imports: Vec<_> = item
307                 .import_to_add
308                 .into_iter()
309                 .filter_map(|(import_path, import_name)| {
310                     Some(lsp_ext::CompletionImport {
311                         full_import_path: import_path,
312                         imported_name: import_name,
313                     })
314                 })
315                 .collect();
316             if !imports.is_empty() {
317                 let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports };
318                 lsp_item.data = Some(to_value(data).unwrap());
319             }
320         }
321     }
322 
323     if let Some((label, indel, relevance)) = ref_match {
324         let mut lsp_item_with_ref = lsp_types::CompletionItem { label, ..lsp_item.clone() };
325         lsp_item_with_ref
326             .additional_text_edits
327             .get_or_insert_with(Default::default)
328             .push(self::text_edit(line_index, indel));
329         set_score(&mut lsp_item_with_ref, max_relevance, relevance);
330         acc.push(lsp_item_with_ref);
331     };
332 
333     acc.push(lsp_item);
334 
335     fn set_score(
336         res: &mut lsp_types::CompletionItem,
337         max_relevance: u32,
338         relevance: CompletionRelevance,
339     ) {
340         if relevance.is_relevant() && relevance.score() == max_relevance {
341             res.preselect = Some(true);
342         }
343         // The relevance needs to be inverted to come up with a sort score
344         // because the client will sort ascending.
345         let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;
346         // Zero pad the string to ensure values can be properly sorted
347         // by the client. Hex format is used because it is easier to
348         // visually compare very large values, which the sort text
349         // tends to be since it is the opposite of the score.
350         res.sort_text = Some(format!("{sort_score:08x}"));
351     }
352 }
353 
signature_help( call_info: SignatureHelp, config: CallInfoConfig, label_offsets: bool, ) -> lsp_types::SignatureHelp354 pub(crate) fn signature_help(
355     call_info: SignatureHelp,
356     config: CallInfoConfig,
357     label_offsets: bool,
358 ) -> lsp_types::SignatureHelp {
359     let (label, parameters) = match (config.params_only, label_offsets) {
360         (concise, false) => {
361             let params = call_info
362                 .parameter_labels()
363                 .map(|label| lsp_types::ParameterInformation {
364                     label: lsp_types::ParameterLabel::Simple(label.to_string()),
365                     documentation: None,
366                 })
367                 .collect::<Vec<_>>();
368             let label =
369                 if concise { call_info.parameter_labels().join(", ") } else { call_info.signature };
370             (label, params)
371         }
372         (false, true) => {
373             let params = call_info
374                 .parameter_ranges()
375                 .iter()
376                 .map(|it| {
377                     let start = call_info.signature[..it.start().into()].chars().count() as u32;
378                     let end = call_info.signature[..it.end().into()].chars().count() as u32;
379                     [start, end]
380                 })
381                 .map(|label_offsets| lsp_types::ParameterInformation {
382                     label: lsp_types::ParameterLabel::LabelOffsets(label_offsets),
383                     documentation: None,
384                 })
385                 .collect::<Vec<_>>();
386             (call_info.signature, params)
387         }
388         (true, true) => {
389             let mut params = Vec::new();
390             let mut label = String::new();
391             let mut first = true;
392             for param in call_info.parameter_labels() {
393                 if !first {
394                     label.push_str(", ");
395                 }
396                 first = false;
397                 let start = label.chars().count() as u32;
398                 label.push_str(param);
399                 let end = label.chars().count() as u32;
400                 params.push(lsp_types::ParameterInformation {
401                     label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
402                     documentation: None,
403                 });
404             }
405 
406             (label, params)
407         }
408     };
409 
410     let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
411         lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
412             kind: lsp_types::MarkupKind::Markdown,
413             value: crate::markdown::format_docs(&doc),
414         })
415     });
416 
417     let active_parameter = call_info.active_parameter.map(|it| it as u32);
418 
419     let signature = lsp_types::SignatureInformation {
420         label,
421         documentation,
422         parameters: Some(parameters),
423         active_parameter,
424     };
425     lsp_types::SignatureHelp {
426         signatures: vec![signature],
427         active_signature: Some(0),
428         active_parameter,
429     }
430 }
431 
inlay_hint( snap: &GlobalStateSnapshot, line_index: &LineIndex, inlay_hint: InlayHint, ) -> Cancellable<lsp_types::InlayHint>432 pub(crate) fn inlay_hint(
433     snap: &GlobalStateSnapshot,
434     line_index: &LineIndex,
435     inlay_hint: InlayHint,
436 ) -> Cancellable<lsp_types::InlayHint> {
437     let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
438 
439     Ok(lsp_types::InlayHint {
440         position: match inlay_hint.position {
441             ide::InlayHintPosition::Before => position(line_index, inlay_hint.range.start()),
442             ide::InlayHintPosition::After => position(line_index, inlay_hint.range.end()),
443         },
444         padding_left: Some(inlay_hint.pad_left),
445         padding_right: Some(inlay_hint.pad_right),
446         kind: match inlay_hint.kind {
447             InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER),
448             InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
449             _ => None,
450         },
451         text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
452         data: None,
453         tooltip,
454         label,
455     })
456 }
457 
inlay_hint_label( snap: &GlobalStateSnapshot, mut label: InlayHintLabel, ) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)>458 fn inlay_hint_label(
459     snap: &GlobalStateSnapshot,
460     mut label: InlayHintLabel,
461 ) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> {
462     let res = match &*label.parts {
463         [InlayHintLabelPart { linked_location: None, .. }] => {
464             let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
465             (
466                 lsp_types::InlayHintLabel::String(text),
467                 match tooltip {
468                     Some(ide::InlayTooltip::String(s)) => {
469                         Some(lsp_types::InlayHintTooltip::String(s))
470                     }
471                     Some(ide::InlayTooltip::Markdown(s)) => {
472                         Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
473                             kind: lsp_types::MarkupKind::Markdown,
474                             value: s,
475                         }))
476                     }
477                     None => None,
478                 },
479             )
480         }
481         _ => {
482             let parts = label
483                 .parts
484                 .into_iter()
485                 .map(|part| {
486                     part.linked_location.map(|range| location(snap, range)).transpose().map(
487                         |location| lsp_types::InlayHintLabelPart {
488                             value: part.text,
489                             tooltip: match part.tooltip {
490                                 Some(ide::InlayTooltip::String(s)) => {
491                                     Some(lsp_types::InlayHintLabelPartTooltip::String(s))
492                                 }
493                                 Some(ide::InlayTooltip::Markdown(s)) => {
494                                     Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
495                                         lsp_types::MarkupContent {
496                                             kind: lsp_types::MarkupKind::Markdown,
497                                             value: s,
498                                         },
499                                     ))
500                                 }
501                                 None => None,
502                             },
503                             location,
504                             command: None,
505                         },
506                     )
507                 })
508                 .collect::<Cancellable<_>>()?;
509             (lsp_types::InlayHintLabel::LabelParts(parts), None)
510         }
511     };
512     Ok(res)
513 }
514 
515 static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
516 
semantic_tokens( text: &str, line_index: &LineIndex, highlights: Vec<HlRange>, semantics_tokens_augments_syntax_tokens: bool, non_standard_tokens: bool, ) -> lsp_types::SemanticTokens517 pub(crate) fn semantic_tokens(
518     text: &str,
519     line_index: &LineIndex,
520     highlights: Vec<HlRange>,
521     semantics_tokens_augments_syntax_tokens: bool,
522     non_standard_tokens: bool,
523 ) -> lsp_types::SemanticTokens {
524     let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
525     let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
526 
527     for highlight_range in highlights {
528         if highlight_range.highlight.is_empty() {
529             continue;
530         }
531 
532         if semantics_tokens_augments_syntax_tokens {
533             match highlight_range.highlight.tag {
534                 HlTag::BoolLiteral
535                 | HlTag::ByteLiteral
536                 | HlTag::CharLiteral
537                 | HlTag::Comment
538                 | HlTag::Keyword
539                 | HlTag::NumericLiteral
540                 | HlTag::Operator(_)
541                 | HlTag::Punctuation(_)
542                 | HlTag::StringLiteral
543                 | HlTag::None
544                     if highlight_range.highlight.mods.is_empty() =>
545                 {
546                     continue
547                 }
548                 _ => (),
549             }
550         }
551 
552         let (mut ty, mut mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
553 
554         if !non_standard_tokens {
555             ty = match standard_fallback_type(ty) {
556                 Some(ty) => ty,
557                 None => continue,
558             };
559             mods.standard_fallback();
560         }
561         let token_index = semantic_tokens::type_index(ty);
562         let modifier_bitset = mods.0;
563 
564         for mut text_range in line_index.index.lines(highlight_range.range) {
565             if text[text_range].ends_with('\n') {
566                 text_range =
567                     TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n'));
568             }
569             let range = range(line_index, text_range);
570             builder.push(range, token_index, modifier_bitset);
571         }
572     }
573 
574     builder.build()
575 }
576 
semantic_token_delta( previous: &lsp_types::SemanticTokens, current: &lsp_types::SemanticTokens, ) -> lsp_types::SemanticTokensDelta577 pub(crate) fn semantic_token_delta(
578     previous: &lsp_types::SemanticTokens,
579     current: &lsp_types::SemanticTokens,
580 ) -> lsp_types::SemanticTokensDelta {
581     let result_id = current.result_id.clone();
582     let edits = semantic_tokens::diff_tokens(&previous.data, &current.data);
583     lsp_types::SemanticTokensDelta { result_id, edits }
584 }
585 
semantic_token_type_and_modifiers( highlight: Highlight, ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet)586 fn semantic_token_type_and_modifiers(
587     highlight: Highlight,
588 ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
589     let mut mods = semantic_tokens::ModifierSet::default();
590     let type_ = match highlight.tag {
591         HlTag::Symbol(symbol) => match symbol {
592             SymbolKind::Attribute => semantic_tokens::DECORATOR,
593             SymbolKind::Derive => semantic_tokens::DERIVE,
594             SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
595             SymbolKind::Module => semantic_tokens::NAMESPACE,
596             SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
597             SymbolKind::Field => semantic_tokens::PROPERTY,
598             SymbolKind::TypeParam => semantic_tokens::TYPE_PARAMETER,
599             SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
600             SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
601             SymbolKind::Label => semantic_tokens::LABEL,
602             SymbolKind::ValueParam => semantic_tokens::PARAMETER,
603             SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
604             SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD,
605             SymbolKind::Local => semantic_tokens::VARIABLE,
606             SymbolKind::Function => {
607                 if highlight.mods.contains(HlMod::Associated) {
608                     semantic_tokens::METHOD
609                 } else {
610                     semantic_tokens::FUNCTION
611                 }
612             }
613             SymbolKind::Const => {
614                 mods |= semantic_tokens::CONSTANT;
615                 mods |= semantic_tokens::STATIC;
616                 semantic_tokens::VARIABLE
617             }
618             SymbolKind::Static => {
619                 mods |= semantic_tokens::STATIC;
620                 semantic_tokens::VARIABLE
621             }
622             SymbolKind::Struct => semantic_tokens::STRUCT,
623             SymbolKind::Enum => semantic_tokens::ENUM,
624             SymbolKind::Variant => semantic_tokens::ENUM_MEMBER,
625             SymbolKind::Union => semantic_tokens::UNION,
626             SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
627             SymbolKind::Trait => semantic_tokens::INTERFACE,
628             SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
629             SymbolKind::Macro => semantic_tokens::MACRO,
630             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
631             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
632         },
633         HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET,
634         HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
635         HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
636         HlTag::ByteLiteral | HlTag::NumericLiteral => semantic_tokens::NUMBER,
637         HlTag::CharLiteral => semantic_tokens::CHAR,
638         HlTag::Comment => semantic_tokens::COMMENT,
639         HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
640         HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
641         HlTag::Keyword => semantic_tokens::KEYWORD,
642         HlTag::None => semantic_tokens::GENERIC,
643         HlTag::Operator(op) => match op {
644             HlOperator::Bitwise => semantic_tokens::BITWISE,
645             HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
646             HlOperator::Logical => semantic_tokens::LOGICAL,
647             HlOperator::Comparison => semantic_tokens::COMPARISON,
648             HlOperator::Other => semantic_tokens::OPERATOR,
649         },
650         HlTag::StringLiteral => semantic_tokens::STRING,
651         HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
652         HlTag::Punctuation(punct) => match punct {
653             HlPunct::Bracket => semantic_tokens::BRACKET,
654             HlPunct::Brace => semantic_tokens::BRACE,
655             HlPunct::Parenthesis => semantic_tokens::PARENTHESIS,
656             HlPunct::Angle => semantic_tokens::ANGLE,
657             HlPunct::Comma => semantic_tokens::COMMA,
658             HlPunct::Dot => semantic_tokens::DOT,
659             HlPunct::Colon => semantic_tokens::COLON,
660             HlPunct::Semi => semantic_tokens::SEMICOLON,
661             HlPunct::Other => semantic_tokens::PUNCTUATION,
662             HlPunct::MacroBang => semantic_tokens::MACRO_BANG,
663         },
664     };
665 
666     for modifier in highlight.mods.iter() {
667         let modifier = match modifier {
668             HlMod::Associated => continue,
669             HlMod::Async => semantic_tokens::ASYNC,
670             HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
671             HlMod::Callable => semantic_tokens::CALLABLE,
672             HlMod::Consuming => semantic_tokens::CONSUMING,
673             HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
674             HlMod::CrateRoot => semantic_tokens::CRATE_ROOT,
675             HlMod::DefaultLibrary => semantic_tokens::DEFAULT_LIBRARY,
676             HlMod::Definition => semantic_tokens::DECLARATION,
677             HlMod::Documentation => semantic_tokens::DOCUMENTATION,
678             HlMod::Injected => semantic_tokens::INJECTED,
679             HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
680             HlMod::Library => semantic_tokens::LIBRARY,
681             HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
682             HlMod::Mutable => semantic_tokens::MUTABLE,
683             HlMod::Public => semantic_tokens::PUBLIC,
684             HlMod::Reference => semantic_tokens::REFERENCE,
685             HlMod::Static => semantic_tokens::STATIC,
686             HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
687             HlMod::Unsafe => semantic_tokens::UNSAFE,
688         };
689         mods |= modifier;
690     }
691 
692     (type_, mods)
693 }
694 
folding_range( text: &str, line_index: &LineIndex, line_folding_only: bool, fold: Fold, ) -> lsp_types::FoldingRange695 pub(crate) fn folding_range(
696     text: &str,
697     line_index: &LineIndex,
698     line_folding_only: bool,
699     fold: Fold,
700 ) -> lsp_types::FoldingRange {
701     let kind = match fold.kind {
702         FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
703         FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
704         FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region),
705         FoldKind::Mods
706         | FoldKind::Block
707         | FoldKind::ArgList
708         | FoldKind::Consts
709         | FoldKind::Statics
710         | FoldKind::WhereClause
711         | FoldKind::ReturnType
712         | FoldKind::Array
713         | FoldKind::MatchArm => None,
714     };
715 
716     let range = range(line_index, fold.range);
717 
718     if line_folding_only {
719         // Clients with line_folding_only == true (such as VSCode) will fold the whole end line
720         // even if it contains text not in the folding range. To prevent that we exclude
721         // range.end.line from the folding region if there is more text after range.end
722         // on the same line.
723         let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))]
724             .chars()
725             .take_while(|it| *it != '\n')
726             .any(|it| !it.is_whitespace());
727 
728         let end_line = if has_more_text_on_end_line {
729             range.end.line.saturating_sub(1)
730         } else {
731             range.end.line
732         };
733 
734         lsp_types::FoldingRange {
735             start_line: range.start.line,
736             start_character: None,
737             end_line,
738             end_character: None,
739             kind,
740             collapsed_text: None,
741         }
742     } else {
743         lsp_types::FoldingRange {
744             start_line: range.start.line,
745             start_character: Some(range.start.character),
746             end_line: range.end.line,
747             end_character: Some(range.end.character),
748             kind,
749             collapsed_text: None,
750         }
751     }
752 }
753 
url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url754 pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url {
755     snap.file_id_to_url(file_id)
756 }
757 
758 /// Returns a `Url` object from a given path, will lowercase drive letters if present.
759 /// This will only happen when processing windows paths.
760 ///
761 /// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
url_from_abs_path(path: &AbsPath) -> lsp_types::Url762 pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url {
763     let url = lsp_types::Url::from_file_path(path).unwrap();
764     match path.as_ref().components().next() {
765         Some(path::Component::Prefix(prefix))
766             if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) =>
767         {
768             // Need to lowercase driver letter
769         }
770         _ => return url,
771     }
772 
773     let driver_letter_range = {
774         let (scheme, drive_letter, _rest) = match url.as_str().splitn(3, ':').collect_tuple() {
775             Some(it) => it,
776             None => return url,
777         };
778         let start = scheme.len() + ':'.len_utf8();
779         start..(start + drive_letter.len())
780     };
781 
782     // Note: lowercasing the `path` itself doesn't help, the `Url::parse`
783     // machinery *also* canonicalizes the drive letter. So, just massage the
784     // string in place.
785     let mut url: String = url.into();
786     url[driver_letter_range].make_ascii_lowercase();
787     lsp_types::Url::parse(&url).unwrap()
788 }
789 
optional_versioned_text_document_identifier( snap: &GlobalStateSnapshot, file_id: FileId, ) -> lsp_types::OptionalVersionedTextDocumentIdentifier790 pub(crate) fn optional_versioned_text_document_identifier(
791     snap: &GlobalStateSnapshot,
792     file_id: FileId,
793 ) -> lsp_types::OptionalVersionedTextDocumentIdentifier {
794     let url = url(snap, file_id);
795     let version = snap.url_file_version(&url);
796     lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version }
797 }
798 
location( snap: &GlobalStateSnapshot, frange: FileRange, ) -> Cancellable<lsp_types::Location>799 pub(crate) fn location(
800     snap: &GlobalStateSnapshot,
801     frange: FileRange,
802 ) -> Cancellable<lsp_types::Location> {
803     let url = url(snap, frange.file_id);
804     let line_index = snap.file_line_index(frange.file_id)?;
805     let range = range(&line_index, frange.range);
806     let loc = lsp_types::Location::new(url, range);
807     Ok(loc)
808 }
809 
810 /// Prefer using `location_link`, if the client has the cap.
location_from_nav( snap: &GlobalStateSnapshot, nav: NavigationTarget, ) -> Cancellable<lsp_types::Location>811 pub(crate) fn location_from_nav(
812     snap: &GlobalStateSnapshot,
813     nav: NavigationTarget,
814 ) -> Cancellable<lsp_types::Location> {
815     let url = url(snap, nav.file_id);
816     let line_index = snap.file_line_index(nav.file_id)?;
817     let range = range(&line_index, nav.full_range);
818     let loc = lsp_types::Location::new(url, range);
819     Ok(loc)
820 }
821 
location_link( snap: &GlobalStateSnapshot, src: Option<FileRange>, target: NavigationTarget, ) -> Cancellable<lsp_types::LocationLink>822 pub(crate) fn location_link(
823     snap: &GlobalStateSnapshot,
824     src: Option<FileRange>,
825     target: NavigationTarget,
826 ) -> Cancellable<lsp_types::LocationLink> {
827     let origin_selection_range = match src {
828         Some(src) => {
829             let line_index = snap.file_line_index(src.file_id)?;
830             let range = range(&line_index, src.range);
831             Some(range)
832         }
833         None => None,
834     };
835     let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
836     let res = lsp_types::LocationLink {
837         origin_selection_range,
838         target_uri,
839         target_range,
840         target_selection_range,
841     };
842     Ok(res)
843 }
844 
location_info( snap: &GlobalStateSnapshot, target: NavigationTarget, ) -> Cancellable<(lsp_types::Url, lsp_types::Range, lsp_types::Range)>845 fn location_info(
846     snap: &GlobalStateSnapshot,
847     target: NavigationTarget,
848 ) -> Cancellable<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
849     let line_index = snap.file_line_index(target.file_id)?;
850 
851     let target_uri = url(snap, target.file_id);
852     let target_range = range(&line_index, target.full_range);
853     let target_selection_range =
854         target.focus_range.map(|it| range(&line_index, it)).unwrap_or(target_range);
855     Ok((target_uri, target_range, target_selection_range))
856 }
857 
goto_definition_response( snap: &GlobalStateSnapshot, src: Option<FileRange>, targets: Vec<NavigationTarget>, ) -> Cancellable<lsp_types::GotoDefinitionResponse>858 pub(crate) fn goto_definition_response(
859     snap: &GlobalStateSnapshot,
860     src: Option<FileRange>,
861     targets: Vec<NavigationTarget>,
862 ) -> Cancellable<lsp_types::GotoDefinitionResponse> {
863     if snap.config.location_link() {
864         let links = targets
865             .into_iter()
866             .map(|nav| location_link(snap, src, nav))
867             .collect::<Cancellable<Vec<_>>>()?;
868         Ok(links.into())
869     } else {
870         let locations = targets
871             .into_iter()
872             .map(|nav| {
873                 location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
874             })
875             .collect::<Cancellable<Vec<_>>>()?;
876         Ok(locations.into())
877     }
878 }
879 
outside_workspace_annotation_id() -> String880 fn outside_workspace_annotation_id() -> String {
881     String::from("OutsideWorkspace")
882 }
883 
snippet_text_document_edit( snap: &GlobalStateSnapshot, is_snippet: bool, file_id: FileId, edit: TextEdit, ) -> Cancellable<lsp_ext::SnippetTextDocumentEdit>884 pub(crate) fn snippet_text_document_edit(
885     snap: &GlobalStateSnapshot,
886     is_snippet: bool,
887     file_id: FileId,
888     edit: TextEdit,
889 ) -> Cancellable<lsp_ext::SnippetTextDocumentEdit> {
890     let text_document = optional_versioned_text_document_identifier(snap, file_id);
891     let line_index = snap.file_line_index(file_id)?;
892     let mut edits: Vec<_> =
893         edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
894 
895     if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
896         for edit in &mut edits {
897             edit.annotation_id = Some(outside_workspace_annotation_id())
898         }
899     }
900     Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
901 }
902 
snippet_text_document_ops( snap: &GlobalStateSnapshot, file_system_edit: FileSystemEdit, ) -> Cancellable<Vec<lsp_ext::SnippetDocumentChangeOperation>>903 pub(crate) fn snippet_text_document_ops(
904     snap: &GlobalStateSnapshot,
905     file_system_edit: FileSystemEdit,
906 ) -> Cancellable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
907     let mut ops = Vec::new();
908     match file_system_edit {
909         FileSystemEdit::CreateFile { dst, initial_contents } => {
910             let uri = snap.anchored_path(&dst);
911             let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile {
912                 uri: uri.clone(),
913                 options: None,
914                 annotation_id: None,
915             });
916             ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(create_file));
917             if !initial_contents.is_empty() {
918                 let text_document =
919                     lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None };
920                 let text_edit = lsp_ext::SnippetTextEdit {
921                     range: lsp_types::Range::default(),
922                     new_text: initial_contents,
923                     insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
924                     annotation_id: None,
925                 };
926                 let edit_file =
927                     lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
928                 ops.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit_file));
929             }
930         }
931         FileSystemEdit::MoveFile { src, dst } => {
932             let old_uri = snap.file_id_to_url(src);
933             let new_uri = snap.anchored_path(&dst);
934             let mut rename_file =
935                 lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
936             if snap.analysis.is_library_file(src).ok() == Some(true)
937                 && snap.config.change_annotation_support()
938             {
939                 rename_file.annotation_id = Some(outside_workspace_annotation_id())
940             }
941             ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
942                 rename_file,
943             )))
944         }
945         FileSystemEdit::MoveDir { src, src_id, dst } => {
946             let old_uri = snap.anchored_path(&src);
947             let new_uri = snap.anchored_path(&dst);
948             let mut rename_file =
949                 lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
950             if snap.analysis.is_library_file(src_id).ok() == Some(true)
951                 && snap.config.change_annotation_support()
952             {
953                 rename_file.annotation_id = Some(outside_workspace_annotation_id())
954             }
955             ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
956                 rename_file,
957             )))
958         }
959     }
960     Ok(ops)
961 }
962 
snippet_workspace_edit( snap: &GlobalStateSnapshot, source_change: SourceChange, ) -> Cancellable<lsp_ext::SnippetWorkspaceEdit>963 pub(crate) fn snippet_workspace_edit(
964     snap: &GlobalStateSnapshot,
965     source_change: SourceChange,
966 ) -> Cancellable<lsp_ext::SnippetWorkspaceEdit> {
967     let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
968 
969     for op in source_change.file_system_edits {
970         let ops = snippet_text_document_ops(snap, op)?;
971         document_changes.extend_from_slice(&ops);
972     }
973     for (file_id, edit) in source_change.source_file_edits {
974         let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?;
975         document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
976     }
977     let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
978         changes: None,
979         document_changes: Some(document_changes),
980         change_annotations: None,
981     };
982     if snap.config.change_annotation_support() {
983         workspace_edit.change_annotations = Some(
984             once((
985                 outside_workspace_annotation_id(),
986                 lsp_types::ChangeAnnotation {
987                     label: String::from("Edit outside of the workspace"),
988                     needs_confirmation: Some(true),
989                     description: Some(String::from(
990                         "This edit lies outside of the workspace and may affect dependencies",
991                     )),
992                 },
993             ))
994             .collect(),
995         )
996     }
997     Ok(workspace_edit)
998 }
999 
workspace_edit( snap: &GlobalStateSnapshot, source_change: SourceChange, ) -> Cancellable<lsp_types::WorkspaceEdit>1000 pub(crate) fn workspace_edit(
1001     snap: &GlobalStateSnapshot,
1002     source_change: SourceChange,
1003 ) -> Cancellable<lsp_types::WorkspaceEdit> {
1004     assert!(!source_change.is_snippet);
1005     snippet_workspace_edit(snap, source_change).map(|it| it.into())
1006 }
1007 
1008 impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit1009     fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit {
1010         lsp_types::WorkspaceEdit {
1011             changes: None,
1012             document_changes: snippet_workspace_edit.document_changes.map(|changes| {
1013                 lsp_types::DocumentChanges::Operations(
1014                     changes
1015                         .into_iter()
1016                         .map(|change| match change {
1017                             lsp_ext::SnippetDocumentChangeOperation::Op(op) => {
1018                                 lsp_types::DocumentChangeOperation::Op(op)
1019                             }
1020                             lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => {
1021                                 lsp_types::DocumentChangeOperation::Edit(
1022                                     lsp_types::TextDocumentEdit {
1023                                         text_document: edit.text_document,
1024                                         edits: edit.edits.into_iter().map(From::from).collect(),
1025                                     },
1026                                 )
1027                             }
1028                         })
1029                         .collect(),
1030                 )
1031             }),
1032             change_annotations: snippet_workspace_edit.change_annotations,
1033         }
1034     }
1035 }
1036 
1037 impl From<lsp_ext::SnippetTextEdit>
1038     for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
1039 {
1040     fn from(
1041         lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
1042     ) -> Self {
1043         match annotation_id {
1044             Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
1045                 text_edit: lsp_types::TextEdit { range, new_text },
1046                 annotation_id,
1047             }),
1048             None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
1049         }
1050     }
1051 }
1052 
call_hierarchy_item( snap: &GlobalStateSnapshot, target: NavigationTarget, ) -> Cancellable<lsp_types::CallHierarchyItem>1053 pub(crate) fn call_hierarchy_item(
1054     snap: &GlobalStateSnapshot,
1055     target: NavigationTarget,
1056 ) -> Cancellable<lsp_types::CallHierarchyItem> {
1057     let name = target.name.to_string();
1058     let detail = target.description.clone();
1059     let kind = target.kind.map(symbol_kind).unwrap_or(lsp_types::SymbolKind::FUNCTION);
1060     let (uri, range, selection_range) = location_info(snap, target)?;
1061     Ok(lsp_types::CallHierarchyItem {
1062         name,
1063         kind,
1064         tags: None,
1065         detail,
1066         uri,
1067         range,
1068         selection_range,
1069         data: None,
1070     })
1071 }
1072 
code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind1073 pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
1074     match kind {
1075         AssistKind::None | AssistKind::Generate => lsp_types::CodeActionKind::EMPTY,
1076         AssistKind::QuickFix => lsp_types::CodeActionKind::QUICKFIX,
1077         AssistKind::Refactor => lsp_types::CodeActionKind::REFACTOR,
1078         AssistKind::RefactorExtract => lsp_types::CodeActionKind::REFACTOR_EXTRACT,
1079         AssistKind::RefactorInline => lsp_types::CodeActionKind::REFACTOR_INLINE,
1080         AssistKind::RefactorRewrite => lsp_types::CodeActionKind::REFACTOR_REWRITE,
1081     }
1082 }
1083 
code_action( snap: &GlobalStateSnapshot, assist: Assist, resolve_data: Option<(usize, lsp_types::CodeActionParams)>, ) -> Cancellable<lsp_ext::CodeAction>1084 pub(crate) fn code_action(
1085     snap: &GlobalStateSnapshot,
1086     assist: Assist,
1087     resolve_data: Option<(usize, lsp_types::CodeActionParams)>,
1088 ) -> Cancellable<lsp_ext::CodeAction> {
1089     let mut res = lsp_ext::CodeAction {
1090         title: assist.label.to_string(),
1091         group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
1092         kind: Some(code_action_kind(assist.id.1)),
1093         edit: None,
1094         is_preferred: None,
1095         data: None,
1096         command: None,
1097     };
1098 
1099     if assist.trigger_signature_help && snap.config.client_commands().trigger_parameter_hints {
1100         res.command = Some(command::trigger_parameter_hints());
1101     }
1102 
1103     match (assist.source_change, resolve_data) {
1104         (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
1105         (None, Some((index, code_action_params))) => {
1106             res.data = Some(lsp_ext::CodeActionData {
1107                 id: format!("{}:{}:{index}", assist.id.0, assist.id.1.name()),
1108                 code_action_params,
1109             });
1110         }
1111         (None, None) => {
1112             stdx::never!("assist should always be resolved if client can't do lazy resolving")
1113         }
1114     };
1115     Ok(res)
1116 }
1117 
runnable( snap: &GlobalStateSnapshot, runnable: Runnable, ) -> Cancellable<lsp_ext::Runnable>1118 pub(crate) fn runnable(
1119     snap: &GlobalStateSnapshot,
1120     runnable: Runnable,
1121 ) -> Cancellable<lsp_ext::Runnable> {
1122     let config = snap.config.runnables();
1123     let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?;
1124     let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
1125     let target = spec.as_ref().map(|s| s.target.clone());
1126     let (cargo_args, executable_args) =
1127         CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg);
1128     let label = runnable.label(target);
1129     let location = location_link(snap, None, runnable.nav)?;
1130 
1131     Ok(lsp_ext::Runnable {
1132         label,
1133         location: Some(location),
1134         kind: lsp_ext::RunnableKind::Cargo,
1135         args: lsp_ext::CargoRunnable {
1136             workspace_root: workspace_root.map(|it| it.into()),
1137             override_cargo: config.override_cargo,
1138             cargo_args,
1139             cargo_extra_args: config.cargo_extra_args,
1140             executable_args,
1141             expect_test: None,
1142         },
1143     })
1144 }
1145 
code_lens( acc: &mut Vec<lsp_types::CodeLens>, snap: &GlobalStateSnapshot, annotation: Annotation, ) -> Cancellable<()>1146 pub(crate) fn code_lens(
1147     acc: &mut Vec<lsp_types::CodeLens>,
1148     snap: &GlobalStateSnapshot,
1149     annotation: Annotation,
1150 ) -> Cancellable<()> {
1151     let client_commands_config = snap.config.client_commands();
1152     match annotation.kind {
1153         AnnotationKind::Runnable(run) => {
1154             let line_index = snap.file_line_index(run.nav.file_id)?;
1155             let annotation_range = range(&line_index, annotation.range);
1156 
1157             let title = run.title();
1158             let can_debug = match run.kind {
1159                 ide::RunnableKind::DocTest { .. } => false,
1160                 ide::RunnableKind::TestMod { .. }
1161                 | ide::RunnableKind::Test { .. }
1162                 | ide::RunnableKind::Bench { .. }
1163                 | ide::RunnableKind::Bin => true,
1164             };
1165             let r = runnable(snap, run)?;
1166 
1167             let lens_config = snap.config.lens();
1168             if lens_config.run
1169                 && client_commands_config.run_single
1170                 && r.args.workspace_root.is_some()
1171             {
1172                 let command = command::run_single(&r, &title);
1173                 acc.push(lsp_types::CodeLens {
1174                     range: annotation_range,
1175                     command: Some(command),
1176                     data: None,
1177                 })
1178             }
1179             if lens_config.debug && can_debug && client_commands_config.debug_single {
1180                 let command = command::debug_single(&r);
1181                 acc.push(lsp_types::CodeLens {
1182                     range: annotation_range,
1183                     command: Some(command),
1184                     data: None,
1185                 })
1186             }
1187             if lens_config.interpret {
1188                 let command = command::interpret_single(&r);
1189                 acc.push(lsp_types::CodeLens {
1190                     range: annotation_range,
1191                     command: Some(command),
1192                     data: None,
1193                 })
1194             }
1195         }
1196         AnnotationKind::HasImpls { pos: file_range, data } => {
1197             if !client_commands_config.show_reference {
1198                 return Ok(());
1199             }
1200             let line_index = snap.file_line_index(file_range.file_id)?;
1201             let annotation_range = range(&line_index, annotation.range);
1202             let url = url(snap, file_range.file_id);
1203 
1204             let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
1205 
1206             let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
1207 
1208             let goto_params = lsp_types::request::GotoImplementationParams {
1209                 text_document_position_params: doc_pos,
1210                 work_done_progress_params: Default::default(),
1211                 partial_result_params: Default::default(),
1212             };
1213 
1214             let command = data.map(|ranges| {
1215                 let locations: Vec<lsp_types::Location> = ranges
1216                     .into_iter()
1217                     .filter_map(|target| {
1218                         location(
1219                             snap,
1220                             FileRange { file_id: target.file_id, range: target.full_range },
1221                         )
1222                         .ok()
1223                     })
1224                     .collect();
1225 
1226                 command::show_references(
1227                     implementation_title(locations.len()),
1228                     &url,
1229                     annotation_range.start,
1230                     locations,
1231                 )
1232             });
1233 
1234             acc.push(lsp_types::CodeLens {
1235                 range: annotation_range,
1236                 command,
1237                 data: (|| {
1238                     let version = snap.url_file_version(&url)?;
1239                     Some(
1240                         to_value(lsp_ext::CodeLensResolveData {
1241                             version,
1242                             kind: lsp_ext::CodeLensResolveDataKind::Impls(goto_params),
1243                         })
1244                         .unwrap(),
1245                     )
1246                 })(),
1247             })
1248         }
1249         AnnotationKind::HasReferences { pos: file_range, data } => {
1250             if !client_commands_config.show_reference {
1251                 return Ok(());
1252             }
1253             let line_index = snap.file_line_index(file_range.file_id)?;
1254             let annotation_range = range(&line_index, annotation.range);
1255             let url = url(snap, file_range.file_id);
1256 
1257             let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
1258 
1259             let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
1260 
1261             let command = data.map(|ranges| {
1262                 let locations: Vec<lsp_types::Location> =
1263                     ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
1264 
1265                 command::show_references(
1266                     reference_title(locations.len()),
1267                     &url,
1268                     annotation_range.start,
1269                     locations,
1270                 )
1271             });
1272 
1273             acc.push(lsp_types::CodeLens {
1274                 range: annotation_range,
1275                 command,
1276                 data: (|| {
1277                     let version = snap.url_file_version(&url)?;
1278                     Some(
1279                         to_value(lsp_ext::CodeLensResolveData {
1280                             version,
1281                             kind: lsp_ext::CodeLensResolveDataKind::References(doc_pos),
1282                         })
1283                         .unwrap(),
1284                     )
1285                 })(),
1286             })
1287         }
1288     }
1289     Ok(())
1290 }
1291 
1292 pub(crate) mod command {
1293     use ide::{FileRange, NavigationTarget};
1294     use serde_json::to_value;
1295 
1296     use crate::{
1297         global_state::GlobalStateSnapshot,
1298         lsp_ext,
1299         to_proto::{location, location_link},
1300     };
1301 
show_references( title: String, uri: &lsp_types::Url, position: lsp_types::Position, locations: Vec<lsp_types::Location>, ) -> lsp_types::Command1302     pub(crate) fn show_references(
1303         title: String,
1304         uri: &lsp_types::Url,
1305         position: lsp_types::Position,
1306         locations: Vec<lsp_types::Location>,
1307     ) -> lsp_types::Command {
1308         // We cannot use the 'editor.action.showReferences' command directly
1309         // because that command requires vscode types which we convert in the handler
1310         // on the client side.
1311 
1312         lsp_types::Command {
1313             title,
1314             command: "rust-analyzer.showReferences".into(),
1315             arguments: Some(vec![
1316                 to_value(uri).unwrap(),
1317                 to_value(position).unwrap(),
1318                 to_value(locations).unwrap(),
1319             ]),
1320         }
1321     }
1322 
run_single(runnable: &lsp_ext::Runnable, title: &str) -> lsp_types::Command1323     pub(crate) fn run_single(runnable: &lsp_ext::Runnable, title: &str) -> lsp_types::Command {
1324         lsp_types::Command {
1325             title: title.to_string(),
1326             command: "rust-analyzer.runSingle".into(),
1327             arguments: Some(vec![to_value(runnable).unwrap()]),
1328         }
1329     }
1330 
debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command1331     pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command {
1332         lsp_types::Command {
1333             title: "Debug".into(),
1334             command: "rust-analyzer.debugSingle".into(),
1335             arguments: Some(vec![to_value(runnable).unwrap()]),
1336         }
1337     }
1338 
interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command1339     pub(crate) fn interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command {
1340         lsp_types::Command {
1341             title: "Interpret".into(),
1342             command: "rust-analyzer.interpretFunction".into(),
1343             // FIXME: use the `_runnable` here.
1344             arguments: Some(vec![]),
1345         }
1346     }
1347 
goto_location( snap: &GlobalStateSnapshot, nav: &NavigationTarget, ) -> Option<lsp_types::Command>1348     pub(crate) fn goto_location(
1349         snap: &GlobalStateSnapshot,
1350         nav: &NavigationTarget,
1351     ) -> Option<lsp_types::Command> {
1352         let value = if snap.config.location_link() {
1353             let link = location_link(snap, None, nav.clone()).ok()?;
1354             to_value(link).ok()?
1355         } else {
1356             let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
1357             let location = location(snap, range).ok()?;
1358             to_value(location).ok()?
1359         };
1360 
1361         Some(lsp_types::Command {
1362             title: nav.name.to_string(),
1363             command: "rust-analyzer.gotoLocation".into(),
1364             arguments: Some(vec![value]),
1365         })
1366     }
1367 
trigger_parameter_hints() -> lsp_types::Command1368     pub(crate) fn trigger_parameter_hints() -> lsp_types::Command {
1369         lsp_types::Command {
1370             title: "triggerParameterHints".into(),
1371             command: "rust-analyzer.triggerParameterHints".into(),
1372             arguments: None,
1373         }
1374     }
1375 }
1376 
implementation_title(count: usize) -> String1377 pub(crate) fn implementation_title(count: usize) -> String {
1378     if count == 1 {
1379         "1 implementation".into()
1380     } else {
1381         format!("{count} implementations")
1382     }
1383 }
1384 
reference_title(count: usize) -> String1385 pub(crate) fn reference_title(count: usize) -> String {
1386     if count == 1 {
1387         "1 reference".into()
1388     } else {
1389         format!("{count} references")
1390     }
1391 }
1392 
markup_content( markup: Markup, kind: ide::HoverDocFormat, ) -> lsp_types::MarkupContent1393 pub(crate) fn markup_content(
1394     markup: Markup,
1395     kind: ide::HoverDocFormat,
1396 ) -> lsp_types::MarkupContent {
1397     let kind = match kind {
1398         ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
1399         ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
1400     };
1401     let value = crate::markdown::format_docs(markup.as_str());
1402     lsp_types::MarkupContent { kind, value }
1403 }
1404 
rename_error(err: RenameError) -> crate::LspError1405 pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
1406     // This is wrong, but we don't have a better alternative I suppose?
1407     // https://github.com/microsoft/language-server-protocol/issues/1341
1408     invalid_params_error(err.to_string())
1409 }
1410 
1411 #[cfg(test)]
1412 mod tests {
1413     use ide::{Analysis, FilePosition};
1414     use test_utils::extract_offset;
1415     use triomphe::Arc;
1416 
1417     use super::*;
1418 
1419     #[test]
conv_fold_line_folding_only_fixup()1420     fn conv_fold_line_folding_only_fixup() {
1421         let text = r#"mod a;
1422 mod b;
1423 mod c;
1424 
1425 fn main() {
1426     if cond {
1427         a::do_a();
1428     } else {
1429         b::do_b();
1430     }
1431 }"#;
1432 
1433         let (analysis, file_id) = Analysis::from_single_file(text.to_string());
1434         let folds = analysis.folding_ranges(file_id).unwrap();
1435         assert_eq!(folds.len(), 4);
1436 
1437         let line_index = LineIndex {
1438             index: Arc::new(ide::LineIndex::new(text)),
1439             endings: LineEndings::Unix,
1440             encoding: PositionEncoding::Utf8,
1441         };
1442         let converted: Vec<lsp_types::FoldingRange> =
1443             folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
1444 
1445         let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
1446         assert_eq!(converted.len(), expected_lines.len());
1447         for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) {
1448             assert_eq!(folding_range.start_line, *start_line);
1449             assert_eq!(folding_range.start_character, None);
1450             assert_eq!(folding_range.end_line, *end_line);
1451             assert_eq!(folding_range.end_character, None);
1452         }
1453     }
1454 
1455     #[test]
calling_function_with_ignored_code_in_signature()1456     fn calling_function_with_ignored_code_in_signature() {
1457         let text = r#"
1458 fn foo() {
1459     bar($0);
1460 }
1461 /// ```
1462 /// # use crate::bar;
1463 /// bar(5);
1464 /// ```
1465 fn bar(_: usize) {}
1466 "#;
1467 
1468         let (offset, text) = extract_offset(text);
1469         let (analysis, file_id) = Analysis::from_single_file(text);
1470         let help = signature_help(
1471             analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(),
1472             CallInfoConfig { params_only: false, docs: true },
1473             false,
1474         );
1475         let docs = match &help.signatures[help.active_signature.unwrap() as usize].documentation {
1476             Some(lsp_types::Documentation::MarkupContent(content)) => &content.value,
1477             _ => panic!("documentation contains markup"),
1478         };
1479         assert!(docs.contains("bar(5)"));
1480         assert!(!docs.contains("use crate::bar"));
1481     }
1482 
1483     // `Url` is not able to parse windows paths on unix machines.
1484     #[test]
1485     #[cfg(target_os = "windows")]
test_lowercase_drive_letter()1486     fn test_lowercase_drive_letter() {
1487         use std::path::Path;
1488 
1489         let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap());
1490         assert_eq!(url.to_string(), "file:///c:/Test");
1491 
1492         let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap());
1493         assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
1494     }
1495 }
1496