Merge branch 'main' into explore-no-setup
This commit is contained in:
commit
364bb0514e
|
@ -229,6 +229,8 @@ impl Completer for VariableCompletion {
|
|||
}
|
||||
}
|
||||
|
||||
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
|
||||
|
||||
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
|
||||
|
||||
output
|
||||
|
|
|
@ -107,7 +107,7 @@ fn subcommand_completer() -> NuCompleter {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn variables_dollar_sign_with_varialblecompletion() {
|
||||
fn variables_dollar_sign_with_variablecompletion() {
|
||||
let (_, _, engine, stack) = new_engine();
|
||||
|
||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||
|
@ -907,6 +907,11 @@ fn variables_completions() {
|
|||
|
||||
// Match results
|
||||
match_suggestions(expected, suggestions);
|
||||
|
||||
let suggestions = completer.complete("$", 1);
|
||||
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
|
||||
|
||||
match_suggestions(expected, suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -56,16 +56,7 @@ impl Command for ErrorMake {
|
|||
Example {
|
||||
description: "Create a simple custom error",
|
||||
example: r#"error make {msg: "my custom error message"}"#,
|
||||
result: Some(Value::error(
|
||||
ShellError::GenericError {
|
||||
error: "my custom error message".into(),
|
||||
msg: "".into(),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
},
|
||||
Span::unknown(),
|
||||
)),
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create a more complex custom error",
|
||||
|
@ -82,16 +73,7 @@ impl Command for ErrorMake {
|
|||
}
|
||||
help: "A help string, suggesting a fix to the user" # optional
|
||||
}"#,
|
||||
result: Some(Value::error(
|
||||
ShellError::GenericError {
|
||||
error: "my custom error message".into(),
|
||||
msg: "my custom label text".into(),
|
||||
span: Some(Span::new(123, 456)),
|
||||
help: Some("A help string, suggesting a fix to the user".into()),
|
||||
inner: vec![],
|
||||
},
|
||||
Span::unknown(),
|
||||
)),
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
mod plugin;
|
||||
mod register;
|
||||
|
||||
pub use plugin::*;
|
||||
pub use register::Register;
|
||||
|
|
|
@ -72,7 +72,7 @@ impl Command for PluginStop {
|
|||
error: format!("Failed to stop the `{}` plugin", name.item),
|
||||
msg: "couldn't find a plugin with this name".into(),
|
||||
span: Some(name.span),
|
||||
help: Some("you may need to `register` the plugin first".into()),
|
||||
help: Some("you may need to `plugin add` the plugin first".into()),
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::engine::CommandType;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Register;
|
||||
|
||||
impl Command for Register {
|
||||
fn name(&self) -> &str {
|
||||
"register"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Register a plugin."
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("register")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||
.required(
|
||||
"plugin",
|
||||
SyntaxShape::Filepath,
|
||||
"Path of executable for plugin.",
|
||||
)
|
||||
.optional(
|
||||
"signature",
|
||||
SyntaxShape::Any,
|
||||
"Block with signature description as json object.",
|
||||
)
|
||||
.named(
|
||||
"shell",
|
||||
SyntaxShape::Filepath,
|
||||
"path of shell used to run plugin (cmd, sh, python, etc)",
|
||||
Some('s'),
|
||||
)
|
||||
.category(Category::Plugin)
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
r#"
|
||||
Deprecated in favor of `plugin add` and `plugin use`.
|
||||
|
||||
This command is a parser keyword. For details, check:
|
||||
https://www.nushell.sh/book/thinking_in_nu.html
|
||||
"#
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["add"]
|
||||
}
|
||||
|
||||
fn command_type(&self) -> CommandType {
|
||||
CommandType::Keyword
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Register `nu_plugin_query` plugin from ~/.cargo/bin/ dir",
|
||||
example: r#"register ~/.cargo/bin/nu_plugin_query"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Register `nu_plugin_query` plugin from `nu -c` (writes/updates $nu.plugin-path)",
|
||||
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ pub fn add_plugin_command_context(mut engine_state: EngineState) -> EngineState
|
|||
PluginRm,
|
||||
PluginStop,
|
||||
PluginUse,
|
||||
Register,
|
||||
);
|
||||
|
||||
working_set.render()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{generate_strftime_list, parse_date_from_string};
|
||||
use chrono::{DateTime, FixedOffset, Local, NaiveTime, TimeZone, Utc};
|
||||
use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, NaiveTime, TimeZone, Utc};
|
||||
use human_date_parser::{from_human_time, ParseResult};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
@ -162,24 +162,37 @@ impl Command for SubCommand {
|
|||
};
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert any standard timestamp string to datetime",
|
||||
description: "Convert timestamp string to datetime with timezone offset",
|
||||
example: "'27.02.2021 1:55 pm +0000' | into datetime",
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
result: example_result_1(1614434100_000000000),
|
||||
},
|
||||
Example {
|
||||
description: "Convert any standard timestamp string to datetime",
|
||||
description: "Convert standard timestamp string to datetime with timezone offset",
|
||||
example: "'2021-02-27T13:55:40.2246+00:00' | into datetime",
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
result: example_result_1(1614434140_224600000),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Convert non-standard timestamp string to datetime using a custom format",
|
||||
"Convert non-standard timestamp string, with timezone offset, to datetime using a custom format",
|
||||
example: "'20210227_135540+0000' | into datetime --format '%Y%m%d_%H%M%S%z'",
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
result: example_result_1(1614434140_000000000),
|
||||
},
|
||||
Example {
|
||||
description: "Convert non-standard timestamp string, without timezone offset, to datetime with custom formatting",
|
||||
example: "'16.11.1984 8:00 am' | into datetime --format '%d.%m.%Y %H:%M %P'",
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
result: Some(Value::date(
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
NaiveDateTime::parse_from_str("16.11.1984 8:00 am", "%d.%m.%Y %H:%M %P")
|
||||
.expect("date calculation should not fail in test"),
|
||||
*Local::now().offset(),
|
||||
),
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Convert nanosecond-precision unix timestamp to a datetime with offset from UTC",
|
||||
|
@ -372,10 +385,21 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
|
||||
Ok(d) => Value::date ( d, head ),
|
||||
Err(reason) => {
|
||||
Value::error (
|
||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
||||
head,
|
||||
)
|
||||
match NaiveDateTime::parse_from_str(val, &dt.0) {
|
||||
Ok(d) => Value::date (
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
d,
|
||||
*Local::now().offset(),
|
||||
),
|
||||
head,
|
||||
),
|
||||
Err(_) => {
|
||||
Value::error (
|
||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
||||
head,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -457,7 +481,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn takes_a_date_format() {
|
||||
fn takes_a_date_format_with_timezone() {
|
||||
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
|
||||
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||
let args = Arguments {
|
||||
|
@ -473,6 +497,26 @@ mod tests {
|
|||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn takes_a_date_format_without_timezone() {
|
||||
let date_str = Value::test_string("16.11.1984 8:00 am");
|
||||
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P".to_string()));
|
||||
let args = Arguments {
|
||||
zone_options: None,
|
||||
format_options: fmt_options,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::date(
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
NaiveDateTime::parse_from_str("16.11.1984 8:00 am", "%d.%m.%Y %H:%M %P").unwrap(),
|
||||
*Local::now().offset(),
|
||||
),
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn takes_iso8601_date_format() {
|
||||
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use itertools::unfold;
|
||||
use nu_engine::{command_prelude::*, ClosureEval};
|
||||
use nu_protocol::engine::Closure;
|
||||
|
||||
|
@ -81,7 +80,8 @@ used as the next argument to the closure, otherwise generation stops.
|
|||
// A type of Option<S> is used to represent state. Invocation
|
||||
// will stop on None. Using Option<S> allows functions to output
|
||||
// one final value before stopping.
|
||||
let iter = unfold(Some(initial), move |state| {
|
||||
let mut state = Some(initial);
|
||||
let iter = std::iter::from_fn(move || {
|
||||
let arg = state.take()?;
|
||||
|
||||
let (output, next_input) = match closure.run_with_value(arg) {
|
||||
|
@ -160,7 +160,7 @@ used as the next argument to the closure, otherwise generation stops.
|
|||
// We use `state` to control when to stop, not `output`. By wrapping
|
||||
// it in a `Some`, we allow the generator to output `None` as a valid output
|
||||
// value.
|
||||
*state = next_input;
|
||||
state = next_input;
|
||||
Some(output)
|
||||
});
|
||||
|
||||
|
|
13
crates/nu-command/tests/commands/conversions/into/binary.rs
Normal file
13
crates/nu-command/tests/commands/conversions/into/binary.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn sets_stream_from_internal_command_as_binary() {
|
||||
let result = nu!("seq 1 10 | to text | into binary | describe");
|
||||
assert_eq!("binary (stream)", result.out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sets_stream_from_external_command_as_binary() {
|
||||
let result = nu!("^nu --testbin cococo | into binary | describe");
|
||||
assert_eq!("binary (stream)", result.out);
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
mod binary;
|
||||
mod int;
|
||||
|
|
|
@ -240,20 +240,6 @@ impl View for TryView {
|
|||
fn show_data(&mut self, i: usize) -> bool {
|
||||
self.table.as_mut().map_or(false, |v| v.show_data(i))
|
||||
}
|
||||
|
||||
// fn setup(&mut self, config: ViewConfig<'_>) {
|
||||
// self.border_color = nu_style_to_tui(config.explore_config.table.separator_style);
|
||||
// self.immediate = config.explore_config.try_reactive;
|
||||
|
||||
// was there any reason for all this???? I don't think so
|
||||
// let mut r = RecordView::new(vec![], vec![], config.explore_config.clone());
|
||||
// r.setup(config);
|
||||
// if let Some(view) = &mut self.table {
|
||||
// view.setup(config);
|
||||
// view.set_orientation(r.get_top_layer_orientation());
|
||||
// view.set_top_layer_orientation(r.get_top_layer_orientation());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn run_command(
|
||||
|
|
|
@ -24,6 +24,3 @@ pub use parser::{
|
|||
is_math_expression_like, parse, parse_block, parse_expression, parse_external_call,
|
||||
parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string, DURATION_UNIT_GROUPS,
|
||||
};
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
pub use parse_keywords::parse_register;
|
||||
|
|
|
@ -82,7 +82,6 @@ pub const UNALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
|
|||
b"source-env",
|
||||
b"source",
|
||||
b"where",
|
||||
b"register",
|
||||
b"plugin use",
|
||||
];
|
||||
|
||||
|
@ -3627,244 +3626,6 @@ pub fn parse_where(working_set: &mut StateWorkingSet, lite_command: &LiteCommand
|
|||
}
|
||||
}
|
||||
|
||||
/// `register` is deprecated and will be removed in 0.94. Use `plugin add` and `plugin use` instead.
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
|
||||
use nu_plugin_engine::PluginDeclaration;
|
||||
use nu_protocol::{
|
||||
engine::Stack, ErrSpan, ParseWarning, PluginIdentity, PluginRegistryItem, PluginSignature,
|
||||
RegisteredPlugin,
|
||||
};
|
||||
|
||||
let spans = &lite_command.parts;
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cwd = working_set.get_cwd();
|
||||
|
||||
// Checking that the function is used with the correct name
|
||||
// Maybe this is not necessary but it is a sanity check
|
||||
if working_set.get_span_contents(spans[0]) != b"register" {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: Wrong call name for 'register' function".into(),
|
||||
Span::concat(spans),
|
||||
));
|
||||
return garbage_pipeline(working_set, spans);
|
||||
}
|
||||
if let Some(redirection) = lite_command.redirection.as_ref() {
|
||||
working_set.error(redirecting_builtin_error("register", redirection));
|
||||
return garbage_pipeline(working_set, spans);
|
||||
}
|
||||
|
||||
// Parsing the spans and checking that they match the register signature
|
||||
// Using a parsed call makes more sense than checking for how many spans are in the call
|
||||
// Also, by creating a call, it can be checked if it matches the declaration signature
|
||||
let (call, call_span) = match working_set.find_decl(b"register") {
|
||||
None => {
|
||||
working_set.error(ParseError::UnknownState(
|
||||
"internal error: Register declaration not found".into(),
|
||||
Span::concat(spans),
|
||||
));
|
||||
return garbage_pipeline(working_set, spans);
|
||||
}
|
||||
Some(decl_id) => {
|
||||
let ParsedInternalCall { call, output } =
|
||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||
let decl = working_set.get_decl(decl_id);
|
||||
|
||||
let call_span = Span::concat(spans);
|
||||
|
||||
let starting_error_count = working_set.parse_errors.len();
|
||||
check_call(working_set, call_span, &decl.signature(), &call);
|
||||
|
||||
let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
|
||||
return garbage_pipeline(working_set, spans);
|
||||
};
|
||||
|
||||
if starting_error_count != working_set.parse_errors.len() || is_help {
|
||||
return Pipeline::from_vec(vec![Expression::new(
|
||||
working_set,
|
||||
Expr::Call(call),
|
||||
call_span,
|
||||
output,
|
||||
)]);
|
||||
}
|
||||
|
||||
(call, call_span)
|
||||
}
|
||||
};
|
||||
|
||||
// Now that the call is parsed, add the deprecation warning
|
||||
working_set
|
||||
.parse_warnings
|
||||
.push(ParseWarning::DeprecatedWarning {
|
||||
old_command: "register".into(),
|
||||
new_suggestion: "use `plugin add` and `plugin use`".into(),
|
||||
span: call.head,
|
||||
url: "https://www.nushell.sh/book/plugins.html".into(),
|
||||
});
|
||||
|
||||
// Extracting the required arguments from the call and keeping them together in a tuple
|
||||
let arguments = call
|
||||
.positional_nth(0)
|
||||
.map(|expr| {
|
||||
let val =
|
||||
eval_constant(working_set, expr).map_err(|err| err.wrap(working_set, call.head))?;
|
||||
let filename = val
|
||||
.coerce_into_string()
|
||||
.map_err(|err| err.wrap(working_set, call.head))?;
|
||||
|
||||
let Some(path) = find_in_dirs(&filename, working_set, &cwd, Some(PLUGIN_DIRS_VAR))
|
||||
else {
|
||||
return Err(ParseError::RegisteredFileNotFound(filename, expr.span));
|
||||
};
|
||||
|
||||
if path.exists() && path.is_file() {
|
||||
Ok((path, expr.span))
|
||||
} else {
|
||||
Err(ParseError::RegisteredFileNotFound(filename, expr.span))
|
||||
}
|
||||
})
|
||||
.expect("required positional has being checked");
|
||||
|
||||
// Signature is an optional value from the call and will be used to decide if
|
||||
// the plugin is called to get the signatures or to use the given signature
|
||||
let signature = call.positional_nth(1).map(|expr| {
|
||||
let signature = working_set.get_span_contents(expr.span);
|
||||
serde_json::from_slice::<PluginSignature>(signature).map_err(|e| {
|
||||
ParseError::LabeledError(
|
||||
"Signature deserialization error".into(),
|
||||
format!("unable to deserialize signature: {e}"),
|
||||
spans[0],
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
// Shell is another optional value used as base to call shell to plugins
|
||||
let shell = call.get_flag_expr("shell").map(|expr| {
|
||||
let shell_expr = working_set.get_span_contents(expr.span);
|
||||
|
||||
String::from_utf8(shell_expr.to_vec())
|
||||
.map_err(|_| ParseError::NonUtf8(expr.span))
|
||||
.and_then(|name| {
|
||||
canonicalize_with(&name, cwd)
|
||||
.map_err(|_| ParseError::RegisteredFileNotFound(name, expr.span))
|
||||
})
|
||||
.and_then(|path| {
|
||||
if path.exists() & path.is_file() {
|
||||
Ok(path)
|
||||
} else {
|
||||
Err(ParseError::RegisteredFileNotFound(
|
||||
format!("{path:?}"),
|
||||
expr.span,
|
||||
))
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let shell = match shell {
|
||||
None => None,
|
||||
Some(path) => match path {
|
||||
Ok(path) => Some(path),
|
||||
Err(err) => {
|
||||
working_set.error(err);
|
||||
return Pipeline::from_vec(vec![Expression::new(
|
||||
working_set,
|
||||
Expr::Call(call),
|
||||
call_span,
|
||||
Type::Any,
|
||||
)]);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// We need the current environment variables for `python` based plugins
|
||||
// Or we'll likely have a problem when a plugin is implemented in a virtual Python environment.
|
||||
let get_envs = || {
|
||||
let stack = Stack::new().capture();
|
||||
nu_engine::env::env_to_strings(working_set.permanent_state, &stack)
|
||||
};
|
||||
|
||||
let error = arguments.and_then(|(path, path_span)| {
|
||||
let path = path.path_buf();
|
||||
|
||||
// Create the plugin identity. This validates that the plugin name starts with `nu_plugin_`
|
||||
let identity = PluginIdentity::new(path, shell).err_span(path_span)?;
|
||||
|
||||
let plugin = nu_plugin_engine::add_plugin_to_working_set(working_set, &identity)
|
||||
.map_err(|err| err.wrap(working_set, call.head))?;
|
||||
|
||||
let signatures = signature.map_or_else(
|
||||
|| {
|
||||
// It's important that the plugin is restarted if we're going to get signatures
|
||||
//
|
||||
// The user would expect that `register` would always run the binary to get new
|
||||
// signatures, in case it was replaced with an updated binary
|
||||
plugin.reset().map_err(|err| {
|
||||
ParseError::LabeledError(
|
||||
"Failed to restart plugin to get new signatures".into(),
|
||||
err.to_string(),
|
||||
spans[0],
|
||||
)
|
||||
})?;
|
||||
|
||||
let metadata_and_signatures = plugin
|
||||
.clone()
|
||||
.get(get_envs)
|
||||
.and_then(|p| {
|
||||
let meta = p.get_metadata()?;
|
||||
let sigs = p.get_signature()?;
|
||||
Ok((meta, sigs))
|
||||
})
|
||||
.map_err(|err| {
|
||||
log::warn!("Error getting metadata and signatures: {err:?}");
|
||||
ParseError::LabeledError(
|
||||
"Error getting metadata and signatures".into(),
|
||||
err.to_string(),
|
||||
spans[0],
|
||||
)
|
||||
});
|
||||
|
||||
match metadata_and_signatures {
|
||||
Ok((meta, sigs)) => {
|
||||
// Set the metadata on the plugin
|
||||
plugin.set_metadata(Some(meta.clone()));
|
||||
// Add the loaded plugin to the delta
|
||||
working_set.update_plugin_registry(PluginRegistryItem::new(
|
||||
&identity,
|
||||
meta,
|
||||
sigs.clone(),
|
||||
));
|
||||
Ok(sigs)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
},
|
||||
|sig| sig.map(|sig| vec![sig]),
|
||||
)?;
|
||||
|
||||
for signature in signatures {
|
||||
// create plugin command declaration (need struct impl Command)
|
||||
// store declaration in working set
|
||||
let plugin_decl = PluginDeclaration::new(plugin.clone(), signature);
|
||||
|
||||
working_set.add_decl(Box::new(plugin_decl));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if let Err(err) = error {
|
||||
working_set.error(err);
|
||||
}
|
||||
|
||||
Pipeline::from_vec(vec![Expression::new(
|
||||
working_set,
|
||||
Expr::Call(call),
|
||||
call_span,
|
||||
Type::Nothing,
|
||||
)])
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
|
||||
use nu_protocol::{FromValue, PluginRegistryFile};
|
||||
|
|
|
@ -5275,15 +5275,6 @@ pub fn parse_expression(working_set: &mut StateWorkingSet, spans: &[Span]) -> Ex
|
|||
}
|
||||
b"where" => parse_where_expr(working_set, &spans[pos..]),
|
||||
#[cfg(feature = "plugin")]
|
||||
b"register" => {
|
||||
working_set.error(ParseError::BuiltinCommandInPipeline(
|
||||
"register".into(),
|
||||
spans[0],
|
||||
));
|
||||
|
||||
parse_call(working_set, &spans[pos..], spans[0])
|
||||
}
|
||||
#[cfg(feature = "plugin")]
|
||||
b"plugin" => {
|
||||
if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"use" {
|
||||
// only 'plugin use' is banned
|
||||
|
@ -5419,8 +5410,6 @@ pub fn parse_builtin_commands(
|
|||
b"export" => parse_export_in_block(working_set, lite_command),
|
||||
b"hide" => parse_hide(working_set, lite_command),
|
||||
b"where" => parse_where(working_set, lite_command),
|
||||
#[cfg(feature = "plugin")]
|
||||
b"register" => parse_register(working_set, lite_command),
|
||||
// Only "plugin use" is a keyword
|
||||
#[cfg(feature = "plugin")]
|
||||
b"plugin"
|
||||
|
|
|
@ -278,8 +278,7 @@ pub fn math_result_type(
|
|||
)
|
||||
}
|
||||
},
|
||||
Operator::Math(Math::Divide) | Operator::Math(Math::Modulo) => match (&lhs.ty, &rhs.ty)
|
||||
{
|
||||
Operator::Math(Math::Divide) => match (&lhs.ty, &rhs.ty) {
|
||||
(Type::Int, Type::Int) => (Type::Int, None),
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
|
@ -328,6 +327,55 @@ pub fn math_result_type(
|
|||
)
|
||||
}
|
||||
},
|
||||
Operator::Math(Math::Modulo) => match (&lhs.ty, &rhs.ty) {
|
||||
(Type::Int, Type::Int) => (Type::Int, None),
|
||||
(Type::Float, Type::Int) => (Type::Float, None),
|
||||
(Type::Int, Type::Float) => (Type::Float, None),
|
||||
(Type::Float, Type::Float) => (Type::Float, None),
|
||||
(Type::Number, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Int) => (Type::Number, None),
|
||||
(Type::Int, Type::Number) => (Type::Number, None),
|
||||
(Type::Number, Type::Float) => (Type::Number, None),
|
||||
(Type::Float, Type::Number) => (Type::Number, None),
|
||||
(Type::Filesize, Type::Filesize) => (Type::Filesize, None),
|
||||
(Type::Filesize, Type::Int) => (Type::Filesize, None),
|
||||
(Type::Filesize, Type::Float) => (Type::Filesize, None),
|
||||
(Type::Duration, Type::Duration) => (Type::Duration, None),
|
||||
(Type::Duration, Type::Int) => (Type::Duration, None),
|
||||
(Type::Duration, Type::Float) => (Type::Duration, None),
|
||||
|
||||
(Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.clone()), None),
|
||||
(Type::Custom(a), _) => (Type::Custom(a.clone()), None),
|
||||
|
||||
(Type::Any, _) => (Type::Any, None),
|
||||
(_, Type::Any) => (Type::Any, None),
|
||||
(Type::Int | Type::Float | Type::Filesize | Type::Duration, _) => {
|
||||
*op = Expression::garbage(working_set, op.span);
|
||||
(
|
||||
Type::Any,
|
||||
Some(ParseError::UnsupportedOperationRHS(
|
||||
"division".into(),
|
||||
op.span,
|
||||
lhs.span,
|
||||
lhs.ty.clone(),
|
||||
rhs.span,
|
||||
rhs.ty.clone(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
*op = Expression::garbage(working_set, op.span);
|
||||
(
|
||||
Type::Any,
|
||||
Some(ParseError::UnsupportedOperationLHS(
|
||||
"division".into(),
|
||||
op.span,
|
||||
lhs.span,
|
||||
lhs.ty.clone(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
},
|
||||
Operator::Math(Math::FloorDivision) => match (&lhs.ty, &rhs.ty) {
|
||||
(Type::Int, Type::Int) => (Type::Int, None),
|
||||
(Type::Float, Type::Int) => (Type::Int, None),
|
||||
|
|
|
@ -95,8 +95,8 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
|
|||
fn get_plugin_config(&self) -> Result<Option<Value>, ShellError> {
|
||||
// Fetch the configuration for a plugin
|
||||
//
|
||||
// The `plugin` must match the registered name of a plugin. For
|
||||
// `register nu_plugin_example` the plugin config lookup uses `"example"`
|
||||
// The `plugin` must match the registered name of a plugin. For `plugin add
|
||||
// nu_plugin_example` the plugin config lookup uses `"example"`
|
||||
Ok(self
|
||||
.get_config()?
|
||||
.plugins
|
||||
|
|
|
@ -340,7 +340,7 @@ where
|
|||
|
||||
/// Build a [`PluginSignature`] from the signature-related methods on [`PluginCommand`].
|
||||
///
|
||||
/// This is sent to the engine on `register`.
|
||||
/// This is sent to the engine on `plugin add`.
|
||||
///
|
||||
/// This is not a public API.
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -38,7 +38,7 @@ pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
|
|||
/// The API for a Nushell plugin
|
||||
///
|
||||
/// A plugin defines multiple commands, which are added to the engine when the user calls
|
||||
/// `register`.
|
||||
/// `plugin add`.
|
||||
///
|
||||
/// The plugin must be able to be safely shared between threads, so that multiple invocations can
|
||||
/// be run in parallel. If interior mutability is desired, consider synchronization primitives such
|
||||
|
|
|
@ -104,7 +104,7 @@ pub struct Config {
|
|||
/// Configuration for plugins.
|
||||
///
|
||||
/// Users can provide configuration for a plugin through this entry. The entry name must
|
||||
/// match the registered plugin name so `register nu_plugin_example` will be able to place
|
||||
/// match the registered plugin name so `plugin add nu_plugin_example` will be able to place
|
||||
/// its configuration under a `nu_plugin_example` column.
|
||||
pub plugins: HashMap<String, Value>,
|
||||
/// Configuration for plugin garbage collection.
|
||||
|
|
|
@ -741,8 +741,8 @@ impl EngineState {
|
|||
|
||||
/// Fetch the configuration for a plugin
|
||||
///
|
||||
/// The `plugin` must match the registered name of a plugin. For `register nu_plugin_example`
|
||||
/// the plugin name to use will be `"example"`
|
||||
/// The `plugin` must match the registered name of a plugin. For `plugin add
|
||||
/// nu_plugin_example` the plugin name to use will be `"example"`
|
||||
pub fn get_plugin_config(&self, plugin: &str) -> Option<&Value> {
|
||||
self.config.plugins.get(plugin)
|
||||
}
|
||||
|
|
|
@ -453,7 +453,10 @@ impl PipelineData {
|
|||
/// Currently this will consume an external command to completion.
|
||||
pub fn check_external_failed(self) -> Result<(Self, bool), ShellError> {
|
||||
if let PipelineData::ByteStream(stream, metadata) = self {
|
||||
// Preserve stream attributes
|
||||
let span = stream.span();
|
||||
let type_ = stream.type_();
|
||||
let known_size = stream.known_size();
|
||||
match stream.into_child() {
|
||||
Ok(mut child) => {
|
||||
// Only check children without stdout. This means that nothing
|
||||
|
@ -485,10 +488,12 @@ impl PipelineData {
|
|||
child.stderr = Some(ChildPipe::Tee(Box::new(Cursor::new(stderr))));
|
||||
}
|
||||
child.set_exit_code(code);
|
||||
let stream = ByteStream::child(child, span);
|
||||
let stream = ByteStream::child(child, span).with_type(type_);
|
||||
Ok((PipelineData::ByteStream(stream, metadata), code != 0))
|
||||
} else {
|
||||
let stream = ByteStream::child(child, span);
|
||||
let stream = ByteStream::child(child, span)
|
||||
.with_type(type_)
|
||||
.with_known_size(known_size);
|
||||
Ok((PipelineData::ByteStream(stream, metadata), false))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ pub trait RegisteredPlugin: Send + Sync {
|
|||
fn stop(&self) -> Result<(), ShellError>;
|
||||
|
||||
/// Stop the plugin and reset any state so that we don't make any assumptions about the plugin
|
||||
/// next time it launches. This is used on `register`.
|
||||
/// next time it launches. This is used on `plugin add`.
|
||||
fn reset(&self) -> Result<(), ShellError>;
|
||||
|
||||
/// Cast the pointer to an [`Any`] so that its concrete type can be retrieved.
|
||||
|
|
|
@ -7,3 +7,8 @@ def show_help_on_commands [] {
|
|||
assert ("item not found" not-in $help_result)
|
||||
}
|
||||
|
||||
#[test]
|
||||
def show_help_on_error_make [] {
|
||||
let help_result = (help error make)
|
||||
assert ("Error: nu::shell::eval_block_with_input" not-in $help_result)
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ in order to create a binary that can be registered into nushell declaration list
|
|||
|
||||
This subcommand demonstrates sending configuration from the nushell `$env.config` to a plugin.
|
||||
|
||||
To register from after building `nushell` run:
|
||||
To make use of the plugin after building `nushell` run:
|
||||
|
||||
```nushell
|
||||
register target/debug/nu_plugin_example
|
||||
plugin add target/debug/nu_plugin_example
|
||||
# or then either restart your current nushell session or run:
|
||||
plugin use target/debug/nu_plugin_example
|
||||
```
|
||||
|
||||
The configuration for the plugin lives in `$env.config.plugins.example`:
|
||||
|
|
|
@ -12,7 +12,7 @@ A nushell plugin to convert data to nushell tables.
|
|||
|
||||
# Usage
|
||||
1. compile the binary: `cargo build`
|
||||
2. register plugin(assume it's compiled in ./target/debug/):
|
||||
2. plugin add plugin(assume it's compiled in ./target/debug/):
|
||||
```
|
||||
register ./target/debug/nu_plugin_formats
|
||||
plugin add ./target/debug/nu_plugin_formats
|
||||
```
|
||||
|
|
|
@ -8,6 +8,6 @@ To install:
|
|||
> cargo install --path .
|
||||
```
|
||||
|
||||
To register (from inside Nushell):
|
||||
To add the plugin (from inside Nushell):
|
||||
```
|
||||
> register <path to installed plugin>
|
||||
> plugin add <path to installed plugin>
|
||||
|
|
|
@ -8,6 +8,6 @@ To install:
|
|||
cargo install --path .
|
||||
```
|
||||
|
||||
To register (from inside Nushell):
|
||||
To add the plugin (from inside Nushell):
|
||||
```
|
||||
register <path to installed plugin>
|
||||
> plugin add <path to installed plugin>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# decode and encode information that is read and written to stdin and stdout
|
||||
#
|
||||
# To register the plugin use:
|
||||
# register <path-to-py-file>
|
||||
# plugin add <path-to-py-file>
|
||||
#
|
||||
# Be careful with the spans. Miette will crash if a span is outside the
|
||||
# size of the contents vector. We strongly suggest using the span found in the
|
||||
|
@ -258,4 +258,4 @@ if __name__ == "__main__":
|
|||
if len(sys.argv) == 2 and sys.argv[1] == "--stdio":
|
||||
plugin()
|
||||
else:
|
||||
print("Run me from inside nushell!")
|
||||
print("Run me from inside nushell!")
|
||||
|
|
|
@ -8,6 +8,6 @@ To install:
|
|||
> cargo install --path .
|
||||
```
|
||||
|
||||
To register (from inside Nushell):
|
||||
To add the plugin (from inside Nushell):
|
||||
```
|
||||
> register <path to installed plugin>
|
||||
> plugin add <path to installed plugin>
|
||||
|
|
|
@ -24,7 +24,7 @@ RUN echo '/usr/bin/nu' >> /etc/shells \
|
|||
&& chmod +x /usr/bin/nu \
|
||||
&& chown -R nushell:nushell /home/nushell/.config/nushell \
|
||||
&& ls /usr/bin/nu_plugin* \
|
||||
| xargs -I{} su -c 'register {}' nushell \
|
||||
| xargs -I{} su -c 'plugin add {}' nushell \
|
||||
&& rm -rf /tmp/*
|
||||
|
||||
USER nushell
|
||||
|
|
|
@ -23,5 +23,4 @@
|
|||
| `coverage-local.sh` | x | x | x |
|
||||
| `install-all.ps1` | ? | x | ? |
|
||||
| `install-all.sh` | x | x | x |
|
||||
| `register-plugins.nu` | x | x | x |
|
||||
| `uninstall-all.sh` | x | x | x |
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
use std log warning
|
||||
|
||||
warning "./scripts/register-plugin.nu will be deprecated, please use the `toolkit plugin register` command instead"
|
||||
|
||||
# are we on windows or not?
|
||||
def windows? [] {
|
||||
$nu.os-info.name == windows
|
||||
}
|
||||
|
||||
# filter out files that end in .d
|
||||
def keep-plugin-executables [] {
|
||||
if (windows?) { where name ends-with '.exe' } else { where name !~ '\.d' }
|
||||
}
|
||||
|
||||
# get list of all plugin files from their installed directory
|
||||
let plugins = (ls ((which nu).path.0 | path dirname) | where name =~ nu_plugin | keep-plugin-executables)
|
||||
for plugin in $plugins {
|
||||
print -n $"registering ($plugin.name), "
|
||||
nu -c $"register '($plugin.name)'"
|
||||
print "success!"
|
||||
}
|
||||
|
||||
# print helpful message
|
||||
print "\nplugins registered, please restart nushell"
|
||||
|
||||
# Plugin Location
|
||||
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_custom_values
|
||||
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_example
|
||||
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_gstat
|
||||
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_inc
|
||||
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_python
|
||||
# https://github.com/nushell/nushell/tree/main/crates/nu_plugin_query
|
||||
# https://github.com/fdncred/nu_plugin_from_parquet
|
||||
# https://github.com/fdncred/nu_plugin_from_regex
|
||||
# https://github.com/fdncred/nu_plugin_pnet
|
||||
# https://github.com/JosephTLyons/nu_plugin_periodic_table
|
||||
# https://github.com/Euphrasiologist/nu_plugin_bio
|
||||
# https://github.com/realcundo/nu_plugin_dcm
|
||||
# https://github.com/enerdgumen/nu_plugin_dotenv
|
||||
# https://github.com/bluk/nu_plugin_from_bencode
|
||||
|
||||
# Older plugins
|
||||
# https://github.com/notryanb/nu_plugin_id3
|
||||
# https://github.com/notryanb/nu_plugin_weather
|
||||
# https://github.com/tiffany352/nu-plugins/tree/main/from_nbt
|
||||
# https://github.com/tiffany352/nu-plugins/tree/main/file_exists
|
||||
# https://github.com/potan/nu_plugin_wifiscan
|
||||
# https://github.com/autophagy/nu_plugin_from_dhall
|
||||
# https://github.com/yanganto/nu_plugin_s3
|
||||
# https://github.com/lukasreuter/nu_plugin_unity
|
||||
# https://github.com/filaretov/nu_plugin_path_temp
|
||||
# https://github.com/cdecompilador/nu_plugin_bg
|
||||
# https://github.com/aJuvan/nu_plugin_kubectl
|
||||
# https://github.com/hedonihilist/nu_plugin_df
|
||||
|
|
@ -572,39 +572,6 @@ fn unbalanced_parens2() -> TestResult {
|
|||
fail_test(r#"("("))"#, "unbalanced ( and )")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_with_string_literal() -> TestResult {
|
||||
fail_test(r#"register 'nu-plugin-math'"#, "File not found")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_with_string_constant() -> TestResult {
|
||||
let input = "\
|
||||
const file = 'nu-plugin-math'
|
||||
register $file
|
||||
";
|
||||
// should not fail with `not a constant`
|
||||
fail_test(input, "File not found")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_with_string_variable() -> TestResult {
|
||||
let input = "\
|
||||
let file = 'nu-plugin-math'
|
||||
register $file
|
||||
";
|
||||
fail_test(input, "Value is not a parse-time constant")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_with_non_string_constant() -> TestResult {
|
||||
let input = "\
|
||||
const file = 6
|
||||
register $file
|
||||
";
|
||||
fail_test(input, "expected string, found int")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_use_with_string_literal() -> TestResult {
|
||||
fail_test(
|
||||
|
|
Loading…
Reference in New Issue
Block a user