mirror of
https://github.com/helix-editor/helix.git
synced 2025-01-19 13:37:06 +04:00
N as extend with search (for now, N should be search_prev).
This commit is contained in:
parent
35b4fe4cd0
commit
73f4abbb37
@ -159,6 +159,12 @@ pub fn into_single(self) -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add(mut self, range: Range) -> Self {
|
||||||
|
let index = self.ranges.len();
|
||||||
|
self.ranges.push(range);
|
||||||
|
|
||||||
|
Self::normalize(self.ranges, index)
|
||||||
|
}
|
||||||
// add_range // push
|
// add_range // push
|
||||||
// replace_range
|
// replace_range
|
||||||
|
|
||||||
@ -204,53 +210,53 @@ pub fn point(pos: usize) -> Self {
|
|||||||
Self::single(pos, pos)
|
Self::single(pos, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn normalize(mut ranges: SmallVec<[Range; 1]>, mut primary_index: usize) -> Selection {
|
||||||
|
let primary = ranges[primary_index];
|
||||||
|
ranges.sort_unstable_by_key(Range::from);
|
||||||
|
primary_index = ranges.iter().position(|&range| range == primary).unwrap();
|
||||||
|
|
||||||
|
let mut result = SmallVec::with_capacity(ranges.len()); // approx
|
||||||
|
|
||||||
|
// TODO: we could do with one vec by removing elements as we mutate
|
||||||
|
|
||||||
|
for (i, range) in ranges.into_iter().enumerate() {
|
||||||
|
// if previous value exists
|
||||||
|
if let Some(prev) = result.last_mut() {
|
||||||
|
// and we overlap it
|
||||||
|
if range.overlaps(prev) {
|
||||||
|
let from = prev.from();
|
||||||
|
let to = std::cmp::max(range.to(), prev.to());
|
||||||
|
|
||||||
|
if i <= primary_index {
|
||||||
|
primary_index -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge into previous
|
||||||
|
if range.anchor > range.head {
|
||||||
|
prev.anchor = to;
|
||||||
|
prev.head = from;
|
||||||
|
} else {
|
||||||
|
prev.anchor = from;
|
||||||
|
prev.head = to;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection {
|
||||||
|
ranges: result,
|
||||||
|
primary_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: consume an iterator or a vec to reduce allocations?
|
// TODO: consume an iterator or a vec to reduce allocations?
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
|
pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
|
||||||
assert!(!ranges.is_empty());
|
assert!(!ranges.is_empty());
|
||||||
|
|
||||||
fn normalize(mut ranges: SmallVec<[Range; 1]>, mut primary_index: usize) -> Selection {
|
|
||||||
let primary = ranges[primary_index];
|
|
||||||
ranges.sort_unstable_by_key(Range::from);
|
|
||||||
primary_index = ranges.iter().position(|&range| range == primary).unwrap();
|
|
||||||
|
|
||||||
let mut result = SmallVec::with_capacity(ranges.len()); // approx
|
|
||||||
|
|
||||||
// TODO: we could do with one vec by removing elements as we mutate
|
|
||||||
|
|
||||||
for (i, range) in ranges.into_iter().enumerate() {
|
|
||||||
// if previous value exists
|
|
||||||
if let Some(prev) = result.last_mut() {
|
|
||||||
// and we overlap it
|
|
||||||
if range.overlaps(prev) {
|
|
||||||
let from = prev.from();
|
|
||||||
let to = std::cmp::max(range.to(), prev.to());
|
|
||||||
|
|
||||||
if i <= primary_index {
|
|
||||||
primary_index -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge into previous
|
|
||||||
if range.anchor > range.head {
|
|
||||||
prev.anchor = to;
|
|
||||||
prev.head = from;
|
|
||||||
} else {
|
|
||||||
prev.anchor = from;
|
|
||||||
prev.head = to;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
Selection {
|
|
||||||
ranges: result,
|
|
||||||
primary_index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fast path for a single selection (cursor)
|
// fast path for a single selection (cursor)
|
||||||
if ranges.len() == 1 {
|
if ranges.len() == 1 {
|
||||||
return Self {
|
return Self {
|
||||||
@ -260,7 +266,7 @@ pub fn point(pos: usize) -> Self {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: only normalize if needed (any ranges out of order)
|
// TODO: only normalize if needed (any ranges out of order)
|
||||||
normalize(ranges, primary_index)
|
Self::normalize(ranges, primary_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a closure and maps each selection over the closure.
|
/// Takes a closure and maps each selection over the closure.
|
||||||
|
@ -608,9 +608,10 @@ pub fn split_selection_on_newline(cx: &mut Context) {
|
|||||||
// I'd probably collect all the matches right now and store the current index. The cache needs
|
// I'd probably collect all the matches right now and store the current index. The cache needs
|
||||||
// wiping if input happens.
|
// wiping if input happens.
|
||||||
|
|
||||||
fn _search(doc: &mut Document, view_id: ViewId, contents: &str, regex: &Regex) {
|
fn _search(doc: &mut Document, view_id: ViewId, contents: &str, regex: &Regex, extend: bool) {
|
||||||
let text = doc.text();
|
let text = doc.text();
|
||||||
let start = doc.selection(view_id).cursor();
|
let selection = doc.selection(view_id);
|
||||||
|
let start = selection.cursor();
|
||||||
|
|
||||||
// use find_at to find the next match after the cursor, loop around the end
|
// use find_at to find the next match after the cursor, loop around the end
|
||||||
let mat = regex
|
let mat = regex
|
||||||
@ -619,7 +620,13 @@ fn _search(doc: &mut Document, view_id: ViewId, contents: &str, regex: &Regex) {
|
|||||||
if let Some(mat) = mat {
|
if let Some(mat) = mat {
|
||||||
let start = text.byte_to_char(mat.start());
|
let start = text.byte_to_char(mat.start());
|
||||||
let end = text.byte_to_char(mat.end());
|
let end = text.byte_to_char(mat.end());
|
||||||
let selection = Selection::single(start, end - 1);
|
|
||||||
|
let selection = if extend {
|
||||||
|
selection.clone().add(Range::new(start, end - 1))
|
||||||
|
} else {
|
||||||
|
Selection::single(start, end - 1)
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: (first_match, regex) stuff in register?
|
// TODO: (first_match, regex) stuff in register?
|
||||||
doc.set_selection(view_id, selection);
|
doc.set_selection(view_id, selection);
|
||||||
};
|
};
|
||||||
@ -639,7 +646,7 @@ pub fn search(cx: &mut Context) {
|
|||||||
let prompt = ui::regex_prompt(cx, "search:".to_string(), move |doc, regex| {
|
let prompt = ui::regex_prompt(cx, "search:".to_string(), move |doc, regex| {
|
||||||
let text = doc.text();
|
let text = doc.text();
|
||||||
let start = doc.selection(view_id).cursor();
|
let start = doc.selection(view_id).cursor();
|
||||||
_search(doc, view_id, &contents, ®ex);
|
_search(doc, view_id, &contents, ®ex, false);
|
||||||
|
|
||||||
// TODO: only store on enter (accept), not update
|
// TODO: only store on enter (accept), not update
|
||||||
register::set('\\', vec![regex.as_str().to_string()]);
|
register::set('\\', vec![regex.as_str().to_string()]);
|
||||||
@ -648,17 +655,25 @@ pub fn search(cx: &mut Context) {
|
|||||||
cx.push_layer(Box::new(prompt));
|
cx.push_layer(Box::new(prompt));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_next(cx: &mut Context) {
|
pub fn _search_next(cx: &mut Context, extend: bool) {
|
||||||
if let Some(query) = register::get('\\') {
|
if let Some(query) = register::get('\\') {
|
||||||
let query = query.first().unwrap();
|
let query = query.first().unwrap();
|
||||||
let view_id = cx.view_id;
|
let view_id = cx.view_id;
|
||||||
let doc = cx.doc();
|
let doc = cx.doc();
|
||||||
let contents = doc.text().slice(..).to_string();
|
let contents = doc.text().slice(..).to_string();
|
||||||
let regex = Regex::new(&query).unwrap();
|
let regex = Regex::new(&query).unwrap();
|
||||||
_search(doc, view_id, &contents, ®ex);
|
_search(doc, view_id, &contents, ®ex, extend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn search_next(cx: &mut Context) {
|
||||||
|
_search_next(cx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend_search_next(cx: &mut Context) {
|
||||||
|
_search_next(cx, true);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_selection(cx: &mut Context) {
|
pub fn search_selection(cx: &mut Context) {
|
||||||
let (view, doc) = cx.current();
|
let (view, doc) = cx.current();
|
||||||
let contents = doc.text().slice(..);
|
let contents = doc.text().slice(..);
|
||||||
|
@ -199,6 +199,7 @@ pub fn default() -> Keymaps {
|
|||||||
key!('/') => commands::search,
|
key!('/') => commands::search,
|
||||||
// ? for search_reverse
|
// ? for search_reverse
|
||||||
key!('n') => commands::search_next,
|
key!('n') => commands::search_next,
|
||||||
|
shift!('N') => commands::extend_search_next,
|
||||||
// N for search_prev
|
// N for search_prev
|
||||||
key!('*') => commands::search_selection,
|
key!('*') => commands::search_selection,
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ pub fn render_view(
|
|||||||
view.area.width - OFFSET,
|
view.area.width - OFFSET,
|
||||||
view.area.height.saturating_sub(1),
|
view.area.height.saturating_sub(1),
|
||||||
); // - 1 for statusline
|
); // - 1 for statusline
|
||||||
|
|
||||||
self.render_buffer(doc, view, area, surface, theme, is_focused);
|
self.render_buffer(doc, view, area, surface, theme, is_focused);
|
||||||
|
|
||||||
// if we're not at the edge of the screen, draw a right border
|
// if we're not at the edge of the screen, draw a right border
|
||||||
|
Loading…
Reference in New Issue
Block a user