use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
-use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId};
-use ide_db::base_db::{CrateId, VfsPath};
+use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
+use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
-use project_model::{
- BuildDataCollector, BuildDataResult, CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target,
-};
+use proc_macro_api::ProcMacroServer;
+use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;
use crate::{
config::Config,
diagnostics::{CheckFixes, DiagnosticCollection},
- document::DocumentData,
from_proto,
line_index::{LineEndings, LineIndex},
lsp_ext,
main_loop::Task,
+ mem_docs::MemDocs,
op_queue::OpQueue,
- reload::SourceRootConfig,
- request_metrics::{LatestRequests, RequestMetrics},
+ reload::{self, SourceRootConfig},
thread_pool::TaskPool,
to_proto::url_from_abs_path,
Result,
pub(crate) config: Arc<Config>,
pub(crate) analysis_host: AnalysisHost,
pub(crate) diagnostics: DiagnosticCollection,
- pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
+ pub(crate) mem_docs: MemDocs,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
pub(crate) shutdown_requested: bool,
+ pub(crate) proc_macro_changed: bool,
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
pub(crate) source_root_config: SourceRootConfig,
- pub(crate) proc_macro_client: Option<ProcMacroClient>,
+ pub(crate) proc_macro_client: Option<ProcMacroServer>,
pub(crate) flycheck: Vec<FlycheckHandle>,
pub(crate) flycheck_sender: Sender<flycheck::Message>,
pub(crate) vfs_progress_n_total: usize,
pub(crate) vfs_progress_n_done: usize,
- /// For both `workspaces` and `workspace_build_data`, the field stores the
- /// data we actually use, while the `OpQueue` stores the result of the last
- /// fetch.
+ /// `workspaces` field stores the data we actually use, while the `OpQueue`
+ /// stores the result of the last fetch.
+ ///
+ /// If the fetch (partially) fails, we do not update the current value.
+ ///
+ /// The handling of build data is subtle. We fetch workspace in two phases:
+ ///
+ /// *First*, we run `cargo metadata`, which gives us fast results for
+ /// initial analysis.
+ ///
+ /// *Second*, we run `cargo check` which runs build scripts and compiles
+ /// proc macros.
///
- /// If the fetch (partially) fails, we do not update the values.
+ /// We need both for the precise analysis, but we want rust-analyzer to be
+ /// at least partially available just after the first phase. That's because
+ /// first phase is much faster, and is much less likely to fail.
+ ///
+ /// This creates a complication -- by the time the second phase completes,
+ /// the results of the fist phase could be invalid. That is, while we run
+ /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
+ /// `cargo metadata` completes before `cargo check`.
+ ///
+ /// An additional complication is that we want to avoid needless work. When
+ /// the user just adds comments or whitespace to Cargo.toml, we do not want
+ /// to invalidate any salsa caches.
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
- pub(crate) fetch_workspaces_queue: OpQueue<(), Vec<anyhow::Result<ProjectWorkspace>>>,
- pub(crate) workspace_build_data: Option<BuildDataResult>,
+ pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
pub(crate) fetch_build_data_queue:
- OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>,
- pub(crate) prime_caches_queue: OpQueue<(), ()>,
+ OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
- latest_requests: Arc<RwLock<LatestRequests>>,
+ pub(crate) prime_caches_queue: OpQueue<()>,
}
/// An immutable snapshot of the world's state at a point in time.
pub(crate) config: Arc<Config>,
pub(crate) analysis: Analysis,
pub(crate) check_fixes: CheckFixes,
- pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
- mem_docs: FxHashMap<VfsPath, DocumentData>,
+ mem_docs: MemDocs,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}
+impl std::panic::UnwindSafe for GlobalStateSnapshot {}
+
impl GlobalState {
pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
let loader = {
let analysis_host = AnalysisHost::new(config.lru_capacity());
let (flycheck_sender, flycheck_receiver) = unbounded();
- GlobalState {
+ let mut this = GlobalState {
sender,
req_queue: ReqQueue::default(),
task_pool,
loader,
- config: Arc::new(config),
+ config: Arc::new(config.clone()),
analysis_host,
diagnostics: Default::default(),
- mem_docs: FxHashMap::default(),
+ mem_docs: MemDocs::default(),
semantic_tokens_cache: Arc::new(Default::default()),
shutdown_requested: false,
+ proc_macro_changed: false,
last_reported_status: None,
source_root_config: SourceRootConfig::default(),
proc_macro_client: None,
workspaces: Arc::new(Vec::new()),
fetch_workspaces_queue: OpQueue::default(),
- workspace_build_data: None,
prime_caches_queue: OpQueue::default(),
fetch_build_data_queue: OpQueue::default(),
- latest_requests: Default::default(),
- }
+ };
+ // Apply any required database inputs from the config.
+ this.update_configuration(config);
+ this
}
pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes");
let mut fs_changes = Vec::new();
- let mut has_fs_changes = false;
+ // A file was added or deleted
+ let mut has_structure_changes = false;
let change = {
let mut change = Change::new();
}
for file in changed_files {
- if file.is_created_or_deleted() {
- if let Some(path) = vfs.file_path(file.file_id).as_path() {
- fs_changes.push((path.to_path_buf(), file.change_kind));
- has_fs_changes = true;
+ if !file.is_created_or_deleted() {
+ let crates = self.analysis_host.raw_database().relevant_crates(file.file_id);
+ let crate_graph = self.analysis_host.raw_database().crate_graph();
+
+ if crates.iter().any(|&krate| !crate_graph[krate].proc_macro.is_empty()) {
+ self.proc_macro_changed = true;
+ }
+ }
+
+ if let Some(path) = vfs.file_path(file.file_id).as_path() {
+ let path = path.to_path_buf();
+ if reload::should_refresh_for_change(&path, file.change_kind) {
+ self.fetch_workspaces_queue.request_op();
+ }
+ fs_changes.push((path, file.change_kind));
+ if file.is_created_or_deleted() {
+ has_structure_changes = true;
}
}
};
change.change_file(file.file_id, text);
}
- if has_fs_changes {
- let roots = self.source_root_config.partition(&vfs);
+ if has_structure_changes {
+ let roots = self.source_root_config.partition(vfs);
change.set_roots(roots);
}
change
};
self.analysis_host.apply_change(change);
- self.maybe_refresh(&fs_changes);
true
}
workspaces: Arc::clone(&self.workspaces),
analysis: self.analysis_host.analysis(),
vfs: Arc::clone(&self.vfs),
- latest_requests: Arc::clone(&self.latest_requests),
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
mem_docs: self.mem_docs.clone(),
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
}
pub(crate) fn respond(&mut self, response: lsp_server::Response) {
if let Some((method, start)) = self.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))
+ }
+ }
+
let duration = start.elapsed();
- log::info!("handled req#{} in {:?}", response.id, duration);
- let metrics = RequestMetrics { id: response.id.clone(), method, duration };
- self.latest_requests.write().record(metrics);
+ tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
self.send(response.into());
}
}
file_id_to_url(&self.vfs.read().0, id)
}
- pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> {
+ pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
let endings = self.vfs.read().1[&file_id];
let index = self.analysis.file_line_index(file_id)?;
let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
}
pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
- let path = from_proto::vfs_path(&url).ok()?;
+ let path = from_proto::vfs_path(url).ok()?;
Some(self.mem_docs.get(&path)?.version)
}
base.pop();
let path = base.join(&path.path).unwrap();
let path = path.as_path().unwrap();
- url_from_abs_path(&path)
+ url_from_abs_path(path)
}
pub(crate) fn cargo_target_for_crate_root(
let path = path.as_path()?;
self.workspaces.iter().find_map(|ws| match ws {
ProjectWorkspace::Cargo { cargo, .. } => {
- cargo.target_by_root(&path).map(|it| (cargo, it))
+ cargo.target_by_root(path).map(|it| (cargo, it))
}
ProjectWorkspace::Json { .. } => None,
+ ProjectWorkspace::DetachedFiles { .. } => None,
})
}
}
pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
let path = vfs.file_path(id);
let path = path.as_path().unwrap();
- url_from_abs_path(&path)
+ url_from_abs_path(path)
}
pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {