diff options
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r-- | crates/ra_lsp_server/src/cargo_target_spec.rs | 6 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 50 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 342 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 74 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/pending_requests.rs | 72 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/subscriptions.rs | 12 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs (renamed from crates/ra_lsp_server/src/server_world.rs) | 58 |
8 files changed, 348 insertions, 268 deletions
diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs index cdf2ec10b..742361155 100644 --- a/crates/ra_lsp_server/src/cargo_target_spec.rs +++ b/crates/ra_lsp_server/src/cargo_target_spec.rs | |||
@@ -1,13 +1,13 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | project_model::{self, TargetKind}, | 2 | project_model::{self, TargetKind}, |
3 | server_world::ServerWorld, | 3 | world::WorldSnapshot, |
4 | Result | 4 | Result |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use ra_ide_api::{FileId, RunnableKind}; | 7 | use ra_ide_api::{FileId, RunnableKind}; |
8 | 8 | ||
9 | pub(crate) fn runnable_args( | 9 | pub(crate) fn runnable_args( |
10 | world: &ServerWorld, | 10 | world: &WorldSnapshot, |
11 | file_id: FileId, | 11 | file_id: FileId, |
12 | kind: &RunnableKind, | 12 | kind: &RunnableKind, |
13 | ) -> Result<Vec<String>> { | 13 | ) -> Result<Vec<String>> { |
@@ -58,7 +58,7 @@ pub struct CargoTargetSpec { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | impl CargoTargetSpec { | 60 | impl CargoTargetSpec { |
61 | pub fn for_file(world: &ServerWorld, file_id: FileId) -> Result<Option<CargoTargetSpec>> { | 61 | pub fn for_file(world: &WorldSnapshot, file_id: FileId) -> Result<Option<CargoTargetSpec>> { |
62 | let &crate_id = match world.analysis().crate_for(file_id)?.first() { | 62 | let &crate_id = match world.analysis().crate_for(file_id)?.first() { |
63 | Some(crate_id) => crate_id, | 63 | Some(crate_id) => crate_id, |
64 | None => return Ok(None), | 64 | None => return Ok(None), |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 1b349d02a..88d29b256 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -12,7 +12,7 @@ use ra_ide_api::{ | |||
12 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 12 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
13 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 13 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
14 | 14 | ||
15 | use crate::{req, server_world::ServerWorld, Result}; | 15 | use crate::{req, world::WorldSnapshot, Result}; |
16 | 16 | ||
17 | pub trait Conv { | 17 | pub trait Conv { |
18 | type Output; | 18 | type Output; |
@@ -228,49 +228,49 @@ impl<T: ConvWith> ConvWith for Option<T> { | |||
228 | } | 228 | } |
229 | 229 | ||
230 | impl<'a> TryConvWith for &'a Url { | 230 | impl<'a> TryConvWith for &'a Url { |
231 | type Ctx = ServerWorld; | 231 | type Ctx = WorldSnapshot; |
232 | type Output = FileId; | 232 | type Output = FileId; |
233 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | 233 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { |
234 | world.uri_to_file_id(self) | 234 | world.uri_to_file_id(self) |
235 | } | 235 | } |
236 | } | 236 | } |
237 | 237 | ||
238 | impl TryConvWith for FileId { | 238 | impl TryConvWith for FileId { |
239 | type Ctx = ServerWorld; | 239 | type Ctx = WorldSnapshot; |
240 | type Output = Url; | 240 | type Output = Url; |
241 | fn try_conv_with(self, world: &ServerWorld) -> Result<Url> { | 241 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Url> { |
242 | world.file_id_to_uri(self) | 242 | world.file_id_to_uri(self) |
243 | } | 243 | } |
244 | } | 244 | } |
245 | 245 | ||
246 | impl<'a> TryConvWith for &'a TextDocumentItem { | 246 | impl<'a> TryConvWith for &'a TextDocumentItem { |
247 | type Ctx = ServerWorld; | 247 | type Ctx = WorldSnapshot; |
248 | type Output = FileId; | 248 | type Output = FileId; |
249 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | 249 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { |
250 | self.uri.try_conv_with(world) | 250 | self.uri.try_conv_with(world) |
251 | } | 251 | } |
252 | } | 252 | } |
253 | 253 | ||
254 | impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { | 254 | impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { |
255 | type Ctx = ServerWorld; | 255 | type Ctx = WorldSnapshot; |
256 | type Output = FileId; | 256 | type Output = FileId; |
257 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | 257 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { |
258 | self.uri.try_conv_with(world) | 258 | self.uri.try_conv_with(world) |
259 | } | 259 | } |
260 | } | 260 | } |
261 | 261 | ||
262 | impl<'a> TryConvWith for &'a TextDocumentIdentifier { | 262 | impl<'a> TryConvWith for &'a TextDocumentIdentifier { |
263 | type Ctx = ServerWorld; | 263 | type Ctx = WorldSnapshot; |
264 | type Output = FileId; | 264 | type Output = FileId; |
265 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileId> { | 265 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { |
266 | world.uri_to_file_id(&self.uri) | 266 | world.uri_to_file_id(&self.uri) |
267 | } | 267 | } |
268 | } | 268 | } |
269 | 269 | ||
270 | impl<'a> TryConvWith for &'a TextDocumentPositionParams { | 270 | impl<'a> TryConvWith for &'a TextDocumentPositionParams { |
271 | type Ctx = ServerWorld; | 271 | type Ctx = WorldSnapshot; |
272 | type Output = FilePosition; | 272 | type Output = FilePosition; |
273 | fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> { | 273 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FilePosition> { |
274 | let file_id = self.text_document.try_conv_with(world)?; | 274 | let file_id = self.text_document.try_conv_with(world)?; |
275 | let line_index = world.analysis().file_line_index(file_id); | 275 | let line_index = world.analysis().file_line_index(file_id); |
276 | let offset = self.position.conv_with(&line_index); | 276 | let offset = self.position.conv_with(&line_index); |
@@ -279,9 +279,9 @@ impl<'a> TryConvWith for &'a TextDocumentPositionParams { | |||
279 | } | 279 | } |
280 | 280 | ||
281 | impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) { | 281 | impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) { |
282 | type Ctx = ServerWorld; | 282 | type Ctx = WorldSnapshot; |
283 | type Output = FileRange; | 283 | type Output = FileRange; |
284 | fn try_conv_with(self, world: &ServerWorld) -> Result<FileRange> { | 284 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileRange> { |
285 | let file_id = self.0.try_conv_with(world)?; | 285 | let file_id = self.0.try_conv_with(world)?; |
286 | let line_index = world.analysis().file_line_index(file_id); | 286 | let line_index = world.analysis().file_line_index(file_id); |
287 | let range = self.1.conv_with(&line_index); | 287 | let range = self.1.conv_with(&line_index); |
@@ -302,9 +302,9 @@ impl<T: TryConvWith> TryConvWith for Vec<T> { | |||
302 | } | 302 | } |
303 | 303 | ||
304 | impl TryConvWith for SourceChange { | 304 | impl TryConvWith for SourceChange { |
305 | type Ctx = ServerWorld; | 305 | type Ctx = WorldSnapshot; |
306 | type Output = req::SourceChange; | 306 | type Output = req::SourceChange; |
307 | fn try_conv_with(self, world: &ServerWorld) -> Result<req::SourceChange> { | 307 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::SourceChange> { |
308 | let cursor_position = match self.cursor_position { | 308 | let cursor_position = match self.cursor_position { |
309 | None => None, | 309 | None => None, |
310 | Some(pos) => { | 310 | Some(pos) => { |
@@ -342,9 +342,9 @@ impl TryConvWith for SourceChange { | |||
342 | } | 342 | } |
343 | 343 | ||
344 | impl TryConvWith for SourceFileEdit { | 344 | impl TryConvWith for SourceFileEdit { |
345 | type Ctx = ServerWorld; | 345 | type Ctx = WorldSnapshot; |
346 | type Output = TextDocumentEdit; | 346 | type Output = TextDocumentEdit; |
347 | fn try_conv_with(self, world: &ServerWorld) -> Result<TextDocumentEdit> { | 347 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<TextDocumentEdit> { |
348 | let text_document = VersionedTextDocumentIdentifier { | 348 | let text_document = VersionedTextDocumentIdentifier { |
349 | uri: self.file_id.try_conv_with(world)?, | 349 | uri: self.file_id.try_conv_with(world)?, |
350 | version: None, | 350 | version: None, |
@@ -356,9 +356,9 @@ impl TryConvWith for SourceFileEdit { | |||
356 | } | 356 | } |
357 | 357 | ||
358 | impl TryConvWith for FileSystemEdit { | 358 | impl TryConvWith for FileSystemEdit { |
359 | type Ctx = ServerWorld; | 359 | type Ctx = WorldSnapshot; |
360 | type Output = ResourceOp; | 360 | type Output = ResourceOp; |
361 | fn try_conv_with(self, world: &ServerWorld) -> Result<ResourceOp> { | 361 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<ResourceOp> { |
362 | let res = match self { | 362 | let res = match self { |
363 | FileSystemEdit::CreateFile { source_root, path } => { | 363 | FileSystemEdit::CreateFile { source_root, path } => { |
364 | let uri = world.path_to_uri(source_root, &path)?; | 364 | let uri = world.path_to_uri(source_root, &path)?; |
@@ -375,9 +375,9 @@ impl TryConvWith for FileSystemEdit { | |||
375 | } | 375 | } |
376 | 376 | ||
377 | impl TryConvWith for &NavigationTarget { | 377 | impl TryConvWith for &NavigationTarget { |
378 | type Ctx = ServerWorld; | 378 | type Ctx = WorldSnapshot; |
379 | type Output = Location; | 379 | type Output = Location; |
380 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { | 380 | fn try_conv_with(self, world: &WorldSnapshot) -> Result<Location> { |
381 | let line_index = world.analysis().file_line_index(self.file_id()); | 381 | let line_index = world.analysis().file_line_index(self.file_id()); |
382 | let range = self.range(); | 382 | let range = self.range(); |
383 | to_location(self.file_id(), range, &world, &line_index) | 383 | to_location(self.file_id(), range, &world, &line_index) |
@@ -386,7 +386,7 @@ impl TryConvWith for &NavigationTarget { | |||
386 | 386 | ||
387 | pub fn to_location_link( | 387 | pub fn to_location_link( |
388 | target: &RangeInfo<NavigationTarget>, | 388 | target: &RangeInfo<NavigationTarget>, |
389 | world: &ServerWorld, | 389 | world: &WorldSnapshot, |
390 | // line index for original range file | 390 | // line index for original range file |
391 | line_index: &LineIndex, | 391 | line_index: &LineIndex, |
392 | ) -> Result<LocationLink> { | 392 | ) -> Result<LocationLink> { |
@@ -410,7 +410,7 @@ pub fn to_location_link( | |||
410 | pub fn to_location( | 410 | pub fn to_location( |
411 | file_id: FileId, | 411 | file_id: FileId, |
412 | range: TextRange, | 412 | range: TextRange, |
413 | world: &ServerWorld, | 413 | world: &WorldSnapshot, |
414 | line_index: &LineIndex, | 414 | line_index: &LineIndex, |
415 | ) -> Result<Location> { | 415 | ) -> Result<Location> { |
416 | let url = file_id.try_conv_with(world)?; | 416 | let url = file_id.try_conv_with(world)?; |
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs index 113883bdd..aabde420b 100644 --- a/crates/ra_lsp_server/src/lib.rs +++ b/crates/ra_lsp_server/src/lib.rs | |||
@@ -7,7 +7,7 @@ mod project_model; | |||
7 | mod vfs_filter; | 7 | mod vfs_filter; |
8 | pub mod req; | 8 | pub mod req; |
9 | pub mod init; | 9 | pub mod init; |
10 | mod server_world; | 10 | mod world; |
11 | 11 | ||
12 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 12 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
13 | pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError, init::InitializationOptions}; | 13 | pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError, init::InitializationOptions}; |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index d29ba94e7..6080a3a4e 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -1,7 +1,8 @@ | |||
1 | mod handlers; | 1 | mod handlers; |
2 | mod subscriptions; | 2 | mod subscriptions; |
3 | pub(crate) mod pending_requests; | ||
3 | 4 | ||
4 | use std::{fmt, path::PathBuf, sync::Arc, time::Instant, any::TypeId}; | 5 | use std::{fmt, path::PathBuf, sync::Arc, time::Instant}; |
5 | 6 | ||
6 | use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; | 7 | use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; |
7 | use failure::{bail, format_err}; | 8 | use failure::{bail, format_err}; |
@@ -12,19 +13,24 @@ use gen_lsp_server::{ | |||
12 | use lsp_types::NumberOrString; | 13 | use lsp_types::NumberOrString; |
13 | use ra_ide_api::{Canceled, FileId, LibraryData}; | 14 | use ra_ide_api::{Canceled, FileId, LibraryData}; |
14 | use ra_vfs::VfsTask; | 15 | use ra_vfs::VfsTask; |
15 | use rustc_hash::FxHashMap; | ||
16 | use serde::{de::DeserializeOwned, Serialize}; | 16 | use serde::{de::DeserializeOwned, Serialize}; |
17 | use threadpool::ThreadPool; | 17 | use threadpool::ThreadPool; |
18 | use ra_prof::profile; | ||
18 | 19 | ||
19 | use crate::{ | 20 | use crate::{ |
20 | main_loop::subscriptions::Subscriptions, | 21 | main_loop::{ |
22 | subscriptions::Subscriptions, | ||
23 | pending_requests::{PendingRequests, PendingRequest}, | ||
24 | }, | ||
21 | project_model::workspace_loader, | 25 | project_model::workspace_loader, |
22 | req, | 26 | req, |
23 | server_world::{ServerWorld, ServerWorldState, CompletedRequest}, | 27 | world::{WorldSnapshot, WorldState}, |
24 | Result, | 28 | Result, |
25 | InitializationOptions, | 29 | InitializationOptions, |
26 | }; | 30 | }; |
27 | use ra_prof::profile; | 31 | |
32 | const THREADPOOL_SIZE: usize = 8; | ||
33 | const MAX_IN_FLIGHT_LIBS: usize = THREADPOOL_SIZE - 3; | ||
28 | 34 | ||
29 | #[derive(Debug, Fail)] | 35 | #[derive(Debug, Fail)] |
30 | #[fail(display = "Language Server request failed with {}. ({})", code, message)] | 36 | #[fail(display = "Language Server request failed with {}. ({})", code, message)] |
@@ -39,34 +45,12 @@ impl LspError { | |||
39 | } | 45 | } |
40 | } | 46 | } |
41 | 47 | ||
42 | #[derive(Debug)] | ||
43 | enum Task { | ||
44 | Respond(RawResponse), | ||
45 | Notify(RawNotification), | ||
46 | } | ||
47 | |||
48 | struct PendingRequest { | ||
49 | received: Instant, | ||
50 | method: String, | ||
51 | } | ||
52 | |||
53 | impl From<(u64, PendingRequest)> for CompletedRequest { | ||
54 | fn from((id, pending): (u64, PendingRequest)) -> CompletedRequest { | ||
55 | CompletedRequest { id, method: pending.method, duration: pending.received.elapsed() } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | const THREADPOOL_SIZE: usize = 8; | ||
60 | |||
61 | pub fn main_loop( | 48 | pub fn main_loop( |
62 | ws_roots: Vec<PathBuf>, | 49 | ws_roots: Vec<PathBuf>, |
63 | options: InitializationOptions, | 50 | options: InitializationOptions, |
64 | msg_receiver: &Receiver<RawMessage>, | 51 | msg_receiver: &Receiver<RawMessage>, |
65 | msg_sender: &Sender<RawMessage>, | 52 | msg_sender: &Sender<RawMessage>, |
66 | ) -> Result<()> { | 53 | ) -> Result<()> { |
67 | let pool = ThreadPool::new(THREADPOOL_SIZE); | ||
68 | let (task_sender, task_receiver) = unbounded::<Task>(); | ||
69 | |||
70 | // FIXME: support dynamic workspace loading. | 54 | // FIXME: support dynamic workspace loading. |
71 | let workspaces = { | 55 | let workspaces = { |
72 | let ws_worker = workspace_loader(); | 56 | let ws_worker = workspace_loader(); |
@@ -89,12 +73,13 @@ pub fn main_loop( | |||
89 | loaded_workspaces | 73 | loaded_workspaces |
90 | }; | 74 | }; |
91 | 75 | ||
92 | let mut state = ServerWorldState::new(ws_roots, workspaces); | 76 | let mut state = WorldState::new(ws_roots, workspaces); |
93 | 77 | ||
94 | log::info!("server initialized, serving requests"); | 78 | let pool = ThreadPool::new(THREADPOOL_SIZE); |
79 | let (task_sender, task_receiver) = unbounded::<Task>(); | ||
80 | let mut pending_requests = PendingRequests::default(); | ||
95 | 81 | ||
96 | let mut pending_requests = FxHashMap::default(); | 82 | log::info!("server initialized, serving requests"); |
97 | let mut subs = Subscriptions::new(); | ||
98 | let main_res = main_loop_inner( | 83 | let main_res = main_loop_inner( |
99 | options, | 84 | options, |
100 | &pool, | 85 | &pool, |
@@ -104,7 +89,6 @@ pub fn main_loop( | |||
104 | task_receiver.clone(), | 89 | task_receiver.clone(), |
105 | &mut state, | 90 | &mut state, |
106 | &mut pending_requests, | 91 | &mut pending_requests, |
107 | &mut subs, | ||
108 | ); | 92 | ); |
109 | 93 | ||
110 | log::info!("waiting for tasks to finish..."); | 94 | log::info!("waiting for tasks to finish..."); |
@@ -122,6 +106,12 @@ pub fn main_loop( | |||
122 | main_res | 106 | main_res |
123 | } | 107 | } |
124 | 108 | ||
109 | #[derive(Debug)] | ||
110 | enum Task { | ||
111 | Respond(RawResponse), | ||
112 | Notify(RawNotification), | ||
113 | } | ||
114 | |||
125 | enum Event { | 115 | enum Event { |
126 | Msg(RawMessage), | 116 | Msg(RawMessage), |
127 | Task(Task), | 117 | Task(Task), |
@@ -171,11 +161,11 @@ fn main_loop_inner( | |||
171 | msg_receiver: &Receiver<RawMessage>, | 161 | msg_receiver: &Receiver<RawMessage>, |
172 | task_sender: Sender<Task>, | 162 | task_sender: Sender<Task>, |
173 | task_receiver: Receiver<Task>, | 163 | task_receiver: Receiver<Task>, |
174 | state: &mut ServerWorldState, | 164 | state: &mut WorldState, |
175 | pending_requests: &mut FxHashMap<u64, PendingRequest>, | 165 | pending_requests: &mut PendingRequests, |
176 | subs: &mut Subscriptions, | ||
177 | ) -> Result<()> { | 166 | ) -> Result<()> { |
178 | // We try not to index more than THREADPOOL_SIZE - 3 libraries at the same | 167 | let mut subs = Subscriptions::default(); |
168 | // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same | ||
179 | // time to always have a thread ready to react to input. | 169 | // time to always have a thread ready to react to input. |
180 | let mut in_flight_libraries = 0; | 170 | let mut in_flight_libraries = 0; |
181 | let mut pending_libraries = Vec::new(); | 171 | let mut pending_libraries = Vec::new(); |
@@ -196,15 +186,16 @@ fn main_loop_inner( | |||
196 | }, | 186 | }, |
197 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) | 187 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) |
198 | }; | 188 | }; |
199 | // NOTE: don't count blocking select! call as a loop-turn time | ||
200 | let _p = profile("main_loop_inner/loop-turn"); | ||
201 | let loop_start = Instant::now(); | 189 | let loop_start = Instant::now(); |
202 | 190 | ||
191 | // NOTE: don't count blocking select! call as a loop-turn time | ||
192 | let _p = profile("main_loop_inner/loop-turn"); | ||
203 | log::info!("loop turn = {:?}", event); | 193 | log::info!("loop turn = {:?}", event); |
204 | let queue_count = pool.queued_count(); | 194 | let queue_count = pool.queued_count(); |
205 | if queue_count > 0 { | 195 | if queue_count > 0 { |
206 | log::info!("queued count = {}", queue_count); | 196 | log::info!("queued count = {}", queue_count); |
207 | } | 197 | } |
198 | |||
208 | let mut state_changed = false; | 199 | let mut state_changed = false; |
209 | match event { | 200 | match event { |
210 | Event::Task(task) => { | 201 | Event::Task(task) => { |
@@ -226,37 +217,18 @@ fn main_loop_inner( | |||
226 | Some(req) => req, | 217 | Some(req) => req, |
227 | None => return Ok(()), | 218 | None => return Ok(()), |
228 | }; | 219 | }; |
229 | match req.cast::<req::CollectGarbage>() { | 220 | on_request( |
230 | Ok((id, ())) => { | 221 | state, |
231 | state.collect_garbage(); | 222 | pending_requests, |
232 | let resp = RawResponse::ok::<req::CollectGarbage>(id, &()); | 223 | pool, |
233 | msg_sender.send(resp.into()).unwrap() | 224 | &task_sender, |
234 | } | 225 | msg_sender, |
235 | Err(req) => { | 226 | loop_start, |
236 | match on_request( | 227 | req, |
237 | state, | 228 | )? |
238 | pending_requests, | ||
239 | pool, | ||
240 | &task_sender, | ||
241 | loop_start, | ||
242 | req, | ||
243 | )? { | ||
244 | None => (), | ||
245 | Some(req) => { | ||
246 | log::error!("unknown request: {:?}", req); | ||
247 | let resp = RawResponse::err( | ||
248 | req.id, | ||
249 | ErrorCode::MethodNotFound as i32, | ||
250 | "unknown request".to_string(), | ||
251 | ); | ||
252 | msg_sender.send(resp.into()).unwrap() | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | } | 229 | } |
258 | RawMessage::Notification(not) => { | 230 | RawMessage::Notification(not) => { |
259 | on_notification(msg_sender, state, pending_requests, subs, not)?; | 231 | on_notification(msg_sender, state, pending_requests, &mut subs, not)?; |
260 | state_changed = true; | 232 | state_changed = true; |
261 | } | 233 | } |
262 | RawMessage::Response(resp) => log::error!("unexpected response: {:?}", resp), | 234 | RawMessage::Response(resp) => log::error!("unexpected response: {:?}", resp), |
@@ -264,7 +236,7 @@ fn main_loop_inner( | |||
264 | }; | 236 | }; |
265 | 237 | ||
266 | pending_libraries.extend(state.process_changes()); | 238 | pending_libraries.extend(state.process_changes()); |
267 | while in_flight_libraries < THREADPOOL_SIZE - 3 && !pending_libraries.is_empty() { | 239 | while in_flight_libraries < MAX_IN_FLIGHT_LIBS && !pending_libraries.is_empty() { |
268 | let (root, files) = pending_libraries.pop().unwrap(); | 240 | let (root, files) = pending_libraries.pop().unwrap(); |
269 | in_flight_libraries += 1; | 241 | in_flight_libraries += 1; |
270 | let sender = libdata_sender.clone(); | 242 | let sender = libdata_sender.clone(); |
@@ -305,13 +277,12 @@ fn main_loop_inner( | |||
305 | fn on_task( | 277 | fn on_task( |
306 | task: Task, | 278 | task: Task, |
307 | msg_sender: &Sender<RawMessage>, | 279 | msg_sender: &Sender<RawMessage>, |
308 | pending_requests: &mut FxHashMap<u64, PendingRequest>, | 280 | pending_requests: &mut PendingRequests, |
309 | state: &mut ServerWorldState, | 281 | state: &mut WorldState, |
310 | ) { | 282 | ) { |
311 | match task { | 283 | match task { |
312 | Task::Respond(response) => { | 284 | Task::Respond(response) => { |
313 | if let Some(pending) = pending_requests.remove(&response.id) { | 285 | if let Some(completed) = pending_requests.finish(response.id) { |
314 | let completed = CompletedRequest::from((response.id, pending)); | ||
315 | log::info!("handled req#{} in {:?}", completed.id, completed.duration); | 286 | log::info!("handled req#{} in {:?}", completed.id, completed.duration); |
316 | state.complete_request(completed); | 287 | state.complete_request(completed); |
317 | msg_sender.send(response.into()).unwrap(); | 288 | msg_sender.send(response.into()).unwrap(); |
@@ -324,23 +295,36 @@ fn on_task( | |||
324 | } | 295 | } |
325 | 296 | ||
326 | fn on_request( | 297 | fn on_request( |
327 | world: &mut ServerWorldState, | 298 | world: &mut WorldState, |
328 | pending_requests: &mut FxHashMap<u64, PendingRequest>, | 299 | pending_requests: &mut PendingRequests, |
329 | pool: &ThreadPool, | 300 | pool: &ThreadPool, |
330 | sender: &Sender<Task>, | 301 | sender: &Sender<Task>, |
302 | msg_sender: &Sender<RawMessage>, | ||
331 | request_received: Instant, | 303 | request_received: Instant, |
332 | req: RawRequest, | 304 | req: RawRequest, |
333 | ) -> Result<Option<RawRequest>> { | 305 | ) -> Result<()> { |
334 | let method = req.method.clone(); | 306 | let mut pool_dispatcher = PoolDispatcher { |
335 | let mut pool_dispatcher = PoolDispatcher { req: Some(req), res: None, pool, world, sender }; | 307 | req: Some(req), |
336 | let req = pool_dispatcher | 308 | pool, |
309 | world, | ||
310 | sender, | ||
311 | msg_sender, | ||
312 | pending_requests, | ||
313 | request_received, | ||
314 | }; | ||
315 | pool_dispatcher | ||
316 | .on_sync::<req::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))? | ||
317 | .on_sync::<req::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | ||
318 | .on_sync::<req::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | ||
319 | .on_sync::<req::SelectionRangeRequest>(|s, p| { | ||
320 | handlers::handle_selection_range(s.snapshot(), p) | ||
321 | })? | ||
322 | .on_sync::<req::FindMatchingBrace>(|s, p| { | ||
323 | handlers::handle_find_matching_brace(s.snapshot(), p) | ||
324 | })? | ||
337 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? | 325 | .on::<req::AnalyzerStatus>(handlers::handle_analyzer_status)? |
338 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? | 326 | .on::<req::SyntaxTree>(handlers::handle_syntax_tree)? |
339 | .on::<req::ExtendSelection>(handlers::handle_extend_selection)? | 327 | .on::<req::ExtendSelection>(handlers::handle_extend_selection)? |
340 | .on::<req::SelectionRangeRequest>(handlers::handle_selection_range)? | ||
341 | .on::<req::FindMatchingBrace>(handlers::handle_find_matching_brace)? | ||
342 | .on::<req::JoinLines>(handlers::handle_join_lines)? | ||
343 | .on::<req::OnEnter>(handlers::handle_on_enter)? | ||
344 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 328 | .on::<req::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
345 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 329 | .on::<req::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
346 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 330 | .on::<req::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
@@ -363,21 +347,13 @@ fn on_request( | |||
363 | .on::<req::Formatting>(handlers::handle_formatting)? | 347 | .on::<req::Formatting>(handlers::handle_formatting)? |
364 | .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? | 348 | .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? |
365 | .finish(); | 349 | .finish(); |
366 | match req { | 350 | Ok(()) |
367 | Ok(id) => { | ||
368 | let prev = | ||
369 | pending_requests.insert(id, PendingRequest { method, received: request_received }); | ||
370 | assert!(prev.is_none(), "duplicate request: {}", id); | ||
371 | Ok(None) | ||
372 | } | ||
373 | Err(req) => Ok(Some(req)), | ||
374 | } | ||
375 | } | 351 | } |
376 | 352 | ||
377 | fn on_notification( | 353 | fn on_notification( |
378 | msg_sender: &Sender<RawMessage>, | 354 | msg_sender: &Sender<RawMessage>, |
379 | state: &mut ServerWorldState, | 355 | state: &mut WorldState, |
380 | pending_requests: &mut FxHashMap<u64, PendingRequest>, | 356 | pending_requests: &mut PendingRequests, |
381 | subs: &mut Subscriptions, | 357 | subs: &mut Subscriptions, |
382 | not: RawNotification, | 358 | not: RawNotification, |
383 | ) -> Result<()> { | 359 | ) -> Result<()> { |
@@ -389,7 +365,7 @@ fn on_notification( | |||
389 | panic!("string id's not supported: {:?}", id); | 365 | panic!("string id's not supported: {:?}", id); |
390 | } | 366 | } |
391 | }; | 367 | }; |
392 | if pending_requests.remove(&id).is_some() { | 368 | if pending_requests.cancel(id) { |
393 | let response = RawResponse::err( | 369 | let response = RawResponse::err( |
394 | id, | 370 | id, |
395 | ErrorCode::RequestCanceled as i32, | 371 | ErrorCode::RequestCanceled as i32, |
@@ -445,91 +421,143 @@ fn on_notification( | |||
445 | 421 | ||
446 | struct PoolDispatcher<'a> { | 422 | struct PoolDispatcher<'a> { |
447 | req: Option<RawRequest>, | 423 | req: Option<RawRequest>, |
448 | res: Option<u64>, | ||
449 | pool: &'a ThreadPool, | 424 | pool: &'a ThreadPool, |
450 | world: &'a mut ServerWorldState, | 425 | world: &'a mut WorldState, |
426 | pending_requests: &'a mut PendingRequests, | ||
427 | msg_sender: &'a Sender<RawMessage>, | ||
451 | sender: &'a Sender<Task>, | 428 | sender: &'a Sender<Task>, |
429 | request_received: Instant, | ||
452 | } | 430 | } |
453 | 431 | ||
454 | impl<'a> PoolDispatcher<'a> { | 432 | impl<'a> PoolDispatcher<'a> { |
455 | fn on<R>(&mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&mut Self> | 433 | /// Dispatches the request onto the current thread |
434 | fn on_sync<R>( | ||
435 | &mut self, | ||
436 | f: fn(&mut WorldState, R::Params) -> Result<R::Result>, | ||
437 | ) -> Result<&mut Self> | ||
456 | where | 438 | where |
457 | R: req::Request + 'static, | 439 | R: req::Request + 'static, |
458 | R::Params: DeserializeOwned + Send + 'static, | 440 | R::Params: DeserializeOwned + Send + 'static, |
459 | R::Result: Serialize + 'static, | 441 | R::Result: Serialize + 'static, |
460 | { | 442 | { |
461 | let req = match self.req.take() { | 443 | let (id, params) = match self.parse::<R>() { |
462 | None => return Ok(self), | 444 | Some(it) => it, |
463 | Some(req) => req, | 445 | None => { |
446 | return Ok(self); | ||
447 | } | ||
464 | }; | 448 | }; |
465 | match req.cast::<R>() { | 449 | let result = f(self.world, params); |
466 | Ok((id, params)) => { | 450 | let task = result_to_task::<R>(id, result); |
467 | // Real time requests block user typing, so we should react quickly to them. | 451 | on_task(task, self.msg_sender, self.pending_requests, self.world); |
468 | // Currently this means that we try to cancel background jobs if we don't have | 452 | Ok(self) |
469 | // a spare thread. | 453 | } |
470 | let is_real_time = TypeId::of::<R>() == TypeId::of::<req::JoinLines>() | ||
471 | || TypeId::of::<R>() == TypeId::of::<req::OnEnter>(); | ||
472 | if self.pool.queued_count() > 0 && is_real_time { | ||
473 | self.world.cancel_requests(); | ||
474 | } | ||
475 | 454 | ||
476 | let world = self.world.snapshot(); | 455 | /// Dispatches the request onto thread pool |
477 | let sender = self.sender.clone(); | 456 | fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> |
478 | self.pool.execute(move || { | 457 | where |
479 | let response = match f(world, params) { | 458 | R: req::Request + 'static, |
480 | Ok(resp) => RawResponse::ok::<R>(id, &resp), | 459 | R::Params: DeserializeOwned + Send + 'static, |
481 | Err(e) => match e.downcast::<LspError>() { | 460 | R::Result: Serialize + 'static, |
482 | Ok(lsp_error) => { | 461 | { |
483 | RawResponse::err(id, lsp_error.code, lsp_error.message) | 462 | let (id, params) = match self.parse::<R>() { |
484 | } | 463 | Some(it) => it, |
485 | Err(e) => { | 464 | None => { |
486 | if is_canceled(&e) { | 465 | return Ok(self); |
487 | // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 | ||
488 | // gets fixed, we can return the proper response. | ||
489 | // This works around the issue where "content modified" error would continuously | ||
490 | // show an message pop-up in VsCode | ||
491 | // RawResponse::err( | ||
492 | // id, | ||
493 | // ErrorCode::ContentModified as i32, | ||
494 | // "content modified".to_string(), | ||
495 | // ) | ||
496 | RawResponse { | ||
497 | id, | ||
498 | result: Some(serde_json::to_value(&()).unwrap()), | ||
499 | error: None, | ||
500 | } | ||
501 | } else { | ||
502 | RawResponse::err( | ||
503 | id, | ||
504 | ErrorCode::InternalError as i32, | ||
505 | format!("{}\n{}", e, e.backtrace()), | ||
506 | ) | ||
507 | } | ||
508 | } | ||
509 | }, | ||
510 | }; | ||
511 | let task = Task::Respond(response); | ||
512 | sender.send(task).unwrap(); | ||
513 | }); | ||
514 | self.res = Some(id); | ||
515 | } | 466 | } |
516 | Err(req) => self.req = Some(req), | 467 | }; |
517 | } | 468 | |
469 | self.pool.execute({ | ||
470 | let world = self.world.snapshot(); | ||
471 | let sender = self.sender.clone(); | ||
472 | move || { | ||
473 | let result = f(world, params); | ||
474 | let task = result_to_task::<R>(id, result); | ||
475 | sender.send(task).unwrap(); | ||
476 | } | ||
477 | }); | ||
478 | |||
518 | Ok(self) | 479 | Ok(self) |
519 | } | 480 | } |
520 | 481 | ||
521 | fn finish(&mut self) -> std::result::Result<u64, RawRequest> { | 482 | fn parse<R>(&mut self) -> Option<(u64, R::Params)> |
522 | match (self.res.take(), self.req.take()) { | 483 | where |
523 | (Some(res), None) => Ok(res), | 484 | R: req::Request + 'static, |
524 | (None, Some(req)) => Err(req), | 485 | R::Params: DeserializeOwned + Send + 'static, |
525 | _ => unreachable!(), | 486 | { |
487 | let req = self.req.take()?; | ||
488 | let (id, params) = match req.cast::<R>() { | ||
489 | Ok(it) => it, | ||
490 | Err(req) => { | ||
491 | self.req = Some(req); | ||
492 | return None; | ||
493 | } | ||
494 | }; | ||
495 | self.pending_requests.start(PendingRequest { | ||
496 | id, | ||
497 | method: R::METHOD.to_string(), | ||
498 | received: self.request_received, | ||
499 | }); | ||
500 | Some((id, params)) | ||
501 | } | ||
502 | |||
503 | fn finish(&mut self) { | ||
504 | match self.req.take() { | ||
505 | None => (), | ||
506 | Some(req) => { | ||
507 | log::error!("unknown request: {:?}", req); | ||
508 | let resp = RawResponse::err( | ||
509 | req.id, | ||
510 | ErrorCode::MethodNotFound as i32, | ||
511 | "unknown request".to_string(), | ||
512 | ); | ||
513 | self.msg_sender.send(resp.into()).unwrap(); | ||
514 | } | ||
526 | } | 515 | } |
527 | } | 516 | } |
528 | } | 517 | } |
529 | 518 | ||
519 | fn result_to_task<R>(id: u64, result: Result<R::Result>) -> Task | ||
520 | where | ||
521 | R: req::Request + 'static, | ||
522 | R::Params: DeserializeOwned + Send + 'static, | ||
523 | R::Result: Serialize + 'static, | ||
524 | { | ||
525 | let response = match result { | ||
526 | Ok(resp) => RawResponse::ok::<R>(id, &resp), | ||
527 | Err(e) => match e.downcast::<LspError>() { | ||
528 | Ok(lsp_error) => RawResponse::err(id, lsp_error.code, lsp_error.message), | ||
529 | Err(e) => { | ||
530 | if is_canceled(&e) { | ||
531 | // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 | ||
532 | // gets fixed, we can return the proper response. | ||
533 | // This works around the issue where "content modified" error would continuously | ||
534 | // show an message pop-up in VsCode | ||
535 | // RawResponse::err( | ||
536 | // id, | ||
537 | // ErrorCode::ContentModified as i32, | ||
538 | // "content modified".to_string(), | ||
539 | // ) | ||
540 | RawResponse { | ||
541 | id, | ||
542 | result: Some(serde_json::to_value(&()).unwrap()), | ||
543 | error: None, | ||
544 | } | ||
545 | } else { | ||
546 | RawResponse::err( | ||
547 | id, | ||
548 | ErrorCode::InternalError as i32, | ||
549 | format!("{}\n{}", e, e.backtrace()), | ||
550 | ) | ||
551 | } | ||
552 | } | ||
553 | }, | ||
554 | }; | ||
555 | Task::Respond(response) | ||
556 | } | ||
557 | |||
530 | fn update_file_notifications_on_threadpool( | 558 | fn update_file_notifications_on_threadpool( |
531 | pool: &ThreadPool, | 559 | pool: &ThreadPool, |
532 | world: ServerWorld, | 560 | world: WorldSnapshot, |
533 | publish_decorations: bool, | 561 | publish_decorations: bool, |
534 | sender: Sender<Task>, | 562 | sender: Sender<Task>, |
535 | subscriptions: Vec<FileId>, | 563 | subscriptions: Vec<FileId>, |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 0ebfd641d..6373240d5 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -24,22 +24,22 @@ use crate::{ | |||
24 | cargo_target_spec::{runnable_args, CargoTargetSpec}, | 24 | cargo_target_spec::{runnable_args, CargoTargetSpec}, |
25 | conv::{to_location, to_location_link, Conv, ConvWith, MapConvWith, TryConvWith}, | 25 | conv::{to_location, to_location_link, Conv, ConvWith, MapConvWith, TryConvWith}, |
26 | req::{self, Decoration}, | 26 | req::{self, Decoration}, |
27 | server_world::ServerWorld, | 27 | world::WorldSnapshot, |
28 | LspError, Result, | 28 | LspError, Result, |
29 | }; | 29 | }; |
30 | 30 | ||
31 | pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result<String> { | 31 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { |
32 | let mut buf = world.status(); | 32 | let mut buf = world.status(); |
33 | writeln!(buf, "\n\nrequests:").unwrap(); | 33 | writeln!(buf, "\n\nrequests:").unwrap(); |
34 | let requests = world.latest_completed_requests.read(); | 34 | let requests = world.latest_requests.read(); |
35 | for (idx, r) in requests.iter().enumerate() { | 35 | for (is_last, r) in requests.iter() { |
36 | let current = if idx == world.request_idx { "*" } else { " " }; | 36 | let mark = if is_last { "*" } else { " " }; |
37 | writeln!(buf, "{:4}{}{:<36}{}ms", r.id, current, r.method, r.duration.as_millis()).unwrap(); | 37 | writeln!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis()).unwrap(); |
38 | } | 38 | } |
39 | Ok(buf) | 39 | Ok(buf) |
40 | } | 40 | } |
41 | 41 | ||
42 | pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> Result<String> { | 42 | pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result<String> { |
43 | let id = params.text_document.try_conv_with(&world)?; | 43 | let id = params.text_document.try_conv_with(&world)?; |
44 | let line_index = world.analysis().file_line_index(id); | 44 | let line_index = world.analysis().file_line_index(id); |
45 | let text_range = params.range.map(|p| p.conv_with(&line_index)); | 45 | let text_range = params.range.map(|p| p.conv_with(&line_index)); |
@@ -49,7 +49,7 @@ pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> | |||
49 | 49 | ||
50 | // FIXME: drop this API | 50 | // FIXME: drop this API |
51 | pub fn handle_extend_selection( | 51 | pub fn handle_extend_selection( |
52 | world: ServerWorld, | 52 | world: WorldSnapshot, |
53 | params: req::ExtendSelectionParams, | 53 | params: req::ExtendSelectionParams, |
54 | ) -> Result<req::ExtendSelectionResult> { | 54 | ) -> Result<req::ExtendSelectionResult> { |
55 | log::error!( | 55 | log::error!( |
@@ -69,7 +69,7 @@ pub fn handle_extend_selection( | |||
69 | } | 69 | } |
70 | 70 | ||
71 | pub fn handle_selection_range( | 71 | pub fn handle_selection_range( |
72 | world: ServerWorld, | 72 | world: WorldSnapshot, |
73 | params: req::SelectionRangeParams, | 73 | params: req::SelectionRangeParams, |
74 | ) -> Result<Vec<req::SelectionRange>> { | 74 | ) -> Result<Vec<req::SelectionRange>> { |
75 | let _p = profile("handle_selection_range"); | 75 | let _p = profile("handle_selection_range"); |
@@ -110,7 +110,7 @@ pub fn handle_selection_range( | |||
110 | } | 110 | } |
111 | 111 | ||
112 | pub fn handle_find_matching_brace( | 112 | pub fn handle_find_matching_brace( |
113 | world: ServerWorld, | 113 | world: WorldSnapshot, |
114 | params: req::FindMatchingBraceParams, | 114 | params: req::FindMatchingBraceParams, |
115 | ) -> Result<Vec<Position>> { | 115 | ) -> Result<Vec<Position>> { |
116 | let _p = profile("handle_find_matching_brace"); | 116 | let _p = profile("handle_find_matching_brace"); |
@@ -129,7 +129,7 @@ pub fn handle_find_matching_brace( | |||
129 | } | 129 | } |
130 | 130 | ||
131 | pub fn handle_join_lines( | 131 | pub fn handle_join_lines( |
132 | world: ServerWorld, | 132 | world: WorldSnapshot, |
133 | params: req::JoinLinesParams, | 133 | params: req::JoinLinesParams, |
134 | ) -> Result<req::SourceChange> { | 134 | ) -> Result<req::SourceChange> { |
135 | let _p = profile("handle_join_lines"); | 135 | let _p = profile("handle_join_lines"); |
@@ -138,7 +138,7 @@ pub fn handle_join_lines( | |||
138 | } | 138 | } |
139 | 139 | ||
140 | pub fn handle_on_enter( | 140 | pub fn handle_on_enter( |
141 | world: ServerWorld, | 141 | world: WorldSnapshot, |
142 | params: req::TextDocumentPositionParams, | 142 | params: req::TextDocumentPositionParams, |
143 | ) -> Result<Option<req::SourceChange>> { | 143 | ) -> Result<Option<req::SourceChange>> { |
144 | let _p = profile("handle_on_enter"); | 144 | let _p = profile("handle_on_enter"); |
@@ -150,7 +150,7 @@ pub fn handle_on_enter( | |||
150 | } | 150 | } |
151 | 151 | ||
152 | pub fn handle_on_type_formatting( | 152 | pub fn handle_on_type_formatting( |
153 | world: ServerWorld, | 153 | world: WorldSnapshot, |
154 | params: req::DocumentOnTypeFormattingParams, | 154 | params: req::DocumentOnTypeFormattingParams, |
155 | ) -> Result<Option<Vec<TextEdit>>> { | 155 | ) -> Result<Option<Vec<TextEdit>>> { |
156 | let _p = profile("handle_on_type_formatting"); | 156 | let _p = profile("handle_on_type_formatting"); |
@@ -181,7 +181,7 @@ pub fn handle_on_type_formatting( | |||
181 | } | 181 | } |
182 | 182 | ||
183 | pub fn handle_document_symbol( | 183 | pub fn handle_document_symbol( |
184 | world: ServerWorld, | 184 | world: WorldSnapshot, |
185 | params: req::DocumentSymbolParams, | 185 | params: req::DocumentSymbolParams, |
186 | ) -> Result<Option<req::DocumentSymbolResponse>> { | 186 | ) -> Result<Option<req::DocumentSymbolResponse>> { |
187 | let file_id = params.text_document.try_conv_with(&world)?; | 187 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -219,7 +219,7 @@ pub fn handle_document_symbol( | |||
219 | } | 219 | } |
220 | 220 | ||
221 | pub fn handle_workspace_symbol( | 221 | pub fn handle_workspace_symbol( |
222 | world: ServerWorld, | 222 | world: WorldSnapshot, |
223 | params: req::WorkspaceSymbolParams, | 223 | params: req::WorkspaceSymbolParams, |
224 | ) -> Result<Option<Vec<SymbolInformation>>> { | 224 | ) -> Result<Option<Vec<SymbolInformation>>> { |
225 | let all_symbols = params.query.contains('#'); | 225 | let all_symbols = params.query.contains('#'); |
@@ -245,7 +245,7 @@ pub fn handle_workspace_symbol( | |||
245 | 245 | ||
246 | return Ok(Some(res)); | 246 | return Ok(Some(res)); |
247 | 247 | ||
248 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { | 248 | fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { |
249 | let mut res = Vec::new(); | 249 | let mut res = Vec::new(); |
250 | for nav in world.analysis().symbol_search(query)? { | 250 | for nav in world.analysis().symbol_search(query)? { |
251 | let info = SymbolInformation { | 251 | let info = SymbolInformation { |
@@ -262,7 +262,7 @@ pub fn handle_workspace_symbol( | |||
262 | } | 262 | } |
263 | 263 | ||
264 | pub fn handle_goto_definition( | 264 | pub fn handle_goto_definition( |
265 | world: ServerWorld, | 265 | world: WorldSnapshot, |
266 | params: req::TextDocumentPositionParams, | 266 | params: req::TextDocumentPositionParams, |
267 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 267 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
268 | let position = params.try_conv_with(&world)?; | 268 | let position = params.try_conv_with(&world)?; |
@@ -282,7 +282,7 @@ pub fn handle_goto_definition( | |||
282 | } | 282 | } |
283 | 283 | ||
284 | pub fn handle_goto_implementation( | 284 | pub fn handle_goto_implementation( |
285 | world: ServerWorld, | 285 | world: WorldSnapshot, |
286 | params: req::TextDocumentPositionParams, | 286 | params: req::TextDocumentPositionParams, |
287 | ) -> Result<Option<req::GotoImplementationResponse>> { | 287 | ) -> Result<Option<req::GotoImplementationResponse>> { |
288 | let position = params.try_conv_with(&world)?; | 288 | let position = params.try_conv_with(&world)?; |
@@ -302,7 +302,7 @@ pub fn handle_goto_implementation( | |||
302 | } | 302 | } |
303 | 303 | ||
304 | pub fn handle_goto_type_definition( | 304 | pub fn handle_goto_type_definition( |
305 | world: ServerWorld, | 305 | world: WorldSnapshot, |
306 | params: req::TextDocumentPositionParams, | 306 | params: req::TextDocumentPositionParams, |
307 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { | 307 | ) -> Result<Option<req::GotoTypeDefinitionResponse>> { |
308 | let position = params.try_conv_with(&world)?; | 308 | let position = params.try_conv_with(&world)?; |
@@ -322,7 +322,7 @@ pub fn handle_goto_type_definition( | |||
322 | } | 322 | } |
323 | 323 | ||
324 | pub fn handle_parent_module( | 324 | pub fn handle_parent_module( |
325 | world: ServerWorld, | 325 | world: WorldSnapshot, |
326 | params: req::TextDocumentPositionParams, | 326 | params: req::TextDocumentPositionParams, |
327 | ) -> Result<Vec<Location>> { | 327 | ) -> Result<Vec<Location>> { |
328 | let position = params.try_conv_with(&world)?; | 328 | let position = params.try_conv_with(&world)?; |
@@ -335,7 +335,7 @@ pub fn handle_parent_module( | |||
335 | } | 335 | } |
336 | 336 | ||
337 | pub fn handle_runnables( | 337 | pub fn handle_runnables( |
338 | world: ServerWorld, | 338 | world: WorldSnapshot, |
339 | params: req::RunnablesParams, | 339 | params: req::RunnablesParams, |
340 | ) -> Result<Vec<req::Runnable>> { | 340 | ) -> Result<Vec<req::Runnable>> { |
341 | let file_id = params.text_document.try_conv_with(&world)?; | 341 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -396,7 +396,7 @@ pub fn handle_runnables( | |||
396 | } | 396 | } |
397 | 397 | ||
398 | pub fn handle_decorations( | 398 | pub fn handle_decorations( |
399 | world: ServerWorld, | 399 | world: WorldSnapshot, |
400 | params: TextDocumentIdentifier, | 400 | params: TextDocumentIdentifier, |
401 | ) -> Result<Vec<Decoration>> { | 401 | ) -> Result<Vec<Decoration>> { |
402 | let file_id = params.try_conv_with(&world)?; | 402 | let file_id = params.try_conv_with(&world)?; |
@@ -404,7 +404,7 @@ pub fn handle_decorations( | |||
404 | } | 404 | } |
405 | 405 | ||
406 | pub fn handle_completion( | 406 | pub fn handle_completion( |
407 | world: ServerWorld, | 407 | world: WorldSnapshot, |
408 | params: req::CompletionParams, | 408 | params: req::CompletionParams, |
409 | ) -> Result<Option<req::CompletionResponse>> { | 409 | ) -> Result<Option<req::CompletionResponse>> { |
410 | let _p = profile("handle_completion"); | 410 | let _p = profile("handle_completion"); |
@@ -447,7 +447,7 @@ pub fn handle_completion( | |||
447 | } | 447 | } |
448 | 448 | ||
449 | pub fn handle_folding_range( | 449 | pub fn handle_folding_range( |
450 | world: ServerWorld, | 450 | world: WorldSnapshot, |
451 | params: FoldingRangeParams, | 451 | params: FoldingRangeParams, |
452 | ) -> Result<Option<Vec<FoldingRange>>> { | 452 | ) -> Result<Option<Vec<FoldingRange>>> { |
453 | let file_id = params.text_document.try_conv_with(&world)?; | 453 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -481,7 +481,7 @@ pub fn handle_folding_range( | |||
481 | } | 481 | } |
482 | 482 | ||
483 | pub fn handle_signature_help( | 483 | pub fn handle_signature_help( |
484 | world: ServerWorld, | 484 | world: WorldSnapshot, |
485 | params: req::TextDocumentPositionParams, | 485 | params: req::TextDocumentPositionParams, |
486 | ) -> Result<Option<req::SignatureHelp>> { | 486 | ) -> Result<Option<req::SignatureHelp>> { |
487 | let position = params.try_conv_with(&world)?; | 487 | let position = params.try_conv_with(&world)?; |
@@ -500,7 +500,7 @@ pub fn handle_signature_help( | |||
500 | } | 500 | } |
501 | 501 | ||
502 | pub fn handle_hover( | 502 | pub fn handle_hover( |
503 | world: ServerWorld, | 503 | world: WorldSnapshot, |
504 | params: req::TextDocumentPositionParams, | 504 | params: req::TextDocumentPositionParams, |
505 | ) -> Result<Option<Hover>> { | 505 | ) -> Result<Option<Hover>> { |
506 | let position = params.try_conv_with(&world)?; | 506 | let position = params.try_conv_with(&world)?; |
@@ -522,7 +522,7 @@ pub fn handle_hover( | |||
522 | 522 | ||
523 | /// Test doc comment | 523 | /// Test doc comment |
524 | pub fn handle_prepare_rename( | 524 | pub fn handle_prepare_rename( |
525 | world: ServerWorld, | 525 | world: WorldSnapshot, |
526 | params: req::TextDocumentPositionParams, | 526 | params: req::TextDocumentPositionParams, |
527 | ) -> Result<Option<PrepareRenameResponse>> { | 527 | ) -> Result<Option<PrepareRenameResponse>> { |
528 | let position = params.try_conv_with(&world)?; | 528 | let position = params.try_conv_with(&world)?; |
@@ -543,7 +543,7 @@ pub fn handle_prepare_rename( | |||
543 | Ok(Some(PrepareRenameResponse::Range(loc.range))) | 543 | Ok(Some(PrepareRenameResponse::Range(loc.range))) |
544 | } | 544 | } |
545 | 545 | ||
546 | pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option<WorkspaceEdit>> { | 546 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { |
547 | let file_id = params.text_document.try_conv_with(&world)?; | 547 | let file_id = params.text_document.try_conv_with(&world)?; |
548 | let line_index = world.analysis().file_line_index(file_id); | 548 | let line_index = world.analysis().file_line_index(file_id); |
549 | let offset = params.position.conv_with(&line_index); | 549 | let offset = params.position.conv_with(&line_index); |
@@ -569,7 +569,7 @@ pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option< | |||
569 | } | 569 | } |
570 | 570 | ||
571 | pub fn handle_references( | 571 | pub fn handle_references( |
572 | world: ServerWorld, | 572 | world: WorldSnapshot, |
573 | params: req::ReferenceParams, | 573 | params: req::ReferenceParams, |
574 | ) -> Result<Option<Vec<Location>>> { | 574 | ) -> Result<Option<Vec<Location>>> { |
575 | let file_id = params.text_document.try_conv_with(&world)?; | 575 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -597,7 +597,7 @@ pub fn handle_references( | |||
597 | } | 597 | } |
598 | 598 | ||
599 | pub fn handle_formatting( | 599 | pub fn handle_formatting( |
600 | world: ServerWorld, | 600 | world: WorldSnapshot, |
601 | params: DocumentFormattingParams, | 601 | params: DocumentFormattingParams, |
602 | ) -> Result<Option<Vec<TextEdit>>> { | 602 | ) -> Result<Option<Vec<TextEdit>>> { |
603 | let file_id = params.text_document.try_conv_with(&world)?; | 603 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -641,7 +641,7 @@ pub fn handle_formatting( | |||
641 | } | 641 | } |
642 | 642 | ||
643 | pub fn handle_code_action( | 643 | pub fn handle_code_action( |
644 | world: ServerWorld, | 644 | world: WorldSnapshot, |
645 | params: req::CodeActionParams, | 645 | params: req::CodeActionParams, |
646 | ) -> Result<Option<CodeActionResponse>> { | 646 | ) -> Result<Option<CodeActionResponse>> { |
647 | let _p = profile("handle_code_action"); | 647 | let _p = profile("handle_code_action"); |
@@ -704,7 +704,7 @@ pub fn handle_code_action( | |||
704 | } | 704 | } |
705 | 705 | ||
706 | pub fn handle_code_lens( | 706 | pub fn handle_code_lens( |
707 | world: ServerWorld, | 707 | world: WorldSnapshot, |
708 | params: req::CodeLensParams, | 708 | params: req::CodeLensParams, |
709 | ) -> Result<Option<Vec<CodeLens>>> { | 709 | ) -> Result<Option<Vec<CodeLens>>> { |
710 | let file_id = params.text_document.try_conv_with(&world)?; | 710 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -781,7 +781,7 @@ enum CodeLensResolveData { | |||
781 | Impls(req::TextDocumentPositionParams), | 781 | Impls(req::TextDocumentPositionParams), |
782 | } | 782 | } |
783 | 783 | ||
784 | pub fn handle_code_lens_resolve(world: ServerWorld, code_lens: CodeLens) -> Result<CodeLens> { | 784 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { |
785 | let data = code_lens.data.unwrap(); | 785 | let data = code_lens.data.unwrap(); |
786 | let resolve = serde_json::from_value(data)?; | 786 | let resolve = serde_json::from_value(data)?; |
787 | match resolve { | 787 | match resolve { |
@@ -826,7 +826,7 @@ pub fn handle_code_lens_resolve(world: ServerWorld, code_lens: CodeLens) -> Resu | |||
826 | } | 826 | } |
827 | 827 | ||
828 | pub fn handle_document_highlight( | 828 | pub fn handle_document_highlight( |
829 | world: ServerWorld, | 829 | world: WorldSnapshot, |
830 | params: req::TextDocumentPositionParams, | 830 | params: req::TextDocumentPositionParams, |
831 | ) -> Result<Option<Vec<DocumentHighlight>>> { | 831 | ) -> Result<Option<Vec<DocumentHighlight>>> { |
832 | let file_id = params.text_document.try_conv_with(&world)?; | 832 | let file_id = params.text_document.try_conv_with(&world)?; |
@@ -845,7 +845,7 @@ pub fn handle_document_highlight( | |||
845 | } | 845 | } |
846 | 846 | ||
847 | pub fn publish_diagnostics( | 847 | pub fn publish_diagnostics( |
848 | world: &ServerWorld, | 848 | world: &WorldSnapshot, |
849 | file_id: FileId, | 849 | file_id: FileId, |
850 | ) -> Result<req::PublishDiagnosticsParams> { | 850 | ) -> Result<req::PublishDiagnosticsParams> { |
851 | let uri = world.file_id_to_uri(file_id)?; | 851 | let uri = world.file_id_to_uri(file_id)?; |
@@ -867,14 +867,14 @@ pub fn publish_diagnostics( | |||
867 | } | 867 | } |
868 | 868 | ||
869 | pub fn publish_decorations( | 869 | pub fn publish_decorations( |
870 | world: &ServerWorld, | 870 | world: &WorldSnapshot, |
871 | file_id: FileId, | 871 | file_id: FileId, |
872 | ) -> Result<req::PublishDecorationsParams> { | 872 | ) -> Result<req::PublishDecorationsParams> { |
873 | let uri = world.file_id_to_uri(file_id)?; | 873 | let uri = world.file_id_to_uri(file_id)?; |
874 | Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? }) | 874 | Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? }) |
875 | } | 875 | } |
876 | 876 | ||
877 | fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> { | 877 | fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> { |
878 | let line_index = world.analysis().file_line_index(file_id); | 878 | let line_index = world.analysis().file_line_index(file_id); |
879 | let res = world | 879 | let res = world |
880 | .analysis() | 880 | .analysis() |
diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs new file mode 100644 index 000000000..741770e45 --- /dev/null +++ b/crates/ra_lsp_server/src/main_loop/pending_requests.rs | |||
@@ -0,0 +1,72 @@ | |||
1 | use std::time::{Duration, Instant}; | ||
2 | |||
3 | use rustc_hash::FxHashMap; | ||
4 | |||
5 | #[derive(Debug)] | ||
6 | pub struct CompletedRequest { | ||
7 | pub id: u64, | ||
8 | pub method: String, | ||
9 | pub duration: Duration, | ||
10 | } | ||
11 | |||
12 | #[derive(Debug)] | ||
13 | pub(crate) struct PendingRequest { | ||
14 | pub(crate) id: u64, | ||
15 | pub(crate) method: String, | ||
16 | pub(crate) received: Instant, | ||
17 | } | ||
18 | |||
19 | impl From<PendingRequest> for CompletedRequest { | ||
20 | fn from(pending: PendingRequest) -> CompletedRequest { | ||
21 | CompletedRequest { | ||
22 | id: pending.id, | ||
23 | method: pending.method, | ||
24 | duration: pending.received.elapsed(), | ||
25 | } | ||
26 | } | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Default)] | ||
30 | pub(crate) struct PendingRequests { | ||
31 | map: FxHashMap<u64, PendingRequest>, | ||
32 | } | ||
33 | |||
34 | impl PendingRequests { | ||
35 | pub(crate) fn start(&mut self, request: PendingRequest) { | ||
36 | let id = request.id; | ||
37 | let prev = self.map.insert(id, request); | ||
38 | assert!(prev.is_none(), "duplicate request with id {}", id); | ||
39 | } | ||
40 | pub(crate) fn cancel(&mut self, id: u64) -> bool { | ||
41 | self.map.remove(&id).is_some() | ||
42 | } | ||
43 | pub(crate) fn finish(&mut self, id: u64) -> Option<CompletedRequest> { | ||
44 | self.map.remove(&id).map(CompletedRequest::from) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | const N_COMPLETED_REQUESTS: usize = 10; | ||
49 | |||
50 | #[derive(Debug, Default)] | ||
51 | pub struct LatestRequests { | ||
52 | // hand-rolling VecDeque here to print things in a nicer way | ||
53 | buf: [Option<CompletedRequest>; N_COMPLETED_REQUESTS], | ||
54 | idx: usize, | ||
55 | } | ||
56 | |||
57 | impl LatestRequests { | ||
58 | pub(crate) fn record(&mut self, request: CompletedRequest) { | ||
59 | // special case: don't track status request itself | ||
60 | if request.method == "rust-analyzer/analyzerStatus" { | ||
61 | return; | ||
62 | } | ||
63 | let idx = self.idx; | ||
64 | self.buf[idx] = Some(request); | ||
65 | self.idx = (idx + 1) % N_COMPLETED_REQUESTS; | ||
66 | } | ||
67 | |||
68 | pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &CompletedRequest)> { | ||
69 | let idx = self.idx; | ||
70 | self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?))) | ||
71 | } | ||
72 | } | ||
diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs index 11bd952d9..470bc1205 100644 --- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs +++ b/crates/ra_lsp_server/src/main_loop/subscriptions.rs | |||
@@ -1,21 +1,19 @@ | |||
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 | pub struct Subscriptions { | 4 | #[derive(Default)] |
5 | pub(crate) struct Subscriptions { | ||
5 | subs: FxHashSet<FileId>, | 6 | subs: FxHashSet<FileId>, |
6 | } | 7 | } |
7 | 8 | ||
8 | impl Subscriptions { | 9 | impl Subscriptions { |
9 | pub fn new() -> Subscriptions { | 10 | pub(crate) fn add_sub(&mut self, file_id: FileId) { |
10 | Subscriptions { subs: FxHashSet::default() } | ||
11 | } | ||
12 | pub fn add_sub(&mut self, file_id: FileId) { | ||
13 | self.subs.insert(file_id); | 11 | self.subs.insert(file_id); |
14 | } | 12 | } |
15 | pub fn remove_sub(&mut self, file_id: FileId) { | 13 | pub(crate) fn remove_sub(&mut self, file_id: FileId) { |
16 | self.subs.remove(&file_id); | 14 | self.subs.remove(&file_id); |
17 | } | 15 | } |
18 | pub fn subscriptions(&self) -> Vec<FileId> { | 16 | pub(crate) fn subscriptions(&self) -> Vec<FileId> { |
19 | self.subs.iter().cloned().collect() | 17 | self.subs.iter().cloned().collect() |
20 | } | 18 | } |
21 | } | 19 | } |
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/world.rs index 7eb4d3e56..e0d2f6306 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | path::{Path, PathBuf}, | 2 | path::{Path, PathBuf}, |
3 | sync::Arc, | 3 | sync::Arc, |
4 | time::Duration, | ||
5 | }; | 4 | }; |
6 | 5 | ||
7 | use lsp_types::Url; | 6 | use lsp_types::Url; |
@@ -16,43 +15,38 @@ use failure::{Error, format_err}; | |||
16 | use gen_lsp_server::ErrorCode; | 15 | use gen_lsp_server::ErrorCode; |
17 | 16 | ||
18 | use crate::{ | 17 | use crate::{ |
18 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, | ||
19 | project_model::ProjectWorkspace, | 19 | project_model::ProjectWorkspace, |
20 | vfs_filter::IncludeRustFiles, | 20 | vfs_filter::IncludeRustFiles, |
21 | Result, | 21 | Result, |
22 | LspError, | 22 | LspError, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | /// `WorldState` is the primary mutable state of the language server | ||
26 | /// | ||
27 | /// The most interesting components are `vfs`, which stores a consistent | ||
28 | /// snapshot of the file systems, and `analysis_host`, which stores our | ||
29 | /// incremental salsa database. | ||
25 | #[derive(Debug)] | 30 | #[derive(Debug)] |
26 | pub struct ServerWorldState { | 31 | pub struct WorldState { |
27 | pub roots_to_scan: usize, | 32 | pub roots_to_scan: usize, |
28 | pub roots: Vec<PathBuf>, | 33 | pub roots: Vec<PathBuf>, |
29 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 34 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
30 | pub analysis_host: AnalysisHost, | 35 | pub analysis_host: AnalysisHost, |
31 | pub vfs: Arc<RwLock<Vfs>>, | 36 | pub vfs: Arc<RwLock<Vfs>>, |
32 | // hand-rolling VecDeque here to print things in a nicer way | 37 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
33 | pub latest_completed_requests: Arc<RwLock<[CompletedRequest; N_COMPLETED_REQUESTS]>>, | ||
34 | pub request_idx: usize, | ||
35 | } | 38 | } |
36 | 39 | ||
37 | const N_COMPLETED_REQUESTS: usize = 10; | 40 | /// An immutable snapshot of the world's state at a point in time. |
38 | 41 | pub struct WorldSnapshot { | |
39 | pub struct ServerWorld { | ||
40 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 42 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
41 | pub analysis: Analysis, | 43 | pub analysis: Analysis, |
42 | pub vfs: Arc<RwLock<Vfs>>, | 44 | pub vfs: Arc<RwLock<Vfs>>, |
43 | pub latest_completed_requests: Arc<RwLock<[CompletedRequest; N_COMPLETED_REQUESTS]>>, | 45 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
44 | pub request_idx: usize, | ||
45 | } | ||
46 | |||
47 | #[derive(Debug, Default)] | ||
48 | pub struct CompletedRequest { | ||
49 | pub id: u64, | ||
50 | pub method: String, | ||
51 | pub duration: Duration, | ||
52 | } | 46 | } |
53 | 47 | ||
54 | impl ServerWorldState { | 48 | impl WorldState { |
55 | pub fn new(folder_roots: Vec<PathBuf>, workspaces: Vec<ProjectWorkspace>) -> ServerWorldState { | 49 | pub fn new(folder_roots: Vec<PathBuf>, workspaces: Vec<ProjectWorkspace>) -> WorldState { |
56 | let mut change = AnalysisChange::new(); | 50 | let mut change = AnalysisChange::new(); |
57 | 51 | ||
58 | let mut roots = Vec::new(); | 52 | let mut roots = Vec::new(); |
@@ -82,14 +76,13 @@ impl ServerWorldState { | |||
82 | 76 | ||
83 | let mut analysis_host = AnalysisHost::default(); | 77 | let mut analysis_host = AnalysisHost::default(); |
84 | analysis_host.apply_change(change); | 78 | analysis_host.apply_change(change); |
85 | ServerWorldState { | 79 | WorldState { |
86 | roots_to_scan, | 80 | roots_to_scan, |
87 | roots: folder_roots, | 81 | roots: folder_roots, |
88 | workspaces: Arc::new(workspaces), | 82 | workspaces: Arc::new(workspaces), |
89 | analysis_host, | 83 | analysis_host, |
90 | vfs: Arc::new(RwLock::new(vfs)), | 84 | vfs: Arc::new(RwLock::new(vfs)), |
91 | latest_completed_requests: Default::default(), | 85 | latest_requests: Default::default(), |
92 | request_idx: 0, | ||
93 | } | 86 | } |
94 | } | 87 | } |
95 | 88 | ||
@@ -149,17 +142,12 @@ impl ServerWorldState { | |||
149 | self.analysis_host.apply_change(change); | 142 | self.analysis_host.apply_change(change); |
150 | } | 143 | } |
151 | 144 | ||
152 | pub fn cancel_requests(&mut self) { | 145 | pub fn snapshot(&self) -> WorldSnapshot { |
153 | self.analysis_host.apply_change(AnalysisChange::new()); | 146 | WorldSnapshot { |
154 | } | ||
155 | |||
156 | pub fn snapshot(&self) -> ServerWorld { | ||
157 | ServerWorld { | ||
158 | workspaces: Arc::clone(&self.workspaces), | 147 | workspaces: Arc::clone(&self.workspaces), |
159 | analysis: self.analysis_host.analysis(), | 148 | analysis: self.analysis_host.analysis(), |
160 | vfs: Arc::clone(&self.vfs), | 149 | vfs: Arc::clone(&self.vfs), |
161 | latest_completed_requests: Arc::clone(&self.latest_completed_requests), | 150 | latest_requests: Arc::clone(&self.latest_requests), |
162 | request_idx: self.request_idx.checked_sub(1).unwrap_or(N_COMPLETED_REQUESTS - 1), | ||
163 | } | 151 | } |
164 | } | 152 | } |
165 | 153 | ||
@@ -172,17 +160,11 @@ impl ServerWorldState { | |||
172 | } | 160 | } |
173 | 161 | ||
174 | pub fn complete_request(&mut self, request: CompletedRequest) { | 162 | pub fn complete_request(&mut self, request: CompletedRequest) { |
175 | // special case: don't track status request itself | 163 | self.latest_requests.write().record(request) |
176 | if request.method == "rust-analyzer/analyzerStatus" { | ||
177 | return; | ||
178 | } | ||
179 | let idx = self.request_idx; | ||
180 | self.latest_completed_requests.write()[idx] = request; | ||
181 | self.request_idx = (idx + 1) % N_COMPLETED_REQUESTS; | ||
182 | } | 164 | } |
183 | } | 165 | } |
184 | 166 | ||
185 | impl ServerWorld { | 167 | impl WorldSnapshot { |
186 | pub fn analysis(&self) -> &Analysis { | 168 | pub fn analysis(&self) -> &Analysis { |
187 | &self.analysis | 169 | &self.analysis |
188 | } | 170 | } |