use super::ops::shift_cursor_for_edit;
use super::{Buffer, Cursor, Editor, char_to_byte};
impl Editor {
pub fn surround_wrap(
&mut self,
buf: &mut Buffer,
open: &str,
close: &str,
from: Cursor,
to: Cursor,
) {
let (lo, hi) = order(from, to);
if lo == hi {
return;
}
let open_chars = open.chars().count();
let close_chars = close.chars().count();
if hi.row < buf.lines.len() {
let hi_byte = char_to_byte(&buf.lines[hi.row], hi.col);
buf.lines[hi.row].insert_str(hi_byte, close);
self.for_each_cursor(|c| shift_cursor_for_edit(c, hi.row, hi.col, 0, close_chars));
}
let lo_byte = char_to_byte(&buf.lines[lo.row], lo.col);
buf.lines[lo.row].insert_str(lo_byte, open);
self.for_each_cursor(|c| shift_cursor_for_edit(c, lo.row, lo.col, 0, open_chars));
self.clamp_col(buf, false);
buf.touch();
}
pub fn surround_strip(&mut self, buf: &mut Buffer, lo: Cursor, hi: Cursor) {
let (lo, hi) = order(lo, hi);
if lo == hi || hi.col == 0 && hi.row == lo.row {
return;
}
let close_col = if hi.col == 0 {
return;
} else {
hi.col - 1
};
delete_one_char(self, buf, hi.row, close_col);
delete_one_char(self, buf, lo.row, lo.col);
self.clamp_col(buf, false);
buf.touch();
}
pub fn surround_replace(
&mut self,
buf: &mut Buffer,
lo: Cursor,
hi: Cursor,
new_open: &str,
new_close: &str,
) {
let (lo, hi) = order(lo, hi);
if lo == hi {
return;
}
let close_col = if hi.col == 0 { return } else { hi.col - 1 };
replace_one_char(self, buf, hi.row, close_col, new_close);
replace_one_char(self, buf, lo.row, lo.col, new_open);
self.clamp_col(buf, false);
buf.touch();
}
}
fn delete_one_char(ed: &mut Editor, buf: &mut Buffer, row: usize, col: usize) {
let line = &mut buf.lines[row];
let nchars = line.chars().count();
if col >= nchars {
return;
}
let start = char_to_byte(line, col);
let end = char_to_byte(line, col + 1);
line.replace_range(start..end, "");
ed.for_each_cursor(|c| shift_cursor_for_edit(c, row, col, 1, 0));
}
fn replace_one_char(ed: &mut Editor, buf: &mut Buffer, row: usize, col: usize, with: &str) {
let line = &mut buf.lines[row];
let nchars = line.chars().count();
if col >= nchars {
return;
}
let start = char_to_byte(line, col);
let end = char_to_byte(line, col + 1);
line.replace_range(start..end, with);
let inserted = with.chars().count();
ed.for_each_cursor(|c| shift_cursor_for_edit(c, row, col, 1, inserted));
}
fn order(a: Cursor, b: Cursor) -> (Cursor, Cursor) {
if (a.row, a.col) <= (b.row, b.col) {
(a, b)
} else {
(b, a)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn buf_of(lines: &[&str]) -> super::super::Ed {
let mut b = super::super::Ed::new();
b.buffer.lines = lines.iter().map(|s| s.to_string()).collect();
b.cursor = Cursor { row: 0, col: 0 };
b
}
#[test]
fn wrap_inline_with_quotes() {
let mut b = buf_of(&["foo bar"]);
b.surround_wrap(
"\"",
"\"",
Cursor { row: 0, col: 0 },
Cursor { row: 0, col: 3 },
);
assert_eq!(b.buffer.lines[0], "\"foo\" bar");
}
#[test]
fn wrap_accepts_multi_char_delimiters() {
let mut b = buf_of(&["foo bar"]);
b.surround_wrap(
"/*",
"*/",
Cursor { row: 0, col: 0 },
Cursor { row: 0, col: 3 },
);
assert_eq!(b.buffer.lines[0], "/*foo*/ bar");
}
#[test]
fn strip_quotes() {
let mut b = buf_of(&["\"foo\""]);
b.surround_strip(Cursor { row: 0, col: 0 }, Cursor { row: 0, col: 5 });
assert_eq!(b.buffer.lines[0], "foo");
}
#[test]
fn replace_quotes_with_parens_tight() {
let mut b = buf_of(&["\"foo\""]);
b.surround_replace(
Cursor { row: 0, col: 0 },
Cursor { row: 0, col: 5 },
"(",
")",
);
assert_eq!(b.buffer.lines[0], "(foo)");
}
#[test]
fn replace_accepts_multi_char_delimiters() {
let mut b = buf_of(&["\"foo\""]);
b.surround_replace(
Cursor { row: 0, col: 0 },
Cursor { row: 0, col: 5 },
"/*",
"*/",
);
assert_eq!(b.buffer.lines[0], "/*foo*/");
}
#[test]
fn wrap_multi_row_keeps_content() {
let mut b = buf_of(&["foo", "bar"]);
b.surround_wrap(
"/*",
"*/",
Cursor { row: 0, col: 0 },
Cursor { row: 1, col: 3 },
);
assert_eq!(b.buffer.lines[0], "/*foo");
assert_eq!(b.buffer.lines[1], "bar*/");
}
#[test]
fn cursor_shifts_after_wrap() {
let mut b = buf_of(&["foo bar"]);
b.cursor = Cursor { row: 0, col: 1 }; b.surround_wrap(
"\"",
"\"",
Cursor { row: 0, col: 0 },
Cursor { row: 0, col: 3 },
);
assert_eq!(b.cursor, Cursor { row: 0, col: 2 });
}
}