mirror of https://github.com/helix-editor/helix
Merge 4d374ed747
into 665ee4da22
commit
fba21800fa
|
@ -770,15 +770,28 @@ pub fn keep_or_remove_matches(
|
|||
regex: &rope::Regex,
|
||||
remove: bool,
|
||||
) -> Option<Selection> {
|
||||
|
||||
let prim_range = selection.primary();
|
||||
let mut diff = usize::MAX;
|
||||
let mut new_prim = 0;
|
||||
|
||||
let result: SmallVec<_> = selection
|
||||
.iter()
|
||||
.filter(|range| regex.is_match(text.regex_input_at(range.from()..range.to())) ^ remove)
|
||||
.enumerate()
|
||||
.map(|(idx, range)| {
|
||||
let new_diff = range.head.abs_diff(prim_range.head);
|
||||
if new_diff < diff {
|
||||
diff = new_diff;
|
||||
new_prim = idx;
|
||||
}
|
||||
range
|
||||
})
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
// TODO: figure out a new primary index
|
||||
if !result.is_empty() {
|
||||
return Some(Selection::new(result, 0));
|
||||
return Some(Selection::new(result, new_prim));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -789,45 +802,61 @@ pub fn select_on_matches(
|
|||
selection: &Selection,
|
||||
regex: &rope::Regex,
|
||||
) -> Option<Selection> {
|
||||
|
||||
let prim_range = selection.primary();
|
||||
let mut diff = usize::MAX;
|
||||
let mut new_prim = 0;
|
||||
let mut result = SmallVec::with_capacity(selection.len());
|
||||
|
||||
for sel in selection {
|
||||
for mat in regex.find_iter(text.regex_input_at(sel.from()..sel.to())) {
|
||||
// TODO: retain range direction
|
||||
|
||||
let start = text.byte_to_char(mat.start());
|
||||
let end = text.byte_to_char(mat.end());
|
||||
|
||||
let range = Range::new(start, end);
|
||||
let range = match sel.direction() {
|
||||
Direction::Forward => Range::new(start, end),
|
||||
Direction::Backward => Range::new(end, start)
|
||||
};
|
||||
|
||||
// Make sure the match is not right outside of the selection.
|
||||
// These invalid matches can come from using RegEx anchors like `^`, `$`
|
||||
if range != Range::point(sel.to()) {
|
||||
let new_diff = range.head.abs_diff(prim_range.head);
|
||||
if new_diff < diff {
|
||||
diff = new_diff;
|
||||
new_prim = result.len();
|
||||
}
|
||||
result.push(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: figure out a new primary index
|
||||
if !result.is_empty() {
|
||||
return Some(Selection::new(result, 0));
|
||||
return Some(Selection::new(result, new_prim));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn split_on_newline(text: RopeSlice, selection: &Selection) -> Selection {
|
||||
|
||||
let mut new_prim = selection.primary_index();
|
||||
let mut result = SmallVec::with_capacity(selection.len());
|
||||
|
||||
for sel in selection {
|
||||
let is_prim = *sel == selection.primary();
|
||||
|
||||
// Special case: zero-width selection.
|
||||
if sel.from() == sel.to() {
|
||||
if is_prim { new_prim = result.len() }
|
||||
result.push(*sel);
|
||||
continue;
|
||||
}
|
||||
|
||||
let saved_len = result.len();
|
||||
let sel_start = sel.from();
|
||||
let sel_end = sel.to();
|
||||
|
||||
let mut start = sel_start;
|
||||
|
||||
for line in sel.slice(text).lines() {
|
||||
|
@ -835,48 +864,70 @@ pub fn split_on_newline(text: RopeSlice, selection: &Selection) -> Selection {
|
|||
break;
|
||||
};
|
||||
let line_end = start + line.len_chars();
|
||||
// TODO: retain range direction
|
||||
result.push(Range::new(start, line_end - line_ending.len_chars()));
|
||||
let range = Range::new(start, line_end - line_ending.len_chars());
|
||||
result.push(
|
||||
if sel.direction() == Direction::Backward { range.flip() }
|
||||
else { range }
|
||||
);
|
||||
start = line_end;
|
||||
}
|
||||
|
||||
if start < sel_end {
|
||||
result.push(Range::new(start, sel_end));
|
||||
}
|
||||
|
||||
if is_prim {
|
||||
new_prim = if sel.head > sel.anchor {
|
||||
result.len() - 1
|
||||
} else { saved_len };
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: figure out a new primary index
|
||||
Selection::new(result, 0)
|
||||
Selection::new(result, new_prim)
|
||||
}
|
||||
|
||||
pub fn split_on_matches(text: RopeSlice, selection: &Selection, regex: &rope::Regex) -> Selection {
|
||||
|
||||
let mut new_prim = selection.primary_index();
|
||||
let mut result = SmallVec::with_capacity(selection.len());
|
||||
|
||||
for sel in selection {
|
||||
let is_prim = *sel == selection.primary();
|
||||
|
||||
// Special case: zero-width selection.
|
||||
if sel.from() == sel.to() {
|
||||
if is_prim { new_prim = result.len() }
|
||||
result.push(*sel);
|
||||
continue;
|
||||
}
|
||||
|
||||
let saved_len = result.len();
|
||||
let sel_start = sel.from();
|
||||
let sel_end = sel.to();
|
||||
let mut start = sel_start;
|
||||
|
||||
for mat in regex.find_iter(text.regex_input_at(sel_start..sel_end)) {
|
||||
// TODO: retain range direction
|
||||
let end = text.byte_to_char(mat.start());
|
||||
result.push(Range::new(start, end));
|
||||
let range = Range::new(start, end);
|
||||
result.push(
|
||||
if sel.direction() == Direction::Backward { range.flip() }
|
||||
else { range }
|
||||
);
|
||||
start = text.byte_to_char(mat.end());
|
||||
}
|
||||
|
||||
if start < sel_end {
|
||||
result.push(Range::new(start, sel_end));
|
||||
}
|
||||
|
||||
if is_prim && result.len() > saved_len {
|
||||
new_prim = if sel.head > sel.anchor {
|
||||
result.len() - 1
|
||||
} else { saved_len };
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: figure out a new primary index
|
||||
Selection::new(result, 0)
|
||||
Selection::new(result, new_prim)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1106,7 +1157,7 @@ mod test {
|
|||
select_on_matches(s, &selection, &rope::Regex::new(r"[A-Z][a-z]*").unwrap()),
|
||||
Some(Selection::new(
|
||||
smallvec![Range::new(0, 6), Range::new(19, 26)],
|
||||
0
|
||||
1
|
||||
))
|
||||
);
|
||||
|
||||
|
@ -1145,7 +1196,7 @@ mod test {
|
|||
select_on_matches(s, &Selection::single(0, 6), &start_of_line),
|
||||
Some(Selection::new(
|
||||
smallvec![Range::point(0), Range::point(5)],
|
||||
0
|
||||
1
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -1165,7 +1216,7 @@ mod test {
|
|||
),
|
||||
Some(Selection::new(
|
||||
smallvec![Range::point(12), Range::new(13, 30), Range::new(31, 36)],
|
||||
0
|
||||
2
|
||||
))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1973,12 +1973,88 @@ fn page_cursor_half_down(cx: &mut Context) {
|
|||
scroll(cx, offset, Direction::Forward, true);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn copy_selection_on_visual_line(cx: &mut Context, direction: Direction) {
|
||||
|
||||
let count = cx.count();
|
||||
let (view, doc) = current!(cx.editor);
|
||||
let text = doc.text().slice(..);
|
||||
let selection = doc.selection(view.id);
|
||||
let text_fmt = doc.text_format(view.inner_area(doc).width, None);
|
||||
let mut annotations = view.text_annotations(doc, None);
|
||||
annotations.clear_line_annotations();
|
||||
|
||||
let mut primary_idx = selection.primary_index();
|
||||
let mut new_ranges = SmallVec::with_capacity(selection.len() * (count + 1));
|
||||
new_ranges.extend_from_slice(selection.ranges());
|
||||
//copy the selection to the relative line number
|
||||
let to_relative_line_number = count > 1;
|
||||
|
||||
for range in selection.iter() {
|
||||
let is_primary = *range == selection.primary();
|
||||
|
||||
// The range is always head exclusive
|
||||
let (head, anchor) =
|
||||
if range.anchor < range.head { (range.head - 1, range.anchor ) }
|
||||
else { (range.head , range.anchor.saturating_sub(1)) };
|
||||
let min_idx = std::cmp::min(head, anchor);
|
||||
|
||||
let (head_pos , _) = visual_offset_from_block(
|
||||
text, min_idx, head , &text_fmt, &annotations);
|
||||
let (anchor_pos, _) = visual_offset_from_block(
|
||||
text, min_idx, anchor, &text_fmt, &annotations);
|
||||
|
||||
let height =
|
||||
std::cmp::max(head_pos.row, anchor_pos.row)
|
||||
- std::cmp::min(head_pos.row, anchor_pos.row)
|
||||
+ 1;
|
||||
|
||||
let mut i = 0;
|
||||
let mut step = 0;
|
||||
while step < count {
|
||||
|
||||
use Direction::*;
|
||||
i += match direction { Forward => 1, Backward => -1, };
|
||||
let offset = i * height as isize;
|
||||
|
||||
let (new_head , _) = char_idx_at_visual_offset(
|
||||
text, head , offset, head_pos.col , &text_fmt, &annotations);
|
||||
let (new_anchor, _) = char_idx_at_visual_offset(
|
||||
text, anchor, offset, anchor_pos.col, &text_fmt, &annotations);
|
||||
|
||||
let (Position { col: new_head_col , ..}, _) = visual_offset_from_block(
|
||||
text, new_head , new_head , &text_fmt, &annotations);
|
||||
let (Position { col: new_anchor_col, ..}, _) = visual_offset_from_block(
|
||||
text, new_anchor, new_anchor, &text_fmt, &annotations);
|
||||
|
||||
// check the bottom doc boundary
|
||||
if new_head >= text.len_chars()
|
||||
|| new_anchor >= text.len_chars() { break }
|
||||
|
||||
// skip lines that are too short
|
||||
if head_pos.col == new_head_col && anchor_pos.col == new_anchor_col {
|
||||
new_ranges.push(
|
||||
Range::point(new_anchor)
|
||||
.put_cursor(text, new_head, true) );
|
||||
if is_primary { primary_idx = new_ranges.len() - 1 }
|
||||
if ! to_relative_line_number { step += 1 }
|
||||
}
|
||||
// always increment if `count` > 1
|
||||
if to_relative_line_number { step += 1 }
|
||||
|
||||
// check the top doc boundary
|
||||
if new_head == 0
|
||||
&& new_anchor == 0 { break }
|
||||
} }
|
||||
|
||||
drop(annotations);
|
||||
doc.set_selection(view.id, Selection::new(new_ranges, primary_idx))
|
||||
}
|
||||
|
||||
#[deprecated = "Doesn't account for softwrap or decorations, use copy_selection_on_visual_line instead"]
|
||||
#[allow(dead_code, deprecated)]
|
||||
// currently uses the deprecated `visual_coords_at_pos`/`pos_at_visual_coords` functions
|
||||
// as this function ignores softwrapping (and virtual text) and instead only cares
|
||||
// about "text visual position"
|
||||
//
|
||||
// TODO: implement a variant of that uses visual lines and respects virtual text
|
||||
fn copy_selection_on_line(cx: &mut Context, direction: Direction) {
|
||||
use helix_core::{pos_at_visual_coords, visual_coords_at_pos};
|
||||
|
||||
|
@ -2061,11 +2137,11 @@ fn copy_selection_on_line(cx: &mut Context, direction: Direction) {
|
|||
}
|
||||
|
||||
fn copy_selection_on_prev_line(cx: &mut Context) {
|
||||
copy_selection_on_line(cx, Direction::Backward)
|
||||
copy_selection_on_visual_line(cx, Direction::Backward)
|
||||
}
|
||||
|
||||
fn copy_selection_on_next_line(cx: &mut Context) {
|
||||
copy_selection_on_line(cx, Direction::Forward)
|
||||
copy_selection_on_visual_line(cx, Direction::Forward)
|
||||
}
|
||||
|
||||
fn select_all(cx: &mut Context) {
|
||||
|
|
Loading…
Reference in New Issue