Remove unused help shell. Slight cleanup and improvement. (#3258)

This commit is contained in:
Andrés N. Robalino 2021-04-03 18:56:46 -05:00 committed by GitHub
parent 28e1a7915d
commit 1c941557c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 97 additions and 368 deletions

View File

@ -89,24 +89,7 @@ async fn enter(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let location_string = location.display().to_string();
let location_clone = location_string.clone();
if location_string.starts_with("help") {
let spec = location_string.split(':').collect::<Vec<&str>>();
if spec.len() == 2 {
let (_, command) = (spec[0], spec[1]);
if scope.has_command(command) {
return Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterHelpShell(
UntaggedValue::string(command).into_value(Tag::unknown()),
),
)));
}
}
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterHelpShell(UntaggedValue::nothing().into_value(Tag::unknown())),
)))
} else if location.is_dir() {
if location.is_dir() {
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::EnterShell(location_clone),
)))

View File

@ -1,9 +1,13 @@
use crate::prelude::*;
use nu_engine::command_dict;
use crate::TaggedListBuilder;
use nu_engine::documentation::generate_docs;
use nu_engine::WholeStreamCommand;
use nu_engine::{Command, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
use nu_protocol::{
NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder,
UntaggedValue, Value,
};
use nu_source::Tag;
use nu_source::{SpannedItem, Tagged};
use nu_value_ext::ValueExt;
@ -208,6 +212,62 @@ You can also learn more at https://www.nushell.sh/book/"#;
}
}
fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut spec = TaggedDictBuilder::new(tag);
spec.insert_untagged("name", UntaggedValue::string(name));
spec.insert_untagged("type", UntaggedValue::string(ty));
spec.insert_untagged(
"required",
UntaggedValue::string(if required { "yes" } else { "no" }),
);
spec.into_value()
}
pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut sig = TaggedListBuilder::new(&tag);
for arg in signature.positional.iter() {
let is_required = matches!(arg.0, PositionalType::Mandatory(_, _));
sig.push_value(for_spec(arg.0.name(), "argument", is_required, &tag));
}
if signature.rest_positional.is_some() {
let is_required = false;
sig.push_value(for_spec("rest", "argument", is_required, &tag));
}
for (name, ty) in signature.named.iter() {
match ty.0 {
NamedType::Mandatory(_, _) => sig.push_value(for_spec(name, "flag", true, &tag)),
NamedType::Optional(_, _) => sig.push_value(for_spec(name, "flag", false, &tag)),
NamedType::Switch(_) => sig.push_value(for_spec(name, "switch", false, &tag)),
}
}
sig.into_value()
}
fn command_dict(command: Command, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut cmd_dict = TaggedDictBuilder::new(&tag);
cmd_dict.insert_untagged("name", UntaggedValue::string(command.name()));
cmd_dict.insert_untagged("type", UntaggedValue::string("Command"));
cmd_dict.insert_value("signature", signature_dict(command.signature(), tag));
cmd_dict.insert_untagged("usage", UntaggedValue::string(command.usage()));
cmd_dict.into_value()
}
#[cfg(test)]
mod tests {
use super::Help;

View File

@ -1,10 +1,9 @@
use crate::prelude::*;
use crate::utils::arguments::arguments;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
@ -13,7 +12,7 @@ use num_traits::Num;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
rest: Vec<Value>,
radix: Option<Tagged<u32>>,
}
@ -29,7 +28,7 @@ impl WholeStreamCommand for SubCommand {
Signature::build("str to-int")
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
.rest(
SyntaxShape::ColumnPath,
SyntaxShape::Any,
"optionally convert text into integer by column paths",
)
}
@ -69,12 +68,11 @@ impl WholeStreamCommand for SubCommand {
}
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest, radix }, input) = args.process().await?;
let (Arguments { mut rest, radix }, input) = args.process().await?;
let (column_paths, _) = arguments(&mut rest)?;
let radix = radix.map(|r| r.item).unwrap_or(10);
let column_paths: Vec<ColumnPath> = rest;
Ok(input
.map(move |v| {
if column_paths.is_empty() {

View File

@ -1,45 +0,0 @@
use crate::TaggedListBuilder;
use nu_source::Tag;
use nu_protocol::{NamedType, PositionalType, Signature, TaggedDictBuilder, UntaggedValue, Value};
fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut spec = TaggedDictBuilder::new(tag);
spec.insert_untagged("name", UntaggedValue::string(name));
spec.insert_untagged("type", UntaggedValue::string(ty));
spec.insert_untagged(
"required",
UntaggedValue::string(if required { "yes" } else { "no" }),
);
spec.into_value()
}
pub fn signature_dict(signature: Signature, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut sig = TaggedListBuilder::new(&tag);
for arg in signature.positional.iter() {
let is_required = matches!(arg.0, PositionalType::Mandatory(_, _));
sig.push_value(for_spec(arg.0.name(), "argument", is_required, &tag));
}
if signature.rest_positional.is_some() {
let is_required = false;
sig.push_value(for_spec("rest", "argument", is_required, &tag));
}
for (name, ty) in signature.named.iter() {
match ty.0 {
NamedType::Mandatory(_, _) => sig.push_value(for_spec(name, "flag", true, &tag)),
NamedType::Optional(_, _) => sig.push_value(for_spec(name, "flag", false, &tag)),
NamedType::Switch(_) => sig.push_value(for_spec(name, "switch", false, &tag)),
}
}
sig.into_value()
}

View File

@ -1,5 +1,4 @@
pub mod base;
pub mod command;
pub mod config;
pub mod dict;
pub mod keybinding;

View File

@ -2,13 +2,12 @@ use crate::call_info::UnevaluatedCallInfo;
use crate::command_args::RawCommandArgs;
use crate::evaluation_context::EvaluationContext;
use crate::filesystem::filesystem_shell::{FilesystemShell, FilesystemShellMode};
use crate::shell::help_shell::HelpShell;
use crate::shell::value_shell::ValueShell;
use futures::StreamExt;
use log::{log_enabled, trace};
use nu_errors::ShellError;
use nu_protocol::hir::{ExternalRedirection, InternalCommand};
use nu_protocol::{CommandAction, Primitive, ReturnSuccess, UntaggedValue, Value};
use nu_protocol::{CommandAction, ReturnSuccess, UntaggedValue, Value};
use nu_source::{PrettyDebug, Span, Tag};
use nu_stream::{trace_stream, InputStream, ToInputStream};
use std::sync::Arc;
@ -127,38 +126,6 @@ pub(crate) async fn run_internal_command(
InputStream::one(tagged_contents)
}
}
CommandAction::EnterHelpShell(value) => match value {
Value {
value: UntaggedValue::Primitive(Primitive::String(cmd)),
tag,
} => {
context.shell_manager.insert_at_current(Box::new(
match HelpShell::for_command(
UntaggedValue::string(cmd).into_value(tag),
&context.scope,
) {
Ok(v) => v,
Err(err) => {
context.error(err);
return InputStream::empty();
}
},
));
InputStream::from_stream(futures::stream::iter(vec![]))
}
_ => {
context.shell_manager.insert_at_current(Box::new(
match HelpShell::index(&context.scope) {
Ok(v) => v,
Err(err) => {
context.error(err);
return InputStream::empty();
}
},
));
InputStream::from_stream(futures::stream::iter(vec![]))
}
},
CommandAction::EnterValueShell(value) => {
context
.shell_manager

View File

@ -58,6 +58,10 @@ impl Clone for FilesystemShell {
}
impl FilesystemShell {
fn is_cli(&self) -> bool {
matches!(&self.mode, FilesystemShellMode::Cli)
}
pub fn basic(mode: FilesystemShellMode) -> Result<FilesystemShell, Error> {
let path = match std::env::current_dir() {
Ok(path) => path,
@ -295,7 +299,7 @@ impl Shell for FilesystemShell {
//Loading local configs in script mode, makes scripts behave different on different
//filesystems and might therefore surprise users. That's why we only load them in cli mode.
if self.mode == FilesystemShellMode::Cli {
if self.is_cli() {
match dunce::canonicalize(self.path()) {
Err(e) => {
trace!(

View File

@ -39,7 +39,6 @@ pub use crate::filesystem::path;
pub use crate::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
pub use crate::print::maybe_print_errors;
pub use crate::runnable_context::RunnableContext;
pub use crate::shell::help_shell::{command_dict, HelpShell};
pub use crate::shell::painter::Painter;
pub use crate::shell::palette::{DefaultPalette, Palette};
pub use crate::shell::shell_manager::ShellManager;

View File

@ -1,252 +0,0 @@
use crate::command_args::EvaluatedWholeStreamCommandArgs;
use crate::evaluate::scope::Scope;
use crate::maybe_text_codec::StringOrBinary;
use crate::shell::shell_args::{CdArgs, CopyArgs, LsArgs, MkdirArgs, MvArgs, RemoveArgs};
use crate::shell::Shell;
use crate::whole_stream_command::Command;
use encoding_rs::Encoding;
use futures::stream::BoxStream;
use nu_data::command::signature_dict;
use nu_errors::ShellError;
use nu_protocol::{
Primitive, ReturnSuccess, ShellTypeName, TaggedDictBuilder, UntaggedValue, Value,
};
use nu_source::Tagged;
use nu_source::{Span, SpannedItem, Tag};
use nu_stream::OutputStream;
use nu_value_ext::ValueExt;
use std::collections::VecDeque;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub fn command_dict(command: Command, tag: impl Into<Tag>) -> Value {
let tag = tag.into();
let mut cmd_dict = TaggedDictBuilder::new(&tag);
cmd_dict.insert_untagged("name", UntaggedValue::string(command.name()));
cmd_dict.insert_untagged("type", UntaggedValue::string("Command"));
cmd_dict.insert_value("signature", signature_dict(command.signature(), tag));
cmd_dict.insert_untagged("usage", UntaggedValue::string(command.usage()));
cmd_dict.into_value()
}
#[derive(Clone, Debug)]
pub struct HelpShell {
pub(crate) path: String,
pub(crate) value: Value,
}
impl HelpShell {
pub fn index(scope: &Scope) -> Result<HelpShell, ShellError> {
let mut cmds = TaggedDictBuilder::new(Tag::unknown());
let mut specs = Vec::new();
for cmd in scope.get_command_names() {
if let Some(cmd_value) = scope.get_command(&cmd) {
let mut spec = TaggedDictBuilder::new(Tag::unknown());
let value = command_dict(cmd_value, Tag::unknown());
spec.insert_untagged("name", cmd);
spec.insert_untagged(
"description",
value
.get_data_by_key("usage".spanned_unknown())
.ok_or_else(|| {
ShellError::untagged_runtime_error(
"Internal error: expected to find usage",
)
})?
.as_string()?,
);
spec.insert_value("details", value);
specs.push(spec.into_value());
} else {
}
}
cmds.insert_untagged("help", UntaggedValue::Table(specs));
Ok(HelpShell {
path: "/help".to_string(),
value: cmds.into_value(),
})
}
pub fn for_command(cmd: Value, scope: &Scope) -> Result<HelpShell, ShellError> {
let mut sh = HelpShell::index(scope)?;
if let Value {
value: UntaggedValue::Primitive(Primitive::String(name)),
..
} = cmd
{
sh.set_path(format!("/help/{:}/details", name));
}
Ok(sh)
}
fn commands(&self) -> VecDeque<Value> {
let mut cmds = VecDeque::new();
let full_path = PathBuf::from(&self.path);
let mut viewed = self.value.clone();
let sep_string = std::path::MAIN_SEPARATOR.to_string();
let sep = OsStr::new(&sep_string);
for p in full_path.iter() {
match p {
x if x == sep => {}
step => {
let step: &str = &step.to_string_lossy().to_string();
let value = viewed.get_data_by_key(step.spanned_unknown());
if let Some(v) = value {
viewed = v.clone();
}
}
}
}
match viewed {
Value {
value: UntaggedValue::Table(l),
..
} => {
for item in l {
cmds.push_back(item.clone());
}
}
x => {
cmds.push_back(x);
}
}
cmds
}
}
impl Shell for HelpShell {
fn name(&self) -> String {
let anchor_name = self.value.anchor_name();
match anchor_name {
Some(x) => format!("{{{}}}", x),
None => format!("<{}>", self.value.type_name()),
}
}
fn homedir(&self) -> Option<PathBuf> {
#[cfg(feature = "dirs")]
{
dirs_next::home_dir()
}
#[cfg(not(feature = "dirs"))]
{
None
}
}
fn path(&self) -> String {
self.path.clone()
}
fn pwd(&self, _: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::empty())
}
fn set_path(&mut self, path: String) {
let _ = std::env::set_current_dir(&path);
self.path = path;
}
fn ls(
&self,
_args: LsArgs,
_name: Tag,
_ctrl_c: Arc<AtomicBool>,
) -> Result<OutputStream, ShellError> {
let output = self
.commands()
.into_iter()
.map(ReturnSuccess::value)
.collect::<VecDeque<_>>();
Ok(output.into())
}
fn cd(&self, args: CdArgs, _name: Tag) -> Result<OutputStream, ShellError> {
let path = match args.path {
None => "/".to_string(),
Some(v) => {
let Tagged { item: target, .. } = v;
let mut cwd = PathBuf::from(&self.path);
if target == PathBuf::from("..") {
cwd.pop();
} else {
match target.to_str() {
Some(target) => match target.chars().next() {
Some(x) if x == '/' => cwd = PathBuf::from(target),
_ => cwd.push(target),
},
None => cwd.push(target),
}
}
cwd.to_string_lossy().to_string()
}
};
let mut stream = VecDeque::new();
stream.push_back(ReturnSuccess::change_cwd(path));
Ok(stream.into())
}
fn cp(&self, _args: CopyArgs, _name: Tag, _path: &str) -> Result<OutputStream, ShellError> {
Ok(OutputStream::empty())
}
fn mv(&self, _args: MvArgs, _name: Tag, _path: &str) -> Result<OutputStream, ShellError> {
Ok(OutputStream::empty())
}
fn mkdir(&self, _args: MkdirArgs, _name: Tag, _path: &str) -> Result<OutputStream, ShellError> {
Ok(OutputStream::empty())
}
fn rm(&self, _args: RemoveArgs, _name: Tag, _path: &str) -> Result<OutputStream, ShellError> {
Ok(OutputStream::empty())
}
fn open(
&self,
_path: &Path,
_name: Span,
_with_encoding: Option<&'static Encoding>,
) -> Result<BoxStream<'static, Result<StringOrBinary, ShellError>>, ShellError> {
Err(ShellError::unimplemented(
"open on help shell is not supported",
))
}
fn save(
&mut self,
_path: &Path,
_contents: &[u8],
_name: Span,
) -> Result<OutputStream, ShellError> {
Err(ShellError::unimplemented(
"save on help shell is not supported",
))
}
fn is_interactive(&self) -> bool {
//Help shell is always interactive
true
}
}

View File

@ -11,7 +11,6 @@ use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub(crate) mod help_shell;
pub(crate) mod painter;
pub(crate) mod palette;
pub(crate) mod shell_args;

View File

@ -18,8 +18,6 @@ pub enum CommandAction {
AutoConvert(Value, String),
/// Enter a value shell, one that allows exploring inside of a Value
EnterValueShell(Value),
/// Enter the help shell, which allows exploring the help system
EnterHelpShell(Value),
/// Add plugins from path given
AddPlugins(String),
/// Unload the config specified by PathBuf if present
@ -50,7 +48,6 @@ impl PrettyDebug for CommandAction {
DbgDocBldr::typed("enter shell", DbgDocBldr::description(s))
}
CommandAction::EnterValueShell(v) => DbgDocBldr::typed("enter value shell", v.pretty()),
CommandAction::EnterHelpShell(v) => DbgDocBldr::typed("enter help shell", v.pretty()),
CommandAction::AddPlugins(..) => DbgDocBldr::description("add plugins"),
CommandAction::PreviousShell => DbgDocBldr::description("previous shell"),
CommandAction::NextShell => DbgDocBldr::description("next shell"),

View File

@ -0,0 +1,3 @@
skip_welcome_message = true
startup = ["def hello-world [] { echo 'Nu World' }"]

View File

@ -1,11 +1,28 @@
use nu_test_support::fs::AbsolutePath;
use nu_test_support::playground::{says, Playground};
use nu_test_support::{nu, pipeline};
use hamcrest2::assert_that;
use hamcrest2::prelude::*;
#[cfg(feature = "directories-support")]
#[cfg(feature = "which-support")]
mod environment;
mod pipeline;
#[should_panic]
#[test]
fn runs_configuration_startup_commands() {
Playground::setup("init_config_startup_commands_test", |dirs, nu| {
let file = AbsolutePath::new(dirs.test().join("startup.toml"));
nu.with_config(&file);
assert_that!(nu.pipeline("hello-world"), says().to_stdout("Nu World"));
});
}
#[test]
fn plugins_are_declared_with_wix() {
let actual = nu!(