Many upgrades for Editor
This commit is contained in:
parent
39d3219df9
commit
8e09a128bb
2 changed files with 151 additions and 17 deletions
|
|
@ -3,6 +3,7 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
use alloc::string::{String, ToString};
|
||||
use core::cmp;
|
||||
use std::iter::once;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::{Action, AttrsList, Buffer, BufferLine, Cursor, Edit, LayoutCursor};
|
||||
|
|
@ -203,24 +204,51 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
}
|
||||
|
||||
fn insert_string(&mut self, data: &str, attrs_list: Option<AttrsList>) {
|
||||
let len = data.len();
|
||||
|
||||
self.delete_selection();
|
||||
let mut remaining_split_len = data.len();
|
||||
|
||||
let line: &mut BufferLine = &mut self.buffer.lines[self.cursor.line];
|
||||
let insert_line = self.cursor.line + 1;
|
||||
|
||||
// Collect text after insertion as a line
|
||||
let after: BufferLine = line.split_off(self.cursor.index);
|
||||
let after_len = after.text().len();
|
||||
|
||||
// Collect attributes
|
||||
let final_attrs = attrs_list.unwrap_or_else(|| AttrsList::new(line.attrs_list().get_span(line.text().len())));
|
||||
let mut final_attrs = attrs_list.unwrap_or_else(|| AttrsList::new(line.attrs_list().get_span(line.text().len())));
|
||||
|
||||
// Append the inserted text
|
||||
line.append(BufferLine::new(data, final_attrs));
|
||||
// Append the inserted text, line by line
|
||||
// we want to see a blank entry if the string ends with a newline
|
||||
let addendum = once("").filter(|_| data.ends_with('\n'));
|
||||
let mut lines_iter = data.split_inclusive('\n').chain(addendum);
|
||||
if let Some(data_line) = lines_iter.next() {
|
||||
let mut these_attrs = final_attrs.split_off(data_line.len());
|
||||
remaining_split_len -= data_line.len();
|
||||
std::mem::swap(&mut these_attrs, &mut final_attrs);
|
||||
line.append(BufferLine::new(data_line.strip_suffix(char::is_control).unwrap_or(data_line), these_attrs));
|
||||
} else {
|
||||
panic!("str::lines() did not yield any elements");
|
||||
}
|
||||
if let Some(data_line) = lines_iter.next_back() {
|
||||
remaining_split_len -= data_line.len();
|
||||
let mut tmp = BufferLine::new(data_line.strip_suffix(char::is_control).unwrap_or(data_line), final_attrs.split_off(remaining_split_len));
|
||||
tmp.append(after);
|
||||
self.buffer.lines.insert(insert_line, tmp);
|
||||
self.cursor.line += 1;
|
||||
} else {
|
||||
line.append(after);
|
||||
}
|
||||
for data_line in lines_iter.rev() {
|
||||
remaining_split_len -= data_line.len();
|
||||
let tmp = BufferLine::new(data_line.strip_suffix(char::is_control).unwrap_or(data_line), final_attrs.split_off(remaining_split_len));
|
||||
self.buffer.lines.insert(insert_line, tmp);
|
||||
self.cursor.line += 1;
|
||||
}
|
||||
|
||||
assert_eq!(remaining_split_len, 0);
|
||||
|
||||
// Append the text after insertion
|
||||
line.append(after);
|
||||
self.cursor.index += len;
|
||||
self.cursor.index = self.buffer.lines[self.cursor.line].text().len() - after_len;
|
||||
}
|
||||
|
||||
fn action(&mut self, action: Action) {
|
||||
|
|
@ -346,17 +374,34 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
self.set_layout_cursor(cursor);
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
Action::ParagraphStart => {
|
||||
self.cursor.index = 0;
|
||||
self.cursor_x_opt = None;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
Action::ParagraphEnd => {
|
||||
self.cursor.index = self.buffer.lines[self.cursor.line].text().len();
|
||||
self.cursor_x_opt = None;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
Action::PageUp => {
|
||||
//TODO: move cursor
|
||||
let mut scroll = self.buffer.scroll();
|
||||
scroll -= self.buffer.visible_lines();
|
||||
self.buffer.set_scroll(scroll);
|
||||
self.action(Action::Vertical(-self.buffer.size().1));
|
||||
},
|
||||
Action::PageDown => {
|
||||
//TODO: move cursor
|
||||
let mut scroll = self.buffer.scroll();
|
||||
scroll += self.buffer.visible_lines();
|
||||
self.buffer.set_scroll(scroll);
|
||||
self.action(Action::Vertical(self.buffer.size().1));
|
||||
},
|
||||
Action::Vertical(px) => {
|
||||
// TODO more efficient
|
||||
let lines = px / self.buffer.metrics().line_height;
|
||||
if lines < 0 {
|
||||
for _ in 0..-lines {
|
||||
self.action(Action::Up);
|
||||
}
|
||||
} else if lines > 0 {
|
||||
for _ in 0..lines {
|
||||
self.action(Action::Down);
|
||||
}
|
||||
}
|
||||
},
|
||||
Action::Escape => {
|
||||
if self.select_opt.take().is_some() {
|
||||
|
|
@ -369,6 +414,8 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
{
|
||||
// Filter out special chars (except for tab), use Action instead
|
||||
log::debug!("Refusing to insert control character {:?}", character);
|
||||
} else if character == '\n' {
|
||||
self.action(Action::Enter);
|
||||
} else {
|
||||
let mut str_buf = [0u8; 8];
|
||||
let str_ref = character.encode_utf8(&mut str_buf);
|
||||
|
|
@ -484,6 +531,75 @@ impl<'a> Edit<'a> for Editor<'a> {
|
|||
scroll += lines;
|
||||
self.buffer.set_scroll(scroll);
|
||||
}
|
||||
Action::PreviousWord => {
|
||||
let line: &mut BufferLine = &mut self.buffer.lines[self.cursor.line];
|
||||
if self.cursor.index > 0 {
|
||||
let mut prev_index = 0;
|
||||
for (i, _) in line.text().unicode_word_indices() {
|
||||
if i < self.cursor.index {
|
||||
prev_index = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.cursor.index = prev_index;
|
||||
self.buffer.set_redraw(true);
|
||||
} else if self.cursor.line > 0 {
|
||||
self.cursor.line -= 1;
|
||||
self.cursor.index = self.buffer.lines[self.cursor.line].text().len();
|
||||
self.buffer.set_redraw(true)
|
||||
}
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
Action::NextWord => {
|
||||
let line: &mut BufferLine = &mut self.buffer.lines[self.cursor.line];
|
||||
if self.cursor.index < line.text().len() {
|
||||
for (i, word) in line.text().unicode_word_indices() {
|
||||
let i = i + word.len();
|
||||
if i > self.cursor.index {
|
||||
self.cursor.index = i;
|
||||
self.buffer.set_redraw(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if self.cursor.line + 1 < self.buffer.lines.len() {
|
||||
self.cursor.line += 1;
|
||||
self.cursor.index = 0;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
Action::LeftWord => {
|
||||
let rtl_opt = self.buffer.lines[self.cursor.line].shape_opt().as_ref().map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
self.action(Action::NextWord);
|
||||
} else {
|
||||
self.action(Action::PreviousWord);
|
||||
}
|
||||
}
|
||||
},
|
||||
Action::RightWord => {
|
||||
let rtl_opt = self.buffer.lines[self.cursor.line].shape_opt().as_ref().map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
self.action(Action::PreviousWord);
|
||||
} else {
|
||||
self.action(Action::NextWord);
|
||||
}
|
||||
}
|
||||
},
|
||||
Action::BufferStart => {
|
||||
self.cursor.line = 0;
|
||||
self.cursor.index = 0;
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
Action::BufferEnd => {
|
||||
self.cursor.line = self.buffer.lines.len() - 1;
|
||||
self.cursor.index = self.buffer.lines[self.cursor.line].text().len();
|
||||
self.cursor_x_opt = None;
|
||||
}
|
||||
}
|
||||
|
||||
if old_cursor != self.cursor {
|
||||
|
|
|
|||
|
|
@ -37,10 +37,16 @@ pub enum Action {
|
|||
Home,
|
||||
/// Move cursor to end of line
|
||||
End,
|
||||
/// Scroll up one page
|
||||
/// Move cursor to start of paragraph
|
||||
ParagraphStart,
|
||||
/// Move cursor to end of paragraph
|
||||
ParagraphEnd,
|
||||
/// Move cursor up one page
|
||||
PageUp,
|
||||
/// Scroll down one page
|
||||
/// Move cursor down one page
|
||||
PageDown,
|
||||
/// Move cursor up or down by a number of pixels
|
||||
Vertical(i32),
|
||||
/// Escape, clears selection
|
||||
Escape,
|
||||
/// Insert character at cursor
|
||||
|
|
@ -57,6 +63,18 @@ pub enum Action {
|
|||
Drag { x: i32, y: i32 },
|
||||
/// Scroll specified number of lines
|
||||
Scroll { lines: i32 },
|
||||
/// Move cursor to previous word boundary
|
||||
PreviousWord,
|
||||
/// Move cursor to next word boundary
|
||||
NextWord,
|
||||
/// Move cursor to next word boundary to the left
|
||||
LeftWord,
|
||||
/// Move cursor to next word boundary to the right
|
||||
RightWord,
|
||||
/// Move cursor to the start of the document
|
||||
BufferStart,
|
||||
/// Move cursor to the end of the document
|
||||
BufferEnd,
|
||||
}
|
||||
|
||||
/// A trait to allow easy replacements of [`Editor`], like `SyntaxEditor`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue