use crate::{
config::{Config, FilesWatcher, LinkedProject},
- global_state::{GlobalState, Status},
+ global_state::GlobalState,
lsp_ext,
main_loop::Task,
};
-use lsp_ext::StatusParams;
#[derive(Debug)]
pub(crate) enum ProjectWorkspaceProgress {
}
impl GlobalState {
+ pub(crate) fn is_quiescent(&self) -> bool {
+ !(self.fetch_workspaces_queue.op_in_progress()
+ || self.fetch_build_data_queue.op_in_progress()
+ || self.vfs_progress_config_version < self.vfs_config_version
+ || self.vfs_progress_n_done < self.vfs_progress_n_total)
+ }
+
pub(crate) fn update_configuration(&mut self, config: Config) {
let _p = profile::span("GlobalState::update_configuration");
let old_config = mem::replace(&mut self.config, Arc::new(config));
if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
return;
}
- match self.status {
- Status::Loading | Status::NeedsReload => return,
- Status::Ready { .. } | Status::Invalid => (),
- }
log::info!(
- "Reloading workspace because of the following changes: {}",
+ "Requesting workspace reload because of the following changes: {}",
itertools::join(
changes
.iter()
.filter(|(path, kind)| is_interesting(path, *kind))
- .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)),
+ .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)),
", "
)
);
- if self.config.cargo_autoreload() {
- self.fetch_workspaces_request();
- } else {
- self.transition(Status::NeedsReload);
- }
+ self.fetch_workspaces_request();
fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
false
}
}
- pub(crate) fn transition(&mut self, new_status: Status) {
- self.status = new_status;
- if self.config.status_notification() {
- let lsp_status = match new_status {
- Status::Loading => lsp_ext::Status::Loading,
- Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
- Status::Ready { partial: false } => lsp_ext::Status::Ready,
- Status::Invalid => lsp_ext::Status::Invalid,
- Status::NeedsReload => lsp_ext::Status::NeedsReload,
- };
- self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
- status: lsp_status,
- });
+ pub(crate) fn report_new_status_if_needed(&mut self) {
+ if !self.config.server_status_notification() {
+ return;
}
- }
-
- pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
- self.fetch_build_data_queue.request_op(build_data_collector);
- }
- pub(crate) fn fetch_build_data_if_needed(&mut self) {
- let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
- Some(it) => it,
- None => return,
+ let mut status = lsp_ext::ServerStatusParams {
+ health: lsp_ext::Health::Ok,
+ quiescent: self.is_quiescent(),
+ message: None,
};
- self.task_pool.handle.spawn_with_sender(move |sender| {
- sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
+ if !self.config.cargo_autoreload()
+ && self.is_quiescent()
+ && self.fetch_workspaces_queue.op_requested()
+ {
+ status.health = lsp_ext::Health::Warning;
+ status.message = Some("Workspace reload required".to_string())
+ }
+ if let Some(error) = self.loading_error() {
+ status.health = lsp_ext::Health::Error;
+ status.message = Some(format!("Workspace reload failed: {}", error))
+ }
- let progress = {
- let sender = sender.clone();
- move |msg| {
- sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
- }
- };
- let res = build_data_collector.collect(&progress);
- sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
- });
- }
- pub(crate) fn fetch_build_data_completed(
- &mut self,
- build_data: anyhow::Result<BuildDataResult>,
- ) {
- self.fetch_build_data_queue.op_completed(Some(build_data))
+ if self.last_reported_status.as_ref() != Some(&status) {
+ self.last_reported_status = Some(status.clone());
+ self.send_notification::<lsp_ext::ServerStatusNotification>(status);
+ }
}
pub(crate) fn fetch_workspaces_request(&mut self) {
self.fetch_workspaces_queue.op_completed(workspaces)
}
- pub(crate) fn switch_workspaces(&mut self) {
- let _p = profile::span("GlobalState::switch_workspaces");
- let workspaces = self.fetch_workspaces_queue.last_op_result();
- log::info!("will switch workspaces: {:?}", workspaces);
+ pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
+ self.fetch_build_data_queue.request_op(build_data_collector);
+ }
+ pub(crate) fn fetch_build_data_if_needed(&mut self) {
+ let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
+ Some(it) => it,
+ None => return,
+ };
+ self.task_pool.handle.spawn_with_sender(move |sender| {
+ sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
- let mut error_message = None;
- let workspaces = workspaces
- .iter()
- .filter_map(|res| match res {
- Ok(it) => Some(it.clone()),
- Err(err) => {
- log::error!("failed to load workspace: {:#}", err);
- let message = error_message.get_or_insert_with(String::new);
- stdx::format_to!(
- message,
- "rust-analyzer failed to load workspace: {:#}\n",
- err
- );
- None
+ let progress = {
+ let sender = sender.clone();
+ move |msg| {
+ sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
}
- })
- .collect::<Vec<_>>();
+ };
+ let res = build_data_collector.collect(&progress);
+ sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
+ });
+ }
+ pub(crate) fn fetch_build_data_completed(
+ &mut self,
+ build_data: anyhow::Result<BuildDataResult>,
+ ) {
+ self.fetch_build_data_queue.op_completed(Some(build_data))
+ }
- let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
- Some(Ok(it)) => Some(it.clone()),
- None => None,
- Some(Err(err)) => {
- log::error!("failed to fetch build data: {:#}", err);
- let message = error_message.get_or_insert_with(String::new);
- stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
- None
- }
- };
+ pub(crate) fn switch_workspaces(&mut self) {
+ let _p = profile::span("GlobalState::switch_workspaces");
+ log::info!("will switch workspaces");
- if let Some(error_message) = error_message {
+ if let Some(error_message) = self.loading_error() {
+ log::error!("failed to switch workspaces: {}", error_message);
self.show_message(lsp_types::MessageType::Error, error_message);
if !self.workspaces.is_empty() {
return;
}
}
+ let workspaces = self
+ .fetch_workspaces_queue
+ .last_op_result()
+ .iter()
+ .filter_map(|res| res.as_ref().ok().cloned())
+ .collect::<Vec<_>>();
+
+ let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
+ Some(Ok(it)) => Some(it.clone()),
+ None | Some(Err(_)) => None,
+ };
+
if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
return;
}
log::info!("did switch workspaces");
}
+ fn loading_error(&self) -> Option<String> {
+ let mut message = None;
+
+ for ws in self.fetch_workspaces_queue.last_op_result() {
+ if let Err(err) = ws {
+ let message = message.get_or_insert_with(String::new);
+ stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err);
+ }
+ }
+
+ if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() {
+ let message = message.get_or_insert_with(String::new);
+ stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
+ }
+
+ message
+ }
+
fn reload_flycheck(&mut self) {
let _p = profile::span("GlobalState::reload_flycheck");
let config = match self.config.flycheck() {