Implement indent-aware delete (#1120)
* delete character backward can make undent behavior * improve to handle mixed indentation
This commit is contained in:
parent
bd56dde6e2
commit
5959356a24
1 changed files with 61 additions and 9 deletions
|
@ -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);
|
||||
(
|
||||
graphemes::nth_prev_grapheme_boundary(text, pos, count),
|
||||
pos,
|
||||
None,
|
||||
)
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue