-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(complete): Add dynamic completion for nushell #5841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| //! Implements dynamic completion for Nushell. | ||
| //! | ||
| //! There is no direct equivalent of other shells' `source $(COMPLETE=... your-clap-bin)` in nushell, | ||
| //! because code being sourced must exist at parse-time. | ||
| //! | ||
| //! One way to get close to that is to split the completion integration into two parts: | ||
| //! 1. a minimal part that goes into `env.nu`, which updates the actual completion integration | ||
| //! 2. the completion integration, which is placed into the user's autoload directory | ||
| //! | ||
| //! To install the completion integration, the user runs | ||
| //! ```nu | ||
| //! COMPLETE=nushell your-clap-bin | save --raw --force --append $nu.env-path | ||
| //! ``` | ||
| // Std | ||
| use std::ffi::{OsStr, OsString}; | ||
| use std::fmt::Display; | ||
| use std::io::{Error, Write}; | ||
| use std::path::Path; | ||
|
|
||
| // External | ||
| use clap::Command; | ||
| use clap_complete::env::EnvCompleter; | ||
|
|
||
| /// Generate integration for dynamic completion in Nushell | ||
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason you have all of these traits implemented? |
||
| pub struct Nushell; | ||
|
|
||
| impl EnvCompleter for Nushell { | ||
| fn name(&self) -> &'static str { | ||
| "nushell" | ||
| } | ||
|
|
||
| fn is(&self, name: &str) -> bool { | ||
| name == "nushell" | ||
| } | ||
|
|
||
| fn write_registration( | ||
| &self, | ||
| var: &str, | ||
| name: &str, | ||
| bin: &str, | ||
| completer: &str, | ||
| buf: &mut dyn Write, | ||
| ) -> Result<(), Error> { | ||
| let mode_var = ModeVar(var).to_string(); | ||
| if std::env::var_os(&mode_var).as_ref().map(|x| x.as_os_str()) | ||
| == Some(OsStr::new("integration")) | ||
| { | ||
| write_completion_script(var, name, bin, completer, buf) | ||
| } else { | ||
| write_refresh_completion_integration(var, name, completer, buf) | ||
| } | ||
| } | ||
|
|
||
| fn write_complete( | ||
| &self, | ||
| cmd: &mut Command, | ||
| args: Vec<OsString>, | ||
| current_dir: Option<&Path>, | ||
| buf: &mut dyn Write, | ||
| ) -> Result<(), Error> { | ||
| let idx = args.len().saturating_sub(1).max(0); | ||
| let candidates = clap_complete::engine::complete(cmd, args, idx, current_dir)?; | ||
| let mut strbuf = String::new(); | ||
| { | ||
| let mut records = write_json::array(&mut strbuf); | ||
| for candidate in candidates { | ||
| let mut record = records.object(); | ||
| record.string("value", candidate.get_value().to_string_lossy().as_ref()); | ||
| if let Some(help) = candidate.get_help() { | ||
| record.string("description", &help.to_string()[..]); | ||
| } | ||
| } | ||
| } | ||
| write!(buf, "{strbuf}") | ||
| } | ||
| } | ||
|
|
||
| struct ModeVar<'a>(&'a str); | ||
|
|
||
| impl<'a> Display for ModeVar<'a> { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "_{0}__mode", self.0) | ||
| } | ||
| } | ||
|
|
||
| fn write_refresh_completion_integration( | ||
| var: &str, | ||
| name: &str, | ||
| completer: &str, | ||
| buf: &mut dyn Write, | ||
| ) -> Result<(), Error> { | ||
| let mode = ModeVar(var); | ||
| writeln!( | ||
| buf, | ||
| r#" | ||
| # Refresh completer integration for {name} (must be in env.nu) | ||
| do {{ | ||
| # Search for existing script to avoid duplicates in case autoload dirs change | ||
| let completer_script_name = '{name}-completer.nu' | ||
| let autoload_dir = $nu.user-autoload-dirs | ||
| | where {{ path join $completer_script_name | path exists }} | ||
| | get 0 --optional | ||
| | default ($nu.user-autoload-dirs | get 0 --optional) | ||
| mkdir $autoload_dir | ||
| let completer_path = ($autoload_dir | path join $completer_script_name) | ||
| {var}=nushell {mode}=integration ^r#'{completer}'# | save --raw --force $completer_path | ||
| }} | ||
| "# | ||
| ) | ||
| } | ||
|
|
||
| fn write_completion_script( | ||
| var: &str, | ||
| name: &str, | ||
| _bin: &str, | ||
| completer: &str, | ||
| buf: &mut dyn Write, | ||
| ) -> Result<(), Error> { | ||
| writeln!( | ||
| buf, | ||
| r#" | ||
| # Performs the completion for {name} | ||
| def {name}-completer [ | ||
| spans: list<string> # The spans that were passed to the external completer closure | ||
| ]: nothing -> list {{ | ||
| {var}=nushell ^r#'{completer}'# -- ...$spans | from json | ||
| }} | ||
| @complete {name}-completer | ||
| def --wrapped {name} [...args] {{ | ||
| ^r#'{completer}'# ...$args | ||
| }} | ||
| "# | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,8 +28,12 @@ use clap::{builder::PossibleValue, Arg, ArgAction, Command}; | |
| use clap_complete::Generator; | ||
|
|
||
| /// Generate Nushell complete file | ||
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this still needed? |
||
| pub struct Nushell; | ||
|
|
||
| #[cfg(feature = "unstable-dynamic")] | ||
| pub mod dynamic; | ||
|
|
||
| impl Generator for Nushell { | ||
| fn file_name(&self, name: &str) -> String { | ||
| format!("{name}.nu") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't comment the groupings