|
|
|
@ -10,10 +10,11 @@ use helix_core::{
|
|
|
|
|
object, pos_at_coords,
|
|
|
|
|
regex::{self, Regex, RegexBuilder},
|
|
|
|
|
register::Register,
|
|
|
|
|
search, selection, surround, textobject, LineEnding, Position, Range, Rope, RopeGraphemes,
|
|
|
|
|
RopeSlice, Selection, SmallVec, Tendril, Transaction,
|
|
|
|
|
search, selection, surround, textobject,
|
|
|
|
|
unicode::width::UnicodeWidthChar,
|
|
|
|
|
LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
|
|
|
|
|
Transaction,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use helix_view::{
|
|
|
|
|
clipboard::ClipboardType,
|
|
|
|
|
document::{Mode, SCRATCH_BUFFER_NAME},
|
|
|
|
@ -4014,19 +4015,70 @@ pub mod insert {
|
|
|
|
|
doc.apply(&transaction, view.id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: handle indent-aware delete
|
|
|
|
|
pub fn delete_char_backward(cx: &mut Context) {
|
|
|
|
|
let count = cx.count();
|
|
|
|
|
let (view, doc) = current!(cx.editor);
|
|
|
|
|
let text = doc.text().slice(..);
|
|
|
|
|
let indent_unit = doc.indent_unit();
|
|
|
|
|
let tab_size = doc.tab_width();
|
|
|
|
|
|
|
|
|
|
let transaction =
|
|
|
|
|
Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
|
|
|
|
|
let pos = range.cursor(text);
|
|
|
|
|
let line_start_pos = text.line_to_char(range.cursor_line(text));
|
|
|
|
|
// considier to delete by indent level if all characters before `pos` are indent units.
|
|
|
|
|
let fragment = Cow::from(text.slice(line_start_pos..pos));
|
|
|
|
|
if !fragment.is_empty() && fragment.chars().all(|ch| ch.is_whitespace()) {
|
|
|
|
|
if text.get_char(pos.saturating_sub(1)) == Some('\t') {
|
|
|
|
|
// fast path, delete one char
|
|
|
|
|
(
|
|
|
|
|
graphemes::nth_prev_grapheme_boundary(text, pos, 1),
|
|
|
|
|
pos,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
let unit_len = indent_unit.chars().count();
|
|
|
|
|
// NOTE: indent_unit always contains 'only spaces' or 'only tab' according to `IndentStyle` definition.
|
|
|
|
|
let unit_size = if indent_unit.starts_with('\t') {
|
|
|
|
|
tab_size * unit_len
|
|
|
|
|
} else {
|
|
|
|
|
unit_len
|
|
|
|
|
};
|
|
|
|
|
let width: usize = fragment
|
|
|
|
|
.chars()
|
|
|
|
|
.map(|ch| {
|
|
|
|
|
if ch == '\t' {
|
|
|
|
|
tab_size
|
|
|
|
|
} else {
|
|
|
|
|
// it can be none if it still meet control characters other than '\t'
|
|
|
|
|
// here just set the width to 1 (or some value better?).
|
|
|
|
|
ch.width().unwrap_or(1)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.sum();
|
|
|
|
|
let mut drop = width % unit_size; // round down to nearest unit
|
|
|
|
|
if drop == 0 {
|
|
|
|
|
drop = unit_size
|
|
|
|
|
}; // if it's already at a unit, consume a whole unit
|
|
|
|
|
let mut chars = fragment.chars().rev();
|
|
|
|
|
let mut start = pos;
|
|
|
|
|
for _ in 0..drop {
|
|
|
|
|
// delete up to `drop` spaces
|
|
|
|
|
match chars.next() {
|
|
|
|
|
Some(' ') => start -= 1,
|
|
|
|
|
_ => break,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(start, pos, None) // delete!
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// delete char
|
|
|
|
|
(
|
|
|
|
|
graphemes::nth_prev_grapheme_boundary(text, pos, count),
|
|
|
|
|
pos,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
doc.apply(&transaction, view.id);
|
|
|
|
|
}
|
|
|
|
|