feat: add native LSP hover action and popup
This commit is contained in:
+100
-1
@@ -29,7 +29,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::{
|
||||
editor::{Editor, Input, Mode, Palette},
|
||||
editor::{Editor, Hover, Input, Mode, Palette},
|
||||
files::{DockSide, FilePicker, FilePresentation, FileView},
|
||||
plugin::{Diagnostic, DiagnosticSeverity},
|
||||
session::EditorSession,
|
||||
@@ -631,6 +631,67 @@ fn draw(
|
||||
editor.selection_navigation_hint(),
|
||||
);
|
||||
}
|
||||
if let Some(hover) = editor.hover() {
|
||||
draw_hover(frame, hover, viewport);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_hover(frame: &mut Frame, hover: &Hover, area: Rect) {
|
||||
if area.width == 0 || area.height == 0 {
|
||||
return;
|
||||
}
|
||||
let [_, column, _] = Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(area.width.min(84)),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
let [_, panel, _] = Layout::vertical([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(
|
||||
area.height
|
||||
.min(hover.lines().len().saturating_add(3).min(18) as u16),
|
||||
),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(column);
|
||||
frame.render_widget(Clear, panel);
|
||||
let bordered = panel.height >= 4 && panel.width >= 4;
|
||||
let block = Block::default()
|
||||
.borders(if bordered {
|
||||
Borders::ALL
|
||||
} else {
|
||||
Borders::NONE
|
||||
})
|
||||
.border_style(Style::default().fg(Color::Cyan))
|
||||
.title(" Hover ");
|
||||
let content = if bordered { block.inner(panel) } else { panel };
|
||||
if bordered {
|
||||
frame.render_widget(block, panel);
|
||||
}
|
||||
let [text_area, help_area] = Layout::vertical([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(u16::from(content.height >= 3)),
|
||||
])
|
||||
.areas(content);
|
||||
let lines: Vec<Line> = hover
|
||||
.lines()
|
||||
.skip(hover.top)
|
||||
.take(usize::from(text_area.height))
|
||||
.map(|line| {
|
||||
Line::from(visible_line(
|
||||
line,
|
||||
display_column(line, hover.left),
|
||||
usize::from(text_area.width),
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
frame.render_widget(Paragraph::new(lines), text_area);
|
||||
frame.render_widget(
|
||||
Paragraph::new("Arrows/Ctrl+h/j/k/l scroll · Esc close")
|
||||
.style(Style::default().fg(Color::DarkGray)),
|
||||
help_area,
|
||||
);
|
||||
}
|
||||
|
||||
fn dock_layout(area: Rect, side: DockSide, focused: bool) -> (Rect, Option<Rect>) {
|
||||
@@ -977,6 +1038,44 @@ mod tests {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_scroll_renders_whole_graphemes_in_tiny_workspace() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor.show_hover("x界e\u{301}👩🚀z\nlast".into()).unwrap();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(6, 1)).unwrap();
|
||||
let mut syntax = SyntaxCache::new();
|
||||
let (mut top, mut left) = (0, 0);
|
||||
for _ in 0..2 {
|
||||
editor.handle(Input::Right);
|
||||
}
|
||||
let rendered = terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left, &mut syntax))
|
||||
.unwrap();
|
||||
assert_eq!(rendered.buffer[(2, 0)].symbol(), "e\u{301}");
|
||||
assert_eq!(rendered.buffer[(3, 0)].symbol(), "👩🚀");
|
||||
assert_eq!(rendered.buffer[(5, 0)].symbol(), "z");
|
||||
assert_eq!(
|
||||
terminal.get_cursor_position().unwrap(),
|
||||
ratatui::layout::Position::new(2, 0)
|
||||
);
|
||||
assert_eq!((top, left), (0, 0));
|
||||
editor.handle(Input::Down);
|
||||
editor.handle(Input::Home);
|
||||
terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left, &mut syntax))
|
||||
.unwrap();
|
||||
assert!(screen_text(&terminal).ends_with("last"));
|
||||
for (width, height) in [(1, 1), (2, 2), (3, 3), (4, 4)] {
|
||||
let mut terminal =
|
||||
Terminal::new(ratatui::backend::TestBackend::new(width, height)).unwrap();
|
||||
terminal
|
||||
.draw(|frame| draw(frame, &editor, &mut top, &mut left, &mut syntax))
|
||||
.unwrap();
|
||||
}
|
||||
assert_eq!(editor.cursor(), crate::editor::Cursor::default());
|
||||
assert_eq!(editor.lines(), &[""]);
|
||||
}
|
||||
|
||||
fn annotation(
|
||||
start: (usize, usize),
|
||||
end: (usize, usize),
|
||||
|
||||
Reference in New Issue
Block a user