Deprecate register and add plugin use (#12607)

# Description

Adds a new keyword, `plugin use`. Unlike `register`, this merely loads
the signatures from the plugin cache file. The file is configurable with
the `--plugin-config` option either to `nu` or to `plugin use` itself,
just like the other `plugin` family of commands. At the REPL, one might
do this to replace `register`:

```nushell
> plugin add ~/.cargo/bin/nu_plugin_foo
> plugin use foo
```

This will not work in a script, because `plugin use` is a keyword and
`plugin add` does not evaluate at parse time (intentionally). This means
we no longer run random binaries during parse.

The `--plugins` option has been added to allow running `nu` with certain
plugins in one step. This is used especially for the `nu_with_plugins!`
test macro, but I'd imagine is generally useful. The only weird quirk is
that it has to be a list, and we don't really do this for any of our
other CLI args at the moment.

`register` now prints a deprecation parse warning.

This should fix #11923, as we now have a complete alternative to
`register`.

# User-Facing Changes

- Add `plugin use` command
- Deprecate `register`
- Add `--plugins` option to `nu` to replace a common use of `register`

# Tests + Formatting

I think I've tested it thoroughly enough and every existing test passes.
Testing nu CLI options and alternate config files is a little hairy and
I wish there were some more generic helpers for this, so this will go on
my TODO list for refactoring.

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

- [ ] Update plugins sections of book
- [ ] Release notes
This commit is contained in:
Devyn Cairns
2024-04-23 04:37:50 -07:00
committed by GitHub
parent 5c7f7883c8
commit 1f4131532d
27 changed files with 759 additions and 172 deletions

View File

@ -78,10 +78,11 @@ pub use serializers::{json::JsonSerializer, msgpack::MsgPackSerializer};
// Used by other nu crates.
#[doc(hidden)]
pub use plugin::{
create_plugin_signature, get_signature, load_plugin_cache_item, load_plugin_file,
serve_plugin_io, EngineInterfaceManager, GetPlugin, Interface, InterfaceManager,
PersistentPlugin, PluginDeclaration, PluginExecutionCommandContext, PluginExecutionContext,
PluginInterface, PluginInterfaceManager, PluginSource, ServePluginError,
add_plugin_to_working_set, create_plugin_signature, get_signature, load_plugin_cache_item,
load_plugin_file, serve_plugin_io, EngineInterfaceManager, GetPlugin, Interface,
InterfaceManager, PersistentPlugin, PluginDeclaration, PluginExecutionCommandContext,
PluginExecutionContext, PluginInterface, PluginInterfaceManager, PluginSource,
ServePluginError,
};
#[doc(hidden)]
pub use protocol::{PluginCustomValue, PluginInput, PluginOutput};

View File

@ -748,9 +748,9 @@ impl PluginInterface {
PluginCall::CustomValueOp(val, _) => Some(val.span),
},
help: Some(format!(
"the plugin may have experienced an error. Try registering the plugin again \
"the plugin may have experienced an error. Try loading the plugin again \
with `{}`",
self.state.source.identity.register_command(),
self.state.source.identity.use_command(),
)),
inner: vec![],
})?;

View File

@ -25,7 +25,7 @@ use nu_engine::documentation::get_flags_section;
use nu_protocol::{
ast::Operator, engine::StateWorkingSet, report_error_new, CustomValue, IntoSpanned,
LabeledError, PipelineData, PluginCacheFile, PluginCacheItem, PluginCacheItemData,
PluginIdentity, PluginSignature, ShellError, Span, Spanned, Value,
PluginIdentity, PluginSignature, RegisteredPlugin, ShellError, Span, Spanned, Value,
};
use thiserror::Error;
@ -942,7 +942,7 @@ pub fn load_plugin_cache_item(
working_set: &mut StateWorkingSet,
plugin: &PluginCacheItem,
span: Option<Span>,
) -> Result<(), ShellError> {
) -> Result<Arc<PersistentPlugin>, ShellError> {
let identity =
PluginIdentity::new(plugin.filename.clone(), plugin.shell.clone()).map_err(|_| {
ShellError::GenericError {
@ -960,39 +960,54 @@ pub fn load_plugin_cache_item(
match &plugin.data {
PluginCacheItemData::Valid { commands } => {
// Find garbage collection config for the plugin
let gc_config = working_set
.get_config()
.plugin_gc
.get(identity.name())
.clone();
let plugin = add_plugin_to_working_set(working_set, &identity)?;
// Add it to / get it from the working set
let plugin = working_set.find_or_create_plugin(&identity, || {
Arc::new(PersistentPlugin::new(identity.clone(), gc_config.clone()))
});
// Downcast the plugin to `PersistentPlugin` - we generally expect this to succeed.
// The trait object only exists so that nu-protocol can contain plugins without knowing
// anything about their implementation, but we only use `PersistentPlugin` in practice.
let plugin: Arc<PersistentPlugin> =
plugin
.as_any()
.downcast()
.map_err(|_| ShellError::NushellFailed {
msg: "encountered unexpected RegisteredPlugin type".into(),
})?;
// Ensure that the plugin is reset. We're going to load new signatures, so we want to
// make sure the running plugin reflects those new signatures, and it's possible that it
// doesn't.
plugin.reset()?;
// Create the declarations from the commands
for signature in commands {
let decl = PluginDeclaration::new(plugin.clone(), signature.clone());
working_set.add_decl(Box::new(decl));
}
Ok(())
Ok(plugin)
}
PluginCacheItemData::Invalid => Err(ShellError::PluginCacheDataInvalid {
plugin_name: identity.name().to_owned(),
register_command: identity.register_command(),
span,
add_command: identity.add_command(),
}),
}
}
#[doc(hidden)]
pub fn add_plugin_to_working_set(
working_set: &mut StateWorkingSet,
identity: &PluginIdentity,
) -> Result<Arc<PersistentPlugin>, ShellError> {
// Find garbage collection config for the plugin
let gc_config = working_set
.get_config()
.plugin_gc
.get(identity.name())
.clone();
// Add it to / get it from the working set
let plugin = working_set.find_or_create_plugin(identity, || {
Arc::new(PersistentPlugin::new(identity.clone(), gc_config.clone()))
});
plugin.set_gc_config(&gc_config);
// Downcast the plugin to `PersistentPlugin` - we generally expect this to succeed.
// The trait object only exists so that nu-protocol can contain plugins without knowing
// anything about their implementation, but we only use `PersistentPlugin` in practice.
plugin
.as_any()
.downcast()
.map_err(|_| ShellError::NushellFailed {
msg: "encountered unexpected RegisteredPlugin type".into(),
})
}