diff options
author | Aleksey Kladov <[email protected]> | 2020-05-10 12:55:24 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-05-10 18:01:26 +0100 |
commit | 1586bab0b97bef411e6187dfc389557edbc5a16e (patch) | |
tree | f4d8b67d398ca663d003099be63da9ebb7652826 | |
parent | 4578154b608fa075595103d0c933da60d55b25c8 (diff) |
Simplify proto conversion
Trait based infra in conv.rs is significantly more complicated than
what we actually need here.
-rw-r--r-- | crates/ra_ide/src/display/navigation_target.rs | 9 | ||||
-rw-r--r-- | crates/ra_text_edit/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/conv.rs | 726 | ||||
-rw-r--r-- | crates/rust-analyzer/src/from_proto.rs | 42 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 392 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 566 |
8 files changed, 807 insertions, 939 deletions
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index de35c6711..5da28edd2 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | TextRange, | 11 | TextRange, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::FileSymbol; | 14 | use crate::{FileRange, FileSymbol}; |
15 | 15 | ||
16 | use super::short_label::ShortLabel; | 16 | use super::short_label::ShortLabel; |
17 | 17 | ||
@@ -22,10 +22,11 @@ use super::short_label::ShortLabel; | |||
22 | /// code, like a function or a struct, but this is not strictly required. | 22 | /// code, like a function or a struct, but this is not strictly required. |
23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 23 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
24 | pub struct NavigationTarget { | 24 | pub struct NavigationTarget { |
25 | // FIXME: use FileRange? | ||
25 | file_id: FileId, | 26 | file_id: FileId, |
27 | full_range: TextRange, | ||
26 | name: SmolStr, | 28 | name: SmolStr, |
27 | kind: SyntaxKind, | 29 | kind: SyntaxKind, |
28 | full_range: TextRange, | ||
29 | focus_range: Option<TextRange>, | 30 | focus_range: Option<TextRange>, |
30 | container_name: Option<SmolStr>, | 31 | container_name: Option<SmolStr>, |
31 | description: Option<String>, | 32 | description: Option<String>, |
@@ -63,6 +64,10 @@ impl NavigationTarget { | |||
63 | self.file_id | 64 | self.file_id |
64 | } | 65 | } |
65 | 66 | ||
67 | pub fn file_range(&self) -> FileRange { | ||
68 | FileRange { file_id: self.file_id, range: self.full_range } | ||
69 | } | ||
70 | |||
66 | pub fn full_range(&self) -> TextRange { | 71 | pub fn full_range(&self) -> TextRange { |
67 | self.full_range | 72 | self.full_range |
68 | } | 73 | } |
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index 3409713ff..37f77cc47 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs | |||
@@ -75,6 +75,7 @@ impl TextEdit { | |||
75 | self.indels.is_empty() | 75 | self.indels.is_empty() |
76 | } | 76 | } |
77 | 77 | ||
78 | // FXME: impl IntoIter instead | ||
78 | pub fn as_indels(&self) -> &[Indel] { | 79 | pub fn as_indels(&self) -> &[Indel] { |
79 | &self.indels | 80 | &self.indels |
80 | } | 81 | } |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs deleted file mode 100644 index f64c90b5b..000000000 --- a/crates/rust-analyzer/src/conv.rs +++ /dev/null | |||
@@ -1,726 +0,0 @@ | |||
1 | //! Convenience module responsible for translating between rust-analyzer's types | ||
2 | //! and LSP types. | ||
3 | |||
4 | use lsp_types::{ | ||
5 | self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, | ||
6 | Location, LocationLink, MarkupContent, MarkupKind, ParameterInformation, ParameterLabel, | ||
7 | Position, Range, RenameFile, ResourceOp, SemanticTokenModifier, SemanticTokenType, | ||
8 | SignatureInformation, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, | ||
9 | TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, | ||
10 | }; | ||
11 | use ra_ide::{ | ||
12 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, | ||
13 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, | ||
14 | InlayHint, InlayKind, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, | ||
15 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, | ||
16 | }; | ||
17 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | ||
18 | use ra_text_edit::{Indel, TextEdit}; | ||
19 | use ra_vfs::LineEndings; | ||
20 | |||
21 | use crate::{ | ||
22 | req, | ||
23 | semantic_tokens::{self, ModifierSet, CONSTANT, CONTROL_FLOW, MUTABLE, UNSAFE}, | ||
24 | world::WorldSnapshot, | ||
25 | Result, | ||
26 | }; | ||
27 | use semantic_tokens::{ | ||
28 | ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION, | ||
29 | UNRESOLVED_REFERENCE, | ||
30 | }; | ||
31 | |||
32 | pub trait Conv { | ||
33 | type Output; | ||
34 | fn conv(self) -> Self::Output; | ||
35 | } | ||
36 | |||
37 | pub trait ConvWith<CTX> { | ||
38 | type Output; | ||
39 | fn conv_with(self, ctx: CTX) -> Self::Output; | ||
40 | } | ||
41 | |||
42 | pub trait TryConvWith<CTX> { | ||
43 | type Output; | ||
44 | fn try_conv_with(self, ctx: CTX) -> Result<Self::Output>; | ||
45 | } | ||
46 | |||
47 | impl Conv for SyntaxKind { | ||
48 | type Output = SymbolKind; | ||
49 | |||
50 | fn conv(self) -> <Self as Conv>::Output { | ||
51 | match self { | ||
52 | SyntaxKind::FN_DEF => SymbolKind::Function, | ||
53 | SyntaxKind::STRUCT_DEF => SymbolKind::Struct, | ||
54 | SyntaxKind::ENUM_DEF => SymbolKind::Enum, | ||
55 | SyntaxKind::ENUM_VARIANT => SymbolKind::EnumMember, | ||
56 | SyntaxKind::TRAIT_DEF => SymbolKind::Interface, | ||
57 | SyntaxKind::MACRO_CALL => SymbolKind::Function, | ||
58 | SyntaxKind::MODULE => SymbolKind::Module, | ||
59 | SyntaxKind::TYPE_ALIAS_DEF => SymbolKind::TypeParameter, | ||
60 | SyntaxKind::RECORD_FIELD_DEF => SymbolKind::Field, | ||
61 | SyntaxKind::STATIC_DEF => SymbolKind::Constant, | ||
62 | SyntaxKind::CONST_DEF => SymbolKind::Constant, | ||
63 | SyntaxKind::IMPL_DEF => SymbolKind::Object, | ||
64 | _ => SymbolKind::Variable, | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | impl Conv for ReferenceAccess { | ||
70 | type Output = ::lsp_types::DocumentHighlightKind; | ||
71 | |||
72 | fn conv(self) -> Self::Output { | ||
73 | use lsp_types::DocumentHighlightKind; | ||
74 | match self { | ||
75 | ReferenceAccess::Read => DocumentHighlightKind::Read, | ||
76 | ReferenceAccess::Write => DocumentHighlightKind::Write, | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | impl Conv for CompletionItemKind { | ||
82 | type Output = ::lsp_types::CompletionItemKind; | ||
83 | |||
84 | fn conv(self) -> <Self as Conv>::Output { | ||
85 | use lsp_types::CompletionItemKind::*; | ||
86 | match self { | ||
87 | CompletionItemKind::Keyword => Keyword, | ||
88 | CompletionItemKind::Snippet => Snippet, | ||
89 | CompletionItemKind::Module => Module, | ||
90 | CompletionItemKind::Function => Function, | ||
91 | CompletionItemKind::Struct => Struct, | ||
92 | CompletionItemKind::Enum => Enum, | ||
93 | CompletionItemKind::EnumVariant => EnumMember, | ||
94 | CompletionItemKind::BuiltinType => Struct, | ||
95 | CompletionItemKind::Binding => Variable, | ||
96 | CompletionItemKind::Field => Field, | ||
97 | CompletionItemKind::Trait => Interface, | ||
98 | CompletionItemKind::TypeAlias => Struct, | ||
99 | CompletionItemKind::Const => Constant, | ||
100 | CompletionItemKind::Static => Value, | ||
101 | CompletionItemKind::Method => Method, | ||
102 | CompletionItemKind::TypeParam => TypeParameter, | ||
103 | CompletionItemKind::Macro => Method, | ||
104 | CompletionItemKind::Attribute => EnumMember, | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | impl Conv for Severity { | ||
110 | type Output = DiagnosticSeverity; | ||
111 | fn conv(self) -> DiagnosticSeverity { | ||
112 | match self { | ||
113 | Severity::Error => DiagnosticSeverity::Error, | ||
114 | Severity::WeakWarning => DiagnosticSeverity::Hint, | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | ||
120 | type Output = ::lsp_types::CompletionItem; | ||
121 | |||
122 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem { | ||
123 | let mut additional_text_edits = Vec::new(); | ||
124 | let mut text_edit = None; | ||
125 | // LSP does not allow arbitrary edits in completion, so we have to do a | ||
126 | // non-trivial mapping here. | ||
127 | for indel in self.text_edit().as_indels() { | ||
128 | if indel.delete.contains_range(self.source_range()) { | ||
129 | text_edit = Some(if indel.delete == self.source_range() { | ||
130 | indel.conv_with((ctx.0, ctx.1)) | ||
131 | } else { | ||
132 | assert!(self.source_range().end() == indel.delete.end()); | ||
133 | let range1 = TextRange::new(indel.delete.start(), self.source_range().start()); | ||
134 | let range2 = self.source_range(); | ||
135 | let edit1 = Indel::replace(range1, String::new()); | ||
136 | let edit2 = Indel::replace(range2, indel.insert.clone()); | ||
137 | additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); | ||
138 | edit2.conv_with((ctx.0, ctx.1)) | ||
139 | }) | ||
140 | } else { | ||
141 | assert!(self.source_range().intersect(indel.delete).is_none()); | ||
142 | additional_text_edits.push(indel.conv_with((ctx.0, ctx.1))); | ||
143 | } | ||
144 | } | ||
145 | let text_edit = text_edit.unwrap(); | ||
146 | |||
147 | let mut res = lsp_types::CompletionItem { | ||
148 | label: self.label().to_string(), | ||
149 | detail: self.detail().map(|it| it.to_string()), | ||
150 | filter_text: Some(self.lookup().to_string()), | ||
151 | kind: self.kind().map(|it| it.conv()), | ||
152 | text_edit: Some(text_edit.into()), | ||
153 | additional_text_edits: Some(additional_text_edits), | ||
154 | documentation: self.documentation().map(|it| it.conv()), | ||
155 | deprecated: Some(self.deprecated()), | ||
156 | command: if self.trigger_call_info() { | ||
157 | let cmd = lsp_types::Command { | ||
158 | title: "triggerParameterHints".into(), | ||
159 | command: "editor.action.triggerParameterHints".into(), | ||
160 | arguments: None, | ||
161 | }; | ||
162 | Some(cmd) | ||
163 | } else { | ||
164 | None | ||
165 | }, | ||
166 | ..Default::default() | ||
167 | }; | ||
168 | |||
169 | if self.score().is_some() { | ||
170 | res.preselect = Some(true) | ||
171 | } | ||
172 | |||
173 | if self.deprecated() { | ||
174 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | ||
175 | } | ||
176 | |||
177 | res.insert_text_format = Some(match self.insert_text_format() { | ||
178 | InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, | ||
179 | InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, | ||
180 | }); | ||
181 | |||
182 | res | ||
183 | } | ||
184 | } | ||
185 | |||
186 | impl ConvWith<&LineIndex> for Position { | ||
187 | type Output = TextSize; | ||
188 | |||
189 | fn conv_with(self, line_index: &LineIndex) -> TextSize { | ||
190 | let line_col = LineCol { line: self.line as u32, col_utf16: self.character as u32 }; | ||
191 | line_index.offset(line_col) | ||
192 | } | ||
193 | } | ||
194 | |||
195 | impl ConvWith<&LineIndex> for TextSize { | ||
196 | type Output = Position; | ||
197 | |||
198 | fn conv_with(self, line_index: &LineIndex) -> Position { | ||
199 | let line_col = line_index.line_col(self); | ||
200 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)) | ||
201 | } | ||
202 | } | ||
203 | |||
204 | impl ConvWith<&LineIndex> for TextRange { | ||
205 | type Output = Range; | ||
206 | |||
207 | fn conv_with(self, line_index: &LineIndex) -> Range { | ||
208 | Range::new(self.start().conv_with(line_index), self.end().conv_with(line_index)) | ||
209 | } | ||
210 | } | ||
211 | |||
212 | impl ConvWith<&LineIndex> for Range { | ||
213 | type Output = TextRange; | ||
214 | |||
215 | fn conv_with(self, line_index: &LineIndex) -> TextRange { | ||
216 | TextRange::new(self.start.conv_with(line_index), self.end.conv_with(line_index)) | ||
217 | } | ||
218 | } | ||
219 | |||
220 | impl Conv for ra_ide::Documentation { | ||
221 | type Output = lsp_types::Documentation; | ||
222 | fn conv(self) -> Documentation { | ||
223 | Documentation::MarkupContent(MarkupContent { | ||
224 | kind: MarkupKind::Markdown, | ||
225 | value: crate::markdown::format_docs(self.as_str()), | ||
226 | }) | ||
227 | } | ||
228 | } | ||
229 | |||
230 | impl ConvWith<bool> for ra_ide::FunctionSignature { | ||
231 | type Output = lsp_types::SignatureInformation; | ||
232 | fn conv_with(self, concise: bool) -> Self::Output { | ||
233 | let (label, documentation, params) = if concise { | ||
234 | let mut params = self.parameters; | ||
235 | if self.has_self_param { | ||
236 | params.remove(0); | ||
237 | } | ||
238 | (params.join(", "), None, params) | ||
239 | } else { | ||
240 | (self.to_string(), self.doc.map(|it| it.conv()), self.parameters) | ||
241 | }; | ||
242 | |||
243 | let parameters: Vec<ParameterInformation> = params | ||
244 | .into_iter() | ||
245 | .map(|param| ParameterInformation { | ||
246 | label: ParameterLabel::Simple(param), | ||
247 | documentation: None, | ||
248 | }) | ||
249 | .collect(); | ||
250 | |||
251 | SignatureInformation { label, documentation, parameters: Some(parameters) } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | impl ConvWith<(&LineIndex, LineEndings)> for TextEdit { | ||
256 | type Output = Vec<lsp_types::TextEdit>; | ||
257 | |||
258 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> { | ||
259 | self.as_indels().iter().map_conv_with(ctx).collect() | ||
260 | } | ||
261 | } | ||
262 | |||
263 | impl ConvWith<(&LineIndex, LineEndings)> for &Indel { | ||
264 | type Output = lsp_types::TextEdit; | ||
265 | |||
266 | fn conv_with( | ||
267 | self, | ||
268 | (line_index, line_endings): (&LineIndex, LineEndings), | ||
269 | ) -> lsp_types::TextEdit { | ||
270 | let mut new_text = self.insert.clone(); | ||
271 | if line_endings == LineEndings::Dos { | ||
272 | new_text = new_text.replace('\n', "\r\n"); | ||
273 | } | ||
274 | lsp_types::TextEdit { range: self.delete.conv_with(line_index), new_text } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | pub(crate) struct FoldConvCtx<'a> { | ||
279 | pub(crate) text: &'a str, | ||
280 | pub(crate) line_index: &'a LineIndex, | ||
281 | pub(crate) line_folding_only: bool, | ||
282 | } | ||
283 | |||
284 | impl ConvWith<&FoldConvCtx<'_>> for Fold { | ||
285 | type Output = lsp_types::FoldingRange; | ||
286 | |||
287 | fn conv_with(self, ctx: &FoldConvCtx) -> lsp_types::FoldingRange { | ||
288 | let kind = match self.kind { | ||
289 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | ||
290 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | ||
291 | FoldKind::Mods => None, | ||
292 | FoldKind::Block => None, | ||
293 | }; | ||
294 | |||
295 | let range = self.range.conv_with(&ctx.line_index); | ||
296 | |||
297 | if ctx.line_folding_only { | ||
298 | // Clients with line_folding_only == true (such as VSCode) will fold the whole end line | ||
299 | // even if it contains text not in the folding range. To prevent that we exclude | ||
300 | // range.end.line from the folding region if there is more text after range.end | ||
301 | // on the same line. | ||
302 | let has_more_text_on_end_line = ctx.text | ||
303 | [TextRange::new(self.range.end(), TextSize::of(ctx.text))] | ||
304 | .chars() | ||
305 | .take_while(|it| *it != '\n') | ||
306 | .any(|it| !it.is_whitespace()); | ||
307 | |||
308 | let end_line = if has_more_text_on_end_line { | ||
309 | range.end.line.saturating_sub(1) | ||
310 | } else { | ||
311 | range.end.line | ||
312 | }; | ||
313 | |||
314 | lsp_types::FoldingRange { | ||
315 | start_line: range.start.line, | ||
316 | start_character: None, | ||
317 | end_line, | ||
318 | end_character: None, | ||
319 | kind, | ||
320 | } | ||
321 | } else { | ||
322 | lsp_types::FoldingRange { | ||
323 | start_line: range.start.line, | ||
324 | start_character: Some(range.start.character), | ||
325 | end_line: range.end.line, | ||
326 | end_character: Some(range.end.character), | ||
327 | kind, | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | |||
333 | impl ConvWith<&LineIndex> for InlayHint { | ||
334 | type Output = req::InlayHint; | ||
335 | fn conv_with(self, line_index: &LineIndex) -> Self::Output { | ||
336 | req::InlayHint { | ||
337 | label: self.label.to_string(), | ||
338 | range: self.range.conv_with(line_index), | ||
339 | kind: match self.kind { | ||
340 | InlayKind::ParameterHint => req::InlayKind::ParameterHint, | ||
341 | InlayKind::TypeHint => req::InlayKind::TypeHint, | ||
342 | InlayKind::ChainingHint => req::InlayKind::ChainingHint, | ||
343 | }, | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | impl Conv for Highlight { | ||
349 | type Output = (u32, u32); | ||
350 | |||
351 | fn conv(self) -> Self::Output { | ||
352 | let mut mods = ModifierSet::default(); | ||
353 | let type_ = match self.tag { | ||
354 | HighlightTag::Struct => SemanticTokenType::STRUCT, | ||
355 | HighlightTag::Enum => SemanticTokenType::ENUM, | ||
356 | HighlightTag::Union => UNION, | ||
357 | HighlightTag::TypeAlias => TYPE_ALIAS, | ||
358 | HighlightTag::Trait => SemanticTokenType::INTERFACE, | ||
359 | HighlightTag::BuiltinType => BUILTIN_TYPE, | ||
360 | HighlightTag::SelfType => SemanticTokenType::TYPE, | ||
361 | HighlightTag::Field => SemanticTokenType::MEMBER, | ||
362 | HighlightTag::Function => SemanticTokenType::FUNCTION, | ||
363 | HighlightTag::Module => SemanticTokenType::NAMESPACE, | ||
364 | HighlightTag::Constant => { | ||
365 | mods |= CONSTANT; | ||
366 | mods |= SemanticTokenModifier::STATIC; | ||
367 | SemanticTokenType::VARIABLE | ||
368 | } | ||
369 | HighlightTag::Static => { | ||
370 | mods |= SemanticTokenModifier::STATIC; | ||
371 | SemanticTokenType::VARIABLE | ||
372 | } | ||
373 | HighlightTag::EnumVariant => ENUM_MEMBER, | ||
374 | HighlightTag::Macro => SemanticTokenType::MACRO, | ||
375 | HighlightTag::Local => SemanticTokenType::VARIABLE, | ||
376 | HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER, | ||
377 | HighlightTag::Lifetime => LIFETIME, | ||
378 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => SemanticTokenType::NUMBER, | ||
379 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => SemanticTokenType::STRING, | ||
380 | HighlightTag::Comment => SemanticTokenType::COMMENT, | ||
381 | HighlightTag::Attribute => ATTRIBUTE, | ||
382 | HighlightTag::Keyword => SemanticTokenType::KEYWORD, | ||
383 | HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, | ||
384 | HighlightTag::FormatSpecifier => FORMAT_SPECIFIER, | ||
385 | }; | ||
386 | |||
387 | for modifier in self.modifiers.iter() { | ||
388 | let modifier = match modifier { | ||
389 | HighlightModifier::Definition => SemanticTokenModifier::DECLARATION, | ||
390 | HighlightModifier::ControlFlow => CONTROL_FLOW, | ||
391 | HighlightModifier::Mutable => MUTABLE, | ||
392 | HighlightModifier::Unsafe => UNSAFE, | ||
393 | }; | ||
394 | mods |= modifier; | ||
395 | } | ||
396 | |||
397 | (semantic_tokens::type_index(type_), mods.0) | ||
398 | } | ||
399 | } | ||
400 | |||
401 | impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> { | ||
402 | type Output = Option<T::Output>; | ||
403 | |||
404 | fn conv_with(self, ctx: CTX) -> Self::Output { | ||
405 | self.map(|x| ConvWith::conv_with(x, ctx)) | ||
406 | } | ||
407 | } | ||
408 | |||
409 | impl TryConvWith<&WorldSnapshot> for &Url { | ||
410 | type Output = FileId; | ||
411 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
412 | world.uri_to_file_id(self) | ||
413 | } | ||
414 | } | ||
415 | |||
416 | impl TryConvWith<&WorldSnapshot> for FileId { | ||
417 | type Output = Url; | ||
418 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Url> { | ||
419 | world.file_id_to_uri(self) | ||
420 | } | ||
421 | } | ||
422 | |||
423 | impl TryConvWith<&WorldSnapshot> for &TextDocumentItem { | ||
424 | type Output = FileId; | ||
425 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
426 | self.uri.try_conv_with(world) | ||
427 | } | ||
428 | } | ||
429 | |||
430 | impl TryConvWith<&WorldSnapshot> for &VersionedTextDocumentIdentifier { | ||
431 | type Output = FileId; | ||
432 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
433 | self.uri.try_conv_with(world) | ||
434 | } | ||
435 | } | ||
436 | |||
437 | impl TryConvWith<&WorldSnapshot> for &TextDocumentIdentifier { | ||
438 | type Output = FileId; | ||
439 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
440 | world.uri_to_file_id(&self.uri) | ||
441 | } | ||
442 | } | ||
443 | |||
444 | impl TryConvWith<&WorldSnapshot> for &TextDocumentPositionParams { | ||
445 | type Output = FilePosition; | ||
446 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FilePosition> { | ||
447 | let file_id = self.text_document.try_conv_with(world)?; | ||
448 | let line_index = world.analysis().file_line_index(file_id)?; | ||
449 | let offset = self.position.conv_with(&line_index); | ||
450 | Ok(FilePosition { file_id, offset }) | ||
451 | } | ||
452 | } | ||
453 | |||
454 | impl TryConvWith<&WorldSnapshot> for (&TextDocumentIdentifier, Range) { | ||
455 | type Output = FileRange; | ||
456 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileRange> { | ||
457 | let file_id = self.0.try_conv_with(world)?; | ||
458 | let line_index = world.analysis().file_line_index(file_id)?; | ||
459 | let range = self.1.conv_with(&line_index); | ||
460 | Ok(FileRange { file_id, range }) | ||
461 | } | ||
462 | } | ||
463 | |||
464 | impl<T: TryConvWith<CTX>, CTX: Copy> TryConvWith<CTX> for Vec<T> { | ||
465 | type Output = Vec<<T as TryConvWith<CTX>>::Output>; | ||
466 | fn try_conv_with(self, ctx: CTX) -> Result<Self::Output> { | ||
467 | let mut res = Vec::with_capacity(self.len()); | ||
468 | for item in self { | ||
469 | res.push(item.try_conv_with(ctx)?); | ||
470 | } | ||
471 | Ok(res) | ||
472 | } | ||
473 | } | ||
474 | |||
475 | impl TryConvWith<&WorldSnapshot> for SourceChange { | ||
476 | type Output = req::SourceChange; | ||
477 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::SourceChange> { | ||
478 | let cursor_position = match self.cursor_position { | ||
479 | None => None, | ||
480 | Some(pos) => { | ||
481 | let line_index = world.analysis().file_line_index(pos.file_id)?; | ||
482 | let edit = self | ||
483 | .source_file_edits | ||
484 | .iter() | ||
485 | .find(|it| it.file_id == pos.file_id) | ||
486 | .map(|it| &it.edit); | ||
487 | let line_col = match edit { | ||
488 | Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), | ||
489 | None => line_index.line_col(pos.offset), | ||
490 | }; | ||
491 | let position = | ||
492 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); | ||
493 | Some(TextDocumentPositionParams { | ||
494 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), | ||
495 | position, | ||
496 | }) | ||
497 | } | ||
498 | }; | ||
499 | let mut document_changes: Vec<DocumentChangeOperation> = Vec::new(); | ||
500 | for resource_op in self.file_system_edits.try_conv_with(world)? { | ||
501 | document_changes.push(DocumentChangeOperation::Op(resource_op)); | ||
502 | } | ||
503 | for text_document_edit in self.source_file_edits.try_conv_with(world)? { | ||
504 | document_changes.push(DocumentChangeOperation::Edit(text_document_edit)); | ||
505 | } | ||
506 | let workspace_edit = WorkspaceEdit { | ||
507 | changes: None, | ||
508 | document_changes: Some(DocumentChanges::Operations(document_changes)), | ||
509 | }; | ||
510 | Ok(req::SourceChange { label: self.label, workspace_edit, cursor_position }) | ||
511 | } | ||
512 | } | ||
513 | |||
514 | impl TryConvWith<&WorldSnapshot> for SourceFileEdit { | ||
515 | type Output = TextDocumentEdit; | ||
516 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<TextDocumentEdit> { | ||
517 | let text_document = VersionedTextDocumentIdentifier { | ||
518 | uri: self.file_id.try_conv_with(world)?, | ||
519 | version: None, | ||
520 | }; | ||
521 | let line_index = world.analysis().file_line_index(self.file_id)?; | ||
522 | let line_endings = world.file_line_endings(self.file_id); | ||
523 | let edits = | ||
524 | self.edit.as_indels().iter().map_conv_with((&line_index, line_endings)).collect(); | ||
525 | Ok(TextDocumentEdit { text_document, edits }) | ||
526 | } | ||
527 | } | ||
528 | |||
529 | impl TryConvWith<&WorldSnapshot> for FileSystemEdit { | ||
530 | type Output = ResourceOp; | ||
531 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<ResourceOp> { | ||
532 | let res = match self { | ||
533 | FileSystemEdit::CreateFile { source_root, path } => { | ||
534 | let uri = world.path_to_uri(source_root, &path)?; | ||
535 | ResourceOp::Create(CreateFile { uri, options: None }) | ||
536 | } | ||
537 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { | ||
538 | let old_uri = world.file_id_to_uri(src)?; | ||
539 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; | ||
540 | ResourceOp::Rename(RenameFile { old_uri, new_uri, options: None }) | ||
541 | } | ||
542 | }; | ||
543 | Ok(res) | ||
544 | } | ||
545 | } | ||
546 | |||
547 | impl TryConvWith<&WorldSnapshot> for &NavigationTarget { | ||
548 | type Output = Location; | ||
549 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Location> { | ||
550 | let line_index = world.analysis().file_line_index(self.file_id())?; | ||
551 | let range = self.range(); | ||
552 | to_location(self.file_id(), range, &world, &line_index) | ||
553 | } | ||
554 | } | ||
555 | |||
556 | impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<NavigationTarget>) { | ||
557 | type Output = LocationLink; | ||
558 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<LocationLink> { | ||
559 | let (src_file_id, target) = self; | ||
560 | |||
561 | let target_uri = target.info.file_id().try_conv_with(world)?; | ||
562 | let src_line_index = world.analysis().file_line_index(src_file_id)?; | ||
563 | let tgt_line_index = world.analysis().file_line_index(target.info.file_id())?; | ||
564 | |||
565 | let target_range = target.info.full_range().conv_with(&tgt_line_index); | ||
566 | |||
567 | let target_selection_range = target | ||
568 | .info | ||
569 | .focus_range() | ||
570 | .map(|it| it.conv_with(&tgt_line_index)) | ||
571 | .unwrap_or(target_range); | ||
572 | |||
573 | let res = LocationLink { | ||
574 | origin_selection_range: Some(target.range.conv_with(&src_line_index)), | ||
575 | target_uri, | ||
576 | target_range, | ||
577 | target_selection_range, | ||
578 | }; | ||
579 | Ok(res) | ||
580 | } | ||
581 | } | ||
582 | |||
583 | impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>) { | ||
584 | type Output = req::GotoDefinitionResponse; | ||
585 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::GotoTypeDefinitionResponse> { | ||
586 | let (file_id, RangeInfo { range, info: navs }) = self; | ||
587 | let links = navs | ||
588 | .into_iter() | ||
589 | .map(|nav| (file_id, RangeInfo::new(range, nav))) | ||
590 | .try_conv_with_to_vec(world)?; | ||
591 | if world.config.client_caps.location_link { | ||
592 | Ok(links.into()) | ||
593 | } else { | ||
594 | let locations: Vec<Location> = links | ||
595 | .into_iter() | ||
596 | .map(|link| Location { uri: link.target_uri, range: link.target_selection_range }) | ||
597 | .collect(); | ||
598 | Ok(locations.into()) | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | |||
603 | pub fn to_call_hierarchy_item( | ||
604 | file_id: FileId, | ||
605 | range: TextRange, | ||
606 | world: &WorldSnapshot, | ||
607 | line_index: &LineIndex, | ||
608 | nav: NavigationTarget, | ||
609 | ) -> Result<lsp_types::CallHierarchyItem> { | ||
610 | Ok(lsp_types::CallHierarchyItem { | ||
611 | name: nav.name().to_string(), | ||
612 | kind: nav.kind().conv(), | ||
613 | tags: None, | ||
614 | detail: nav.description().map(|it| it.to_string()), | ||
615 | uri: file_id.try_conv_with(&world)?, | ||
616 | range: nav.range().conv_with(&line_index), | ||
617 | selection_range: range.conv_with(&line_index), | ||
618 | }) | ||
619 | } | ||
620 | |||
621 | pub fn to_location( | ||
622 | file_id: FileId, | ||
623 | range: TextRange, | ||
624 | world: &WorldSnapshot, | ||
625 | line_index: &LineIndex, | ||
626 | ) -> Result<Location> { | ||
627 | let url = file_id.try_conv_with(world)?; | ||
628 | let loc = Location::new(url, range.conv_with(line_index)); | ||
629 | Ok(loc) | ||
630 | } | ||
631 | |||
632 | pub trait MapConvWith<CTX>: Sized { | ||
633 | type Output; | ||
634 | |||
635 | fn map_conv_with(self, ctx: CTX) -> ConvWithIter<Self, CTX> { | ||
636 | ConvWithIter { iter: self, ctx } | ||
637 | } | ||
638 | } | ||
639 | |||
640 | impl<CTX, I> MapConvWith<CTX> for I | ||
641 | where | ||
642 | I: Iterator, | ||
643 | I::Item: ConvWith<CTX>, | ||
644 | { | ||
645 | type Output = <I::Item as ConvWith<CTX>>::Output; | ||
646 | } | ||
647 | |||
648 | pub struct ConvWithIter<I, CTX> { | ||
649 | iter: I, | ||
650 | ctx: CTX, | ||
651 | } | ||
652 | |||
653 | impl<I, CTX> Iterator for ConvWithIter<I, CTX> | ||
654 | where | ||
655 | I: Iterator, | ||
656 | I::Item: ConvWith<CTX>, | ||
657 | CTX: Copy, | ||
658 | { | ||
659 | type Item = <I::Item as ConvWith<CTX>>::Output; | ||
660 | |||
661 | fn next(&mut self) -> Option<Self::Item> { | ||
662 | self.iter.next().map(|item| item.conv_with(self.ctx)) | ||
663 | } | ||
664 | } | ||
665 | |||
666 | pub trait TryConvWithToVec<CTX>: Sized { | ||
667 | type Output; | ||
668 | |||
669 | fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>>; | ||
670 | } | ||
671 | |||
672 | impl<I, CTX> TryConvWithToVec<CTX> for I | ||
673 | where | ||
674 | I: Iterator, | ||
675 | I::Item: TryConvWith<CTX>, | ||
676 | CTX: Copy, | ||
677 | { | ||
678 | type Output = <I::Item as TryConvWith<CTX>>::Output; | ||
679 | |||
680 | fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>> { | ||
681 | self.map(|it| it.try_conv_with(ctx)).collect() | ||
682 | } | ||
683 | } | ||
684 | |||
685 | #[cfg(test)] | ||
686 | mod tests { | ||
687 | use super::*; | ||
688 | use test_utils::extract_ranges; | ||
689 | |||
690 | #[test] | ||
691 | fn conv_fold_line_folding_only_fixup() { | ||
692 | let text = r#"<fold>mod a; | ||
693 | mod b; | ||
694 | mod c;</fold> | ||
695 | |||
696 | fn main() <fold>{ | ||
697 | if cond <fold>{ | ||
698 | a::do_a(); | ||
699 | }</fold> else <fold>{ | ||
700 | b::do_b(); | ||
701 | }</fold> | ||
702 | }</fold>"#; | ||
703 | |||
704 | let (ranges, text) = extract_ranges(text, "fold"); | ||
705 | assert_eq!(ranges.len(), 4); | ||
706 | let folds = vec![ | ||
707 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
708 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
709 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
710 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
711 | ]; | ||
712 | |||
713 | let line_index = LineIndex::new(&text); | ||
714 | let ctx = FoldConvCtx { text: &text, line_index: &line_index, line_folding_only: true }; | ||
715 | let converted: Vec<_> = folds.into_iter().map_conv_with(&ctx).collect(); | ||
716 | |||
717 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; | ||
718 | assert_eq!(converted.len(), expected_lines.len()); | ||
719 | for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) { | ||
720 | assert_eq!(folding_range.start_line, *start_line); | ||
721 | assert_eq!(folding_range.start_character, None); | ||
722 | assert_eq!(folding_range.end_line, *end_line); | ||
723 | assert_eq!(folding_range.end_character, None); | ||
724 | } | ||
725 | } | ||
726 | } | ||
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs new file mode 100644 index 000000000..4bb16a496 --- /dev/null +++ b/crates/rust-analyzer/src/from_proto.rs | |||
@@ -0,0 +1,42 @@ | |||
1 | //! Conversion lsp_types types to rust-analyzer specific ones. | ||
2 | use ra_db::{FileId, FilePosition, FileRange}; | ||
3 | use ra_ide::{LineCol, LineIndex}; | ||
4 | use ra_syntax::{TextRange, TextSize}; | ||
5 | |||
6 | use crate::{world::WorldSnapshot, Result}; | ||
7 | |||
8 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { | ||
9 | let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; | ||
10 | line_index.offset(line_col) | ||
11 | } | ||
12 | |||
13 | pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange { | ||
14 | let start = offset(line_index, range.start); | ||
15 | let end = offset(line_index, range.end); | ||
16 | TextRange::new(start, end) | ||
17 | } | ||
18 | |||
19 | pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { | ||
20 | world.uri_to_file_id(url) | ||
21 | } | ||
22 | |||
23 | pub(crate) fn file_position( | ||
24 | world: &WorldSnapshot, | ||
25 | tdpp: lsp_types::TextDocumentPositionParams, | ||
26 | ) -> Result<FilePosition> { | ||
27 | let file_id = file_id(world, &tdpp.text_document.uri)?; | ||
28 | let line_index = world.analysis().file_line_index(file_id)?; | ||
29 | let offset = offset(&*line_index, tdpp.position); | ||
30 | Ok(FilePosition { file_id, offset }) | ||
31 | } | ||
32 | |||
33 | pub(crate) fn file_range( | ||
34 | world: &WorldSnapshot, | ||
35 | text_document_identifier: lsp_types::TextDocumentIdentifier, | ||
36 | range: lsp_types::Range, | ||
37 | ) -> Result<FileRange> { | ||
38 | let file_id = file_id(world, &text_document_identifier.uri)?; | ||
39 | let line_index = world.analysis().file_line_index(file_id)?; | ||
40 | let range = text_range(&line_index, range); | ||
41 | Ok(FileRange { file_id, range }) | ||
42 | } | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 036bf62a7..6d719108c 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -20,9 +20,11 @@ macro_rules! eprintln { | |||
20 | mod vfs_glob; | 20 | mod vfs_glob; |
21 | mod caps; | 21 | mod caps; |
22 | mod cargo_target_spec; | 22 | mod cargo_target_spec; |
23 | mod conv; | 23 | mod to_proto; |
24 | mod from_proto; | ||
24 | mod main_loop; | 25 | mod main_loop; |
25 | mod markdown; | 26 | mod markdown; |
27 | // TODO: rename to lsp_ext | ||
26 | pub mod req; | 28 | pub mod req; |
27 | pub mod config; | 29 | pub mod config; |
28 | mod world; | 30 | mod world; |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 17b0b95b9..e27d85dc9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -37,8 +37,8 @@ use threadpool::ThreadPool; | |||
37 | 37 | ||
38 | use crate::{ | 38 | use crate::{ |
39 | config::{Config, FilesWatcher}, | 39 | config::{Config, FilesWatcher}, |
40 | conv::{ConvWith, TryConvWith}, | ||
41 | diagnostics::DiagnosticTask, | 40 | diagnostics::DiagnosticTask, |
41 | from_proto, | ||
42 | main_loop::{ | 42 | main_loop::{ |
43 | pending_requests::{PendingRequest, PendingRequests}, | 43 | pending_requests::{PendingRequest, PendingRequests}, |
44 | subscriptions::Subscriptions, | 44 | subscriptions::Subscriptions, |
@@ -584,7 +584,7 @@ fn on_notification( | |||
584 | Ok(params) => { | 584 | Ok(params) => { |
585 | let DidChangeTextDocumentParams { text_document, content_changes } = params; | 585 | let DidChangeTextDocumentParams { text_document, content_changes } = params; |
586 | let world = state.snapshot(); | 586 | let world = state.snapshot(); |
587 | let file_id = text_document.try_conv_with(&world)?; | 587 | let file_id = from_proto::file_id(&world, &text_document.uri)?; |
588 | let line_index = world.analysis().file_line_index(file_id)?; | 588 | let line_index = world.analysis().file_line_index(file_id)?; |
589 | let uri = text_document.uri; | 589 | let uri = text_document.uri; |
590 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 590 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
@@ -694,7 +694,7 @@ fn apply_document_changes( | |||
694 | line_index = Cow::Owned(LineIndex::new(&old_text)); | 694 | line_index = Cow::Owned(LineIndex::new(&old_text)); |
695 | } | 695 | } |
696 | index_valid = IndexValid::UpToLineExclusive(range.start.line); | 696 | index_valid = IndexValid::UpToLineExclusive(range.start.line); |
697 | let range = range.conv_with(&line_index); | 697 | let range = from_proto::text_range(&line_index, range); |
698 | let mut text = old_text.to_owned(); | 698 | let mut text = old_text.to_owned(); |
699 | match std::panic::catch_unwind(move || { | 699 | match std::panic::catch_unwind(move || { |
700 | text.replace_range(Range::<usize>::from(range), &change.text); | 700 | text.replace_range(Range::<usize>::from(range), &change.text); |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index f4353af64..daa5b4411 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -22,6 +22,7 @@ use ra_ide::{ | |||
22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | ||
25 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; |
26 | use rustc_hash::FxHashMap; | 27 | use rustc_hash::FxHashMap; |
27 | use serde::{Deserialize, Serialize}; | 28 | use serde::{Deserialize, Serialize}; |
@@ -31,18 +32,14 @@ use stdx::format_to; | |||
31 | use crate::{ | 32 | use crate::{ |
32 | cargo_target_spec::CargoTargetSpec, | 33 | cargo_target_spec::CargoTargetSpec, |
33 | config::RustfmtConfig, | 34 | config::RustfmtConfig, |
34 | conv::{ | ||
35 | to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, | ||
36 | TryConvWithToVec, | ||
37 | }, | ||
38 | diagnostics::DiagnosticTask, | 35 | diagnostics::DiagnosticTask, |
39 | from_json, | 36 | from_json, from_proto, |
40 | req::{self, InlayHint, InlayHintsParams}, | 37 | req::{self, InlayHint, InlayHintsParams}, |
41 | semantic_tokens::SemanticTokensBuilder, | 38 | semantic_tokens::SemanticTokensBuilder, |
39 | to_proto, | ||
42 | world::WorldSnapshot, | 40 | world::WorldSnapshot, |
43 | LspError, Result, | 41 | LspError, Result, |
44 | }; | 42 | }; |
45 | use ra_project_model::TargetKind; | ||
46 | 43 | ||
47 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 44 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { |
48 | let _p = profile("handle_analyzer_status"); | 45 | let _p = profile("handle_analyzer_status"); |
@@ -58,9 +55,9 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | |||
58 | 55 | ||
59 | pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> { | 56 | pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> { |
60 | let _p = profile("handle_syntax_tree"); | 57 | let _p = profile("handle_syntax_tree"); |
61 | let id = params.text_document.try_conv_with(&world)?; | 58 | let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
62 | let line_index = world.analysis().file_line_index(id)?; | 59 | let line_index = world.analysis().file_line_index(id)?; |
63 | let text_range = params.range.map(|p| p.conv_with(&line_index)); | 60 | let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); |
64 | let res = world.analysis().syntax_tree(id, text_range)?; | 61 | let res = world.analysis().syntax_tree(id, text_range)?; |
65 | Ok(res) | 62 | Ok(res) |
66 | } | 63 | } |
@@ -70,9 +67,9 @@ pub fn handle_expand_macro( | |||
70 | params: req::ExpandMacroParams, | 67 | params: req::ExpandMacroParams, |
71 | ) -> Result<Option<req::ExpandedMacro>> { | 68 | ) -> Result<Option<req::ExpandedMacro>> { |
72 | let _p = profile("handle_expand_macro"); | 69 | let _p = profile("handle_expand_macro"); |
73 | let file_id = params.text_document.try_conv_with(&world)?; | 70 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
74 | let line_index = world.analysis().file_line_index(file_id)?; | 71 | let line_index = world.analysis().file_line_index(file_id)?; |
75 | let offset = params.position.map(|p| p.conv_with(&line_index)); | 72 | let offset = params.position.map(|p| from_proto::offset(&line_index, p)); |
76 | 73 | ||
77 | match offset { | 74 | match offset { |
78 | None => Ok(None), | 75 | None => Ok(None), |
@@ -88,16 +85,16 @@ pub fn handle_selection_range( | |||
88 | params: req::SelectionRangeParams, | 85 | params: req::SelectionRangeParams, |
89 | ) -> Result<Option<Vec<req::SelectionRange>>> { | 86 | ) -> Result<Option<Vec<req::SelectionRange>>> { |
90 | let _p = profile("handle_selection_range"); | 87 | let _p = profile("handle_selection_range"); |
91 | let file_id = params.text_document.try_conv_with(&world)?; | 88 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
92 | let line_index = world.analysis().file_line_index(file_id)?; | 89 | let line_index = world.analysis().file_line_index(file_id)?; |
93 | let res: Result<Vec<req::SelectionRange>> = params | 90 | let res: Result<Vec<req::SelectionRange>> = params |
94 | .positions | 91 | .positions |
95 | .into_iter() | 92 | .into_iter() |
96 | .map_conv_with(&line_index) | ||
97 | .map(|position| { | 93 | .map(|position| { |
94 | let offset = from_proto::offset(&line_index, position); | ||
98 | let mut ranges = Vec::new(); | 95 | let mut ranges = Vec::new(); |
99 | { | 96 | { |
100 | let mut range = TextRange::new(position, position); | 97 | let mut range = TextRange::new(offset, offset); |
101 | loop { | 98 | loop { |
102 | ranges.push(range); | 99 | ranges.push(range); |
103 | let frange = FileRange { file_id, range }; | 100 | let frange = FileRange { file_id, range }; |
@@ -110,12 +107,12 @@ pub fn handle_selection_range( | |||
110 | } | 107 | } |
111 | } | 108 | } |
112 | let mut range = req::SelectionRange { | 109 | let mut range = req::SelectionRange { |
113 | range: ranges.last().unwrap().conv_with(&line_index), | 110 | range: to_proto::range(&line_index, *ranges.last().unwrap()), |
114 | parent: None, | 111 | parent: None, |
115 | }; | 112 | }; |
116 | for r in ranges.iter().rev().skip(1) { | 113 | for &r in ranges.iter().rev().skip(1) { |
117 | range = req::SelectionRange { | 114 | range = req::SelectionRange { |
118 | range: r.conv_with(&line_index), | 115 | range: to_proto::range(&line_index, r), |
119 | parent: Some(Box::new(range)), | 116 | parent: Some(Box::new(range)), |
120 | } | 117 | } |
121 | } | 118 | } |
@@ -131,22 +128,19 @@ pub fn handle_find_matching_brace( | |||
131 | params: req::FindMatchingBraceParams, | 128 | params: req::FindMatchingBraceParams, |
132 | ) -> Result<Vec<Position>> { | 129 | ) -> Result<Vec<Position>> { |
133 | let _p = profile("handle_find_matching_brace"); | 130 | let _p = profile("handle_find_matching_brace"); |
134 | let file_id = params.text_document.try_conv_with(&world)?; | 131 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
135 | let line_index = world.analysis().file_line_index(file_id)?; | 132 | let line_index = world.analysis().file_line_index(file_id)?; |
136 | let res = params | 133 | let res = params |
137 | .offsets | 134 | .offsets |
138 | .into_iter() | 135 | .into_iter() |
139 | .map_conv_with(&line_index) | 136 | .map(|position| { |
140 | .map(|offset| { | 137 | let offset = from_proto::offset(&line_index, position); |
141 | if let Ok(Some(matching_brace_offset)) = | 138 | let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { |
142 | world.analysis().matching_brace(FilePosition { file_id, offset }) | 139 | Ok(Some(matching_brace_offset)) => matching_brace_offset, |
143 | { | 140 | Err(_) | Ok(None) => offset, |
144 | matching_brace_offset | 141 | }; |
145 | } else { | 142 | to_proto::position(&line_index, offset) |
146 | offset | ||
147 | } | ||
148 | }) | 143 | }) |
149 | .map_conv_with(&line_index) | ||
150 | .collect(); | 144 | .collect(); |
151 | Ok(res) | 145 | Ok(res) |
152 | } | 146 | } |
@@ -156,8 +150,9 @@ pub fn handle_join_lines( | |||
156 | params: req::JoinLinesParams, | 150 | params: req::JoinLinesParams, |
157 | ) -> Result<req::SourceChange> { | 151 | ) -> Result<req::SourceChange> { |
158 | let _p = profile("handle_join_lines"); | 152 | let _p = profile("handle_join_lines"); |
159 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | 153 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; |
160 | world.analysis().join_lines(frange)?.try_conv_with(&world) | 154 | let source_change = world.analysis().join_lines(frange)?; |
155 | to_proto::source_change(&world, source_change) | ||
161 | } | 156 | } |
162 | 157 | ||
163 | pub fn handle_on_enter( | 158 | pub fn handle_on_enter( |
@@ -165,10 +160,10 @@ pub fn handle_on_enter( | |||
165 | params: req::TextDocumentPositionParams, | 160 | params: req::TextDocumentPositionParams, |
166 | ) -> Result<Option<req::SourceChange>> { | 161 | ) -> Result<Option<req::SourceChange>> { |
167 | let _p = profile("handle_on_enter"); | 162 | let _p = profile("handle_on_enter"); |
168 | let position = params.try_conv_with(&world)?; | 163 | let position = from_proto::file_position(&world, params)?; |
169 | match world.analysis().on_enter(position)? { | 164 | match world.analysis().on_enter(position)? { |
170 | None => Ok(None), | 165 | None => Ok(None), |
171 | Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), | 166 | Some(source_change) => to_proto::source_change(&world, source_change).map(Some), |
172 | } | 167 | } |
173 | } | 168 | } |
174 | 169 | ||
@@ -178,7 +173,7 @@ pub fn handle_on_type_formatting( | |||
178 | params: req::DocumentOnTypeFormattingParams, | 173 | params: req::DocumentOnTypeFormattingParams, |
179 | ) -> Result<Option<Vec<TextEdit>>> { | 174 | ) -> Result<Option<Vec<TextEdit>>> { |
180 | let _p = profile("handle_on_type_formatting"); | 175 | let _p = profile("handle_on_type_formatting"); |
181 | let mut position = params.text_document_position.try_conv_with(&world)?; | 176 | let mut position = from_proto::file_position(&world, params.text_document_position)?; |
182 | let line_index = world.analysis().file_line_index(position.file_id)?; | 177 | let line_index = world.analysis().file_line_index(position.file_id)?; |
183 | let line_endings = world.file_line_endings(position.file_id); | 178 | let line_endings = world.file_line_endings(position.file_id); |
184 | 179 | ||
@@ -208,7 +203,7 @@ pub fn handle_on_type_formatting( | |||
208 | // This should be a single-file edit | 203 | // This should be a single-file edit |
209 | let edit = edit.source_file_edits.pop().unwrap(); | 204 | let edit = edit.source_file_edits.pop().unwrap(); |
210 | 205 | ||
211 | let change: Vec<TextEdit> = edit.edit.conv_with((&line_index, line_endings)); | 206 | let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit); |
212 | Ok(Some(change)) | 207 | Ok(Some(change)) |
213 | } | 208 | } |
214 | 209 | ||
@@ -217,9 +212,8 @@ pub fn handle_document_symbol( | |||
217 | params: req::DocumentSymbolParams, | 212 | params: req::DocumentSymbolParams, |
218 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 213 | ) -> Result<Option<req::DocumentSymbolResponse>> { |
219 | let _p = profile("handle_document_symbol"); | 214 | let _p = profile("handle_document_symbol"); |
220 | let file_id = params.text_document.try_conv_with(&world)?; | 215 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
221 | let line_index = world.analysis().file_line_index(file_id)?; | 216 | let line_index = world.analysis().file_line_index(file_id)?; |
222 | let url = file_id.try_conv_with(&world)?; | ||
223 | 217 | ||
224 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); | 218 | let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); |
225 | 219 | ||
@@ -227,10 +221,10 @@ pub fn handle_document_symbol( | |||
227 | let doc_symbol = DocumentSymbol { | 221 | let doc_symbol = DocumentSymbol { |
228 | name: symbol.label, | 222 | name: symbol.label, |
229 | detail: symbol.detail, | 223 | detail: symbol.detail, |
230 | kind: symbol.kind.conv(), | 224 | kind: to_proto::symbol_kind(symbol.kind), |
231 | deprecated: Some(symbol.deprecated), | 225 | deprecated: Some(symbol.deprecated), |
232 | range: symbol.node_range.conv_with(&line_index), | 226 | range: to_proto::range(&line_index, symbol.node_range), |
233 | selection_range: symbol.navigation_range.conv_with(&line_index), | 227 | selection_range: to_proto::range(&line_index, symbol.navigation_range), |
234 | children: None, | 228 | children: None, |
235 | }; | 229 | }; |
236 | parents.push((doc_symbol, symbol.parent)); | 230 | parents.push((doc_symbol, symbol.parent)); |
@@ -249,34 +243,35 @@ pub fn handle_document_symbol( | |||
249 | } | 243 | } |
250 | } | 244 | } |
251 | 245 | ||
252 | if world.config.client_caps.hierarchical_symbols { | 246 | let res = if world.config.client_caps.hierarchical_symbols { |
253 | Ok(Some(document_symbols.into())) | 247 | document_symbols.into() |
254 | } else { | 248 | } else { |
249 | let url = to_proto::url(&world, file_id)?; | ||
255 | let mut symbol_information = Vec::<SymbolInformation>::new(); | 250 | let mut symbol_information = Vec::<SymbolInformation>::new(); |
256 | for symbol in document_symbols { | 251 | for symbol in document_symbols { |
257 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); | 252 | flatten_document_symbol(&symbol, None, &url, &mut symbol_information); |
258 | } | 253 | } |
254 | symbol_information.into() | ||
255 | }; | ||
256 | return Ok(Some(res)); | ||
259 | 257 | ||
260 | Ok(Some(symbol_information.into())) | 258 | fn flatten_document_symbol( |
261 | } | 259 | symbol: &DocumentSymbol, |
262 | } | 260 | container_name: Option<String>, |
263 | 261 | url: &Url, | |
264 | fn flatten_document_symbol( | 262 | res: &mut Vec<SymbolInformation>, |
265 | symbol: &DocumentSymbol, | 263 | ) { |
266 | container_name: Option<String>, | 264 | res.push(SymbolInformation { |
267 | url: &Url, | 265 | name: symbol.name.clone(), |
268 | res: &mut Vec<SymbolInformation>, | 266 | kind: symbol.kind, |
269 | ) { | 267 | deprecated: symbol.deprecated, |
270 | res.push(SymbolInformation { | 268 | location: Location::new(url.clone(), symbol.range), |
271 | name: symbol.name.clone(), | 269 | container_name: container_name, |
272 | kind: symbol.kind, | 270 | }); |
273 | deprecated: symbol.deprecated, | ||
274 | location: Location::new(url.clone(), symbol.range), | ||
275 | container_name: container_name, | ||
276 | }); | ||
277 | 271 | ||
278 | for child in symbol.children.iter().flatten() { | 272 | for child in symbol.children.iter().flatten() { |
279 | flatten_document_symbol(child, Some(symbol.name.clone()), url, res); | 273 | flatten_document_symbol(child, Some(symbol.name.clone()), url, res); |
274 | } | ||
280 | } | 275 | } |
281 | } | 276 | } |
282 | 277 | ||
@@ -313,8 +308,8 @@ pub fn handle_workspace_symbol( | |||
313 | for nav in world.analysis().symbol_search(query)? { | 308 | for nav in world.analysis().symbol_search(query)? { |
314 | let info = SymbolInformation { | 309 | let info = SymbolInformation { |
315 | name: nav.name().to_string(), | 310 | name: nav.name().to_string(), |
316 | kind: nav.kind().conv(), | 311 | kind: to_proto::symbol_kind(nav.kind()), |
317 | location: nav.try_conv_with(world)?, | 312 | location: to_proto::location(world, nav.file_range())?, |
318 | container_name: nav.container_name().map(|v| v.to_string()), | 313 | container_name: nav.container_name().map(|v| v.to_string()), |
319 | deprecated: None, | 314 | deprecated: None, |
320 | }; | 315 | }; |
@@ -329,12 +324,16 @@ pub fn handle_goto_definition( | |||
329 | params: req::GotoDefinitionParams, | 324 | params: req::GotoDefinitionParams, |
330 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 325 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
331 | let _p = profile("handle_goto_definition"); | 326 | let _p = profile("handle_goto_definition"); |
332 | let position = params.text_document_position_params.try_conv_with(&world)?; | 327 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
333 | let nav_info = match world.analysis().goto_definition(position)? { | 328 | let nav_info = match world.analysis().goto_definition(position)? { |
334 | None => return Ok(None), | 329 | None => return Ok(None), |
335 | Some(it) => it, | 330 | Some(it) => it, |
336 | }; | 331 | }; |
337 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 332 | let res = to_proto::goto_definition_response( |
333 | &world, | ||
334 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
335 | nav_info.info, | ||
336 | )?; | ||
338 | Ok(Some(res)) | 337 | Ok(Some(res)) |
339 | } | 338 | } |
340 | 339 | ||
@@ -343,12 +342,16 @@ pub fn handle_goto_implementation( | |||
343 | params: req::GotoImplementationParams, | 342 | params: req::GotoImplementationParams, |
344 | ) -> Result<Option<req::GotoImplementationResponse>> { | 343 | ) -> Result<Option<req::GotoImplementationResponse>> { |
345 | let _p = profile("handle_goto_implementation"); | 344 | let _p = profile("handle_goto_implementation"); |
346 | let position = params.text_document_position_params.try_conv_with(&world)?; | 345 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
347 | let nav_info = match world.analysis().goto_implementation(position)? { | 346 | let nav_info = match world.analysis().goto_implementation(position)? { |
348 | None => return Ok(None), | 347 | None => return Ok(None), |
349 | Some(it) => it, | 348 | Some(it) => it, |
350 | }; | 349 | }; |
351 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 350 | let res = to_proto::goto_definition_response( |
351 | &world, | ||
352 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
353 | nav_info.info, | ||
354 | )?; | ||
352 | Ok(Some(res)) | 355 | Ok(Some(res)) |
353 | } | 356 | } |
354 | 357 | ||
@@ -357,12 +360,16 @@ pub fn handle_goto_type_definition( | |||
357 | params: req::GotoTypeDefinitionParams, | 360 | params: req::GotoTypeDefinitionParams, |
358 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { | 361 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { |
359 | let _p = profile("handle_goto_type_definition"); | 362 | let _p = profile("handle_goto_type_definition"); |
360 | let position = params.text_document_position_params.try_conv_with(&world)?; | 363 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
361 | let nav_info = match world.analysis().goto_type_definition(position)? { | 364 | let nav_info = match world.analysis().goto_type_definition(position)? { |
362 | None => return Ok(None), | 365 | None => return Ok(None), |
363 | Some(it) => it, | 366 | Some(it) => it, |
364 | }; | 367 | }; |
365 | let res = (position.file_id, nav_info).try_conv_with(&world)?; | 368 | let res = to_proto::goto_definition_response( |
369 | &world, | ||
370 | FileRange { file_id: position.file_id, range: nav_info.range }, | ||
371 | nav_info.info, | ||
372 | )?; | ||
366 | Ok(Some(res)) | 373 | Ok(Some(res)) |
367 | } | 374 | } |
368 | 375 | ||
@@ -371,8 +378,13 @@ pub fn handle_parent_module( | |||
371 | params: req::TextDocumentPositionParams, | 378 | params: req::TextDocumentPositionParams, |
372 | ) -> Result<Vec<Location>> { | 379 | ) -> Result<Vec<Location>> { |
373 | let _p = profile("handle_parent_module"); | 380 | let _p = profile("handle_parent_module"); |
374 | let position = params.try_conv_with(&world)?; | 381 | let position = from_proto::file_position(&world, params)?; |
375 | world.analysis().parent_module(position)?.iter().try_conv_with_to_vec(&world) | 382 | world |
383 | .analysis() | ||
384 | .parent_module(position)? | ||
385 | .into_iter() | ||
386 | .map(|it| to_proto::location(&world, it.file_range())) | ||
387 | .collect::<Result<Vec<_>>>() | ||
376 | } | 388 | } |
377 | 389 | ||
378 | pub fn handle_runnables( | 390 | pub fn handle_runnables( |
@@ -380,9 +392,9 @@ pub fn handle_runnables( | |||
380 | params: req::RunnablesParams, | 392 | params: req::RunnablesParams, |
381 | ) -> Result<Vec<req::Runnable>> { | 393 | ) -> Result<Vec<req::Runnable>> { |
382 | let _p = profile("handle_runnables"); | 394 | let _p = profile("handle_runnables"); |
383 | let file_id = params.text_document.try_conv_with(&world)?; | 395 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
384 | let line_index = world.analysis().file_line_index(file_id)?; | 396 | let line_index = world.analysis().file_line_index(file_id)?; |
385 | let offset = params.position.map(|it| it.conv_with(&line_index)); | 397 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
386 | let mut res = Vec::new(); | 398 | let mut res = Vec::new(); |
387 | let workspace_root = world.workspace_root_for(file_id); | 399 | let workspace_root = world.workspace_root_for(file_id); |
388 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; | 400 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; |
@@ -439,7 +451,7 @@ pub fn handle_completion( | |||
439 | params: req::CompletionParams, | 451 | params: req::CompletionParams, |
440 | ) -> Result<Option<req::CompletionResponse>> { | 452 | ) -> Result<Option<req::CompletionResponse>> { |
441 | let _p = profile("handle_completion"); | 453 | let _p = profile("handle_completion"); |
442 | let position = params.text_document_position.try_conv_with(&world)?; | 454 | let position = from_proto::file_position(&world, params.text_document_position)?; |
443 | let completion_triggered_after_single_colon = { | 455 | let completion_triggered_after_single_colon = { |
444 | let mut res = false; | 456 | let mut res = false; |
445 | if let Some(ctx) = params.context { | 457 | if let Some(ctx) = params.context { |
@@ -468,8 +480,10 @@ pub fn handle_completion( | |||
468 | }; | 480 | }; |
469 | let line_index = world.analysis().file_line_index(position.file_id)?; | 481 | let line_index = world.analysis().file_line_index(position.file_id)?; |
470 | let line_endings = world.file_line_endings(position.file_id); | 482 | let line_endings = world.file_line_endings(position.file_id); |
471 | let items: Vec<CompletionItem> = | 483 | let items: Vec<CompletionItem> = items |
472 | items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); | 484 | .into_iter() |
485 | .map(|item| to_proto::completion_item(&line_index, line_endings, item)) | ||
486 | .collect(); | ||
473 | 487 | ||
474 | Ok(Some(items.into())) | 488 | Ok(Some(items.into())) |
475 | } | 489 | } |
@@ -479,17 +493,16 @@ pub fn handle_folding_range( | |||
479 | params: FoldingRangeParams, | 493 | params: FoldingRangeParams, |
480 | ) -> Result<Option<Vec<FoldingRange>>> { | 494 | ) -> Result<Option<Vec<FoldingRange>>> { |
481 | let _p = profile("handle_folding_range"); | 495 | let _p = profile("handle_folding_range"); |
482 | let file_id = params.text_document.try_conv_with(&world)?; | 496 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
483 | let folds = world.analysis().folding_ranges(file_id)?; | 497 | let folds = world.analysis().folding_ranges(file_id)?; |
484 | let text = world.analysis().file_text(file_id)?; | 498 | let text = world.analysis().file_text(file_id)?; |
485 | let line_index = world.analysis().file_line_index(file_id)?; | 499 | let line_index = world.analysis().file_line_index(file_id)?; |
486 | let ctx = FoldConvCtx { | 500 | let line_folding_only = world.config.client_caps.line_folding_only; |
487 | text: &text, | 501 | let res = folds |
488 | line_index: &line_index, | 502 | .into_iter() |
489 | line_folding_only: world.config.client_caps.line_folding_only, | 503 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) |
490 | }; | 504 | .collect(); |
491 | let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); | 505 | Ok(Some(res)) |
492 | Ok(res) | ||
493 | } | 506 | } |
494 | 507 | ||
495 | pub fn handle_signature_help( | 508 | pub fn handle_signature_help( |
@@ -497,34 +510,34 @@ pub fn handle_signature_help( | |||
497 | params: req::SignatureHelpParams, | 510 | params: req::SignatureHelpParams, |
498 | ) -> Result<Option<req::SignatureHelp>> { | 511 | ) -> Result<Option<req::SignatureHelp>> { |
499 | let _p = profile("handle_signature_help"); | 512 | let _p = profile("handle_signature_help"); |
500 | let position = params.text_document_position_params.try_conv_with(&world)?; | 513 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
501 | if let Some(call_info) = world.analysis().call_info(position)? { | 514 | let call_info = match world.analysis().call_info(position)? { |
502 | let concise = !world.config.call_info_full; | 515 | None => return Ok(None), |
503 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); | 516 | Some(it) => it, |
504 | if concise && call_info.signature.has_self_param { | 517 | }; |
505 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | 518 | let concise = !world.config.call_info_full; |
506 | } | 519 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); |
507 | let sig_info = call_info.signature.conv_with(concise); | 520 | if concise && call_info.signature.has_self_param { |
508 | 521 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | |
509 | Ok(Some(req::SignatureHelp { | ||
510 | signatures: vec![sig_info], | ||
511 | active_signature: Some(0), | ||
512 | active_parameter, | ||
513 | })) | ||
514 | } else { | ||
515 | Ok(None) | ||
516 | } | 522 | } |
523 | let sig_info = to_proto::signature_information(call_info.signature, concise); | ||
524 | |||
525 | Ok(Some(req::SignatureHelp { | ||
526 | signatures: vec![sig_info], | ||
527 | active_signature: Some(0), | ||
528 | active_parameter, | ||
529 | })) | ||
517 | } | 530 | } |
518 | 531 | ||
519 | pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> { | 532 | pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> { |
520 | let _p = profile("handle_hover"); | 533 | let _p = profile("handle_hover"); |
521 | let position = params.text_document_position_params.try_conv_with(&world)?; | 534 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
522 | let info = match world.analysis().hover(position)? { | 535 | let info = match world.analysis().hover(position)? { |
523 | None => return Ok(None), | 536 | None => return Ok(None), |
524 | Some(info) => info, | 537 | Some(info) => info, |
525 | }; | 538 | }; |
526 | let line_index = world.analysis.file_line_index(position.file_id)?; | 539 | let line_index = world.analysis.file_line_index(position.file_id)?; |
527 | let range = info.range.conv_with(&line_index); | 540 | let range = to_proto::range(&line_index, info.range); |
528 | let res = Hover { | 541 | let res = Hover { |
529 | contents: HoverContents::Markup(MarkupContent { | 542 | contents: HoverContents::Markup(MarkupContent { |
530 | kind: MarkupKind::Markdown, | 543 | kind: MarkupKind::Markdown, |
@@ -540,7 +553,7 @@ pub fn handle_prepare_rename( | |||
540 | params: req::TextDocumentPositionParams, | 553 | params: req::TextDocumentPositionParams, |
541 | ) -> Result<Option<PrepareRenameResponse>> { | 554 | ) -> Result<Option<PrepareRenameResponse>> { |
542 | let _p = profile("handle_prepare_rename"); | 555 | let _p = profile("handle_prepare_rename"); |
543 | let position = params.try_conv_with(&world)?; | 556 | let position = from_proto::file_position(&world, params)?; |
544 | 557 | ||
545 | let optional_change = world.analysis().rename(position, "dummy")?; | 558 | let optional_change = world.analysis().rename(position, "dummy")?; |
546 | let range = match optional_change { | 559 | let range = match optional_change { |
@@ -548,15 +561,14 @@ pub fn handle_prepare_rename( | |||
548 | Some(it) => it.range, | 561 | Some(it) => it.range, |
549 | }; | 562 | }; |
550 | 563 | ||
551 | let file_id = params.text_document.try_conv_with(&world)?; | 564 | let line_index = world.analysis().file_line_index(position.file_id)?; |
552 | let line_index = world.analysis().file_line_index(file_id)?; | 565 | let range = to_proto::range(&line_index, range); |
553 | let range = range.conv_with(&line_index); | ||
554 | Ok(Some(PrepareRenameResponse::Range(range))) | 566 | Ok(Some(PrepareRenameResponse::Range(range))) |
555 | } | 567 | } |
556 | 568 | ||
557 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { | 569 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { |
558 | let _p = profile("handle_rename"); | 570 | let _p = profile("handle_rename"); |
559 | let position = params.text_document_position.try_conv_with(&world)?; | 571 | let position = from_proto::file_position(&world, params.text_document_position)?; |
560 | 572 | ||
561 | if params.new_name.is_empty() { | 573 | if params.new_name.is_empty() { |
562 | return Err(LspError::new( | 574 | return Err(LspError::new( |
@@ -567,14 +579,13 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio | |||
567 | } | 579 | } |
568 | 580 | ||
569 | let optional_change = world.analysis().rename(position, &*params.new_name)?; | 581 | let optional_change = world.analysis().rename(position, &*params.new_name)?; |
570 | let change = match optional_change { | 582 | let source_change = match optional_change { |
571 | None => return Ok(None), | 583 | None => return Ok(None), |
572 | Some(it) => it.info, | 584 | Some(it) => it.info, |
573 | }; | 585 | }; |
574 | 586 | ||
575 | let source_change_req = change.try_conv_with(&world)?; | 587 | let source_change = to_proto::source_change(&world, source_change)?; |
576 | 588 | Ok(Some(source_change.workspace_edit)) | |
577 | Ok(Some(source_change_req.workspace_edit)) | ||
578 | } | 589 | } |
579 | 590 | ||
580 | pub fn handle_references( | 591 | pub fn handle_references( |
@@ -582,7 +593,7 @@ pub fn handle_references( | |||
582 | params: req::ReferenceParams, | 593 | params: req::ReferenceParams, |
583 | ) -> Result<Option<Vec<Location>>> { | 594 | ) -> Result<Option<Vec<Location>>> { |
584 | let _p = profile("handle_references"); | 595 | let _p = profile("handle_references"); |
585 | let position = params.text_document_position.try_conv_with(&world)?; | 596 | let position = from_proto::file_position(&world, params.text_document_position)?; |
586 | 597 | ||
587 | let refs = match world.analysis().find_all_refs(position, None)? { | 598 | let refs = match world.analysis().find_all_refs(position, None)? { |
588 | None => return Ok(None), | 599 | None => return Ok(None), |
@@ -591,33 +602,13 @@ pub fn handle_references( | |||
591 | 602 | ||
592 | let locations = if params.context.include_declaration { | 603 | let locations = if params.context.include_declaration { |
593 | refs.into_iter() | 604 | refs.into_iter() |
594 | .filter_map(|reference| { | 605 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) |
595 | let line_index = | ||
596 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; | ||
597 | to_location( | ||
598 | reference.file_range.file_id, | ||
599 | reference.file_range.range, | ||
600 | &world, | ||
601 | &line_index, | ||
602 | ) | ||
603 | .ok() | ||
604 | }) | ||
605 | .collect() | 606 | .collect() |
606 | } else { | 607 | } else { |
607 | // Only iterate over the references if include_declaration was false | 608 | // Only iterate over the references if include_declaration was false |
608 | refs.references() | 609 | refs.references() |
609 | .iter() | 610 | .iter() |
610 | .filter_map(|reference| { | 611 | .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) |
611 | let line_index = | ||
612 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; | ||
613 | to_location( | ||
614 | reference.file_range.file_id, | ||
615 | reference.file_range.range, | ||
616 | &world, | ||
617 | &line_index, | ||
618 | ) | ||
619 | .ok() | ||
620 | }) | ||
621 | .collect() | 612 | .collect() |
622 | }; | 613 | }; |
623 | 614 | ||
@@ -629,12 +620,12 @@ pub fn handle_formatting( | |||
629 | params: DocumentFormattingParams, | 620 | params: DocumentFormattingParams, |
630 | ) -> Result<Option<Vec<TextEdit>>> { | 621 | ) -> Result<Option<Vec<TextEdit>>> { |
631 | let _p = profile("handle_formatting"); | 622 | let _p = profile("handle_formatting"); |
632 | let file_id = params.text_document.try_conv_with(&world)?; | 623 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
633 | let file = world.analysis().file_text(file_id)?; | 624 | let file = world.analysis().file_text(file_id)?; |
634 | let crate_ids = world.analysis().crate_for(file_id)?; | 625 | let crate_ids = world.analysis().crate_for(file_id)?; |
635 | 626 | ||
636 | let file_line_index = world.analysis().file_line_index(file_id)?; | 627 | let file_line_index = world.analysis().file_line_index(file_id)?; |
637 | let end_position = TextSize::of(file.as_str()).conv_with(&file_line_index); | 628 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); |
638 | 629 | ||
639 | let mut rustfmt = match &world.config.rustfmt { | 630 | let mut rustfmt = match &world.config.rustfmt { |
640 | RustfmtConfig::Rustfmt { extra_args } => { | 631 | RustfmtConfig::Rustfmt { extra_args } => { |
@@ -700,33 +691,14 @@ pub fn handle_formatting( | |||
700 | }])) | 691 | }])) |
701 | } | 692 | } |
702 | 693 | ||
703 | fn create_single_code_action(assist: Assist, world: &WorldSnapshot) -> Result<CodeAction> { | ||
704 | let arg = to_value(assist.source_change.try_conv_with(world)?)?; | ||
705 | let title = assist.label; | ||
706 | let command = Command { | ||
707 | title: title.clone(), | ||
708 | command: "rust-analyzer.applySourceChange".to_string(), | ||
709 | arguments: Some(vec![arg]), | ||
710 | }; | ||
711 | |||
712 | Ok(CodeAction { | ||
713 | title, | ||
714 | kind: Some(String::new()), | ||
715 | diagnostics: None, | ||
716 | edit: None, | ||
717 | command: Some(command), | ||
718 | is_preferred: None, | ||
719 | }) | ||
720 | } | ||
721 | |||
722 | pub fn handle_code_action( | 694 | pub fn handle_code_action( |
723 | world: WorldSnapshot, | 695 | world: WorldSnapshot, |
724 | params: req::CodeActionParams, | 696 | params: req::CodeActionParams, |
725 | ) -> Result<Option<CodeActionResponse>> { | 697 | ) -> Result<Option<CodeActionResponse>> { |
726 | let _p = profile("handle_code_action"); | 698 | let _p = profile("handle_code_action"); |
727 | let file_id = params.text_document.try_conv_with(&world)?; | 699 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
728 | let line_index = world.analysis().file_line_index(file_id)?; | 700 | let line_index = world.analysis().file_line_index(file_id)?; |
729 | let range = params.range.conv_with(&line_index); | 701 | let range = from_proto::text_range(&line_index, params.range); |
730 | 702 | ||
731 | let diagnostics = world.analysis().diagnostics(file_id)?; | 703 | let diagnostics = world.analysis().diagnostics(file_id)?; |
732 | let mut res = CodeActionResponse::default(); | 704 | let mut res = CodeActionResponse::default(); |
@@ -739,7 +711,7 @@ pub fn handle_code_action( | |||
739 | 711 | ||
740 | for source_edit in fixes_from_diagnostics { | 712 | for source_edit in fixes_from_diagnostics { |
741 | let title = source_edit.label.clone(); | 713 | let title = source_edit.label.clone(); |
742 | let edit = source_edit.try_conv_with(&world)?; | 714 | let edit = to_proto::source_change(&world, source_edit)?; |
743 | 715 | ||
744 | let command = Command { | 716 | let command = Command { |
745 | title, | 717 | title, |
@@ -758,7 +730,7 @@ pub fn handle_code_action( | |||
758 | } | 730 | } |
759 | 731 | ||
760 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { | 732 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { |
761 | let fix_range = fix.range.conv_with(&line_index); | 733 | let fix_range = from_proto::text_range(&line_index, fix.range); |
762 | if fix_range.intersect(range).is_none() { | 734 | if fix_range.intersect(range).is_none() { |
763 | continue; | 735 | continue; |
764 | } | 736 | } |
@@ -779,21 +751,21 @@ pub fn handle_code_action( | |||
779 | .1 | 751 | .1 |
780 | .push(assist), | 752 | .push(assist), |
781 | None => { | 753 | None => { |
782 | res.push(create_single_code_action(assist, &world)?.into()); | 754 | res.push(to_proto::code_action(&world, assist)?.into()); |
783 | } | 755 | } |
784 | } | 756 | } |
785 | } | 757 | } |
786 | 758 | ||
787 | for (group_label, (idx, assists)) in grouped_assists { | 759 | for (group_label, (idx, assists)) in grouped_assists { |
788 | if assists.len() == 1 { | 760 | if assists.len() == 1 { |
789 | res[idx] = | 761 | res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into(); |
790 | create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into(); | ||
791 | } else { | 762 | } else { |
792 | let title = group_label; | 763 | let title = group_label; |
793 | 764 | ||
794 | let mut arguments = Vec::with_capacity(assists.len()); | 765 | let mut arguments = Vec::with_capacity(assists.len()); |
795 | for assist in assists { | 766 | for assist in assists { |
796 | arguments.push(to_value(assist.source_change.try_conv_with(&world)?)?); | 767 | let source_change = to_proto::source_change(&world, assist.source_change)?; |
768 | arguments.push(to_value(source_change)?); | ||
797 | } | 769 | } |
798 | 770 | ||
799 | let command = Some(Command { | 771 | let command = Some(Command { |
@@ -838,7 +810,7 @@ pub fn handle_code_lens( | |||
838 | params: req::CodeLensParams, | 810 | params: req::CodeLensParams, |
839 | ) -> Result<Option<Vec<CodeLens>>> { | 811 | ) -> Result<Option<Vec<CodeLens>>> { |
840 | let _p = profile("handle_code_lens"); | 812 | let _p = profile("handle_code_lens"); |
841 | let file_id = params.text_document.try_conv_with(&world)?; | 813 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
842 | let line_index = world.analysis().file_line_index(file_id)?; | 814 | let line_index = world.analysis().file_line_index(file_id)?; |
843 | 815 | ||
844 | let mut lenses: Vec<CodeLens> = Default::default(); | 816 | let mut lenses: Vec<CodeLens> = Default::default(); |
@@ -902,7 +874,7 @@ pub fn handle_code_lens( | |||
902 | _ => false, | 874 | _ => false, |
903 | }) | 875 | }) |
904 | .map(|it| { | 876 | .map(|it| { |
905 | let range = it.node_range.conv_with(&line_index); | 877 | let range = to_proto::range(&line_index, it.node_range); |
906 | let pos = range.start; | 878 | let pos = range.start; |
907 | let lens_params = req::GotoImplementationParams { | 879 | let lens_params = req::GotoImplementationParams { |
908 | text_document_position_params: req::TextDocumentPositionParams::new( | 880 | text_document_position_params: req::TextDocumentPositionParams::new( |
@@ -979,34 +951,33 @@ pub fn handle_document_highlight( | |||
979 | params: req::DocumentHighlightParams, | 951 | params: req::DocumentHighlightParams, |
980 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 952 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
981 | let _p = profile("handle_document_highlight"); | 953 | let _p = profile("handle_document_highlight"); |
982 | let file_id = params.text_document_position_params.text_document.try_conv_with(&world)?; | 954 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
983 | let line_index = world.analysis().file_line_index(file_id)?; | 955 | let line_index = world.analysis().file_line_index(position.file_id)?; |
984 | 956 | ||
985 | let refs = match world.analysis().find_all_refs( | 957 | let refs = match world |
986 | params.text_document_position_params.try_conv_with(&world)?, | 958 | .analysis() |
987 | Some(SearchScope::single_file(file_id)), | 959 | .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? |
988 | )? { | 960 | { |
989 | None => return Ok(None), | 961 | None => return Ok(None), |
990 | Some(refs) => refs, | 962 | Some(refs) => refs, |
991 | }; | 963 | }; |
992 | 964 | ||
993 | Ok(Some( | 965 | let res = refs |
994 | refs.into_iter() | 966 | .into_iter() |
995 | .filter(|reference| reference.file_range.file_id == file_id) | 967 | .filter(|reference| reference.file_range.file_id == position.file_id) |
996 | .map(|reference| DocumentHighlight { | 968 | .map(|reference| DocumentHighlight { |
997 | range: reference.file_range.range.conv_with(&line_index), | 969 | range: to_proto::range(&line_index, reference.file_range.range), |
998 | kind: reference.access.map(|it| it.conv()), | 970 | kind: reference.access.map(to_proto::document_highlight_kind), |
999 | }) | 971 | }) |
1000 | .collect(), | 972 | .collect(); |
1001 | )) | 973 | Ok(Some(res)) |
1002 | } | 974 | } |
1003 | 975 | ||
1004 | pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { | 976 | pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { |
1005 | let _p = profile("handle_ssr"); | 977 | let _p = profile("handle_ssr"); |
1006 | world | 978 | let source_change = |
1007 | .analysis() | 979 | world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; |
1008 | .structural_search_replace(¶ms.query, params.parse_only)?? | 980 | to_proto::source_change(&world, source_change) |
1009 | .try_conv_with(&world) | ||
1010 | } | 981 | } |
1011 | 982 | ||
1012 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 983 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { |
@@ -1017,8 +988,8 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia | |||
1017 | .diagnostics(file_id)? | 988 | .diagnostics(file_id)? |
1018 | .into_iter() | 989 | .into_iter() |
1019 | .map(|d| Diagnostic { | 990 | .map(|d| Diagnostic { |
1020 | range: d.range.conv_with(&line_index), | 991 | range: to_proto::range(&line_index, d.range), |
1021 | severity: Some(d.severity.conv()), | 992 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1022 | code: None, | 993 | code: None, |
1023 | source: Some("rust-analyzer".to_string()), | 994 | source: Some("rust-analyzer".to_string()), |
1024 | message: d.message, | 995 | message: d.message, |
@@ -1045,7 +1016,7 @@ fn to_lsp_runnable( | |||
1045 | RunnableKind::Bin => "run binary".to_string(), | 1016 | RunnableKind::Bin => "run binary".to_string(), |
1046 | }; | 1017 | }; |
1047 | Ok(req::Runnable { | 1018 | Ok(req::Runnable { |
1048 | range: runnable.range.conv_with(&line_index), | 1019 | range: to_proto::range(&line_index, runnable.range), |
1049 | label, | 1020 | label, |
1050 | bin: "cargo".to_string(), | 1021 | bin: "cargo".to_string(), |
1051 | args, | 1022 | args, |
@@ -1064,13 +1035,13 @@ pub fn handle_inlay_hints( | |||
1064 | params: InlayHintsParams, | 1035 | params: InlayHintsParams, |
1065 | ) -> Result<Vec<InlayHint>> { | 1036 | ) -> Result<Vec<InlayHint>> { |
1066 | let _p = profile("handle_inlay_hints"); | 1037 | let _p = profile("handle_inlay_hints"); |
1067 | let file_id = params.text_document.try_conv_with(&world)?; | 1038 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
1068 | let analysis = world.analysis(); | 1039 | let analysis = world.analysis(); |
1069 | let line_index = analysis.file_line_index(file_id)?; | 1040 | let line_index = analysis.file_line_index(file_id)?; |
1070 | Ok(analysis | 1041 | Ok(analysis |
1071 | .inlay_hints(file_id, &world.config.inlay_hints)? | 1042 | .inlay_hints(file_id, &world.config.inlay_hints)? |
1072 | .into_iter() | 1043 | .into_iter() |
1073 | .map_conv_with(&line_index) | 1044 | .map(|it| to_proto::inlay_int(&line_index, it)) |
1074 | .collect()) | 1045 | .collect()) |
1075 | } | 1046 | } |
1076 | 1047 | ||
@@ -1079,21 +1050,19 @@ pub fn handle_call_hierarchy_prepare( | |||
1079 | params: CallHierarchyPrepareParams, | 1050 | params: CallHierarchyPrepareParams, |
1080 | ) -> Result<Option<Vec<CallHierarchyItem>>> { | 1051 | ) -> Result<Option<Vec<CallHierarchyItem>>> { |
1081 | let _p = profile("handle_call_hierarchy_prepare"); | 1052 | let _p = profile("handle_call_hierarchy_prepare"); |
1082 | let position = params.text_document_position_params.try_conv_with(&world)?; | 1053 | let position = from_proto::file_position(&world, params.text_document_position_params)?; |
1083 | let file_id = position.file_id; | ||
1084 | 1054 | ||
1085 | let nav_info = match world.analysis().call_hierarchy(position)? { | 1055 | let nav_info = match world.analysis().call_hierarchy(position)? { |
1086 | None => return Ok(None), | 1056 | None => return Ok(None), |
1087 | Some(it) => it, | 1057 | Some(it) => it, |
1088 | }; | 1058 | }; |
1089 | 1059 | ||
1090 | let line_index = world.analysis().file_line_index(file_id)?; | 1060 | let RangeInfo { range: _, info: navs } = nav_info; |
1091 | let RangeInfo { range, info: navs } = nav_info; | ||
1092 | let res = navs | 1061 | let res = navs |
1093 | .into_iter() | 1062 | .into_iter() |
1094 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) | 1063 | .filter(|it| it.kind() == SyntaxKind::FN_DEF) |
1095 | .filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok()) | 1064 | .map(|it| to_proto::call_hierarchy_item(&world, it)) |
1096 | .collect(); | 1065 | .collect::<Result<Vec<_>>>()?; |
1097 | 1066 | ||
1098 | Ok(Some(res)) | 1067 | Ok(Some(res)) |
1099 | } | 1068 | } |
@@ -1106,7 +1075,7 @@ pub fn handle_call_hierarchy_incoming( | |||
1106 | let item = params.item; | 1075 | let item = params.item; |
1107 | 1076 | ||
1108 | let doc = TextDocumentIdentifier::new(item.uri); | 1077 | let doc = TextDocumentIdentifier::new(item.uri); |
1109 | let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; | 1078 | let frange = from_proto::file_range(&world, doc, item.range)?; |
1110 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1079 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1111 | 1080 | ||
1112 | let call_items = match world.analysis().incoming_calls(fpos)? { | 1081 | let call_items = match world.analysis().incoming_calls(fpos)? { |
@@ -1119,11 +1088,14 @@ pub fn handle_call_hierarchy_incoming( | |||
1119 | for call_item in call_items.into_iter() { | 1088 | for call_item in call_items.into_iter() { |
1120 | let file_id = call_item.target.file_id(); | 1089 | let file_id = call_item.target.file_id(); |
1121 | let line_index = world.analysis().file_line_index(file_id)?; | 1090 | let line_index = world.analysis().file_line_index(file_id)?; |
1122 | let range = call_item.target.range(); | 1091 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; |
1123 | let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; | ||
1124 | res.push(CallHierarchyIncomingCall { | 1092 | res.push(CallHierarchyIncomingCall { |
1125 | from: item, | 1093 | from: item, |
1126 | from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), | 1094 | from_ranges: call_item |
1095 | .ranges | ||
1096 | .into_iter() | ||
1097 | .map(|it| to_proto::range(&line_index, it)) | ||
1098 | .collect(), | ||
1127 | }); | 1099 | }); |
1128 | } | 1100 | } |
1129 | 1101 | ||
@@ -1138,7 +1110,7 @@ pub fn handle_call_hierarchy_outgoing( | |||
1138 | let item = params.item; | 1110 | let item = params.item; |
1139 | 1111 | ||
1140 | let doc = TextDocumentIdentifier::new(item.uri); | 1112 | let doc = TextDocumentIdentifier::new(item.uri); |
1141 | let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; | 1113 | let frange = from_proto::file_range(&world, doc, item.range)?; |
1142 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 1114 | let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
1143 | 1115 | ||
1144 | let call_items = match world.analysis().outgoing_calls(fpos)? { | 1116 | let call_items = match world.analysis().outgoing_calls(fpos)? { |
@@ -1151,11 +1123,14 @@ pub fn handle_call_hierarchy_outgoing( | |||
1151 | for call_item in call_items.into_iter() { | 1123 | for call_item in call_items.into_iter() { |
1152 | let file_id = call_item.target.file_id(); | 1124 | let file_id = call_item.target.file_id(); |
1153 | let line_index = world.analysis().file_line_index(file_id)?; | 1125 | let line_index = world.analysis().file_line_index(file_id)?; |
1154 | let range = call_item.target.range(); | 1126 | let item = to_proto::call_hierarchy_item(&world, call_item.target)?; |
1155 | let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; | ||
1156 | res.push(CallHierarchyOutgoingCall { | 1127 | res.push(CallHierarchyOutgoingCall { |
1157 | to: item, | 1128 | to: item, |
1158 | from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), | 1129 | from_ranges: call_item |
1130 | .ranges | ||
1131 | .into_iter() | ||
1132 | .map(|it| to_proto::range(&line_index, it)) | ||
1133 | .collect(), | ||
1159 | }); | 1134 | }); |
1160 | } | 1135 | } |
1161 | 1136 | ||
@@ -1168,19 +1143,20 @@ pub fn handle_semantic_tokens( | |||
1168 | ) -> Result<Option<SemanticTokensResult>> { | 1143 | ) -> Result<Option<SemanticTokensResult>> { |
1169 | let _p = profile("handle_semantic_tokens"); | 1144 | let _p = profile("handle_semantic_tokens"); |
1170 | 1145 | ||
1171 | let file_id = params.text_document.try_conv_with(&world)?; | 1146 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
1172 | let text = world.analysis().file_text(file_id)?; | 1147 | let text = world.analysis().file_text(file_id)?; |
1173 | let line_index = world.analysis().file_line_index(file_id)?; | 1148 | let line_index = world.analysis().file_line_index(file_id)?; |
1174 | 1149 | ||
1175 | let mut builder = SemanticTokensBuilder::default(); | 1150 | let mut builder = SemanticTokensBuilder::default(); |
1176 | 1151 | ||
1177 | for highlight_range in world.analysis().highlight(file_id)?.into_iter() { | 1152 | for highlight_range in world.analysis().highlight(file_id)?.into_iter() { |
1178 | let (token_index, modifier_bitset) = highlight_range.highlight.conv(); | 1153 | let (token_index, modifier_bitset) = |
1154 | to_proto::token_type_index_modifiers_bitself(highlight_range.highlight); | ||
1179 | for mut range in line_index.lines(highlight_range.range) { | 1155 | for mut range in line_index.lines(highlight_range.range) { |
1180 | if text[range].ends_with('\n') { | 1156 | if text[range].ends_with('\n') { |
1181 | range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); | 1157 | range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); |
1182 | } | 1158 | } |
1183 | let range = range.conv_with(&line_index); | 1159 | let range = to_proto::range(&line_index, range); |
1184 | builder.push(range, token_index, modifier_bitset); | 1160 | builder.push(range, token_index, modifier_bitset); |
1185 | } | 1161 | } |
1186 | } | 1162 | } |
@@ -1196,14 +1172,16 @@ pub fn handle_semantic_tokens_range( | |||
1196 | ) -> Result<Option<SemanticTokensRangeResult>> { | 1172 | ) -> Result<Option<SemanticTokensRangeResult>> { |
1197 | let _p = profile("handle_semantic_tokens_range"); | 1173 | let _p = profile("handle_semantic_tokens_range"); |
1198 | 1174 | ||
1199 | let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; | 1175 | let frange = from_proto::file_range(&world, params.text_document, params.range)?; |
1200 | let line_index = world.analysis().file_line_index(frange.file_id)?; | 1176 | let line_index = world.analysis().file_line_index(frange.file_id)?; |
1201 | 1177 | ||
1202 | let mut builder = SemanticTokensBuilder::default(); | 1178 | let mut builder = SemanticTokensBuilder::default(); |
1203 | 1179 | ||
1204 | for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { | 1180 | for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { |
1205 | let (token_type, token_modifiers) = highlight_range.highlight.conv(); | 1181 | let (token_type, token_modifiers) = |
1206 | builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); | 1182 | to_proto::token_type_index_modifiers_bitself(highlight_range.highlight); |
1183 | let range = to_proto::range(&line_index, highlight_range.range); | ||
1184 | builder.push(range, token_type, token_modifiers); | ||
1207 | } | 1185 | } |
1208 | 1186 | ||
1209 | let tokens = builder.build(); | 1187 | let tokens = builder.build(); |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs new file mode 100644 index 000000000..39f2e6d4d --- /dev/null +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -0,0 +1,566 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | ||
2 | use ra_db::{FileId, FileRange}; | ||
3 | use ra_ide::{ | ||
4 | translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, | ||
5 | FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, | ||
6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, | ||
7 | SourceChange, SourceFileEdit, | ||
8 | }; | ||
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | ||
10 | use ra_text_edit::{Indel, TextEdit}; | ||
11 | use ra_vfs::LineEndings; | ||
12 | |||
13 | use crate::{req, semantic_tokens, world::WorldSnapshot, Result}; | ||
14 | |||
15 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | ||
16 | let line_col = line_index.line_col(offset); | ||
17 | let line = u64::from(line_col.line); | ||
18 | let character = u64::from(line_col.col_utf16); | ||
19 | lsp_types::Position::new(line, character) | ||
20 | } | ||
21 | |||
22 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { | ||
23 | let start = position(line_index, range.start()); | ||
24 | let end = position(line_index, range.end()); | ||
25 | lsp_types::Range::new(start, end) | ||
26 | } | ||
27 | |||
28 | pub(crate) fn symbol_kind(syntax_kind: SyntaxKind) -> lsp_types::SymbolKind { | ||
29 | match syntax_kind { | ||
30 | SyntaxKind::FN_DEF => lsp_types::SymbolKind::Function, | ||
31 | SyntaxKind::STRUCT_DEF => lsp_types::SymbolKind::Struct, | ||
32 | SyntaxKind::ENUM_DEF => lsp_types::SymbolKind::Enum, | ||
33 | SyntaxKind::ENUM_VARIANT => lsp_types::SymbolKind::EnumMember, | ||
34 | SyntaxKind::TRAIT_DEF => lsp_types::SymbolKind::Interface, | ||
35 | SyntaxKind::MACRO_CALL => lsp_types::SymbolKind::Function, | ||
36 | SyntaxKind::MODULE => lsp_types::SymbolKind::Module, | ||
37 | SyntaxKind::TYPE_ALIAS_DEF => lsp_types::SymbolKind::TypeParameter, | ||
38 | SyntaxKind::RECORD_FIELD_DEF => lsp_types::SymbolKind::Field, | ||
39 | SyntaxKind::STATIC_DEF => lsp_types::SymbolKind::Constant, | ||
40 | SyntaxKind::CONST_DEF => lsp_types::SymbolKind::Constant, | ||
41 | SyntaxKind::IMPL_DEF => lsp_types::SymbolKind::Object, | ||
42 | _ => lsp_types::SymbolKind::Variable, | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub(crate) fn document_highlight_kind( | ||
47 | reference_access: ReferenceAccess, | ||
48 | ) -> lsp_types::DocumentHighlightKind { | ||
49 | match reference_access { | ||
50 | ReferenceAccess::Read => lsp_types::DocumentHighlightKind::Read, | ||
51 | ReferenceAccess::Write => lsp_types::DocumentHighlightKind::Write, | ||
52 | } | ||
53 | } | ||
54 | |||
55 | pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity { | ||
56 | match severity { | ||
57 | Severity::Error => lsp_types::DiagnosticSeverity::Error, | ||
58 | Severity::WeakWarning => lsp_types::DiagnosticSeverity::Hint, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation { | ||
63 | let value = crate::markdown::format_docs(documentation.as_str()); | ||
64 | let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }; | ||
65 | lsp_types::Documentation::MarkupContent(markup_content) | ||
66 | } | ||
67 | |||
68 | pub(crate) fn insert_text_format( | ||
69 | insert_text_format: InsertTextFormat, | ||
70 | ) -> lsp_types::InsertTextFormat { | ||
71 | match insert_text_format { | ||
72 | InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, | ||
73 | InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, | ||
74 | } | ||
75 | } | ||
76 | |||
77 | pub(crate) fn completion_item_kind( | ||
78 | completion_item_kind: CompletionItemKind, | ||
79 | ) -> lsp_types::CompletionItemKind { | ||
80 | match completion_item_kind { | ||
81 | CompletionItemKind::Keyword => lsp_types::CompletionItemKind::Keyword, | ||
82 | CompletionItemKind::Snippet => lsp_types::CompletionItemKind::Snippet, | ||
83 | CompletionItemKind::Module => lsp_types::CompletionItemKind::Module, | ||
84 | CompletionItemKind::Function => lsp_types::CompletionItemKind::Function, | ||
85 | CompletionItemKind::Struct => lsp_types::CompletionItemKind::Struct, | ||
86 | CompletionItemKind::Enum => lsp_types::CompletionItemKind::Enum, | ||
87 | CompletionItemKind::EnumVariant => lsp_types::CompletionItemKind::EnumMember, | ||
88 | CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::Struct, | ||
89 | CompletionItemKind::Binding => lsp_types::CompletionItemKind::Variable, | ||
90 | CompletionItemKind::Field => lsp_types::CompletionItemKind::Field, | ||
91 | CompletionItemKind::Trait => lsp_types::CompletionItemKind::Interface, | ||
92 | CompletionItemKind::TypeAlias => lsp_types::CompletionItemKind::Struct, | ||
93 | CompletionItemKind::Const => lsp_types::CompletionItemKind::Constant, | ||
94 | CompletionItemKind::Static => lsp_types::CompletionItemKind::Value, | ||
95 | CompletionItemKind::Method => lsp_types::CompletionItemKind::Method, | ||
96 | CompletionItemKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter, | ||
97 | CompletionItemKind::Macro => lsp_types::CompletionItemKind::Method, | ||
98 | CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember, | ||
99 | } | ||
100 | } | ||
101 | |||
102 | pub(crate) fn text_edit( | ||
103 | line_index: &LineIndex, | ||
104 | line_endings: LineEndings, | ||
105 | indel: Indel, | ||
106 | ) -> lsp_types::TextEdit { | ||
107 | let range = range(line_index, indel.delete); | ||
108 | let new_text = match line_endings { | ||
109 | LineEndings::Unix => indel.insert, | ||
110 | LineEndings::Dos => indel.insert.replace('\n', "\r\n"), | ||
111 | }; | ||
112 | lsp_types::TextEdit { range, new_text } | ||
113 | } | ||
114 | |||
115 | pub(crate) fn text_edit_vec( | ||
116 | line_index: &LineIndex, | ||
117 | line_endings: LineEndings, | ||
118 | text_edit: TextEdit, | ||
119 | ) -> Vec<lsp_types::TextEdit> { | ||
120 | text_edit | ||
121 | .as_indels() | ||
122 | .iter() | ||
123 | .map(|it| self::text_edit(line_index, line_endings, it.clone())) | ||
124 | .collect() | ||
125 | } | ||
126 | |||
127 | pub(crate) fn completion_item( | ||
128 | line_index: &LineIndex, | ||
129 | line_endings: LineEndings, | ||
130 | completion_item: CompletionItem, | ||
131 | ) -> lsp_types::CompletionItem { | ||
132 | let mut additional_text_edits = Vec::new(); | ||
133 | let mut text_edit = None; | ||
134 | // LSP does not allow arbitrary edits in completion, so we have to do a | ||
135 | // non-trivial mapping here. | ||
136 | let source_range = completion_item.source_range(); | ||
137 | for indel in completion_item.text_edit().as_indels() { | ||
138 | if indel.delete.contains_range(source_range) { | ||
139 | text_edit = Some(if indel.delete == source_range { | ||
140 | self::text_edit(line_index, line_endings, indel.clone()) | ||
141 | } else { | ||
142 | assert!(source_range.end() == indel.delete.end()); | ||
143 | let range1 = TextRange::new(indel.delete.start(), source_range.start()); | ||
144 | let range2 = source_range; | ||
145 | let indel1 = Indel::replace(range1, String::new()); | ||
146 | let indel2 = Indel::replace(range2, indel.insert.clone()); | ||
147 | additional_text_edits.push(self::text_edit(line_index, line_endings, indel1)); | ||
148 | self::text_edit(line_index, line_endings, indel2) | ||
149 | }) | ||
150 | } else { | ||
151 | assert!(source_range.intersect(indel.delete).is_none()); | ||
152 | let text_edit = self::text_edit(line_index, line_endings, indel.clone()); | ||
153 | additional_text_edits.push(text_edit); | ||
154 | } | ||
155 | } | ||
156 | let text_edit = text_edit.unwrap(); | ||
157 | |||
158 | let mut res = lsp_types::CompletionItem { | ||
159 | label: completion_item.label().to_string(), | ||
160 | detail: completion_item.detail().map(|it| it.to_string()), | ||
161 | filter_text: Some(completion_item.lookup().to_string()), | ||
162 | kind: completion_item.kind().map(completion_item_kind), | ||
163 | text_edit: Some(text_edit.into()), | ||
164 | additional_text_edits: Some(additional_text_edits), | ||
165 | documentation: completion_item.documentation().map(documentation), | ||
166 | deprecated: Some(completion_item.deprecated()), | ||
167 | command: if completion_item.trigger_call_info() { | ||
168 | let cmd = lsp_types::Command { | ||
169 | title: "triggerParameterHints".into(), | ||
170 | command: "editor.action.triggerParameterHints".into(), | ||
171 | arguments: None, | ||
172 | }; | ||
173 | Some(cmd) | ||
174 | } else { | ||
175 | None | ||
176 | }, | ||
177 | ..Default::default() | ||
178 | }; | ||
179 | |||
180 | if completion_item.score().is_some() { | ||
181 | res.preselect = Some(true) | ||
182 | } | ||
183 | |||
184 | if completion_item.deprecated() { | ||
185 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | ||
186 | } | ||
187 | |||
188 | res.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); | ||
189 | |||
190 | res | ||
191 | } | ||
192 | |||
193 | pub(crate) fn signature_information( | ||
194 | signature: FunctionSignature, | ||
195 | concise: bool, | ||
196 | ) -> lsp_types::SignatureInformation { | ||
197 | let (label, documentation, params) = if concise { | ||
198 | let mut params = signature.parameters; | ||
199 | if signature.has_self_param { | ||
200 | params.remove(0); | ||
201 | } | ||
202 | (params.join(", "), None, params) | ||
203 | } else { | ||
204 | (signature.to_string(), signature.doc.map(documentation), signature.parameters) | ||
205 | }; | ||
206 | |||
207 | let parameters: Vec<lsp_types::ParameterInformation> = params | ||
208 | .into_iter() | ||
209 | .map(|param| lsp_types::ParameterInformation { | ||
210 | label: lsp_types::ParameterLabel::Simple(param), | ||
211 | documentation: None, | ||
212 | }) | ||
213 | .collect(); | ||
214 | |||
215 | lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) } | ||
216 | } | ||
217 | |||
218 | pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> req::InlayHint { | ||
219 | req::InlayHint { | ||
220 | label: inlay_hint.label.to_string(), | ||
221 | range: range(line_index, inlay_hint.range), | ||
222 | kind: match inlay_hint.kind { | ||
223 | InlayKind::ParameterHint => req::InlayKind::ParameterHint, | ||
224 | InlayKind::TypeHint => req::InlayKind::TypeHint, | ||
225 | InlayKind::ChainingHint => req::InlayKind::ChainingHint, | ||
226 | }, | ||
227 | } | ||
228 | } | ||
229 | |||
230 | // TODO: this is wrong | ||
231 | pub(crate) fn token_type_index_modifiers_bitself(highlight: Highlight) -> (u32, u32) { | ||
232 | let mut mods = semantic_tokens::ModifierSet::default(); | ||
233 | let type_ = match highlight.tag { | ||
234 | HighlightTag::Struct => lsp_types::SemanticTokenType::STRUCT, | ||
235 | HighlightTag::Enum => lsp_types::SemanticTokenType::ENUM, | ||
236 | HighlightTag::Union => semantic_tokens::UNION, | ||
237 | HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, | ||
238 | HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, | ||
239 | HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, | ||
240 | HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, | ||
241 | HighlightTag::Field => lsp_types::SemanticTokenType::MEMBER, | ||
242 | HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, | ||
243 | HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE, | ||
244 | HighlightTag::Constant => { | ||
245 | mods |= semantic_tokens::CONSTANT; | ||
246 | mods |= lsp_types::SemanticTokenModifier::STATIC; | ||
247 | lsp_types::SemanticTokenType::VARIABLE | ||
248 | } | ||
249 | HighlightTag::Static => { | ||
250 | mods |= lsp_types::SemanticTokenModifier::STATIC; | ||
251 | lsp_types::SemanticTokenType::VARIABLE | ||
252 | } | ||
253 | HighlightTag::EnumVariant => semantic_tokens::ENUM_MEMBER, | ||
254 | HighlightTag::Macro => lsp_types::SemanticTokenType::MACRO, | ||
255 | HighlightTag::Local => lsp_types::SemanticTokenType::VARIABLE, | ||
256 | HighlightTag::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, | ||
257 | HighlightTag::Lifetime => semantic_tokens::LIFETIME, | ||
258 | HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { | ||
259 | lsp_types::SemanticTokenType::NUMBER | ||
260 | } | ||
261 | HighlightTag::CharLiteral | HighlightTag::StringLiteral => { | ||
262 | lsp_types::SemanticTokenType::STRING | ||
263 | } | ||
264 | HighlightTag::Comment => lsp_types::SemanticTokenType::COMMENT, | ||
265 | HighlightTag::Attribute => semantic_tokens::ATTRIBUTE, | ||
266 | HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | ||
267 | HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | ||
268 | HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | ||
269 | }; | ||
270 | |||
271 | for modifier in highlight.modifiers.iter() { | ||
272 | let modifier = match modifier { | ||
273 | HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, | ||
274 | HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, | ||
275 | HighlightModifier::Mutable => semantic_tokens::MUTABLE, | ||
276 | HighlightModifier::Unsafe => semantic_tokens::UNSAFE, | ||
277 | }; | ||
278 | mods |= modifier; | ||
279 | } | ||
280 | |||
281 | (semantic_tokens::type_index(type_), mods.0) | ||
282 | } | ||
283 | |||
284 | pub(crate) fn folding_range( | ||
285 | text: &str, | ||
286 | line_index: &LineIndex, | ||
287 | line_folding_only: bool, | ||
288 | fold: Fold, | ||
289 | ) -> lsp_types::FoldingRange { | ||
290 | let kind = match fold.kind { | ||
291 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | ||
292 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | ||
293 | FoldKind::Mods | FoldKind::Block => None, | ||
294 | }; | ||
295 | |||
296 | let range = range(line_index, fold.range); | ||
297 | |||
298 | if line_folding_only { | ||
299 | // Clients with line_folding_only == true (such as VSCode) will fold the whole end line | ||
300 | // even if it contains text not in the folding range. To prevent that we exclude | ||
301 | // range.end.line from the folding region if there is more text after range.end | ||
302 | // on the same line. | ||
303 | let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))] | ||
304 | .chars() | ||
305 | .take_while(|it| *it != '\n') | ||
306 | .any(|it| !it.is_whitespace()); | ||
307 | |||
308 | let end_line = if has_more_text_on_end_line { | ||
309 | range.end.line.saturating_sub(1) | ||
310 | } else { | ||
311 | range.end.line | ||
312 | }; | ||
313 | |||
314 | lsp_types::FoldingRange { | ||
315 | start_line: range.start.line, | ||
316 | start_character: None, | ||
317 | end_line, | ||
318 | end_character: None, | ||
319 | kind, | ||
320 | } | ||
321 | } else { | ||
322 | lsp_types::FoldingRange { | ||
323 | start_line: range.start.line, | ||
324 | start_character: Some(range.start.character), | ||
325 | end_line: range.end.line, | ||
326 | end_character: Some(range.end.character), | ||
327 | kind, | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | |||
332 | pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { | ||
333 | world.file_id_to_uri(file_id) | ||
334 | } | ||
335 | |||
336 | pub(crate) fn text_document_identifier( | ||
337 | world: &WorldSnapshot, | ||
338 | file_id: FileId, | ||
339 | ) -> Result<lsp_types::TextDocumentIdentifier> { | ||
340 | let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? }; | ||
341 | Ok(res) | ||
342 | } | ||
343 | |||
344 | pub(crate) fn versioned_text_document_identifier( | ||
345 | world: &WorldSnapshot, | ||
346 | file_id: FileId, | ||
347 | version: Option<i64>, | ||
348 | ) -> Result<lsp_types::VersionedTextDocumentIdentifier> { | ||
349 | let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; | ||
350 | Ok(res) | ||
351 | } | ||
352 | |||
353 | pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { | ||
354 | let url = url(world, frange.file_id)?; | ||
355 | let line_index = world.analysis().file_line_index(frange.file_id)?; | ||
356 | let range = range(&line_index, frange.range); | ||
357 | let loc = lsp_types::Location::new(url, range); | ||
358 | Ok(loc) | ||
359 | } | ||
360 | |||
361 | pub(crate) fn location_link( | ||
362 | world: &WorldSnapshot, | ||
363 | src: FileRange, | ||
364 | target: NavigationTarget, | ||
365 | ) -> Result<lsp_types::LocationLink> { | ||
366 | let src_location = location(world, src)?; | ||
367 | let (target_uri, target_range, target_selection_range) = location_info(world, target)?; | ||
368 | let res = lsp_types::LocationLink { | ||
369 | origin_selection_range: Some(src_location.range), | ||
370 | target_uri, | ||
371 | target_range, | ||
372 | target_selection_range, | ||
373 | }; | ||
374 | Ok(res) | ||
375 | } | ||
376 | |||
377 | fn location_info( | ||
378 | world: &WorldSnapshot, | ||
379 | target: NavigationTarget, | ||
380 | ) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { | ||
381 | let line_index = world.analysis().file_line_index(target.file_id())?; | ||
382 | |||
383 | let target_uri = url(world, target.file_id())?; | ||
384 | let target_range = range(&line_index, target.full_range()); | ||
385 | let target_selection_range = | ||
386 | target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); | ||
387 | Ok((target_uri, target_range, target_selection_range)) | ||
388 | } | ||
389 | |||
390 | pub(crate) fn goto_definition_response( | ||
391 | world: &WorldSnapshot, | ||
392 | src: FileRange, | ||
393 | targets: Vec<NavigationTarget>, | ||
394 | ) -> Result<lsp_types::GotoDefinitionResponse> { | ||
395 | if world.config.client_caps.location_link { | ||
396 | let links = targets | ||
397 | .into_iter() | ||
398 | .map(|nav| location_link(world, src, nav)) | ||
399 | .collect::<Result<Vec<_>>>()?; | ||
400 | Ok(links.into()) | ||
401 | } else { | ||
402 | let locations = targets | ||
403 | .into_iter() | ||
404 | .map(|nav| { | ||
405 | location( | ||
406 | world, | ||
407 | FileRange { | ||
408 | file_id: nav.file_id(), | ||
409 | range: nav.focus_range().unwrap_or(nav.range()), | ||
410 | }, | ||
411 | ) | ||
412 | }) | ||
413 | .collect::<Result<Vec<_>>>()?; | ||
414 | Ok(locations.into()) | ||
415 | } | ||
416 | } | ||
417 | |||
418 | pub(crate) fn text_document_edit( | ||
419 | world: &WorldSnapshot, | ||
420 | source_file_edit: SourceFileEdit, | ||
421 | ) -> Result<lsp_types::TextDocumentEdit> { | ||
422 | let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; | ||
423 | let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; | ||
424 | let line_endings = world.file_line_endings(source_file_edit.file_id); | ||
425 | let edits = source_file_edit | ||
426 | .edit | ||
427 | .as_indels() | ||
428 | .iter() | ||
429 | .map(|it| text_edit(&line_index, line_endings, it.clone())) | ||
430 | .collect(); | ||
431 | Ok(lsp_types::TextDocumentEdit { text_document, edits }) | ||
432 | } | ||
433 | |||
434 | pub(crate) fn resource_op( | ||
435 | world: &WorldSnapshot, | ||
436 | file_system_edit: FileSystemEdit, | ||
437 | ) -> Result<lsp_types::ResourceOp> { | ||
438 | let res = match file_system_edit { | ||
439 | FileSystemEdit::CreateFile { source_root, path } => { | ||
440 | let uri = world.path_to_uri(source_root, &path)?; | ||
441 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) | ||
442 | } | ||
443 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { | ||
444 | let old_uri = world.file_id_to_uri(src)?; | ||
445 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; | ||
446 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) | ||
447 | } | ||
448 | }; | ||
449 | Ok(res) | ||
450 | } | ||
451 | |||
452 | pub(crate) fn source_change( | ||
453 | world: &WorldSnapshot, | ||
454 | source_change: SourceChange, | ||
455 | ) -> Result<req::SourceChange> { | ||
456 | let cursor_position = match source_change.cursor_position { | ||
457 | None => None, | ||
458 | Some(pos) => { | ||
459 | let line_index = world.analysis().file_line_index(pos.file_id)?; | ||
460 | let edit = source_change | ||
461 | .source_file_edits | ||
462 | .iter() | ||
463 | .find(|it| it.file_id == pos.file_id) | ||
464 | .map(|it| &it.edit); | ||
465 | let line_col = match edit { | ||
466 | Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), | ||
467 | None => line_index.line_col(pos.offset), | ||
468 | }; | ||
469 | let position = | ||
470 | lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); | ||
471 | Some(lsp_types::TextDocumentPositionParams { | ||
472 | text_document: text_document_identifier(world, pos.file_id)?, | ||
473 | position, | ||
474 | }) | ||
475 | } | ||
476 | }; | ||
477 | let mut document_changes: Vec<lsp_types::DocumentChangeOperation> = Vec::new(); | ||
478 | for op in source_change.file_system_edits { | ||
479 | let op = resource_op(&world, op)?; | ||
480 | document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); | ||
481 | } | ||
482 | for edit in source_change.source_file_edits { | ||
483 | let edit = text_document_edit(&world, edit)?; | ||
484 | document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); | ||
485 | } | ||
486 | let workspace_edit = lsp_types::WorkspaceEdit { | ||
487 | changes: None, | ||
488 | document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), | ||
489 | }; | ||
490 | Ok(req::SourceChange { label: source_change.label, workspace_edit, cursor_position }) | ||
491 | } | ||
492 | |||
493 | pub fn call_hierarchy_item( | ||
494 | world: &WorldSnapshot, | ||
495 | target: NavigationTarget, | ||
496 | ) -> Result<lsp_types::CallHierarchyItem> { | ||
497 | let name = target.name().to_string(); | ||
498 | let detail = target.description().map(|it| it.to_string()); | ||
499 | let kind = symbol_kind(target.kind()); | ||
500 | let (uri, range, selection_range) = location_info(world, target)?; | ||
501 | Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) | ||
502 | } | ||
503 | |||
504 | #[cfg(test)] | ||
505 | mod tests { | ||
506 | use test_utils::extract_ranges; | ||
507 | |||
508 | use super::*; | ||
509 | |||
510 | #[test] | ||
511 | fn conv_fold_line_folding_only_fixup() { | ||
512 | let text = r#"<fold>mod a; | ||
513 | mod b; | ||
514 | mod c;</fold> | ||
515 | |||
516 | fn main() <fold>{ | ||
517 | if cond <fold>{ | ||
518 | a::do_a(); | ||
519 | }</fold> else <fold>{ | ||
520 | b::do_b(); | ||
521 | }</fold> | ||
522 | }</fold>"#; | ||
523 | |||
524 | let (ranges, text) = extract_ranges(text, "fold"); | ||
525 | assert_eq!(ranges.len(), 4); | ||
526 | let folds = vec![ | ||
527 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
528 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
529 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
530 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
531 | ]; | ||
532 | |||
533 | let line_index = LineIndex::new(&text); | ||
534 | let converted: Vec<lsp_types::FoldingRange> = | ||
535 | folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); | ||
536 | |||
537 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; | ||
538 | assert_eq!(converted.len(), expected_lines.len()); | ||
539 | for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) { | ||
540 | assert_eq!(folding_range.start_line, *start_line); | ||
541 | assert_eq!(folding_range.start_character, None); | ||
542 | assert_eq!(folding_range.end_line, *end_line); | ||
543 | assert_eq!(folding_range.end_character, None); | ||
544 | } | ||
545 | } | ||
546 | } | ||
547 | |||
548 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> { | ||
549 | let source_change = source_change(&world, assist.source_change)?; | ||
550 | let arg = serde_json::to_value(source_change)?; | ||
551 | let title = assist.label; | ||
552 | let command = lsp_types::Command { | ||
553 | title: title.clone(), | ||
554 | command: "rust-analyzer.applySourceChange".to_string(), | ||
555 | arguments: Some(vec![arg]), | ||
556 | }; | ||
557 | |||
558 | Ok(lsp_types::CodeAction { | ||
559 | title, | ||
560 | kind: Some(String::new()), | ||
561 | diagnostics: None, | ||
562 | edit: None, | ||
563 | command: Some(command), | ||
564 | is_preferred: None, | ||
565 | }) | ||
566 | } | ||