1 use languageserver_types::{
2 self, CreateFile, DocumentChangeOperation, DocumentChanges, InsertTextFormat, Location, LocationLink,
3 Position, Range, RenameFile, ResourceOp, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
4 TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
8 CompletionItem, CompletionItemKind, FileId, FilePosition, FileRange, FileSystemEdit,
9 InsertText, NavigationTarget, SourceChange, SourceFileEdit, RangeInfo,
10 LineCol, LineIndex, translate_offset_with_edit
12 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
13 use ra_text_edit::{AtomTextEdit, TextEdit};
15 use crate::{req, server_world::ServerWorld, Result};
19 fn conv(self) -> Self::Output;
25 fn conv_with(self, ctx: &Self::Ctx) -> Self::Output;
28 pub trait TryConvWith {
31 fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>;
34 impl Conv for SyntaxKind {
35 type Output = SymbolKind;
37 fn conv(self) -> <Self as Conv>::Output {
39 SyntaxKind::FN_DEF => SymbolKind::Function,
40 SyntaxKind::STRUCT_DEF => SymbolKind::Struct,
41 SyntaxKind::ENUM_DEF => SymbolKind::Enum,
42 SyntaxKind::TRAIT_DEF => SymbolKind::Interface,
43 SyntaxKind::MODULE => SymbolKind::Module,
44 SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter,
45 SyntaxKind::STATIC_DEF => SymbolKind::Constant,
46 SyntaxKind::CONST_DEF => SymbolKind::Constant,
47 SyntaxKind::IMPL_BLOCK => SymbolKind::Object,
48 _ => SymbolKind::Variable,
53 impl Conv for CompletionItemKind {
54 type Output = ::languageserver_types::CompletionItemKind;
56 fn conv(self) -> <Self as Conv>::Output {
57 use languageserver_types::CompletionItemKind::*;
59 CompletionItemKind::Keyword => Keyword,
60 CompletionItemKind::Snippet => Snippet,
61 CompletionItemKind::Module => Module,
62 CompletionItemKind::Function => Function,
63 CompletionItemKind::Struct => Struct,
64 CompletionItemKind::Enum => Enum,
65 CompletionItemKind::EnumVariant => EnumMember,
66 CompletionItemKind::Binding => Variable,
67 CompletionItemKind::Field => Field,
68 CompletionItemKind::Trait => Interface,
69 CompletionItemKind::TypeAlias => Struct,
70 CompletionItemKind::Const => Constant,
71 CompletionItemKind::Static => Value,
76 impl Conv for CompletionItem {
77 type Output = ::languageserver_types::CompletionItem;
79 fn conv(self) -> <Self as Conv>::Output {
80 let mut res = ::languageserver_types::CompletionItem {
81 label: self.label().to_string(),
82 detail: self.detail().map(|it| it.to_string()),
83 filter_text: Some(self.lookup().to_string()),
84 kind: self.kind().map(|it| it.conv()),
87 match self.insert_text() {
88 InsertText::PlainText { text } => {
89 res.insert_text = Some(text);
90 res.insert_text_format = Some(InsertTextFormat::PlainText);
92 InsertText::Snippet { text } => {
93 res.insert_text = Some(text);
94 res.insert_text_format = Some(InsertTextFormat::Snippet);
101 impl ConvWith for Position {
102 type Ctx = LineIndex;
103 type Output = TextUnit;
105 fn conv_with(self, line_index: &LineIndex) -> TextUnit {
106 let line_col = LineCol {
107 line: self.line as u32,
108 col_utf16: self.character as u32,
110 line_index.offset(line_col)
114 impl ConvWith for TextUnit {
115 type Ctx = LineIndex;
116 type Output = Position;
118 fn conv_with(self, line_index: &LineIndex) -> Position {
119 let line_col = line_index.line_col(self);
120 Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16))
124 impl ConvWith for TextRange {
125 type Ctx = LineIndex;
128 fn conv_with(self, line_index: &LineIndex) -> Range {
130 self.start().conv_with(line_index),
131 self.end().conv_with(line_index),
136 impl ConvWith for Range {
137 type Ctx = LineIndex;
138 type Output = TextRange;
140 fn conv_with(self, line_index: &LineIndex) -> TextRange {
142 self.start.conv_with(line_index),
143 self.end.conv_with(line_index),
148 impl ConvWith for TextEdit {
149 type Ctx = LineIndex;
150 type Output = Vec<languageserver_types::TextEdit>;
152 fn conv_with(self, line_index: &LineIndex) -> Vec<languageserver_types::TextEdit> {
155 .map_conv_with(line_index)
160 impl<'a> ConvWith for &'a AtomTextEdit {
161 type Ctx = LineIndex;
162 type Output = languageserver_types::TextEdit;
164 fn conv_with(self, line_index: &LineIndex) -> languageserver_types::TextEdit {
165 languageserver_types::TextEdit {
166 range: self.delete.conv_with(line_index),
167 new_text: self.insert.clone(),
172 impl<T: ConvWith> ConvWith for Option<T> {
173 type Ctx = <T as ConvWith>::Ctx;
174 type Output = Option<<T as ConvWith>::Output>;
175 fn conv_with(self, ctx: &Self::Ctx) -> Self::Output {
176 self.map(|x| ConvWith::conv_with(x, ctx))
180 impl<'a> TryConvWith for &'a Url {
181 type Ctx = ServerWorld;
182 type Output = FileId;
183 fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
184 world.uri_to_file_id(self)
188 impl TryConvWith for FileId {
189 type Ctx = ServerWorld;
191 fn try_conv_with(self, world: &ServerWorld) -> Result<Url> {
192 world.file_id_to_uri(self)
196 impl<'a> TryConvWith for &'a TextDocumentItem {
197 type Ctx = ServerWorld;
198 type Output = FileId;
199 fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
200 self.uri.try_conv_with(world)
204 impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier {
205 type Ctx = ServerWorld;
206 type Output = FileId;
207 fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
208 self.uri.try_conv_with(world)
212 impl<'a> TryConvWith for &'a TextDocumentIdentifier {
213 type Ctx = ServerWorld;
214 type Output = FileId;
215 fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> {
216 world.uri_to_file_id(&self.uri)
220 impl<'a> TryConvWith for &'a TextDocumentPositionParams {
221 type Ctx = ServerWorld;
222 type Output = FilePosition;
223 fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> {
224 let file_id = self.text_document.try_conv_with(world)?;
225 let line_index = world.analysis().file_line_index(file_id);
226 let offset = self.position.conv_with(&line_index);
227 Ok(FilePosition { file_id, offset })
231 impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) {
232 type Ctx = ServerWorld;
233 type Output = FileRange;
234 fn try_conv_with(self, world: &ServerWorld) -> Result<FileRange> {
235 let file_id = self.0.try_conv_with(world)?;
236 let line_index = world.analysis().file_line_index(file_id);
237 let range = self.1.conv_with(&line_index);
238 Ok(FileRange { file_id, range })
242 impl<T: TryConvWith> TryConvWith for Vec<T> {
243 type Ctx = <T as TryConvWith>::Ctx;
244 type Output = Vec<<T as TryConvWith>::Output>;
245 fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> {
246 let mut res = Vec::with_capacity(self.len());
248 res.push(item.try_conv_with(ctx)?);
254 impl TryConvWith for SourceChange {
255 type Ctx = ServerWorld;
256 type Output = req::SourceChange;
257 fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> {
258 let cursor_position = match self.cursor_position {
261 let line_index = world.analysis().file_line_index(pos.file_id);
265 .find(|it| it.file_id == pos.file_id)
267 let line_col = match edit {
268 Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit),
269 None => line_index.line_col(pos.offset),
272 Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
273 Some(TextDocumentPositionParams {
274 text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
279 let mut document_changes: Vec<DocumentChangeOperation> = Vec::new();
280 for resource_op in self.file_system_edits.try_conv_with(world)? {
281 document_changes.push(DocumentChangeOperation::Op(resource_op));
283 for text_document_edit in self.source_file_edits.try_conv_with(world)? {
284 document_changes.push(DocumentChangeOperation::Edit(text_document_edit));
286 let workspace_edit = WorkspaceEdit {
288 document_changes: Some(DocumentChanges::Operations(document_changes)),
290 Ok(req::SourceChange {
298 impl TryConvWith for SourceFileEdit {
299 type Ctx = ServerWorld;
300 type Output = TextDocumentEdit;
301 fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> {
302 let text_document = VersionedTextDocumentIdentifier {
303 uri: self.file_id.try_conv_with(world)?,
306 let line_index = world.analysis().file_line_index(self.file_id);
311 .map_conv_with(&line_index)
313 Ok(TextDocumentEdit {
320 impl TryConvWith for FileSystemEdit {
321 type Ctx = ServerWorld;
322 type Output = ResourceOp;
323 fn try_conv_with(self, world: &ServerWorld) -> Result<ResourceOp> {
324 let res = match self {
325 FileSystemEdit::CreateFile { source_root, path } => {
326 let uri = world.path_to_uri(source_root, &path)?.to_string();
327 ResourceOp::Create(CreateFile { uri, options: None })
329 FileSystemEdit::MoveFile {
334 let old_uri = world.file_id_to_uri(src)?.to_string();
335 let new_uri = world.path_to_uri(dst_source_root, &dst_path)?.to_string();
336 ResourceOp::Rename(RenameFile {
347 impl TryConvWith for &NavigationTarget {
348 type Ctx = ServerWorld;
349 type Output = Location;
350 fn try_conv_with(self, world: &ServerWorld) -> Result<Location> {
351 let line_index = world.analysis().file_line_index(self.file_id());
352 let range = self.focus_range().unwrap_or(self.full_range());
353 to_location(self.file_id(), range, &world, &line_index)
357 pub fn to_location_link(
358 target: &RangeInfo<NavigationTarget>,
360 // line index for original range file
361 line_index: &LineIndex,
362 ) -> Result<LocationLink> {
363 let url = target.info.file_id().try_conv_with(world)?;
364 let tgt_line_index = world.analysis().file_line_index(target.info.file_id());
366 let res = LocationLink {
367 origin_selection_range: Some(target.range.conv_with(line_index)),
368 target_uri: url.to_string(),
369 target_range: target.info.full_range().conv_with(&tgt_line_index),
370 target_selection_range: target
373 .map(|it| it.conv_with(&tgt_line_index)),
382 line_index: &LineIndex,
383 ) -> Result<Location> {
384 let url = file_id.try_conv_with(world)?;
385 let loc = Location::new(url, range.conv_with(line_index));
389 pub trait MapConvWith<'a>: Sized + 'a {
393 fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> {
394 ConvWithIter { iter: self, ctx }
398 impl<'a, I> MapConvWith<'a> for I
403 type Ctx = <I::Item as ConvWith>::Ctx;
404 type Output = <I::Item as ConvWith>::Output;
407 pub struct ConvWithIter<'a, I, Ctx: 'a> {
412 impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
415 I::Item: ConvWith<Ctx = Ctx>,
417 type Item = <I::Item as ConvWith>::Output;
419 fn next(&mut self) -> Option<Self::Item> {
420 self.iter.next().map(|item| item.conv_with(self.ctx))