forked from extern/nushell
Add Argument::span() and Call::arguments_span() (#10983)
# Description These make it easy to make a Span that covers an entire argument and the span of all arguments in a Call. Call::arguments_span() is useful for errors where a command may accept arguments or the pipeline, but not both. Argument::span() is useful for errors where an arguments is incompatible with one or more other arguments. In particular, I wish to use this to create an error for an implementation of #9563 that either allows arguments to set limits: ```nushell limits set RLIMIT_NOFILE --soft 255 --hard 1024 ``` Or pipeline: ```nushell {name: RLIMIT_NOFILE, soft: 255} | limits set ``` But not both: ``` ❯ [{name: RLIMIT_NOFILE, soft: 255, hard: 1024}] | limits set AS --soft 5 --hard 5 Error: nu:🐚:incompatible_parameters × Incompatible parameters. ╭─[source:1:1] 1 │ [{name: RLIMIT_NOFILE, soft: 255, hard: 1024}] | limits set AS --soft 5 --hard 5 · ───────────────────────┬────────────────────── ──────────┬───────── · │ ╰── or arguments, not both · ╰── Supply either pipeline ╰──── ``` # User-Facing Changes Only nushell Command API changes
This commit is contained in:
parent
da59dfe7d2
commit
c26fca7419
@ -12,6 +12,28 @@ pub enum Argument {
|
||||
Unknown(Expression), // unknown argument used in "fall-through" signatures
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
/// The span for an argument
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Argument::Positional(e) => e.span,
|
||||
Argument::Named((named, short, expr)) => {
|
||||
let start = named.span.start;
|
||||
let end = if let Some(expr) = expr {
|
||||
expr.span.end
|
||||
} else if let Some(short) = short {
|
||||
short.span.end
|
||||
} else {
|
||||
named.span.end
|
||||
};
|
||||
|
||||
Span::new(start, end)
|
||||
}
|
||||
Argument::Unknown(e) => e.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Call {
|
||||
/// identifier of the declaration to call
|
||||
@ -36,6 +58,26 @@ impl Call {
|
||||
}
|
||||
}
|
||||
|
||||
/// The span encompassing the arguments
|
||||
///
|
||||
/// If there are no arguments the span covers where the first argument would exist
|
||||
///
|
||||
/// If there are one or more arguments the span encompasses the start of the first argument to
|
||||
/// end of the last argument
|
||||
pub fn arguments_span(&self) -> Span {
|
||||
let past = self.head.past();
|
||||
|
||||
let start = self
|
||||
.arguments
|
||||
.first()
|
||||
.map(|a| a.span())
|
||||
.unwrap_or(past)
|
||||
.start;
|
||||
let end = self.arguments.last().map(|a| a.span()).unwrap_or(past).end;
|
||||
|
||||
Span::new(start, end)
|
||||
}
|
||||
|
||||
pub fn named_iter(
|
||||
&self,
|
||||
) -> impl Iterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)> {
|
||||
@ -166,3 +208,64 @@ impl Call {
|
||||
span
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn argument_span_named() {
|
||||
let named = Spanned {
|
||||
item: "named".to_string(),
|
||||
span: Span::new(2, 3),
|
||||
};
|
||||
let short = Spanned {
|
||||
item: "short".to_string(),
|
||||
span: Span::new(5, 7),
|
||||
};
|
||||
let expr = Expression::garbage(Span::new(11, 13));
|
||||
|
||||
let arg = Argument::Named((named.clone(), None, None));
|
||||
|
||||
assert_eq!(Span::new(2, 3), arg.span());
|
||||
|
||||
let arg = Argument::Named((named.clone(), Some(short.clone()), None));
|
||||
|
||||
assert_eq!(Span::new(2, 7), arg.span());
|
||||
|
||||
let arg = Argument::Named((named.clone(), None, Some(expr.clone())));
|
||||
|
||||
assert_eq!(Span::new(2, 13), arg.span());
|
||||
|
||||
let arg = Argument::Named((named.clone(), Some(short.clone()), Some(expr.clone())));
|
||||
|
||||
assert_eq!(Span::new(2, 13), arg.span());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_span_positional() {
|
||||
let span = Span::new(2, 3);
|
||||
let expr = Expression::garbage(span);
|
||||
let arg = Argument::Positional(expr);
|
||||
|
||||
assert_eq!(span, arg.span());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_span_unknown() {
|
||||
let span = Span::new(2, 3);
|
||||
let expr = Expression::garbage(span);
|
||||
let arg = Argument::Unknown(expr);
|
||||
|
||||
assert_eq!(span, arg.span());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_arguments_span() {
|
||||
let mut call = Call::new(Span::new(0, 1));
|
||||
call.add_positional(Expression::garbage(Span::new(2, 3)));
|
||||
call.add_positional(Expression::garbage(Span::new(5, 7)));
|
||||
|
||||
assert_eq!(Span::new(2, 7), call.arguments_span());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user