Add 'overlay new' command (#5647)

* Add 'overlay new' command

* Add missing file
This commit is contained in:
Jakub Žádník 2022-05-26 17:47:04 +03:00 committed by GitHub
parent 0594f9e7aa
commit 2042f7f769
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 181 additions and 0 deletions

View File

@ -1,9 +1,11 @@
mod add; mod add;
mod command; mod command;
mod list; mod list;
mod new;
mod remove; mod remove;
pub use add::OverlayAdd; pub use add::OverlayAdd;
pub use command::Overlay; pub use command::Overlay;
pub use list::OverlayList; pub use list::OverlayList;
pub use new::OverlayNew;
pub use remove::OverlayRemove; pub use remove::OverlayRemove;

View File

@ -0,0 +1,74 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape};
#[derive(Clone)]
pub struct OverlayNew;
impl Command for OverlayNew {
fn name(&self) -> &str {
"overlay new"
}
fn usage(&self) -> &str {
"Create an empty overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay new")
.required("name", SyntaxShape::String, "Name of the overlay")
// TODO:
// .switch(
// "prefix",
// "Prepend module name to the imported symbols",
// Some('p'),
// )
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"The command will first create an empty module, then add it as an overlay.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let name_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
stack.add_overlay(name_arg.item);
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Create an empty overlay",
example: r#"overlay new spam"#,
result: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(OverlayNew {})
}
}

View File

@ -55,6 +55,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
Overlay, Overlay,
OverlayAdd, OverlayAdd,
OverlayList, OverlayList,
OverlayNew,
OverlayRemove, OverlayRemove,
Let, Let,
Metadata, Metadata,

View File

@ -1745,6 +1745,9 @@ pub fn parse_overlay(
None, None,
); );
} }
b"new" => {
return parse_overlay_new(working_set, spans, expand_aliases_denylist);
}
b"remove" => { b"remove" => {
return parse_overlay_remove(working_set, spans, expand_aliases_denylist); return parse_overlay_remove(working_set, spans, expand_aliases_denylist);
} }
@ -1802,6 +1805,96 @@ pub fn parse_overlay(
) )
} }
pub fn parse_overlay_new(
working_set: &mut StateWorkingSet,
spans: &[Span],
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay new" {
return (
garbage_pipeline(spans),
Some(ParseError::UnknownState(
"internal error: Wrong call name for 'overlay new' command".into(),
span(spans),
)),
);
}
let (call, call_span) = match working_set.find_decl(b"overlay new") {
Some(decl_id) => {
let (call, mut err) = parse_internal_call(
working_set,
span(&spans[0..2]),
&spans[2..],
decl_id,
expand_aliases_denylist,
);
let decl = working_set.get_decl(decl_id);
let call_span = span(spans);
err = check_call(call_span, &decl.signature(), &call).or(err);
if err.is_some() || call.has_flag("help") {
return (
Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call),
span: call_span,
ty: Type::Any,
custom_completion: None,
}]),
err,
);
}
(call, call_span)
}
None => {
return (
garbage_pipeline(spans),
Some(ParseError::UnknownState(
"internal error: 'overlay new' declaration not found".into(),
span(spans),
)),
)
}
};
let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) {
if let Some(s) = expr.as_string() {
(s, expr.span)
} else {
return (
garbage_pipeline(spans),
Some(ParseError::UnknownState(
"internal error: Module name not a string".into(),
expr.span,
)),
);
}
} else {
return (
garbage_pipeline(spans),
Some(ParseError::UnknownState(
"internal error: Missing required positional after call parsing".into(),
call_span,
)),
);
};
let pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call),
span: span(spans),
ty: Type::Any,
custom_completion: None,
}]);
let module_id = working_set.add_module(&overlay_name, Module::new());
working_set.add_overlay(overlay_name.as_bytes().to_vec(), module_id, vec![], vec![]);
(pipeline, None)
}
pub fn parse_overlay_add( pub fn parse_overlay_add(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
spans: &[Span], spans: &[Span],

View File

@ -496,3 +496,14 @@ fn reset_overrides() {
assert_eq!(actual.out, "foo"); assert_eq!(actual.out, "foo");
assert_eq!(actual_repl.out, "foo"); assert_eq!(actual_repl.out, "foo");
} }
#[test]
fn overlay_new() {
let inp = &[r#"overlay new spam"#, r#"overlay list | last"#];
let actual = nu!(cwd: "tests/overlays", pipeline(&inp.join("; ")));
let actual_repl = nu_repl("tests/overlays", inp);
assert_eq!(actual.out, "spam");
assert_eq!(actual_repl.out, "spam");
}