Skip to content

feat: new command to goto hover (like goto definition) #12208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions book/src/generated/static-cmd.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
| `remove_primary_selection` | Remove primary selection | normal: `` <A-,> ``, select: `` <A-,> `` |
| `completion` | Invoke completion popup | insert: `` <C-x> `` |
| `hover` | Show docs for item under cursor | normal: `` <space>k ``, select: `` <space>k `` |
| `hover_dump` | Show docs for item under cursor in a new buffer | normal: `` <space>K ``, select: `` <space>K `` |
| `toggle_comments` | Comment/uncomment selections | normal: `` <C-c> ``, `` <space>c ``, select: `` <C-c> ``, `` <space>c `` |
| `toggle_line_comments` | Line comment/uncomment selections | normal: `` <space><A-c> ``, select: `` <space><A-c> `` |
| `toggle_block_comments` | Block comment/uncomment selections | normal: `` <space>C ``, select: `` <space>C `` |
Expand Down
1 change: 1 addition & 0 deletions book/src/keymap.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ This layer is a kludge of mappings, mostly pickers.
| `g` | Open changed file picker | `changed_file_picker` |
| `G` | Debug (experimental) | N/A |
| `k` | Show documentation for item under cursor in a [popup](#popup) (**LSP**) | `hover` |
| `K` | Go to documentation for item under cursor in a new buffer (**LSP**) | `goto_hover` |
| `s` | Open document symbol picker (**LSP**) | `symbol_picker` |
| `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` |
| `d` | Open document diagnostics picker (**LSP**) | `diagnostics_picker` |
Expand Down
1 change: 1 addition & 0 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ impl MappableCommand {
remove_primary_selection, "Remove primary selection",
completion, "Invoke completion popup",
hover, "Show docs for item under cursor",
goto_hover, "Show docs for item under cursor in a new buffer",
toggle_comments, "Comment/uncomment selections",
toggle_line_comments, "Line comment/uncomment selections",
toggle_block_comments, "Block comment/uncomment selections",
Expand Down
47 changes: 40 additions & 7 deletions helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use super::{align_view, push_jump, Align, Context, Editor};

use helix_core::{
diagnostic::DiagnosticProvider, syntax::LanguageServerFeature,
text_annotations::InlineAnnotation, Selection, Uri,
text_annotations::InlineAnnotation, Rope, Selection, Uri,
};
use helix_stdx::path;
use helix_view::{
Expand All @@ -32,7 +32,9 @@ use crate::{
ui::{self, overlay::overlaid, FileLocation, Picker, Popup, PromptEvent},
};

use std::{cmp::Ordering, collections::HashSet, fmt::Display, future::Future, path::Path};
use std::{
cmp::Ordering, collections::HashSet, fmt::Display, future::Future, path::Path, sync::Arc,
};

/// Gets the first language server that is attached to a document which supports a specific feature.
/// If there is no configured language server that supports the feature, this displays a status message.
Expand Down Expand Up @@ -1015,7 +1017,12 @@ pub fn signature_help(cx: &mut Context) {
.trigger_signature_help(SignatureHelpInvoked::Manual, cx.editor)
}

pub fn hover(cx: &mut Context) {
enum HoverDisplay {
Popup,
File,
}

fn hover_impl(cx: &mut Context, hover_action: HoverDisplay) {
use ui::lsp::hover::Hover;

let (view, doc) = current!(cx.editor);
Expand Down Expand Up @@ -1062,15 +1069,41 @@ pub fn hover(cx: &mut Context) {
return;
}

// create new popup
let contents = Hover::new(hovers, editor.syn_loader.clone());
let popup = Popup::new(Hover::ID, contents).auto_close(true);
compositor.replace_or_push(Hover::ID, popup);
let hover = Hover::new(hovers, editor.syn_loader.clone());

match hover_action {
HoverDisplay::Popup => {
let popup = Popup::new(Hover::ID, hover).auto_close(true);
compositor.replace_or_push(Hover::ID, popup);
}
HoverDisplay::File => {
editor.new_file_from_document(
Action::Replace,
Document::from(
Rope::from(hover.content_string()),
None,
Arc::clone(&editor.config),
),
);
let hover_doc = doc_mut!(editor);

let _ = hover_doc
.set_language_by_language_id("markdown", editor.syn_loader.clone());
}
}
};
Ok(Callback::EditorCompositor(Box::new(call)))
});
}

pub fn hover(cx: &mut Context) {
hover_impl(cx, HoverDisplay::Popup)
}

pub fn goto_hover(cx: &mut Context) {
hover_impl(cx, HoverDisplay::File)
}

pub fn rename_symbol(cx: &mut Context) {
fn get_prefill_from_word_boundary(editor: &Editor) -> String {
let (view, doc) = current_ref!(editor);
Expand Down
1 change: 1 addition & 0 deletions helix-term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"R" => replace_selections_with_clipboard,
"/" => global_search,
"k" => hover,
"K" => goto_hover,
"r" => rename_symbol,
"h" => select_references_to_symbol_under_cursor,
"c" => toggle_comments,
Expand Down
35 changes: 25 additions & 10 deletions helix-term/src/ui/lsp/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,15 @@ impl Hover {
.into_iter()
.enumerate()
.map(|(idx, (server_name, hover))| {
let header = (n_hovers > 1).then(|| {
Markdown::new(
format!("**[{}/{}] {}**", idx + 1, n_hovers, server_name),
config_loader.clone(),
)
});
let header = (n_hovers > 1)
.then(|| format!("**[{}/{}] {}**\n", idx + 1, n_hovers, server_name))
.map(|h| Markdown::new(h, Arc::clone(&config_loader)));

let body = Markdown::new(
hover_contents_to_string(hover.contents),
config_loader.clone(),
Arc::clone(&config_loader),
);

(header, body)
})
.collect();
Expand All @@ -54,10 +53,26 @@ impl Hover {
self.contents.len() > 1
}

fn content(&self) -> &(Option<Markdown>, Markdown) {
fn content_markdown(&self) -> &(Option<Markdown>, Markdown) {
&self.contents[self.active_index]
}

pub fn content_string(&self) -> String {
self.contents
.iter()
.map(|(header, body)| {
let header: String = header
.iter()
.map(|header| header.contents.clone())
.collect();

format!("{}{}", header, body.contents)
})
.collect::<Vec<String>>()
.join("\n\n---\n\n")
+ "\n"
}

fn set_index(&mut self, index: usize) {
assert!((0..self.contents.len()).contains(&index));
self.active_index = index;
Expand All @@ -75,7 +90,7 @@ impl Component for Hover {
let margin = Margin::all(1);
let area = area.inner(margin);

let (header, contents) = self.content();
let (header, contents) = self.content_markdown();

// show header and border only when more than one results
if let Some(header) = header {
Expand Down Expand Up @@ -110,7 +125,7 @@ impl Component for Hover {
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
let max_text_width = viewport.0.saturating_sub(PADDING_HORIZONTAL).clamp(10, 120);

let (header, contents) = self.content();
let (header, contents) = self.content_markdown();

let header_width = header
.as_ref()
Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/ui/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub fn highlighted_code_block<'a>(
}

pub struct Markdown {
contents: String,
pub contents: String,

config_loader: Arc<ArcSwap<syntax::Loader>>,
}
Expand Down
2 changes: 1 addition & 1 deletion helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1726,7 +1726,7 @@ impl Editor {
id
}

fn new_file_from_document(&mut self, action: Action, doc: Document) -> DocumentId {
pub fn new_file_from_document(&mut self, action: Action, doc: Document) -> DocumentId {
let id = self.new_document(doc);
self.switch(id, action);
id
Expand Down
Loading