feat: enlarge floating hover and highlight Markdown content
This commit is contained in:
+108
-12
@@ -567,16 +567,7 @@ fn draw(
|
||||
spans
|
||||
.get(span_index)
|
||||
.filter(|span| span.start_byte <= byte)
|
||||
.map(|span| {
|
||||
Style::default().fg(match span.kind {
|
||||
SyntaxKind::Comment => Color::DarkGray,
|
||||
SyntaxKind::String => Color::Green,
|
||||
SyntaxKind::Keyword => Color::Magenta,
|
||||
SyntaxKind::Number => Color::Yellow,
|
||||
SyntaxKind::Type | SyntaxKind::Markup => Color::Cyan,
|
||||
SyntaxKind::Function => Color::LightBlue,
|
||||
})
|
||||
})
|
||||
.map(|span| syntax_style(span.kind))
|
||||
});
|
||||
if let Some(style) = style
|
||||
&& end > *left
|
||||
@@ -907,6 +898,7 @@ fn draw_startup_diagnostic(frame: &mut Frame, diagnostic: &StartupDiagnostic, ar
|
||||
#[derive(Default)]
|
||||
struct WheelPanels {
|
||||
hover: Option<(Rect, usize)>,
|
||||
hover_syntax: SyntaxCache,
|
||||
files: Option<(Rect, usize, usize)>,
|
||||
manual_files: Option<FileWheelOffset>,
|
||||
}
|
||||
@@ -1037,7 +1029,7 @@ fn draw_hover(frame: &mut Frame, hover: &Hover, area: Rect, panels: &mut WheelPa
|
||||
}
|
||||
let [_, column, _] = Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(area.width.min(84)),
|
||||
Constraint::Length(area.width.min(100)),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.areas(area);
|
||||
@@ -1045,7 +1037,7 @@ fn draw_hover(frame: &mut Frame, hover: &Hover, area: Rect, panels: &mut WheelPa
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(
|
||||
area.height
|
||||
.min(hover.lines().len().saturating_add(3).min(18) as u16),
|
||||
.min(hover.lines().len().saturating_add(3).min(24) as u16),
|
||||
),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
@@ -1083,6 +1075,50 @@ fn draw_hover(frame: &mut Frame, hover: &Hover, area: Rect, panels: &mut WheelPa
|
||||
})
|
||||
.collect();
|
||||
frame.render_widget(Paragraph::new(lines), text_area);
|
||||
panels
|
||||
.hover_syntax
|
||||
.update_text(hover.text(), std::path::Path::new("hover.md"));
|
||||
let width = usize::from(text_area.width);
|
||||
for (row, line) in hover
|
||||
.lines()
|
||||
.enumerate()
|
||||
.skip(hover.top)
|
||||
.take(usize::from(text_area.height))
|
||||
{
|
||||
let spans = panels.hover_syntax.line(row);
|
||||
if spans.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let left = display_column(line, hover.left);
|
||||
let right = left.saturating_add(width);
|
||||
let mut column = 0;
|
||||
let mut span_index = 0;
|
||||
for (byte, grapheme) in line.grapheme_indices(true) {
|
||||
if column >= right {
|
||||
break;
|
||||
}
|
||||
let cells = if grapheme == "\t" {
|
||||
TAB_WIDTH - column % TAB_WIDTH
|
||||
} else {
|
||||
UnicodeWidthStr::width(grapheme)
|
||||
};
|
||||
let end = column + cells;
|
||||
while span_index < spans.len() && spans[span_index].end_byte <= byte {
|
||||
span_index += 1;
|
||||
}
|
||||
if let Some(span) = spans.get(span_index).filter(|span| span.start_byte <= byte)
|
||||
&& end > left
|
||||
&& (grapheme == "\t" || (column >= left && end <= right))
|
||||
{
|
||||
let style = syntax_style(span.kind);
|
||||
let y = text_area.y + (row - hover.top) as u16;
|
||||
for cell in column.max(left)..end.min(right) {
|
||||
frame.buffer_mut()[(text_area.x + (cell - left) as u16, y)].set_style(style);
|
||||
}
|
||||
}
|
||||
column = end;
|
||||
}
|
||||
}
|
||||
frame.render_widget(
|
||||
Paragraph::new("Arrows/Ctrl+h/j/k/l scroll · Esc close")
|
||||
.style(Style::default().fg(Color::DarkGray)),
|
||||
@@ -1401,6 +1437,17 @@ fn draw_palette(frame: &mut Frame, palette: &Palette, mode: Mode, navigation_hin
|
||||
);
|
||||
}
|
||||
|
||||
fn syntax_style(kind: SyntaxKind) -> Style {
|
||||
Style::default().fg(match kind {
|
||||
SyntaxKind::Comment => Color::DarkGray,
|
||||
SyntaxKind::String => Color::Green,
|
||||
SyntaxKind::Keyword => Color::Magenta,
|
||||
SyntaxKind::Number => Color::Yellow,
|
||||
SyntaxKind::Type | SyntaxKind::Markup => Color::Cyan,
|
||||
SyntaxKind::Function => Color::LightBlue,
|
||||
})
|
||||
}
|
||||
|
||||
fn semantic_style(token: &SemanticToken) -> Style {
|
||||
use SemanticTokenKind::*;
|
||||
let readonly = token.modifiers & SemanticTokenModifier::Readonly as u16 != 0;
|
||||
@@ -1482,6 +1529,55 @@ mod tests {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_shows_more_documentation_with_markdown_and_fenced_code_colors() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
let title = format!("# {}TAIL", "Heading ".repeat(11));
|
||||
let details = (5..36)
|
||||
.map(|row| format!("detail-{row:02}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
editor
|
||||
.show_hover(format!(
|
||||
"{title}\n\n```rust\nfn answer() -> i32 {{ 42 }}\n```\n{details}"
|
||||
))
|
||||
.unwrap();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 32)).unwrap();
|
||||
let mut panels = WheelPanels::default();
|
||||
terminal
|
||||
.draw(|frame| draw_hover(frame, editor.hover().unwrap(), frame.area(), &mut panels))
|
||||
.unwrap();
|
||||
assert!(screen_text(&terminal).contains("TAIL"));
|
||||
assert!(screen_text(&terminal).contains("detail-20"));
|
||||
let (panel, _) = panels.hover.unwrap();
|
||||
let (x, y) = (panel.x + 1, panel.y + 1);
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(x, y)].fg, Color::Cyan);
|
||||
assert_eq!(buffer[(x, y + 3)].symbol(), "f");
|
||||
assert_eq!(buffer[(x, y + 3)].fg, Color::Magenta);
|
||||
assert_eq!(buffer[(x + 6, y + 3)].fg, Color::LightBlue);
|
||||
assert_eq!(buffer[(x + 21, y + 3)].fg, Color::Yellow);
|
||||
|
||||
for _ in 0..3 {
|
||||
editor.handle(Input::Down);
|
||||
}
|
||||
editor.handle(Input::Right);
|
||||
terminal
|
||||
.draw(|frame| draw_hover(frame, editor.hover().unwrap(), frame.area(), &mut panels))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(x, y)].symbol(), "n");
|
||||
assert_eq!(buffer[(x, y)].fg, Color::Magenta);
|
||||
assert_eq!(buffer[(x + 20, y)].fg, Color::Yellow);
|
||||
|
||||
editor.show_hover("# Replacement".into()).unwrap();
|
||||
terminal
|
||||
.draw(|frame| draw_hover(frame, editor.hover().unwrap(), frame.area(), &mut panels))
|
||||
.unwrap();
|
||||
assert!(screen_text(&terminal).contains("# Replacement"));
|
||||
assert!(!screen_text(&terminal).contains("answer"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_scroll_renders_whole_graphemes_in_tiny_workspace() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user