Repository: nushell/nushell
File: crates/nu-lsp/src/lib.rs (Rust)

Code before:
                            let value_string = val
                                .coerce_into_string()
                                .ok()
                                .map(|s| format!("\n---\n{}", s))
                                .unwrap_or_default();
                            format!("```\n{}\n```{}", ty, value_string)
                        })
                        .unwrap_or("`unknown`".into()),
                )
            }
            Id::Declaration(decl_id) => markdown_hover(Self::get_decl_description(
                working_set.get_decl(decl_id),
                false,
            )),
            Id::Module(module_id) => {
                let description = working_set
                    .get_module_comments(module_id)?
                    .iter()
                    .map(|sp| String::from_utf8_lossy(working_set.get_span_contents(*sp)).into())
                    .collect::<Vec<String>>()
                    .join("\n");
                markdown_hover(description)
            }
            Id::Value(t) => markdown_hover(format!("`{}`", t)),
            Id::External(cmd) => {
                let output = std::process::Command::new("man").arg(&cmd).output();
                let manpage_str = match output {
                    Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
                    Err(_) => format!("No manpage found for {}", &cmd),
                };
                markdown_hover(manpage_str.to_string())
            }
        }
    }

    fn complete(&mut self, params: &CompletionParams) -> Option<CompletionResponse> {
        let path_uri = params.text_document_position.text_document.uri.to_owned();
        let docs = self.docs.lock().ok()?;
        let file = docs.get_document(&path_uri)?;

        let engine_state = Arc::new(self.initial_engine_state.clone());
        let mut completer = NuCompleter::new(engine_state.clone(), Arc::new(Stack::new()));

        let location = file.offset_at(params.text_document_position.position) as usize;
        let results = completer.fetch_completions_at(&file.get_content(None)[..location], location);
        (!results.is_empty()).then_some(CompletionResponse::Array(
            results
                .into_iter()
                .map(|r| {
                    let mut start = params.text_document_position.position;
                    start.character -= (r.suggestion.span.end - r.suggestion.span.start) as u32;

Review comment (suggestion):
`man` doesn't typically exist on Windows. We need to stay as cross platform as possible. I wonder if it would be better to do `cmd --help` which could be `/help`, `/?` or `--help` on Windows, depending on the command.

Code after:
                            let value_string = val
                                .coerce_into_string()
                                .ok()
                                .map(|s| format!("\n---\n{}", s))
                                .unwrap_or_default();
                            format!("```\n{}\n```{}", ty, value_string)
                        })
                        .unwrap_or("`unknown`".into()),
                )
            }
            Id::Declaration(decl_id) => markdown_hover(Self::get_decl_description(
                working_set.get_decl(decl_id),
                false,
            )),
            Id::Module(module_id) => {
                let description = working_set
                    .get_module_comments(module_id)?
                    .iter()
                    .map(|sp| String::from_utf8_lossy(working_set.get_span_contents(*sp)).into())
                    .collect::<Vec<String>>()
                    .join("\n");
                markdown_hover(description)
            }
            Id::Value(t) => markdown_hover(format!("`{}`", t)),
            Id::External(cmd) => {
                let command_output = if cfg!(windows) {
                    std::process::Command::new("powershell.exe")
                        .args(["-NoProfile", "-Command", "help", &cmd])
                        .output()
                } else {
                    std::process::Command::new("man").arg(&cmd).output()
                };
                let manpage_str = match command_output {
                    Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
                    Err(_) => format!("No command help found for {}", &cmd),
                };
                markdown_hover(manpage_str)
            }
        }
    }

    fn complete(&mut self, params: &CompletionParams) -> Option<CompletionResponse> {
        let path_uri = params.text_document_position.text_document.uri.to_owned();
        let docs = self.docs.lock().ok()?;
        let file = docs.get_document(&path_uri)?;

        let engine_state = Arc::new(self.initial_engine_state.clone());
        let mut completer = NuCompleter::new(engine_state.clone(), Arc::new(Stack::new()));

        let location = file.offset_at(params.text_document_position.position) as usize;
        let results = completer.fetch_completions_at(&file.get_content(None)[..location], location);