diff options
Diffstat (limited to 'crates/ra_lsp_server/src/conv.rs')
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs new file mode 100644 index 000000000..759e5e914 --- /dev/null +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -0,0 +1,296 @@ | |||
1 | use languageserver_types::{ | ||
2 | Range, SymbolKind, Position, TextEdit, Location, Url, | ||
3 | TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, | ||
4 | TextDocumentPositionParams, TextDocumentEdit, | ||
5 | }; | ||
6 | use ra_editor::{LineIndex, LineCol, Edit, AtomEdit}; | ||
7 | use ra_syntax::{SyntaxKind, TextUnit, TextRange}; | ||
8 | use ra_analysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit}; | ||
9 | |||
10 | use { | ||
11 | Result, | ||
12 | server_world::ServerWorld, | ||
13 | req, | ||
14 | }; | ||
15 | |||
16 | pub trait Conv { | ||
17 | type Output; | ||
18 | fn conv(self) -> Self::Output; | ||
19 | } | ||
20 | |||
21 | pub trait ConvWith { | ||
22 | type Ctx; | ||
23 | type Output; | ||
24 | fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; | ||
25 | } | ||
26 | |||
27 | pub trait TryConvWith { | ||
28 | type Ctx; | ||
29 | type Output; | ||
30 | fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>; | ||
31 | } | ||
32 | |||
33 | impl Conv for SyntaxKind { | ||
34 | type Output = SymbolKind; | ||
35 | |||
36 | fn conv(self) -> <Self as Conv>::Output { | ||
37 | match self { | ||
38 | SyntaxKind::FN_DEF => SymbolKind::Function, | ||
39 | SyntaxKind::STRUCT_DEF => SymbolKind::Struct, | ||
40 | SyntaxKind::ENUM_DEF => SymbolKind::Enum, | ||
41 | SyntaxKind::TRAIT_DEF => SymbolKind::Interface, | ||
42 | SyntaxKind::MODULE => SymbolKind::Module, | ||
43 | SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter, | ||
44 | SyntaxKind::STATIC_DEF => SymbolKind::Constant, | ||
45 | SyntaxKind::CONST_DEF => SymbolKind::Constant, | ||
46 | SyntaxKind::IMPL_ITEM => SymbolKind::Object, | ||
47 | _ => SymbolKind::Variable, | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | impl ConvWith for Position { | ||
53 | type Ctx = LineIndex; | ||
54 | type Output = TextUnit; | ||
55 | |||
56 | fn conv_with(self, line_index: &LineIndex) -> TextUnit { | ||
57 | // TODO: UTF-16 | ||
58 | let line_col = LineCol { | ||
59 | line: self.line as u32, | ||
60 | col: (self.character as u32).into(), | ||
61 | }; | ||
62 | line_index.offset(line_col) | ||
63 | } | ||
64 | } | ||
65 | |||
66 | impl ConvWith for TextUnit { | ||
67 | type Ctx = LineIndex; | ||
68 | type Output = Position; | ||
69 | |||
70 | fn conv_with(self, line_index: &LineIndex) -> Position { | ||
71 | let line_col = line_index.line_col(self); | ||
72 | // TODO: UTF-16 | ||
73 | Position::new(line_col.line as u64, u32::from(line_col.col) as u64) | ||
74 | } | ||
75 | } | ||
76 | |||
77 | impl ConvWith for TextRange { | ||
78 | type Ctx = LineIndex; | ||
79 | type Output = Range; | ||
80 | |||
81 | fn conv_with(self, line_index: &LineIndex) -> Range { | ||
82 | Range::new( | ||
83 | self.start().conv_with(line_index), | ||
84 | self.end().conv_with(line_index), | ||
85 | ) | ||
86 | } | ||
87 | } | ||
88 | |||
89 | impl ConvWith for Range { | ||
90 | type Ctx = LineIndex; | ||
91 | type Output = TextRange; | ||
92 | |||
93 | fn conv_with(self, line_index: &LineIndex) -> TextRange { | ||
94 | TextRange::from_to( | ||
95 | self.start.conv_with(line_index), | ||
96 | self.end.conv_with(line_index), | ||
97 | ) | ||
98 | } | ||
99 | } | ||
100 | |||
101 | impl ConvWith for Edit { | ||
102 | type Ctx = LineIndex; | ||
103 | type Output = Vec<TextEdit>; | ||
104 | |||
105 | fn conv_with(self, line_index: &LineIndex) -> Vec<TextEdit> { | ||
106 | self.into_atoms() | ||
107 | .into_iter() | ||
108 | .map_conv_with(line_index) | ||
109 | .collect() | ||
110 | } | ||
111 | } | ||
112 | |||
113 | impl ConvWith for AtomEdit { | ||
114 | type Ctx = LineIndex; | ||
115 | type Output = TextEdit; | ||
116 | |||
117 | fn conv_with(self, line_index: &LineIndex) -> TextEdit { | ||
118 | TextEdit { | ||
119 | range: self.delete.conv_with(line_index), | ||
120 | new_text: self.insert, | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | |||
125 | impl<T: ConvWith> ConvWith for Option<T> { | ||
126 | type Ctx = <T as ConvWith>::Ctx; | ||
127 | type Output = Option<<T as ConvWith>::Output>; | ||
128 | fn conv_with(self, ctx: &Self::Ctx) -> Self::Output { | ||
129 | self.map(|x| ConvWith::conv_with(x, ctx)) | ||
130 | } | ||
131 | } | ||
132 | |||
133 | impl<'a> TryConvWith for &'a Url { | ||
134 | type Ctx = ServerWorld; | ||
135 | type Output = FileId; | ||
136 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | ||
137 | world.uri_to_file_id(self) | ||
138 | } | ||
139 | } | ||
140 | |||
141 | impl TryConvWith for FileId { | ||
142 | type Ctx = ServerWorld; | ||
143 | type Output = Url; | ||
144 | fn try_conv_with(self, world: &ServerWorld) -> Result<Url> { | ||
145 | world.file_id_to_uri(self) | ||
146 | } | ||
147 | } | ||
148 | |||
149 | impl<'a> TryConvWith for &'a TextDocumentItem { | ||
150 | type Ctx = ServerWorld; | ||
151 | type Output = FileId; | ||
152 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | ||
153 | self.uri.try_conv_with(world) | ||
154 | } | ||
155 | } | ||
156 | |||
157 | impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { | ||
158 | type Ctx = ServerWorld; | ||
159 | type Output = FileId; | ||
160 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | ||
161 | self.uri.try_conv_with(world) | ||
162 | } | ||
163 | } | ||
164 | |||
165 | impl<'a> TryConvWith for &'a TextDocumentIdentifier { | ||
166 | type Ctx = ServerWorld; | ||
167 | type Output = FileId; | ||
168 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | ||
169 | world.uri_to_file_id(&self.uri) | ||
170 | } | ||
171 | } | ||
172 | |||
173 | impl<T: TryConvWith> TryConvWith for Vec<T> { | ||
174 | type Ctx = <T as TryConvWith>::Ctx; | ||
175 | type Output = Vec<<T as TryConvWith>::Output>; | ||
176 | fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> { | ||
177 | let mut res = Vec::with_capacity(self.len()); | ||
178 | for item in self { | ||
179 | res.push(item.try_conv_with(ctx)?); | ||
180 | } | ||
181 | Ok(res) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | impl TryConvWith for SourceChange { | ||
186 | type Ctx = ServerWorld; | ||
187 | type Output = req::SourceChange; | ||
188 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> { | ||
189 | let cursor_position = match self.cursor_position { | ||
190 | None => None, | ||
191 | Some(pos) => { | ||
192 | let line_index = world.analysis().file_line_index(pos.file_id); | ||
193 | Some(TextDocumentPositionParams { | ||
194 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), | ||
195 | position: pos.offset.conv_with(&line_index), | ||
196 | }) | ||
197 | } | ||
198 | }; | ||
199 | let source_file_edits = self.source_file_edits.try_conv_with(world)?; | ||
200 | let file_system_edits = self.file_system_edits.try_conv_with(world)?; | ||
201 | Ok(req::SourceChange { | ||
202 | label: self.label, | ||
203 | source_file_edits, | ||
204 | file_system_edits, | ||
205 | cursor_position, | ||
206 | }) | ||
207 | } | ||
208 | } | ||
209 | |||
210 | impl TryConvWith for SourceFileEdit { | ||
211 | type Ctx = ServerWorld; | ||
212 | type Output = TextDocumentEdit; | ||
213 | fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> { | ||
214 | let text_document = VersionedTextDocumentIdentifier { | ||
215 | uri: self.file_id.try_conv_with(world)?, | ||
216 | version: None, | ||
217 | }; | ||
218 | let line_index = world.analysis().file_line_index(self.file_id); | ||
219 | let edits = self.edits | ||
220 | .into_iter() | ||
221 | .map_conv_with(&line_index) | ||
222 | .collect(); | ||
223 | Ok(TextDocumentEdit { text_document, edits }) | ||
224 | } | ||
225 | } | ||
226 | |||
227 | impl TryConvWith for FileSystemEdit { | ||
228 | type Ctx = ServerWorld; | ||
229 | type Output = req::FileSystemEdit; | ||
230 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::FileSystemEdit> { | ||
231 | let res = match self { | ||
232 | FileSystemEdit::CreateFile { anchor, path } => { | ||
233 | let uri = world.file_id_to_uri(anchor)?; | ||
234 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
235 | let uri = uri.join(path)?; | ||
236 | req::FileSystemEdit::CreateFile { uri } | ||
237 | }, | ||
238 | FileSystemEdit::MoveFile { file, path } => { | ||
239 | let src = world.file_id_to_uri(file)?; | ||
240 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
241 | let dst = src.join(path)?; | ||
242 | req::FileSystemEdit::MoveFile { src, dst } | ||
243 | }, | ||
244 | }; | ||
245 | Ok(res) | ||
246 | } | ||
247 | } | ||
248 | |||
249 | pub fn to_location( | ||
250 | file_id: FileId, | ||
251 | range: TextRange, | ||
252 | world: &ServerWorld, | ||
253 | line_index: &LineIndex, | ||
254 | ) -> Result<Location> { | ||
255 | let url = file_id.try_conv_with(world)?; | ||
256 | let loc = Location::new( | ||
257 | url, | ||
258 | range.conv_with(line_index), | ||
259 | ); | ||
260 | Ok(loc) | ||
261 | } | ||
262 | |||
263 | pub trait MapConvWith<'a>: Sized { | ||
264 | type Ctx; | ||
265 | type Output; | ||
266 | |||
267 | fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> { | ||
268 | ConvWithIter { iter: self, ctx } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | impl<'a, I> MapConvWith<'a> for I | ||
273 | where I: Iterator, | ||
274 | I::Item: ConvWith | ||
275 | { | ||
276 | type Ctx = <I::Item as ConvWith>::Ctx; | ||
277 | type Output = <I::Item as ConvWith>::Output; | ||
278 | } | ||
279 | |||
280 | pub struct ConvWithIter<'a, I, Ctx: 'a> { | ||
281 | iter: I, | ||
282 | ctx: &'a Ctx, | ||
283 | } | ||
284 | |||
285 | impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx> | ||
286 | where | ||
287 | I: Iterator, | ||
288 | I::Item: ConvWith<Ctx=Ctx>, | ||
289 | { | ||
290 | type Item = <I::Item as ConvWith>::Output; | ||
291 | |||
292 | fn next(&mut self) -> Option<Self::Item> { | ||
293 | self.iter.next().map(|item| item.conv_with(self.ctx)) | ||
294 | } | ||
295 | } | ||
296 | |||