fix(nu-lsp): hover on external 0x08 handling (#16316)

# Description
issue report: discord `#editor-support` 2025-07-30 (discords `copy
message link` button is broken)

The LSP hover uses `man` under the hood.
Some `man` implementations use `0x08` to underline text, make text bold,
etc.
Nu previously just stripped the `0x08`, leaving the duplicate
characters, random underscores, etc.

With this PR it strips both `0x08` and its preceding character before
stripping the ANSI.

`groff` (`man`'s rendering engine) prints the underscore first
(`_\x08A`) when underscoring (probably in case something cant print
chars on top of another) and thus the char before has to be stripped.

different `man` implementations accept different arguments (example: the
one i use errors from `-P`) and i was unable to find a env-var or
argument to fix the problem (`-T ascii` does nothing on my pc).

I am fairly new to `Cow` stuff, so this is probably far from the most
performant implementation.
Feel free to give me pointers, fork this PR, or whatever.

## Potential bugs

I would be suprised, but some `man` implementation might render the char
first and the underscore second.

## Testing

Apparently not every `man` implementation is affected by the original
bug..

I use:
* voidlinux (gnu)
* groff: `1.23.0`
* man: `man-db-2.13.0_1`
* editor: `helix 25.01.1 (bcb6c20a)`

```
> man rev | to json
"REV(1)                           User Commands                          REV(1)\n\nN\bNA\bAM\bME\bE\n       rev - reverse lines characterwise\n\nS\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS\n       r\bre\bev\bv [option] [_\bf_\bi_\bl_\be...]\n\nD\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN\n       The r\bre\bev\bv utility copies the specified files to standard output,\n       reversing the order of characters in every line. If no files are\n       specified, standard input is read.\n\n       This utility is a line-oriented tool and it uses in-memory allocated\n       buffer for a whole wide-char line. If the input file is huge and\n       without line breaks then allocating the memory for the file may be\n       unsuccessful.\n\nO\bOP\bPT\bTI\bIO\bON\bNS\bS\n       -\b-h\bh, -\b--\b-h\bhe\bel\blp\bp\n           Display help text and exit.\n\n       -\b-V\bV, -\b--\b-v\bve\ber\brs\bsi\bio\bon\bn\n           Print version and exit.\n\n       -\b-0\b0, -\b--\b-z\bze\ber\bro\bo\n           _\bZ_\be_\br_\bo _\bt_\be_\br_\bm_\bi_\bn_\ba_\bt_\bi_\bo_\bn. Use the byte '\\0' as line separator.\n\nS\bSE\bEE\bE A\bAL\bLS\bSO\bO\n       t\bta\bac\bc(1)\n\nR\bRE\bEP\bPO\bOR\bRT\bTI\bIN\bNG\bG B\bBU\bUG\bGS\bS\n       For bug reports, use the issue tracker at\n       <https://github.com/util-linux/util-linux/issues>.\n\nA\bAV\bVA\bAI\bIL\bLA\bAB\bBI\bIL\bLI\bIT\bTY\bY\n       The r\bre\bev\bv command is part of the util-linux package which can be\n       downloaded from _\bL_\bi_\bn_\bu_\bx _\bK_\be_\br_\bn_\be_\bl _\bA_\br_\bc_\bh_\bi_\bv_\be\n       <https://www.kernel.org/pub/linux/utils/util-linux/>.\n\nutil-linux 2.40.2                 2024-01-31                            REV(1)"
```

# User-Facing Changes

I think this is just a bugfix.

# Tests + Formatting

- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used`
to check that you're using the standard code style
- [x] `cargo test --workspace` to check that all tests pass (on Windows
make sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- [x] `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

# After Submitting

Co-authored-by: blindFS <blindfs19@gmail.com>
This commit is contained in:
Jan9103
2025-08-19 09:15:14 +00:00
committed by GitHub
parent 458b8a5362
commit 5ce9c38540
3 changed files with 16 additions and 1 deletions

1
Cargo.lock generated
View File

@@ -4023,6 +4023,7 @@ version = "0.106.2"
dependencies = [
"assert-json-diff",
"crossbeam-channel",
"fancy-regex",
"lsp-server",
"lsp-textdocument",
"lsp-types",

View File

@@ -15,6 +15,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.106.2" }
nu-utils = { path = "../nu-utils", version = "0.106.2" }
crossbeam-channel = { workspace = true }
fancy-regex = { workspace = true }
lsp-server = { workspace = true }
lsp-textdocument = { workspace = true }
lsp-types = { workspace = true }

View File

@@ -1,5 +1,6 @@
use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind};
use nu_protocol::{PositionalArg, engine::Command};
use std::borrow::Cow;
use crate::{
Id, LanguageServer,
@@ -188,6 +189,15 @@ impl LanguageServer {
}
Id::Value(t) => markdown_hover(format!("`{t}`")),
Id::External(cmd) => {
fn fix_manpage_ascii_shenanigans(text: &str) -> Cow<str> {
if cfg!(windows) {
Cow::Borrowed(text)
} else {
let re =
fancy_regex::Regex::new(r".\x08").expect("regular expression error");
re.replace_all(text, "")
}
}
let command_output = if cfg!(windows) {
std::process::Command::new("powershell.exe")
.args(["-NoProfile", "-Command", "help", &cmd])
@@ -197,7 +207,10 @@ impl LanguageServer {
};
let manpage_str = match command_output {
Ok(output) => nu_utils::strip_ansi_likely(
String::from_utf8_lossy(&output.stdout).as_ref(),
fix_manpage_ascii_shenanigans(
String::from_utf8_lossy(&output.stdout).as_ref(),
)
.as_ref(),
)
.to_string(),
Err(_) => format!("No command help found for {}", &cmd),