Skip to content

Commit

Permalink
Add a UnindexedProject notification and a corresponding setting.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbarsky committed Dec 4, 2023
1 parent 255eed4 commit 87f79ff
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 41 deletions.
14 changes: 1 addition & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ debug = 0
# chalk-derive = { path = "../chalk/chalk-derive" }
# line-index = { path = "lib/line-index" }
# la-arena = { path = "lib/la-arena" }
# lsp-server = { path = "lib/lsp-server" }
lsp-server = { path = "lib/lsp-server" }


# ungrammar = { path = "../ungrammar" }
Expand Down
9 changes: 8 additions & 1 deletion crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ config_data! {
/// Whether to show `can't find Cargo.toml` error message.
notifications_cargoTomlNotFound: bool = "true",

/// Whether to send an UnindexedProject notification to the client.
notifications_unindexedProject: bool = "false",

/// How many worker threads in the main loop. The default `null` means to pick automatically.
numThreads: Option<usize> = "null",

Expand Down Expand Up @@ -719,6 +722,7 @@ pub enum FilesWatcher {
#[derive(Debug, Clone)]
pub struct NotificationsConfig {
pub cargo_toml_not_found: bool,
pub unindexed_project: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -1191,7 +1195,10 @@ impl Config {
}

pub fn notifications(&self) -> NotificationsConfig {
NotificationsConfig { cargo_toml_not_found: self.data.notifications_cargoTomlNotFound }
NotificationsConfig {
cargo_toml_not_found: self.data.notifications_cargoTomlNotFound,
unindexed_project: self.data.notifications_unindexedProject,
}
}

pub fn cargo_autoreload(&self) -> bool {
Expand Down
89 changes: 69 additions & 20 deletions crates/rust-analyzer/src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
mem_docs::MemDocs,
op_queue::OpQueue,
reload,
task_pool::TaskPool,
task_pool::{TaskPool, TaskQueue},
};

// Enforces drop order
Expand All @@ -53,8 +53,7 @@ type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
/// Note that this struct has more than one impl in various modules!
#[doc(alias = "GlobalMess")]
pub(crate) struct GlobalState {
sender: Sender<lsp_server::Message>,
req_queue: ReqQueue,
pub(crate) conn: Conn,

pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
Expand Down Expand Up @@ -83,7 +82,7 @@ pub(crate) struct GlobalState {
pub(crate) last_flycheck_error: Option<String>,

// VFS
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle + Send>, Receiver<vfs::loader::Message>>,
pub(crate) vfs: Arc<RwLock<(vfs::Vfs, IntMap<FileId, LineEndings>)>>,
pub(crate) vfs_config_version: u32,
pub(crate) vfs_progress_config_version: u32,
Expand Down Expand Up @@ -125,6 +124,16 @@ pub(crate) struct GlobalState {
OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
pub(crate) prime_caches_queue: OpQueue,

/// a deferred task queue. this should only be used if the enqueued Task
/// can only run *after* [`GlobalState::process_changes`] has been called.
pub(crate) task_queue: TaskQueue,
}

#[derive(Clone)]
pub(crate) struct Conn {
pub(crate) sender: Sender<lsp_server::Message>,
req_queue: Arc<ReqQueue>,
}

/// An immutable snapshot of the world's state at a point in time.
Expand All @@ -145,11 +154,13 @@ impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
let conn = Conn { sender, req_queue: Arc::new(ReqQueue::default()) };

let loader = {
let (sender, receiver) = unbounded::<vfs::loader::Message>();
let handle: vfs_notify::NotifyHandle =
vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
let handle = Box::new(handle) as Box<dyn vfs::loader::Handle + Send>;
Handle { handle, receiver }
};

Expand All @@ -164,14 +175,18 @@ impl GlobalState {
Handle { handle, receiver }
};

let task_queue = {
let (sender, receiver) = unbounded();
TaskQueue { sender, receiver }
};

let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
if let Some(capacities) = config.lru_query_capacities() {
analysis_host.update_lru_capacities(capacities);
}
let (flycheck_sender, flycheck_receiver) = unbounded();
let mut this = GlobalState {
sender,
req_queue: ReqQueue::default(),
conn,
task_pool,
fmt_pool,
loader,
Expand Down Expand Up @@ -209,6 +224,8 @@ impl GlobalState {
fetch_proc_macros_queue: OpQueue::default(),

prime_caches_queue: OpQueue::default(),

task_queue,
};
// Apply any required database inputs from the config.
this.update_configuration(config);
Expand Down Expand Up @@ -376,13 +393,13 @@ impl GlobalState {
params: R::Params,
handler: ReqHandler,
) {
let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
self.send(request.into());
self.conn.send_request::<R>(params, handler);
}

pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
let handler = self
.req_queue
let req_queue = Arc::make_mut(&mut self.conn.req_queue);

let handler = req_queue
.outgoing
.complete(response.id.clone())
.expect("received response for unknown request");
Expand All @@ -393,22 +410,20 @@ impl GlobalState {
&self,
params: N::Params,
) {
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
self.send(not.into());
self.conn.send_notification::<N>(params);
}

pub(crate) fn register_request(
&mut self,
request: &lsp_server::Request,
request_received: Instant,
) {
self.req_queue
.incoming
.register(request.id.clone(), (request.method.clone(), request_received));
self.conn.register_request(request, request_received);
}

pub(crate) fn respond(&mut self, response: lsp_server::Response) {
if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
let req_queue = Arc::make_mut(&mut self.conn.req_queue);
if let Some((method, start)) = req_queue.incoming.complete(response.id.clone()) {
if let Some(err) = &response.error {
if err.message.starts_with("server panicked") {
self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
Expand All @@ -417,17 +432,51 @@ impl GlobalState {

let duration = start.elapsed();
tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
self.send(response.into());
self.conn.send(response.into());
}
}

pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
if let Some(response) = self.req_queue.incoming.cancel(request_id) {
self.conn.cancel(request_id);
}

pub(crate) fn is_completed(&self, request: &lsp_server::Request) -> bool {
self.conn.is_completed(&request)
}
}

impl Conn {
pub(crate) fn send_request<R: lsp_types::request::Request>(
&mut self,
params: R::Params,
handler: ReqHandler,
) {
let req_queue = Arc::make_mut(&mut self.req_queue);
let request = req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
self.send(request.into());
}

pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
&self,
params: N::Params,
) {
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
self.send(not.into());
}

fn register_request(&mut self, request: &lsp_server::Request, request_received: Instant) {
let req_queue = Arc::make_mut(&mut self.req_queue);
req_queue.incoming.register(request.id.clone(), (request.method.clone(), request_received));
}

fn cancel(&mut self, request_id: lsp_server::RequestId) {
let req_queue = Arc::make_mut(&mut self.req_queue);
if let Some(response) = req_queue.incoming.cancel(request_id) {
self.send(response.into());
}
}

pub(crate) fn is_completed(&self, request: &lsp_server::Request) -> bool {
fn is_completed(&self, request: &lsp_server::Request) -> bool {
self.req_queue.incoming.is_completed(&request.id)
}

Expand Down
8 changes: 8 additions & 0 deletions crates/rust-analyzer/src/handlers/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ pub(crate) fn handle_did_open_text_document(
if already_exists {
tracing::error!("duplicate DidOpenTextDocument: {}", path);
}

state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes()));
if state.config.notifications().unindexed_project {
tracing::debug!("queuing task");
let _ = state
.task_queue
.sender
.send(crate::main_loop::QueuedTask::CheckIfIndexed(params.text_document.uri));
}
}
Ok(())
}
Expand Down
13 changes: 13 additions & 0 deletions crates/rust-analyzer/src/lsp/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,3 +696,16 @@ pub struct CompletionImport {
pub struct ClientCommandOptions {
pub commands: Vec<String>,
}

pub enum UnindexedProject {}

impl Notification for UnindexedProject {
type Params = UnindexedProjectParams;
const METHOD: &'static str = "rust-analyzer/unindexedProject";
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct UnindexedProjectParams {
pub text_documents: Vec<TextDocumentIdentifier>,
}
43 changes: 42 additions & 1 deletion crates/rust-analyzer/src/main_loop.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! The main loop of `rust-analyzer` responsible for dispatching LSP
//! requests/replies and notifications back to the client.
use crate::lsp::ext;
use std::{
fmt,
time::{Duration, Instant},
Expand Down Expand Up @@ -56,10 +57,16 @@ pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> {
enum Event {
Lsp(lsp_server::Message),
Task(Task),
QueuedTask(QueuedTask),
Vfs(vfs::loader::Message),
Flycheck(flycheck::Message),
}

#[derive(Debug)]
pub(crate) enum QueuedTask {
CheckIfIndexed(lsp_types::Url),
}

#[derive(Debug)]
pub(crate) enum Task {
Response(lsp_server::Response),
Expand Down Expand Up @@ -104,6 +111,7 @@ impl fmt::Debug for Event {
match self {
Event::Lsp(it) => fmt::Debug::fmt(it, f),
Event::Task(it) => fmt::Debug::fmt(it, f),
Event::QueuedTask(it) => fmt::Debug::fmt(it, f),
Event::Vfs(it) => fmt::Debug::fmt(it, f),
Event::Flycheck(it) => fmt::Debug::fmt(it, f),
}
Expand Down Expand Up @@ -182,6 +190,9 @@ impl GlobalState {
recv(self.task_pool.receiver) -> task =>
Some(Event::Task(task.unwrap())),

recv(self.task_queue.receiver) -> task =>
Some(Event::QueuedTask(task.unwrap())),

recv(self.fmt_pool.receiver) -> task =>
Some(Event::Task(task.unwrap())),

Expand All @@ -199,7 +210,7 @@ impl GlobalState {
let _p = profile::span("GlobalState::handle_event");

let event_dbg_msg = format!("{event:?}");
tracing::debug!("{:?} handle_event({})", loop_start, event_dbg_msg);
tracing::debug!(?loop_start, ?event, "handle_event");
if tracing::enabled!(tracing::Level::INFO) {
let task_queue_len = self.task_pool.handle.len();
if task_queue_len > 0 {
Expand All @@ -214,6 +225,10 @@ impl GlobalState {
lsp_server::Message::Notification(not) => self.on_notification(not)?,
lsp_server::Message::Response(resp) => self.complete_request(resp),
},
Event::QueuedTask(task) => {
let _p = profile::span("GlobalState::handle_event/queued_task");
self.handle_queued_task(task);
}
Event::Task(task) => {
let _p = profile::span("GlobalState::handle_event/task");
let mut prime_caches_progress = Vec::new();
Expand Down Expand Up @@ -607,6 +622,32 @@ impl GlobalState {
}
}

fn handle_queued_task(&mut self, task: QueuedTask) {
match task {
QueuedTask::CheckIfIndexed(uri) => {
let snap = self.snapshot();
let conn = self.conn.clone();

self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |_| {
tracing::debug!(?uri, "handling uri");
let id = from_proto::file_id(&snap, &uri).expect("unable to get FileId");
if let Ok(crates) = &snap.analysis.crates_for(id) {
if crates.is_empty() {
conn.send_notification::<ext::UnindexedProject>(
ext::UnindexedProjectParams {
text_documents: vec![lsp_types::TextDocumentIdentifier { uri }],
},
);
tracing::debug!("sent notification");
} else {
tracing::debug!(?uri, "is indexed");
}
}
});
}
}
}

fn handle_flycheck_msg(&mut self, message: flycheck::Message) {
match message {
flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => {
Expand Down
Loading

0 comments on commit 87f79ff

Please sign in to comment.