diff options
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r-- | crates/ra_lsp_server/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 78 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 4 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 58 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 39 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/server_world.rs | 11 |
7 files changed, 139 insertions, 54 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index fc10096e5..3c8c240cd 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -12,7 +12,6 @@ failure = "0.1.2" | |||
12 | failure_derive = "0.1.2" | 12 | failure_derive = "0.1.2" |
13 | serde_json = "1.0.24" | 13 | serde_json = "1.0.24" |
14 | serde = "1.0.71" | 14 | serde = "1.0.71" |
15 | serde_derive = "1.0.71" | ||
16 | drop_bomb = "0.1.0" | 15 | drop_bomb = "0.1.0" |
17 | crossbeam-channel = "0.2.4" | 16 | crossbeam-channel = "0.2.4" |
18 | flexi_logger = "0.10.0" | 17 | flexi_logger = "0.10.0" |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 7467f472c..051f1f995 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | use languageserver_types::{ | 1 | use languageserver_types::{ |
2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, | 2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, |
3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, | 3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, |
4 | }; | 4 | }; |
5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileNodeEdit, FilePosition}; | 5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition, CompletionItem, CompletionItemKind, InsertText}; |
6 | use ra_editor::{LineCol, LineIndex}; | 6 | use ra_editor::{LineCol, LineIndex}; |
7 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 7 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
@@ -45,6 +45,46 @@ impl Conv for SyntaxKind { | |||
45 | } | 45 | } |
46 | } | 46 | } |
47 | 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 | |||
48 | impl ConvWith for Position { | 88 | impl ConvWith for Position { |
49 | type Ctx = LineIndex; | 89 | type Ctx = LineIndex; |
50 | type Output = TextUnit; | 90 | type Output = TextUnit; |
@@ -97,21 +137,21 @@ impl ConvWith for TextEdit { | |||
97 | type Output = Vec<languageserver_types::TextEdit>; | 137 | type Output = Vec<languageserver_types::TextEdit>; |
98 | 138 | ||
99 | fn conv_with(self, line_index: &LineIndex) -> Vec<languageserver_types::TextEdit> { | 139 | fn conv_with(self, line_index: &LineIndex) -> Vec<languageserver_types::TextEdit> { |
100 | self.into_atoms() | 140 | self.as_atoms() |
101 | .into_iter() | 141 | .into_iter() |
102 | .map_conv_with(line_index) | 142 | .map_conv_with(line_index) |
103 | .collect() | 143 | .collect() |
104 | } | 144 | } |
105 | } | 145 | } |
106 | 146 | ||
107 | impl ConvWith for AtomTextEdit { | 147 | impl<'a> ConvWith for &'a AtomTextEdit { |
108 | type Ctx = LineIndex; | 148 | type Ctx = LineIndex; |
109 | type Output = languageserver_types::TextEdit; | 149 | type Output = languageserver_types::TextEdit; |
110 | 150 | ||
111 | fn conv_with(self, line_index: &LineIndex) -> languageserver_types::TextEdit { | 151 | fn conv_with(self, line_index: &LineIndex) -> languageserver_types::TextEdit { |
112 | languageserver_types::TextEdit { | 152 | languageserver_types::TextEdit { |
113 | range: self.delete.conv_with(line_index), | 153 | range: self.delete.conv_with(line_index), |
114 | new_text: self.insert, | 154 | new_text: self.insert.clone(), |
115 | } | 155 | } |
116 | } | 156 | } |
117 | } | 157 | } |
@@ -199,7 +239,7 @@ impl TryConvWith for SourceChange { | |||
199 | .source_file_edits | 239 | .source_file_edits |
200 | .iter() | 240 | .iter() |
201 | .find(|it| it.file_id == pos.file_id) | 241 | .find(|it| it.file_id == pos.file_id) |
202 | .map(|it| it.edits.as_slice()) | 242 | .map(|it| it.edit.as_atoms()) |
203 | .unwrap_or(&[]); | 243 | .unwrap_or(&[]); |
204 | let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); | 244 | let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); |
205 | let position = | 245 | let position = |
@@ -256,7 +296,7 @@ fn translate_offset_with_edit( | |||
256 | } | 296 | } |
257 | } | 297 | } |
258 | 298 | ||
259 | impl TryConvWith for SourceFileNodeEdit { | 299 | impl TryConvWith for SourceFileEdit { |
260 | type Ctx = ServerWorld; | 300 | type Ctx = ServerWorld; |
261 | type Output = TextDocumentEdit; | 301 | type Output = TextDocumentEdit; |
262 | fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> { | 302 | fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> { |
@@ -265,7 +305,12 @@ impl TryConvWith for SourceFileNodeEdit { | |||
265 | version: None, | 305 | version: None, |
266 | }; | 306 | }; |
267 | let line_index = world.analysis().file_line_index(self.file_id); | 307 | let line_index = world.analysis().file_line_index(self.file_id); |
268 | let edits = self.edits.into_iter().map_conv_with(&line_index).collect(); | 308 | let edits = self |
309 | .edit | ||
310 | .as_atoms() | ||
311 | .iter() | ||
312 | .map_conv_with(&line_index) | ||
313 | .collect(); | ||
269 | Ok(TextDocumentEdit { | 314 | Ok(TextDocumentEdit { |
270 | text_document, | 315 | text_document, |
271 | edits, | 316 | edits, |
@@ -278,16 +323,17 @@ impl TryConvWith for FileSystemEdit { | |||
278 | type Output = req::FileSystemEdit; | 323 | type Output = req::FileSystemEdit; |
279 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::FileSystemEdit> { | 324 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::FileSystemEdit> { |
280 | let res = match self { | 325 | let res = match self { |
281 | FileSystemEdit::CreateFile { anchor, path } => { | 326 | FileSystemEdit::CreateFile { source_root, path } => { |
282 | let uri = world.file_id_to_uri(anchor)?; | 327 | let uri = world.path_to_uri(source_root, &path)?; |
283 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | ||
284 | let uri = uri.join(path)?; | ||
285 | req::FileSystemEdit::CreateFile { uri } | 328 | req::FileSystemEdit::CreateFile { uri } |
286 | } | 329 | } |
287 | FileSystemEdit::MoveFile { file, path } => { | 330 | FileSystemEdit::MoveFile { |
288 | let src = world.file_id_to_uri(file)?; | 331 | src, |
289 | let path = &path.as_str()[3..]; // strip `../` b/c url is weird | 332 | dst_source_root, |
290 | let dst = src.join(path)?; | 333 | dst_path, |
334 | } => { | ||
335 | let src = world.file_id_to_uri(src)?; | ||
336 | let dst = world.path_to_uri(dst_source_root, &dst_path)?; | ||
291 | req::FileSystemEdit::MoveFile { src, dst } | 337 | req::FileSystemEdit::MoveFile { src, dst } |
292 | } | 338 | } |
293 | }; | 339 | }; |
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index 721665eed..33aa30d70 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use serde_derive::Deserialize; | 1 | use serde::Deserialize; |
2 | use serde::Deserialize as _D; | ||
3 | use flexi_logger::{Duplicate, Logger}; | 2 | use flexi_logger::{Duplicate, Logger}; |
4 | use gen_lsp_server::{run_server, stdio_transport}; | 3 | use gen_lsp_server::{run_server, stdio_transport}; |
4 | |||
5 | use ra_lsp_server::Result; | 5 | use ra_lsp_server::Result; |
6 | 6 | ||
7 | fn main() -> Result<()> { | 7 | fn main() -> Result<()> { |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 8b787c329..a5a2b5eec 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -2,6 +2,7 @@ mod handlers; | |||
2 | mod subscriptions; | 2 | mod subscriptions; |
3 | 3 | ||
4 | use std::{ | 4 | use std::{ |
5 | fmt, | ||
5 | path::PathBuf, | 6 | path::PathBuf, |
6 | sync::Arc, | 7 | sync::Arc, |
7 | }; | 8 | }; |
@@ -109,6 +110,50 @@ pub fn main_loop( | |||
109 | Ok(()) | 110 | Ok(()) |
110 | } | 111 | } |
111 | 112 | ||
113 | enum Event { | ||
114 | Msg(RawMessage), | ||
115 | Task(Task), | ||
116 | Vfs(VfsTask), | ||
117 | Lib(LibraryData), | ||
118 | } | ||
119 | |||
120 | impl fmt::Debug for Event { | ||
121 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
122 | let debug_verbose_not = |not: &RawNotification, f: &mut fmt::Formatter| { | ||
123 | f.debug_struct("RawNotification") | ||
124 | .field("method", ¬.method) | ||
125 | .finish() | ||
126 | }; | ||
127 | |||
128 | match self { | ||
129 | Event::Msg(RawMessage::Notification(not)) => { | ||
130 | if not.is::<req::DidOpenTextDocument>() || not.is::<req::DidChangeTextDocument>() { | ||
131 | return debug_verbose_not(not, f); | ||
132 | } | ||
133 | } | ||
134 | Event::Task(Task::Notify(not)) => { | ||
135 | if not.is::<req::PublishDecorations>() || not.is::<req::PublishDiagnostics>() { | ||
136 | return debug_verbose_not(not, f); | ||
137 | } | ||
138 | } | ||
139 | Event::Task(Task::Respond(resp)) => { | ||
140 | return f | ||
141 | .debug_struct("RawResponse") | ||
142 | .field("id", &resp.id) | ||
143 | .field("error", &resp.error) | ||
144 | .finish(); | ||
145 | } | ||
146 | _ => (), | ||
147 | } | ||
148 | match self { | ||
149 | Event::Msg(it) => fmt::Debug::fmt(it, f), | ||
150 | Event::Task(it) => fmt::Debug::fmt(it, f), | ||
151 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | ||
152 | Event::Lib(it) => fmt::Debug::fmt(it, f), | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
112 | fn main_loop_inner( | 157 | fn main_loop_inner( |
113 | internal_mode: bool, | 158 | internal_mode: bool, |
114 | supports_decorations: bool, | 159 | supports_decorations: bool, |
@@ -123,13 +168,6 @@ fn main_loop_inner( | |||
123 | ) -> Result<()> { | 168 | ) -> Result<()> { |
124 | let (libdata_sender, libdata_receiver) = unbounded(); | 169 | let (libdata_sender, libdata_receiver) = unbounded(); |
125 | loop { | 170 | loop { |
126 | #[derive(Debug)] | ||
127 | enum Event { | ||
128 | Msg(RawMessage), | ||
129 | Task(Task), | ||
130 | Vfs(VfsTask), | ||
131 | Lib(LibraryData), | ||
132 | } | ||
133 | log::trace!("selecting"); | 171 | log::trace!("selecting"); |
134 | let event = select! { | 172 | let event = select! { |
135 | recv(msg_receiver, msg) => match msg { | 173 | recv(msg_receiver, msg) => match msg { |
@@ -143,7 +181,8 @@ fn main_loop_inner( | |||
143 | } | 181 | } |
144 | recv(libdata_receiver, data) => Event::Lib(data.unwrap()) | 182 | recv(libdata_receiver, data) => Event::Lib(data.unwrap()) |
145 | }; | 183 | }; |
146 | log::info!("{:?}", event); | 184 | log::info!("loop_turn = {:?}", event); |
185 | let start = std::time::Instant::now(); | ||
147 | let mut state_changed = false; | 186 | let mut state_changed = false; |
148 | match event { | 187 | match event { |
149 | Event::Task(task) => on_task(task, msg_sender, pending_requests), | 188 | Event::Task(task) => on_task(task, msg_sender, pending_requests), |
@@ -206,6 +245,7 @@ fn main_loop_inner( | |||
206 | subs.subscriptions(), | 245 | subs.subscriptions(), |
207 | ) | 246 | ) |
208 | } | 247 | } |
248 | log::info!("loop_turn = {:?}", start.elapsed()); | ||
209 | } | 249 | } |
210 | } | 250 | } |
211 | 251 | ||
@@ -387,7 +427,7 @@ impl<'a> PoolDispatcher<'a> { | |||
387 | RawResponse::err( | 427 | RawResponse::err( |
388 | id, | 428 | id, |
389 | ErrorCode::ContentModified as i32, | 429 | ErrorCode::ContentModified as i32, |
390 | e.to_string(), | 430 | format!("content modified: {}", e), |
391 | ) | 431 | ) |
392 | } else { | 432 | } else { |
393 | RawResponse::err( | 433 | RawResponse::err( |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 572ae7fb5..252d1ba3e 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -2,9 +2,9 @@ use std::collections::HashMap; | |||
2 | 2 | ||
3 | use gen_lsp_server::ErrorCode; | 3 | use gen_lsp_server::ErrorCode; |
4 | use languageserver_types::{ | 4 | use languageserver_types::{ |
5 | CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic, | 5 | CodeActionResponse, Command, Diagnostic, |
6 | DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, | 6 | DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, |
7 | FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, MarkedString, Position, | 7 | FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, |
8 | PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, | 8 | PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, |
9 | WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, | 9 | WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, |
10 | }; | 10 | }; |
@@ -107,9 +107,16 @@ pub fn handle_on_type_formatting( | |||
107 | }; | 107 | }; |
108 | let edits = match world.analysis().on_eq_typed(position) { | 108 | let edits = match world.analysis().on_eq_typed(position) { |
109 | None => return Ok(None), | 109 | None => return Ok(None), |
110 | Some(mut action) => action.source_file_edits.pop().unwrap().edits, | 110 | Some(mut action) => action |
111 | .source_file_edits | ||
112 | .pop() | ||
113 | .unwrap() | ||
114 | .edit | ||
115 | .as_atoms() | ||
116 | .iter() | ||
117 | .map_conv_with(&line_index) | ||
118 | .collect(), | ||
111 | }; | 119 | }; |
112 | let edits = edits.into_iter().map_conv_with(&line_index).collect(); | ||
113 | Ok(Some(edits)) | 120 | Ok(Some(edits)) |
114 | } | 121 | } |
115 | 122 | ||
@@ -412,22 +419,7 @@ pub fn handle_completion( | |||
412 | None => return Ok(None), | 419 | None => return Ok(None), |
413 | Some(items) => items, | 420 | Some(items) => items, |
414 | }; | 421 | }; |
415 | let items = items | 422 | let items = items.into_iter().map(|item| item.conv()).collect(); |
416 | .into_iter() | ||
417 | .map(|item| { | ||
418 | let mut res = CompletionItem { | ||
419 | label: item.label, | ||
420 | filter_text: item.lookup, | ||
421 | ..Default::default() | ||
422 | }; | ||
423 | if let Some(snip) = item.snippet { | ||
424 | res.insert_text = Some(snip); | ||
425 | res.insert_text_format = Some(InsertTextFormat::Snippet); | ||
426 | res.kind = Some(CompletionItemKind::Keyword); | ||
427 | }; | ||
428 | res | ||
429 | }) | ||
430 | .collect(); | ||
431 | 423 | ||
432 | Ok(Some(req::CompletionResponse::Array(items))) | 424 | Ok(Some(req::CompletionResponse::Array(items))) |
433 | } | 425 | } |
@@ -446,8 +438,9 @@ pub fn handle_folding_range( | |||
446 | .into_iter() | 438 | .into_iter() |
447 | .map(|fold| { | 439 | .map(|fold| { |
448 | let kind = match fold.kind { | 440 | let kind = match fold.kind { |
449 | FoldKind::Comment => FoldingRangeKind::Comment, | 441 | FoldKind::Comment => Some(FoldingRangeKind::Comment), |
450 | FoldKind::Imports => FoldingRangeKind::Imports, | 442 | FoldKind::Imports => Some(FoldingRangeKind::Imports), |
443 | FoldKind::Block => None, | ||
451 | }; | 444 | }; |
452 | let range = fold.range.conv_with(&line_index); | 445 | let range = fold.range.conv_with(&line_index); |
453 | FoldingRange { | 446 | FoldingRange { |
@@ -455,7 +448,7 @@ pub fn handle_folding_range( | |||
455 | start_character: Some(range.start.character), | 448 | start_character: Some(range.start.character), |
456 | end_line: range.end.line, | 449 | end_line: range.end.line, |
457 | end_character: Some(range.start.character), | 450 | end_character: Some(range.start.character), |
458 | kind: Some(kind), | 451 | kind, |
459 | } | 452 | } |
460 | }) | 453 | }) |
461 | .collect(), | 454 | .collect(), |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 999792ecb..747ab8a8c 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use serde_derive::{Serialize, Deserialize}; | 1 | use serde::{Serialize, Deserialize}; |
2 | use languageserver_types::{Location, Position, Range, TextDocumentIdentifier, Url}; | 2 | use languageserver_types::{Location, Position, Range, TextDocumentIdentifier, Url}; |
3 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
4 | use url_serde; | 4 | use url_serde; |
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 785877c4b..c183c25af 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -8,7 +8,7 @@ use ra_analysis::{ | |||
8 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, | 8 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, |
9 | SourceRootId | 9 | SourceRootId |
10 | }; | 10 | }; |
11 | use ra_vfs::{Vfs, VfsChange, VfsFile}; | 11 | use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; |
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use relative_path::RelativePathBuf; | 13 | use relative_path::RelativePathBuf; |
14 | use parking_lot::RwLock; | 14 | use parking_lot::RwLock; |
@@ -107,7 +107,6 @@ impl ServerWorldState { | |||
107 | let mut libs = Vec::new(); | 107 | let mut libs = Vec::new(); |
108 | let mut change = AnalysisChange::new(); | 108 | let mut change = AnalysisChange::new(); |
109 | for c in changes { | 109 | for c in changes { |
110 | log::info!("vfs change {:?}", c); | ||
111 | match c { | 110 | match c { |
112 | VfsChange::AddRoot { root, files } => { | 111 | VfsChange::AddRoot { root, files } => { |
113 | let root_path = self.vfs.read().root2path(root); | 112 | let root_path = self.vfs.read().root2path(root); |
@@ -183,4 +182,12 @@ impl ServerWorld { | |||
183 | .map_err(|_| format_err!("can't convert path to url: {}", path.display()))?; | 182 | .map_err(|_| format_err!("can't convert path to url: {}", path.display()))?; |
184 | Ok(url) | 183 | Ok(url) |
185 | } | 184 | } |
185 | |||
186 | pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> { | ||
187 | let base = self.vfs.read().root2path(VfsRoot(root.0)); | ||
188 | let path = path.to_path(base); | ||
189 | let url = Url::from_file_path(&path) | ||
190 | .map_err(|_| format_err!("can't convert path to url: {}", path.display()))?; | ||
191 | Ok(url) | ||
192 | } | ||
186 | } | 193 | } |