mirror of https://github.com/helix-editor/helix
write to search registers when selecting by regex
parent
3318953bf6
commit
c5a4fa78aa
|
@ -783,13 +783,15 @@ pub fn select_on_matches(
|
||||||
text: RopeSlice,
|
text: RopeSlice,
|
||||||
selection: &Selection,
|
selection: &Selection,
|
||||||
regex: &rope::Regex,
|
regex: &rope::Regex,
|
||||||
) -> Option<Selection> {
|
) -> Option<(Selection, Vec<Vec<String>>)> {
|
||||||
let mut result = SmallVec::with_capacity(selection.len());
|
let mut result = SmallVec::with_capacity(selection.len());
|
||||||
|
let mut captures = vec![vec![]; regex.captures_len()];
|
||||||
|
|
||||||
for sel in selection {
|
for sel in selection {
|
||||||
for mat in regex.find_iter(text.regex_input_at(sel.from()..sel.to())) {
|
for cap in regex.captures_iter(text.regex_input_at(sel.from()..sel.to())) {
|
||||||
// TODO: retain range direction
|
// TODO: retain range direction
|
||||||
|
|
||||||
|
let mat = cap.get_match().unwrap();
|
||||||
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());
|
||||||
|
|
||||||
|
@ -798,13 +800,20 @@ pub fn select_on_matches(
|
||||||
// These invalid matches can come from using RegEx anchors like `^`, `$`
|
// These invalid matches can come from using RegEx anchors like `^`, `$`
|
||||||
if range != Range::point(sel.to()) {
|
if range != Range::point(sel.to()) {
|
||||||
result.push(range);
|
result.push(range);
|
||||||
|
for (i, captures) in captures.iter_mut().enumerate() {
|
||||||
|
captures.push(
|
||||||
|
cap.get_group(i)
|
||||||
|
.map(|group| text.slice(group.range()).to_string())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: figure out a new primary index
|
// TODO: figure out a new primary index
|
||||||
if !result.is_empty() {
|
if !result.is_empty() {
|
||||||
return Some(Selection::new(result, 0));
|
return Some((Selection::new(result, 0), captures));
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -1099,9 +1108,9 @@ mod test {
|
||||||
let selection = Selection::single(0, r.len_chars());
|
let selection = Selection::single(0, r.len_chars());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &selection, &rope::Regex::new(r"[A-Z][a-z]*").unwrap()),
|
select_on_matches(s, &selection, &rope::Regex::new(r"[A-Z][a-z]*").unwrap()),
|
||||||
Some(Selection::new(
|
Some((
|
||||||
smallvec![Range::new(0, 6), Range::new(19, 26)],
|
Selection::new(smallvec![Range::new(0, 6), Range::new(19, 26)], 0),
|
||||||
0
|
vec![vec!["Nobody".to_string(), "Spanish".to_string()]]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1120,7 +1129,7 @@ mod test {
|
||||||
// line without ending
|
// line without ending
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &Selection::single(0, 4), &start_of_line),
|
select_on_matches(s, &Selection::single(0, 4), &start_of_line),
|
||||||
Some(Selection::single(0, 0))
|
Some((Selection::single(0, 0), vec![vec!["".to_string()]]))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &Selection::single(0, 4), &end_of_line),
|
select_on_matches(s, &Selection::single(0, 4), &end_of_line),
|
||||||
|
@ -1129,23 +1138,23 @@ mod test {
|
||||||
// line with ending
|
// line with ending
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &Selection::single(0, 5), &start_of_line),
|
select_on_matches(s, &Selection::single(0, 5), &start_of_line),
|
||||||
Some(Selection::single(0, 0))
|
Some((Selection::single(0, 0), vec![vec!["".to_string()]]))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &Selection::single(0, 5), &end_of_line),
|
select_on_matches(s, &Selection::single(0, 5), &end_of_line),
|
||||||
Some(Selection::single(4, 4))
|
Some((Selection::single(4, 4), vec![vec!["".to_string()]]))
|
||||||
);
|
);
|
||||||
// line with start of next line
|
// line with start of next line
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &Selection::single(0, 6), &start_of_line),
|
select_on_matches(s, &Selection::single(0, 6), &start_of_line),
|
||||||
Some(Selection::new(
|
Some((
|
||||||
smallvec![Range::point(0), Range::point(5)],
|
Selection::new(smallvec![Range::point(0), Range::point(5)], 0),
|
||||||
0
|
vec![vec!["".to_string(), "".to_string()]]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
select_on_matches(s, &Selection::single(0, 6), &end_of_line),
|
select_on_matches(s, &Selection::single(0, 6), &end_of_line),
|
||||||
Some(Selection::single(4, 4))
|
Some((Selection::single(4, 4), vec![vec!["".to_string()]]))
|
||||||
);
|
);
|
||||||
|
|
||||||
// multiple lines
|
// multiple lines
|
||||||
|
@ -1158,9 +1167,16 @@ mod test {
|
||||||
.build(r"^[a-z ]*$")
|
.build(r"^[a-z ]*$")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
),
|
),
|
||||||
Some(Selection::new(
|
Some((
|
||||||
smallvec![Range::point(12), Range::new(13, 30), Range::new(31, 36)],
|
Selection::new(
|
||||||
0
|
smallvec![Range::point(12), Range::new(13, 30), Range::new(31, 36)],
|
||||||
|
0
|
||||||
|
),
|
||||||
|
vec![vec![
|
||||||
|
"".to_string(),
|
||||||
|
"contains multiple".to_string(),
|
||||||
|
"lines".to_string()
|
||||||
|
]]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2012,10 +2012,11 @@ fn select_regex(cx: &mut Context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let text = doc.text().slice(..);
|
let text = doc.text().slice(..);
|
||||||
if let Some(selection) =
|
if let Some((selection, captures)) =
|
||||||
selection::select_on_matches(text, doc.selection(view.id), ®ex)
|
selection::select_on_matches(text, doc.selection(view.id), ®ex)
|
||||||
{
|
{
|
||||||
doc.set_selection(view.id, selection);
|
doc.set_selection(view.id, selection);
|
||||||
|
cx.editor.registers.write_search_results(captures);
|
||||||
} else {
|
} else {
|
||||||
cx.editor.set_error("nothing selected");
|
cx.editor.set_error("nothing selected");
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,9 @@ impl Registers {
|
||||||
pub fn write(&mut self, name: char, mut values: Vec<String>) -> Result<()> {
|
pub fn write(&mut self, name: char, mut values: Vec<String>) -> Result<()> {
|
||||||
match name {
|
match name {
|
||||||
'_' => Ok(()),
|
'_' => Ok(()),
|
||||||
'#' | '.' | '%' => Err(anyhow::anyhow!("Register {name} does not support writing")),
|
'#' | '.' | '%' | '&' | '1'..='9' => {
|
||||||
|
Err(anyhow::anyhow!("Register {name} does not support writing"))
|
||||||
|
}
|
||||||
'*' | '+' => {
|
'*' | '+' => {
|
||||||
self.clipboard_provider.load().set_contents(
|
self.clipboard_provider.load().set_contents(
|
||||||
&values.join(NATIVE_LINE_ENDING.as_str()),
|
&values.join(NATIVE_LINE_ENDING.as_str()),
|
||||||
|
@ -105,7 +107,9 @@ impl Registers {
|
||||||
pub fn push(&mut self, name: char, mut value: String) -> Result<()> {
|
pub fn push(&mut self, name: char, mut value: String) -> Result<()> {
|
||||||
match name {
|
match name {
|
||||||
'_' => Ok(()),
|
'_' => Ok(()),
|
||||||
'#' | '.' | '%' => Err(anyhow::anyhow!("Register {name} does not support pushing")),
|
'#' | '.' | '%' | '&' | '1'..='9' => {
|
||||||
|
Err(anyhow::anyhow!("Register {name} does not support pushing"))
|
||||||
|
}
|
||||||
'*' | '+' => {
|
'*' | '+' => {
|
||||||
let clipboard_type = match name {
|
let clipboard_type = match name {
|
||||||
'+' => ClipboardType::Clipboard,
|
'+' => ClipboardType::Clipboard,
|
||||||
|
@ -140,6 +144,17 @@ impl Registers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_search_results(&mut self, search_results: Vec<Vec<String>>) {
|
||||||
|
let len = search_results.len();
|
||||||
|
for (i, mut values) in search_results.into_iter().enumerate() {
|
||||||
|
values.reverse();
|
||||||
|
self.inner.insert(search_register_name(i), values);
|
||||||
|
}
|
||||||
|
for i in len..=9 {
|
||||||
|
self.inner.remove(&search_register_name(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn first<'a>(&'a self, name: char, editor: &'a Editor) -> Option<Cow<'a, str>> {
|
pub fn first<'a>(&'a self, name: char, editor: &'a Editor) -> Option<Cow<'a, str>> {
|
||||||
self.read(name, editor).and_then(|mut values| values.next())
|
self.read(name, editor).and_then(|mut values| values.next())
|
||||||
}
|
}
|
||||||
|
@ -168,6 +183,16 @@ impl Registers {
|
||||||
('%', "<document path>"),
|
('%', "<document path>"),
|
||||||
('+', "<system clipboard>"),
|
('+', "<system clipboard>"),
|
||||||
('*', "<primary clipboard>"),
|
('*', "<primary clipboard>"),
|
||||||
|
('&', "<last regex match>"),
|
||||||
|
('1', "<last regex match group 1>"),
|
||||||
|
('2', "<last regex match group 2>"),
|
||||||
|
('3', "<last regex match group 3>"),
|
||||||
|
('4', "<last regex match group 4>"),
|
||||||
|
('5', "<last regex match group 5>"),
|
||||||
|
('6', "<last regex match group 6>"),
|
||||||
|
('7', "<last regex match group 7>"),
|
||||||
|
('8', "<last regex match group 8>"),
|
||||||
|
('9', "<last regex match group 9>"),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.copied(),
|
.copied(),
|
||||||
|
@ -192,7 +217,7 @@ impl Registers {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
'_' | '#' | '.' | '%' => false,
|
'_' | '#' | '.' | '%' | '&' | '1'..='9' => false,
|
||||||
_ => self.inner.remove(&name).is_some(),
|
_ => self.inner.remove(&name).is_some(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,6 +243,14 @@ impl Registers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_register_name(i: usize) -> char {
|
||||||
|
if i == 0 {
|
||||||
|
'&'
|
||||||
|
} else {
|
||||||
|
char::from(i as u8 + b'0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_from_clipboard<'a>(
|
fn read_from_clipboard<'a>(
|
||||||
provider: &ClipboardProvider,
|
provider: &ClipboardProvider,
|
||||||
saved_values: Option<&'a Vec<String>>,
|
saved_values: Option<&'a Vec<String>>,
|
||||||
|
|
Loading…
Reference in New Issue