aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/db.rs3
-rw-r--r--crates/ra_hir/src/ty/traits.rs5
-rw-r--r--crates/ra_lsp_server/src/cargo_target_spec.rs6
-rw-r--r--crates/ra_lsp_server/src/conv.rs50
-rw-r--r--crates/ra_lsp_server/src/lib.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs342
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs74
-rw-r--r--crates/ra_lsp_server/src/main_loop/pending_requests.rs72
-rw-r--r--crates/ra_lsp_server/src/main_loop/subscriptions.rs12
-rw-r--r--crates/ra_lsp_server/src/world.rs (renamed from crates/ra_lsp_server/src/server_world.rs)58
-rw-r--r--crates/ra_prof/src/lib.rs13
11 files changed, 359 insertions, 278 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 11cdf9c34..d84e1598b 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,5 +1,6 @@
1use std::sync::{Arc, Mutex}; 1use std::sync::Arc;
2 2
3use parking_lot::Mutex;
3use ra_syntax::{SyntaxNode, TreeArc, SmolStr, ast}; 4use ra_syntax::{SyntaxNode, TreeArc, SmolStr, ast};
4use ra_db::{SourceDatabase, salsa}; 5use ra_db::{SourceDatabase, salsa};
5 6
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index fc90f56d5..db78f25ca 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -1,6 +1,7 @@
1//! Trait solving using Chalk. 1//! Trait solving using Chalk.
2use std::sync::{Arc, Mutex}; 2use std::sync::Arc;
3 3
4use parking_lot::Mutex;
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5use log::debug; 6use log::debug;
6use chalk_ir::cast::Cast; 7use chalk_ir::cast::Cast;
@@ -61,7 +62,7 @@ fn solve(
61 let context = ChalkContext { db, krate }; 62 let context = ChalkContext { db, krate };
62 let solver = db.solver(krate); 63 let solver = db.solver(krate);
63 debug!("solve goal: {:?}", goal); 64 debug!("solve goal: {:?}", goal);
64 let solution = solver.lock().unwrap().solve_with_fuel(&context, goal, Some(1000)); 65 let solution = solver.lock().solve_with_fuel(&context, goal, Some(1000));
65 debug!("solve({:?}) => {:?}", goal, solution); 66 debug!("solve({:?}) => {:?}", goal, solution);
66 solution 67 solution
67} 68}
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 @@
1use crate::{ 1use 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
7use ra_ide_api::{FileId, RunnableKind}; 7use ra_ide_api::{FileId, RunnableKind};
8 8
9pub(crate) fn runnable_args( 9pub(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
60impl CargoTargetSpec { 60impl 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::{
12use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 12use ra_syntax::{SyntaxKind, TextRange, TextUnit};
13use ra_text_edit::{AtomTextEdit, TextEdit}; 13use ra_text_edit::{AtomTextEdit, TextEdit};
14 14
15use crate::{req, server_world::ServerWorld, Result}; 15use crate::{req, world::WorldSnapshot, Result};
16 16
17pub trait Conv { 17pub trait Conv {
18 type Output; 18 type Output;
@@ -228,49 +228,49 @@ impl<T: ConvWith> ConvWith for Option<T> {
228} 228}
229 229
230impl<'a> TryConvWith for &'a Url { 230impl<'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
238impl TryConvWith for FileId { 238impl 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
246impl<'a> TryConvWith for &'a TextDocumentItem { 246impl<'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
254impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { 254impl<'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
262impl<'a> TryConvWith for &'a TextDocumentIdentifier { 262impl<'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
270impl<'a> TryConvWith for &'a TextDocumentPositionParams { 270impl<'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
281impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) { 281impl<'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
304impl TryConvWith for SourceChange { 304impl 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
344impl TryConvWith for SourceFileEdit { 344impl 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
358impl TryConvWith for FileSystemEdit { 358impl 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
377impl TryConvWith for &NavigationTarget { 377impl 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
387pub fn to_location_link( 387pub 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(
410pub fn to_location( 410pub 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;
7mod vfs_filter; 7mod vfs_filter;
8pub mod req; 8pub mod req;
9pub mod init; 9pub mod init;
10mod server_world; 10mod world;
11 11
12pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 12pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
13pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError, init::InitializationOptions}; 13pub 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 @@
1mod handlers; 1mod handlers;
2mod subscriptions; 2mod subscriptions;
3pub(crate) mod pending_requests;
3 4
4use std::{fmt, path::PathBuf, sync::Arc, time::Instant, any::TypeId}; 5use std::{fmt, path::PathBuf, sync::Arc, time::Instant};
5 6
6use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; 7use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender};
7use failure::{bail, format_err}; 8use failure::{bail, format_err};
@@ -12,19 +13,24 @@ use gen_lsp_server::{
12use lsp_types::NumberOrString; 13use lsp_types::NumberOrString;
13use ra_ide_api::{Canceled, FileId, LibraryData}; 14use ra_ide_api::{Canceled, FileId, LibraryData};
14use ra_vfs::VfsTask; 15use ra_vfs::VfsTask;
15use rustc_hash::FxHashMap;
16use serde::{de::DeserializeOwned, Serialize}; 16use serde::{de::DeserializeOwned, Serialize};
17use threadpool::ThreadPool; 17use threadpool::ThreadPool;
18use ra_prof::profile;
18 19
19use crate::{ 20use 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};
27use ra_prof::profile; 31
32const THREADPOOL_SIZE: usize = 8;
33const 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)]
43enum Task {
44 Respond(RawResponse),
45 Notify(RawNotification),
46}
47
48struct PendingRequest {
49 received: Instant,
50 method: String,
51}
52
53impl 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
59const THREADPOOL_SIZE: usize = 8;
60
61pub fn main_loop( 48pub 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)]
110enum Task {
111 Respond(RawResponse),
112 Notify(RawNotification),
113}
114
125enum Event { 115enum 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(
305fn on_task( 277fn 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
326fn on_request( 297fn 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
377fn on_notification( 353fn 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
446struct PoolDispatcher<'a> { 422struct 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
454impl<'a> PoolDispatcher<'a> { 432impl<'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
519fn result_to_task<R>(id: u64, result: Result<R::Result>) -> Task
520where
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
530fn update_file_notifications_on_threadpool( 558fn 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
31pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result<String> { 31pub 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
42pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> Result<String> { 42pub 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
51pub fn handle_extend_selection( 51pub 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
71pub fn handle_selection_range( 71pub 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
112pub fn handle_find_matching_brace( 112pub 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
131pub fn handle_join_lines( 131pub 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
140pub fn handle_on_enter( 140pub 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
152pub fn handle_on_type_formatting( 152pub 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
183pub fn handle_document_symbol( 183pub 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
221pub fn handle_workspace_symbol( 221pub 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
264pub fn handle_goto_definition( 264pub 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
284pub fn handle_goto_implementation( 284pub 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
304pub fn handle_goto_type_definition( 304pub 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
324pub fn handle_parent_module( 324pub 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
337pub fn handle_runnables( 337pub 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
398pub fn handle_decorations( 398pub 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
406pub fn handle_completion( 406pub 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
449pub fn handle_folding_range( 449pub 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
483pub fn handle_signature_help( 483pub 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
502pub fn handle_hover( 502pub 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
524pub fn handle_prepare_rename( 524pub 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
546pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 546pub 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
571pub fn handle_references( 571pub 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
599pub fn handle_formatting( 599pub 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
643pub fn handle_code_action( 643pub 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
706pub fn handle_code_lens( 706pub 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
784pub fn handle_code_lens_resolve(world: ServerWorld, code_lens: CodeLens) -> Result<CodeLens> { 784pub 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
828pub fn handle_document_highlight( 828pub 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
847pub fn publish_diagnostics( 847pub 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
869pub fn publish_decorations( 869pub 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
877fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> { 877fn 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 @@
1use std::time::{Duration, Instant};
2
3use rustc_hash::FxHashMap;
4
5#[derive(Debug)]
6pub struct CompletedRequest {
7 pub id: u64,
8 pub method: String,
9 pub duration: Duration,
10}
11
12#[derive(Debug)]
13pub(crate) struct PendingRequest {
14 pub(crate) id: u64,
15 pub(crate) method: String,
16 pub(crate) received: Instant,
17}
18
19impl 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)]
30pub(crate) struct PendingRequests {
31 map: FxHashMap<u64, PendingRequest>,
32}
33
34impl 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
48const N_COMPLETED_REQUESTS: usize = 10;
49
50#[derive(Debug, Default)]
51pub 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
57impl 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 @@
1use ra_ide_api::FileId; 1use ra_ide_api::FileId;
2use rustc_hash::FxHashSet; 2use rustc_hash::FxHashSet;
3 3
4pub struct Subscriptions { 4#[derive(Default)]
5pub(crate) struct Subscriptions {
5 subs: FxHashSet<FileId>, 6 subs: FxHashSet<FileId>,
6} 7}
7 8
8impl Subscriptions { 9impl 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 @@
1use std::{ 1use std::{
2 path::{Path, PathBuf}, 2 path::{Path, PathBuf},
3 sync::Arc, 3 sync::Arc,
4 time::Duration,
5}; 4};
6 5
7use lsp_types::Url; 6use lsp_types::Url;
@@ -16,43 +15,38 @@ use failure::{Error, format_err};
16use gen_lsp_server::ErrorCode; 15use gen_lsp_server::ErrorCode;
17 16
18use crate::{ 17use 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)]
26pub struct ServerWorldState { 31pub 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
37const N_COMPLETED_REQUESTS: usize = 10; 40/// An immutable snapshot of the world's state at a point in time.
38 41pub struct WorldSnapshot {
39pub 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)]
48pub struct CompletedRequest {
49 pub id: u64,
50 pub method: String,
51 pub duration: Duration,
52} 46}
53 47
54impl ServerWorldState { 48impl 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
185impl ServerWorld { 167impl WorldSnapshot {
186 pub fn analysis(&self) -> &Analysis { 168 pub fn analysis(&self) -> &Analysis {
187 &self.analysis 169 &self.analysis
188 } 170 }
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 031405879..e681b8e0b 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -76,17 +76,16 @@ pub fn profile(desc: &str) -> Profiler {
76 } 76 }
77 }; 77 };
78 } 78 }
79
80 if stack.starts.len() > stack.filter_data.depth { 79 if stack.starts.len() > stack.filter_data.depth {
81 return Profiler { desc: None }; 80 return Profiler { desc: None };
82 } 81 }
83 82 let allowed = &stack.filter_data.allowed;
84 if stack.filter_data.allowed.is_empty() || stack.filter_data.allowed.contains(desc) { 83 if stack.starts.is_empty() && !allowed.is_empty() && !allowed.contains(desc) {
85 stack.starts.push(Instant::now()); 84 return Profiler { desc: None };
86 Profiler { desc: Some(desc.to_string()) }
87 } else {
88 Profiler { desc: None }
89 } 85 }
86
87 stack.starts.push(Instant::now());
88 Profiler { desc: Some(desc.to_string()) }
90 }) 89 })
91} 90}
92 91