mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-25 02:46:17 +04:00
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`.
This commit is contained in:
parent
6b8ef632ea
commit
937a79d4b2
@ -242,6 +242,7 @@ ### `[editor.search]` Section
|
|||||||
|--|--|---------|
|
|--|--|---------|
|
||||||
| `smart-case` | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | `true` |
|
| `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` |
|
| `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
|
### `[editor.whitespace]` Section
|
||||||
|
|
||||||
|
@ -39,8 +39,8 @@
|
|||||||
RopeReader, RopeSlice, Selection, SmallVec, Syntax, Tendril, Transaction,
|
RopeReader, RopeSlice, Selection, SmallVec, Syntax, Tendril, Transaction,
|
||||||
};
|
};
|
||||||
use helix_view::{
|
use helix_view::{
|
||||||
document::{FormatterError, Mode, SearchMatch, SCRATCH_BUFFER_NAME},
|
document::{FormatterError, Mode, SearchMatch, SearchMatchLimit, SCRATCH_BUFFER_NAME},
|
||||||
editor::Action,
|
editor::{Action, OptionToml, SearchConfig},
|
||||||
info::Info,
|
info::Info,
|
||||||
input::KeyEvent,
|
input::KeyEvent,
|
||||||
keyboard::KeyCode,
|
keyboard::KeyCode,
|
||||||
@ -2071,12 +2071,13 @@ fn search_impl(
|
|||||||
movement: Movement,
|
movement: Movement,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
scrolloff: usize,
|
scrolloff: usize,
|
||||||
wrap_around: bool,
|
search_config: &SearchConfig,
|
||||||
show_warnings: bool,
|
show_warnings: bool,
|
||||||
) {
|
) {
|
||||||
let (view, doc) = current!(editor);
|
let (view, doc) = current!(editor);
|
||||||
let text = doc.text().slice(..);
|
let text = doc.text().slice(..);
|
||||||
let selection = doc.selection(view.id);
|
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
|
// Get the right side of the primary block cursor for forward search, or the
|
||||||
// grapheme before the start of the selection for reverse search.
|
// grapheme before the start of the selection for reverse search.
|
||||||
@ -2147,9 +2148,38 @@ fn search_impl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (idx, mat) = mat.unwrap();
|
let (idx, mat) = mat.unwrap();
|
||||||
let last_idx = match all_matches.last() {
|
let match_count = match search_config.max_matches {
|
||||||
None => idx,
|
OptionToml::None => match all_matches.last() {
|
||||||
Some((last_idx, _)) => last_idx,
|
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.
|
// Move the cursor to the match.
|
||||||
@ -2185,7 +2215,7 @@ fn search_impl(
|
|||||||
view.id,
|
view.id,
|
||||||
SearchMatch {
|
SearchMatch {
|
||||||
idx: idx + 1,
|
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 reg = cx.register.unwrap_or('/');
|
||||||
let config = cx.editor.config();
|
let config = cx.editor.config();
|
||||||
let scrolloff = config.scrolloff;
|
let scrolloff = config.scrolloff;
|
||||||
let wrap_around = config.search.wrap_around;
|
|
||||||
let movement = if cx.editor.mode() == Mode::Select {
|
let movement = if cx.editor.mode() == Mode::Select {
|
||||||
Movement::Extend
|
Movement::Extend
|
||||||
} else {
|
} else {
|
||||||
@ -2244,7 +2273,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
|
|||||||
movement,
|
movement,
|
||||||
direction,
|
direction,
|
||||||
scrolloff,
|
scrolloff,
|
||||||
wrap_around,
|
&config.search,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -2265,7 +2294,6 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
|
|||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
let wrap_around = search_config.wrap_around;
|
|
||||||
if let Ok(regex) = rope::RegexBuilder::new()
|
if let Ok(regex) = rope::RegexBuilder::new()
|
||||||
.syntax(
|
.syntax(
|
||||||
rope::Config::new()
|
rope::Config::new()
|
||||||
@ -2281,7 +2309,7 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir
|
|||||||
movement,
|
movement,
|
||||||
direction,
|
direction,
|
||||||
scrolloff,
|
scrolloff,
|
||||||
wrap_around,
|
search_config,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use helix_lsp::lsp::DiagnosticSeverity;
|
use helix_lsp::lsp::DiagnosticSeverity;
|
||||||
use helix_view::document::DEFAULT_LANGUAGE_NAME;
|
use helix_view::document::DEFAULT_LANGUAGE_NAME;
|
||||||
use helix_view::{
|
use helix_view::{
|
||||||
document::{Mode, SearchMatch, SCRATCH_BUFFER_NAME},
|
document::{Mode, SearchMatch, SearchMatchLimit, SCRATCH_BUFFER_NAME},
|
||||||
graphics::Rect,
|
graphics::Rect,
|
||||||
theme::Style,
|
theme::Style,
|
||||||
Document, Editor, View,
|
Document, Editor, View,
|
||||||
@ -538,6 +538,10 @@ fn render_search_position<F>(context: &mut RenderContext, write: F)
|
|||||||
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
|
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
|
||||||
{
|
{
|
||||||
if let Some(SearchMatch { idx, count }) = context.doc.get_last_search_match(context.view.id) {
|
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),
|
IoError(#[from] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum SearchMatchLimit {
|
||||||
|
Limitless(usize),
|
||||||
|
Limited(usize),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct SearchMatch {
|
pub struct SearchMatch {
|
||||||
/// nth match from the beginning of the document.
|
/// nth match from the beginning of the document.
|
||||||
pub idx: usize,
|
pub idx: usize,
|
||||||
/// Total number of matches in the document.
|
/// Total number of matches in the document.
|
||||||
pub count: usize,
|
pub count: SearchMatchLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Document {
|
pub struct Document {
|
||||||
|
@ -452,6 +452,14 @@ fn default() -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
||||||
pub struct SearchConfig {
|
pub struct SearchConfig {
|
||||||
@ -459,6 +467,8 @@ pub struct SearchConfig {
|
|||||||
pub smart_case: bool,
|
pub smart_case: bool,
|
||||||
/// Whether the search should wrap after depleting the matches. Default to true.
|
/// Whether the search should wrap after depleting the matches. Default to true.
|
||||||
pub wrap_around: bool,
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@ -994,6 +1004,7 @@ fn default() -> Self {
|
|||||||
Self {
|
Self {
|
||||||
wrap_around: true,
|
wrap_around: true,
|
||||||
smart_case: true,
|
smart_case: true,
|
||||||
|
max_matches: OptionToml::Some(100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user