aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_lsp_server/src')
-rw-r--r--crates/ra_lsp_server/src/caps.rs16
-rw-r--r--crates/ra_lsp_server/src/conv.rs66
-rw-r--r--crates/ra_lsp_server/src/lib.rs21
-rw-r--r--crates/ra_lsp_server/src/main.rs6
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs206
-rw-r--r--crates/ra_lsp_server/src/main_loop/mod.rs161
-rw-r--r--crates/ra_lsp_server/src/main_loop/subscriptions.rs6
-rw-r--r--crates/ra_lsp_server/src/path_map.rs28
-rw-r--r--crates/ra_lsp_server/src/project_model.rs32
-rw-r--r--crates/ra_lsp_server/src/req.rs27
-rw-r--r--crates/ra_lsp_server/src/server_world.rs76
-rw-r--r--crates/ra_lsp_server/src/thread_watcher.rs19
-rw-r--r--crates/ra_lsp_server/src/vfs.rs12
13 files changed, 350 insertions, 326 deletions
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index 5598ec75f..1dd495791 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -1,14 +1,8 @@
1use languageserver_types::{ 1use languageserver_types::{
2 ServerCapabilities, 2 CodeActionProviderCapability, CompletionOptions, DocumentOnTypeFormattingOptions,
3 CodeActionProviderCapability, 3 ExecuteCommandOptions, FoldingRangeProviderCapability, ServerCapabilities,
4 FoldingRangeProviderCapability, 4 SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
5 TextDocumentSyncCapability,
6 TextDocumentSyncOptions, 5 TextDocumentSyncOptions,
7 TextDocumentSyncKind,
8 ExecuteCommandOptions,
9 CompletionOptions,
10 SignatureHelpOptions,
11 DocumentOnTypeFormattingOptions,
12}; 6};
13 7
14pub fn server_capabilities() -> ServerCapabilities { 8pub fn server_capabilities() -> ServerCapabilities {
@@ -20,7 +14,7 @@ pub fn server_capabilities() -> ServerCapabilities {
20 will_save: None, 14 will_save: None,
21 will_save_wait_until: None, 15 will_save_wait_until: None,
22 save: None, 16 save: None,
23 } 17 },
24 )), 18 )),
25 hover_provider: None, 19 hover_provider: None,
26 completion_provider: Some(CompletionOptions { 20 completion_provider: Some(CompletionOptions {
@@ -28,7 +22,7 @@ pub fn server_capabilities() -> ServerCapabilities {
28 trigger_characters: None, 22 trigger_characters: None,
29 }), 23 }),
30 signature_help_provider: Some(SignatureHelpOptions { 24 signature_help_provider: Some(SignatureHelpOptions {
31 trigger_characters: Some(vec!["(".to_string(), ",".to_string()]) 25 trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
32 }), 26 }),
33 definition_provider: Some(true), 27 definition_provider: Some(true),
34 type_definition_provider: None, 28 type_definition_provider: None,
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index a75b160c5..8325e8c1e 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -1,17 +1,12 @@
1use languageserver_types::{ 1use languageserver_types::{
2 Range, SymbolKind, Position, TextEdit, Location, Url, 2 Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
3 TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentItem, 3 TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, VersionedTextDocumentIdentifier,
4 TextDocumentPositionParams, TextDocumentEdit,
5};
6use ra_editor::{LineIndex, LineCol, Edit, AtomEdit};
7use ra_syntax::{SyntaxKind, TextUnit, TextRange};
8use ra_analysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit};
9
10use crate::{
11 Result,
12 server_world::ServerWorld,
13 req,
14}; 4};
5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit};
6use ra_editor::{AtomEdit, Edit, LineCol, LineIndex};
7use ra_syntax::{SyntaxKind, TextRange, TextUnit};
8
9use crate::{req, server_world::ServerWorld, Result};
15 10
16pub trait Conv { 11pub trait Conv {
17 type Output; 12 type Output;
@@ -190,8 +185,12 @@ impl TryConvWith for SourceChange {
190 None => None, 185 None => None,
191 Some(pos) => { 186 Some(pos) => {
192 let line_index = world.analysis().file_line_index(pos.file_id); 187 let line_index = world.analysis().file_line_index(pos.file_id);
193 let edits = self.source_file_edits.iter().find(|it| it.file_id == pos.file_id) 188 let edits = self
194 .map(|it| it.edits.as_slice()).unwrap_or(&[]); 189 .source_file_edits
190 .iter()
191 .find(|it| it.file_id == pos.file_id)
192 .map(|it| it.edits.as_slice())
193 .unwrap_or(&[]);
195 let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); 194 let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits);
196 let position = Position::new(line_col.line as u64, u32::from(line_col.col) as u64); 195 let position = Position::new(line_col.line as u64, u32::from(line_col.col) as u64);
197 Some(TextDocumentPositionParams { 196 Some(TextDocumentPositionParams {
@@ -224,11 +223,11 @@ fn translate_offset_with_edit(
224 let fallback = pre_edit_index.line_col(offset); 223 let fallback = pre_edit_index.line_col(offset);
225 let edit = match edits.first() { 224 let edit = match edits.first() {
226 None => return fallback, 225 None => return fallback,
227 Some(edit) => edit 226 Some(edit) => edit,
228 }; 227 };
229 let end_offset = edit.delete.start() + TextUnit::of_str(&edit.insert); 228 let end_offset = edit.delete.start() + TextUnit::of_str(&edit.insert);
230 if !(edit.delete.start() <= offset && offset <= end_offset) { 229 if !(edit.delete.start() <= offset && offset <= end_offset) {
231 return fallback 230 return fallback;
232 } 231 }
233 let rel_offset = offset - edit.delete.start(); 232 let rel_offset = offset - edit.delete.start();
234 let in_edit_line_col = LineIndex::new(&edit.insert).line_col(rel_offset); 233 let in_edit_line_col = LineIndex::new(&edit.insert).line_col(rel_offset);
@@ -255,11 +254,11 @@ impl TryConvWith for SourceFileEdit {
255 version: None, 254 version: None,
256 }; 255 };
257 let line_index = world.analysis().file_line_index(self.file_id); 256 let line_index = world.analysis().file_line_index(self.file_id);
258 let edits = self.edits 257 let edits = self.edits.into_iter().map_conv_with(&line_index).collect();
259 .into_iter() 258 Ok(TextDocumentEdit {
260 .map_conv_with(&line_index) 259 text_document,
261 .collect(); 260 edits,
262 Ok(TextDocumentEdit { text_document, edits }) 261 })
263 } 262 }
264} 263}
265 264
@@ -273,13 +272,13 @@ impl TryConvWith for FileSystemEdit {
273 let path = &path.as_str()[3..]; // strip `../` b/c url is weird 272 let path = &path.as_str()[3..]; // strip `../` b/c url is weird
274 let uri = uri.join(path)?; 273 let uri = uri.join(path)?;
275 req::FileSystemEdit::CreateFile { uri } 274 req::FileSystemEdit::CreateFile { uri }
276 }, 275 }
277 FileSystemEdit::MoveFile { file, path } => { 276 FileSystemEdit::MoveFile { file, path } => {
278 let src = world.file_id_to_uri(file)?; 277 let src = world.file_id_to_uri(file)?;
279 let path = &path.as_str()[3..]; // strip `../` b/c url is weird 278 let path = &path.as_str()[3..]; // strip `../` b/c url is weird
280 let dst = src.join(path)?; 279 let dst = src.join(path)?;
281 req::FileSystemEdit::MoveFile { src, dst } 280 req::FileSystemEdit::MoveFile { src, dst }
282 }, 281 }
283 }; 282 };
284 Ok(res) 283 Ok(res)
285 } 284 }
@@ -291,12 +290,9 @@ pub fn to_location(
291 world: &ServerWorld, 290 world: &ServerWorld,
292 line_index: &LineIndex, 291 line_index: &LineIndex,
293) -> Result<Location> { 292) -> Result<Location> {
294 let url = file_id.try_conv_with(world)?; 293 let url = file_id.try_conv_with(world)?;
295 let loc = Location::new( 294 let loc = Location::new(url, range.conv_with(line_index));
296 url, 295 Ok(loc)
297 range.conv_with(line_index),
298 );
299 Ok(loc)
300} 296}
301 297
302pub trait MapConvWith<'a>: Sized + 'a { 298pub trait MapConvWith<'a>: Sized + 'a {
@@ -309,8 +305,9 @@ pub trait MapConvWith<'a>: Sized + 'a {
309} 305}
310 306
311impl<'a, I> MapConvWith<'a> for I 307impl<'a, I> MapConvWith<'a> for I
312 where I: Iterator + 'a, 308where
313 I::Item: ConvWith 309 I: Iterator + 'a,
310 I::Item: ConvWith,
314{ 311{
315 type Ctx = <I::Item as ConvWith>::Ctx; 312 type Ctx = <I::Item as ConvWith>::Ctx;
316 type Output = <I::Item as ConvWith>::Output; 313 type Output = <I::Item as ConvWith>::Output;
@@ -322,9 +319,9 @@ pub struct ConvWithIter<'a, I, Ctx: 'a> {
322} 319}
323 320
324impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx> 321impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
325 where 322where
326 I: Iterator, 323 I: Iterator,
327 I::Item: ConvWith<Ctx=Ctx>, 324 I::Item: ConvWith<Ctx = Ctx>,
328{ 325{
329 type Item = <I::Item as ConvWith>::Output; 326 type Item = <I::Item as ConvWith>::Output;
330 327
@@ -332,4 +329,3 @@ impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx>
332 self.iter.next().map(|item| item.conv_with(self.ctx)) 329 self.iter.next().map(|item| item.conv_with(self.ctx))
333 } 330 }
334} 331}
335
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs
index 7224b1476..f1b17f282 100644
--- a/crates/ra_lsp_server/src/lib.rs
+++ b/crates/ra_lsp_server/src/lib.rs
@@ -2,39 +2,36 @@
2extern crate failure; 2extern crate failure;
3#[macro_use] 3#[macro_use]
4extern crate serde_derive; 4extern crate serde_derive;
5extern crate languageserver_types;
5extern crate serde; 6extern crate serde;
6extern crate serde_json; 7extern crate serde_json;
7extern crate languageserver_types;
8#[macro_use] 8#[macro_use]
9extern crate crossbeam_channel; 9extern crate crossbeam_channel;
10extern crate rayon; 10extern crate rayon;
11#[macro_use] 11#[macro_use]
12extern crate log; 12extern crate log;
13extern crate cargo_metadata;
13extern crate drop_bomb; 14extern crate drop_bomb;
14extern crate url_serde;
15extern crate walkdir;
16extern crate im; 15extern crate im;
17extern crate relative_path; 16extern crate relative_path;
18extern crate cargo_metadata;
19extern crate rustc_hash; 17extern crate rustc_hash;
18extern crate url_serde;
19extern crate walkdir;
20 20
21extern crate gen_lsp_server; 21extern crate gen_lsp_server;
22extern crate ra_editor;
23extern crate ra_analysis; 22extern crate ra_analysis;
23extern crate ra_editor;
24extern crate ra_syntax; 24extern crate ra_syntax;
25 25
26mod caps; 26mod caps;
27pub mod req;
28mod conv; 27mod conv;
29mod main_loop; 28mod main_loop;
30mod vfs;
31mod path_map; 29mod path_map;
32mod server_world;
33mod project_model; 30mod project_model;
31pub mod req;
32mod server_world;
34pub mod thread_watcher; 33pub mod thread_watcher;
34mod vfs;
35 35
36pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 36pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
37pub use crate::{ 37pub use crate::{caps::server_capabilities, main_loop::main_loop};
38 main_loop::main_loop,
39 caps::server_capabilities,
40};
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index e5d1792b7..9f62347f1 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -6,7 +6,7 @@ extern crate flexi_logger;
6extern crate gen_lsp_server; 6extern crate gen_lsp_server;
7extern crate ra_lsp_server; 7extern crate ra_lsp_server;
8 8
9use flexi_logger::{Logger, Duplicate}; 9use flexi_logger::{Duplicate, Logger};
10use gen_lsp_server::{run_server, stdio_transport}; 10use gen_lsp_server::{run_server, stdio_transport};
11use ra_lsp_server::Result; 11use ra_lsp_server::Result;
12 12
@@ -38,7 +38,8 @@ fn main_inner() -> Result<()> {
38 receiver, 38 receiver,
39 sender, 39 sender,
40 |params, r, s| { 40 |params, r, s| {
41 let root = params.root_uri 41 let root = params
42 .root_uri
42 .and_then(|it| it.to_file_path().ok()) 43 .and_then(|it| it.to_file_path().ok())
43 .unwrap_or(cwd); 44 .unwrap_or(cwd);
44 ra_lsp_server::main_loop(false, root, r, s) 45 ra_lsp_server::main_loop(false, root, r, s)
@@ -49,4 +50,3 @@ fn main_inner() -> Result<()> {
49 info!("... IO is down"); 50 info!("... IO is down");
50 Ok(()) 51 Ok(())
51} 52}
52
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 5acb39b60..c25b63852 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -1,23 +1,20 @@
1use rustc_hash::FxHashMap; 1use rustc_hash::FxHashMap;
2 2
3use languageserver_types::{ 3use languageserver_types::{
4 Diagnostic, DiagnosticSeverity, DocumentSymbol, 4 CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic,
5 CodeActionResponse, Command, TextDocumentIdentifier, 5 DiagnosticSeverity, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams,
6 SymbolInformation, Position, Location, TextEdit, 6 InsertTextFormat, Location, Position, SymbolInformation, TextDocumentIdentifier, TextEdit,
7 CompletionItem, InsertTextFormat, CompletionItemKind,
8 FoldingRange, FoldingRangeParams, FoldingRangeKind
9}; 7};
8use ra_analysis::{FileId, FoldKind, JobToken, Query, RunnableKind};
9use ra_syntax::text_utils::contains_offset_nonstrict;
10use serde_json::to_value; 10use serde_json::to_value;
11use ra_analysis::{Query, FileId, RunnableKind, JobToken, FoldKind};
12use ra_syntax::{
13 text_utils::contains_offset_nonstrict
14};
15 11
16use crate::{ 12use crate::{
17 req::{self, Decoration}, Result, 13 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith},
18 conv::{Conv, ConvWith, TryConvWith, MapConvWith, to_location},
19 server_world::ServerWorld,
20 project_model::TargetKind, 14 project_model::TargetKind,
15 req::{self, Decoration},
16 server_world::ServerWorld,
17 Result,
21}; 18};
22 19
23pub fn handle_syntax_tree( 20pub fn handle_syntax_tree(
@@ -38,7 +35,9 @@ pub fn handle_extend_selection(
38 let file_id = params.text_document.try_conv_with(&world)?; 35 let file_id = params.text_document.try_conv_with(&world)?;
39 let file = world.analysis().file_syntax(file_id); 36 let file = world.analysis().file_syntax(file_id);
40 let line_index = world.analysis().file_line_index(file_id); 37 let line_index = world.analysis().file_line_index(file_id);
41 let selections = params.selections.into_iter() 38 let selections = params
39 .selections
40 .into_iter()
42 .map_conv_with(&line_index) 41 .map_conv_with(&line_index)
43 .map(|r| world.analysis().extend_selection(&file, r)) 42 .map(|r| world.analysis().extend_selection(&file, r))
44 .map_conv_with(&line_index) 43 .map_conv_with(&line_index)
@@ -54,11 +53,15 @@ pub fn handle_find_matching_brace(
54 let file_id = params.text_document.try_conv_with(&world)?; 53 let file_id = params.text_document.try_conv_with(&world)?;
55 let file = world.analysis().file_syntax(file_id); 54 let file = world.analysis().file_syntax(file_id);
56 let line_index = world.analysis().file_line_index(file_id); 55 let line_index = world.analysis().file_line_index(file_id);
57 let res = params.offsets 56 let res = params
57 .offsets
58 .into_iter() 58 .into_iter()
59 .map_conv_with(&line_index) 59 .map_conv_with(&line_index)
60 .map(|offset| { 60 .map(|offset| {
61 world.analysis().matching_brace(&file, offset).unwrap_or(offset) 61 world
62 .analysis()
63 .matching_brace(&file, offset)
64 .unwrap_or(offset)
62 }) 65 })
63 .map_conv_with(&line_index) 66 .map_conv_with(&line_index)
64 .collect(); 67 .collect();
@@ -73,7 +76,9 @@ pub fn handle_join_lines(
73 let file_id = params.text_document.try_conv_with(&world)?; 76 let file_id = params.text_document.try_conv_with(&world)?;
74 let line_index = world.analysis().file_line_index(file_id); 77 let line_index = world.analysis().file_line_index(file_id);
75 let range = params.range.conv_with(&line_index); 78 let range = params.range.conv_with(&line_index);
76 world.analysis().join_lines(file_id, range) 79 world
80 .analysis()
81 .join_lines(file_id, range)
77 .try_conv_with(&world) 82 .try_conv_with(&world)
78} 83}
79 84
@@ -87,7 +92,7 @@ pub fn handle_on_enter(
87 let offset = params.position.conv_with(&line_index); 92 let offset = params.position.conv_with(&line_index);
88 match world.analysis().on_enter(file_id, offset) { 93 match world.analysis().on_enter(file_id, offset) {
89 None => Ok(None), 94 None => Ok(None),
90 Some(edit) => Ok(Some(edit.try_conv_with(&world)?)) 95 Some(edit) => Ok(Some(edit.try_conv_with(&world)?)),
91 } 96 }
92} 97}
93 98
@@ -158,7 +163,9 @@ pub fn handle_workspace_symbol(
158 let all_symbols = params.query.contains("#"); 163 let all_symbols = params.query.contains("#");
159 let libs = params.query.contains("*"); 164 let libs = params.query.contains("*");
160 let query = { 165 let query = {
161 let query: String = params.query.chars() 166 let query: String = params
167 .query
168 .chars()
162 .filter(|&c| c != '#' && c != '*') 169 .filter(|&c| c != '#' && c != '*')
163 .collect(); 170 .collect();
164 let mut q = Query::new(query); 171 let mut q = Query::new(query);
@@ -180,22 +187,23 @@ pub fn handle_workspace_symbol(
180 187
181 return Ok(Some(res)); 188 return Ok(Some(res));
182 189
183 fn exec_query(world: &ServerWorld, query: Query, token: &JobToken) -> Result<Vec<SymbolInformation>> { 190 fn exec_query(
191 world: &ServerWorld,
192 query: Query,
193 token: &JobToken,
194 ) -> Result<Vec<SymbolInformation>> {
184 let mut res = Vec::new(); 195 let mut res = Vec::new();
185 for (file_id, symbol) in world.analysis().symbol_search(query, token) { 196 for (file_id, symbol) in world.analysis().symbol_search(query, token) {
186 let line_index = world.analysis().file_line_index(file_id); 197 let line_index = world.analysis().file_line_index(file_id);
187 let info = SymbolInformation { 198 let info = SymbolInformation {
188 name: symbol.name.to_string(), 199 name: symbol.name.to_string(),
189 kind: symbol.kind.conv(), 200 kind: symbol.kind.conv(),
190 location: to_location( 201 location: to_location(file_id, symbol.node_range, world, &line_index)?,
191 file_id, symbol.node_range,
192 world, &line_index
193 )?,
194 container_name: None, 202 container_name: None,
195 deprecated: None, 203 deprecated: None,
196 }; 204 };
197 res.push(info); 205 res.push(info);
198 }; 206 }
199 Ok(res) 207 Ok(res)
200 } 208 }
201} 209}
@@ -209,12 +217,12 @@ pub fn handle_goto_definition(
209 let line_index = world.analysis().file_line_index(file_id); 217 let line_index = world.analysis().file_line_index(file_id);
210 let offset = params.position.conv_with(&line_index); 218 let offset = params.position.conv_with(&line_index);
211 let mut res = Vec::new(); 219 let mut res = Vec::new();
212 for (file_id, symbol) in world.analysis().approximately_resolve_symbol(file_id, offset, &token) { 220 for (file_id, symbol) in world
221 .analysis()
222 .approximately_resolve_symbol(file_id, offset, &token)
223 {
213 let line_index = world.analysis().file_line_index(file_id); 224 let line_index = world.analysis().file_line_index(file_id);
214 let location = to_location( 225 let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
215 file_id, symbol.node_range,
216 &world, &line_index,
217 )?;
218 res.push(location) 226 res.push(location)
219 } 227 }
220 Ok(Some(req::GotoDefinitionResponse::Array(res))) 228 Ok(Some(req::GotoDefinitionResponse::Array(res)))
@@ -229,10 +237,7 @@ pub fn handle_parent_module(
229 let mut res = Vec::new(); 237 let mut res = Vec::new();
230 for (file_id, symbol) in world.analysis().parent_module(file_id) { 238 for (file_id, symbol) in world.analysis().parent_module(file_id) {
231 let line_index = world.analysis().file_line_index(file_id); 239 let line_index = world.analysis().file_line_index(file_id);
232 let location = to_location( 240 let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
233 file_id, symbol.node_range,
234 &world, &line_index
235 )?;
236 res.push(location); 241 res.push(location);
237 } 242 }
238 Ok(res) 243 Ok(res)
@@ -259,21 +264,16 @@ pub fn handle_runnables(
259 let r = req::Runnable { 264 let r = req::Runnable {
260 range: runnable.range.conv_with(&line_index), 265 range: runnable.range.conv_with(&line_index),
261 label: match &runnable.kind { 266 label: match &runnable.kind {
262 RunnableKind::Test { name } => 267 RunnableKind::Test { name } => format!("test {}", name),
263 format!("test {}", name), 268 RunnableKind::Bin => "run binary".to_string(),
264 RunnableKind::Bin =>
265 "run binary".to_string(),
266 }, 269 },
267 bin: "cargo".to_string(), 270 bin: "cargo".to_string(),
268 args, 271 args,
269 env: { 272 env: {
270 let mut m = FxHashMap::default(); 273 let mut m = FxHashMap::default();
271 m.insert( 274 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
272 "RUST_BACKTRACE".to_string(),
273 "short".to_string(),
274 );
275 m 275 m
276 } 276 },
277 }; 277 };
278 res.push(r); 278 res.push(r);
279 } 279 }
@@ -283,10 +283,16 @@ pub fn handle_runnables(
283 let spec = if let Some(&crate_id) = world.analysis().crate_for(file_id).first() { 283 let spec = if let Some(&crate_id) = world.analysis().crate_for(file_id).first() {
284 let file_id = world.analysis().crate_root(crate_id); 284 let file_id = world.analysis().crate_root(crate_id);
285 let path = world.path_map.get_path(file_id); 285 let path = world.path_map.get_path(file_id);
286 world.workspaces.iter() 286 world
287 .workspaces
288 .iter()
287 .filter_map(|ws| { 289 .filter_map(|ws| {
288 let tgt = ws.target_by_root(path)?; 290 let tgt = ws.target_by_root(path)?;
289 Some((tgt.package(ws).name(ws).clone(), tgt.name(ws).clone(), tgt.kind(ws))) 291 Some((
292 tgt.package(ws).name(ws).clone(),
293 tgt.name(ws).clone(),
294 tgt.kind(ws),
295 ))
290 }) 296 })
291 .next() 297 .next()
292 } else { 298 } else {
@@ -294,22 +300,22 @@ pub fn handle_runnables(
294 }; 300 };
295 let mut res = Vec::new(); 301 let mut res = Vec::new();
296 match kind { 302 match kind {
297 RunnableKind::Test { name } => { 303 RunnableKind::Test { name } => {
298 res.push("test".to_string()); 304 res.push("test".to_string());
299 if let Some((pkg_name, tgt_name, tgt_kind)) = spec { 305 if let Some((pkg_name, tgt_name, tgt_kind)) = spec {
300 spec_args(pkg_name, tgt_name, tgt_kind, &mut res); 306 spec_args(pkg_name, tgt_name, tgt_kind, &mut res);
301 }
302 res.push("--".to_string());
303 res.push(name.to_string());
304 res.push("--nocapture".to_string());
305 } 307 }
306 RunnableKind::Bin => { 308 res.push("--".to_string());
307 res.push("run".to_string()); 309 res.push(name.to_string());
308 if let Some((pkg_name, tgt_name, tgt_kind)) = spec { 310 res.push("--nocapture".to_string());
309 spec_args(pkg_name, tgt_name, tgt_kind, &mut res); 311 }
310 } 312 RunnableKind::Bin => {
313 res.push("run".to_string());
314 if let Some((pkg_name, tgt_name, tgt_kind)) = spec {
315 spec_args(pkg_name, tgt_name, tgt_kind, &mut res);
311 } 316 }
312 } 317 }
318 }
313 res 319 res
314 } 320 }
315 321
@@ -362,12 +368,13 @@ pub fn handle_completion(
362 None => return Ok(None), 368 None => return Ok(None),
363 Some(items) => items, 369 Some(items) => items,
364 }; 370 };
365 let items = items.into_iter() 371 let items = items
372 .into_iter()
366 .map(|item| { 373 .map(|item| {
367 let mut res = CompletionItem { 374 let mut res = CompletionItem {
368 label: item.label, 375 label: item.label,
369 filter_text: item.lookup, 376 filter_text: item.lookup,
370 .. Default::default() 377 ..Default::default()
371 }; 378 };
372 if let Some(snip) = item.snippet { 379 if let Some(snip) = item.snippet {
373 res.insert_text = Some(snip); 380 res.insert_text = Some(snip);
@@ -389,24 +396,27 @@ pub fn handle_folding_range(
389 let file_id = params.text_document.try_conv_with(&world)?; 396 let file_id = params.text_document.try_conv_with(&world)?;
390 let line_index = world.analysis().file_line_index(file_id); 397 let line_index = world.analysis().file_line_index(file_id);
391 398
392 let res = Some(world.analysis() 399 let res = Some(
393 .folding_ranges(file_id) 400 world
394 .into_iter() 401 .analysis()
395 .map(|fold| { 402 .folding_ranges(file_id)
396 let kind = match fold.kind { 403 .into_iter()
397 FoldKind::Comment => FoldingRangeKind::Comment, 404 .map(|fold| {
398 FoldKind::Imports => FoldingRangeKind::Imports 405 let kind = match fold.kind {
399 }; 406 FoldKind::Comment => FoldingRangeKind::Comment,
400 let range = fold.range.conv_with(&line_index); 407 FoldKind::Imports => FoldingRangeKind::Imports,
401 FoldingRange { 408 };
402 start_line: range.start.line, 409 let range = fold.range.conv_with(&line_index);
403 start_character: Some(range.start.character), 410 FoldingRange {
404 end_line: range.end.line, 411 start_line: range.start.line,
405 end_character: Some(range.start.character), 412 start_character: Some(range.start.character),
406 kind: Some(kind) 413 end_line: range.end.line,
407 } 414 end_character: Some(range.start.character),
408 }) 415 kind: Some(kind),
409 .collect()); 416 }
417 })
418 .collect(),
419 );
410 420
411 Ok(res) 421 Ok(res)
412} 422}
@@ -422,25 +432,28 @@ pub fn handle_signature_help(
422 let line_index = world.analysis().file_line_index(file_id); 432 let line_index = world.analysis().file_line_index(file_id);
423 let offset = params.position.conv_with(&line_index); 433 let offset = params.position.conv_with(&line_index);
424 434
425 if let Some((descriptor, active_param)) = world.analysis().resolve_callable(file_id, offset, &token) { 435 if let Some((descriptor, active_param)) =
426 let parameters : Vec<ParameterInformation> = 436 world.analysis().resolve_callable(file_id, offset, &token)
427 descriptor.params.iter().map(|param| 437 {
428 ParameterInformation { 438 let parameters: Vec<ParameterInformation> = descriptor
429 label: param.clone(), 439 .params
430 documentation: None 440 .iter()
431 } 441 .map(|param| ParameterInformation {
432 ).collect(); 442 label: param.clone(),
443 documentation: None,
444 })
445 .collect();
433 446
434 let sig_info = SignatureInformation { 447 let sig_info = SignatureInformation {
435 label: descriptor.label, 448 label: descriptor.label,
436 documentation: None, 449 documentation: None,
437 parameters: Some(parameters) 450 parameters: Some(parameters),
438 }; 451 };
439 452
440 Ok(Some(req::SignatureHelp { 453 Ok(Some(req::SignatureHelp {
441 signatures: vec![sig_info], 454 signatures: vec![sig_info],
442 active_signature: Some(0), 455 active_signature: Some(0),
443 active_parameter: active_param.map(|a| a as u64) 456 active_parameter: active_param.map(|a| a as u64),
444 })) 457 }))
445 } else { 458 } else {
446 Ok(None) 459 Ok(None)
@@ -457,7 +470,10 @@ pub fn handle_code_action(
457 let range = params.range.conv_with(&line_index); 470 let range = params.range.conv_with(&line_index);
458 471
459 let assists = world.analysis().assists(file_id, range).into_iter(); 472 let assists = world.analysis().assists(file_id, range).into_iter();
460 let fixes = world.analysis().diagnostics(file_id).into_iter() 473 let fixes = world
474 .analysis()
475 .diagnostics(file_id)
476 .into_iter()
461 .filter_map(|d| Some((d.range, d.fix?))) 477 .filter_map(|d| Some((d.range, d.fix?)))
462 .filter(|(range, _fix)| contains_offset_nonstrict(*range, range.start())) 478 .filter(|(range, _fix)| contains_offset_nonstrict(*range, range.start()))
463 .map(|(_range, fix)| fix); 479 .map(|(_range, fix)| fix);
@@ -483,7 +499,9 @@ pub fn publish_diagnostics(
483) -> Result<req::PublishDiagnosticsParams> { 499) -> Result<req::PublishDiagnosticsParams> {
484 let uri = world.file_id_to_uri(file_id)?; 500 let uri = world.file_id_to_uri(file_id)?;
485 let line_index = world.analysis().file_line_index(file_id); 501 let line_index = world.analysis().file_line_index(file_id);
486 let diagnostics = world.analysis().diagnostics(file_id) 502 let diagnostics = world
503 .analysis()
504 .diagnostics(file_id)
487 .into_iter() 505 .into_iter()
488 .map(|d| Diagnostic { 506 .map(|d| Diagnostic {
489 range: d.range.conv_with(&line_index), 507 range: d.range.conv_with(&line_index),
@@ -492,7 +510,8 @@ pub fn publish_diagnostics(
492 source: Some("rust-analyzer".to_string()), 510 source: Some("rust-analyzer".to_string()),
493 message: d.message, 511 message: d.message,
494 related_information: None, 512 related_information: None,
495 }).collect(); 513 })
514 .collect();
496 Ok(req::PublishDiagnosticsParams { uri, diagnostics }) 515 Ok(req::PublishDiagnosticsParams { uri, diagnostics })
497} 516}
498 517
@@ -509,10 +528,13 @@ pub fn publish_decorations(
509 528
510fn highlight(world: &ServerWorld, file_id: FileId) -> Vec<Decoration> { 529fn highlight(world: &ServerWorld, file_id: FileId) -> Vec<Decoration> {
511 let line_index = world.analysis().file_line_index(file_id); 530 let line_index = world.analysis().file_line_index(file_id);
512 world.analysis().highlight(file_id) 531 world
532 .analysis()
533 .highlight(file_id)
513 .into_iter() 534 .into_iter()
514 .map(|h| Decoration { 535 .map(|h| Decoration {
515 range: h.range.conv_with(&line_index), 536 range: h.range.conv_with(&line_index),
516 tag: h.tag, 537 tag: h.tag,
517 }).collect() 538 })
539 .collect()
518} 540}
diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs
index cf2477cb5..a11baf4aa 100644
--- a/crates/ra_lsp_server/src/main_loop/mod.rs
+++ b/crates/ra_lsp_server/src/main_loop/mod.rs
@@ -1,29 +1,26 @@
1mod handlers; 1mod handlers;
2mod subscriptions; 2mod subscriptions;
3 3
4use std::{ 4use std::path::PathBuf;
5 path::PathBuf,
6};
7 5
8use serde::{Serialize, de::DeserializeOwned}; 6use crossbeam_channel::{unbounded, Receiver, Sender};
9use crossbeam_channel::{unbounded, Sender, Receiver};
10use rayon::{self, ThreadPool};
11use languageserver_types::{NumberOrString};
12use ra_analysis::{FileId, JobHandle, JobToken, LibraryData};
13use gen_lsp_server::{ 7use gen_lsp_server::{
14 RawRequest, RawNotification, RawMessage, RawResponse, ErrorCode, 8 handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse,
15 handle_shutdown,
16}; 9};
10use languageserver_types::NumberOrString;
11use ra_analysis::{FileId, JobHandle, JobToken, LibraryData};
12use rayon::{self, ThreadPool};
17use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
14use serde::{de::DeserializeOwned, Serialize};
18 15
19use crate::{ 16use crate::{
17 main_loop::subscriptions::Subscriptions,
18 project_model::{workspace_loader, CargoWorkspace},
20 req, 19 req,
21 Result, 20 server_world::{ServerWorld, ServerWorldState},
22 vfs::{self, FileEvent},
23 server_world::{ServerWorldState, ServerWorld},
24 main_loop::subscriptions::{Subscriptions},
25 project_model::{CargoWorkspace, workspace_loader},
26 thread_watcher::Worker, 21 thread_watcher::Worker,
22 vfs::{self, FileEvent},
23 Result,
27}; 24};
28 25
29#[derive(Debug)] 26#[derive(Debug)]
@@ -147,56 +144,50 @@ fn main_loop_inner(
147 } 144 }
148 state_changed = true; 145 state_changed = true;
149 } 146 }
150 Event::Ws(ws) => { 147 Event::Ws(ws) => match ws {
151 match ws { 148 Ok(ws) => {
152 Ok(ws) => { 149 let workspaces = vec![ws];
153 let workspaces = vec![ws]; 150 feedback(internal_mode, "workspace loaded", msg_sender);
154 feedback(internal_mode, "workspace loaded", msg_sender); 151 for ws in workspaces.iter() {
155 for ws in workspaces.iter() { 152 for pkg in ws.packages().filter(|pkg| !pkg.is_member(ws)) {
156 for pkg in ws.packages().filter(|pkg| !pkg.is_member(ws)) { 153 debug!("sending root, {}", pkg.root(ws).to_path_buf().display());
157 debug!("sending root, {}", pkg.root(ws).to_path_buf().display()); 154 fs_worker.send(pkg.root(ws).to_path_buf());
158 fs_worker.send(pkg.root(ws).to_path_buf());
159 }
160 } 155 }
161 state.set_workspaces(workspaces);
162 state_changed = true;
163 } 156 }
164 Err(e) => warn!("loading workspace failed: {}", e), 157 state.set_workspaces(workspaces);
158 state_changed = true;
165 } 159 }
166 } 160 Err(e) => warn!("loading workspace failed: {}", e),
161 },
167 Event::Lib(lib) => { 162 Event::Lib(lib) => {
168 feedback(internal_mode, "library loaded", msg_sender); 163 feedback(internal_mode, "library loaded", msg_sender);
169 state.add_lib(lib); 164 state.add_lib(lib);
170 } 165 }
171 Event::Msg(msg) => { 166 Event::Msg(msg) => match msg {
172 match msg { 167 RawMessage::Request(req) => {
173 RawMessage::Request(req) => { 168 let req = match handle_shutdown(req, msg_sender) {
174 let req = match handle_shutdown(req, msg_sender) { 169 Some(req) => req,
175 Some(req) => req, 170 None => return Ok(()),
176 None => return Ok(()), 171 };
177 }; 172 match on_request(state, pending_requests, pool, &task_sender, req)? {
178 match on_request(state, pending_requests, pool, &task_sender, req)? { 173 None => (),
179 None => (), 174 Some(req) => {
180 Some(req) => { 175 error!("unknown request: {:?}", req);
181 error!("unknown request: {:?}", req); 176 let resp = RawResponse::err(
182 let resp = RawResponse::err( 177 req.id,
183 req.id, 178 ErrorCode::MethodNotFound as i32,
184 ErrorCode::MethodNotFound as i32, 179 "unknown request".to_string(),
185 "unknown request".to_string(), 180 );
186 ); 181 msg_sender.send(RawMessage::Response(resp))
187 msg_sender.send(RawMessage::Response(resp))
188 }
189 } 182 }
190 } 183 }
191 RawMessage::Notification(not) => {
192 on_notification(msg_sender, state, pending_requests, subs, not)?;
193 state_changed = true;
194 }
195 RawMessage::Response(resp) => {
196 error!("unexpected response: {:?}", resp)
197 }
198 } 184 }
199 } 185 RawMessage::Notification(not) => {
186 on_notification(msg_sender, state, pending_requests, subs, not)?;
187 state_changed = true;
188 }
189 RawMessage::Response(resp) => error!("unexpected response: {:?}", resp),
190 },
200 }; 191 };
201 192
202 if state_changed { 193 if state_changed {
@@ -222,8 +213,7 @@ fn on_task(
222 } 213 }
223 msg_sender.send(RawMessage::Response(response)) 214 msg_sender.send(RawMessage::Response(response))
224 } 215 }
225 Task::Notify(n) => 216 Task::Notify(n) => msg_sender.send(RawMessage::Notification(n)),
226 msg_sender.send(RawMessage::Notification(n)),
227 } 217 }
228} 218}
229 219
@@ -237,7 +227,9 @@ fn on_request(
237 let mut pool_dispatcher = PoolDispatcher { 227 let mut pool_dispatcher = PoolDispatcher {
238 req: Some(req), 228 req: Some(req),
239 res: None, 229 res: None,
240 pool, world, sender 230 pool,
231 world,
232 sender,
241 }; 233 };
242 let req = pool_dispatcher 234 let req = pool_dispatcher
243 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? 235 .on::<req::SyntaxTree>(handlers::handle_syntax_tree)?
@@ -262,7 +254,7 @@ fn on_request(
262 let inserted = pending_requests.insert(id, handle).is_none(); 254 let inserted = pending_requests.insert(id, handle).is_none();
263 assert!(inserted, "duplicate request: {}", id); 255 assert!(inserted, "duplicate request: {}", id);
264 Ok(None) 256 Ok(None)
265 }, 257 }
266 Err(req) => Ok(Some(req)), 258 Err(req) => Ok(Some(req)),
267 } 259 }
268} 260}
@@ -285,45 +277,53 @@ fn on_notification(
285 if let Some(handle) = pending_requests.remove(&id) { 277 if let Some(handle) = pending_requests.remove(&id) {
286 handle.cancel(); 278 handle.cancel();
287 } 279 }
288 return Ok(()) 280 return Ok(());
289 } 281 }
290 Err(not) => not, 282 Err(not) => not,
291 }; 283 };
292 let not = match not.cast::<req::DidOpenTextDocument>() { 284 let not = match not.cast::<req::DidOpenTextDocument>() {
293 Ok(params) => { 285 Ok(params) => {
294 let uri = params.text_document.uri; 286 let uri = params.text_document.uri;
295 let path = uri.to_file_path() 287 let path = uri
288 .to_file_path()
296 .map_err(|()| format_err!("invalid uri: {}", uri))?; 289 .map_err(|()| format_err!("invalid uri: {}", uri))?;
297 let file_id = state.add_mem_file(path, params.text_document.text); 290 let file_id = state.add_mem_file(path, params.text_document.text);
298 subs.add_sub(file_id); 291 subs.add_sub(file_id);
299 return Ok(()) 292 return Ok(());
300 } 293 }
301 Err(not) => not, 294 Err(not) => not,
302 }; 295 };
303 let not = match not.cast::<req::DidChangeTextDocument>() { 296 let not = match not.cast::<req::DidChangeTextDocument>() {
304 Ok(mut params) => { 297 Ok(mut params) => {
305 let uri = params.text_document.uri; 298 let uri = params.text_document.uri;
306 let path = uri.to_file_path() 299 let path = uri
300 .to_file_path()
307 .map_err(|()| format_err!("invalid uri: {}", uri))?; 301 .map_err(|()| format_err!("invalid uri: {}", uri))?;
308 let text = params.content_changes.pop() 302 let text = params
303 .content_changes
304 .pop()
309 .ok_or_else(|| format_err!("empty changes"))? 305 .ok_or_else(|| format_err!("empty changes"))?
310 .text; 306 .text;
311 state.change_mem_file(path.as_path(), text)?; 307 state.change_mem_file(path.as_path(), text)?;
312 return Ok(()) 308 return Ok(());
313 } 309 }
314 Err(not) => not, 310 Err(not) => not,
315 }; 311 };
316 let not = match not.cast::<req::DidCloseTextDocument>() { 312 let not = match not.cast::<req::DidCloseTextDocument>() {
317 Ok(params) => { 313 Ok(params) => {
318 let uri = params.text_document.uri; 314 let uri = params.text_document.uri;
319 let path = uri.to_file_path() 315 let path = uri
316 .to_file_path()
320 .map_err(|()| format_err!("invalid uri: {}", uri))?; 317 .map_err(|()| format_err!("invalid uri: {}", uri))?;
321 let file_id = state.remove_mem_file(path.as_path())?; 318 let file_id = state.remove_mem_file(path.as_path())?;
322 subs.remove_sub(file_id); 319 subs.remove_sub(file_id);
323 let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; 320 let params = req::PublishDiagnosticsParams {
321 uri,
322 diagnostics: Vec::new(),
323 };
324 let not = RawNotification::new::<req::PublishDiagnostics>(&params); 324 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
325 msg_sender.send(RawMessage::Notification(not)); 325 msg_sender.send(RawMessage::Notification(not));
326 return Ok(()) 326 return Ok(());
327 } 327 }
328 Err(not) => not, 328 Err(not) => not,
329 }; 329 };
@@ -342,11 +342,12 @@ struct PoolDispatcher<'a> {
342impl<'a> PoolDispatcher<'a> { 342impl<'a> PoolDispatcher<'a> {
343 fn on<'b, R>( 343 fn on<'b, R>(
344 &'b mut self, 344 &'b mut self,
345 f: fn(ServerWorld, R::Params, JobToken) -> Result<R::Result> 345 f: fn(ServerWorld, R::Params, JobToken) -> Result<R::Result>,
346 ) -> Result<&'b mut Self> 346 ) -> Result<&'b mut Self>
347 where R: req::Request, 347 where
348 R::Params: DeserializeOwned + Send + 'static, 348 R: req::Request,
349 R::Result: Serialize + 'static, 349 R::Params: DeserializeOwned + Send + 'static,
350 R::Result: Serialize + 'static,
350 { 351 {
351 let req = match self.req.take() { 352 let req = match self.req.take() {
352 None => return Ok(self), 353 None => return Ok(self),
@@ -360,16 +361,16 @@ impl<'a> PoolDispatcher<'a> {
360 self.pool.spawn(move || { 361 self.pool.spawn(move || {
361 let resp = match f(world, params, token) { 362 let resp = match f(world, params, token) {
362 Ok(resp) => RawResponse::ok::<R>(id, &resp), 363 Ok(resp) => RawResponse::ok::<R>(id, &resp),
363 Err(e) => RawResponse::err(id, ErrorCode::InternalError as i32, e.to_string()), 364 Err(e) => {
365 RawResponse::err(id, ErrorCode::InternalError as i32, e.to_string())
366 }
364 }; 367 };
365 let task = Task::Respond(resp); 368 let task = Task::Respond(resp);
366 sender.send(task); 369 sender.send(task);
367 }); 370 });
368 self.res = Some((id, handle)); 371 self.res = Some((id, handle));
369 } 372 }
370 Err(req) => { 373 Err(req) => self.req = Some(req),
371 self.req = Some(req)
372 }
373 } 374 }
374 Ok(self) 375 Ok(self)
375 } 376 }
@@ -392,18 +393,14 @@ fn update_file_notifications_on_threadpool(
392 pool.spawn(move || { 393 pool.spawn(move || {
393 for file_id in subscriptions { 394 for file_id in subscriptions {
394 match handlers::publish_diagnostics(&world, file_id) { 395 match handlers::publish_diagnostics(&world, file_id) {
395 Err(e) => { 396 Err(e) => error!("failed to compute diagnostics: {:?}", e),
396 error!("failed to compute diagnostics: {:?}", e)
397 }
398 Ok(params) => { 397 Ok(params) => {
399 let not = RawNotification::new::<req::PublishDiagnostics>(&params); 398 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
400 sender.send(Task::Notify(not)); 399 sender.send(Task::Notify(not));
401 } 400 }
402 } 401 }
403 match handlers::publish_decorations(&world, file_id) { 402 match handlers::publish_decorations(&world, file_id) {
404 Err(e) => { 403 Err(e) => error!("failed to compute decorations: {:?}", e),
405 error!("failed to compute decorations: {:?}", e)
406 }
407 Ok(params) => { 404 Ok(params) => {
408 let not = RawNotification::new::<req::PublishDecorations>(&params); 405 let not = RawNotification::new::<req::PublishDecorations>(&params);
409 sender.send(Task::Notify(not)) 406 sender.send(Task::Notify(not))
diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
index 310153382..03f41e870 100644
--- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs
+++ b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
@@ -1,5 +1,5 @@
1use rustc_hash::FxHashSet;
2use ra_analysis::FileId; 1use ra_analysis::FileId;
2use rustc_hash::FxHashSet;
3 3
4pub struct Subscriptions { 4pub struct Subscriptions {
5 subs: FxHashSet<FileId>, 5 subs: FxHashSet<FileId>,
@@ -7,7 +7,9 @@ pub struct Subscriptions {
7 7
8impl Subscriptions { 8impl Subscriptions {
9 pub fn new() -> Subscriptions { 9 pub fn new() -> Subscriptions {
10 Subscriptions { subs: FxHashSet::default() } 10 Subscriptions {
11 subs: FxHashSet::default(),
12 }
11 } 13 }
12 pub fn add_sub(&mut self, file_id: FileId) { 14 pub fn add_sub(&mut self, file_id: FileId) {
13 self.subs.insert(file_id); 15 self.subs.insert(file_id);
diff --git a/crates/ra_lsp_server/src/path_map.rs b/crates/ra_lsp_server/src/path_map.rs
index 19c3b1d3b..585013acd 100644
--- a/crates/ra_lsp_server/src/path_map.rs
+++ b/crates/ra_lsp_server/src/path_map.rs
@@ -1,11 +1,13 @@
1use std::path::{PathBuf, Path, Component};
2use im; 1use im;
3use relative_path::RelativePath;
4use ra_analysis::{FileId, FileResolver}; 2use ra_analysis::{FileId, FileResolver};
3use relative_path::RelativePath;
4
5use std::path::{Component, Path, PathBuf};
5 6
6#[derive(Debug, Clone, Copy, PartialEq, Eq)] 7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Root { 8pub enum Root {
8 Workspace, Lib 9 Workspace,
10 Lib,
9} 11}
10 12
11#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
@@ -21,7 +23,8 @@ impl PathMap {
21 Default::default() 23 Default::default()
22 } 24 }
23 pub fn get_or_insert(&mut self, path: PathBuf, root: Root) -> FileId { 25 pub fn get_or_insert(&mut self, path: PathBuf, root: Root) -> FileId {
24 self.path2id.get(path.as_path()) 26 self.path2id
27 .get(path.as_path())
25 .map(|&id| id) 28 .map(|&id| id)
26 .unwrap_or_else(|| { 29 .unwrap_or_else(|| {
27 let id = self.new_file_id(); 30 let id = self.new_file_id();
@@ -33,9 +36,7 @@ impl PathMap {
33 self.path2id.get(path).map(|&id| id) 36 self.path2id.get(path).map(|&id| id)
34 } 37 }
35 pub fn get_path(&self, file_id: FileId) -> &Path { 38 pub fn get_path(&self, file_id: FileId) -> &Path {
36 self.id2path.get(&file_id) 39 self.id2path.get(&file_id).unwrap().as_path()
37 .unwrap()
38 .as_path()
39 } 40 }
40 pub fn get_root(&self, file_id: FileId) -> Root { 41 pub fn get_root(&self, file_id: FileId) -> Root {
41 self.id2root[&file_id] 42 self.id2root[&file_id]
@@ -55,7 +56,12 @@ impl PathMap {
55 56
56impl FileResolver for PathMap { 57impl FileResolver for PathMap {
57 fn file_stem(&self, file_id: FileId) -> String { 58 fn file_stem(&self, file_id: FileId) -> String {
58 self.get_path(file_id).file_stem().unwrap().to_str().unwrap().to_string() 59 self.get_path(file_id)
60 .file_stem()
61 .unwrap()
62 .to_str()
63 .unwrap()
64 .to_string()
59 } 65 }
60 66
61 fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { 67 fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> {
@@ -101,10 +107,6 @@ mod test {
101 let mut m = PathMap::new(); 107 let mut m = PathMap::new();
102 let id1 = m.get_or_insert(PathBuf::from("/foo"), Root::Workspace); 108 let id1 = m.get_or_insert(PathBuf::from("/foo"), Root::Workspace);
103 let id2 = m.get_or_insert(PathBuf::from("/foo/bar.rs"), Root::Workspace); 109 let id2 = m.get_or_insert(PathBuf::from("/foo/bar.rs"), Root::Workspace);
104 assert_eq!( 110 assert_eq!(m.resolve(id1, &RelativePath::new("bar.rs")), Some(id2),)
105 m.resolve(id1, &RelativePath::new("bar.rs")),
106 Some(id2),
107 )
108 } 111 }
109} 112}
110
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
index c144d9596..d170ceb73 100644
--- a/crates/ra_lsp_server/src/project_model.rs
+++ b/crates/ra_lsp_server/src/project_model.rs
@@ -1,13 +1,12 @@
1use std::{
2 path::{Path, PathBuf},
3};
4use rustc_hash::{FxHashMap, FxHashSet};
5use cargo_metadata::{metadata_run, CargoOpt}; 1use cargo_metadata::{metadata_run, CargoOpt};
6use ra_syntax::SmolStr; 2use ra_syntax::SmolStr;
3use rustc_hash::{FxHashMap, FxHashSet};
4
5use std::path::{Path, PathBuf};
7 6
8use crate::{ 7use crate::{
8 thread_watcher::{ThreadWatcher, Worker},
9 Result, 9 Result,
10 thread_watcher::{Worker, ThreadWatcher},
11}; 10};
12 11
13#[derive(Debug, Clone)] 12#[derive(Debug, Clone)]
@@ -39,7 +38,12 @@ struct TargetData {
39 38
40#[derive(Debug, Clone, Copy, PartialEq, Eq)] 39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum TargetKind { 40pub enum TargetKind {
42 Bin, Lib, Example, Test, Bench, Other, 41 Bin,
42 Lib,
43 Example,
44 Test,
45 Bench,
46 Other,
43} 47}
44 48
45impl Package { 49impl Package {
@@ -49,7 +53,7 @@ impl Package {
49 pub fn root(self, ws: &CargoWorkspace) -> &Path { 53 pub fn root(self, ws: &CargoWorkspace) -> &Path {
50 ws.pkg(self).manifest.parent().unwrap() 54 ws.pkg(self).manifest.parent().unwrap()
51 } 55 }
52 pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item=Target> + 'a { 56 pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
53 ws.pkg(self).targets.iter().cloned() 57 ws.pkg(self).targets.iter().cloned()
54 } 58 }
55 pub fn is_member(self, ws: &CargoWorkspace) -> bool { 59 pub fn is_member(self, ws: &CargoWorkspace) -> bool {
@@ -78,13 +82,15 @@ impl CargoWorkspace {
78 let meta = metadata_run( 82 let meta = metadata_run(
79 Some(cargo_toml.as_path()), 83 Some(cargo_toml.as_path()),
80 true, 84 true,
81 Some(CargoOpt::AllFeatures) 85 Some(CargoOpt::AllFeatures),
82 ).map_err(|e| format_err!("cargo metadata failed: {}", e))?; 86 )
87 .map_err(|e| format_err!("cargo metadata failed: {}", e))?;
83 let mut pkg_by_id = FxHashMap::default(); 88 let mut pkg_by_id = FxHashMap::default();
84 let mut packages = Vec::new(); 89 let mut packages = Vec::new();
85 let mut targets = Vec::new(); 90 let mut targets = Vec::new();
86 91
87 let ws_members: FxHashSet<String> = meta.workspace_members 92 let ws_members: FxHashSet<String> = meta
93 .workspace_members
88 .into_iter() 94 .into_iter()
89 .map(|it| it.raw) 95 .map(|it| it.raw)
90 .collect(); 96 .collect();
@@ -114,7 +120,7 @@ impl CargoWorkspace {
114 120
115 Ok(CargoWorkspace { packages, targets }) 121 Ok(CargoWorkspace { packages, targets })
116 } 122 }
117 pub fn packages<'a>(&'a self) -> impl Iterator<Item=Package> + 'a { 123 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
118 (0..self.packages.len()).map(Package) 124 (0..self.packages.len()).map(Package)
119 } 125 }
120 pub fn target_by_root(&self, root: &Path) -> Option<Target> { 126 pub fn target_by_root(&self, root: &Path) -> Option<Target> {
@@ -155,7 +161,7 @@ impl TargetKind {
155 "example" => TargetKind::Example, 161 "example" => TargetKind::Example,
156 _ if kind.contains("lib") => TargetKind::Lib, 162 _ if kind.contains("lib") => TargetKind::Lib,
157 _ => continue, 163 _ => continue,
158 } 164 };
159 } 165 }
160 TargetKind::Other 166 TargetKind::Other
161 } 167 }
@@ -170,6 +176,6 @@ pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, ThreadWat
170 .into_iter() 176 .into_iter()
171 .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path())) 177 .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path()))
172 .for_each(|it| output_sender.send(it)) 178 .for_each(|it| output_sender.send(it))
173 } 179 },
174 ) 180 )
175} 181}
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 1630edf7f..b76bfbcbc 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -1,20 +1,13 @@
1use languageserver_types::{Location, Position, Range, TextDocumentIdentifier, Url};
1use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
2use languageserver_types::{TextDocumentIdentifier, Range, Url, Position, Location};
3use url_serde; 3use url_serde;
4 4
5pub use languageserver_types::{ 5pub use languageserver_types::{
6 request::*, notification::*, 6 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CompletionParams,
7 InitializeResult, PublishDiagnosticsParams, 7 CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams,
8 DocumentSymbolParams, DocumentSymbolResponse, 8 DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult,
9 CodeActionParams, ApplyWorkspaceEditParams, 9 PublishDiagnosticsParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams,
10 ExecuteCommandParams, 10 TextEdit, WorkspaceSymbolParams,
11 WorkspaceSymbolParams,
12 TextDocumentPositionParams,
13 TextEdit,
14 CompletionParams, CompletionResponse,
15 DocumentOnTypeFormattingParams,
16 TextDocumentEdit,
17 SignatureHelp, Hover
18}; 11};
19 12
20pub enum SyntaxTree {} 13pub enum SyntaxTree {}
@@ -28,7 +21,7 @@ impl Request for SyntaxTree {
28#[derive(Deserialize, Debug)] 21#[derive(Deserialize, Debug)]
29#[serde(rename_all = "camelCase")] 22#[serde(rename_all = "camelCase")]
30pub struct SyntaxTreeParams { 23pub struct SyntaxTreeParams {
31 pub text_document: TextDocumentIdentifier 24 pub text_document: TextDocumentIdentifier,
32} 25}
33 26
34pub enum ExtendSelection {} 27pub enum ExtendSelection {}
@@ -94,7 +87,7 @@ pub struct PublishDecorationsParams {
94#[serde(rename_all = "camelCase")] 87#[serde(rename_all = "camelCase")]
95pub struct Decoration { 88pub struct Decoration {
96 pub range: Range, 89 pub range: Range,
97 pub tag: &'static str 90 pub tag: &'static str,
98} 91}
99 92
100pub enum ParentModule {} 93pub enum ParentModule {}
@@ -167,14 +160,14 @@ pub struct SourceChange {
167pub enum FileSystemEdit { 160pub enum FileSystemEdit {
168 CreateFile { 161 CreateFile {
169 #[serde(with = "url_serde")] 162 #[serde(with = "url_serde")]
170 uri: Url 163 uri: Url,
171 }, 164 },
172 MoveFile { 165 MoveFile {
173 #[serde(with = "url_serde")] 166 #[serde(with = "url_serde")]
174 src: Url, 167 src: Url,
175 #[serde(with = "url_serde")] 168 #[serde(with = "url_serde")]
176 dst: Url, 169 dst: Url,
177 } 170 },
178} 171}
179 172
180pub enum InternalFeedback {} 173pub enum InternalFeedback {}
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index 9b3013ae8..35ff65ea1 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -1,18 +1,18 @@
1use std::{ 1use std::{
2 fs, 2 fs,
3 path::{PathBuf, Path}, 3 path::{Path, PathBuf},
4 sync::Arc, 4 sync::Arc,
5}; 5};
6 6
7use rustc_hash::FxHashMap;
8use languageserver_types::Url; 7use languageserver_types::Url;
9use ra_analysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver}; 8use ra_analysis::{Analysis, AnalysisHost, CrateGraph, CrateId, FileId, FileResolver, LibraryData};
9use rustc_hash::FxHashMap;
10 10
11use crate::{ 11use crate::{
12 Result,
13 path_map::{PathMap, Root}, 12 path_map::{PathMap, Root},
14 vfs::{FileEvent, FileEventKind},
15 project_model::CargoWorkspace, 13 project_model::CargoWorkspace,
14 vfs::{FileEvent, FileEventKind},
15 Result,
16}; 16};
17 17
18#[derive(Debug)] 18#[derive(Debug)]
@@ -42,16 +42,15 @@ impl ServerWorldState {
42 { 42 {
43 let pm = &mut self.path_map; 43 let pm = &mut self.path_map;
44 let mm = &mut self.mem_map; 44 let mm = &mut self.mem_map;
45 let changes = events.into_iter() 45 let changes = events
46 .into_iter()
46 .map(|event| { 47 .map(|event| {
47 let text = match event.kind { 48 let text = match event.kind {
48 FileEventKind::Add(text) => Some(text), 49 FileEventKind::Add(text) => Some(text),
49 }; 50 };
50 (event.path, text) 51 (event.path, text)
51 }) 52 })
52 .map(|(path, text)| { 53 .map(|(path, text)| (pm.get_or_insert(path, Root::Workspace), text))
53 (pm.get_or_insert(path, Root::Workspace), text)
54 })
55 .filter_map(|(id, text)| { 54 .filter_map(|(id, text)| {
56 if mm.contains_key(&id) { 55 if mm.contains_key(&id) {
57 mm.insert(id, text); 56 mm.insert(id, text);
@@ -62,12 +61,17 @@ impl ServerWorldState {
62 }); 61 });
63 self.analysis_host.change_files(changes); 62 self.analysis_host.change_files(changes);
64 } 63 }
65 self.analysis_host.set_file_resolver(Arc::new(self.path_map.clone())); 64 self.analysis_host
65 .set_file_resolver(Arc::new(self.path_map.clone()));
66 } 66 }
67 pub fn events_to_files(&mut self, events: Vec<FileEvent>) -> (Vec<(FileId, String)>, Arc<FileResolver>) { 67 pub fn events_to_files(
68 &mut self,
69 events: Vec<FileEvent>,
70 ) -> (Vec<(FileId, String)>, Arc<FileResolver>) {
68 let files = { 71 let files = {
69 let pm = &mut self.path_map; 72 let pm = &mut self.path_map;
70 events.into_iter() 73 events
74 .into_iter()
71 .map(|event| { 75 .map(|event| {
72 let text = match event.kind { 76 let text = match event.kind {
73 FileEventKind::Add(text) => text, 77 FileEventKind::Add(text) => text,
@@ -86,7 +90,8 @@ impl ServerWorldState {
86 90
87 pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId { 91 pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId {
88 let file_id = self.path_map.get_or_insert(path, Root::Workspace); 92 let file_id = self.path_map.get_or_insert(path, Root::Workspace);
89 self.analysis_host.set_file_resolver(Arc::new(self.path_map.clone())); 93 self.analysis_host
94 .set_file_resolver(Arc::new(self.path_map.clone()));
90 self.mem_map.insert(file_id, None); 95 self.mem_map.insert(file_id, None);
91 if self.path_map.get_root(file_id) != Root::Lib { 96 if self.path_map.get_root(file_id) != Root::Lib {
92 self.analysis_host.change_file(file_id, Some(text)); 97 self.analysis_host.change_file(file_id, Some(text));
@@ -95,9 +100,10 @@ impl ServerWorldState {
95 } 100 }
96 101
97 pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> { 102 pub fn change_mem_file(&mut self, path: &Path, text: String) -> Result<()> {
98 let file_id = self.path_map.get_id(path).ok_or_else(|| { 103 let file_id = self
99 format_err!("change to unknown file: {}", path.display()) 104 .path_map
100 })?; 105 .get_id(path)
106 .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
101 if self.path_map.get_root(file_id) != Root::Lib { 107 if self.path_map.get_root(file_id) != Root::Lib {
102 self.analysis_host.change_file(file_id, Some(text)); 108 self.analysis_host.change_file(file_id, Some(text));
103 } 109 }
@@ -105,9 +111,10 @@ impl ServerWorldState {
105 } 111 }
106 112
107 pub fn remove_mem_file(&mut self, path: &Path) -> Result<FileId> { 113 pub fn remove_mem_file(&mut self, path: &Path) -> Result<FileId> {
108 let file_id = self.path_map.get_id(path).ok_or_else(|| { 114 let file_id = self
109 format_err!("change to unknown file: {}", path.display()) 115 .path_map
110 })?; 116 .get_id(path)
117 .ok_or_else(|| format_err!("change to unknown file: {}", path.display()))?;
111 match self.mem_map.remove(&file_id) { 118 match self.mem_map.remove(&file_id) {
112 Some(_) => (), 119 Some(_) => (),
113 None => bail!("unmatched close notification"), 120 None => bail!("unmatched close notification"),
@@ -122,17 +129,17 @@ impl ServerWorldState {
122 pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { 129 pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
123 let mut crate_roots = FxHashMap::default(); 130 let mut crate_roots = FxHashMap::default();
124 ws.iter() 131 ws.iter()
125 .flat_map(|ws| { 132 .flat_map(|ws| {
126 ws.packages() 133 ws.packages()
127 .flat_map(move |pkg| pkg.targets(ws)) 134 .flat_map(move |pkg| pkg.targets(ws))
128 .map(move |tgt| tgt.root(ws)) 135 .map(move |tgt| tgt.root(ws))
129 }) 136 })
130 .for_each(|root| { 137 .for_each(|root| {
131 if let Some(file_id) = self.path_map.get_id(root) { 138 if let Some(file_id) = self.path_map.get_id(root) {
132 let crate_id = CrateId(crate_roots.len() as u32); 139 let crate_id = CrateId(crate_roots.len() as u32);
133 crate_roots.insert(crate_id, file_id); 140 crate_roots.insert(crate_id, file_id);
134 } 141 }
135 }); 142 });
136 let crate_graph = CrateGraph { crate_roots }; 143 let crate_graph = CrateGraph { crate_roots };
137 self.workspaces = Arc::new(ws); 144 self.workspaces = Arc::new(ws);
138 self.analysis_host.set_crate_graph(crate_graph); 145 self.analysis_host.set_crate_graph(crate_graph);
@@ -141,7 +148,7 @@ impl ServerWorldState {
141 ServerWorld { 148 ServerWorld {
142 workspaces: Arc::clone(&self.workspaces), 149 workspaces: Arc::clone(&self.workspaces),
143 analysis: self.analysis_host.analysis(), 150 analysis: self.analysis_host.analysis(),
144 path_map: self.path_map.clone() 151 path_map: self.path_map.clone(),
145 } 152 }
146 } 153 }
147} 154}
@@ -152,9 +159,12 @@ impl ServerWorld {
152 } 159 }
153 160
154 pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> { 161 pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
155 let path = uri.to_file_path() 162 let path = uri
163 .to_file_path()
156 .map_err(|()| format_err!("invalid uri: {}", uri))?; 164 .map_err(|()| format_err!("invalid uri: {}", uri))?;
157 self.path_map.get_id(&path).ok_or_else(|| format_err!("unknown file: {}", path.display())) 165 self.path_map
166 .get_id(&path)
167 .ok_or_else(|| format_err!("unknown file: {}", path.display()))
158 } 168 }
159 169
160 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { 170 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
diff --git a/crates/ra_lsp_server/src/thread_watcher.rs b/crates/ra_lsp_server/src/thread_watcher.rs
index 3257effcb..67952eb74 100644
--- a/crates/ra_lsp_server/src/thread_watcher.rs
+++ b/crates/ra_lsp_server/src/thread_watcher.rs
@@ -1,7 +1,8 @@
1use std::thread;
2use crossbeam_channel::{bounded, unbounded, Sender, Receiver};
3use drop_bomb::DropBomb;
4use crate::Result; 1use crate::Result;
2use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
3use drop_bomb::DropBomb;
4
5use std::thread;
5 6
6pub struct Worker<I, O> { 7pub struct Worker<I, O> {
7 pub inp: Sender<I>, 8 pub inp: Sender<I>,
@@ -50,11 +51,13 @@ impl ThreadWatcher {
50 info!("waiting for {} to finish ...", self.name); 51 info!("waiting for {} to finish ...", self.name);
51 let name = self.name; 52 let name = self.name;
52 self.bomb.defuse(); 53 self.bomb.defuse();
53 let res = self.thread.join() 54 let res = self
55 .thread
56 .join()
54 .map_err(|_| format_err!("ThreadWatcher {} died", name)); 57 .map_err(|_| format_err!("ThreadWatcher {} died", name));
55 match &res { 58 match &res {
56 Ok(()) => info!("... {} terminated with ok", name), 59 Ok(()) => info!("... {} terminated with ok", name),
57 Err(_) => error!("... {} terminated with err", name) 60 Err(_) => error!("... {} terminated with err", name),
58 } 61 }
59 res 62 res
60 } 63 }
@@ -66,5 +69,9 @@ impl ThreadWatcher {
66fn worker_chan<I, O>(buf: usize) -> ((Sender<I>, Receiver<O>), Receiver<I>, Sender<O>) { 69fn worker_chan<I, O>(buf: usize) -> ((Sender<I>, Receiver<O>), Receiver<I>, Sender<O>) {
67 let (input_sender, input_receiver) = bounded::<I>(buf); 70 let (input_sender, input_receiver) = bounded::<I>(buf);
68 let (output_sender, output_receiver) = unbounded::<O>(); 71 let (output_sender, output_receiver) = unbounded::<O>();
69 ((input_sender, output_receiver), input_receiver, output_sender) 72 (
73 (input_sender, output_receiver),
74 input_receiver,
75 output_sender,
76 )
70} 77}
diff --git a/crates/ra_lsp_server/src/vfs.rs b/crates/ra_lsp_server/src/vfs.rs
index d8f9b1aac..417a3c19a 100644
--- a/crates/ra_lsp_server/src/vfs.rs
+++ b/crates/ra_lsp_server/src/vfs.rs
@@ -1,14 +1,11 @@
1use std::{ 1use std::{
2 path::{PathBuf, Path},
3 fs, 2 fs,
3 path::{Path, PathBuf},
4}; 4};
5 5
6use walkdir::WalkDir; 6use walkdir::WalkDir;
7 7
8use crate::{ 8use crate::thread_watcher::{ThreadWatcher, Worker};
9 thread_watcher::{Worker, ThreadWatcher},
10};
11
12 9
13#[derive(Debug)] 10#[derive(Debug)]
14pub struct FileEvent { 11pub struct FileEvent {
@@ -24,7 +21,8 @@ pub enum FileEventKind {
24pub fn roots_loader() -> (Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, ThreadWatcher) { 21pub fn roots_loader() -> (Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, ThreadWatcher) {
25 Worker::<PathBuf, (PathBuf, Vec<FileEvent>)>::spawn( 22 Worker::<PathBuf, (PathBuf, Vec<FileEvent>)>::spawn(
26 "roots loader", 23 "roots loader",
27 128, |input_receiver, output_sender| { 24 128,
25 |input_receiver, output_sender| {
28 input_receiver 26 input_receiver
29 .into_iter() 27 .into_iter()
30 .map(|path| { 28 .map(|path| {
@@ -34,7 +32,7 @@ pub fn roots_loader() -> (Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, ThreadWatc
34 (path, events) 32 (path, events)
35 }) 33 })
36 .for_each(|it| output_sender.send(it)) 34 .for_each(|it| output_sender.send(it))
37 } 35 },
38 ) 36 )
39} 37}
40 38