mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 21:28:51 +00:00
Automatically reload project info on Cargo.toml changes
This commit is contained in:
parent
5fa8f8e376
commit
d0a8f6a3eb
7 changed files with 57 additions and 28 deletions
|
@ -24,7 +24,7 @@ use rustc_hash::FxHashMap;
|
||||||
///
|
///
|
||||||
/// We use absolute paths here, `cargo metadata` guarantees to always produce
|
/// We use absolute paths here, `cargo metadata` guarantees to always produce
|
||||||
/// abs paths.
|
/// abs paths.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct CargoWorkspace {
|
pub struct CargoWorkspace {
|
||||||
packages: Arena<PackageData>,
|
packages: Arena<PackageData>,
|
||||||
targets: Arena<TargetData>,
|
targets: Arena<TargetData>,
|
||||||
|
@ -68,7 +68,7 @@ pub type Package = Idx<PackageData>;
|
||||||
|
|
||||||
pub type Target = Idx<TargetData>;
|
pub type Target = Idx<TargetData>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct PackageData {
|
pub struct PackageData {
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -83,13 +83,13 @@ pub struct PackageData {
|
||||||
pub proc_macro_dylib_path: Option<AbsPathBuf>,
|
pub proc_macro_dylib_path: Option<AbsPathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct PackageDependency {
|
pub struct PackageDependency {
|
||||||
pub pkg: Package,
|
pub pkg: Package,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct TargetData {
|
pub struct TargetData {
|
||||||
pub package: Package,
|
pub package: Package,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub use crate::{
|
||||||
};
|
};
|
||||||
pub use ra_proc_macro::ProcMacroClient;
|
pub use ra_proc_macro::ProcMacroClient;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum ProjectWorkspace {
|
pub enum ProjectWorkspace {
|
||||||
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
|
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
|
||||||
Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
|
Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
|
||||||
|
|
|
@ -3,19 +3,19 @@
|
||||||
use std::{convert::TryFrom, env, ops, path::Path, process::Command};
|
use std::{convert::TryFrom, env, ops, path::Path, process::Command};
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Result};
|
use anyhow::{bail, format_err, Result};
|
||||||
|
use paths::{AbsPath, AbsPathBuf};
|
||||||
use ra_arena::{Arena, Idx};
|
use ra_arena::{Arena, Idx};
|
||||||
|
|
||||||
use crate::output;
|
use crate::output;
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Sysroot {
|
pub struct Sysroot {
|
||||||
crates: Arena<SysrootCrateData>,
|
crates: Arena<SysrootCrateData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SysrootCrate = Idx<SysrootCrateData>;
|
pub type SysrootCrate = Idx<SysrootCrateData>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct SysrootCrateData {
|
pub struct SysrootCrateData {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub root: AbsPathBuf,
|
pub root: AbsPathBuf,
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub struct Config {
|
||||||
pub files: FilesConfig,
|
pub files: FilesConfig,
|
||||||
pub notifications: NotificationsConfig,
|
pub notifications: NotificationsConfig,
|
||||||
|
|
||||||
|
pub cargo_autoreload: bool,
|
||||||
pub cargo: CargoConfig,
|
pub cargo: CargoConfig,
|
||||||
pub rustfmt: RustfmtConfig,
|
pub rustfmt: RustfmtConfig,
|
||||||
pub flycheck: Option<FlycheckConfig>,
|
pub flycheck: Option<FlycheckConfig>,
|
||||||
|
@ -141,6 +142,7 @@ impl Config {
|
||||||
files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
|
files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
|
||||||
notifications: NotificationsConfig { cargo_toml_not_found: true },
|
notifications: NotificationsConfig { cargo_toml_not_found: true },
|
||||||
|
|
||||||
|
cargo_autoreload: true,
|
||||||
cargo: CargoConfig::default(),
|
cargo: CargoConfig::default(),
|
||||||
rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
|
rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
|
||||||
flycheck: Some(FlycheckConfig::CargoCommand {
|
flycheck: Some(FlycheckConfig::CargoCommand {
|
||||||
|
@ -189,6 +191,7 @@ impl Config {
|
||||||
};
|
};
|
||||||
self.notifications =
|
self.notifications =
|
||||||
NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
|
NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
|
||||||
|
self.cargo_autoreload = data.cargo_autoreload;
|
||||||
self.cargo = CargoConfig {
|
self.cargo = CargoConfig {
|
||||||
no_default_features: data.cargo_noDefaultFeatures,
|
no_default_features: data.cargo_noDefaultFeatures,
|
||||||
all_features: data.cargo_allFeatures,
|
all_features: data.cargo_allFeatures,
|
||||||
|
@ -364,6 +367,7 @@ config_data! {
|
||||||
struct ConfigData {
|
struct ConfigData {
|
||||||
callInfo_full: bool = true,
|
callInfo_full: bool = true,
|
||||||
|
|
||||||
|
cargo_autoreload: bool = true,
|
||||||
cargo_allFeatures: bool = false,
|
cargo_allFeatures: bool = false,
|
||||||
cargo_features: Vec<String> = Vec::new(),
|
cargo_features: Vec<String> = Vec::new(),
|
||||||
cargo_loadOutDirsFromCheck: bool = false,
|
cargo_loadOutDirsFromCheck: bool = false,
|
||||||
|
|
|
@ -314,19 +314,6 @@ impl GlobalState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transition(&mut self, new_status: Status) {
|
|
||||||
self.status = Status::Ready;
|
|
||||||
if self.config.client_caps.status_notification {
|
|
||||||
let lsp_status = match new_status {
|
|
||||||
Status::Loading => lsp_ext::Status::Loading,
|
|
||||||
Status::Ready => lsp_ext::Status::Ready,
|
|
||||||
Status::Invalid => lsp_ext::Status::Invalid,
|
|
||||||
Status::NeedsReload => lsp_ext::Status::NeedsReload,
|
|
||||||
};
|
|
||||||
self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
|
fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
|
||||||
self.register_request(&req, request_received);
|
self.register_request(&req, request_received);
|
||||||
|
|
||||||
|
@ -441,12 +428,7 @@ impl GlobalState {
|
||||||
if let Some(flycheck) = &this.flycheck {
|
if let Some(flycheck) = &this.flycheck {
|
||||||
flycheck.handle.update();
|
flycheck.handle.update();
|
||||||
}
|
}
|
||||||
let uri = params.text_document.uri.as_str();
|
this.maybe_refresh(params.text_document.uri.as_str());
|
||||||
if uri.ends_with("Cargo.toml") || uri.ends_with("Cargo.lock") {
|
|
||||||
if matches!(this.status, Status::Ready | Status::Invalid) {
|
|
||||||
this.transition(Status::NeedsReload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?
|
})?
|
||||||
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
|
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
|
||||||
|
|
|
@ -10,7 +10,8 @@ use vfs::{file_set::FileSetConfig, AbsPath};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, FilesWatcher, LinkedProject},
|
config::{Config, FilesWatcher, LinkedProject},
|
||||||
global_state::{GlobalState, Handle},
|
global_state::{GlobalState, Handle, Status},
|
||||||
|
lsp_ext,
|
||||||
main_loop::Task,
|
main_loop::Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,6 +27,32 @@ impl GlobalState {
|
||||||
self.reload_flycheck();
|
self.reload_flycheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub(crate) fn maybe_refresh(&mut self, saved_doc_url: &str) {
|
||||||
|
if !(saved_doc_url.ends_with("Cargo.toml") || saved_doc_url.ends_with("Cargo.lock")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match self.status {
|
||||||
|
Status::Loading | Status::NeedsReload => return,
|
||||||
|
Status::Ready | Status::Invalid => (),
|
||||||
|
}
|
||||||
|
if self.config.cargo_autoreload {
|
||||||
|
self.fetch_workspaces();
|
||||||
|
} else {
|
||||||
|
self.transition(Status::NeedsReload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn transition(&mut self, new_status: Status) {
|
||||||
|
self.status = new_status;
|
||||||
|
if self.config.client_caps.status_notification {
|
||||||
|
let lsp_status = match new_status {
|
||||||
|
Status::Loading => lsp_ext::Status::Loading,
|
||||||
|
Status::Ready => lsp_ext::Status::Ready,
|
||||||
|
Status::Invalid => lsp_ext::Status::Invalid,
|
||||||
|
Status::NeedsReload => lsp_ext::Status::NeedsReload,
|
||||||
|
};
|
||||||
|
self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
pub(crate) fn fetch_workspaces(&mut self) {
|
pub(crate) fn fetch_workspaces(&mut self) {
|
||||||
self.task_pool.handle.spawn({
|
self.task_pool.handle.spawn({
|
||||||
let linked_projects = self.config.linked_projects.clone();
|
let linked_projects = self.config.linked_projects.clone();
|
||||||
|
@ -53,10 +80,13 @@ impl GlobalState {
|
||||||
}
|
}
|
||||||
pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
|
pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
|
||||||
log::info!("reloading projects: {:?}", self.config.linked_projects);
|
log::info!("reloading projects: {:?}", self.config.linked_projects);
|
||||||
|
|
||||||
|
let mut has_errors = false;
|
||||||
let workspaces = workspaces
|
let workspaces = workspaces
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|res| {
|
.filter_map(|res| {
|
||||||
res.map_err(|err| {
|
res.map_err(|err| {
|
||||||
|
has_errors = true;
|
||||||
log::error!("failed to load workspace: {:#}", err);
|
log::error!("failed to load workspace: {:#}", err);
|
||||||
self.show_message(
|
self.show_message(
|
||||||
lsp_types::MessageType::Error,
|
lsp_types::MessageType::Error,
|
||||||
|
@ -67,6 +97,14 @@ impl GlobalState {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if &*self.workspaces == &workspaces {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.workspaces.is_empty() && has_errors {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let FilesWatcher::Client = self.config.files.watcher {
|
if let FilesWatcher::Client = self.config.files.watcher {
|
||||||
let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
|
let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
|
||||||
watchers: workspaces
|
watchers: workspaces
|
||||||
|
|
|
@ -237,6 +237,11 @@
|
||||||
"default": true,
|
"default": true,
|
||||||
"markdownDescription": "Whether to show `can't find Cargo.toml` error message"
|
"markdownDescription": "Whether to show `can't find Cargo.toml` error message"
|
||||||
},
|
},
|
||||||
|
"rust-analyzer.cargo.autoreload": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"markdownDescription": "Automatically refresh project info via `cargo metadata` on Cargo.toml changes"
|
||||||
|
},
|
||||||
"rust-analyzer.cargo.noDefaultFeatures": {
|
"rust-analyzer.cargo.noDefaultFeatures": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
|
|
Loading…
Reference in a new issue