diff options
author | Aleksey Kladov <[email protected]> | 2020-02-18 12:30:40 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-18 12:30:40 +0000 |
commit | d05480a178b132e62b8aff8986a8cb3dd3a89c0b (patch) | |
tree | 0fc36373073a66c2bbd6c7cfae6cb734527d847f /crates/ra_lsp_server/src/conv.rs | |
parent | 2768476e491d985317b08230824f96e6718f338a (diff) | |
parent | 865759925be6b72f7ef39124ed0e4c86c0412a69 (diff) |
Merge pull request #3216 from matklad/rename-to-rust-analyzer
rename binary to rust-analyzer
Diffstat (limited to 'crates/ra_lsp_server/src/conv.rs')
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 630 |
1 files changed, 0 insertions, 630 deletions
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs deleted file mode 100644 index 90ef74056..000000000 --- a/crates/ra_lsp_server/src/conv.rs +++ /dev/null | |||
@@ -1,630 +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, Position, Range, RenameFile, ResourceOp, | ||
7 | SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, | ||
8 | TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, | ||
9 | }; | ||
10 | use ra_ide::{ | ||
11 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, | ||
12 | FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, | ||
13 | NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, | ||
14 | }; | ||
15 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | ||
16 | use ra_text_edit::{AtomTextEdit, TextEdit}; | ||
17 | use ra_vfs::LineEndings; | ||
18 | |||
19 | use crate::{req, world::WorldSnapshot, Result}; | ||
20 | |||
21 | pub trait Conv { | ||
22 | type Output; | ||
23 | fn conv(self) -> Self::Output; | ||
24 | } | ||
25 | |||
26 | pub trait ConvWith<CTX> { | ||
27 | type Output; | ||
28 | fn conv_with(self, ctx: CTX) -> Self::Output; | ||
29 | } | ||
30 | |||
31 | pub trait TryConvWith<CTX> { | ||
32 | type Output; | ||
33 | fn try_conv_with(self, ctx: CTX) -> Result<Self::Output>; | ||
34 | } | ||
35 | |||
36 | impl Conv for SyntaxKind { | ||
37 | type Output = SymbolKind; | ||
38 | |||
39 | fn conv(self) -> <Self as Conv>::Output { | ||
40 | match self { | ||
41 | SyntaxKind::FN_DEF => SymbolKind::Function, | ||
42 | SyntaxKind::STRUCT_DEF => SymbolKind::Struct, | ||
43 | SyntaxKind::ENUM_DEF => SymbolKind::Enum, | ||
44 | SyntaxKind::ENUM_VARIANT => SymbolKind::EnumMember, | ||
45 | SyntaxKind::TRAIT_DEF => SymbolKind::Interface, | ||
46 | SyntaxKind::MACRO_CALL => SymbolKind::Function, | ||
47 | SyntaxKind::MODULE => SymbolKind::Module, | ||
48 | SyntaxKind::TYPE_ALIAS_DEF => SymbolKind::TypeParameter, | ||
49 | SyntaxKind::RECORD_FIELD_DEF => SymbolKind::Field, | ||
50 | SyntaxKind::STATIC_DEF => SymbolKind::Constant, | ||
51 | SyntaxKind::CONST_DEF => SymbolKind::Constant, | ||
52 | SyntaxKind::IMPL_BLOCK => SymbolKind::Object, | ||
53 | _ => SymbolKind::Variable, | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | impl Conv for ReferenceAccess { | ||
59 | type Output = ::lsp_types::DocumentHighlightKind; | ||
60 | |||
61 | fn conv(self) -> Self::Output { | ||
62 | use lsp_types::DocumentHighlightKind; | ||
63 | match self { | ||
64 | ReferenceAccess::Read => DocumentHighlightKind::Read, | ||
65 | ReferenceAccess::Write => DocumentHighlightKind::Write, | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl Conv for CompletionItemKind { | ||
71 | type Output = ::lsp_types::CompletionItemKind; | ||
72 | |||
73 | fn conv(self) -> <Self as Conv>::Output { | ||
74 | use lsp_types::CompletionItemKind::*; | ||
75 | match self { | ||
76 | CompletionItemKind::Keyword => Keyword, | ||
77 | CompletionItemKind::Snippet => Snippet, | ||
78 | CompletionItemKind::Module => Module, | ||
79 | CompletionItemKind::Function => Function, | ||
80 | CompletionItemKind::Struct => Struct, | ||
81 | CompletionItemKind::Enum => Enum, | ||
82 | CompletionItemKind::EnumVariant => EnumMember, | ||
83 | CompletionItemKind::BuiltinType => Struct, | ||
84 | CompletionItemKind::Binding => Variable, | ||
85 | CompletionItemKind::Field => Field, | ||
86 | CompletionItemKind::Trait => Interface, | ||
87 | CompletionItemKind::TypeAlias => Struct, | ||
88 | CompletionItemKind::Const => Constant, | ||
89 | CompletionItemKind::Static => Value, | ||
90 | CompletionItemKind::Method => Method, | ||
91 | CompletionItemKind::TypeParam => TypeParameter, | ||
92 | CompletionItemKind::Macro => Method, | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | impl Conv for Severity { | ||
98 | type Output = DiagnosticSeverity; | ||
99 | fn conv(self) -> DiagnosticSeverity { | ||
100 | match self { | ||
101 | Severity::Error => DiagnosticSeverity::Error, | ||
102 | Severity::WeakWarning => DiagnosticSeverity::Hint, | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | ||
108 | type Output = ::lsp_types::CompletionItem; | ||
109 | |||
110 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem { | ||
111 | let mut additional_text_edits = Vec::new(); | ||
112 | let mut text_edit = None; | ||
113 | // LSP does not allow arbitrary edits in completion, so we have to do a | ||
114 | // non-trivial mapping here. | ||
115 | for atom_edit in self.text_edit().as_atoms() { | ||
116 | if self.source_range().is_subrange(&atom_edit.delete) { | ||
117 | text_edit = Some(if atom_edit.delete == self.source_range() { | ||
118 | atom_edit.conv_with(ctx) | ||
119 | } else { | ||
120 | assert!(self.source_range().end() == atom_edit.delete.end()); | ||
121 | let range1 = | ||
122 | TextRange::from_to(atom_edit.delete.start(), self.source_range().start()); | ||
123 | let range2 = self.source_range(); | ||
124 | let edit1 = AtomTextEdit::replace(range1, String::new()); | ||
125 | let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); | ||
126 | additional_text_edits.push(edit1.conv_with(ctx)); | ||
127 | edit2.conv_with(ctx) | ||
128 | }) | ||
129 | } else { | ||
130 | assert!(self.source_range().intersection(&atom_edit.delete).is_none()); | ||
131 | additional_text_edits.push(atom_edit.conv_with(ctx)); | ||
132 | } | ||
133 | } | ||
134 | let text_edit = text_edit.unwrap(); | ||
135 | |||
136 | let mut res = lsp_types::CompletionItem { | ||
137 | label: self.label().to_string(), | ||
138 | detail: self.detail().map(|it| it.to_string()), | ||
139 | filter_text: Some(self.lookup().to_string()), | ||
140 | kind: self.kind().map(|it| it.conv()), | ||
141 | text_edit: Some(text_edit), | ||
142 | additional_text_edits: Some(additional_text_edits), | ||
143 | documentation: self.documentation().map(|it| it.conv()), | ||
144 | deprecated: Some(self.deprecated()), | ||
145 | ..Default::default() | ||
146 | }; | ||
147 | |||
148 | if self.deprecated() { | ||
149 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | ||
150 | } | ||
151 | |||
152 | res.insert_text_format = Some(match self.insert_text_format() { | ||
153 | InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, | ||
154 | InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, | ||
155 | }); | ||
156 | |||
157 | res | ||
158 | } | ||
159 | } | ||
160 | |||
161 | impl ConvWith<&LineIndex> for Position { | ||
162 | type Output = TextUnit; | ||
163 | |||
164 | fn conv_with(self, line_index: &LineIndex) -> TextUnit { | ||
165 | let line_col = LineCol { line: self.line as u32, col_utf16: self.character as u32 }; | ||
166 | line_index.offset(line_col) | ||
167 | } | ||
168 | } | ||
169 | |||
170 | impl ConvWith<&LineIndex> for TextUnit { | ||
171 | type Output = Position; | ||
172 | |||
173 | fn conv_with(self, line_index: &LineIndex) -> Position { | ||
174 | let line_col = line_index.line_col(self); | ||
175 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)) | ||
176 | } | ||
177 | } | ||
178 | |||
179 | impl ConvWith<&LineIndex> for TextRange { | ||
180 | type Output = Range; | ||
181 | |||
182 | fn conv_with(self, line_index: &LineIndex) -> Range { | ||
183 | Range::new(self.start().conv_with(line_index), self.end().conv_with(line_index)) | ||
184 | } | ||
185 | } | ||
186 | |||
187 | impl ConvWith<&LineIndex> for Range { | ||
188 | type Output = TextRange; | ||
189 | |||
190 | fn conv_with(self, line_index: &LineIndex) -> TextRange { | ||
191 | TextRange::from_to(self.start.conv_with(line_index), self.end.conv_with(line_index)) | ||
192 | } | ||
193 | } | ||
194 | |||
195 | impl Conv for ra_ide::Documentation { | ||
196 | type Output = lsp_types::Documentation; | ||
197 | fn conv(self) -> Documentation { | ||
198 | Documentation::MarkupContent(MarkupContent { | ||
199 | kind: MarkupKind::Markdown, | ||
200 | value: crate::markdown::format_docs(self.as_str()), | ||
201 | }) | ||
202 | } | ||
203 | } | ||
204 | |||
205 | impl Conv for ra_ide::FunctionSignature { | ||
206 | type Output = lsp_types::SignatureInformation; | ||
207 | fn conv(self) -> Self::Output { | ||
208 | use lsp_types::{ParameterInformation, ParameterLabel, SignatureInformation}; | ||
209 | |||
210 | let label = self.to_string(); | ||
211 | |||
212 | let documentation = self.doc.map(|it| it.conv()); | ||
213 | |||
214 | let parameters: Vec<ParameterInformation> = self | ||
215 | .parameters | ||
216 | .into_iter() | ||
217 | .map(|param| ParameterInformation { | ||
218 | label: ParameterLabel::Simple(param), | ||
219 | documentation: None, | ||
220 | }) | ||
221 | .collect(); | ||
222 | |||
223 | SignatureInformation { label, documentation, parameters: Some(parameters) } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | impl ConvWith<(&LineIndex, LineEndings)> for TextEdit { | ||
228 | type Output = Vec<lsp_types::TextEdit>; | ||
229 | |||
230 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> { | ||
231 | self.as_atoms().iter().map_conv_with(ctx).collect() | ||
232 | } | ||
233 | } | ||
234 | |||
235 | impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit { | ||
236 | type Output = lsp_types::TextEdit; | ||
237 | |||
238 | fn conv_with( | ||
239 | self, | ||
240 | (line_index, line_endings): (&LineIndex, LineEndings), | ||
241 | ) -> lsp_types::TextEdit { | ||
242 | let mut new_text = self.insert.clone(); | ||
243 | if line_endings == LineEndings::Dos { | ||
244 | new_text = new_text.replace('\n', "\r\n"); | ||
245 | } | ||
246 | lsp_types::TextEdit { range: self.delete.conv_with(line_index), new_text } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | pub(crate) struct FoldConvCtx<'a> { | ||
251 | pub(crate) text: &'a str, | ||
252 | pub(crate) line_index: &'a LineIndex, | ||
253 | pub(crate) line_folding_only: bool, | ||
254 | } | ||
255 | |||
256 | impl ConvWith<&FoldConvCtx<'_>> for Fold { | ||
257 | type Output = lsp_types::FoldingRange; | ||
258 | |||
259 | fn conv_with(self, ctx: &FoldConvCtx) -> lsp_types::FoldingRange { | ||
260 | let kind = match self.kind { | ||
261 | FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), | ||
262 | FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), | ||
263 | FoldKind::Mods => None, | ||
264 | FoldKind::Block => None, | ||
265 | }; | ||
266 | |||
267 | let range = self.range.conv_with(&ctx.line_index); | ||
268 | |||
269 | if ctx.line_folding_only { | ||
270 | // Clients with line_folding_only == true (such as VSCode) will fold the whole end line | ||
271 | // even if it contains text not in the folding range. To prevent that we exclude | ||
272 | // range.end.line from the folding region if there is more text after range.end | ||
273 | // on the same line. | ||
274 | let has_more_text_on_end_line = ctx.text | ||
275 | [TextRange::from_to(self.range.end(), TextUnit::of_str(ctx.text))] | ||
276 | .chars() | ||
277 | .take_while(|it| *it != '\n') | ||
278 | .any(|it| !it.is_whitespace()); | ||
279 | |||
280 | let end_line = if has_more_text_on_end_line { | ||
281 | range.end.line.saturating_sub(1) | ||
282 | } else { | ||
283 | range.end.line | ||
284 | }; | ||
285 | |||
286 | lsp_types::FoldingRange { | ||
287 | start_line: range.start.line, | ||
288 | start_character: None, | ||
289 | end_line, | ||
290 | end_character: None, | ||
291 | kind, | ||
292 | } | ||
293 | } else { | ||
294 | lsp_types::FoldingRange { | ||
295 | start_line: range.start.line, | ||
296 | start_character: Some(range.start.character), | ||
297 | end_line: range.end.line, | ||
298 | end_character: Some(range.end.character), | ||
299 | kind, | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | |||
305 | impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> { | ||
306 | type Output = Option<T::Output>; | ||
307 | |||
308 | fn conv_with(self, ctx: CTX) -> Self::Output { | ||
309 | self.map(|x| ConvWith::conv_with(x, ctx)) | ||
310 | } | ||
311 | } | ||
312 | |||
313 | impl TryConvWith<&WorldSnapshot> for &Url { | ||
314 | type Output = FileId; | ||
315 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
316 | world.uri_to_file_id(self) | ||
317 | } | ||
318 | } | ||
319 | |||
320 | impl TryConvWith<&WorldSnapshot> for FileId { | ||
321 | type Output = Url; | ||
322 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Url> { | ||
323 | world.file_id_to_uri(self) | ||
324 | } | ||
325 | } | ||
326 | |||
327 | impl TryConvWith<&WorldSnapshot> for &TextDocumentItem { | ||
328 | type Output = FileId; | ||
329 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
330 | self.uri.try_conv_with(world) | ||
331 | } | ||
332 | } | ||
333 | |||
334 | impl TryConvWith<&WorldSnapshot> for &VersionedTextDocumentIdentifier { | ||
335 | type Output = FileId; | ||
336 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
337 | self.uri.try_conv_with(world) | ||
338 | } | ||
339 | } | ||
340 | |||
341 | impl TryConvWith<&WorldSnapshot> for &TextDocumentIdentifier { | ||
342 | type Output = FileId; | ||
343 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { | ||
344 | world.uri_to_file_id(&self.uri) | ||
345 | } | ||
346 | } | ||
347 | |||
348 | impl TryConvWith<&WorldSnapshot> for &TextDocumentPositionParams { | ||
349 | type Output = FilePosition; | ||
350 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FilePosition> { | ||
351 | let file_id = self.text_document.try_conv_with(world)?; | ||
352 | let line_index = world.analysis().file_line_index(file_id)?; | ||
353 | let offset = self.position.conv_with(&line_index); | ||
354 | Ok(FilePosition { file_id, offset }) | ||
355 | } | ||
356 | } | ||
357 | |||
358 | impl TryConvWith<&WorldSnapshot> for (&TextDocumentIdentifier, Range) { | ||
359 | type Output = FileRange; | ||
360 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileRange> { | ||
361 | let file_id = self.0.try_conv_with(world)?; | ||
362 | let line_index = world.analysis().file_line_index(file_id)?; | ||
363 | let range = self.1.conv_with(&line_index); | ||
364 | Ok(FileRange { file_id, range }) | ||
365 | } | ||
366 | } | ||
367 | |||
368 | impl<T: TryConvWith<CTX>, CTX: Copy> TryConvWith<CTX> for Vec<T> { | ||
369 | type Output = Vec<<T as TryConvWith<CTX>>::Output>; | ||
370 | fn try_conv_with(self, ctx: CTX) -> Result<Self::Output> { | ||
371 | let mut res = Vec::with_capacity(self.len()); | ||
372 | for item in self { | ||
373 | res.push(item.try_conv_with(ctx)?); | ||
374 | } | ||
375 | Ok(res) | ||
376 | } | ||
377 | } | ||
378 | |||
379 | impl TryConvWith<&WorldSnapshot> for SourceChange { | ||
380 | type Output = req::SourceChange; | ||
381 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::SourceChange> { | ||
382 | let cursor_position = match self.cursor_position { | ||
383 | None => None, | ||
384 | Some(pos) => { | ||
385 | let line_index = world.analysis().file_line_index(pos.file_id)?; | ||
386 | let edit = self | ||
387 | .source_file_edits | ||
388 | .iter() | ||
389 | .find(|it| it.file_id == pos.file_id) | ||
390 | .map(|it| &it.edit); | ||
391 | let line_col = match edit { | ||
392 | Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), | ||
393 | None => line_index.line_col(pos.offset), | ||
394 | }; | ||
395 | let position = | ||
396 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); | ||
397 | Some(TextDocumentPositionParams { | ||
398 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), | ||
399 | position, | ||
400 | }) | ||
401 | } | ||
402 | }; | ||
403 | let mut document_changes: Vec<DocumentChangeOperation> = Vec::new(); | ||
404 | for resource_op in self.file_system_edits.try_conv_with(world)? { | ||
405 | document_changes.push(DocumentChangeOperation::Op(resource_op)); | ||
406 | } | ||
407 | for text_document_edit in self.source_file_edits.try_conv_with(world)? { | ||
408 | document_changes.push(DocumentChangeOperation::Edit(text_document_edit)); | ||
409 | } | ||
410 | let workspace_edit = WorkspaceEdit { | ||
411 | changes: None, | ||
412 | document_changes: Some(DocumentChanges::Operations(document_changes)), | ||
413 | }; | ||
414 | Ok(req::SourceChange { label: self.label, workspace_edit, cursor_position }) | ||
415 | } | ||
416 | } | ||
417 | |||
418 | impl TryConvWith<&WorldSnapshot> for SourceFileEdit { | ||
419 | type Output = TextDocumentEdit; | ||
420 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<TextDocumentEdit> { | ||
421 | let text_document = VersionedTextDocumentIdentifier { | ||
422 | uri: self.file_id.try_conv_with(world)?, | ||
423 | version: None, | ||
424 | }; | ||
425 | let line_index = world.analysis().file_line_index(self.file_id)?; | ||
426 | let line_endings = world.file_line_endings(self.file_id); | ||
427 | let edits = | ||
428 | self.edit.as_atoms().iter().map_conv_with((&line_index, line_endings)).collect(); | ||
429 | Ok(TextDocumentEdit { text_document, edits }) | ||
430 | } | ||
431 | } | ||
432 | |||
433 | impl TryConvWith<&WorldSnapshot> for FileSystemEdit { | ||
434 | type Output = ResourceOp; | ||
435 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<ResourceOp> { | ||
436 | let res = match self { | ||
437 | FileSystemEdit::CreateFile { source_root, path } => { | ||
438 | let uri = world.path_to_uri(source_root, &path)?; | ||
439 | ResourceOp::Create(CreateFile { uri, options: None }) | ||
440 | } | ||
441 | FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { | ||
442 | let old_uri = world.file_id_to_uri(src)?; | ||
443 | let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; | ||
444 | ResourceOp::Rename(RenameFile { old_uri, new_uri, options: None }) | ||
445 | } | ||
446 | }; | ||
447 | Ok(res) | ||
448 | } | ||
449 | } | ||
450 | |||
451 | impl TryConvWith<&WorldSnapshot> for &NavigationTarget { | ||
452 | type Output = Location; | ||
453 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Location> { | ||
454 | let line_index = world.analysis().file_line_index(self.file_id())?; | ||
455 | let range = self.range(); | ||
456 | to_location(self.file_id(), range, &world, &line_index) | ||
457 | } | ||
458 | } | ||
459 | |||
460 | impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<NavigationTarget>) { | ||
461 | type Output = LocationLink; | ||
462 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<LocationLink> { | ||
463 | let (src_file_id, target) = self; | ||
464 | |||
465 | let target_uri = target.info.file_id().try_conv_with(world)?; | ||
466 | let src_line_index = world.analysis().file_line_index(src_file_id)?; | ||
467 | let tgt_line_index = world.analysis().file_line_index(target.info.file_id())?; | ||
468 | |||
469 | let target_range = target.info.full_range().conv_with(&tgt_line_index); | ||
470 | |||
471 | let target_selection_range = target | ||
472 | .info | ||
473 | .focus_range() | ||
474 | .map(|it| it.conv_with(&tgt_line_index)) | ||
475 | .unwrap_or(target_range); | ||
476 | |||
477 | let res = LocationLink { | ||
478 | origin_selection_range: Some(target.range.conv_with(&src_line_index)), | ||
479 | target_uri, | ||
480 | target_range, | ||
481 | target_selection_range, | ||
482 | }; | ||
483 | Ok(res) | ||
484 | } | ||
485 | } | ||
486 | |||
487 | impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>) { | ||
488 | type Output = req::GotoDefinitionResponse; | ||
489 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::GotoTypeDefinitionResponse> { | ||
490 | let (file_id, RangeInfo { range, info: navs }) = self; | ||
491 | let links = navs | ||
492 | .into_iter() | ||
493 | .map(|nav| (file_id, RangeInfo::new(range, nav))) | ||
494 | .try_conv_with_to_vec(world)?; | ||
495 | if world.options.supports_location_link { | ||
496 | Ok(links.into()) | ||
497 | } else { | ||
498 | let locations: Vec<Location> = links | ||
499 | .into_iter() | ||
500 | .map(|link| Location { uri: link.target_uri, range: link.target_selection_range }) | ||
501 | .collect(); | ||
502 | Ok(locations.into()) | ||
503 | } | ||
504 | } | ||
505 | } | ||
506 | |||
507 | pub fn to_call_hierarchy_item( | ||
508 | file_id: FileId, | ||
509 | range: TextRange, | ||
510 | world: &WorldSnapshot, | ||
511 | line_index: &LineIndex, | ||
512 | nav: NavigationTarget, | ||
513 | ) -> Result<lsp_types::CallHierarchyItem> { | ||
514 | Ok(lsp_types::CallHierarchyItem { | ||
515 | name: nav.name().to_string(), | ||
516 | kind: nav.kind().conv(), | ||
517 | tags: None, | ||
518 | detail: nav.description().map(|it| it.to_string()), | ||
519 | uri: file_id.try_conv_with(&world)?, | ||
520 | range: nav.range().conv_with(&line_index), | ||
521 | selection_range: range.conv_with(&line_index), | ||
522 | }) | ||
523 | } | ||
524 | |||
525 | pub fn to_location( | ||
526 | file_id: FileId, | ||
527 | range: TextRange, | ||
528 | world: &WorldSnapshot, | ||
529 | line_index: &LineIndex, | ||
530 | ) -> Result<Location> { | ||
531 | let url = file_id.try_conv_with(world)?; | ||
532 | let loc = Location::new(url, range.conv_with(line_index)); | ||
533 | Ok(loc) | ||
534 | } | ||
535 | |||
536 | pub trait MapConvWith<CTX>: Sized { | ||
537 | type Output; | ||
538 | |||
539 | fn map_conv_with(self, ctx: CTX) -> ConvWithIter<Self, CTX> { | ||
540 | ConvWithIter { iter: self, ctx } | ||
541 | } | ||
542 | } | ||
543 | |||
544 | impl<CTX, I> MapConvWith<CTX> for I | ||
545 | where | ||
546 | I: Iterator, | ||
547 | I::Item: ConvWith<CTX>, | ||
548 | { | ||
549 | type Output = <I::Item as ConvWith<CTX>>::Output; | ||
550 | } | ||
551 | |||
552 | pub struct ConvWithIter<I, CTX> { | ||
553 | iter: I, | ||
554 | ctx: CTX, | ||
555 | } | ||
556 | |||
557 | impl<I, CTX> Iterator for ConvWithIter<I, CTX> | ||
558 | where | ||
559 | I: Iterator, | ||
560 | I::Item: ConvWith<CTX>, | ||
561 | CTX: Copy, | ||
562 | { | ||
563 | type Item = <I::Item as ConvWith<CTX>>::Output; | ||
564 | |||
565 | fn next(&mut self) -> Option<Self::Item> { | ||
566 | self.iter.next().map(|item| item.conv_with(self.ctx)) | ||
567 | } | ||
568 | } | ||
569 | |||
570 | pub trait TryConvWithToVec<CTX>: Sized { | ||
571 | type Output; | ||
572 | |||
573 | fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>>; | ||
574 | } | ||
575 | |||
576 | impl<I, CTX> TryConvWithToVec<CTX> for I | ||
577 | where | ||
578 | I: Iterator, | ||
579 | I::Item: TryConvWith<CTX>, | ||
580 | CTX: Copy, | ||
581 | { | ||
582 | type Output = <I::Item as TryConvWith<CTX>>::Output; | ||
583 | |||
584 | fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>> { | ||
585 | self.map(|it| it.try_conv_with(ctx)).collect() | ||
586 | } | ||
587 | } | ||
588 | |||
589 | #[cfg(test)] | ||
590 | mod tests { | ||
591 | use super::*; | ||
592 | use test_utils::extract_ranges; | ||
593 | |||
594 | #[test] | ||
595 | fn conv_fold_line_folding_only_fixup() { | ||
596 | let text = r#"<fold>mod a; | ||
597 | mod b; | ||
598 | mod c;</fold> | ||
599 | |||
600 | fn main() <fold>{ | ||
601 | if cond <fold>{ | ||
602 | a::do_a(); | ||
603 | }</fold> else <fold>{ | ||
604 | b::do_b(); | ||
605 | }</fold> | ||
606 | }</fold>"#; | ||
607 | |||
608 | let (ranges, text) = extract_ranges(text, "fold"); | ||
609 | assert_eq!(ranges.len(), 4); | ||
610 | let folds = vec![ | ||
611 | Fold { range: ranges[0], kind: FoldKind::Mods }, | ||
612 | Fold { range: ranges[1], kind: FoldKind::Block }, | ||
613 | Fold { range: ranges[2], kind: FoldKind::Block }, | ||
614 | Fold { range: ranges[3], kind: FoldKind::Block }, | ||
615 | ]; | ||
616 | |||
617 | let line_index = LineIndex::new(&text); | ||
618 | let ctx = FoldConvCtx { text: &text, line_index: &line_index, line_folding_only: true }; | ||
619 | let converted: Vec<_> = folds.into_iter().map_conv_with(&ctx).collect(); | ||
620 | |||
621 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; | ||
622 | assert_eq!(converted.len(), expected_lines.len()); | ||
623 | for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) { | ||
624 | assert_eq!(folding_range.start_line, *start_line); | ||
625 | assert_eq!(folding_range.start_character, None); | ||
626 | assert_eq!(folding_range.end_line, *end_line); | ||
627 | assert_eq!(folding_range.end_character, None); | ||
628 | } | ||
629 | } | ||
630 | } | ||