@@ -76,8 +76,8 @@
static INVERT: LazyLock<String> = LazyLock::new(|| Attribute::Reverse.to_string());
static NORMAL: LazyLock<String> = LazyLock::new(|| Attribute::NoReverse.to_string());
static ANSI_REGEX: LazyLock<Regex> = LazyLock::new(|| {
- Regex::new("[\\u001b\\u009b]\\[[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]")
- .unwrap()
+ // CSI parameter/intermediate/final classes also cover colon-form SGR.
+ Regex::new("(?:\\u001b\\[|\\u009b)[0-?]*[ -/]*[@-~]").unwrap()
});
static WORD: LazyLock<Regex> = LazyLock::new(|| {
@@ -242,7 +242,7 @@
}
}
-fn line_matches_query(line: &str, query: &Regex) -> bool {
+pub(crate) fn line_matches_query(line: &str, query: &Regex) -> bool {
let stripped = ANSI_REGEX.replace_all(line, "");
query.is_match(stripped.as_ref())
}
@@ -742,8 +742,7 @@
query: &'b Regex,
accurate: bool,
) -> HighlightMatchesArgs<'a, 'b> {
- let stripped_str = ANSI_REGEX.replace_all(line, "");
- let is_match = query.is_match(&stripped_str);
+ let is_match = line_matches_query(line, query);
HighlightMatchesArgs {
line,
query,
@@ -752,87 +751,8 @@
}
}
-fn highlight_line_matches_ansi(line: &str, query: ®ex::Regex, accurate: bool) -> String {
- let stripped_str = ANSI_REGEX.replace_all(line, "");
-
- // if it doesn't match, don't even try. Just return.
- if !query.is_match(&stripped_str) {
- return line.to_string();
- }
-
- // sum_width is used to calculate the total width of the ansi escapes
- // up to the point in the original string where it is being used
- let mut sum_width = 0;
-
- // find all ansi escapes in the original string, and map them
- // to a Vec<(usize, &str)> where
- // .0 == the start index in the STRIPPED string
- // .1 == the escape sequence itself
- let escapes = ANSI_REGEX
- .find_iter(line)
- .map(|escape| {
- let start = escape.start();
- let as_str = escape.as_str();
- let ret = (start - sum_width, as_str);
- sum_width += as_str.len();
- ret
- })
- .collect::<Vec<_>>();
-
- // The matches of the term you're looking for, so that you can easily determine where
- // the invert attributes will be placed
- let matches = query
- .find_iter(&stripped_str)
- .flat_map(|c| [c.start(), c.end()])
- .collect::<Vec<_>>();
-
- // Highlight all the instances of the search term in the stripped string
- // by inverting their background/foreground colors
- let mut inverted = query
- .replace_all(&stripped_str, |caps: ®ex::Captures| {
- format!("{}{}{}", *INVERT, &caps[0], *NORMAL)
- })
- .to_string();
-
- // inserted_escs_len == the total length of the ascii escapes which have been re-inserted
- // into the stripped string at the point where it is being checked.
- let mut inserted_escs_len = 0;
- for esc in escapes {
- let match_count = matches.iter().take_while(|m| **m <= esc.0).count();
- // Find how many invert|normal markers appear before this escape
-
- // find the number of invert strings and number of uninvert strings that have been
- // inserted up to this point in the string
- let num_invert = match_count / 2;
- let num_normal = match_count - num_invert;
-
- // calculate the index which this escape should be re-inserted at by adding
- // its position in the stripped string to the total length of the ansi escapes
- // (both highlighting and the ones from the original string).
- // TODO: Add more docs to this
- let mut pos = if !accurate && match_count % 2 == 1 {
- // INFO: Its safe to unwrap here
- matches.get(match_count).unwrap()
- + NORMAL.len()
- + inserted_escs_len
- + (num_invert * INVERT.len())
- + (num_normal * NORMAL.len())
- } else {
- esc.0 + inserted_escs_len + (num_invert * INVERT.len()) + (num_normal * NORMAL.len())
- };
-
- if match_count % 2 == 1 {
- pos = pos.saturating_sub(1);
- }
-
- // insert the escape back in
- inverted.insert_str(pos, esc.1);
-
- // increment the length of the escapes inserted back in
- inserted_escs_len += esc.1.len();
- }
-
- inverted
+fn highlight_line_matches_ansi(line: &str, query: ®ex::Regex, _accurate: bool) -> String {
+ crate::pager::search_overlay::highlight(line, query, &ANSI_REGEX)
}
/// Highlights the search match
@@ -1307,7 +1227,7 @@
let res = highlight_line_matches(&orig, &Regex::new("test").unwrap(), false);
assert_eq!(
res.0,
- format!("this {}is a {}test{}{}", ESC, *INVERT, *NORMAL, NONE)
+ format!("this {ESC}is a {}te{NONE}{}st{}", *INVERT, *INVERT, *NORMAL)
);
}
@@ -1317,7 +1237,7 @@
let res = highlight_line_matches(&orig, &Regex::new("test").unwrap(), false);
assert_eq!(
res.0,
- format!("this is a {}test{}{ESC} again{}", *INVERT, *NORMAL, NONE)
+ format!("this is a {}te{ESC}{}st{} again{NONE}", *INVERT, *INVERT, *NORMAL)
);
}
@@ -1337,7 +1257,7 @@
let res = highlight_line_matches(&orig, &Regex::new("test").unwrap(), false);
assert_eq!(
res.0,
- format!("this is a {}test{}{ESC}{NONE} again", *INVERT, *NORMAL)
+ format!("this is a {}t{ESC}{}es{NONE}{}t{} again", *INVERT, *INVERT, *INVERT, *NORMAL)
);
}
@@ -1348,7 +1268,7 @@
assert_eq!(
res.0,
format!(
- "this {e}is a {i}test{n}{nn} again {e}yeah{nn} {i}test{n}",
+ "this {e}is a {i}te{nn}{i}st{n} again {e}yeah{nn} {i}test{n}",
e = ESC,
i = *INVERT,
n = *NORMAL,
@@ -1369,7 +1289,7 @@
assert_eq!(
res.0,
format!(
- "{i}{e}test{n}{nn} this {e}is a {i}te{NONE}st{n} again {e}yeah{nn} {i}test{n}",
+ "{e}{i}test{nn}{i}{n} this {e}is a {i}te{NONE}{i}st{n} again {e}yeah{nn} {i}test{n}",
e = ESC,
i = *INVERT,
n = *NORMAL,
@@ -1398,7 +1318,7 @@
let res = highlight_line_matches(&orig, &Regex::new("test").unwrap(), true);
assert_eq!(
res.0,
- format!("this {ESC}is a {}te{NONE}st{}", *INVERT, *NORMAL)
+ format!("this {ESC}is a {}te{NONE}{}st{}", *INVERT, *INVERT, *NORMAL)
);
}
@@ -1408,7 +1328,7 @@
let res = highlight_line_matches(&orig, &Regex::new("test").unwrap(), true);
assert_eq!(
res.0,
- format!("this is a {}te{ESC}st{} again{NONE}", *INVERT, *NORMAL)
+ format!("this is a {}te{ESC}{}st{} again{NONE}", *INVERT, *INVERT, *NORMAL)
);
}
@@ -1428,7 +1348,7 @@
let res = highlight_line_matches(&orig, &Regex::new("test").unwrap(), true);
assert_eq!(
res.0,
- format!("this is a {}t{ESC}es{NONE}t{} again", *INVERT, *NORMAL)
+ format!("this is a {}t{ESC}{}es{NONE}{}t{} again", *INVERT, *INVERT, *INVERT, *NORMAL)
);
}
@@ -1439,7 +1359,7 @@
assert_eq!(
res.0,
format!(
- "this {e}is a {i}te{nn}st{n} again {e}yeah{nn} {i}test{n}",
+ "this {e}is a {i}te{nn}{i}st{n} again {e}yeah{nn} {i}test{n}",
e = ESC,
i = *INVERT,
n = *NORMAL,
@@ -597,7 +597,7 @@
search_term: Option<&'a Regex>,
) -> impl Iterator<Item = (SearchFormattedRow<'a, 'a>, bool)> + 'a {
rows.map(move |row| {
- let is_match = search_term.is_some_and(|st| st.is_match(row.raw_row()));
+ let is_match = search_term.is_some_and(|st| search::line_matches_query(row.raw_row(), st));
(
SearchFormattedRow {
row,
@@ -427,12 +427,14 @@
};
p.reformat_display();
- p.upper_mark = *p
+ if let Some(&row) = p
.search_state
.search_idx
.iter()
.nth(p.search_state.search_mark)
- .unwrap();
+ {
+ p.upper_mark = row;
+ }
command_queue.push_back(Command::Io(IoCommand::RedrawDisplay));
command_queue.push_back(Command::Io(IoCommand::RedrawPrompt));
}