Improve hovers, including for custom commands (#8881)

# Description

Improves the info we give in hovers.


![image](https://user-images.githubusercontent.com/547158/231880027-78d8f3b1-a7cb-49aa-93d3-25cdbf2521d2.png)

Note: need to work on highlighting a bit before this is ready.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# 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` 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
> 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.
-->
This commit is contained in:
JT 2023-04-14 11:28:16 +12:00 committed by GitHub
parent 8ddebcb932
commit 4a8124ad1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -23,9 +23,9 @@ fn find_id(
file: &[u8],
location: &Value,
) -> Option<(Id, usize, Span)> {
let offset = working_set.next_span_start();
let file_id = working_set.add_file(file_path.to_string(), file);
let offset = working_set.get_span_for_file(file_id).start;
let block = parse(working_set, Some(file_path), file, false);
let flattened = flatten_block(working_set, &block);
if let Ok(location) = location.as_i64() {
@ -201,19 +201,131 @@ pub fn hover(engine_state: &mut EngineState, file_path: &String, location: &Valu
Some((Id::Declaration(decl_id), offset, span)) => {
let decl = working_set.get_decl(decl_id);
let mut description = format!("```\n### Signature\n```\n{}\n\n", decl.signature());
let mut description = "```\n### Signature\n```\n".to_string();
description.push_str(&format!("```\n### Usage\n {}\n```\n", decl.usage()));
let signature = decl.signature();
description.push_str(&format!(" {}", signature.name));
if !signature.named.is_empty() {
description.push_str(" {flags}")
}
for required_arg in &signature.required_positional {
description.push_str(&format!(" <{}>", required_arg.name));
}
for optional_arg in &signature.optional_positional {
description.push_str(&format!(" <{}?>", optional_arg.name));
}
if let Some(arg) = &signature.rest_positional {
description.push_str(&format!(" <...{}>", arg.name));
}
description.push_str("\n```\n");
if !signature.required_positional.is_empty()
|| !signature.optional_positional.is_empty()
|| signature.rest_positional.is_some()
{
description.push_str("\n### Parameters\n\n");
let mut first = true;
for required_arg in &signature.required_positional {
if !first {
description.push_str("\\\n");
} else {
first = false;
}
description.push_str(&format!(
" `{}: {}`",
required_arg.name,
required_arg.shape.to_type()
));
if !required_arg.desc.is_empty() {
description.push_str(&format!(" - {}", required_arg.desc));
}
description.push('\n');
}
for optional_arg in &signature.optional_positional {
if !first {
description.push_str("\\\n");
} else {
first = false;
}
description.push_str(&format!(
" `{}: {}`",
optional_arg.name,
optional_arg.shape.to_type()
));
if !optional_arg.desc.is_empty() {
description.push_str(&format!(" - {}", optional_arg.desc));
}
description.push('\n');
}
if let Some(arg) = &signature.rest_positional {
if !first {
description.push_str("\\\n");
}
description.push_str(&format!(" `...{}: {}`", arg.name, arg.shape.to_type()));
if !arg.desc.is_empty() {
description.push_str(&format!(" - {}", arg.desc));
}
description.push('\n');
}
description.push('\n');
}
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if !first {
description.push_str("\\\n");
} else {
first = false;
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{}`", short_flag));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ")
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()))
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
}
description.push('\n');
}
if !signature.input_output_types.is_empty() {
description.push_str("\n### Input/output\n");
description.push_str("\n```\n");
for input_output in &signature.input_output_types {
description.push_str(&format!(" {} | {}\n", input_output.0, input_output.1));
}
description.push_str("\n```\n");
}
description.push_str(&format!("### Usage\n {}\n", decl.usage()));
if !decl.extra_usage().is_empty() {
description.push_str(&format!(
"\n```\n### Extra usage:\n {}\n```\n",
decl.extra_usage()
));
description.push_str(&format!("\n### Extra usage:\n {}\n", decl.extra_usage()));
}
if !decl.examples().is_empty() {
description.push_str("\n```\n### Example(s)\n```\n");
description.push_str("### Example(s)\n```\n");
for example in decl.examples() {
description.push_str(&format!(