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
|
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)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
/// identifier of the declaration to 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(
|
pub fn named_iter(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)> {
|
) -> impl Iterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)> {
|
||||||
@ -166,3 +208,64 @@ impl Call {
|
|||||||
span
|
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