1
0
зеркало из https://github.com/glebtv/tui-textarea.git synced 2026-09-08 00:15:50 +03:00

return if text was modified or not from methods to edit text

Этот коммит содержится в:
rhysd
2022-06-15 20:15:28 +09:00
родитель 6ebd099f28
Коммит b7facac135
4 изменённых файлов: 180 добавлений и 88 удалений

Просмотреть файл

@@ -274,7 +274,9 @@ loop {
match Input::from(read()?) { match Input::from(read()?) {
Input { key: Key::Char('m'), ctrl: true, alt: false } Input { key: Key::Char('m'), ctrl: true, alt: false }
| Input { key: Key::Enter, .. } => continue, | Input { key: Key::Enter, .. } => continue,
input => textarea.input(key), input => {
textarea.input(key);
}
} }
} }
@@ -347,8 +349,12 @@ loop {
_ => {}, _ => {},
}, },
Mode::Insert => match Input::from(read()?) { Mode::Insert => match Input::from(read()?) {
Input { key: Key::Esc, .. } => mode = Mode::Normal, Input { key: Key::Esc, .. } => {
input => textarea.input(input), // Use default key mappings in insert mode mode = Mode::Normal;
}
input => {
textarea.input(input); // Use default key mappings in insert mode
}
}, },
} }
} }

Просмотреть файл

@@ -28,6 +28,7 @@ macro_rules! error {
struct Buffer<'a> { struct Buffer<'a> {
textarea: TextArea<'a>, textarea: TextArea<'a>,
path: PathBuf, path: PathBuf,
modified: bool,
} }
impl<'a> Buffer<'a> { impl<'a> Buffer<'a> {
@@ -44,15 +45,23 @@ impl<'a> Buffer<'a> {
TextArea::default() TextArea::default()
}; };
textarea.set_line_number_style(Style::default().fg(Color::DarkGray)); textarea.set_line_number_style(Style::default().fg(Color::DarkGray));
Ok(Self { textarea, path }) Ok(Self {
textarea,
path,
modified: false,
})
} }
fn save(&self) -> io::Result<()> { fn save(&mut self) -> io::Result<()> {
if !self.modified {
return Ok(());
}
let mut f = io::BufWriter::new(fs::File::create(&self.path)?); let mut f = io::BufWriter::new(fs::File::create(&self.path)?);
for line in self.textarea.lines() { for line in self.textarea.lines() {
f.write_all(line.as_bytes())?; f.write_all(line.as_bytes())?;
f.write_all(b"\n")?; f.write_all(b"\n")?;
} }
self.modified = false;
Ok(()) Ok(())
} }
} }
@@ -111,11 +120,13 @@ impl<'a> Editor<'a> {
let widget = textarea.widget(); let widget = textarea.widget();
f.render_widget(widget, chunks[0]); f.render_widget(widget, chunks[0]);
let modified = if buffer.modified { " [modified]" } else { "" };
let status = format!( let status = format!(
"[{}/{}] {:?}", "[{}/{}] {:?}{}",
self.current + 1, self.current + 1,
self.buffers.len(), self.buffers.len(),
buffer.path, buffer.path,
modified,
); );
let status = let status =
Paragraph::new(status).style(Style::default().add_modifier(Modifier::REVERSED)); Paragraph::new(status).style(Style::default().add_modifier(Modifier::REVERSED));
@@ -161,7 +172,10 @@ impl<'a> Editor<'a> {
self.buffers[self.current].save()?; self.buffers[self.current].save()?;
self.message = Some("Saved!"); self.message = Some("Saved!");
} }
input => self.buffers[self.current].textarea.input(input), input => {
let buffer = &mut self.buffers[self.current];
buffer.modified = buffer.textarea.input(input);
}
} }
} }

Просмотреть файл

@@ -54,7 +54,9 @@ fn main() -> Result<(), Box<dyn Error>> {
loop { loop {
match events.recv()? { match events.recv()? {
Event::Key(Key::Esc) => break, Event::Key(Key::Esc) => break,
Event::Key(key) => textarea.input(key), Event::Key(key) => {
textarea.input(key);
}
Event::Tick => {} Event::Tick => {}
} }
term.draw(|f| { term.draw(|f| {

Просмотреть файл

@@ -97,14 +97,15 @@ impl<'a, S: Into<String>> FromIterator<S> for TextArea<'a> {
} }
} }
impl<'a> Default for TextArea<'a> {
/// Create [`TextArea`] instance with empty text content. /// Create [`TextArea`] instance with empty text content.
/// ``` /// ```
/// use tui_textarea::TextArea; /// use tui_textarea::TextArea;
/// ///
/// let textarea = TextArea::default(); /// let textarea = TextArea::default();
/// assert_eq!(textarea.lines(), [""]); /// assert_eq!(textarea.lines(), [""]);
/// assert!(textarea.is_empty());
/// ``` /// ```
impl<'a> Default for TextArea<'a> {
fn default() -> Self { fn default() -> Self {
Self::new(vec![String::new()]) Self::new(vec![String::new()])
} }
@@ -143,6 +144,7 @@ impl<'a> TextArea<'a> {
/// [the module document](./index.html). /// [the module document](./index.html).
/// `crossterm` and `termion` features enable conversion from their own key event types into [`Input`] so this /// `crossterm` and `termion` features enable conversion from their own key event types into [`Input`] so this
/// method can take the event values directly. /// method can take the event values directly.
/// This method returns if the input modified text contents or not in the textarea.
/// ```ignore /// ```ignore
/// use tui_textarea::{TextArea, Key, Input}; /// use tui_textarea::{TextArea, Key, Input};
/// ///
@@ -164,16 +166,20 @@ impl<'a> TextArea<'a> {
/// ///
/// // Handle backend-agnostic key input /// // Handle backend-agnostic key input
/// let input = Input { key: Key::Char('a'), ctrl: false, alt: false }; /// let input = Input { key: Key::Char('a'), ctrl: false, alt: false };
/// textarea.input(input); /// let modified = textarea.input(input);
/// assert!(modified);
/// ``` /// ```
pub fn input(&mut self, input: impl Into<Input>) { pub fn input(&mut self, input: impl Into<Input>) -> bool {
let input = input.into(); let input = input.into();
match input { let modified = match input {
Input { Input {
key: Key::Char(c), key: Key::Char(c),
ctrl: false, ctrl: false,
alt: false, alt: false,
} => self.insert_char(c), } => {
self.insert_char(c);
true
}
Input { Input {
key: Key::Tab, key: Key::Tab,
ctrl: false, ctrl: false,
@@ -206,7 +212,10 @@ impl<'a> TextArea<'a> {
} }
| Input { | Input {
key: Key::Enter, .. key: Key::Enter, ..
} => self.insert_newline(), } => {
self.insert_newline();
true
}
Input { Input {
key: Key::Char('k'), key: Key::Char('k'),
ctrl: true, ctrl: true,
@@ -251,7 +260,10 @@ impl<'a> TextArea<'a> {
key: Key::Down, key: Key::Down,
ctrl: false, ctrl: false,
alt: false, alt: false,
} => self.move_cursor(CursorMove::Down), } => {
self.move_cursor(CursorMove::Down);
false
}
Input { Input {
key: Key::Char('p'), key: Key::Char('p'),
ctrl: true, ctrl: true,
@@ -261,7 +273,10 @@ impl<'a> TextArea<'a> {
key: Key::Up, key: Key::Up,
ctrl: false, ctrl: false,
alt: false, alt: false,
} => self.move_cursor(CursorMove::Up), } => {
self.move_cursor(CursorMove::Up);
false
}
Input { Input {
key: Key::Char('f'), key: Key::Char('f'),
ctrl: true, ctrl: true,
@@ -271,7 +286,10 @@ impl<'a> TextArea<'a> {
key: Key::Right, key: Key::Right,
ctrl: false, ctrl: false,
alt: false, alt: false,
} => self.move_cursor(CursorMove::Forward), } => {
self.move_cursor(CursorMove::Forward);
false
}
Input { Input {
key: Key::Char('b'), key: Key::Char('b'),
ctrl: true, ctrl: true,
@@ -281,7 +299,10 @@ impl<'a> TextArea<'a> {
key: Key::Left, key: Key::Left,
ctrl: false, ctrl: false,
alt: false, alt: false,
} => self.move_cursor(CursorMove::Back), } => {
self.move_cursor(CursorMove::Back);
false
}
Input { Input {
key: Key::Char('a'), key: Key::Char('a'),
ctrl: true, ctrl: true,
@@ -292,7 +313,10 @@ impl<'a> TextArea<'a> {
key: Key::Left | Key::Char('b'), key: Key::Left | Key::Char('b'),
ctrl: true, ctrl: true,
alt: true, alt: true,
} => self.move_cursor(CursorMove::Head), } => {
self.move_cursor(CursorMove::Head);
false
}
Input { Input {
key: Key::Char('e'), key: Key::Char('e'),
ctrl: true, ctrl: true,
@@ -303,7 +327,10 @@ impl<'a> TextArea<'a> {
key: Key::Right | Key::Char('f'), key: Key::Right | Key::Char('f'),
ctrl: true, ctrl: true,
alt: true, alt: true,
} => self.move_cursor(CursorMove::End), } => {
self.move_cursor(CursorMove::End);
false
}
Input { Input {
key: Key::Char('<'), key: Key::Char('<'),
ctrl: false, ctrl: false,
@@ -313,7 +340,10 @@ impl<'a> TextArea<'a> {
key: Key::Up | Key::Char('p'), key: Key::Up | Key::Char('p'),
ctrl: true, ctrl: true,
alt: true, alt: true,
} => self.move_cursor(CursorMove::Top), } => {
self.move_cursor(CursorMove::Top);
false
}
Input { Input {
key: Key::Char('>'), key: Key::Char('>'),
ctrl: false, ctrl: false,
@@ -323,7 +353,10 @@ impl<'a> TextArea<'a> {
key: Key::Down | Key::Char('n'), key: Key::Down | Key::Char('n'),
ctrl: true, ctrl: true,
alt: true, alt: true,
} => self.move_cursor(CursorMove::Bottom), } => {
self.move_cursor(CursorMove::Bottom);
false
}
Input { Input {
key: Key::Char('f'), key: Key::Char('f'),
ctrl: false, ctrl: false,
@@ -333,7 +366,10 @@ impl<'a> TextArea<'a> {
key: Key::Right, key: Key::Right,
ctrl: true, ctrl: true,
alt: false, alt: false,
} => self.move_cursor(CursorMove::WordForward), } => {
self.move_cursor(CursorMove::WordForward);
false
}
Input { Input {
key: Key::Char('b'), key: Key::Char('b'),
ctrl: false, ctrl: false,
@@ -343,7 +379,10 @@ impl<'a> TextArea<'a> {
key: Key::Left, key: Key::Left,
ctrl: true, ctrl: true,
alt: false, alt: false,
} => self.move_cursor(CursorMove::WordBack), } => {
self.move_cursor(CursorMove::WordBack);
false
}
Input { Input {
key: Key::Char('n'), key: Key::Char('n'),
ctrl: false, ctrl: false,
@@ -356,7 +395,10 @@ impl<'a> TextArea<'a> {
} }
| Input { | Input {
key: Key::PageDown, .. key: Key::PageDown, ..
} => self.move_cursor(CursorMove::ParagraphForward), } => {
self.move_cursor(CursorMove::ParagraphForward);
false
}
Input { Input {
key: Key::Char('p'), key: Key::Char('p'),
ctrl: false, ctrl: false,
@@ -369,28 +411,27 @@ impl<'a> TextArea<'a> {
} }
| Input { | Input {
key: Key::PageUp, .. key: Key::PageUp, ..
} => self.move_cursor(CursorMove::ParagraphBack), } => {
self.move_cursor(CursorMove::ParagraphBack);
false
}
Input { Input {
key: Key::Char('u'), key: Key::Char('u'),
ctrl: true, ctrl: true,
alt: false, alt: false,
} => { } => self.undo(),
self.undo();
}
Input { Input {
key: Key::Char('r'), key: Key::Char('r'),
ctrl: true, ctrl: true,
alt: false, alt: false,
} => { } => self.redo(),
self.redo();
}
Input { Input {
key: Key::Char('y' | 'v'), key: Key::Char('y' | 'v'),
ctrl: true, ctrl: true,
alt: false, alt: false,
} => self.paste(), } => self.paste(),
_ => {} _ => false,
} };
// Check invariants // Check invariants
debug_assert!(!self.lines.is_empty(), "no line after {:?}", input); debug_assert!(!self.lines.is_empty(), "no line after {:?}", input);
@@ -410,6 +451,8 @@ impl<'a> TextArea<'a> {
self.lines[r], self.lines[r],
input, input,
); );
modified
} }
/// Handle a key input without default key mappings. This method handles only /// Handle a key input without default key mappings. This method handles only
@@ -419,16 +462,20 @@ impl<'a> TextArea<'a> {
/// - Backspace /// - Backspace
/// - Delete /// - Delete
/// ///
/// This method returns if the input modified text contents or not in the textarea.
///
/// This method is useful when you want to define your own key mappings and don't want default key mappings. /// This method is useful when you want to define your own key mappings and don't want default key mappings.
/// See 'Define your own key mappings' section in [the module document](./index.html). /// See 'Define your own key mappings' section in [the module document](./index.html).
pub fn input_without_shortcuts(&mut self, input: impl Into<Input>) { pub fn input_without_shortcuts(&mut self, input: impl Into<Input>) -> bool {
let input = input.into(); match input.into() {
match input {
Input { Input {
key: Key::Char(c), key: Key::Char(c),
ctrl: false, ctrl: false,
alt: false, alt: false,
} => self.insert_char(c), } => {
self.insert_char(c);
true
}
Input { Input {
key: Key::Tab, key: Key::Tab,
ctrl: false, ctrl: false,
@@ -443,8 +490,11 @@ impl<'a> TextArea<'a> {
} => self.delete_next_char(), } => self.delete_next_char(),
Input { Input {
key: Key::Enter, .. key: Key::Enter, ..
} => self.insert_newline(), } => {
_ => {} self.insert_newline();
true
}
_ => false,
} }
} }
@@ -475,7 +525,8 @@ impl<'a> TextArea<'a> {
self.push_history(EditKind::InsertChar(c, i), (row, col)); self.push_history(EditKind::InsertChar(c, i), (row, col));
} }
/// Insert a string at current cursor position. Currently the string must not contain any newlines. /// Insert a string at current cursor position. Currently the string must not contain any newlines. This method
/// returns if some text was inserted or not in the textarea.
/// ``` /// ```
/// use tui_textarea::TextArea; /// use tui_textarea::TextArea;
/// ///
@@ -484,10 +535,10 @@ impl<'a> TextArea<'a> {
/// textarea.insert_str("hello"); /// textarea.insert_str("hello");
/// assert_eq!(textarea.lines(), ["hello"]); /// assert_eq!(textarea.lines(), ["hello"]);
/// ``` /// ```
pub fn insert_str<S: Into<String>>(&mut self, s: S) { pub fn insert_str<S: Into<String>>(&mut self, s: S) -> bool {
let s = s.into(); let s = s.into();
if s.is_empty() { if s.is_empty() {
return; return false;
} }
let (row, col) = self.cursor; let (row, col) = self.cursor;
@@ -507,10 +558,11 @@ impl<'a> TextArea<'a> {
self.cursor.1 += s.chars().count(); self.cursor.1 += s.chars().count();
self.push_history(EditKind::Insert(s, i), (row, col)); self.push_history(EditKind::Insert(s, i), (row, col));
true
} }
/// Delete a string in current cursor line. The `chars` parameter means number of characters, not a byte length of /// Delete a string in current cursor line. The `chars` parameter means number of characters, not a byte length of
/// the string. /// the string. This method returns if some text was deleted or not in the textarea.
/// ``` /// ```
/// use tui_textarea::TextArea; /// use tui_textarea::TextArea;
/// ///
@@ -519,9 +571,9 @@ impl<'a> TextArea<'a> {
/// textarea.delete_str(1, 2); /// textarea.delete_str(1, 2);
/// assert_eq!(textarea.lines(), ["🐱🐮"]); /// assert_eq!(textarea.lines(), ["🐱🐮"]);
/// ``` /// ```
pub fn delete_str(&mut self, col: usize, chars: usize) { pub fn delete_str(&mut self, col: usize, chars: usize) -> bool {
if chars == 0 { if chars == 0 {
return; return false;
} }
let row = self.cursor.0; let row = self.cursor.0;
@@ -538,10 +590,15 @@ impl<'a> TextArea<'a> {
self.cursor = (row, col); self.cursor = (row, col);
self.push_history(EditKind::Remove(removed.clone(), i), (row, col)); self.push_history(EditKind::Remove(removed.clone(), i), (row, col));
self.yank = removed; self.yank = removed;
true
} else {
false
} }
} }
/// Insert a tab at current cursor position. /// Insert a tab at current cursor position. Note that this method does nothing when the tab length is 0. This
/// method returns if a tab string was inserted or not in the textarea.
/// textarea.
/// ``` /// ```
/// use tui_textarea::TextArea; /// use tui_textarea::TextArea;
/// ///
@@ -550,11 +607,12 @@ impl<'a> TextArea<'a> {
/// textarea.insert_tab(); /// textarea.insert_tab();
/// assert_eq!(textarea.lines(), [" hi"]); /// assert_eq!(textarea.lines(), [" hi"]);
/// ``` /// ```
pub fn insert_tab(&mut self) { pub fn insert_tab(&mut self) -> bool {
if self.tab_len > 0 { if self.tab_len == 0 {
let len = self.tab_len as usize - self.cursor.1 % self.tab_len as usize; return false;
self.insert_str(spaces(len));
} }
let len = self.tab_len as usize - self.cursor.1 % self.tab_len as usize;
self.insert_str(spaces(len))
} }
/// Insert a newline at current cursor position. /// Insert a newline at current cursor position.
@@ -583,7 +641,8 @@ impl<'a> TextArea<'a> {
self.push_history(EditKind::InsertNewline(idx), (row, col)); self.push_history(EditKind::InsertNewline(idx), (row, col));
} }
/// Delete a newline from **head** of current cursor line. /// Delete a newline from **head** of current cursor line. This method returns if a newline was deleted or not in
/// the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -593,10 +652,10 @@ impl<'a> TextArea<'a> {
/// textarea.delete_newline(); /// textarea.delete_newline();
/// assert_eq!(textarea.lines(), ["helloworld"]); /// assert_eq!(textarea.lines(), ["helloworld"]);
/// ``` /// ```
pub fn delete_newline(&mut self) { pub fn delete_newline(&mut self) -> bool {
let (row, col) = self.cursor; let (row, col) = self.cursor;
if row == 0 { if row == 0 {
return; return false;
} }
let line = self.lines.remove(row); let line = self.lines.remove(row);
@@ -606,10 +665,11 @@ impl<'a> TextArea<'a> {
self.cursor = (row - 1, prev_line.chars().count()); self.cursor = (row - 1, prev_line.chars().count());
prev_line.push_str(&line); prev_line.push_str(&line);
self.push_history(EditKind::DeleteNewline(prev_line_end), (row, col)); self.push_history(EditKind::DeleteNewline(prev_line_end), (row, col));
true
} }
/// Delete one character before cursor. When the cursor is at head of line, the newline before the cursor will be /// Delete one character before cursor. When the cursor is at head of line, the newline before the cursor will be
/// removed. /// removed. This method returns if some text was deleted or not in the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -619,11 +679,10 @@ impl<'a> TextArea<'a> {
/// textarea.delete_char(); /// textarea.delete_char();
/// assert_eq!(textarea.lines(), ["bc"]); /// assert_eq!(textarea.lines(), ["bc"]);
/// ``` /// ```
pub fn delete_char(&mut self) { pub fn delete_char(&mut self) -> bool {
let (row, col) = self.cursor; let (row, col) = self.cursor;
if col == 0 { if col == 0 {
self.delete_newline(); return self.delete_newline();
return;
} }
let line = &mut self.lines[row]; let line = &mut self.lines[row];
@@ -631,11 +690,14 @@ impl<'a> TextArea<'a> {
line.remove(i); line.remove(i);
self.cursor.1 -= 1; self.cursor.1 -= 1;
self.push_history(EditKind::DeleteChar(c, i), (row, col)); self.push_history(EditKind::DeleteChar(c, i), (row, col));
true
} else {
false
} }
} }
/// Delete one character next to cursor. When the cursor is at end of line, the newline next to the cursor will be /// Delete one character next to cursor. When the cursor is at end of line, the newline next to the cursor will be
/// removed. /// removed. This method returns if a character was deleted or not in the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -645,17 +707,17 @@ impl<'a> TextArea<'a> {
/// textarea.delete_next_char(); /// textarea.delete_next_char();
/// assert_eq!(textarea.lines(), ["ac"]); /// assert_eq!(textarea.lines(), ["ac"]);
/// ``` /// ```
pub fn delete_next_char(&mut self) { pub fn delete_next_char(&mut self) -> bool {
let before = self.cursor; let before = self.cursor;
self.move_cursor(CursorMove::Forward); self.move_cursor(CursorMove::Forward);
if before == self.cursor { if before == self.cursor {
return; // Cursor didn't move, meant no character at next of cursor. return false; // Cursor didn't move, meant no character at next of cursor.
} }
self.delete_char(); self.delete_char()
} }
/// Delete string from cursor to end of the line. When the cursor is at end of line, the newline next to the cursor /// Delete string from cursor to end of the line. When the cursor is at end of line, the newline next to the cursor
/// will be removed. /// will be removed. This method returns if some text was deleted or not in the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -668,12 +730,12 @@ impl<'a> TextArea<'a> {
/// textarea.delete_line_by_end(); /// textarea.delete_line_by_end();
/// assert_eq!(textarea.lines(), ["ab"]); /// assert_eq!(textarea.lines(), ["ab"]);
/// ``` /// ```
pub fn delete_line_by_end(&mut self) { pub fn delete_line_by_end(&mut self) -> bool {
self.delete_str(self.cursor.1, usize::MAX); self.delete_str(self.cursor.1, usize::MAX)
} }
/// Delete string from cursor to head of the line. When the cursor is at head of line, the newline before the cursor /// Delete string from cursor to head of the line. When the cursor is at head of line, the newline before the cursor
/// will be removed. /// will be removed. This method returns if some text was deleted or not in the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -686,13 +748,16 @@ impl<'a> TextArea<'a> {
/// textarea.delete_line_by_head(); /// textarea.delete_line_by_head();
/// assert_eq!(textarea.lines(), ["cde"]); /// assert_eq!(textarea.lines(), ["cde"]);
/// ``` /// ```
pub fn delete_line_by_head(&mut self) { pub fn delete_line_by_head(&mut self) -> bool {
self.delete_str(0, self.cursor.1); self.delete_str(0, self.cursor.1)
} }
/// Delete a word before cursor. Word boundary appears at spaces, punctuations, and others. For example `fn foo(a)` /// Delete a word before cursor. Word boundary appears at spaces, punctuations, and others. For example `fn foo(a)`
/// consists of words `fn`, `foo`, `(`, `a`, `)`. When the cursor is at head of line, the newline before the cursor /// consists of words `fn`, `foo`, `(`, `a`, `)`. When the cursor is at head of line, the newline before the cursor
/// will be removed. /// will be removed.
///
/// This method returns if some text was deleted or not in the textarea.
///
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -705,20 +770,23 @@ impl<'a> TextArea<'a> {
/// textarea.delete_word(); /// textarea.delete_word();
/// assert_eq!(textarea.lines(), ["aaa "]); /// assert_eq!(textarea.lines(), ["aaa "]);
/// ``` /// ```
pub fn delete_word(&mut self) { pub fn delete_word(&mut self) -> bool {
let (r, c) = self.cursor; let (r, c) = self.cursor;
if let Some(col) = find_word_start_backward(&self.lines[r], c) { if let Some(col) = find_word_start_backward(&self.lines[r], c) {
self.delete_str(col, c - col); self.delete_str(col, c - col)
} else if c > 0 { } else if c > 0 {
self.delete_str(0, c); self.delete_str(0, c)
} else { } else {
self.delete_newline(); self.delete_newline()
} }
} }
/// Delete a word next to cursor. Word boundary appears at spaces, punctuations, and others. For example `fn foo(a)` /// Delete a word next to cursor. Word boundary appears at spaces, punctuations, and others. For example `fn foo(a)`
/// consists of words `fn`, `foo`, `(`, `a`, `)`. When the cursor is at end of line, the newline next to the cursor /// consists of words `fn`, `foo`, `(`, `a`, `)`. When the cursor is at end of line, the newline next to the cursor
/// will be removed. /// will be removed.
///
/// This method returns if some text was deleted or not in the textarea.
///
/// ``` /// ```
/// use tui_textarea::TextArea; /// use tui_textarea::TextArea;
/// ///
@@ -729,24 +797,27 @@ impl<'a> TextArea<'a> {
/// textarea.delete_next_word(); /// textarea.delete_next_word();
/// assert_eq!(textarea.lines(), [" ccc"]); /// assert_eq!(textarea.lines(), [" ccc"]);
/// ``` /// ```
pub fn delete_next_word(&mut self) { pub fn delete_next_word(&mut self) -> bool {
let (r, c) = self.cursor; let (r, c) = self.cursor;
let line = &self.lines[r]; let line = &self.lines[r];
if let Some(col) = find_word_end_forward(line, c) { if let Some(col) = find_word_end_forward(line, c) {
self.delete_str(c, col - c); self.delete_str(c, col - c)
} else { } else {
let end_col = line.chars().count(); let end_col = line.chars().count();
if c < end_col { if c < end_col {
self.delete_str(c, end_col - c); self.delete_str(c, end_col - c)
} else if r + 1 < self.lines.len() { } else if r + 1 < self.lines.len() {
self.cursor = (r + 1, 0); self.cursor = (r + 1, 0);
self.delete_newline(); self.delete_newline()
} else {
false
} }
} }
} }
/// Paste a string previously deleted by [`TextArea::delete_line_by_head`], [`TextArea::delete_line_by_end`], /// Paste a string previously deleted by [`TextArea::delete_line_by_head`], [`TextArea::delete_line_by_end`],
/// [`TextArea::delete_word`], [`TextArea::delete_next_word`]. /// [`TextArea::delete_word`], [`TextArea::delete_next_word`]. This method returns if some text was inserted or not
/// in the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -757,9 +828,9 @@ impl<'a> TextArea<'a> {
/// textarea.paste(); /// textarea.paste();
/// assert_eq!(textarea.lines(), [" bbb cccaaa"]); /// assert_eq!(textarea.lines(), [" bbb cccaaa"]);
/// ``` /// ```
pub fn paste(&mut self) { pub fn paste(&mut self) -> bool {
let yank = std::mem::take(&mut self.yank); let yank = std::mem::take(&mut self.yank);
self.insert_str(yank); self.insert_str(yank)
} }
/// Move the cursor to the position specified by the [`CursorMove`] parameter. For each kind of cursor moves, see /// Move the cursor to the position specified by the [`CursorMove`] parameter. For each kind of cursor moves, see
@@ -780,8 +851,7 @@ impl<'a> TextArea<'a> {
} }
} }
/// Undo the last modification. When no modification is added to the text, this method does not modify contents and /// Undo the last modification. This method returns if the undo modified text contents or not in the textarea.
/// returns `false`.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///
@@ -801,7 +871,7 @@ impl<'a> TextArea<'a> {
} }
} }
/// Redo the last undo change. When no undo change remain, this method does not modify contents and returns `false`. /// Redo the last undo change. This method returns if the redo modified text contents or not in the textarea.
/// ``` /// ```
/// use tui_textarea::{TextArea, CursorMove}; /// use tui_textarea::{TextArea, CursorMove};
/// ///