mirror of
https://github.com/nushell/nushell.git
synced 2025-02-01 19:19:24 +01:00
Change help commands
to use name from scope instead of the name from the declaration (#14490)
# Description Before this PR, `help commands` uses the name from a command's declaration rather than the name in the scope. This is problematic when trying to view the help page for the `main` command of a module. For example, `std bench`: ```nushell use std/bench help bench # => Error: nu::parser::not_found # => # => × Not found. # => ╭─[entry #10:1:6] # => 1 │ help bench # => · ──┬── # => · ╰── did not find anything under this name # => ╰──── ``` This can also cause confusion when importing specific commands from modules. Furthermore, if there are multiple commands with the same name from different modules, the help text for _both_ will appear when querying their help text (this is especially problematic for `main` commands, see #14033): ```nushell use std/iter help iter find # => Error: nu::parser::not_found # => # => × Not found. # => ╭─[entry #3:1:6] # => 1│ help iter find # => · ────┬──── # => · ╰── did not find anything under this name # => ╰──── help find # => Searches terms in the input. # => # => Search terms: filter, regex, search, condition # => # => Usage: # => > find {flags} ...(rest) # [...] # => Returns the first element of the list that matches the # => closure predicate, `null` otherwise # [...] # (full text omitted for brevity) ``` This PR changes `help commands` to use the name as it is in scope, so prefixing any command in scope with `help` will show the correct help text. ```nushell use std/bench help bench # [help text for std bench] use std/iter help iter find # [help text for std iter find] use std help std bench # [help text for std bench] help std iter find # [help text for std iter find] ``` Additionally, the IR code generation for commands called with the `--help` text has been updated to reflect this change. This does have one side effect: when a module has a `main` command defined, running `help <name>` (which checks `help aliases`, then `help commands`, then `help modules`) will show the help text for the `main` command rather than the module. The help text for the module is still accessible with `help modules <name>`. Fixes #10499, #10311, #11609, #13470, #14033, and #14402. Partially fixes #10707. Does **not** fix #11447. # User-Facing Changes * Help text for commands can be obtained by running `help <command name>`, where the command name is the same thing you would type in order to execute the command. Previously, it was the name of the function as written in the source file. * For example, for the following module `spam` with command `meow`: ```nushell module spam { # help text export def meow [] {} } ``` * Before this PR: * Regardless of how `meow` is `use`d, the help text is viewable by running `help meow`. * After this PR: * When imported with `use spam`: The `meow` command is executed by running `spam meow` and the `help` text is viewable by running `help spam meow`. * When imported with `use spam foo`: The `meow` command is executed by running `meow` and the `help` text is viewable by running `meow`. * When a module has a `main` command defined, `help <module name>` will return help for the main command, rather than the module. To access the help for the module, use `help modules <module name>`. # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` # After Submitting N/A
This commit is contained in:
parent
7d2e8875e0
commit
8f9aa1a250
@ -87,21 +87,10 @@ pub fn help_commands(
|
||||
name.push_str(&r.item);
|
||||
}
|
||||
|
||||
let output = engine_state
|
||||
.get_decls_sorted(false)
|
||||
.into_iter()
|
||||
.filter_map(|(_, decl_id)| {
|
||||
let decl = engine_state.get_decl(decl_id);
|
||||
(decl.name() == name).then_some(decl)
|
||||
})
|
||||
.map(|cmd| get_full_help(cmd, engine_state, stack))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
if !output.is_empty() {
|
||||
Ok(
|
||||
Value::string(output.join("======================\n\n"), call.head)
|
||||
.into_pipeline_data(),
|
||||
)
|
||||
if let Some(decl) = engine_state.find_decl(name.as_bytes(), &[]) {
|
||||
let cmd = engine_state.get_decl(decl);
|
||||
let help_text = get_full_help(cmd, engine_state, stack);
|
||||
Ok(Value::string(help_text, call.head).into_pipeline_data())
|
||||
} else {
|
||||
Err(ShellError::CommandNotFound {
|
||||
span: Span::merge_many(rest.iter().map(|s| s.span)),
|
||||
|
@ -107,21 +107,10 @@ pub fn help_externs(
|
||||
name.push_str(&r.item);
|
||||
}
|
||||
|
||||
let output = engine_state
|
||||
.get_decls_sorted(false)
|
||||
.into_iter()
|
||||
.filter_map(|(_, decl_id)| {
|
||||
let decl = engine_state.get_decl(decl_id);
|
||||
(decl.name() == name).then_some(decl)
|
||||
})
|
||||
.map(|cmd| get_full_help(cmd, engine_state, stack))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
if !output.is_empty() {
|
||||
Ok(
|
||||
Value::string(output.join("======================\n\n"), call.head)
|
||||
.into_pipeline_data(),
|
||||
)
|
||||
if let Some(decl) = engine_state.find_decl(name.as_bytes(), &[]) {
|
||||
let cmd = engine_state.get_decl(decl);
|
||||
let help_text = get_full_help(cmd, engine_state, stack);
|
||||
Ok(Value::string(help_text, call.head).into_pipeline_data())
|
||||
} else {
|
||||
Err(ShellError::CommandNotFound {
|
||||
span: Span::merge_many(rest.iter().map(|s| s.span)),
|
||||
|
@ -20,12 +20,11 @@ pub(crate) fn compile_call(
|
||||
|
||||
// Check if this call has --help - if so, just redirect to `help`
|
||||
if call.named_iter().any(|(name, _, _)| name.item == "help") {
|
||||
return compile_help(
|
||||
working_set,
|
||||
builder,
|
||||
decl.name().into_spanned(call.head),
|
||||
io_reg,
|
||||
);
|
||||
let name = working_set
|
||||
.find_decl_name(call.decl_id) // check for name in scope
|
||||
.and_then(|name| std::str::from_utf8(name).ok())
|
||||
.unwrap_or(decl.name()); // fall back to decl's name
|
||||
return compile_help(working_set, builder, name.into_spanned(call.head), io_reg);
|
||||
}
|
||||
|
||||
// Try to figure out if this is a keyword call like `if`, and handle those specially
|
||||
|
@ -459,21 +459,48 @@ impl<'a> StateWorkingSet<'a> {
|
||||
}
|
||||
|
||||
// check overlay in perma
|
||||
for overlay_frame in self
|
||||
.permanent_state
|
||||
.active_overlays(&removed_overlays)
|
||||
.rev()
|
||||
{
|
||||
visibility.append(&overlay_frame.visibility);
|
||||
self.permanent_state.find_decl(name, &removed_overlays)
|
||||
}
|
||||
|
||||
pub fn find_decl_name(&self, decl_id: DeclId) -> Option<&[u8]> {
|
||||
let mut removed_overlays = vec![];
|
||||
|
||||
let mut visibility: Visibility = Visibility::new();
|
||||
|
||||
for scope_frame in self.delta.scope.iter().rev() {
|
||||
if self.search_predecls {
|
||||
for (name, id) in scope_frame.predecls.iter() {
|
||||
if id == &decl_id {
|
||||
return Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check overlay in delta
|
||||
for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() {
|
||||
visibility.append(&overlay_frame.visibility);
|
||||
|
||||
if self.search_predecls {
|
||||
for (name, id) in overlay_frame.predecls.iter() {
|
||||
if id == &decl_id {
|
||||
return Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(decl_id) = overlay_frame.get_decl(name) {
|
||||
if visibility.is_decl_id_visible(&decl_id) {
|
||||
return Some(decl_id);
|
||||
for (name, id) in overlay_frame.decls.iter() {
|
||||
if id == &decl_id {
|
||||
return Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
// check overlay in perma
|
||||
self.permanent_state
|
||||
.find_decl_name(decl_id, &removed_overlays)
|
||||
}
|
||||
|
||||
pub fn find_module(&self, name: &[u8]) -> Option<ModuleId> {
|
||||
|
Loading…
Reference in New Issue
Block a user