Add option to limit the search match counter to a max.

The new option `max-matches` can be set to `"none"` or an integer that
specifies that maximum number of matches. It's set by default to `100`.
pull/11700/head
Luis Useche 2 months ago
parent 6b8ef632ea
commit 937a79d4b2

@ -242,6 +242,7 @@ Search specific options.
|--|--|---------|
| `smart-case` | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | `true` |
| `wrap-around`| Whether the search should wrap after depleting the matches | `true` |
| `max-matches`| Maximum number of matches that will be counted for the denominator of `search-position`. Possible values are integers or `"none"` for no limit. | `100` |
### `[editor.whitespace]` Section

@ -39,8 +39,8 @@ use helix_core::{
RopeReader, RopeSlice, Selection, SmallVec, Syntax, Tendril, Transaction,
};
use helix_view::{
document::{FormatterError, Mode, SearchMatch, SCRATCH_BUFFER_NAME},
editor::Action,
document::{FormatterError, Mode, SearchMatch, SearchMatchLimit, SCRATCH_BUFFER_NAME},
editor::{Action, OptionToml, SearchConfig},
info::Info,
input::KeyEvent,
keyboard::KeyCode,
@ -2071,12 +2071,13 @@ fn search_impl(
movement: Movement,
direction: Direction,
scrolloff: usize,
wrap_around: bool,
search_config: &SearchConfig,
show_warnings: bool,
) {
let (view, doc) = current!(editor);
let text = doc.text().slice(..);
let selection = doc.selection(view.id);
let wrap_around = search_config.wrap_around;
// Get the right side of the primary block cursor for forward search, or the
// grapheme before the start of the selection for reverse search.
@ -2147,9 +2148,38 @@ fn search_impl(
}
let (idx, mat) = mat.unwrap();
let last_idx = match all_matches.last() {
None => idx,
Some((last_idx, _)) => last_idx,
let match_count = match search_config.max_matches {
OptionToml::None => match all_matches.last() {
None => SearchMatchLimit::Limitless(idx + 1),
Some((last_idx, _)) => SearchMatchLimit::Limitless(last_idx + 1),
},
OptionToml::Some(max) => {
if all_matches.peek().is_none() {
// Case #1: If we consumed `all_matches`, it means that we have
// the last match in `mat`. Hence, we know exactly how many
// matches there are. To respect the `max` option, if it goes
// beyong `max`, we limit the counter to `max`.
if idx + 1 > max {
SearchMatchLimit::Limited(max)
} else {
SearchMatchLimit::Limitless(idx + 1)
}
} else {
// Case #2: If we are here, we have at least one match in
// `all_matches`. We need to find the last match that's
// less than `max`. If we find it, we simply return it as a
// `Limitless` denominator because it doesn't go beyong the
// user option. The two remaining cases are `last_idx == max`
// and `None` (when the remaining matches are all greater than
// `max`) for which we return a `Limited` denominator.
match all_matches.take_while(|(idx, _)| idx <= &max).last() {
Some((last_idx, _)) if last_idx < max => {
SearchMatchLimit::Limitless(last_idx + 1)
}
_ => SearchMatchLimit::Limited(max),
}
}
}
};
// Move the cursor to the match.
@ -2185,7 +2215,7 @@ fn search_impl(
view.id,
SearchMatch {
idx: idx + 1,
count: last_idx + 1,
count: match_count,
},
);
}
@ -2211,7 +2241,6 @@ fn searcher(cx: &mut Context, direction: Direction) {
let reg = cx.register.unwrap_or('/');
let config = cx.editor.config();
let scrolloff = config.scrolloff;
let wrap_around = config.search.wrap_around;
let movement = if cx.editor.mode() == Mode::Select {
Movement::Extend
} else {
@ -2244,7 +2273,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
movement,
direction,
scrolloff,
wrap_around,
&config.search,
false,
);
},
@ -2265,7 +2294,6 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
} else {
false
};
let wrap_around = search_config.wrap_around;
if let Ok(regex) = rope::RegexBuilder::new()
.syntax(
rope::Config::new()
@ -2281,7 +2309,7 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
movement,
direction,
scrolloff,
wrap_around,
search_config,
true,
);
}

@ -2,7 +2,7 @@ use helix_core::{coords_at_pos, encoding, Position};
use helix_lsp::lsp::DiagnosticSeverity;
use helix_view::document::DEFAULT_LANGUAGE_NAME;
use helix_view::{
document::{Mode, SearchMatch, SCRATCH_BUFFER_NAME},
document::{Mode, SearchMatch, SearchMatchLimit, SCRATCH_BUFFER_NAME},
graphics::Rect,
theme::Style,
Document, Editor, View,
@ -538,6 +538,10 @@ where
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
{
if let Some(SearchMatch { idx, count }) = context.doc.get_last_search_match(context.view.id) {
write(context, format!(" [{}/{}] ", idx, count), None);
let count_str = match count {
SearchMatchLimit::Limitless(count) => format!("{}", count),
SearchMatchLimit::Limited(max) => format!(">{}", max),
};
write(context, format!(" [{}/{}] ", idx, count_str), None);
}
}

@ -130,12 +130,18 @@ pub enum DocumentOpenError {
IoError(#[from] io::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum SearchMatchLimit {
Limitless(usize),
Limited(usize),
}
#[derive(Debug, Clone, Copy)]
pub struct SearchMatch {
/// nth match from the beginning of the document.
pub idx: usize,
/// Total number of matches in the document.
pub count: usize,
pub count: SearchMatchLimit,
}
pub struct Document {

@ -452,6 +452,14 @@ impl Default for LspConfig {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub enum OptionToml<T> {
None,
#[serde(untagged)]
Some(T),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
pub struct SearchConfig {
@ -459,6 +467,8 @@ pub struct SearchConfig {
pub smart_case: bool,
/// Whether the search should wrap after depleting the matches. Default to true.
pub wrap_around: bool,
/// Maximum number of counted matches when searching in a document. `None` means no limit. Default to 100.
pub max_matches: OptionToml<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -994,6 +1004,7 @@ impl Default for SearchConfig {
Self {
wrap_around: true,
smart_case: true,
max_matches: OptionToml::Some(100),
}
}
}

Loading…
Cancel
Save