1 //! Conversion of rust-analyzer specific types to lsp_types equivalents.
5 sync
::atomic
::{AtomicU32, Ordering}
,
9 Annotation
, AnnotationKind
, Assist
, AssistKind
, Cancellable
, CompletionItem
,
10 CompletionItemKind
, CompletionRelevance
, Documentation
, FileId
, FileRange
, FileSystemEdit
,
11 Fold
, FoldKind
, Highlight
, HlMod
, HlOperator
, HlPunct
, HlRange
, HlTag
, Indel
,
12 InlayFieldsToResolve
, InlayHint
, InlayHintLabel
, InlayHintLabelPart
, InlayKind
, Markup
,
13 NavigationTarget
, ReferenceCategory
, RenameError
, Runnable
, Severity
, SignatureHelp
,
14 SnippetEdit
, SourceChange
, StructureNodeKind
, SymbolKind
, TextEdit
, TextRange
, TextSize
,
16 use ide_db
::rust_doc
::format_docs
;
17 use itertools
::Itertools
;
18 use serde_json
::to_value
;
22 cargo_target_spec
::CargoTargetSpec
,
23 config
::{CallInfoConfig, Config}
,
24 global_state
::GlobalStateSnapshot
,
25 line_index
::{LineEndings, LineIndex, PositionEncoding}
,
27 semantic_tokens
::{self, standard_fallback_type}
,
28 utils
::invalid_params_error
,
31 lsp_ext
::{self, SnippetTextEdit}
,
34 pub(crate) fn position(line_index
: &LineIndex
, offset
: TextSize
) -> lsp_types
::Position
{
35 let line_col
= line_index
.index
.line_col(offset
);
36 match line_index
.encoding
{
37 PositionEncoding
::Utf8
=> lsp_types
::Position
::new(line_col
.line
, line_col
.col
),
38 PositionEncoding
::Wide(enc
) => {
39 let line_col
= line_index
.index
.to_wide(enc
, line_col
).unwrap();
40 lsp_types
::Position
::new(line_col
.line
, line_col
.col
)
45 pub(crate) fn range(line_index
: &LineIndex
, range
: TextRange
) -> lsp_types
::Range
{
46 let start
= position(line_index
, range
.start());
47 let end
= position(line_index
, range
.end());
48 lsp_types
::Range
::new(start
, end
)
51 pub(crate) fn symbol_kind(symbol_kind
: SymbolKind
) -> lsp_types
::SymbolKind
{
53 SymbolKind
::Function
=> lsp_types
::SymbolKind
::FUNCTION
,
54 SymbolKind
::Struct
=> lsp_types
::SymbolKind
::STRUCT
,
55 SymbolKind
::Enum
=> lsp_types
::SymbolKind
::ENUM
,
56 SymbolKind
::Variant
=> lsp_types
::SymbolKind
::ENUM_MEMBER
,
57 SymbolKind
::Trait
| SymbolKind
::TraitAlias
=> lsp_types
::SymbolKind
::INTERFACE
,
59 | SymbolKind
::BuiltinAttr
60 | SymbolKind
::Attribute
62 | SymbolKind
::DeriveHelper
=> lsp_types
::SymbolKind
::FUNCTION
,
63 SymbolKind
::Module
| SymbolKind
::ToolModule
=> lsp_types
::SymbolKind
::MODULE
,
64 SymbolKind
::TypeAlias
| SymbolKind
::TypeParam
| SymbolKind
::SelfType
=> {
65 lsp_types
::SymbolKind
::TYPE_PARAMETER
67 SymbolKind
::Field
=> lsp_types
::SymbolKind
::FIELD
,
68 SymbolKind
::Static
=> lsp_types
::SymbolKind
::CONSTANT
,
69 SymbolKind
::Const
=> lsp_types
::SymbolKind
::CONSTANT
,
70 SymbolKind
::ConstParam
=> lsp_types
::SymbolKind
::CONSTANT
,
71 SymbolKind
::Impl
=> lsp_types
::SymbolKind
::OBJECT
,
73 | SymbolKind
::SelfParam
74 | SymbolKind
::LifetimeParam
75 | SymbolKind
::ValueParam
76 | SymbolKind
::Label
=> lsp_types
::SymbolKind
::VARIABLE
,
77 SymbolKind
::Union
=> lsp_types
::SymbolKind
::STRUCT
,
81 pub(crate) fn structure_node_kind(kind
: StructureNodeKind
) -> lsp_types
::SymbolKind
{
83 StructureNodeKind
::SymbolKind(symbol
) => symbol_kind(symbol
),
84 StructureNodeKind
::Region
=> lsp_types
::SymbolKind
::NAMESPACE
,
88 pub(crate) fn document_highlight_kind(
89 category
: ReferenceCategory
,
90 ) -> Option
<lsp_types
::DocumentHighlightKind
> {
92 ReferenceCategory
::Read
=> Some(lsp_types
::DocumentHighlightKind
::READ
),
93 ReferenceCategory
::Write
=> Some(lsp_types
::DocumentHighlightKind
::WRITE
),
94 ReferenceCategory
::Import
=> None
,
98 pub(crate) fn diagnostic_severity(severity
: Severity
) -> lsp_types
::DiagnosticSeverity
{
100 Severity
::Error
=> lsp_types
::DiagnosticSeverity
::ERROR
,
101 Severity
::Warning
=> lsp_types
::DiagnosticSeverity
::WARNING
,
102 Severity
::WeakWarning
=> lsp_types
::DiagnosticSeverity
::HINT
,
104 Severity
::Allow
=> lsp_types
::DiagnosticSeverity
::INFORMATION
,
108 pub(crate) fn documentation(documentation
: Documentation
) -> lsp_types
::Documentation
{
109 let value
= format_docs(&documentation
);
110 let markup_content
= lsp_types
::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
;
111 lsp_types
::Documentation
::MarkupContent(markup_content
)
114 pub(crate) fn completion_item_kind(
115 completion_item_kind
: CompletionItemKind
,
116 ) -> lsp_types
::CompletionItemKind
{
117 match completion_item_kind
{
118 CompletionItemKind
::Binding
=> lsp_types
::CompletionItemKind
::VARIABLE
,
119 CompletionItemKind
::BuiltinType
=> lsp_types
::CompletionItemKind
::STRUCT
,
120 CompletionItemKind
::InferredType
=> lsp_types
::CompletionItemKind
::SNIPPET
,
121 CompletionItemKind
::Keyword
=> lsp_types
::CompletionItemKind
::KEYWORD
,
122 CompletionItemKind
::Method
=> lsp_types
::CompletionItemKind
::METHOD
,
123 CompletionItemKind
::Snippet
=> lsp_types
::CompletionItemKind
::SNIPPET
,
124 CompletionItemKind
::UnresolvedReference
=> lsp_types
::CompletionItemKind
::REFERENCE
,
125 CompletionItemKind
::SymbolKind(symbol
) => match symbol
{
126 SymbolKind
::Attribute
=> lsp_types
::CompletionItemKind
::FUNCTION
,
127 SymbolKind
::Const
=> lsp_types
::CompletionItemKind
::CONSTANT
,
128 SymbolKind
::ConstParam
=> lsp_types
::CompletionItemKind
::TYPE_PARAMETER
,
129 SymbolKind
::Derive
=> lsp_types
::CompletionItemKind
::FUNCTION
,
130 SymbolKind
::DeriveHelper
=> lsp_types
::CompletionItemKind
::FUNCTION
,
131 SymbolKind
::Enum
=> lsp_types
::CompletionItemKind
::ENUM
,
132 SymbolKind
::Field
=> lsp_types
::CompletionItemKind
::FIELD
,
133 SymbolKind
::Function
=> lsp_types
::CompletionItemKind
::FUNCTION
,
134 SymbolKind
::Impl
=> lsp_types
::CompletionItemKind
::TEXT
,
135 SymbolKind
::Label
=> lsp_types
::CompletionItemKind
::VARIABLE
,
136 SymbolKind
::LifetimeParam
=> lsp_types
::CompletionItemKind
::TYPE_PARAMETER
,
137 SymbolKind
::Local
=> lsp_types
::CompletionItemKind
::VARIABLE
,
138 SymbolKind
::Macro
=> lsp_types
::CompletionItemKind
::FUNCTION
,
139 SymbolKind
::Module
=> lsp_types
::CompletionItemKind
::MODULE
,
140 SymbolKind
::SelfParam
=> lsp_types
::CompletionItemKind
::VALUE
,
141 SymbolKind
::SelfType
=> lsp_types
::CompletionItemKind
::TYPE_PARAMETER
,
142 SymbolKind
::Static
=> lsp_types
::CompletionItemKind
::VALUE
,
143 SymbolKind
::Struct
=> lsp_types
::CompletionItemKind
::STRUCT
,
144 SymbolKind
::Trait
=> lsp_types
::CompletionItemKind
::INTERFACE
,
145 SymbolKind
::TraitAlias
=> lsp_types
::CompletionItemKind
::INTERFACE
,
146 SymbolKind
::TypeAlias
=> lsp_types
::CompletionItemKind
::STRUCT
,
147 SymbolKind
::TypeParam
=> lsp_types
::CompletionItemKind
::TYPE_PARAMETER
,
148 SymbolKind
::Union
=> lsp_types
::CompletionItemKind
::STRUCT
,
149 SymbolKind
::ValueParam
=> lsp_types
::CompletionItemKind
::VALUE
,
150 SymbolKind
::Variant
=> lsp_types
::CompletionItemKind
::ENUM_MEMBER
,
151 SymbolKind
::BuiltinAttr
=> lsp_types
::CompletionItemKind
::FUNCTION
,
152 SymbolKind
::ToolModule
=> lsp_types
::CompletionItemKind
::MODULE
,
157 pub(crate) fn text_edit(line_index
: &LineIndex
, indel
: Indel
) -> lsp_types
::TextEdit
{
158 let range
= range(line_index
, indel
.delete
);
159 let new_text
= match line_index
.endings
{
160 LineEndings
::Unix
=> indel
.insert
,
161 LineEndings
::Dos
=> indel
.insert
.replace('
\n'
, "\r\n"),
163 lsp_types
::TextEdit { range, new_text }
166 pub(crate) fn completion_text_edit(
167 line_index
: &LineIndex
,
168 insert_replace_support
: Option
<lsp_types
::Position
>,
170 ) -> lsp_types
::CompletionTextEdit
{
171 let text_edit
= text_edit(line_index
, indel
);
172 match insert_replace_support
{
173 Some(cursor_pos
) => lsp_types
::InsertReplaceEdit
{
174 new_text
: text_edit
.new_text
,
175 insert
: lsp_types
::Range { start: text_edit.range.start, end: cursor_pos }
,
176 replace
: text_edit
.range
,
179 None
=> text_edit
.into(),
183 pub(crate) fn snippet_text_edit(
184 line_index
: &LineIndex
,
187 ) -> lsp_ext
::SnippetTextEdit
{
188 let text_edit
= text_edit(line_index
, indel
);
189 let insert_text_format
=
190 if is_snippet { Some(lsp_types::InsertTextFormat::SNIPPET) }
else { None }
;
191 lsp_ext
::SnippetTextEdit
{
192 range
: text_edit
.range
,
193 new_text
: text_edit
.new_text
,
199 pub(crate) fn text_edit_vec(
200 line_index
: &LineIndex
,
202 ) -> Vec
<lsp_types
::TextEdit
> {
203 text_edit
.into_iter().map(|indel
| self::text_edit(line_index
, indel
)).collect()
206 pub(crate) fn snippet_text_edit_vec(
207 line_index
: &LineIndex
,
210 ) -> Vec
<lsp_ext
::SnippetTextEdit
> {
213 .map(|indel
| self::snippet_text_edit(line_index
, is_snippet
, indel
))
217 pub(crate) fn completion_items(
219 line_index
: &LineIndex
,
220 tdpp
: lsp_types
::TextDocumentPositionParams
,
221 items
: Vec
<CompletionItem
>,
222 ) -> Vec
<lsp_types
::CompletionItem
> {
223 let max_relevance
= items
.iter().map(|it
| it
.relevance
.score()).max().unwrap_or_default();
224 let mut res
= Vec
::with_capacity(items
.len());
226 completion_item(&mut res
, config
, line_index
, &tdpp
, max_relevance
, item
);
229 if let Some(limit
) = config
.completion().limit
{
230 res
.sort_by(|item1
, item2
| item1
.sort_text
.cmp(&item2
.sort_text
));
238 acc
: &mut Vec
<lsp_types
::CompletionItem
>,
240 line_index
: &LineIndex
,
241 tdpp
: &lsp_types
::TextDocumentPositionParams
,
243 item
: CompletionItem
,
245 let insert_replace_support
= config
.insert_replace_support().then_some(tdpp
.position
);
246 let ref_match
= item
.ref_match();
247 let lookup
= item
.lookup().to_string();
249 let mut additional_text_edits
= Vec
::new();
251 // LSP does not allow arbitrary edits in completion, so we have to do a
252 // non-trivial mapping here.
254 let mut text_edit
= None
;
255 let source_range
= item
.source_range
;
256 for indel
in item
.text_edit
{
257 if indel
.delete
.contains_range(source_range
) {
258 // Extract this indel as the main edit
259 text_edit
= Some(if indel
.delete
== source_range
{
260 self::completion_text_edit(line_index
, insert_replace_support
, indel
.clone())
262 assert
!(source_range
.end() == indel
.delete
.end());
263 let range1
= TextRange
::new(indel
.delete
.start(), source_range
.start());
264 let range2
= source_range
;
265 let indel1
= Indel
::delete(range1
);
266 let indel2
= Indel
::replace(range2
, indel
.insert
.clone());
267 additional_text_edits
.push(self::text_edit(line_index
, indel1
));
268 self::completion_text_edit(line_index
, insert_replace_support
, indel2
)
271 assert
!(source_range
.intersect(indel
.delete
).is_none());
272 let text_edit
= self::text_edit(line_index
, indel
.clone());
273 additional_text_edits
.push(text_edit
);
279 let insert_text_format
= item
.is_snippet
.then_some(lsp_types
::InsertTextFormat
::SNIPPET
);
280 let tags
= item
.deprecated
.then(|| vec
![lsp_types
::CompletionItemTag
::DEPRECATED
]);
281 let command
= if item
.trigger_call_info
&& config
.client_commands().trigger_parameter_hints
{
282 Some(command
::trigger_parameter_hints())
287 let mut lsp_item
= lsp_types
::CompletionItem
{
288 label
: item
.label
.to_string(),
290 filter_text
: Some(lookup
),
291 kind
: Some(completion_item_kind(item
.kind
)),
292 text_edit
: Some(text_edit
),
293 additional_text_edits
: Some(additional_text_edits
),
294 documentation
: item
.documentation
.map(documentation
),
295 deprecated
: Some(item
.deprecated
),
302 if config
.completion_label_details_support() {
303 lsp_item
.label_details
= Some(lsp_types
::CompletionItemLabelDetails
{
305 description
: lsp_item
.detail
.clone(),
309 set_score(&mut lsp_item
, max_relevance
, item
.relevance
);
311 if config
.completion().enable_imports_on_the_fly
{
312 if !item
.import_to_add
.is_empty() {
313 let imports
: Vec
<_
> = item
316 .filter_map(|(import_path
, import_name
)| {
317 Some(lsp_ext
::CompletionImport
{
318 full_import_path
: import_path
,
319 imported_name
: import_name
,
323 if !imports
.is_empty() {
324 let data
= lsp_ext
::CompletionResolveData { position: tdpp.clone(), imports }
;
325 lsp_item
.data
= Some(to_value(data
).unwrap());
330 if let Some((label
, indel
, relevance
)) = ref_match
{
331 let mut lsp_item_with_ref
= lsp_types
::CompletionItem { label, ..lsp_item.clone() }
;
333 .additional_text_edits
334 .get_or_insert_with(Default
::default)
335 .push(self::text_edit(line_index
, indel
));
336 set_score(&mut lsp_item_with_ref
, max_relevance
, relevance
);
337 acc
.push(lsp_item_with_ref
);
343 res
: &mut lsp_types
::CompletionItem
,
345 relevance
: CompletionRelevance
,
347 if relevance
.is_relevant() && relevance
.score() == max_relevance
{
348 res
.preselect
= Some(true);
350 // The relevance needs to be inverted to come up with a sort score
351 // because the client will sort ascending.
352 let sort_score
= relevance
.score() ^
0xFF_FF_FF_FF;
353 // Zero pad the string to ensure values can be properly sorted
354 // by the client. Hex format is used because it is easier to
355 // visually compare very large values, which the sort text
356 // tends to be since it is the opposite of the score.
357 res
.sort_text
= Some(format
!("{sort_score:08x}"));
361 pub(crate) fn signature_help(
362 call_info
: SignatureHelp
,
363 config
: CallInfoConfig
,
365 ) -> lsp_types
::SignatureHelp
{
366 let (label
, parameters
) = match (config
.params_only
, label_offsets
) {
367 (concise
, false) => {
368 let params
= call_info
370 .map(|label
| lsp_types
::ParameterInformation
{
371 label
: lsp_types
::ParameterLabel
::Simple(label
.to_string()),
374 .collect
::<Vec
<_
>>();
376 if concise { call_info.parameter_labels().join(", ") }
else { call_info.signature }
;
380 let params
= call_info
384 let start
= call_info
.signature
[..it
.start().into()].chars().count() as u32;
385 let end
= call_info
.signature
[..it
.end().into()].chars().count() as u32;
388 .map(|label_offsets
| lsp_types
::ParameterInformation
{
389 label
: lsp_types
::ParameterLabel
::LabelOffsets(label_offsets
),
392 .collect
::<Vec
<_
>>();
393 (call_info
.signature
, params
)
396 let mut params
= Vec
::new();
397 let mut label
= String
::new();
398 let mut first
= true;
399 for param
in call_info
.parameter_labels() {
401 label
.push_str(", ");
404 let start
= label
.chars().count() as u32;
405 label
.push_str(param
);
406 let end
= label
.chars().count() as u32;
407 params
.push(lsp_types
::ParameterInformation
{
408 label
: lsp_types
::ParameterLabel
::LabelOffsets([start
, end
]),
417 let documentation
= call_info
.doc
.filter(|_
| config
.docs
).map(|doc
| {
418 lsp_types
::Documentation
::MarkupContent(lsp_types
::MarkupContent
{
419 kind
: lsp_types
::MarkupKind
::Markdown
,
420 value
: format_docs(&doc
),
424 let active_parameter
= call_info
.active_parameter
.map(|it
| it
as u32);
426 let signature
= lsp_types
::SignatureInformation
{
429 parameters
: Some(parameters
),
432 lsp_types
::SignatureHelp
{
433 signatures
: vec
![signature
],
434 active_signature
: Some(0),
439 pub(crate) fn inlay_hint(
440 snap
: &GlobalStateSnapshot
,
441 fields_to_resolve
: &InlayFieldsToResolve
,
442 line_index
: &LineIndex
,
444 inlay_hint
: InlayHint
,
445 ) -> Cancellable
<lsp_types
::InlayHint
> {
446 let needs_resolve
= inlay_hint
.needs_resolve
;
447 let (label
, tooltip
, mut something_to_resolve
) =
448 inlay_hint_label(snap
, fields_to_resolve
, needs_resolve
, inlay_hint
.label
)?
;
449 let text_edits
= if needs_resolve
&& fields_to_resolve
.resolve_text_edits
{
450 something_to_resolve
|= inlay_hint
.text_edit
.is_some();
453 inlay_hint
.text_edit
.map(|it
| text_edit_vec(line_index
, it
))
455 let data
= if needs_resolve
&& something_to_resolve
{
456 Some(to_value(lsp_ext
::InlayHintResolveData { file_id: file_id.0 }
).unwrap())
461 Ok(lsp_types
::InlayHint
{
462 position
: match inlay_hint
.position
{
463 ide
::InlayHintPosition
::Before
=> position(line_index
, inlay_hint
.range
.start()),
464 ide
::InlayHintPosition
::After
=> position(line_index
, inlay_hint
.range
.end()),
466 padding_left
: Some(inlay_hint
.pad_left
),
467 padding_right
: Some(inlay_hint
.pad_right
),
468 kind
: match inlay_hint
.kind
{
469 InlayKind
::Parameter
=> Some(lsp_types
::InlayHintKind
::PARAMETER
),
470 InlayKind
::Type
| InlayKind
::Chaining
=> Some(lsp_types
::InlayHintKind
::TYPE
),
481 snap
: &GlobalStateSnapshot
,
482 fields_to_resolve
: &InlayFieldsToResolve
,
484 mut label
: InlayHintLabel
,
485 ) -> Cancellable
<(lsp_types
::InlayHintLabel
, Option
<lsp_types
::InlayHintTooltip
>, bool
)> {
486 let mut something_to_resolve
= false;
487 let (label
, tooltip
) = match &*label
.parts
{
488 [InlayHintLabelPart { linked_location: None, .. }
] => {
489 let InlayHintLabelPart { text, tooltip, .. }
= label
.parts
.pop().unwrap();
490 let hint_tooltip
= if needs_resolve
&& fields_to_resolve
.resolve_hint_tooltip
{
491 something_to_resolve
|= tooltip
.is_some();
495 Some(ide
::InlayTooltip
::String(s
)) => {
496 Some(lsp_types
::InlayHintTooltip
::String(s
))
498 Some(ide
::InlayTooltip
::Markdown(s
)) => {
499 Some(lsp_types
::InlayHintTooltip
::MarkupContent(lsp_types
::MarkupContent
{
500 kind
: lsp_types
::MarkupKind
::Markdown
,
507 (lsp_types
::InlayHintLabel
::String(text
), hint_tooltip
)
514 let tooltip
= if needs_resolve
&& fields_to_resolve
.resolve_label_tooltip
{
515 something_to_resolve
|= part
.tooltip
.is_some();
519 Some(ide
::InlayTooltip
::String(s
)) => {
520 Some(lsp_types
::InlayHintLabelPartTooltip
::String(s
))
522 Some(ide
::InlayTooltip
::Markdown(s
)) => {
523 Some(lsp_types
::InlayHintLabelPartTooltip
::MarkupContent(
524 lsp_types
::MarkupContent
{
525 kind
: lsp_types
::MarkupKind
::Markdown
,
533 let location
= if needs_resolve
&& fields_to_resolve
.resolve_label_location
{
534 something_to_resolve
|= part
.linked_location
.is_some();
537 part
.linked_location
.map(|range
| location(snap
, range
)).transpose()?
539 Ok(lsp_types
::InlayHintLabelPart
{
546 .collect
::<Cancellable
<_
>>()?
;
547 (lsp_types
::InlayHintLabel
::LabelParts(parts
), None
)
550 Ok((label
, tooltip
, something_to_resolve
))
553 static TOKEN_RESULT_COUNTER
: AtomicU32
= AtomicU32
::new(1);
555 pub(crate) fn semantic_tokens(
557 line_index
: &LineIndex
,
558 highlights
: Vec
<HlRange
>,
559 semantics_tokens_augments_syntax_tokens
: bool
,
560 non_standard_tokens
: bool
,
561 ) -> lsp_types
::SemanticTokens
{
562 let id
= TOKEN_RESULT_COUNTER
.fetch_add(1, Ordering
::SeqCst
).to_string();
563 let mut builder
= semantic_tokens
::SemanticTokensBuilder
::new(id
);
565 for highlight_range
in highlights
{
566 if highlight_range
.highlight
.is_empty() {
570 if semantics_tokens_augments_syntax_tokens
{
571 match highlight_range
.highlight
.tag
{
577 | HlTag
::NumericLiteral
579 | HlTag
::Punctuation(_
)
580 | HlTag
::StringLiteral
582 if highlight_range
.highlight
.mods
.is_empty() =>
590 let (mut ty
, mut mods
) = semantic_token_type_and_modifiers(highlight_range
.highlight
);
592 if !non_standard_tokens
{
593 ty
= match standard_fallback_type(ty
) {
597 mods
.standard_fallback();
599 let token_index
= semantic_tokens
::type_index(ty
);
600 let modifier_bitset
= mods
.0;
602 for mut text_range
in line_index
.index
.lines(highlight_range
.range
) {
603 if text
[text_range
].ends_with('
\n'
) {
605 TextRange
::new(text_range
.start(), text_range
.end() - TextSize
::of('
\n'
));
607 let range
= range(line_index
, text_range
);
608 builder
.push(range
, token_index
, modifier_bitset
);
615 pub(crate) fn semantic_token_delta(
616 previous
: &lsp_types
::SemanticTokens
,
617 current
: &lsp_types
::SemanticTokens
,
618 ) -> lsp_types
::SemanticTokensDelta
{
619 let result_id
= current
.result_id
.clone();
620 let edits
= semantic_tokens
::diff_tokens(&previous
.data
, ¤t
.data
);
621 lsp_types
::SemanticTokensDelta { result_id, edits }
624 fn semantic_token_type_and_modifiers(
625 highlight
: Highlight
,
626 ) -> (lsp_types
::SemanticTokenType
, semantic_tokens
::ModifierSet
) {
627 let mut mods
= semantic_tokens
::ModifierSet
::default();
628 let type_
= match highlight
.tag
{
629 HlTag
::Symbol(symbol
) => match symbol
{
630 SymbolKind
::Attribute
=> semantic_tokens
::DECORATOR
,
631 SymbolKind
::Derive
=> semantic_tokens
::DERIVE
,
632 SymbolKind
::DeriveHelper
=> semantic_tokens
::DERIVE_HELPER
,
633 SymbolKind
::Module
=> semantic_tokens
::NAMESPACE
,
634 SymbolKind
::Impl
=> semantic_tokens
::TYPE_ALIAS
,
635 SymbolKind
::Field
=> semantic_tokens
::PROPERTY
,
636 SymbolKind
::TypeParam
=> semantic_tokens
::TYPE_PARAMETER
,
637 SymbolKind
::ConstParam
=> semantic_tokens
::CONST_PARAMETER
,
638 SymbolKind
::LifetimeParam
=> semantic_tokens
::LIFETIME
,
639 SymbolKind
::Label
=> semantic_tokens
::LABEL
,
640 SymbolKind
::ValueParam
=> semantic_tokens
::PARAMETER
,
641 SymbolKind
::SelfParam
=> semantic_tokens
::SELF_KEYWORD
,
642 SymbolKind
::SelfType
=> semantic_tokens
::SELF_TYPE_KEYWORD
,
643 SymbolKind
::Local
=> semantic_tokens
::VARIABLE
,
644 SymbolKind
::Function
=> {
645 if highlight
.mods
.contains(HlMod
::Associated
) {
646 semantic_tokens
::METHOD
648 semantic_tokens
::FUNCTION
651 SymbolKind
::Const
=> {
652 mods
|= semantic_tokens
::CONSTANT
;
653 mods
|= semantic_tokens
::STATIC
;
654 semantic_tokens
::VARIABLE
656 SymbolKind
::Static
=> {
657 mods
|= semantic_tokens
::STATIC
;
658 semantic_tokens
::VARIABLE
660 SymbolKind
::Struct
=> semantic_tokens
::STRUCT
,
661 SymbolKind
::Enum
=> semantic_tokens
::ENUM
,
662 SymbolKind
::Variant
=> semantic_tokens
::ENUM_MEMBER
,
663 SymbolKind
::Union
=> semantic_tokens
::UNION
,
664 SymbolKind
::TypeAlias
=> semantic_tokens
::TYPE_ALIAS
,
665 SymbolKind
::Trait
=> semantic_tokens
::INTERFACE
,
666 SymbolKind
::TraitAlias
=> semantic_tokens
::INTERFACE
,
667 SymbolKind
::Macro
=> semantic_tokens
::MACRO
,
668 SymbolKind
::BuiltinAttr
=> semantic_tokens
::BUILTIN_ATTRIBUTE
,
669 SymbolKind
::ToolModule
=> semantic_tokens
::TOOL_MODULE
,
671 HlTag
::AttributeBracket
=> semantic_tokens
::ATTRIBUTE_BRACKET
,
672 HlTag
::BoolLiteral
=> semantic_tokens
::BOOLEAN
,
673 HlTag
::BuiltinType
=> semantic_tokens
::BUILTIN_TYPE
,
674 HlTag
::ByteLiteral
| HlTag
::NumericLiteral
=> semantic_tokens
::NUMBER
,
675 HlTag
::CharLiteral
=> semantic_tokens
::CHAR
,
676 HlTag
::Comment
=> semantic_tokens
::COMMENT
,
677 HlTag
::EscapeSequence
=> semantic_tokens
::ESCAPE_SEQUENCE
,
678 HlTag
::InvalidEscapeSequence
=> semantic_tokens
::INVALID_ESCAPE_SEQUENCE
,
679 HlTag
::FormatSpecifier
=> semantic_tokens
::FORMAT_SPECIFIER
,
680 HlTag
::Keyword
=> semantic_tokens
::KEYWORD
,
681 HlTag
::None
=> semantic_tokens
::GENERIC
,
682 HlTag
::Operator(op
) => match op
{
683 HlOperator
::Bitwise
=> semantic_tokens
::BITWISE
,
684 HlOperator
::Arithmetic
=> semantic_tokens
::ARITHMETIC
,
685 HlOperator
::Logical
=> semantic_tokens
::LOGICAL
,
686 HlOperator
::Comparison
=> semantic_tokens
::COMPARISON
,
687 HlOperator
::Other
=> semantic_tokens
::OPERATOR
,
689 HlTag
::StringLiteral
=> semantic_tokens
::STRING
,
690 HlTag
::UnresolvedReference
=> semantic_tokens
::UNRESOLVED_REFERENCE
,
691 HlTag
::Punctuation(punct
) => match punct
{
692 HlPunct
::Bracket
=> semantic_tokens
::BRACKET
,
693 HlPunct
::Brace
=> semantic_tokens
::BRACE
,
694 HlPunct
::Parenthesis
=> semantic_tokens
::PARENTHESIS
,
695 HlPunct
::Angle
=> semantic_tokens
::ANGLE
,
696 HlPunct
::Comma
=> semantic_tokens
::COMMA
,
697 HlPunct
::Dot
=> semantic_tokens
::DOT
,
698 HlPunct
::Colon
=> semantic_tokens
::COLON
,
699 HlPunct
::Semi
=> semantic_tokens
::SEMICOLON
,
700 HlPunct
::Other
=> semantic_tokens
::PUNCTUATION
,
701 HlPunct
::MacroBang
=> semantic_tokens
::MACRO_BANG
,
705 for modifier
in highlight
.mods
.iter() {
706 let modifier
= match modifier
{
707 HlMod
::Associated
=> continue,
708 HlMod
::Async
=> semantic_tokens
::ASYNC
,
709 HlMod
::Attribute
=> semantic_tokens
::ATTRIBUTE_MODIFIER
,
710 HlMod
::Callable
=> semantic_tokens
::CALLABLE
,
711 HlMod
::Consuming
=> semantic_tokens
::CONSUMING
,
712 HlMod
::ControlFlow
=> semantic_tokens
::CONTROL_FLOW
,
713 HlMod
::CrateRoot
=> semantic_tokens
::CRATE_ROOT
,
714 HlMod
::DefaultLibrary
=> semantic_tokens
::DEFAULT_LIBRARY
,
715 HlMod
::Definition
=> semantic_tokens
::DECLARATION
,
716 HlMod
::Documentation
=> semantic_tokens
::DOCUMENTATION
,
717 HlMod
::Injected
=> semantic_tokens
::INJECTED
,
718 HlMod
::IntraDocLink
=> semantic_tokens
::INTRA_DOC_LINK
,
719 HlMod
::Library
=> semantic_tokens
::LIBRARY
,
720 HlMod
::Macro
=> semantic_tokens
::MACRO_MODIFIER
,
721 HlMod
::Mutable
=> semantic_tokens
::MUTABLE
,
722 HlMod
::Public
=> semantic_tokens
::PUBLIC
,
723 HlMod
::Reference
=> semantic_tokens
::REFERENCE
,
724 HlMod
::Static
=> semantic_tokens
::STATIC
,
725 HlMod
::Trait
=> semantic_tokens
::TRAIT_MODIFIER
,
726 HlMod
::Unsafe
=> semantic_tokens
::UNSAFE
,
734 pub(crate) fn folding_range(
736 line_index
: &LineIndex
,
737 line_folding_only
: bool
,
739 ) -> lsp_types
::FoldingRange
{
740 let kind
= match fold
.kind
{
741 FoldKind
::Comment
=> Some(lsp_types
::FoldingRangeKind
::Comment
),
742 FoldKind
::Imports
=> Some(lsp_types
::FoldingRangeKind
::Imports
),
743 FoldKind
::Region
=> Some(lsp_types
::FoldingRangeKind
::Region
),
749 | FoldKind
::WhereClause
750 | FoldKind
::ReturnType
752 | FoldKind
::MatchArm
=> None
,
755 let range
= range(line_index
, fold
.range
);
757 if line_folding_only
{
758 // Clients with line_folding_only == true (such as VSCode) will fold the whole end line
759 // even if it contains text not in the folding range. To prevent that we exclude
760 // range.end.line from the folding region if there is more text after range.end
762 let has_more_text_on_end_line
= text
[TextRange
::new(fold
.range
.end(), TextSize
::of(text
))]
764 .take_while(|it
| *it
!= '
\n'
)
765 .any(|it
| !it
.is_whitespace());
767 let end_line
= if has_more_text_on_end_line
{
768 range
.end
.line
.saturating_sub(1)
773 lsp_types
::FoldingRange
{
774 start_line
: range
.start
.line
,
775 start_character
: None
,
779 collapsed_text
: None
,
782 lsp_types
::FoldingRange
{
783 start_line
: range
.start
.line
,
784 start_character
: Some(range
.start
.character
),
785 end_line
: range
.end
.line
,
786 end_character
: Some(range
.end
.character
),
788 collapsed_text
: None
,
793 pub(crate) fn url(snap
: &GlobalStateSnapshot
, file_id
: FileId
) -> lsp_types
::Url
{
794 snap
.file_id_to_url(file_id
)
797 /// Returns a `Url` object from a given path, will lowercase drive letters if present.
798 /// This will only happen when processing windows paths.
800 /// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
801 pub(crate) fn url_from_abs_path(path
: &AbsPath
) -> lsp_types
::Url
{
802 let url
= lsp_types
::Url
::from_file_path(path
).unwrap();
803 match path
.as_ref().components().next() {
804 Some(path
::Component
::Prefix(prefix
))
805 if matches
!(prefix
.kind(), path
::Prefix
::Disk(_
) | path
::Prefix
::VerbatimDisk(_
)) =>
807 // Need to lowercase driver letter
812 let driver_letter_range
= {
813 let (scheme
, drive_letter
, _rest
) = match url
.as_str().splitn(3, '
:'
).collect_tuple() {
817 let start
= scheme
.len() + '
:'
.len_utf8();
818 start
..(start
+ drive_letter
.len())
821 // Note: lowercasing the `path` itself doesn't help, the `Url::parse`
822 // machinery *also* canonicalizes the drive letter. So, just massage the
824 let mut url
: String
= url
.into();
825 url
[driver_letter_range
].make_ascii_lowercase();
826 lsp_types
::Url
::parse(&url
).unwrap()
829 pub(crate) fn optional_versioned_text_document_identifier(
830 snap
: &GlobalStateSnapshot
,
832 ) -> lsp_types
::OptionalVersionedTextDocumentIdentifier
{
833 let url
= url(snap
, file_id
);
834 let version
= snap
.url_file_version(&url
);
835 lsp_types
::OptionalVersionedTextDocumentIdentifier { uri: url, version }
838 pub(crate) fn location(
839 snap
: &GlobalStateSnapshot
,
841 ) -> Cancellable
<lsp_types
::Location
> {
842 let url
= url(snap
, frange
.file_id
);
843 let line_index
= snap
.file_line_index(frange
.file_id
)?
;
844 let range
= range(&line_index
, frange
.range
);
845 let loc
= lsp_types
::Location
::new(url
, range
);
849 /// Prefer using `location_link`, if the client has the cap.
850 pub(crate) fn location_from_nav(
851 snap
: &GlobalStateSnapshot
,
852 nav
: NavigationTarget
,
853 ) -> Cancellable
<lsp_types
::Location
> {
854 let url
= url(snap
, nav
.file_id
);
855 let line_index
= snap
.file_line_index(nav
.file_id
)?
;
856 let range
= range(&line_index
, nav
.full_range
);
857 let loc
= lsp_types
::Location
::new(url
, range
);
861 pub(crate) fn location_link(
862 snap
: &GlobalStateSnapshot
,
863 src
: Option
<FileRange
>,
864 target
: NavigationTarget
,
865 ) -> Cancellable
<lsp_types
::LocationLink
> {
866 let origin_selection_range
= match src
{
868 let line_index
= snap
.file_line_index(src
.file_id
)?
;
869 let range
= range(&line_index
, src
.range
);
874 let (target_uri
, target_range
, target_selection_range
) = location_info(snap
, target
)?
;
875 let res
= lsp_types
::LocationLink
{
876 origin_selection_range
,
879 target_selection_range
,
885 snap
: &GlobalStateSnapshot
,
886 target
: NavigationTarget
,
887 ) -> Cancellable
<(lsp_types
::Url
, lsp_types
::Range
, lsp_types
::Range
)> {
888 let line_index
= snap
.file_line_index(target
.file_id
)?
;
890 let target_uri
= url(snap
, target
.file_id
);
891 let target_range
= range(&line_index
, target
.full_range
);
892 let target_selection_range
=
893 target
.focus_range
.map(|it
| range(&line_index
, it
)).unwrap_or(target_range
);
894 Ok((target_uri
, target_range
, target_selection_range
))
897 pub(crate) fn goto_definition_response(
898 snap
: &GlobalStateSnapshot
,
899 src
: Option
<FileRange
>,
900 targets
: Vec
<NavigationTarget
>,
901 ) -> Cancellable
<lsp_types
::GotoDefinitionResponse
> {
902 if snap
.config
.location_link() {
905 .map(|nav
| location_link(snap
, src
, nav
))
906 .collect
::<Cancellable
<Vec
<_
>>>()?
;
909 let locations
= targets
912 location(snap
, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }
)
914 .collect
::<Cancellable
<Vec
<_
>>>()?
;
919 fn outside_workspace_annotation_id() -> String
{
920 String
::from("OutsideWorkspace")
923 fn merge_text_and_snippet_edits(
924 line_index
: &LineIndex
,
926 snippet_edit
: SnippetEdit
,
927 ) -> Vec
<SnippetTextEdit
> {
928 let mut edits
: Vec
<SnippetTextEdit
> = vec
![];
929 let mut snippets
= snippet_edit
.into_edit_ranges().into_iter().peekable();
930 let mut text_edits
= edit
.into_iter();
932 while let Some(current_indel
) = text_edits
.next() {
935 TextSize
::try_from(current_indel
.insert
.len()).unwrap_or(TextSize
::from(u32::MAX
));
936 TextRange
::at(current_indel
.delete
.start(), insert_len
)
939 // insert any snippets before the text edit
940 for (snippet_index
, snippet_range
) in
941 snippets
.take_while_ref(|(_
, range
)| range
.end() < new_range
.start())
943 let snippet_range
= if !stdx
::always
!(
944 snippet_range
.is_empty(),
945 "placeholder range {:?} is before current text edit range {:?}",
949 // only possible for tabstops, so make sure it's an empty/insert range
950 TextRange
::empty(snippet_range
.start())
955 let range
= range(&line_index
, snippet_range
);
956 let new_text
= format
!("${snippet_index}");
958 edits
.push(SnippetTextEdit
{
961 insert_text_format
: Some(lsp_types
::InsertTextFormat
::SNIPPET
),
966 if snippets
.peek().is_some_and(|(_
, range
)| new_range
.intersect(*range
).is_some()) {
967 // at least one snippet edit intersects this text edit,
968 // so gather all of the edits that intersect this text edit
969 let mut all_snippets
= snippets
970 .take_while_ref(|(_
, range
)| new_range
.intersect(*range
).is_some())
973 // ensure all of the ranges are wholly contained inside of the new range
974 all_snippets
.retain(|(_
, range
)| {
976 new_range
.contains_range(*range
),
977 "found placeholder range {:?} which wasn't fully inside of text edit's new range {:?}", range
, new_range
981 let mut text_edit
= text_edit(line_index
, current_indel
);
983 // escape out snippet text
984 stdx
::replace(&mut text_edit
.new_text
, '
\\'
, r
"\\");
985 stdx
::replace(&mut text_edit
.new_text
, '$'
, r
"\$");
988 for (index
, range
) in all_snippets
.iter().rev() {
989 let start
= (range
.start() - new_range
.start()).into();
990 let end
= (range
.end() - new_range
.start()).into();
992 if range
.is_empty() {
993 text_edit
.new_text
.insert_str(start
, &format
!("${index}"));
995 text_edit
.new_text
.insert(end
, '
}'
);
996 text_edit
.new_text
.insert_str(start
, &format
!("${{{index}:"));
1000 edits
.push(SnippetTextEdit
{
1001 range
: text_edit
.range
,
1002 new_text
: text_edit
.new_text
,
1003 insert_text_format
: Some(lsp_types
::InsertTextFormat
::SNIPPET
),
1004 annotation_id
: None
,
1007 // snippet edit was beyond the current one
1008 // since it wasn't consumed, it's available for the next pass
1009 edits
.push(snippet_text_edit(line_index
, false, current_indel
));
1013 // insert any remaining tabstops
1014 edits
.extend(snippets
.map(|(snippet_index
, snippet_range
)| {
1015 let snippet_range
= if !stdx
::always
!(
1016 snippet_range
.is_empty(),
1017 "found placeholder snippet {:?} without a text edit",
1020 TextRange
::empty(snippet_range
.start())
1025 let range
= range(&line_index
, snippet_range
);
1026 let new_text
= format
!("${snippet_index}");
1031 insert_text_format
: Some(lsp_types
::InsertTextFormat
::SNIPPET
),
1032 annotation_id
: None
,
1039 pub(crate) fn snippet_text_document_edit(
1040 snap
: &GlobalStateSnapshot
,
1044 snippet_edit
: Option
<SnippetEdit
>,
1045 ) -> Cancellable
<lsp_ext
::SnippetTextDocumentEdit
> {
1046 let text_document
= optional_versioned_text_document_identifier(snap
, file_id
);
1047 let line_index
= snap
.file_line_index(file_id
)?
;
1048 let mut edits
= if let Some(snippet_edit
) = snippet_edit
{
1049 merge_text_and_snippet_edits(&line_index
, edit
, snippet_edit
)
1051 edit
.into_iter().map(|it
| snippet_text_edit(&line_index
, is_snippet
, it
)).collect()
1054 if snap
.analysis
.is_library_file(file_id
)?
&& snap
.config
.change_annotation_support() {
1055 for edit
in &mut edits
{
1056 edit
.annotation_id
= Some(outside_workspace_annotation_id())
1059 Ok(lsp_ext
::SnippetTextDocumentEdit { text_document, edits }
)
1062 pub(crate) fn snippet_text_document_ops(
1063 snap
: &GlobalStateSnapshot
,
1064 file_system_edit
: FileSystemEdit
,
1065 ) -> Cancellable
<Vec
<lsp_ext
::SnippetDocumentChangeOperation
>> {
1066 let mut ops
= Vec
::new();
1067 match file_system_edit
{
1068 FileSystemEdit
::CreateFile { dst, initial_contents }
=> {
1069 let uri
= snap
.anchored_path(&dst
);
1070 let create_file
= lsp_types
::ResourceOp
::Create(lsp_types
::CreateFile
{
1073 annotation_id
: None
,
1075 ops
.push(lsp_ext
::SnippetDocumentChangeOperation
::Op(create_file
));
1076 if !initial_contents
.is_empty() {
1078 lsp_types
::OptionalVersionedTextDocumentIdentifier { uri, version: None }
;
1079 let text_edit
= lsp_ext
::SnippetTextEdit
{
1080 range
: lsp_types
::Range
::default(),
1081 new_text
: initial_contents
,
1082 insert_text_format
: Some(lsp_types
::InsertTextFormat
::PLAIN_TEXT
),
1083 annotation_id
: None
,
1086 lsp_ext
::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }
;
1087 ops
.push(lsp_ext
::SnippetDocumentChangeOperation
::Edit(edit_file
));
1090 FileSystemEdit
::MoveFile { src, dst }
=> {
1091 let old_uri
= snap
.file_id_to_url(src
);
1092 let new_uri
= snap
.anchored_path(&dst
);
1093 let mut rename_file
=
1094 lsp_types
::RenameFile { old_uri, new_uri, options: None, annotation_id: None }
;
1095 if snap
.analysis
.is_library_file(src
).ok() == Some(true)
1096 && snap
.config
.change_annotation_support()
1098 rename_file
.annotation_id
= Some(outside_workspace_annotation_id())
1100 ops
.push(lsp_ext
::SnippetDocumentChangeOperation
::Op(lsp_types
::ResourceOp
::Rename(
1104 FileSystemEdit
::MoveDir { src, src_id, dst }
=> {
1105 let old_uri
= snap
.anchored_path(&src
);
1106 let new_uri
= snap
.anchored_path(&dst
);
1107 let mut rename_file
=
1108 lsp_types
::RenameFile { old_uri, new_uri, options: None, annotation_id: None }
;
1109 if snap
.analysis
.is_library_file(src_id
).ok() == Some(true)
1110 && snap
.config
.change_annotation_support()
1112 rename_file
.annotation_id
= Some(outside_workspace_annotation_id())
1114 ops
.push(lsp_ext
::SnippetDocumentChangeOperation
::Op(lsp_types
::ResourceOp
::Rename(
1122 pub(crate) fn snippet_workspace_edit(
1123 snap
: &GlobalStateSnapshot
,
1124 source_change
: SourceChange
,
1125 ) -> Cancellable
<lsp_ext
::SnippetWorkspaceEdit
> {
1126 let mut document_changes
: Vec
<lsp_ext
::SnippetDocumentChangeOperation
> = Vec
::new();
1128 for op
in source_change
.file_system_edits
{
1129 let ops
= snippet_text_document_ops(snap
, op
)?
;
1130 document_changes
.extend_from_slice(&ops
);
1132 for (file_id
, (edit
, snippet_edit
)) in source_change
.source_file_edits
{
1133 let edit
= snippet_text_document_edit(
1135 source_change
.is_snippet
,
1140 document_changes
.push(lsp_ext
::SnippetDocumentChangeOperation
::Edit(edit
));
1142 let mut workspace_edit
= lsp_ext
::SnippetWorkspaceEdit
{
1144 document_changes
: Some(document_changes
),
1145 change_annotations
: None
,
1147 if snap
.config
.change_annotation_support() {
1148 workspace_edit
.change_annotations
= Some(
1150 outside_workspace_annotation_id(),
1151 lsp_types
::ChangeAnnotation
{
1152 label
: String
::from("Edit outside of the workspace"),
1153 needs_confirmation
: Some(true),
1154 description
: Some(String
::from(
1155 "This edit lies outside of the workspace and may affect dependencies",
1165 pub(crate) fn workspace_edit(
1166 snap
: &GlobalStateSnapshot
,
1167 source_change
: SourceChange
,
1168 ) -> Cancellable
<lsp_types
::WorkspaceEdit
> {
1169 assert
!(!source_change
.is_snippet
);
1170 snippet_workspace_edit(snap
, source_change
).map(|it
| it
.into())
1173 impl From
<lsp_ext
::SnippetWorkspaceEdit
> for lsp_types
::WorkspaceEdit
{
1174 fn from(snippet_workspace_edit
: lsp_ext
::SnippetWorkspaceEdit
) -> lsp_types
::WorkspaceEdit
{
1175 lsp_types
::WorkspaceEdit
{
1177 document_changes
: snippet_workspace_edit
.document_changes
.map(|changes
| {
1178 lsp_types
::DocumentChanges
::Operations(
1181 .map(|change
| match change
{
1182 lsp_ext
::SnippetDocumentChangeOperation
::Op(op
) => {
1183 lsp_types
::DocumentChangeOperation
::Op(op
)
1185 lsp_ext
::SnippetDocumentChangeOperation
::Edit(edit
) => {
1186 lsp_types
::DocumentChangeOperation
::Edit(
1187 lsp_types
::TextDocumentEdit
{
1188 text_document
: edit
.text_document
,
1189 edits
: edit
.edits
.into_iter().map(From
::from
).collect(),
1197 change_annotations
: snippet_workspace_edit
.change_annotations
,
1202 impl From
<lsp_ext
::SnippetTextEdit
>
1203 for lsp_types
::OneOf
<lsp_types
::TextEdit
, lsp_types
::AnnotatedTextEdit
>
1206 lsp_ext
::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }
: lsp_ext
::SnippetTextEdit
,
1208 match annotation_id
{
1209 Some(annotation_id
) => lsp_types
::OneOf
::Right(lsp_types
::AnnotatedTextEdit
{
1210 text_edit
: lsp_types
::TextEdit { range, new_text }
,
1213 None
=> lsp_types
::OneOf
::Left(lsp_types
::TextEdit { range, new_text }
),
1218 pub(crate) fn call_hierarchy_item(
1219 snap
: &GlobalStateSnapshot
,
1220 target
: NavigationTarget
,
1221 ) -> Cancellable
<lsp_types
::CallHierarchyItem
> {
1222 let name
= target
.name
.to_string();
1223 let detail
= target
.description
.clone();
1224 let kind
= target
.kind
.map(symbol_kind
).unwrap_or(lsp_types
::SymbolKind
::FUNCTION
);
1225 let (uri
, range
, selection_range
) = location_info(snap
, target
)?
;
1226 Ok(lsp_types
::CallHierarchyItem
{
1238 pub(crate) fn code_action_kind(kind
: AssistKind
) -> lsp_types
::CodeActionKind
{
1240 AssistKind
::None
| AssistKind
::Generate
=> lsp_types
::CodeActionKind
::EMPTY
,
1241 AssistKind
::QuickFix
=> lsp_types
::CodeActionKind
::QUICKFIX
,
1242 AssistKind
::Refactor
=> lsp_types
::CodeActionKind
::REFACTOR
,
1243 AssistKind
::RefactorExtract
=> lsp_types
::CodeActionKind
::REFACTOR_EXTRACT
,
1244 AssistKind
::RefactorInline
=> lsp_types
::CodeActionKind
::REFACTOR_INLINE
,
1245 AssistKind
::RefactorRewrite
=> lsp_types
::CodeActionKind
::REFACTOR_REWRITE
,
1249 pub(crate) fn code_action(
1250 snap
: &GlobalStateSnapshot
,
1252 resolve_data
: Option
<(usize, lsp_types
::CodeActionParams
)>,
1253 ) -> Cancellable
<lsp_ext
::CodeAction
> {
1254 let mut res
= lsp_ext
::CodeAction
{
1255 title
: assist
.label
.to_string(),
1256 group
: assist
.group
.filter(|_
| snap
.config
.code_action_group()).map(|gr
| gr
.0),
1257 kind
: Some(code_action_kind(assist
.id
.1)),
1264 if assist
.trigger_signature_help
&& snap
.config
.client_commands().trigger_parameter_hints
{
1265 res
.command
= Some(command
::trigger_parameter_hints());
1268 match (assist
.source_change
, resolve_data
) {
1269 (Some(it
), _
) => res
.edit
= Some(snippet_workspace_edit(snap
, it
)?
),
1270 (None
, Some((index
, code_action_params
))) => {
1271 res
.data
= Some(lsp_ext
::CodeActionData
{
1272 id
: format
!("{}:{}:{index}", assist
.id
.0, assist
.id
.1.name()),
1277 stdx
::never
!("assist should always be resolved if client can't do lazy resolving")
1283 pub(crate) fn runnable(
1284 snap
: &GlobalStateSnapshot
,
1286 ) -> Cancellable
<lsp_ext
::Runnable
> {
1287 let config
= snap
.config
.runnables();
1288 let spec
= CargoTargetSpec
::for_file(snap
, runnable
.nav
.file_id
)?
;
1289 let workspace_root
= spec
.as_ref().map(|it
| it
.workspace_root
.clone());
1290 let target
= spec
.as_ref().map(|s
| s
.target
.clone());
1291 let (cargo_args
, executable_args
) =
1292 CargoTargetSpec
::runnable_args(snap
, spec
, &runnable
.kind
, &runnable
.cfg
);
1293 let label
= runnable
.label(target
);
1294 let location
= location_link(snap
, None
, runnable
.nav
)?
;
1296 Ok(lsp_ext
::Runnable
{
1298 location
: Some(location
),
1299 kind
: lsp_ext
::RunnableKind
::Cargo
,
1300 args
: lsp_ext
::CargoRunnable
{
1301 workspace_root
: workspace_root
.map(|it
| it
.into()),
1302 override_cargo
: config
.override_cargo
,
1304 cargo_extra_args
: config
.cargo_extra_args
,
1311 pub(crate) fn code_lens(
1312 acc
: &mut Vec
<lsp_types
::CodeLens
>,
1313 snap
: &GlobalStateSnapshot
,
1314 annotation
: Annotation
,
1315 ) -> Cancellable
<()> {
1316 let client_commands_config
= snap
.config
.client_commands();
1317 match annotation
.kind
{
1318 AnnotationKind
::Runnable(run
) => {
1319 let line_index
= snap
.file_line_index(run
.nav
.file_id
)?
;
1320 let annotation_range
= range(&line_index
, annotation
.range
);
1322 let title
= run
.title();
1323 let can_debug
= match run
.kind
{
1324 ide
::RunnableKind
::DocTest { .. }
=> false,
1325 ide
::RunnableKind
::TestMod { .. }
1326 | ide
::RunnableKind
::Test { .. }
1327 | ide
::RunnableKind
::Bench { .. }
1328 | ide
::RunnableKind
::Bin
=> true,
1330 let r
= runnable(snap
, run
)?
;
1332 let lens_config
= snap
.config
.lens();
1334 && client_commands_config
.run_single
1335 && r
.args
.workspace_root
.is_some()
1337 let command
= command
::run_single(&r
, &title
);
1338 acc
.push(lsp_types
::CodeLens
{
1339 range
: annotation_range
,
1340 command
: Some(command
),
1344 if lens_config
.debug
&& can_debug
&& client_commands_config
.debug_single
{
1345 let command
= command
::debug_single(&r
);
1346 acc
.push(lsp_types
::CodeLens
{
1347 range
: annotation_range
,
1348 command
: Some(command
),
1352 if lens_config
.interpret
{
1353 let command
= command
::interpret_single(&r
);
1354 acc
.push(lsp_types
::CodeLens
{
1355 range
: annotation_range
,
1356 command
: Some(command
),
1361 AnnotationKind
::HasImpls { pos, data }
=> {
1362 if !client_commands_config
.show_reference
{
1365 let line_index
= snap
.file_line_index(pos
.file_id
)?
;
1366 let annotation_range
= range(&line_index
, annotation
.range
);
1367 let url
= url(snap
, pos
.file_id
);
1368 let pos
= position(&line_index
, pos
.offset
);
1370 let id
= lsp_types
::TextDocumentIdentifier { uri: url.clone() }
;
1372 let doc_pos
= lsp_types
::TextDocumentPositionParams
::new(id
, pos
);
1374 let goto_params
= lsp_types
::request
::GotoImplementationParams
{
1375 text_document_position_params
: doc_pos
,
1376 work_done_progress_params
: Default
::default(),
1377 partial_result_params
: Default
::default(),
1380 let command
= data
.map(|ranges
| {
1381 let locations
: Vec
<lsp_types
::Location
> = ranges
1383 .filter_map(|target
| {
1386 FileRange { file_id: target.file_id, range: target.full_range }
,
1392 command
::show_references(
1393 implementation_title(locations
.len()),
1400 acc
.push(lsp_types
::CodeLens
{
1401 range
: annotation_range
,
1404 let version
= snap
.url_file_version(&url
)?
;
1406 to_value(lsp_ext
::CodeLensResolveData
{
1408 kind
: lsp_ext
::CodeLensResolveDataKind
::Impls(goto_params
),
1415 AnnotationKind
::HasReferences { pos, data }
=> {
1416 if !client_commands_config
.show_reference
{
1419 let line_index
= snap
.file_line_index(pos
.file_id
)?
;
1420 let annotation_range
= range(&line_index
, annotation
.range
);
1421 let url
= url(snap
, pos
.file_id
);
1422 let pos
= position(&line_index
, pos
.offset
);
1424 let id
= lsp_types
::TextDocumentIdentifier { uri: url.clone() }
;
1426 let doc_pos
= lsp_types
::TextDocumentPositionParams
::new(id
, pos
);
1428 let command
= data
.map(|ranges
| {
1429 let locations
: Vec
<lsp_types
::Location
> =
1430 ranges
.into_iter().filter_map(|range
| location(snap
, range
).ok()).collect();
1432 command
::show_references(reference_title(locations
.len()), &url
, pos
, locations
)
1435 acc
.push(lsp_types
::CodeLens
{
1436 range
: annotation_range
,
1439 let version
= snap
.url_file_version(&url
)?
;
1441 to_value(lsp_ext
::CodeLensResolveData
{
1443 kind
: lsp_ext
::CodeLensResolveDataKind
::References(doc_pos
),
1454 pub(crate) mod command
{
1455 use ide
::{FileRange, NavigationTarget}
;
1456 use serde_json
::to_value
;
1459 global_state
::GlobalStateSnapshot
,
1460 lsp
::to_proto
::{location, location_link}
,
1464 pub(crate) fn show_references(
1466 uri
: &lsp_types
::Url
,
1467 position
: lsp_types
::Position
,
1468 locations
: Vec
<lsp_types
::Location
>,
1469 ) -> lsp_types
::Command
{
1470 // We cannot use the 'editor.action.showReferences' command directly
1471 // because that command requires vscode types which we convert in the handler
1472 // on the client side.
1474 lsp_types
::Command
{
1476 command
: "rust-analyzer.showReferences".into(),
1477 arguments
: Some(vec
![
1478 to_value(uri
).unwrap(),
1479 to_value(position
).unwrap(),
1480 to_value(locations
).unwrap(),
1485 pub(crate) fn run_single(runnable
: &lsp_ext
::Runnable
, title
: &str) -> lsp_types
::Command
{
1486 lsp_types
::Command
{
1487 title
: title
.to_string(),
1488 command
: "rust-analyzer.runSingle".into(),
1489 arguments
: Some(vec
![to_value(runnable
).unwrap()]),
1493 pub(crate) fn debug_single(runnable
: &lsp_ext
::Runnable
) -> lsp_types
::Command
{
1494 lsp_types
::Command
{
1495 title
: "Debug".into(),
1496 command
: "rust-analyzer.debugSingle".into(),
1497 arguments
: Some(vec
![to_value(runnable
).unwrap()]),
1501 pub(crate) fn interpret_single(_runnable
: &lsp_ext
::Runnable
) -> lsp_types
::Command
{
1502 lsp_types
::Command
{
1503 title
: "Interpret".into(),
1504 command
: "rust-analyzer.interpretFunction".into(),
1505 // FIXME: use the `_runnable` here.
1506 arguments
: Some(vec
![]),
1510 pub(crate) fn goto_location(
1511 snap
: &GlobalStateSnapshot
,
1512 nav
: &NavigationTarget
,
1513 ) -> Option
<lsp_types
::Command
> {
1514 let value
= if snap
.config
.location_link() {
1515 let link
= location_link(snap
, None
, nav
.clone()).ok()?
;
1516 to_value(link
).ok()?
1518 let range
= FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }
;
1519 let location
= location(snap
, range
).ok()?
;
1520 to_value(location
).ok()?
1523 Some(lsp_types
::Command
{
1524 title
: nav
.name
.to_string(),
1525 command
: "rust-analyzer.gotoLocation".into(),
1526 arguments
: Some(vec
![value
]),
1530 pub(crate) fn trigger_parameter_hints() -> lsp_types
::Command
{
1531 lsp_types
::Command
{
1532 title
: "triggerParameterHints".into(),
1533 command
: "rust-analyzer.triggerParameterHints".into(),
1539 pub(crate) fn implementation_title(count
: usize) -> String
{
1541 "1 implementation".into()
1543 format
!("{count} implementations")
1547 pub(crate) fn reference_title(count
: usize) -> String
{
1549 "1 reference".into()
1551 format
!("{count} references")
1555 pub(crate) fn markup_content(
1557 kind
: ide
::HoverDocFormat
,
1558 ) -> lsp_types
::MarkupContent
{
1559 let kind
= match kind
{
1560 ide
::HoverDocFormat
::Markdown
=> lsp_types
::MarkupKind
::Markdown
,
1561 ide
::HoverDocFormat
::PlainText
=> lsp_types
::MarkupKind
::PlainText
,
1563 let value
= format_docs(&Documentation
::new(markup
.into()));
1564 lsp_types
::MarkupContent { kind, value }
1567 pub(crate) fn rename_error(err
: RenameError
) -> LspError
{
1568 // This is wrong, but we don't have a better alternative I suppose?
1569 // https://github.com/microsoft/language-server-protocol/issues/1341
1570 invalid_params_error(err
.to_string())
1575 use expect_test
::{expect, Expect}
;
1576 use ide
::{Analysis, FilePosition}
;
1577 use ide_db
::source_change
::Snippet
;
1578 use test_utils
::extract_offset
;
1584 fn conv_fold_line_folding_only_fixup() {
1585 let text
= r
#"mod a;
1597 let (analysis
, file_id
) = Analysis
::from_single_file(text
.to_string());
1598 let folds
= analysis
.folding_ranges(file_id
).unwrap();
1599 assert_eq
!(folds
.len(), 4);
1601 let line_index
= LineIndex
{
1602 index
: Arc
::new(ide
::LineIndex
::new(text
)),
1603 endings
: LineEndings
::Unix
,
1604 encoding
: PositionEncoding
::Utf8
,
1606 let converted
: Vec
<lsp_types
::FoldingRange
> =
1607 folds
.into_iter().map(|it
| folding_range(text
, &line_index
, true, it
)).collect();
1609 let expected_lines
= [(0, 2), (4, 10), (5, 6), (7, 9)];
1610 assert_eq
!(converted
.len(), expected_lines
.len());
1611 for (folding_range
, (start_line
, end_line
)) in converted
.iter().zip(expected_lines
.iter()) {
1612 assert_eq
!(folding_range
.start_line
, *start_line
);
1613 assert_eq
!(folding_range
.start_character
, None
);
1614 assert_eq
!(folding_range
.end_line
, *end_line
);
1615 assert_eq
!(folding_range
.end_character
, None
);
1620 fn calling_function_with_ignored_code_in_signature() {
1626 /// # use crate::bar;
1632 let (offset
, text
) = extract_offset(text
);
1633 let (analysis
, file_id
) = Analysis
::from_single_file(text
);
1634 let help
= signature_help(
1635 analysis
.signature_help(FilePosition { file_id, offset }
).unwrap().unwrap(),
1636 CallInfoConfig { params_only: false, docs: true }
,
1639 let docs
= match &help
.signatures
[help
.active_signature
.unwrap() as usize].documentation
{
1640 Some(lsp_types
::Documentation
::MarkupContent(content
)) => &content
.value
,
1641 _
=> panic
!("documentation contains markup"),
1643 assert
!(docs
.contains("bar(5)"));
1644 assert
!(!docs
.contains("use crate::bar"));
1647 fn check_rendered_snippets(edit
: TextEdit
, snippets
: SnippetEdit
, expect
: Expect
) {
1648 let text
= r
#"/* place to put all ranges in */"#;
1649 let line_index
= LineIndex
{
1650 index
: Arc
::new(ide
::LineIndex
::new(text
)),
1651 endings
: LineEndings
::Unix
,
1652 encoding
: PositionEncoding
::Utf8
,
1655 let res
= merge_text_and_snippet_edits(&line_index
, edit
, snippets
);
1656 expect
.assert_debug_eq(&res
);
1660 fn snippet_rendering_only_tabstops() {
1661 let edit
= TextEdit
::builder().finish();
1662 let snippets
= SnippetEdit
::new(vec
![
1663 Snippet
::Tabstop(0.into
()),
1664 Snippet
::Tabstop(0.into
()),
1665 Snippet
::Tabstop(1.into
()),
1666 Snippet
::Tabstop(1.into
()),
1669 check_rendered_snippets(
1686 insert_text_format: Some(
1689 annotation_id: None,
1703 insert_text_format: Some(
1706 annotation_id: None,
1720 insert_text_format: Some(
1723 annotation_id: None,
1737 insert_text_format: Some(
1740 annotation_id: None,
1748 fn snippet_rendering_only_text_edits() {
1749 let mut edit
= TextEdit
::builder();
1750 edit
.insert(0.into
(), "abc".to_owned());
1751 edit
.insert(3.into
(), "def".to_owned());
1752 let edit
= edit
.finish();
1753 let snippets
= SnippetEdit
::new(vec
![]);
1755 check_rendered_snippets(
1772 insert_text_format: None,
1773 annotation_id: None,
1787 insert_text_format: None,
1788 annotation_id: None,
1796 fn snippet_rendering_tabstop_after_text_edit() {
1797 let mut edit
= TextEdit
::builder();
1798 edit
.insert(0.into
(), "abc".to_owned());
1799 let edit
= edit
.finish();
1800 let snippets
= SnippetEdit
::new(vec
![Snippet
::Tabstop(7.into
())]);
1802 check_rendered_snippets(
1819 insert_text_format: None,
1820 annotation_id: None,
1834 insert_text_format: Some(
1837 annotation_id: None,
1845 fn snippet_rendering_tabstops_before_text_edit() {
1846 let mut edit
= TextEdit
::builder();
1847 edit
.insert(2.into
(), "abc".to_owned());
1848 let edit
= edit
.finish();
1850 SnippetEdit
::new(vec
![Snippet
::Tabstop(0.into
()), Snippet
::Tabstop(0.into
())]);
1852 check_rendered_snippets(
1869 insert_text_format: Some(
1872 annotation_id: None,
1886 insert_text_format: Some(
1889 annotation_id: None,
1903 insert_text_format: None,
1904 annotation_id: None,
1912 fn snippet_rendering_tabstops_between_text_edits() {
1913 let mut edit
= TextEdit
::builder();
1914 edit
.insert(0.into
(), "abc".to_owned());
1915 edit
.insert(7.into
(), "abc".to_owned());
1916 let edit
= edit
.finish();
1918 SnippetEdit
::new(vec
![Snippet
::Tabstop(4.into
()), Snippet
::Tabstop(4.into
())]);
1920 check_rendered_snippets(
1937 insert_text_format: None,
1938 annotation_id: None,
1952 insert_text_format: Some(
1955 annotation_id: None,
1969 insert_text_format: Some(
1972 annotation_id: None,
1986 insert_text_format: None,
1987 annotation_id: None,
1995 fn snippet_rendering_multiple_tabstops_in_text_edit() {
1996 let mut edit
= TextEdit
::builder();
1997 edit
.insert(0.into
(), "abcdefghijkl".to_owned());
1998 let edit
= edit
.finish();
1999 let snippets
= SnippetEdit
::new(vec
![
2000 Snippet
::Tabstop(0.into
()),
2001 Snippet
::Tabstop(5.into
()),
2002 Snippet
::Tabstop(12.into
()),
2005 check_rendered_snippets(
2021 new_text: "$1abcde$2fghijkl$0",
2022 insert_text_format: Some(
2025 annotation_id: None,
2033 fn snippet_rendering_multiple_placeholders_in_text_edit() {
2034 let mut edit
= TextEdit
::builder();
2035 edit
.insert(0.into
(), "abcdefghijkl".to_owned());
2036 let edit
= edit
.finish();
2037 let snippets
= SnippetEdit
::new(vec
![
2038 Snippet
::Placeholder(TextRange
::new(0.into
(), 3.into
())),
2039 Snippet
::Placeholder(TextRange
::new(5.into
(), 7.into
())),
2040 Snippet
::Placeholder(TextRange
::new(10.into
(), 12.into
())),
2043 check_rendered_snippets(
2059 new_text: "${1:abc}de${2:fg}hij${0:kl}",
2060 insert_text_format: Some(
2063 annotation_id: None,
2071 fn snippet_rendering_escape_snippet_bits() {
2072 // only needed for snippet formats
2073 let mut edit
= TextEdit
::builder();
2074 edit
.insert(0.into
(), r
"abc\def$".to_owned());
2075 edit
.insert(8.into
(), r
"ghi\jkl$".to_owned());
2076 let edit
= edit
.finish();
2078 SnippetEdit
::new(vec
![Snippet
::Placeholder(TextRange
::new(0.into
(), 3.into
()))]);
2080 check_rendered_snippets(
2096 new_text: "${0:abc}\\\\def\\$",
2097 insert_text_format: Some(
2100 annotation_id: None,
2113 new_text: "ghi\\jkl$",
2114 insert_text_format: None,
2115 annotation_id: None,
2122 // `Url` is not able to parse windows paths on unix machines.
2124 #[cfg(target_os = "windows")]
2125 fn test_lowercase_drive_letter() {
2126 use std
::path
::Path
;
2128 let url
= url_from_abs_path(Path
::new("C:\\Test").try_into().unwrap());
2129 assert_eq
!(url
.to_string(), "file:///c:/Test");
2131 let url
= url_from_abs_path(Path
::new(r
#"\\localhost\C$\my_dir"#).try_into().unwrap());
2132 assert_eq
!(url
.to_string(), "file://localhost/C$/my_dir");