Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/app/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ impl App {
self.search.idx
}

#[cfg(test)]
pub(crate) fn search_matches(&self) -> &[usize] {
&self.search.matches
}
Expand Down
32 changes: 26 additions & 6 deletions src/markdown/highlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,23 @@ fn skip_ranges_ending_by(ranges: &[Range<usize>], index: &mut usize, pos: usize)
}

pub(crate) fn highlight_line<'a>(line: &Line<'a>, theme: &MarkdownTheme, query: &str) -> Line<'a> {
with_link_marker(|marker| highlight_line_with_marker(line, theme, query, marker))
with_link_marker(|marker| highlight_line_with_marker(line, theme, query, marker, false))
}

pub(crate) fn highlight_matches_only<'a>(
line: &Line<'a>,
theme: &MarkdownTheme,
query: &str,
) -> Line<'a> {
with_link_marker(|marker| highlight_line_with_marker(line, theme, query, marker, true))
}

fn highlight_line_with_marker<'a>(
line: &Line<'a>,
theme: &MarkdownTheme,
query: &str,
marker: &str,
matches_only: bool,
) -> Line<'a> {
let spans = &line.spans;
let content_start = content_span_start(spans);
Expand All @@ -78,13 +87,24 @@ fn highlight_line_with_marker<'a>(
}

let line_bg = theme.search_highlight_bg;
let match_style_extra = Style::default()
.bg(theme.search_match_bg)
.add_modifier(Modifier::BOLD);
let match_style_extra = if matches_only {
Style::default().bg(line_bg).add_modifier(Modifier::BOLD)
} else {
Style::default()
.bg(theme.search_match_bg)
.add_modifier(Modifier::BOLD)
};
let base_style_of = |span: &Span<'_>| {
if matches_only {
span.style
} else {
span.style.bg(line_bg)
}
};
let mut result: Vec<Span<'a>> = Vec::with_capacity(spans.len());

for span in &spans[..content_start] {
result.push(Span::styled(span.content.clone(), span.style.bg(line_bg)));
result.push(Span::styled(span.content.clone(), base_style_of(span)));
}

let mut char_offset: usize = 0;
Expand All @@ -96,7 +116,7 @@ fn highlight_line_with_marker<'a>(
}
let span_len = span.content.chars().count();
let span_end = char_offset + span_len;
let base_style = span.style.bg(line_bg);
let base_style = base_style_of(span);

skip_ranges_ending_by(&ranges, &mut range_index, char_offset);
if ranges
Expand Down
2 changes: 1 addition & 1 deletion src/markdown/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub(crate) mod toc;
pub(crate) mod width;
mod wrapping;

pub(crate) use highlight::highlight_line;
pub(crate) use highlight::{highlight_line, highlight_matches_only};
pub(crate) use links::{LinkId, LinkSpan};
pub(crate) use syntax::resolve_syntax;
use tables::{handle_table_event, start_table, TableBuf};
Expand Down
8 changes: 8 additions & 0 deletions src/render/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ pub(super) fn render_content_panel(f: &mut Frame, app: &mut App, area: Rect) {
let visible_end = (scroll + viewport_height).min(app.total());
let mut visible_lines = app.visible_lines(scroll, visible_end).to_vec();

for &row in app.search_matches() {
if Some(row) != active_highlight_line && (scroll..visible_end).contains(&row) {
let line = &mut visible_lines[row - scroll];
*line =
crate::markdown::highlight_matches_only(line, &theme.markdown, app.search_query());
}
}

if let Some(line_idx) = active_highlight_line {
if (scroll..visible_end).contains(&line_idx) {
if let Some((_, highlighted_line)) = app.highlighted_line_cache() {
Expand Down
37 changes: 21 additions & 16 deletions src/render/diff/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,30 @@ use ratatui::{
Frame,
};

fn highlight_active_match(
fn highlight_search_matches(
app: &App,
visible_lines: &mut [Line<'static>],
scroll: usize,
split: bool,
) {
let Some(row) = app.active_highlight_line() else {
return;
};
let Some(line) = row
.checked_sub(scroll)
.and_then(|rel| visible_lines.get_mut(rel))
else {
return;
};
let original = split.then(|| line.clone());
*line = crate::markdown::highlight_line(line, &app_theme().markdown, app.search_query());
if let Some(original) = original {
restore_blank_split_side(line, &original);
let active = app.active_highlight_line();
let theme = &app_theme().markdown;
for &row in app.search_matches() {
let Some(line) = row
.checked_sub(scroll)
.and_then(|rel| visible_lines.get_mut(rel))
else {
continue;
};
let original = split.then(|| line.clone());
*line = if Some(row) == active {
crate::markdown::highlight_line(line, theme, app.search_query())
} else {
crate::markdown::highlight_matches_only(line, theme, app.search_query())
};
if let Some(original) = original {
restore_blank_split_side(line, &original);
}
}
}

Expand Down Expand Up @@ -85,7 +90,7 @@ pub(crate) fn render_diff_panel(f: &mut Frame, app: &mut App, area: Rect) {
let viewport_height = area.height as usize;
let visible_end = (scroll + viewport_height).min(app.total());
let mut visible_lines = app.visible_lines(scroll, visible_end).to_vec();
highlight_active_match(app, &mut visible_lines, scroll, false);
highlight_search_matches(app, &mut visible_lines, scroll, false);

let active_file_idx = app.diff_current_file_from_scroll();
if let Some(state) = app.diff() {
Expand Down Expand Up @@ -329,7 +334,7 @@ pub(super) fn render_split_lines_block(
} else {
Vec::new()
};
highlight_active_match(app, &mut visible_lines, scroll, true);
highlight_search_matches(app, &mut visible_lines, scroll, true);

let active_file_idx = app.diff_current_file_from_scroll();
for (fi, &top_idx) in built.file_top_idx.iter().enumerate() {
Expand Down
22 changes: 20 additions & 2 deletions src/tests/markdown_links.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{test_assets, test_md_theme};
use crate::markdown::{
display_width, highlight_line, parse_markdown, parse_markdown_with_width, resolve_syntax,
LinkId, LinkSpan,
display_width, highlight_line, highlight_matches_only, parse_markdown,
parse_markdown_with_width, resolve_syntax, LinkId, LinkSpan,
};
use crate::theme::app_theme;
use ratatui::{
Expand Down Expand Up @@ -571,3 +571,21 @@ fn adversarial_table_fixture_preserves_visual_destination_order() {
> 1
);
}

#[test]
fn highlight_matches_only_bolds_matches_without_line_bg() {
let theme = test_md_theme();
let bold = Style::default().add_modifier(ratatui::style::Modifier::BOLD);
let line = Line::from(vec![Span::raw("say he"), Span::styled("llo wor", bold)]);
let result = highlight_matches_only(&line, &theme, "hello");
let text: Vec<_> = result.spans.iter().map(|s| s.content.as_ref()).collect();
assert_eq!(text, ["say ", "he", "llo", " wor"]);
assert_eq!(result.spans[0].style.bg, None);
assert_eq!(result.spans[1].style.bg, Some(theme.search_highlight_bg));
assert_eq!(result.spans[2].style.bg, Some(theme.search_highlight_bg));
assert_eq!(result.spans[3].style.bg, None);
assert!(result.spans[1]
.style
.add_modifier
.contains(ratatui::style::Modifier::BOLD));
}
Loading