mirror of
https://github.com/nushell/nushell.git
synced 2025-05-18 17:00:46 +02:00
(*third* try at posting this PR, #9104, like #9084, got polluted with unrelated commits. I'm never going to pull from the github feature branch again!) # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> Show parameter defaults in scope command signature, where they're available for display by help. per https://github.com/nushell/nushell/issues/8928. I found unexpected ramifications in one completer (NuHelpCompleter) and plugins, which both use the flag-formatting routine from builtin help. For the moment I made the minimum necessary changes to get the mainline scenario to pass tests and run. But we should circle back on what to do with plugins and help completer.. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> 1. New `parameter_default` column to `signatures` table in `$nu.scope.commands` It is populated with whatever parameters can be defaulted: currently positional args and named flags. 2. Built in help (both `help <command>` and `<command> --help` will display the defaults 3. Help completer will display defaults for flags, but not for positionals. Example: A custom command with some default parameters: ``` 〉cat ~/work/dflts.nu # sample function to show defaults in help export def main [ arg1: string # mandatory positional arg2:string=abc # optional positional --switch # no default here --named:int # named flag, no default --other:string=def # flag --hard:record<foo:int bar:string, bas:bool> # default can be compound type = {foo:22, bar:"other worlds", bas:false} ] { {arg1: $arg1, arg2: $arg2, switch: $switch, named: $named, other: $other, hard: $hard, } } 〉use ~/work/dflts.nu 〉$nu.scope.commands | where name == 'dflts' | get signatures.0.any | reject short_flag description custom_completion ╭───┬────────────────┬────────────────┬──────────────────────────────────────────┬─────────────┬───────────────────────────╮ │ # │ parameter_name │ parameter_type │ syntax_shape │ is_optional │ parameter_default │ ├───┼────────────────┼────────────────┼──────────────────────────────────────────┼─────────────┼───────────────────────────┤ │ 0 │ │ input │ any │ false │ │ │ 1 │ arg1 │ positional │ string │ false │ │ │ 2 │ arg2 │ positional │ string │ true │ abc │ │ 3 │ switch │ switch │ │ true │ │ │ 4 │ named │ named │ int │ true │ │ │ 5 │ other │ named │ string │ true │ def │ │ 6 │ hard │ named │ record<foo: int, bar: string, bas: bool> │ true │ ╭───────┬───────────────╮ │ │ │ │ │ │ │ │ foo │ 22 │ │ │ │ │ │ │ │ │ bar │ other worlds │ │ │ │ │ │ │ │ │ bas │ false │ │ │ │ │ │ │ │ ╰───────┴───────────────╯ │ │ 7 │ │ output │ any │ false │ │ ╰───┴────────────────┴────────────────┴──────────────────────────────────────────┴─────────────┴───────────────────────────╯ 〉help dflts sample function to show defaults in help Usage: > dflts {flags} <arg1> (arg2) Flags: --switch - switch -- no default here --named <Int> - named flag, typed, but no default --other <String> - flag with default (default: 'def') --hard <Record([("foo", Int), ("bar", String), ("bas", Boolean)])> - default can be compound type (default: {foo: 22, bar: 'other worlds', bas: false}) -h, --help - Display the help message for this command Parameters: arg1 <string>: mandatory positional arg2 <string>: optional positional (optional, default: 'abc') ``` Compared to (relevant bits of) help output previously: ``` Flags: -h, --help - Display the help message for this command -, --switch - no default here -, --named <int> - named flag, no default -, --other <string> - flag -, --hard <record<foo: int, bar: string, bas: bool>> - default can be compound type Signatures: <any> | dflts <string> <string> -> <any> Parameters: arg1 <string>: mandatory positional (optional) arg2 <string>: optional positional ``` # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > [x] toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
123 lines
4.5 KiB
Rust
123 lines
4.5 KiB
Rust
use nu_engine::documentation::get_flags_section;
|
|
use nu_protocol::{engine::EngineState, levenshtein_distance};
|
|
use reedline::{Completer, Suggestion};
|
|
use std::fmt::Write;
|
|
use std::sync::Arc;
|
|
|
|
pub struct NuHelpCompleter(Arc<EngineState>);
|
|
|
|
impl NuHelpCompleter {
|
|
pub fn new(engine_state: Arc<EngineState>) -> Self {
|
|
Self(engine_state)
|
|
}
|
|
|
|
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
|
let full_commands = self.0.get_signatures_with_examples(false);
|
|
|
|
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
|
let mut commands = full_commands
|
|
.iter()
|
|
.filter(|(sig, _, _, _, _)| {
|
|
sig.name.to_lowercase().contains(&line.to_lowercase())
|
|
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
|
|| sig
|
|
.search_terms
|
|
.iter()
|
|
.any(|term| term.to_lowercase().contains(&line.to_lowercase()))
|
|
|| sig
|
|
.extra_usage
|
|
.to_lowercase()
|
|
.contains(&line.to_lowercase())
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
commands.sort_by(|(a, _, _, _, _), (b, _, _, _, _)| {
|
|
let a_distance = levenshtein_distance(line, &a.name);
|
|
let b_distance = levenshtein_distance(line, &b.name);
|
|
a_distance.cmp(&b_distance)
|
|
});
|
|
|
|
commands
|
|
.into_iter()
|
|
.map(|(sig, examples, _, _, _)| {
|
|
let mut long_desc = String::new();
|
|
|
|
let usage = &sig.usage;
|
|
if !usage.is_empty() {
|
|
long_desc.push_str(usage);
|
|
long_desc.push_str("\r\n\r\n");
|
|
}
|
|
|
|
let extra_usage = &sig.extra_usage;
|
|
if !extra_usage.is_empty() {
|
|
long_desc.push_str(extra_usage);
|
|
long_desc.push_str("\r\n\r\n");
|
|
}
|
|
|
|
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
|
|
|
if !sig.named.is_empty() {
|
|
long_desc.push_str(&get_flags_section(sig, |v| {
|
|
v.into_string_parsable(", ", &self.0.config)
|
|
}))
|
|
}
|
|
|
|
if !sig.required_positional.is_empty()
|
|
|| !sig.optional_positional.is_empty()
|
|
|| sig.rest_positional.is_some()
|
|
{
|
|
long_desc.push_str("\r\nParameters:\r\n");
|
|
for positional in &sig.required_positional {
|
|
let _ = write!(long_desc, " {}: {}\r\n", positional.name, positional.desc);
|
|
}
|
|
for positional in &sig.optional_positional {
|
|
let opt_suffix = if let Some(value) = &positional.default_value {
|
|
format!(
|
|
" (optional, default: {})",
|
|
&value.into_string_parsable(", ", &self.0.config),
|
|
)
|
|
} else {
|
|
(" (optional)").to_string()
|
|
};
|
|
let _ = write!(
|
|
long_desc,
|
|
" (optional) {}: {}{}\r\n",
|
|
positional.name, positional.desc, opt_suffix
|
|
);
|
|
}
|
|
|
|
if let Some(rest_positional) = &sig.rest_positional {
|
|
let _ = write!(
|
|
long_desc,
|
|
" ...{}: {}\r\n",
|
|
rest_positional.name, rest_positional.desc
|
|
);
|
|
}
|
|
}
|
|
|
|
let extra: Vec<String> = examples
|
|
.iter()
|
|
.map(|example| example.example.replace('\n', "\r\n"))
|
|
.collect();
|
|
|
|
Suggestion {
|
|
value: sig.name.clone(),
|
|
description: Some(long_desc),
|
|
extra: Some(extra),
|
|
span: reedline::Span {
|
|
start: pos,
|
|
end: pos + line.len(),
|
|
},
|
|
append_whitespace: false,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
impl Completer for NuHelpCompleter {
|
|
fn complete(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
|
self.completion_helper(line, pos)
|
|
}
|
|
}
|