diff --git a/Cargo.lock b/Cargo.lock index 1e924d92f424..29987ada283c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2311,6 +2311,7 @@ dependencies = [ "ide-ssr", "indexmap", "itertools 0.14.0", + "libc", "load-cargo", "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index d1283ca59e8c..2e89a9dc9cde 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -51,6 +51,7 @@ toml.workspace = true walkdir = "2.5.0" semver.workspace = true memchr = "2.7.5" +libc.workspace = true cargo_metadata.workspace = true process-wrap.workspace = true dhat = { version = "0.3.3", optional = true } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2371f7a65649..77574fdc0b17 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -888,6 +888,11 @@ config_data! { /// Note: The option must be specified as an array of command line arguments, with /// the first argument being the name of the command to run. check_overrideCommand | checkOnSave_overrideCommand: Option> = None, + /// Set the priority of the check command. + /// + /// On Unix, this will use `nice` to set the priority. + /// On Windows, this will use `BELOW_NORMAL_PRIORITY_CLASS`. + check_priority | checkOnSave_priority: ProcessPriority = ProcessPriority::Normal, /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. /// /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. @@ -2414,6 +2419,7 @@ impl Config { extra_env: self.extra_env(source_root).clone(), target_dir_config: self.target_dir_from_config(source_root), set_test: true, + priority: crate::flycheck::ProcessPriority::Normal, } } @@ -2432,6 +2438,10 @@ impl Config { crate::flycheck::InvocationStrategy::PerWorkspace } }, + priority: match self.check_priority(source_root) { + ProcessPriority::Normal => crate::flycheck::ProcessPriority::Normal, + ProcessPriority::Low => crate::flycheck::ProcessPriority::Low, + }, } } Some(_) | None => FlycheckConfig::CargoCommand { @@ -2472,6 +2482,10 @@ impl Config { extra_env: self.check_extra_env(source_root), target_dir_config: self.target_dir_from_config(source_root), set_test: *self.cfg_setTest(source_root), + priority: match self.check_priority(source_root) { + ProcessPriority::Normal => crate::flycheck::ProcessPriority::Normal, + ProcessPriority::Low => crate::flycheck::ProcessPriority::Low, + }, }, ansi_color_output: self.color_diagnostic_output(), }, @@ -2865,6 +2879,13 @@ enum CargoFeaturesDef { Selected(Vec), } +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[serde(rename_all = "snake_case")] +pub(crate) enum ProcessPriority { + Normal, + Low, +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] pub(crate) enum InvocationStrategy { @@ -3787,6 +3808,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "The command will be executed once with the opened project as the working directory." ], }, + "ProcessPriority" => set! { + "type": "string", + "enum": ["normal", "low"], + "enumDescriptions": [ + "Run with normal priority.", + "Run with low priority (e.g., nice on Unix)." + ], + }, "Option" => set! { "anyOf": [ { diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index 14a4a1752f41..55bcfc574923 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -36,6 +36,13 @@ pub(crate) enum InvocationStrategy { PerWorkspace, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum ProcessPriority { + Normal, + Low, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct CargoOptions { pub(crate) target_tuples: Vec, @@ -48,6 +55,7 @@ pub(crate) struct CargoOptions { pub(crate) extra_test_bin_args: Vec, pub(crate) extra_env: FxHashMap>, pub(crate) target_dir_config: TargetDirectoryConfig, + pub(crate) priority: ProcessPriority, } #[derive(Clone, Debug)] @@ -101,6 +109,7 @@ pub(crate) enum FlycheckConfig { args: Vec, extra_env: FxHashMap>, invocation_strategy: InvocationStrategy, + priority: ProcessPriority, }, } @@ -700,9 +709,16 @@ impl FlycheckActor { self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), ); cmd.args(&options.extra_args); + set_priority(&mut cmd, options.priority); Some(cmd) } - FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { + FlycheckConfig::CustomCommand { + command, + args, + extra_env, + invocation_strategy, + priority, + } => { let root = match invocation_strategy { InvocationStrategy::Once => &*self.root, InvocationStrategy::PerWorkspace => { @@ -734,6 +750,7 @@ impl FlycheckActor { } } + set_priority(&mut cmd, *priority); Some(cmd) } } @@ -745,6 +762,33 @@ impl FlycheckActor { } } +fn set_priority(cmd: &mut Command, priority: ProcessPriority) { + match priority { + ProcessPriority::Normal => (), + ProcessPriority::Low => { + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + unsafe { + cmd.pre_exec(|| { + if libc::setpriority(libc::PRIO_PROCESS, 0, 10) != 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(()) + }); + } + } + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + cmd.creation_flags( + windows_sys::Win32::System::Threading::BELOW_NORMAL_PRIORITY_CLASS, + ); + } + } + } +} + #[allow(clippy::large_enum_variant)] enum CargoCheckMessage { CompilerArtifact(cargo_metadata::Artifact), diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index 6b7ef049645c..d8df86eb978d 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md @@ -338,6 +338,16 @@ Note: The option must be specified as an array of command line arguments, with the first argument being the name of the command to run. +## rust-analyzer.check.priority {#check.priority} + +Default: `"normal"` + +Set the priority of the check command. + +On Unix, this will use `nice` to set the priority. +On Windows, this will use `BELOW_NORMAL_PRIORITY_CLASS`. + + ## rust-analyzer.check.targets {#check.targets} Default: `null` diff --git a/editors/code/package.json b/editors/code/package.json index d0410c70da67..b6969a9ab387 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1225,6 +1225,24 @@ } } }, + { + "title": "Check", + "properties": { + "rust-analyzer.check.priority": { + "markdownDescription": "Set the priority of the check command.\n\nOn Unix, this will use `nice` to set the priority.\nOn Windows, this will use `BELOW_NORMAL_PRIORITY_CLASS`.", + "default": "normal", + "type": "string", + "enum": [ + "normal", + "low" + ], + "enumDescriptions": [ + "Run with normal priority.", + "Run with low priority (e.g., nice on Unix)." + ] + } + } + }, { "title": "Check", "properties": {