}
}
- fn insert(&mut self, text: &str) {
+ fn insert_after(&mut self, text: &str) {
self.text = self.text[..self.point].to_owned() + text +
&self.text[self.point..];
+ }
+
+ fn insert(&mut self, text: &str) {
+ self.insert_after(text);
self.point += text.len();
}
self.point = self.text.len();
}
- fn cut(&mut self, start: usize, end: usize) {
+ fn delete(&mut self, start: usize, end: usize) -> String {
assert!(self.is_char_boundary(start));
assert!(self.is_char_boundary(end));
assert!(end >= start);
if end == start {
- return;
+ return "".to_owned();
}
let new_point = if self.point <= start {
self.point + start - end
};
- self.paste_buffer = self.text[start..end].to_owned();
+ let deleted_text = self.text[start..end].to_owned();
self.text = self.text[..start].to_owned() + &self.text[end..];
self.point = new_point;
+ deleted_text
+ }
+
+ fn cut(&mut self, start: usize, end: usize) {
+ let deleted = self.delete(start, end);
+ if !deleted.is_empty() {
+ self.paste_buffer = deleted;
+ }
}
fn handle_keypress(&mut self, key: OurKey) {
let (_x, y) = self.cursor_pos.expect("post_update should have run");
self.goto_xy(usize::MAX, y);
}
+
+ fn cut_to_end_of_line(&mut self) {
+ let (_x, y) = self.cursor_pos.expect("post_update should have run");
+ let eol_index = self.layout.partition_point(|cell| cell.y <= y);
+ let eol_pos = match self.layout.get(eol_index) {
+ Some(cell) => cell.pos,
+ None => self.core.text.len(),
+ };
+
+ let slice = &self.core.text[self.core.point..eol_pos];
+
+ if slice == "\n" {
+ // If the slice is _just_ \n, we delete it, so that you
+ // get the emacs-like behaviour where hammering on ^K
+ // alternates between deleting the contents of a line and
+ // the empty line it left behind. But we don't put that
+ // boring \n in the paste buffer.
+ self.core.delete(self.core.point, eol_pos);
+ } else if slice.ends_with("\n") {
+ // If the slice goes up to but not including a \n,
+ // literally cut everything up to but not including the
+ // end of the line.
+ self.core.cut(self.core.point, eol_pos - 1);
+ } else {
+ // Otherwise, the line we cut to the end of is a non-final
+ // line of a wrapped paragraph. Counterintuitively, in
+ // this case, I _insert_ a \n in place of the cut text,
+ // because that way the text on later lines doesn't rewrap
+ // confusingly.
+ //
+ // I'm not _sure_ this is the best thing, but the naïve
+ // thing isn't great either, and I think on balance this
+ // does more or less what my reflexes expect.
+ //
+ // Put another way: after ^K actually cuts some
+ // non-newline text, we expect that the cursor should
+ // always be on a newline or end-of-buffer.
+ self.core.cut(self.core.point, eol_pos);
+ self.core.insert_after("\n");
+ }
+ }
}
#[test]
(Start, Ctrl('A')) | (Start, Home) => self.beginning_of_line(),
(Start, Ctrl('E')) | (Start, End) => self.end_of_line(),
- (Start, Return) => {
- self.core.insert("\n");
- self.post_update();
- }
+ (Start, Ctrl('K')) => self.cut_to_end_of_line(),
+
+ // Return is an ordinary self-inserting key, but we have
+ // to handle it specially here, because EditorCore doesn't
+ // handle it at all, because in BottomLineEditor it _is_
+ // special
+ (Start, Return) => self.core.insert("\n"),
// ^O is a prefix key that is followed by various less
// common keystrokes