use crate::undo::{EditKind, UndoStack};
use super::LineInputState;
use super::chunking::{self, cursor_to_visual};
use super::editing;
use super::types::{LineInputMessage, LineInputOutput};
pub(super) fn update(state: &mut LineInputState, msg: LineInputMessage) -> Option<LineInputOutput> {
match msg {
LineInputMessage::Insert(c) => {
let selection_chars = state
.selection_range()
.map(|(s, e)| state.buffer[s..e].chars().count())
.unwrap_or(0);
let effective_len = state.buffer.chars().count() - selection_chars;
if let Some(max) = state.max_length {
if effective_len >= max {
return None;
}
}
if c.is_whitespace() {
state.undo_stack.break_group();
}
let snapshot = state.snapshot();
state.undo_stack.save(snapshot, EditKind::Insert);
state.delete_selection();
let (new_buffer, new_cursor) = editing::insert_char(&state.buffer, state.cursor, c);
state.buffer = new_buffer;
state.cursor = new_cursor;
if c.is_whitespace() {
state.undo_stack.break_group();
}
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
}
LineInputMessage::Backspace => {
let snapshot = state.snapshot();
if state.has_selection() {
state.undo_stack.save(snapshot, EditKind::Delete);
state.delete_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else if let Some((new_buffer, new_cursor)) =
editing::backspace(&state.buffer, state.cursor)
{
state.undo_stack.save(snapshot, EditKind::Delete);
state.buffer = new_buffer;
state.cursor = new_cursor;
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::Delete => {
let snapshot = state.snapshot();
if state.has_selection() {
state.undo_stack.save(snapshot, EditKind::Delete);
state.delete_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else if let Some(new_buffer) = editing::delete_at(&state.buffer, state.cursor) {
state.undo_stack.save(snapshot, EditKind::Delete);
state.buffer = new_buffer;
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::DeleteWordBack => {
let snapshot = state.snapshot();
if state.has_selection() {
state.undo_stack.save(snapshot, EditKind::Delete);
state.delete_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else if let Some((new_buffer, new_cursor)) =
editing::delete_word_back(&state.buffer, state.cursor)
{
state.undo_stack.save(snapshot, EditKind::Delete);
state.buffer = new_buffer;
state.cursor = new_cursor;
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::DeleteWordForward => {
let snapshot = state.snapshot();
if state.has_selection() {
state.undo_stack.save(snapshot, EditKind::Delete);
state.delete_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else if let Some(new_buffer) =
editing::delete_word_forward(&state.buffer, state.cursor)
{
state.undo_stack.save(snapshot, EditKind::Delete);
state.buffer = new_buffer;
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::DeleteToEnd => {
if state.cursor >= state.buffer.len() {
return None;
}
let snapshot = state.snapshot();
state.undo_stack.save(snapshot, EditKind::Delete);
state.buffer.truncate(state.cursor);
state.clear_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
}
LineInputMessage::Clear => {
if state.buffer.is_empty() {
return None;
}
let snapshot = state.snapshot();
state.undo_stack.save(snapshot, EditKind::Other);
state.buffer.clear();
state.cursor = 0;
state.clear_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
}
LineInputMessage::SetValue(value) => {
let snapshot = state.snapshot();
state.undo_stack.save(snapshot, EditKind::Other);
let value = if let Some(max) = state.max_length {
if value.chars().count() > max {
value.chars().take(max).collect()
} else {
value
}
} else {
value
};
state.buffer = value;
state.cursor = state.buffer.len();
state.clear_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
}
LineInputMessage::Paste(text) => {
let snapshot = state.snapshot();
state.undo_stack.save(snapshot, EditKind::Other);
state.delete_selection();
let remaining = state.remaining_capacity();
if remaining == 0 {
return None;
}
let text = if remaining < usize::MAX {
text.chars().take(remaining).collect()
} else {
text
};
let (new_buffer, new_cursor) = editing::insert_str(&state.buffer, state.cursor, &text);
state.buffer = new_buffer;
state.cursor = new_cursor;
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
}
LineInputMessage::Left => {
state.clear_selection();
state.cursor = editing::move_left(&state.buffer, state.cursor);
None
}
LineInputMessage::Right => {
state.clear_selection();
state.cursor = editing::move_right(&state.buffer, state.cursor);
None
}
LineInputMessage::Home => {
state.clear_selection();
state.cursor = 0;
None
}
LineInputMessage::End => {
state.clear_selection();
state.cursor = state.buffer.len();
None
}
LineInputMessage::WordLeft => {
state.clear_selection();
state.cursor = editing::move_word_left(&state.buffer, state.cursor);
None
}
LineInputMessage::WordRight => {
state.clear_selection();
state.cursor = editing::move_word_right(&state.buffer, state.cursor);
None
}
LineInputMessage::VisualUp => {
state.clear_selection();
let width = state.last_display_width;
let (row, col) = cursor_to_visual(&state.buffer, state.cursor, width);
if row > 0 {
state.cursor = chunking::visual_to_cursor(&state.buffer, row - 1, col, width);
}
None
}
LineInputMessage::VisualDown => {
state.clear_selection();
let width = state.last_display_width;
let (row, col) = cursor_to_visual(&state.buffer, state.cursor, width);
state.cursor = chunking::visual_to_cursor(&state.buffer, row + 1, col, width);
None
}
LineInputMessage::SelectLeft => {
state.ensure_selection_anchor();
state.cursor = editing::move_left(&state.buffer, state.cursor);
None
}
LineInputMessage::SelectRight => {
state.ensure_selection_anchor();
state.cursor = editing::move_right(&state.buffer, state.cursor);
None
}
LineInputMessage::SelectHome => {
state.ensure_selection_anchor();
state.cursor = 0;
None
}
LineInputMessage::SelectEnd => {
state.ensure_selection_anchor();
state.cursor = state.buffer.len();
None
}
LineInputMessage::SelectWordLeft => {
state.ensure_selection_anchor();
state.cursor = editing::move_word_left(&state.buffer, state.cursor);
None
}
LineInputMessage::SelectWordRight => {
state.ensure_selection_anchor();
state.cursor = editing::move_word_right(&state.buffer, state.cursor);
None
}
LineInputMessage::SelectAll => {
if state.buffer.is_empty() {
return None;
}
state.selection_anchor = Some(0);
state.cursor = state.buffer.len();
None
}
LineInputMessage::Copy => {
if let Some(text) = state.selected_text() {
let text = text.to_string();
state.clipboard = text.clone();
Some(LineInputOutput::Copied(text))
} else {
None
}
}
LineInputMessage::Cut => {
if let Some(text) = state.selected_text() {
let text = text.to_string();
state.clipboard = text.clone();
let snapshot = state.snapshot();
state.undo_stack.save(snapshot, EditKind::Other);
state.delete_selection();
state.history.exit_browse();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::HistoryPrev => {
if let Some(entry) = state.history.prev(&state.buffer) {
state.buffer = entry.to_string();
state.cursor = state.buffer.len();
state.clear_selection();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::HistoryNext => {
if let Some(entry) = state.history.next() {
state.buffer = entry;
state.cursor = state.buffer.len();
state.clear_selection();
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::Submit => {
let value = state.buffer.clone();
state.history.push(value.clone());
state.buffer.clear();
state.cursor = 0;
state.clear_selection();
state.undo_stack = UndoStack::default();
Some(LineInputOutput::Submitted(value))
}
LineInputMessage::Undo => {
let current = state.snapshot();
if let Some(restored) = state.undo_stack.undo(current) {
state.restore(restored);
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
LineInputMessage::Redo => {
let current = state.snapshot();
if let Some(restored) = state.undo_stack.redo(current) {
state.restore(restored);
Some(LineInputOutput::Changed(state.buffer.clone()))
} else {
None
}
}
}
}