diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_lsp_server/src/cargo_target_spec.rs | 13 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 26 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 277 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/subscriptions.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/project_model.rs | 21 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/thread_worker.rs | 49 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 3 |
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 @@ | |||
1 | use crate::{ | ||
2 | project_model::{self, TargetKind}, | ||
3 | world::WorldSnapshot, | ||
4 | Result, | ||
5 | }; | ||
6 | |||
7 | use ra_ide_api::{FileId, RunnableKind}; | 1 | use ra_ide_api::{FileId, RunnableKind}; |
2 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | ||
3 | |||
4 | use crate::{world::WorldSnapshot, Result}; | ||
8 | 5 | ||
9 | pub(crate) fn runnable_args( | 6 | pub(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; | |||
4 | mod conv; | 4 | mod conv; |
5 | mod main_loop; | 5 | mod main_loop; |
6 | mod markdown; | 6 | mod markdown; |
7 | mod project_model; | ||
8 | pub mod req; | 7 | pub mod req; |
9 | pub mod config; | 8 | pub mod config; |
10 | mod world; | 9 | mod world; |
11 | mod thread_worker; | ||
12 | 10 | ||
13 | pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; | 11 | pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; |
14 | pub use crate::{ | 12 | pub 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}; | |||
5 | use ra_prof; | 5 | use ra_prof; |
6 | 6 | ||
7 | fn main() -> Result<()> { | 7 | fn main() -> Result<()> { |
8 | setup_logging()?; | ||
9 | run_server()?; | ||
10 | Ok(()) | ||
11 | } | ||
12 | |||
13 | fn 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 | ||
31 | fn main_inner() -> Result<()> { | 29 | fn 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 | ||
5 | use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; | 5 | use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; |
6 | 6 | ||
7 | use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; | 7 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; |
8 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 8 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
9 | use lsp_types::{ClientCapabilities, NumberOrString}; | 9 | use lsp_types::{ClientCapabilities, NumberOrString}; |
10 | use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; | 10 | use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | use ra_vfs::VfsTask; | 12 | use ra_vfs::VfsTask; |
13 | use relative_path::RelativePathBuf; | ||
13 | use serde::{de::DeserializeOwned, Serialize}; | 14 | use serde::{de::DeserializeOwned, Serialize}; |
14 | use threadpool::ThreadPool; | 15 | use 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 | ||
195 | fn main_loop_inner( | 228 | #[derive(Debug, Default)] |
229 | struct 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 | |||
239 | fn 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 | ||
312 | fn on_task( | 337 | fn 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 @@ | |||
1 | use ra_ide_api::FileId; | 1 | use ra_ide_api::FileId; |
2 | use rustc_hash::FxHashSet; | 2 | use rustc_hash::FxHashSet; |
3 | 3 | ||
4 | #[derive(Default)] | 4 | #[derive(Default, Debug)] |
5 | pub(crate) struct Subscriptions { | 5 | pub(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 @@ | |||
1 | use std::path::PathBuf; | ||
2 | |||
3 | use crate::{thread_worker::Worker, Result}; | ||
4 | |||
5 | pub use ra_project_model::{ | ||
6 | CargoWorkspace, Package, ProjectWorkspace, Sysroot, Target, TargetKind, | ||
7 | }; | ||
8 | |||
9 | pub 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 | |||
3 | use crossbeam_channel::{bounded, unbounded, Receiver, Sender}; | ||
4 | |||
5 | /// A wrapper around event-processing thread with automatic shutdown semantics. | ||
6 | pub 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 | |||
23 | impl<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 | |||
42 | impl<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 | }; |
14 | use ra_project_model::ProjectWorkspace; | ||
14 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask}; | 15 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask}; |
15 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 16 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
16 | use relative_path::RelativePathBuf; | 17 | use relative_path::RelativePathBuf; |
17 | 18 | ||
18 | use crate::{ | 19 | use 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)] |
36 | pub struct WorldState { | 36 | pub 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>>, |