forked from extern/nushell
* Moves off of draining between filters. Instead, the sink will pull on the stream, and will drain element-wise. This moves the whole stream to being lazy. * Adds ctrl-c support and connects it into some of the key points where we pull on the stream. If a ctrl-c is detect, we immediately halt pulling on the stream and return to the prompt. * Moves away from having a SourceMap where anchor locations are stored. Now AnchorLocation is kept directly in the Tag. * To make this possible, split tag and span. Span is largely used in the parser and is copyable. Tag is now no longer copyable.
322 lines
8.3 KiB
Rust
322 lines
8.3 KiB
Rust
// TODO: Temporary redirect
|
|
pub(crate) use crate::context::CommandRegistry;
|
|
use crate::evaluate::{evaluate_baseline_expr, Scope};
|
|
use crate::parser::{hir, hir::SyntaxShape};
|
|
use crate::prelude::*;
|
|
use derive_new::new;
|
|
use indexmap::IndexMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fmt;
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub enum NamedType {
|
|
Switch,
|
|
Mandatory(SyntaxShape),
|
|
Optional(SyntaxShape),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum PositionalType {
|
|
Mandatory(String, SyntaxShape),
|
|
Optional(String, SyntaxShape),
|
|
}
|
|
|
|
impl PositionalType {
|
|
pub fn mandatory(name: &str, ty: SyntaxShape) -> PositionalType {
|
|
PositionalType::Mandatory(name.to_string(), ty)
|
|
}
|
|
|
|
pub fn mandatory_any(name: &str) -> PositionalType {
|
|
PositionalType::Mandatory(name.to_string(), SyntaxShape::Any)
|
|
}
|
|
|
|
pub fn mandatory_block(name: &str) -> PositionalType {
|
|
PositionalType::Mandatory(name.to_string(), SyntaxShape::Block)
|
|
}
|
|
|
|
pub fn optional(name: &str, ty: SyntaxShape) -> PositionalType {
|
|
PositionalType::Optional(name.to_string(), ty)
|
|
}
|
|
|
|
pub fn optional_any(name: &str) -> PositionalType {
|
|
PositionalType::Optional(name.to_string(), SyntaxShape::Any)
|
|
}
|
|
|
|
pub(crate) fn name(&self) -> &str {
|
|
match self {
|
|
PositionalType::Mandatory(s, _) => s,
|
|
PositionalType::Optional(s, _) => s,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn syntax_type(&self) -> SyntaxShape {
|
|
match *self {
|
|
PositionalType::Mandatory(_, t) => t,
|
|
PositionalType::Optional(_, t) => t,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone, new)]
|
|
pub struct Signature {
|
|
pub name: String,
|
|
#[new(default)]
|
|
pub usage: String,
|
|
#[new(default)]
|
|
pub positional: Vec<PositionalType>,
|
|
#[new(value = "None")]
|
|
pub rest_positional: Option<SyntaxShape>,
|
|
#[new(default)]
|
|
pub named: IndexMap<String, NamedType>,
|
|
#[new(value = "false")]
|
|
pub is_filter: bool,
|
|
}
|
|
|
|
impl Signature {
|
|
pub fn build(name: impl Into<String>) -> Signature {
|
|
Signature::new(name.into())
|
|
}
|
|
|
|
pub fn desc(mut self, usage: impl Into<String>) -> Signature {
|
|
self.usage = usage.into();
|
|
self
|
|
}
|
|
|
|
pub fn required(mut self, name: impl Into<String>, ty: impl Into<SyntaxShape>) -> Signature {
|
|
self.positional
|
|
.push(PositionalType::Mandatory(name.into(), ty.into()));
|
|
|
|
self
|
|
}
|
|
|
|
pub fn optional(mut self, name: impl Into<String>, ty: impl Into<SyntaxShape>) -> Signature {
|
|
self.positional
|
|
.push(PositionalType::Optional(name.into(), ty.into()));
|
|
|
|
self
|
|
}
|
|
|
|
pub fn named(mut self, name: impl Into<String>, ty: impl Into<SyntaxShape>) -> Signature {
|
|
self.named
|
|
.insert(name.into(), NamedType::Optional(ty.into()));
|
|
|
|
self
|
|
}
|
|
|
|
pub fn required_named(
|
|
mut self,
|
|
name: impl Into<String>,
|
|
ty: impl Into<SyntaxShape>,
|
|
) -> Signature {
|
|
self.named
|
|
.insert(name.into(), NamedType::Mandatory(ty.into()));
|
|
|
|
self
|
|
}
|
|
|
|
pub fn switch(mut self, name: impl Into<String>) -> Signature {
|
|
self.named.insert(name.into(), NamedType::Switch);
|
|
|
|
self
|
|
}
|
|
|
|
pub fn filter(mut self) -> Signature {
|
|
self.is_filter = true;
|
|
self
|
|
}
|
|
|
|
pub fn rest(mut self, ty: SyntaxShape) -> Signature {
|
|
self.rest_positional = Some(ty);
|
|
self
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, new, Serialize, Deserialize, Clone)]
|
|
pub struct EvaluatedArgs {
|
|
pub positional: Option<Vec<Tagged<Value>>>,
|
|
pub named: Option<IndexMap<String, Tagged<Value>>>,
|
|
}
|
|
|
|
impl EvaluatedArgs {
|
|
pub fn slice_from(&self, from: usize) -> Vec<Tagged<Value>> {
|
|
let positional = &self.positional;
|
|
|
|
match positional {
|
|
None => vec![],
|
|
Some(list) => list[from..].to_vec(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(new)]
|
|
pub struct DebugEvaluatedPositional<'a> {
|
|
positional: &'a Option<Vec<Tagged<Value>>>,
|
|
}
|
|
|
|
impl fmt::Debug for DebugEvaluatedPositional<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match &self.positional {
|
|
None => write!(f, "None"),
|
|
Some(positional) => f
|
|
.debug_list()
|
|
.entries(positional.iter().map(|p| p.debug()))
|
|
.finish(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(new)]
|
|
pub struct DebugEvaluatedNamed<'a> {
|
|
named: &'a Option<IndexMap<String, Tagged<Value>>>,
|
|
}
|
|
|
|
impl fmt::Debug for DebugEvaluatedNamed<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match &self.named {
|
|
None => write!(f, "None"),
|
|
Some(named) => f
|
|
.debug_map()
|
|
.entries(named.iter().map(|(k, v)| (k, v.debug())))
|
|
.finish(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct DebugEvaluatedArgs<'a> {
|
|
args: &'a EvaluatedArgs,
|
|
}
|
|
|
|
impl fmt::Debug for DebugEvaluatedArgs<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let mut s = f.debug_struct("Args");
|
|
|
|
s.field(
|
|
"positional",
|
|
&DebugEvaluatedPositional::new(&self.args.positional),
|
|
);
|
|
s.field("named", &DebugEvaluatedNamed::new(&self.args.named));
|
|
|
|
s.finish()
|
|
}
|
|
}
|
|
|
|
impl EvaluatedArgs {
|
|
pub fn debug(&self) -> DebugEvaluatedArgs<'_> {
|
|
DebugEvaluatedArgs { args: self }
|
|
}
|
|
|
|
pub fn nth(&self, pos: usize) -> Option<&Tagged<Value>> {
|
|
match &self.positional {
|
|
None => None,
|
|
Some(array) => array.iter().nth(pos),
|
|
}
|
|
}
|
|
|
|
pub fn expect_nth(&self, pos: usize) -> Result<&Tagged<Value>, ShellError> {
|
|
match &self.positional {
|
|
None => Err(ShellError::unimplemented("Better error: expect_nth")),
|
|
Some(array) => match array.iter().nth(pos) {
|
|
None => Err(ShellError::unimplemented("Better error: expect_nth")),
|
|
Some(item) => Ok(item),
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
match &self.positional {
|
|
None => 0,
|
|
Some(array) => array.len(),
|
|
}
|
|
}
|
|
|
|
pub fn has(&self, name: &str) -> bool {
|
|
match &self.named {
|
|
None => false,
|
|
Some(named) => named.contains_key(name),
|
|
}
|
|
}
|
|
|
|
pub fn get(&self, name: &str) -> Option<&Tagged<Value>> {
|
|
match &self.named {
|
|
None => None,
|
|
Some(named) => named.get(name),
|
|
}
|
|
}
|
|
|
|
pub fn positional_iter(&self) -> PositionalIter<'_> {
|
|
match &self.positional {
|
|
None => PositionalIter::Empty,
|
|
Some(v) => {
|
|
let iter = v.iter();
|
|
PositionalIter::Array(iter)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum PositionalIter<'a> {
|
|
Empty,
|
|
Array(std::slice::Iter<'a, Tagged<Value>>),
|
|
}
|
|
|
|
impl<'a> Iterator for PositionalIter<'a> {
|
|
type Item = &'a Tagged<Value>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
match self {
|
|
PositionalIter::Empty => None,
|
|
PositionalIter::Array(iter) => iter.next(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn evaluate_args(
|
|
call: &hir::Call,
|
|
registry: &CommandRegistry,
|
|
scope: &Scope,
|
|
source: &Text,
|
|
) -> Result<EvaluatedArgs, ShellError> {
|
|
let positional: Result<Option<Vec<_>>, _> = call
|
|
.positional()
|
|
.as_ref()
|
|
.map(|p| {
|
|
p.iter()
|
|
.map(|e| evaluate_baseline_expr(e, registry, scope, source))
|
|
.collect()
|
|
})
|
|
.transpose();
|
|
|
|
let positional = positional?;
|
|
|
|
let named: Result<Option<IndexMap<String, Tagged<Value>>>, ShellError> = call
|
|
.named()
|
|
.as_ref()
|
|
.map(|n| {
|
|
let mut results = IndexMap::new();
|
|
|
|
for (name, value) in n.named.iter() {
|
|
match value {
|
|
hir::named::NamedValue::PresentSwitch(tag) => {
|
|
results.insert(name.clone(), Value::boolean(true).tagged(tag));
|
|
}
|
|
hir::named::NamedValue::Value(expr) => {
|
|
results.insert(
|
|
name.clone(),
|
|
evaluate_baseline_expr(expr, registry, scope, source)?,
|
|
);
|
|
}
|
|
|
|
_ => {}
|
|
};
|
|
}
|
|
|
|
Ok(results)
|
|
})
|
|
.transpose();
|
|
|
|
let named = named?;
|
|
|
|
Ok(EvaluatedArgs::new(positional, named))
|
|
}
|