]> git.lizzy.rs Git - rust.git/blob - crates/ra_lsp_server/src/conv.rs
change to `TextEdit` to avoid allocation and sort
[rust.git] / crates / ra_lsp_server / src / conv.rs
1 use languageserver_types::{
2     self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
3     TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat,
4 };
5 use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition, CompletionItem, CompletionItemKind, InsertText};
6 use ra_editor::{LineCol, LineIndex, translate_offset_with_edit};
7 use ra_text_edit::{AtomTextEdit, TextEdit};
8 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
9
10 use crate::{req, server_world::ServerWorld, Result};
11
12 pub trait Conv {
13     type Output;
14     fn conv(self) -> Self::Output;
15 }
16
17 pub trait ConvWith {
18     type Ctx;
19     type Output;
20     fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
21 }
22
23 pub trait TryConvWith {
24     type Ctx;
25     type Output;
26     fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>;
27 }
28
29 impl Conv for SyntaxKind {
30     type Output = SymbolKind;
31
32     fn conv(self) -> <Self as Conv>::Output {
33         match self {
34             SyntaxKind::FN_DEF => SymbolKind::Function,
35             SyntaxKind::STRUCT_DEF => SymbolKind::Struct,
36             SyntaxKind::ENUM_DEF => SymbolKind::Enum,
37             SyntaxKind::TRAIT_DEF => SymbolKind::Interface,
38             SyntaxKind::MODULE => SymbolKind::Module,
39             SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter,
40             SyntaxKind::STATIC_DEF => SymbolKind::Constant,
41             SyntaxKind::CONST_DEF => SymbolKind::Constant,
42             SyntaxKind::IMPL_ITEM => SymbolKind::Object,
43             _ => SymbolKind::Variable,
44         }
45     }
46 }
47
48 impl Conv for CompletionItemKind {
49     type Output = ::languageserver_types::CompletionItemKind;
50
51     fn conv(self) -> <Self as Conv>::Output {
52         use ::languageserver_types::CompletionItemKind::*;
53         match self {
54             CompletionItemKind::Keyword => Keyword,
55             CompletionItemKind::Snippet => Snippet,
56             CompletionItemKind::Module => Module,
57             CompletionItemKind::Function => Function,
58             CompletionItemKind::Binding => Variable,
59         }
60     }
61 }
62
63 impl Conv for CompletionItem {
64     type Output = ::languageserver_types::CompletionItem;
65
66     fn conv(self) -> <Self as Conv>::Output {
67         let mut res = ::languageserver_types::CompletionItem {
68             label: self.label().to_string(),
69             filter_text: Some(self.lookup().to_string()),
70             kind: self.kind().map(|it| it.conv()),
71             ..Default::default()
72         };
73         match self.insert_text() {
74             InsertText::PlainText { text } => {
75                 res.insert_text = Some(text);
76                 res.insert_text_format = Some(InsertTextFormat::PlainText);
77             }
78             InsertText::Snippet { text } => {
79                 res.insert_text = Some(text);
80                 res.insert_text_format = Some(InsertTextFormat::Snippet);
81                 res.kind = Some(languageserver_types::CompletionItemKind::Keyword);
82             }
83         }
84         res
85     }
86 }
87
88 impl ConvWith for Position {
89     type Ctx = LineIndex;
90     type Output = TextUnit;
91
92     fn conv_with(self, line_index: &LineIndex) -> TextUnit {
93         let line_col = LineCol {
94             line: self.line as u32,
95             col_utf16: self.character as u32,
96         };
97         line_index.offset(line_col)
98     }
99 }
100
101 impl ConvWith for TextUnit {
102     type Ctx = LineIndex;
103     type Output = Position;
104
105     fn conv_with(self, line_index: &LineIndex) -> Position {
106         let line_col = line_index.line_col(self);
107         Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16))
108     }
109 }
110
111 impl ConvWith for TextRange {
112     type Ctx = LineIndex;
113     type Output = Range;
114
115     fn conv_with(self, line_index: &LineIndex) -> Range {
116         Range::new(
117             self.start().conv_with(line_index),
118             self.end().conv_with(line_index),
119         )
120     }
121 }
122
123 impl ConvWith for Range {
124     type Ctx = LineIndex;
125     type Output = TextRange;
126
127     fn conv_with(self, line_index: &LineIndex) -> TextRange {
128         TextRange::from_to(
129             self.start.conv_with(line_index),
130             self.end.conv_with(line_index),
131         )
132     }
133 }
134
135 impl ConvWith for TextEdit {
136     type Ctx = LineIndex;
137     type Output = Vec<languageserver_types::TextEdit>;
138
139     fn conv_with(self, line_index: &LineIndex) -> Vec<languageserver_types::TextEdit> {
140         self.as_atoms()
141             .into_iter()
142             .map_conv_with(line_index)
143             .collect()
144     }
145 }
146
147 impl<'a> ConvWith for &'a AtomTextEdit {
148     type Ctx = LineIndex;
149     type Output = languageserver_types::TextEdit;
150
151     fn conv_with(self, line_index: &LineIndex) -> languageserver_types::TextEdit {
152         languageserver_types::TextEdit {
153             range: self.delete.conv_with(line_index),
154             new_text: self.insert.clone(),
155         }
156     }
157 }
158
159 impl<T: ConvWith> ConvWith for Option<T> {
160     type Ctx = <T as ConvWith>::Ctx;
161     type Output = Option<<T as ConvWith>::Output>;
162     fn conv_with(self, ctx: &Self::Ctx) -> Self::Output {
163         self.map(|x| ConvWith::conv_with(x, ctx))
164     }
165 }
166
167 impl<'a> TryConvWith for &'a Url {
168     type Ctx = ServerWorld;
169     type Output = FileId;
170     fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
171         world.uri_to_file_id(self)
172     }
173 }
174
175 impl TryConvWith for FileId {
176     type Ctx = ServerWorld;
177     type Output = Url;
178     fn try_conv_with(self, world: &ServerWorld) -> Result<Url> {
179         world.file_id_to_uri(self)
180     }
181 }
182
183 impl<'a> TryConvWith for &'a TextDocumentItem {
184     type Ctx = ServerWorld;
185     type Output = FileId;
186     fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
187         self.uri.try_conv_with(world)
188     }
189 }
190
191 impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier {
192     type Ctx = ServerWorld;
193     type Output = FileId;
194     fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
195         self.uri.try_conv_with(world)
196     }
197 }
198
199 impl<'a> TryConvWith for &'a TextDocumentIdentifier {
200     type Ctx = ServerWorld;
201     type Output = FileId;
202     fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
203         world.uri_to_file_id(&self.uri)
204     }
205 }
206
207 impl<'a> TryConvWith for &'a TextDocumentPositionParams {
208     type Ctx = ServerWorld;
209     type Output = FilePosition;
210     fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> {
211         let file_id = self.text_document.try_conv_with(world)?;
212         let line_index = world.analysis().file_line_index(file_id);
213         let offset = self.position.conv_with(&line_index);
214         Ok(FilePosition { file_id, offset })
215     }
216 }
217
218 impl<T: TryConvWith> TryConvWith for Vec<T> {
219     type Ctx = <T as TryConvWith>::Ctx;
220     type Output = Vec<<T as TryConvWith>::Output>;
221     fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> {
222         let mut res = Vec::with_capacity(self.len());
223         for item in self {
224             res.push(item.try_conv_with(ctx)?);
225         }
226         Ok(res)
227     }
228 }
229
230 impl TryConvWith for SourceChange {
231     type Ctx = ServerWorld;
232     type Output = req::SourceChange;
233     fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> {
234         let cursor_position = match self.cursor_position {
235             None => None,
236             Some(pos) => {
237                 let line_index = world.analysis().file_line_index(pos.file_id);
238                 let edit = self
239                     .source_file_edits
240                     .iter()
241                     .find(|it| it.file_id == pos.file_id)
242                     .map(|it| &it.edit);
243                 let line_col = match edit {
244                     Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
245                     None => line_index.line_col(pos.offset),
246                 };
247                 let position =
248                     Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
249                 Some(TextDocumentPositionParams {
250                     text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
251                     position,
252                 })
253             }
254         };
255         let source_file_edits = self.source_file_edits.try_conv_with(world)?;
256         let file_system_edits = self.file_system_edits.try_conv_with(world)?;
257         Ok(req::SourceChange {
258             label: self.label,
259             source_file_edits,
260             file_system_edits,
261             cursor_position,
262         })
263     }
264 }
265
266 impl TryConvWith for SourceFileEdit {
267     type Ctx = ServerWorld;
268     type Output = TextDocumentEdit;
269     fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> {
270         let text_document = VersionedTextDocumentIdentifier {
271             uri: self.file_id.try_conv_with(world)?,
272             version: None,
273         };
274         let line_index = world.analysis().file_line_index(self.file_id);
275         let edits = self
276             .edit
277             .as_atoms()
278             .iter()
279             .map_conv_with(&line_index)
280             .collect();
281         Ok(TextDocumentEdit {
282             text_document,
283             edits,
284         })
285     }
286 }
287
288 impl TryConvWith for FileSystemEdit {
289     type Ctx = ServerWorld;
290     type Output = req::FileSystemEdit;
291     fn try_conv_with(self, world: &ServerWorld) -> Result<req::FileSystemEdit> {
292         let res = match self {
293             FileSystemEdit::CreateFile { source_root, path } => {
294                 let uri = world.path_to_uri(source_root, &path)?;
295                 req::FileSystemEdit::CreateFile { uri }
296             }
297             FileSystemEdit::MoveFile {
298                 src,
299                 dst_source_root,
300                 dst_path,
301             } => {
302                 let src = world.file_id_to_uri(src)?;
303                 let dst = world.path_to_uri(dst_source_root, &dst_path)?;
304                 req::FileSystemEdit::MoveFile { src, dst }
305             }
306         };
307         Ok(res)
308     }
309 }
310
311 pub fn to_location(
312     file_id: FileId,
313     range: TextRange,
314     world: &ServerWorld,
315     line_index: &LineIndex,
316 ) -> Result<Location> {
317     let url = file_id.try_conv_with(world)?;
318     let loc = Location::new(url, range.conv_with(line_index));
319     Ok(loc)
320 }
321
322 pub trait MapConvWith<'a>: Sized + 'a {
323     type Ctx;
324     type Output;
325
326     fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> {
327         ConvWithIter { iter: self, ctx }
328     }
329 }
330
331 impl<'a, I> MapConvWith<'a> for I
332 where
333     I: Iterator + 'a,
334     I::Item: ConvWith,
335 {
336     type Ctx = <I::Item as ConvWith>::Ctx;
337     type Output = <I::Item as ConvWith>::Output;
338 }
339
340 pub struct ConvWithIter<'a, I, Ctx: 'a> {
341     iter: I,
342     ctx: &'a Ctx,
343 }
344
345 impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
346 where
347     I: Iterator,
348     I::Item: ConvWith<Ctx = Ctx>,
349 {
350     type Item = <I::Item as ConvWith>::Output;
351
352     fn next(&mut self) -> Option<Self::Item> {
353         self.iter.next().map(|item| item.conv_with(self.ctx))
354     }
355 }