feat(watch): implement --debounce flag with duration (#16187)

- fixes #16178
- `watch --debounce-ms` deprecated

Co-authored-by: Luca Scherzer <luca.scherzer@de.clara.net>
This commit is contained in:
Luca Scherzer
2025-07-29 16:10:40 +02:00
committed by GitHub
parent 2e4900f085
commit 459f3c0c28
4 changed files with 66 additions and 15 deletions

View File

@ -1,6 +1,6 @@
# Security Policy
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
We want to fix those issues without exposing our users to unnecessary risk. Thus we want to explain our security policy.
Additional issues may be part of *safety* where the behavior of Nushell as designed and implemented can cause unintended harm or a bug causes damage without the involvement of a third party.
@ -11,7 +11,7 @@ Only if you provide a strong reasoning and the necessary resources, will we cons
## Reporting a Vulnerability
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
Either reach out to the core team on [our Discord server](https://discord.gg/NtAbbGn) to arrange a private channel or use the [GitHub vulnerability reporting form](https://github.com/nushell/nushell/security/advisories/new).
Please try to answer the following questions:
- How can we reach you for further questions?

View File

@ -6,7 +6,11 @@ use notify_debouncer_full::{
},
};
use nu_engine::{ClosureEval, command_prelude::*};
use nu_protocol::{engine::Closure, report_shell_error, shell_error::io::IoError};
use nu_protocol::{
DeprecationEntry, DeprecationType, ReportMode, engine::Closure, report_shell_error,
shell_error::io::IoError,
};
use std::{
path::PathBuf,
sync::mpsc::{RecvTimeoutError, channel},
@ -33,6 +37,16 @@ impl Command for Watch {
vec!["watcher", "reload", "filesystem"]
}
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
vec![DeprecationEntry {
ty: DeprecationType::Flag("--debounce-ms".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.107.0".into()),
expected_removal: Some("0.109.0".into()),
help: Some("`--debounce-ms` will be removed in favour of `--debounce`".into()),
}]
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("watch")
.input_output_types(vec![(Type::Nothing, Type::table())])
@ -43,7 +57,13 @@ impl Command for Watch {
.named(
"debounce-ms",
SyntaxShape::Int,
"Debounce changes for this many milliseconds (default: 100). Adjust if you find that single writes are reported as multiple events",
"Debounce changes for this many milliseconds (default: 100). Adjust if you find that single writes are reported as multiple events (deprecated)",
None,
)
.named(
"debounce",
SyntaxShape::Duration,
"Debounce changes for this duration (default: 100ms). Adjust if you find that single writes are reported as multiple events",
Some('d'),
)
.named(
@ -95,11 +115,25 @@ impl Command for Watch {
let quiet = call.has_flag(engine_state, stack, "quiet")?;
let debounce_duration_flag: Option<Spanned<i64>> =
let debounce_duration_flag_ms: Option<Spanned<i64>> =
call.get_flag(engine_state, stack, "debounce-ms")?;
let debounce_duration = match debounce_duration_flag {
Some(val) => match u64::try_from(val.item) {
Ok(val) => Duration::from_millis(val),
let debounce_duration_flag: Option<Spanned<Value>> =
call.get_flag(engine_state, stack, "debounce")?;
let debounce_duration: Duration = match (debounce_duration_flag, debounce_duration_flag_ms)
{
(None, None) => DEFAULT_WATCH_DEBOUNCE_DURATION,
(Some(l), Some(r)) => {
return Err(ShellError::IncompatibleParameters {
left_message: "Here".to_string(),
left_span: l.span,
right_message: "and here".to_string(),
right_span: r.span,
});
}
(None, Some(val)) => match u64::try_from(val.item) {
Ok(v) => Duration::from_millis(v),
Err(_) => {
return Err(ShellError::TypeMismatch {
err_message: "Debounce duration is invalid".to_string(),
@ -107,7 +141,18 @@ impl Command for Watch {
});
}
},
None => DEFAULT_WATCH_DEBOUNCE_DURATION,
(Some(v), None) => {
let Value::Duration { val, .. } = v.item else {
return Err(ShellError::TypeMismatch {
err_message: "Debounce duration must be a duration".to_string(),
span: v.item.span(),
});
};
Duration::from_nanos(u64::try_from(val).map_err(|_| ShellError::TypeMismatch {
err_message: "Debounce duration is invalid".to_string(),
span: v.item.span(),
})?)
}
};
let glob_flag: Option<Spanned<String>> = call.get_flag(engine_state, stack, "glob")?;
@ -294,6 +339,11 @@ impl Command for Watch {
example: r#"watch /foo/bar { |op, path| $"($op) - ($path)(char nl)" | save --append changes_in_bar.log }"#,
result: None,
},
Example {
description: "Print file changes with a debounce time of 5 minutes",
example: r#"watch /foo/bar --debounce 5min { |op, path| $"Registered ($op) on ($path)" | print }"#,
result: None,
},
Example {
description: "Note: if you are looking to run a command every N units of time, this can be accomplished with a loop and sleep",
example: r#"loop { command; sleep duration }"#,

View File

@ -89,6 +89,7 @@ pub enum Value {
internal_span: Span,
},
Duration {
/// The duration in nanoseconds.
val: i64,
/// note: spans are being refactored out of Value
/// please use .span() instead of matching this span value

View File

@ -307,13 +307,13 @@ export def "check pr" [
export def run [
--experimental-options: oneof<list<string>, string> # enable or disable experimental options
] {
let experimental_options_arg = $experimental_options
| default []
| [$in]
| flatten
| str join ","
let experimental_options_arg = $experimental_options
| default []
| [$in]
| flatten
| str join ","
| $"[($in)]"
^cargo run -- ...[
--experimental-options $experimental_options_arg
-e "$env.PROMPT_COMMAND_RIGHT = $'(ansi magenta_reverse)trying Nushell inside Cargo(ansi reset)'"