aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_lsp_server/src/cargo_target_spec.rs13
-rw-r--r--crates/ra_lsp_server/src/lib.rs2
-rw-r--r--crates/ra_lsp_server/src/main.rs26
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs277
-rw-r--r--crates/ra_lsp_server/src/main_loop/subscriptions.rs2
-rw-r--r--crates/ra_lsp_server/src/project_model.rs21
-rw-r--r--crates/ra_lsp_server/src/thread_worker.rs49
-rw-r--r--crates/ra_lsp_server/src/world.rs3
8 files changed, 172 insertions, 221 deletions
diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs
index 050c5fd95..a083bb311 100644
--- a/crates/ra_lsp_server/src/cargo_target_spec.rs
+++ b/crates/ra_lsp_server/src/cargo_target_spec.rs
@@ -1,10 +1,7 @@
1use crate::{
2 project_model::{self, TargetKind},
3 world::WorldSnapshot,
4 Result,
5};
6
7use ra_ide_api::{FileId, RunnableKind}; 1use ra_ide_api::{FileId, RunnableKind};
2use ra_project_model::{self, ProjectWorkspace, TargetKind};
3
4use crate::{world::WorldSnapshot, Result};
8 5
9pub(crate) fn runnable_args( 6pub(crate) fn runnable_args(
10 world: &WorldSnapshot, 7 world: &WorldSnapshot,
@@ -66,7 +63,7 @@ impl CargoTargetSpec {
66 let file_id = world.analysis().crate_root(crate_id)?; 63 let file_id = world.analysis().crate_root(crate_id)?;
67 let path = world.vfs.read().file2path(ra_vfs::VfsFile(file_id.0)); 64 let path = world.vfs.read().file2path(ra_vfs::VfsFile(file_id.0));
68 let res = world.workspaces.iter().find_map(|ws| match ws { 65 let res = world.workspaces.iter().find_map(|ws| match ws {
69 project_model::ProjectWorkspace::Cargo { cargo, .. } => { 66 ProjectWorkspace::Cargo { cargo, .. } => {
70 let tgt = cargo.target_by_root(&path)?; 67 let tgt = cargo.target_by_root(&path)?;
71 Some(CargoTargetSpec { 68 Some(CargoTargetSpec {
72 package: tgt.package(&cargo).name(&cargo).to_string(), 69 package: tgt.package(&cargo).name(&cargo).to_string(),
@@ -74,7 +71,7 @@ impl CargoTargetSpec {
74 target_kind: tgt.kind(&cargo), 71 target_kind: tgt.kind(&cargo),
75 }) 72 })
76 } 73 }
77 project_model::ProjectWorkspace::Json { .. } => None, 74 ProjectWorkspace::Json { .. } => None,
78 }); 75 });
79 Ok(res) 76 Ok(res)
80 } 77 }
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs
index 69a577b3e..fa3d88abd 100644
--- a/crates/ra_lsp_server/src/lib.rs
+++ b/crates/ra_lsp_server/src/lib.rs
@@ -4,11 +4,9 @@ mod cargo_target_spec;
4mod conv; 4mod conv;
5mod main_loop; 5mod main_loop;
6mod markdown; 6mod markdown;
7mod project_model;
8pub mod req; 7pub mod req;
9pub mod config; 8pub mod config;
10mod world; 9mod world;
11mod thread_worker;
12 10
13pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 11pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
14pub use crate::{ 12pub use crate::{
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index 88504bb89..d40fed947 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -5,37 +5,37 @@ use ra_lsp_server::{show_message, Result, ServerConfig};
5use ra_prof; 5use ra_prof;
6 6
7fn main() -> Result<()> { 7fn main() -> Result<()> {
8 setup_logging()?;
9 run_server()?;
10 Ok(())
11}
12
13fn setup_logging() -> Result<()> {
8 std::env::set_var("RUST_BACKTRACE", "short"); 14 std::env::set_var("RUST_BACKTRACE", "short");
15
9 let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All); 16 let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All);
10 match std::env::var("RA_LOG_DIR") { 17 match std::env::var("RA_LOG_DIR") {
11 Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?, 18 Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?,
12 _ => logger.start()?, 19 _ => logger.start()?,
13 }; 20 };
21
14 ra_prof::set_filter(match std::env::var("RA_PROFILE") { 22 ra_prof::set_filter(match std::env::var("RA_PROFILE") {
15 Ok(spec) => ra_prof::Filter::from_spec(&spec), 23 Ok(spec) => ra_prof::Filter::from_spec(&spec),
16 Err(_) => ra_prof::Filter::disabled(), 24 Err(_) => ra_prof::Filter::disabled(),
17 }); 25 });
18 log::info!("lifecycle: server started"); 26 Ok(())
19 match std::panic::catch_unwind(main_inner) {
20 Ok(res) => {
21 log::info!("lifecycle: terminating process with {:?}", res);
22 res
23 }
24 Err(_) => {
25 log::error!("server panicked");
26 Err("server panicked")?
27 }
28 }
29} 27}
30 28
31fn main_inner() -> Result<()> { 29fn run_server() -> Result<()> {
32 let cwd = std::env::current_dir()?; 30 log::info!("lifecycle: server started");
31
33 let (connection, io_threads) = Connection::stdio(); 32 let (connection, io_threads) = Connection::stdio();
34 let server_capabilities = serde_json::to_value(ra_lsp_server::server_capabilities()).unwrap(); 33 let server_capabilities = serde_json::to_value(ra_lsp_server::server_capabilities()).unwrap();
35 34
36 let initialize_params = connection.initialize(server_capabilities)?; 35 let initialize_params = connection.initialize(server_capabilities)?;
37 let initialize_params: lsp_types::InitializeParams = serde_json::from_value(initialize_params)?; 36 let initialize_params: lsp_types::InitializeParams = serde_json::from_value(initialize_params)?;
38 37
38 let cwd = std::env::current_dir()?;
39 let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); 39 let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
40 40
41 let workspace_roots = initialize_params 41 let workspace_roots = initialize_params
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 42ebb5cdf..80f0216e8 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -4,12 +4,13 @@ pub(crate) mod pending_requests;
4 4
5use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; 5use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant};
6 6
7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; 7use crossbeam_channel::{select, unbounded, RecvError, Sender};
8use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 8use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
9use lsp_types::{ClientCapabilities, NumberOrString}; 9use lsp_types::{ClientCapabilities, NumberOrString};
10use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; 10use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId};
11use ra_prof::profile; 11use ra_prof::profile;
12use ra_vfs::VfsTask; 12use ra_vfs::VfsTask;
13use relative_path::RelativePathBuf;
13use serde::{de::DeserializeOwned, Serialize}; 14use serde::{de::DeserializeOwned, Serialize};
14use threadpool::ThreadPool; 15use threadpool::ThreadPool;
15 16
@@ -18,7 +19,6 @@ use crate::{
18 pending_requests::{PendingRequest, PendingRequests}, 19 pending_requests::{PendingRequest, PendingRequests},
19 subscriptions::Subscriptions, 20 subscriptions::Subscriptions,
20 }, 21 },
21 project_model::workspace_loader,
22 req, 22 req,
23 world::{Options, WorldSnapshot, WorldState}, 23 world::{Options, WorldSnapshot, WorldState},
24 Result, ServerConfig, 24 Result, ServerConfig,
@@ -54,14 +54,17 @@ pub fn main_loop(
54 connection: &Connection, 54 connection: &Connection,
55) -> Result<()> { 55) -> Result<()> {
56 log::info!("server_config: {:#?}", config); 56 log::info!("server_config: {:#?}", config);
57
57 // FIXME: support dynamic workspace loading. 58 // FIXME: support dynamic workspace loading.
58 let workspaces = { 59 let workspaces = {
59 let ws_worker = workspace_loader(config.with_sysroot);
60 let mut loaded_workspaces = Vec::new(); 60 let mut loaded_workspaces = Vec::new();
61 for ws_root in &ws_roots { 61 for ws_root in &ws_roots {
62 ws_worker.sender().send(ws_root.clone()).unwrap(); 62 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot(
63 match ws_worker.receiver().recv().unwrap() { 63 ws_root.as_path(),
64 Ok(ws) => loaded_workspaces.push(ws), 64 config.with_sysroot,
65 );
66 match workspace {
67 Ok(workspace) => loaded_workspaces.push(workspace),
65 Err(e) => { 68 Err(e) => {
66 log::error!("loading workspace failed: {}", e); 69 log::error!("loading workspace failed: {}", e);
67 70
@@ -75,11 +78,13 @@ pub fn main_loop(
75 } 78 }
76 loaded_workspaces 79 loaded_workspaces
77 }; 80 };
81
78 let globs = config 82 let globs = config
79 .exclude_globs 83 .exclude_globs
80 .iter() 84 .iter()
81 .map(|glob| ra_vfs_glob::Glob::new(glob)) 85 .map(|glob| ra_vfs_glob::Glob::new(glob))
82 .collect::<std::result::Result<Vec<_>, _>>()?; 86 .collect::<std::result::Result<Vec<_>, _>>()?;
87
83 let feature_flags = { 88 let feature_flags = {
84 let mut ff = FeatureFlags::default(); 89 let mut ff = FeatureFlags::default();
85 for (flag, value) in config.feature_flags { 90 for (flag, value) in config.feature_flags {
@@ -95,7 +100,8 @@ pub fn main_loop(
95 ff 100 ff
96 }; 101 };
97 log::info!("feature_flags: {:#?}", feature_flags); 102 log::info!("feature_flags: {:#?}", feature_flags);
98 let mut state = WorldState::new( 103
104 let mut world_state = WorldState::new(
99 ws_roots, 105 ws_roots,
100 workspaces, 106 workspaces,
101 config.lru_capacity, 107 config.lru_capacity,
@@ -113,31 +119,58 @@ pub fn main_loop(
113 119
114 let pool = ThreadPool::new(THREADPOOL_SIZE); 120 let pool = ThreadPool::new(THREADPOOL_SIZE);
115 let (task_sender, task_receiver) = unbounded::<Task>(); 121 let (task_sender, task_receiver) = unbounded::<Task>();
116 let mut pending_requests = PendingRequests::default(); 122 let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>();
123 let mut loop_state = LoopState::default();
117 124
118 log::info!("server initialized, serving requests"); 125 log::info!("server initialized, serving requests");
119 let main_res = main_loop_inner( 126 {
120 &pool, 127 let task_sender = task_sender;
121 connection, 128 let libdata_sender = libdata_sender;
122 task_sender, 129 loop {
123 task_receiver.clone(), 130 log::trace!("selecting");
124 &mut state, 131 let event = select! {
125 &mut pending_requests, 132 recv(&connection.receiver) -> msg => match msg {
126 ); 133 Ok(msg) => Event::Msg(msg),
134 Err(RecvError) => Err("client exited without shutdown")?,
135 },
136 recv(task_receiver) -> task => Event::Task(task.unwrap()),
137 recv(world_state.task_receiver) -> task => match task {
138 Ok(task) => Event::Vfs(task),
139 Err(RecvError) => Err("vfs died")?,
140 },
141 recv(libdata_receiver) -> data => Event::Lib(data.unwrap())
142 };
143 if let Event::Msg(Message::Request(req)) = &event {
144 if connection.handle_shutdown(&req)? {
145 break;
146 };
147 }
148 loop_turn(
149 &pool,
150 &task_sender,
151 &libdata_sender,
152 connection,
153 &mut world_state,
154 &mut loop_state,
155 event,
156 )?;
157 }
158 }
127 159
128 log::info!("waiting for tasks to finish..."); 160 log::info!("waiting for tasks to finish...");
129 task_receiver 161 task_receiver.into_iter().for_each(|task| {
130 .into_iter() 162 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state)
131 .for_each(|task| on_task(task, &connection.sender, &mut pending_requests, &mut state)); 163 });
164 libdata_receiver.into_iter().for_each(|lib| drop(lib));
132 log::info!("...tasks have finished"); 165 log::info!("...tasks have finished");
133 log::info!("joining threadpool..."); 166 log::info!("joining threadpool...");
134 drop(pool); 167 drop(pool);
135 log::info!("...threadpool has finished"); 168 log::info!("...threadpool has finished");
136 169
137 let vfs = Arc::try_unwrap(state.vfs).expect("all snapshots should be dead"); 170 let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead");
138 drop(vfs); 171 drop(vfs);
139 172
140 main_res 173 Ok(())
141} 174}
142 175
143#[derive(Debug)] 176#[derive(Debug)]
@@ -192,121 +225,113 @@ impl fmt::Debug for Event {
192 } 225 }
193} 226}
194 227
195fn main_loop_inner( 228#[derive(Debug, Default)]
229struct LoopState {
230 pending_requests: PendingRequests,
231 subscriptions: Subscriptions,
232 // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same
233 // time to always have a thread ready to react to input.
234 in_flight_libraries: usize,
235 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
236 workspace_loaded: bool,
237}
238
239fn loop_turn(
196 pool: &ThreadPool, 240 pool: &ThreadPool,
241 task_sender: &Sender<Task>,
242 libdata_sender: &Sender<LibraryData>,
197 connection: &Connection, 243 connection: &Connection,
198 task_sender: Sender<Task>, 244 world_state: &mut WorldState,
199 task_receiver: Receiver<Task>, 245 loop_state: &mut LoopState,
200 state: &mut WorldState, 246 event: Event,
201 pending_requests: &mut PendingRequests,
202) -> Result<()> { 247) -> Result<()> {
203 let mut subs = Subscriptions::default(); 248 let loop_start = Instant::now();
204 // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same 249
205 // time to always have a thread ready to react to input. 250 // NOTE: don't count blocking select! call as a loop-turn time
206 let mut in_flight_libraries = 0; 251 let _p = profile("main_loop_inner/loop-turn");
207 let mut pending_libraries = Vec::new(); 252 log::info!("loop turn = {:?}", event);
208 let mut send_workspace_notification = true; 253 let queue_count = pool.queued_count();
209 254 if queue_count > 0 {
210 let (libdata_sender, libdata_receiver) = unbounded(); 255 log::info!("queued count = {}", queue_count);
211 loop { 256 }
212 log::trace!("selecting");
213 let event = select! {
214 recv(&connection.receiver) -> msg => match msg {
215 Ok(msg) => Event::Msg(msg),
216 Err(RecvError) => Err("client exited without shutdown")?,
217 },
218 recv(task_receiver) -> task => Event::Task(task.unwrap()),
219 recv(state.task_receiver) -> task => match task {
220 Ok(task) => Event::Vfs(task),
221 Err(RecvError) => Err("vfs died")?,
222 },
223 recv(libdata_receiver) -> data => Event::Lib(data.unwrap())
224 };
225 let loop_start = Instant::now();
226
227 // NOTE: don't count blocking select! call as a loop-turn time
228 let _p = profile("main_loop_inner/loop-turn");
229 log::info!("loop turn = {:?}", event);
230 let queue_count = pool.queued_count();
231 if queue_count > 0 {
232 log::info!("queued count = {}", queue_count);
233 }
234 257
235 let mut state_changed = false; 258 let mut state_changed = false;
236 match event { 259 match event {
237 Event::Task(task) => { 260 Event::Task(task) => {
238 on_task(task, &connection.sender, pending_requests, state); 261 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state);
239 state.maybe_collect_garbage(); 262 world_state.maybe_collect_garbage();
240 } 263 }
241 Event::Vfs(task) => { 264 Event::Vfs(task) => {
242 state.vfs.write().handle_task(task); 265 world_state.vfs.write().handle_task(task);
266 state_changed = true;
267 }
268 Event::Lib(lib) => {
269 world_state.add_lib(lib);
270 world_state.maybe_collect_garbage();
271 loop_state.in_flight_libraries -= 1;
272 }
273 Event::Msg(msg) => match msg {
274 Message::Request(req) => on_request(
275 world_state,
276 &mut loop_state.pending_requests,
277 pool,
278 task_sender,
279 &connection.sender,
280 loop_start,
281 req,
282 )?,
283 Message::Notification(not) => {
284 on_notification(
285 &connection.sender,
286 world_state,
287 &mut loop_state.pending_requests,
288 &mut loop_state.subscriptions,
289 not,
290 )?;
243 state_changed = true; 291 state_changed = true;
244 } 292 }
245 Event::Lib(lib) => { 293 Message::Response(resp) => log::error!("unexpected response: {:?}", resp),
246 state.add_lib(lib); 294 },
247 state.maybe_collect_garbage(); 295 };
248 in_flight_libraries -= 1;
249 }
250 Event::Msg(msg) => match msg {
251 Message::Request(req) => {
252 if connection.handle_shutdown(&req)? {
253 return Ok(());
254 };
255 on_request(
256 state,
257 pending_requests,
258 pool,
259 &task_sender,
260 &connection.sender,
261 loop_start,
262 req,
263 )?
264 }
265 Message::Notification(not) => {
266 on_notification(&connection.sender, state, pending_requests, &mut subs, not)?;
267 state_changed = true;
268 }
269 Message::Response(resp) => log::error!("unexpected response: {:?}", resp),
270 },
271 };
272 296
273 pending_libraries.extend(state.process_changes()); 297 loop_state.pending_libraries.extend(world_state.process_changes());
274 while in_flight_libraries < MAX_IN_FLIGHT_LIBS && !pending_libraries.is_empty() { 298 while loop_state.in_flight_libraries < MAX_IN_FLIGHT_LIBS
275 let (root, files) = pending_libraries.pop().unwrap(); 299 && !loop_state.pending_libraries.is_empty()
276 in_flight_libraries += 1; 300 {
277 let sender = libdata_sender.clone(); 301 let (root, files) = loop_state.pending_libraries.pop().unwrap();
278 pool.execute(move || { 302 loop_state.in_flight_libraries += 1;
279 log::info!("indexing {:?} ... ", root); 303 let sender = libdata_sender.clone();
280 let _p = profile(&format!("indexed {:?}", root)); 304 pool.execute(move || {
281 let data = LibraryData::prepare(root, files); 305 log::info!("indexing {:?} ... ", root);
282 sender.send(data).unwrap(); 306 let _p = profile(&format!("indexed {:?}", root));
283 }); 307 let data = LibraryData::prepare(root, files);
284 } 308 sender.send(data).unwrap();
309 });
310 }
285 311
286 if send_workspace_notification 312 if !loop_state.workspace_loaded
287 && state.roots_to_scan == 0 313 && world_state.roots_to_scan == 0
288 && pending_libraries.is_empty() 314 && loop_state.pending_libraries.is_empty()
289 && in_flight_libraries == 0 315 && loop_state.in_flight_libraries == 0
290 { 316 {
291 let n_packages: usize = state.workspaces.iter().map(|it| it.n_packages()).sum(); 317 loop_state.workspace_loaded = true;
292 if state.feature_flags().get("notifications.workspace-loaded") { 318 let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
293 let msg = format!("workspace loaded, {} rust packages", n_packages); 319 if world_state.feature_flags().get("notifications.workspace-loaded") {
294 show_message(req::MessageType::Info, msg, &connection.sender); 320 let msg = format!("workspace loaded, {} rust packages", n_packages);
295 } 321 show_message(req::MessageType::Info, msg, &connection.sender);
296 // Only send the notification first time
297 send_workspace_notification = false;
298 } 322 }
323 }
299 324
300 if state_changed { 325 if state_changed {
301 update_file_notifications_on_threadpool( 326 update_file_notifications_on_threadpool(
302 pool, 327 pool,
303 state.snapshot(), 328 world_state.snapshot(),
304 state.options.publish_decorations, 329 world_state.options.publish_decorations,
305 task_sender.clone(), 330 task_sender.clone(),
306 subs.subscriptions(), 331 loop_state.subscriptions.subscriptions(),
307 ) 332 )
308 }
309 } 333 }
334 Ok(())
310} 335}
311 336
312fn on_task( 337fn on_task(
diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
index 470bc1205..bbeda723c 100644
--- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs
+++ b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
@@ -1,7 +1,7 @@
1use ra_ide_api::FileId; 1use ra_ide_api::FileId;
2use rustc_hash::FxHashSet; 2use rustc_hash::FxHashSet;
3 3
4#[derive(Default)] 4#[derive(Default, Debug)]
5pub(crate) struct Subscriptions { 5pub(crate) struct Subscriptions {
6 subs: FxHashSet<FileId>, 6 subs: FxHashSet<FileId>,
7} 7}
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
deleted file mode 100644
index 6234563f2..000000000
--- a/crates/ra_lsp_server/src/project_model.rs
+++ /dev/null
@@ -1,21 +0,0 @@
1use std::path::PathBuf;
2
3use crate::{thread_worker::Worker, Result};
4
5pub use ra_project_model::{
6 CargoWorkspace, Package, ProjectWorkspace, Sysroot, Target, TargetKind,
7};
8
9pub fn workspace_loader(with_sysroot: bool) -> Worker<PathBuf, Result<ProjectWorkspace>> {
10 Worker::<PathBuf, Result<ProjectWorkspace>>::spawn(
11 "workspace loader",
12 1,
13 move |input_receiver, output_sender| {
14 input_receiver
15 .into_iter()
16 .map(|path| ProjectWorkspace::discover_with_sysroot(path.as_path(), with_sysroot))
17 .try_for_each(|it| output_sender.send(it))
18 .unwrap()
19 },
20 )
21}
diff --git a/crates/ra_lsp_server/src/thread_worker.rs b/crates/ra_lsp_server/src/thread_worker.rs
deleted file mode 100644
index 68e5c124d..000000000
--- a/crates/ra_lsp_server/src/thread_worker.rs
+++ /dev/null
@@ -1,49 +0,0 @@
1//! Small utility to correctly spawn crossbeam-channel based worker threads.
2
3use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
4
5/// A wrapper around event-processing thread with automatic shutdown semantics.
6pub struct Worker<I, O> {
7 // XXX: field order is significant here.
8 //
9 // In Rust, fields are dropped in the declaration order, and we rely on this
10 // here. We must close input first, so that the `thread` (who holds the
11 // opposite side of the channel) noticed shutdown. Then, we must join the
12 // thread, but we must keep out alive so that the thread does not panic.
13 //
14 // Note that a potential problem here is that we might drop some messages
15 // from receiver on the floor. This is ok for rust-analyzer: we have only a
16 // single client, so, if we are shutting down, nobody is interested in the
17 // unfinished work anyway!
18 sender: Sender<I>,
19 _thread: jod_thread::JoinHandle<()>,
20 receiver: Receiver<O>,
21}
22
23impl<I, O> Worker<I, O> {
24 pub fn spawn<F>(name: &'static str, buf: usize, f: F) -> Worker<I, O>
25 where
26 F: FnOnce(Receiver<I>, Sender<O>) + Send + 'static,
27 I: Send + 'static,
28 O: Send + 'static,
29 {
30 // Set up worker channels in a deadlock-avoiding way. If one sets both input
31 // and output buffers to a fixed size, a worker might get stuck.
32 let (sender, input_receiver) = bounded::<I>(buf);
33 let (output_sender, receiver) = unbounded::<O>();
34 let _thread = jod_thread::Builder::new()
35 .name(name.to_string())
36 .spawn(move || f(input_receiver, output_sender))
37 .expect("failed to spawn a thread");
38 Worker { sender, _thread, receiver }
39 }
40}
41
42impl<I, O> Worker<I, O> {
43 pub fn sender(&self) -> &Sender<I> {
44 &self.sender
45 }
46 pub fn receiver(&self) -> &Receiver<O> {
47 &self.receiver
48 }
49}
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 73d7f8fb9..e1c5c3343 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -11,13 +11,13 @@ use ra_ide_api::{
11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
12 SourceRootId, 12 SourceRootId,
13}; 13};
14use ra_project_model::ProjectWorkspace;
14use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask}; 15use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask};
15use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 16use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
16use relative_path::RelativePathBuf; 17use relative_path::RelativePathBuf;
17 18
18use crate::{ 19use crate::{
19 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 20 main_loop::pending_requests::{CompletedRequest, LatestRequests},
20 project_model::ProjectWorkspace,
21 LspError, Result, 21 LspError, Result,
22}; 22};
23 23
@@ -35,6 +35,7 @@ pub struct Options {
35#[derive(Debug)] 35#[derive(Debug)]
36pub struct WorldState { 36pub struct WorldState {
37 pub options: Options, 37 pub options: Options,
38 //FIXME: this belongs to `LoopState` rather than to `WorldState`
38 pub roots_to_scan: usize, 39 pub roots_to_scan: usize,
39 pub roots: Vec<PathBuf>, 40 pub roots: Vec<PathBuf>,
40 pub workspaces: Arc<Vec<ProjectWorkspace>>, 41 pub workspaces: Arc<Vec<ProjectWorkspace>>,