REFACTOR: build help pages instead of printing them (#9253)

# Description
in order to write tests for the `std help` commands, as we currently
have in the Rust source base, we need to be able to manipulate the
output of the `std help` commands.

however, until now they've all been directly printing the help pages as
they go...

this PR tries to build the help pages and return them at the end instead
of printing them on the fly 👍

> **Note**
> this is quite a rewrite of the `help.nu` module 🤔 
> i think it might be best to either
> - look at the commits in order
> - test the changes by testing the commands in the REPL and comparing
them to their previous `std help` versions

# User-Facing Changes
```
$nothing
```

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
```
$nothing
```
This commit is contained in:
Antoine Stevan 2023-05-26 11:22:51 +02:00 committed by GitHub
parent 15406a4247
commit 6481bf272c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -98,57 +98,55 @@ def "nu-complete list-externs" [] {
$nu.scope.commands | where is_extern | select name usage | rename value description $nu.scope.commands | where is_extern | select name usage | rename value description
} }
def print-help-header [ def build-help-header [
text: string text: string
--no-newline (-n): bool --no-newline (-n): bool
] { ] {
let header = $"(ansi green)($text)(ansi reset):" let header = $"(ansi green)($text)(ansi reset):"
if $no_newline { if $no_newline {
print -n $header $header
} else { } else {
print $header $header ++ "\n"
} }
} }
def show-module [module: record] { def build-module-page [module: record] {
if not ($module.usage? | is-empty) { let usage = (if not ($module.usage? | is-empty) {[
print $module.usage $module.usage
print "" ""
} ]} else { [] })
print-help-header -n "Module" let name = [
print $" ($module.name)" $"(build-help-header -n "Module") ($module.name)"
print "" ""
]
if not ($module.commands? | is-empty) { let commands = (if not ($module.commands? | is-empty) {[
print-help-header "Exported commands" (build-help-header -n "Exported commands")
print -n " " $" (
$module.commands | each {|command|
let commands_string = ( $'($command) (char lparen)($module.name) ($command)(char rparen)'
$module.commands
| each {|command|
$"($command) (char lparen)($module.name) ($command)(char rparen)"
} }
| str join ", " | str join ', '
) )"
""
]} else { [] })
print $commands_string let aliases = (if not ($module.aliases? | is-empty) {[
print "" (build-help-header -n "Exported aliases")
} $" ($module.aliases | str join ', ')"
""
]} else { [] })
if not ($module.aliases? | is-empty) { let env_block = (if ($module.env_block? | is-empty) {[
print-help-header "Exported aliases" $"This module (ansi cyan)does not export(ansi reset) environment."
print $" ($module.aliases | str join ', ')" ]} else {[
print "" $"This module (ansi cyan)exports(ansi reset) environment."
} (view source $module.env_block)
]})
if ($module.env_block? | is-empty) { [$usage $name $commands $aliases $env_block] | flatten | str join "\n"
print $"This module (ansi cyan)does not export(ansi reset) environment."
} else {
print $"This module (ansi cyan)exports(ansi reset) environment."
print (view source $module.env_block)
}
} }
# Show help on nushell modules. # Show help on nushell modules.
@ -252,24 +250,27 @@ export def modules [
module-not-found-error (metadata $module | get span) module-not-found-error (metadata $module | get span)
} }
show-module ($found_module | get 0) build-module-page ($found_module | get 0)
" " # signal something was shown
} else { } else {
$modules $modules
} }
} }
def show-alias [alias: record] { def build-alias-page [alias: record] {
if not ($alias.usage? | is-empty) { let usage = (if not ($alias.usage? | is-empty) {[
print $alias.usage $alias.usage
print "" ""
} ]} else { [] })
print-help-header -n "Alias" let rest = [
print $" ($alias.name)" (build-help-header -n "Alias")
print "" $" ($alias.name)"
print-help-header "Expansion" ""
print $" ($alias.expansion)" (build-help-header -n "Expansion")
$" ($alias.expansion)"
]
[$usage $rest] | flatten | str join "\n"
} }
# Show help on nushell aliases. # Show help on nushell aliases.
@ -355,21 +356,24 @@ export def aliases [
alias-not-found-error (metadata $alias | get span) alias-not-found-error (metadata $alias | get span)
} }
show-alias ($found_alias | get 0) build-alias-page ($found_alias | get 0)
" " # signal something was shown
} else { } else {
$aliases $aliases
} }
} }
def show-extern [extern: record] { def build-extern-page [extern: record] {
if not ($extern.usage? | is-empty) { let usage = (if not ($extern.usage? | is-empty) {[
print $extern.usage $extern.usage
print "" ""
} ]} else { [] })
print-help-header -n "Extern" let rest = [
print $" ($extern.name)" (build-help-header -n "Extern")
$" ($extern.name)"
]
[$usage $rest] | flatten | str join "\n"
} }
# Show help on nushell externs. # Show help on nushell externs.
@ -394,23 +398,24 @@ export def externs [
extern-not-found-error (metadata $extern | get span) extern-not-found-error (metadata $extern | get span)
} }
show-extern ($found_extern | get 0) build-extern-page ($found_extern | get 0)
" " # signal something was shown
} else { } else {
$externs $externs
} }
} }
def show-operator [operator: record] { def build-operator-page [operator: record] {
print-help-header "Description" [
print $" ($operator.description)" (build-help-header -n "Description")
print "" $" ($operator.description)"
print-help-header -n "Operator" ""
print ($" ($operator.name) (char lparen)(ansi cyan_bold)($operator.operator)(ansi reset)(char rparen)") (build-help-header -n "Operator")
print-help-header -n "Type" $" ($operator.name) (char lparen)(ansi cyan_bold)($operator.operator)(ansi reset)(char rparen)"
print $" ($operator.type)" (build-help-header -n "Type")
print-help-header -n "Precedence" $" ($operator.type)"
print $" ($operator.precedence)" (build-help-header -n "Precedence")
$" ($operator.precedence)"
] | str join "\n"
} }
# Show help on nushell operators. # Show help on nushell operators.
@ -459,191 +464,212 @@ export def operators [
operator-not-found-error (metadata $operator | get span) operator-not-found-error (metadata $operator | get span)
} }
show-operator ($found_operator | get 0) build-operator-page ($found_operator | get 0)
" " # signal something was shown
} else { } else {
$operators $operators
} }
} }
def show-command [command: record] { def build-command-page [command: record] {
if not ($command.usage? | is-empty) { let usage = (if not ($command.usage? | is-empty) {[
print $command.usage $command.usage
} ]} else { [] })
if not ($command.extra_usage? | is-empty) { let extra_usage = (if not ($command.extra_usage? | is-empty) {[
print "" ""
print $command.extra_usage $command.extra_usage
} ]} else { [] })
if not ($command.search_terms? | is-empty) { let search_terms = (if not ($command.search_terms? | is-empty) {[
print "" ""
print-help-header -n "Search terms" $"(build-help-header -n 'Search terms') ($command.search_terms)"
print $" ($command.search_terms)" ]} else { [] })
}
if not ($command.module_name? | is-empty) { let module = (if not ($command.module_name? | is-empty) {[
print "" ""
print-help-header -n "Module" $"(build-help-header -n 'Module') ($command.module_name)"
print $" ($command.module_name)" ]} else { [] })
}
if not ($command.category? | is-empty) { let category = (if not ($command.category? | is-empty) {[
print "" ""
print-help-header -n "Category" $"(build-help-header -n 'Category') ($command.category)"
print $" ($command.category)" ]} else { [] })
}
print "" let this = ([
print "This command:" ""
if ($command.creates_scope) { "This command:"
print $"- (ansi cyan)does create(ansi reset) a scope." ] | append (
} else { if ($command.creates_scope) {
print $"- (ansi cyan)does not create(ansi reset) a scope." $"- (ansi cyan)does create(ansi reset) a scope."
} } else {
if ($command.is_builtin) { $"- (ansi cyan)does not create(ansi reset) a scope."
print $"- (ansi cyan)is(ansi reset) a built-in command." }
} else { ) | append (
print $"- (ansi cyan)is not(ansi reset) a built-in command." if ($command.is_builtin) {
} $"- (ansi cyan)is(ansi reset) a built-in command."
if ($command.is_sub) { } else {
print $"- (ansi cyan)is(ansi reset) a subcommand." $"- (ansi cyan)is not(ansi reset) a built-in command."
} else { }
print $"- (ansi cyan)is not(ansi reset) a subcommand." ) | append (
} if ($command.is_sub) {
if ($command.is_plugin) { $"- (ansi cyan)is(ansi reset) a subcommand."
print $"- (ansi cyan)is part(ansi reset) of a plugin." } else {
} else { $"- (ansi cyan)is not(ansi reset) a subcommand."
print $"- (ansi cyan)is not part(ansi reset) of a plugin." }
} ) | append (
if ($command.is_custom) { if ($command.is_plugin) {
print $"- (ansi cyan)is(ansi reset) a custom command." $"- (ansi cyan)is part(ansi reset) of a plugin."
} else { } else {
print $"- (ansi cyan)is not(ansi reset) a custom command." $"- (ansi cyan)is not part(ansi reset) of a plugin."
} }
if ($command.is_keyword) { ) | append (
print $"- (ansi cyan)is(ansi reset) a keyword." if ($command.is_custom) {
} else { $"- (ansi cyan)is(ansi reset) a custom command."
print $"- (ansi cyan)is not(ansi reset) a keyword." } else {
} $"- (ansi cyan)is not(ansi reset) a custom command."
}
) | append (
if ($command.is_keyword) {
$"- (ansi cyan)is(ansi reset) a keyword."
} else {
$"- (ansi cyan)is not(ansi reset) a keyword."
}
))
let signatures = ($command.signatures | transpose | get column1) let signatures = ($command.signatures | transpose | get column1)
if not ($signatures | is-empty) { let cli_usage = (if not ($signatures | is-empty) {
let parameters = ($signatures | get 0 | where parameter_type != input and parameter_type != output) let parameters = ($signatures | get 0 | where parameter_type != input and parameter_type != output)
let positionals = ($parameters | where parameter_type == positional and parameter_type != rest) let positionals = ($parameters | where parameter_type == positional and parameter_type != rest)
let flags = ($parameters | where parameter_type != positional and parameter_type != rest) let flags = ($parameters | where parameter_type != positional and parameter_type != rest)
print "" [
print-help-header "Usage" ""
print -n " > " (build-help-header -n "Usage")
print -n $"($command.name) " ([
if not ($flags | is-empty) { $" > ($command.name) "
print -n $"{flags} " (if not ($flags | is-empty) { "{flags} " } else "")
} ($positionals | each {|param|
for param in $positionals { $"<($param.parameter_name)> "
print -n $"<($param.parameter_name)> " })
} ] | flatten | str join "")
print "" ""
} ]
} else { [] })
let subcommands = ($nu.scope.commands | where name =~ $"^($command.name) " | select name usage) let subcommands = ($nu.scope.commands | where name =~ $"^($command.name) " | select name usage)
if not ($subcommands | is-empty) { let subcommands = (if not ($subcommands | is-empty) {[
print "" (build-help-header "Subcommands")
print-help-header "Subcommands" ($subcommands | each {|subcommand |
for subcommand in $subcommands { $" (ansi teal)($subcommand.name)(ansi reset) - ($subcommand.usage)"
print $" (ansi teal)($subcommand.name)(ansi reset) - ($subcommand.usage)" } | str join "\n")
} ]} else { [] })
}
if not ($signatures | is-empty) { let rest = (if not ($signatures | is-empty) {
let parameters = ($signatures | get 0 | where parameter_type != input and parameter_type != output) let parameters = ($signatures | get 0 | where parameter_type != input and parameter_type != output)
let positionals = ($parameters | where parameter_type == positional and parameter_type != rest) let positionals = ($parameters | where parameter_type == positional and parameter_type != rest)
let flags = ($parameters | where parameter_type != positional and parameter_type != rest) let flags = ($parameters | where parameter_type != positional and parameter_type != rest)
let is_rest = (not ($parameters | where parameter_type == rest | is-empty)) let is_rest = (not ($parameters | where parameter_type == rest | is-empty))
print "" ([
print-help-header "Flags" ""
for flag in $flags { (build-help-header "Flags")
let flag_parts = [ " ", ($flags | each {|flag|
(if ($flag.short_flag | is-empty) { "" } else { [
$"-(ansi teal)($flag.short_flag)(ansi reset), " " ",
}), (if ($flag.short_flag | is-empty) { "" } else {
(if ($flag.parameter_name | is-empty) { "" } else { $"-(ansi teal)($flag.short_flag)(ansi reset), "
$"--(ansi teal)($flag.parameter_name)(ansi reset)"
}),
(if ($flag.syntax_shape | is-empty) { "" } else {
$": <(ansi light_blue)($flag.syntax_shape)(ansi reset)>"
}),
(if ($flag.description | is-empty) { "" } else {
$" - ($flag.description)"
}),
(if ($flag.parameter_default | is-empty) { "" } else {
$" \(default: ($flag.parameter_default)\)"
}),
]
print ($flag_parts | str join "")
}
print $" (ansi teal)-h(ansi reset), --(ansi teal)help(ansi reset) - Display the help message for this command"
print ""
print-help-header "Signatures"
for signature in $signatures {
let input = ($signature | where parameter_type == input | get 0)
let output = ($signature | where parameter_type == output | get 0)
print -n $" <($input.syntax_shape)> | ($command.name)"
for positional in $positionals {
print -n $" <($positional.syntax_shape)>"
}
print $" -> <($output.syntax_shape)>"
}
if (not ($positionals | is-empty)) or $is_rest {
print ""
print-help-header "Parameters"
for positional in $positionals {
let arg_parts = [ " ",
$"(ansi teal)($positional.parameter_name)(ansi reset)",
(if ($positional.syntax_shape | is-empty) { "" } else {
$": <(ansi light_blue)($positional.syntax_shape)(ansi reset)>"
}), }),
(if ($positional.description | is-empty) { "" } else { (if ($flag.parameter_name | is-empty) { "" } else {
$" ($positional.description)" $"--(ansi teal)($flag.parameter_name)(ansi reset)"
}), }),
(if ($positional.parameter_default | is-empty) { "" } else { (if ($flag.syntax_shape | is-empty) { "" } else {
$" \(optional, default: ($positional.parameter_default)\)" $": <(ansi light_blue)($flag.syntax_shape)(ansi reset)>"
}),
(if ($flag.description | is-empty) { "" } else {
$" - ($flag.description)"
}),
(if ($flag.parameter_default | is-empty) { "" } else {
$" \(default: ($flag.parameter_default)\)"
}),
] | str join ""
} | str join "\n")
$" (ansi teal)-h(ansi reset), --(ansi teal)help(ansi reset) - Display the help message for this command"
""
(build-help-header "Signatures")
($signatures | each {|signature|
let input = ($signature | where parameter_type == input | get 0)
let output = ($signature | where parameter_type == output | get 0)
([
$" <($input.syntax_shape)> | ($command.name)"
($positionals | each {|positional|
$" <($positional.syntax_shape)>"
}) })
] $" -> <($output.syntax_shape)>"
print ($arg_parts | str join "") ] | str join "")
} } | str join "\n")
if $is_rest { (if (not ($positionals | is-empty)) or $is_rest {[
let rest = ($parameters | where parameter_type == rest | get 0) ""
print $" ...(ansi teal)rest(ansi reset): <(ansi light_blue)($rest.syntax_shape)(ansi reset)> ($rest.description)" (build-help-header "Parameters")
} ($positionals | each {|positional|
} ([
} " ",
$"(ansi teal)($positional.parameter_name)(ansi reset)",
(if ($positional.syntax_shape | is-empty) { "" } else {
$": <(ansi light_blue)($positional.syntax_shape)(ansi reset)>"
}),
(if ($positional.description | is-empty) { "" } else {
$" ($positional.description)"
}),
(if ($positional.parameter_default | is-empty) { "" } else {
$" \(optional, default: ($positional.parameter_default)\)"
})
] | str join "")
} | str join "\n")
if not ($command.examples | is-empty) { (if $is_rest {
print "" let rest = ($parameters | where parameter_type == rest | get 0)
print-help-header -n "Examples" $" ...(ansi teal)rest(ansi reset): <(ansi light_blue)($rest.syntax_shape)(ansi reset)> ($rest.description)"
for example in $command.examples { })
print "" ]} else { [] })
print $" ($example.description)" ] | flatten)
print $" > ($example.example | nu-highlight)" } else { [] })
if not ($example.result | is-empty) {
for line in ( let examples = (if not ($command.examples | is-empty) {[
$example.result | table | if ($example.result | describe) == "binary" { str join } else { lines } ""
) { (build-help-header -n "Examples")
print $" ($line)" ($command.examples | each {|example| [
$" ($example.description)"
$" > ($example.example | nu-highlight)"
(if not ($example.result | is-empty) {
$example.result
| table
| if ($example.result | describe) == "binary" { str join } else { lines }
| each {|line|
$" ($line)"
} }
} | str join "\n"
} })
} ""
] | str join "\n"})
] | flatten} else { [] })
print "" [
$usage
$extra_usage
$search_terms
$module
$category
$this
$cli_usage
$subcommands
$rest
$examples
] | flatten | str join "\n"
} }
# Show help on commands. # Show help on commands.
@ -669,8 +695,7 @@ export def commands [
} }
} }
show-command ($found_command | get 0) build-command-page ($found_command | get 0)
" " # signal something was shown
} else { } else {
$commands | select name category usage signatures search_terms $commands | select name category usage signatures search_terms
} }