Added undo/redo support
This commit is contained in:
parent
5925e37fba
commit
5169d5ae92
7 changed files with 244 additions and 55 deletions
|
|
@ -42,6 +42,8 @@ pub enum Action {
|
||||||
SelectAll, // Fully select the entire input
|
SelectAll, // Fully select the entire input
|
||||||
Save, // Save the current buffer
|
Save, // Save the current buffer
|
||||||
Mouse(MouseAction, [isize; 2]),
|
Mouse(MouseAction, [isize; 2]),
|
||||||
|
Undo,
|
||||||
|
Redo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How far should movement go?
|
/// How far should movement go?
|
||||||
|
|
@ -65,6 +67,8 @@ pub enum Event {
|
||||||
Action(Action),
|
Action(Action),
|
||||||
// The incoming event is a raw user input.
|
// The incoming event is a raw user input.
|
||||||
Raw(RawEvent),
|
Raw(RawEvent),
|
||||||
|
// A terminal bell ring
|
||||||
|
Bell,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Action> for Event {
|
impl From<Action> for Event {
|
||||||
|
|
@ -85,6 +89,7 @@ impl Event {
|
||||||
match self {
|
match self {
|
||||||
Self::Action(a) => Some(a.clone()),
|
Self::Action(a) => Some(a.clone()),
|
||||||
Self::Raw(te) => translate(te),
|
Self::Raw(te) => translate(te),
|
||||||
|
Self::Bell => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -409,6 +414,24 @@ impl RawEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_undo_redo(&self) -> Option<Action> {
|
||||||
|
match &self.0 {
|
||||||
|
TerminalEvent::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('z'),
|
||||||
|
modifiers: KeyModifiers::CONTROL,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
..
|
||||||
|
}) => Some(Action::Undo),
|
||||||
|
TerminalEvent::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('y'),
|
||||||
|
modifiers: KeyModifiers::CONTROL,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
..
|
||||||
|
}) => Some(Action::Redo),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_mouse(&self, area: Area) -> Option<Action> {
|
pub fn to_mouse(&self, area: Area) -> Option<Action> {
|
||||||
let TerminalEvent::Mouse(ev) = self.0 else {
|
let TerminalEvent::Mouse(ev) = self.0 else {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -48,11 +48,12 @@ fn main() -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have the UI handle events
|
// Have the UI handle events
|
||||||
if ui
|
match ui.handle(&mut state, Event::from_raw(ev)) {
|
||||||
.handle(&mut state, Event::from_raw(ev))
|
Ok(r) if r.is_end() => return Ok(()),
|
||||||
.map_or(false, |r| r.into_ended().is_some())
|
Ok(_) => {}
|
||||||
{
|
Err(Event::Bell) => term.ring_bell(),
|
||||||
return Ok(());
|
// Unhandled event!
|
||||||
|
Err(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
210
src/state.rs
210
src/state.rs
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use slotmap::{HopSlotMap, new_key_type};
|
use slotmap::{HopSlotMap, new_key_type};
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
io,
|
io,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
|
@ -15,7 +16,7 @@ new_key_type! {
|
||||||
pub struct CursorId;
|
pub struct CursorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
pub base: usize,
|
pub base: usize,
|
||||||
pub pos: usize,
|
pub pos: usize,
|
||||||
|
|
@ -147,6 +148,31 @@ pub struct Buffer {
|
||||||
pub cursors: HopSlotMap<CursorId, Cursor>,
|
pub cursors: HopSlotMap<CursorId, Cursor>,
|
||||||
pub dir: Option<PathBuf>,
|
pub dir: Option<PathBuf>,
|
||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
|
pub undo: Vec<Change>,
|
||||||
|
pub redo: Vec<Change>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Change {
|
||||||
|
kind: ChangeKind,
|
||||||
|
cursors: HashMap<CursorId, (Cursor, Cursor)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ChangeKind {
|
||||||
|
Insert(usize, Vec<char>),
|
||||||
|
Remove(usize, Vec<char>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Change {
|
||||||
|
fn invert(mut self) -> Self {
|
||||||
|
self.kind = match self.kind {
|
||||||
|
ChangeKind::Insert(at, s) => ChangeKind::Remove(at, s),
|
||||||
|
ChangeKind::Remove(at, s) => ChangeKind::Insert(at, s),
|
||||||
|
};
|
||||||
|
for (from, to) in self.cursors.values_mut() {
|
||||||
|
core::mem::swap(from, to);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
|
|
@ -175,6 +201,8 @@ impl Buffer {
|
||||||
cursors: HopSlotMap::default(),
|
cursors: HopSlotMap::default(),
|
||||||
dir,
|
dir,
|
||||||
path: Some(path),
|
path: Some(path),
|
||||||
|
undo: Vec::new(),
|
||||||
|
redo: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +233,7 @@ impl Buffer {
|
||||||
.map(|hl| hl.highlighter.highlight(self.text.chars()));
|
.map(|hl| hl.highlighter.highlight(self.text.chars()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.unsaved = true;
|
self.unsaved = true;
|
||||||
|
|
||||||
self.text.chars.clear();
|
self.text.chars.clear();
|
||||||
|
|
@ -214,6 +242,7 @@ impl Buffer {
|
||||||
self.cursors.values_mut().for_each(|cursor| {
|
self.cursors.values_mut().for_each(|cursor| {
|
||||||
*cursor = Cursor::default();
|
*cursor = Cursor::default();
|
||||||
});
|
});
|
||||||
|
self.undo = Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn goto_cursor(&mut self, cursor_id: CursorId, pos: [isize; 2]) {
|
pub fn goto_cursor(&mut self, cursor_id: CursorId, pos: [isize; 2]) {
|
||||||
|
|
@ -398,18 +427,92 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, pos: usize, chars: impl IntoIterator<Item = char>) {
|
fn push_undo(&mut self, mut change: Change) {
|
||||||
self.unsaved = true;
|
self.redo.clear(); // TODO: Maybe add tree undos?
|
||||||
|
|
||||||
|
let Some(last) = self.undo.last_mut() else {
|
||||||
|
return self.undo.push(change);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attempt to merge changes together
|
||||||
|
match (&mut last.kind, &mut change.kind) {
|
||||||
|
(ChangeKind::Insert(at, s), ChangeKind::Insert(at2, s2)) if *at + s.len() == *at2 => {
|
||||||
|
s.append(s2);
|
||||||
|
}
|
||||||
|
(ChangeKind::Remove(at, s), ChangeKind::Remove(at2, s2)) if *at == *at2 + s2.len() => {
|
||||||
|
s2.append(s);
|
||||||
|
*s = core::mem::take(s2);
|
||||||
|
*at = *at2;
|
||||||
|
}
|
||||||
|
_ => return self.undo.push(change),
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id, (from2, to2)) in change.cursors {
|
||||||
|
last.cursors
|
||||||
|
.entry(id)
|
||||||
|
.and_modify(|(_, to)| *to = to2)
|
||||||
|
.or_insert((from2, to2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_change(&mut self, change: &Change) {
|
||||||
|
match &change.kind {
|
||||||
|
ChangeKind::Insert(at, s) => {
|
||||||
|
for (i, c) in s.iter().enumerate() {
|
||||||
|
self.text.chars.insert(at + i, *c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChangeKind::Remove(at, s) => {
|
||||||
|
self.text.chars.drain(*at..*at + s.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (id, (_, to)) in change.cursors.iter() {
|
||||||
|
if let Some(c) = self.cursors.get_mut(*id) {
|
||||||
|
// panic!("Changing {c:?} to {to:?}");
|
||||||
|
*c = *to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.update_highlights();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn undo(&mut self) -> bool {
|
||||||
|
if let Some(change) = self.undo.pop() {
|
||||||
|
let change = change.invert();
|
||||||
|
self.apply_change(&change);
|
||||||
|
self.redo.push(change);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redo(&mut self) -> bool {
|
||||||
|
if let Some(change) = self.redo.pop() {
|
||||||
|
let change = change.invert();
|
||||||
|
self.apply_change(&change);
|
||||||
|
self.undo.push(change);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_inner(&mut self, pos: usize, chars: impl IntoIterator<Item = char>) -> Change {
|
||||||
|
let chars = chars.into_iter().collect::<Vec<_>>();
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
for c in chars {
|
let base = pos.min(self.text.chars.len());
|
||||||
self.text
|
for c in &chars {
|
||||||
.chars
|
self.text.chars.insert(base + n, *c);
|
||||||
.insert((pos + n).min(self.text.chars.len()), c);
|
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
self.update_highlights();
|
self.update_highlights();
|
||||||
self.cursors.values_mut().for_each(|cursor| {
|
Change {
|
||||||
|
kind: ChangeKind::Insert(base, chars),
|
||||||
|
cursors: self
|
||||||
|
.cursors
|
||||||
|
.iter_mut()
|
||||||
|
.map(|(id, cursor)| {
|
||||||
|
let old = *cursor;
|
||||||
if cursor.base >= pos {
|
if cursor.base >= pos {
|
||||||
cursor.base += n;
|
cursor.base += n;
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +520,56 @@ impl Buffer {
|
||||||
cursor.pos += n;
|
cursor.pos += n;
|
||||||
cursor.reset_desired_col(&self.text);
|
cursor.reset_desired_col(&self.text);
|
||||||
}
|
}
|
||||||
});
|
(id, (old, *cursor))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, pos: usize, chars: impl IntoIterator<Item = char>) {
|
||||||
|
self.unsaved = true;
|
||||||
|
let change = self.insert_inner(pos, chars);
|
||||||
|
self.push_undo(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes range is well-formed
|
||||||
|
fn remove_inner(&mut self, range: Range<usize>) -> Change {
|
||||||
|
self.unsaved = true;
|
||||||
|
|
||||||
|
// TODO: Bell if false?
|
||||||
|
let removed = self.text.chars.drain(range.clone()).collect();
|
||||||
|
self.update_highlights();
|
||||||
|
Change {
|
||||||
|
kind: ChangeKind::Remove(range.start, removed),
|
||||||
|
cursors: self
|
||||||
|
.cursors
|
||||||
|
.iter_mut()
|
||||||
|
.map(|(id, cursor)| {
|
||||||
|
let old = *cursor;
|
||||||
|
if cursor.base >= range.start {
|
||||||
|
cursor.base = cursor
|
||||||
|
.base
|
||||||
|
.saturating_sub(range.end - range.start)
|
||||||
|
.max(range.start);
|
||||||
|
}
|
||||||
|
if cursor.pos >= range.start {
|
||||||
|
cursor.pos = cursor
|
||||||
|
.pos
|
||||||
|
.saturating_sub(range.end - range.start)
|
||||||
|
.max(range.start);
|
||||||
|
cursor.reset_desired_col(&self.text);
|
||||||
|
}
|
||||||
|
(id, (old, *cursor))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes range is well-formed
|
||||||
|
pub fn remove(&mut self, range: Range<usize>) {
|
||||||
|
self.unsaved = true;
|
||||||
|
let change = self.remove_inner(range);
|
||||||
|
self.push_undo(change);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_after(&mut self, cursor_id: CursorId, chars: impl IntoIterator<Item = char>) {
|
pub fn insert_after(&mut self, cursor_id: CursorId, chars: impl IntoIterator<Item = char>) {
|
||||||
|
|
@ -444,30 +596,6 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assumes range is well-formed
|
|
||||||
pub fn remove(&mut self, range: Range<usize>) {
|
|
||||||
self.unsaved = true;
|
|
||||||
|
|
||||||
// TODO: Bell if false?
|
|
||||||
self.text.chars.drain(range.clone());
|
|
||||||
self.update_highlights();
|
|
||||||
self.cursors.values_mut().for_each(|cursor| {
|
|
||||||
if cursor.base >= range.start {
|
|
||||||
cursor.base = cursor
|
|
||||||
.base
|
|
||||||
.saturating_sub(range.end - range.start)
|
|
||||||
.max(range.start);
|
|
||||||
}
|
|
||||||
if cursor.pos >= range.start {
|
|
||||||
cursor.pos = cursor
|
|
||||||
.pos
|
|
||||||
.saturating_sub(range.end - range.start)
|
|
||||||
.max(range.start);
|
|
||||||
cursor.reset_desired_col(&self.text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn backspace(&mut self, cursor_id: CursorId) {
|
pub fn backspace(&mut self, cursor_id: CursorId) {
|
||||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -507,11 +635,17 @@ impl Buffer {
|
||||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let line = self.text.to_coord(cursor.pos)[1];
|
let coord = self.text.to_coord(cursor.pos);
|
||||||
let line_start = self.text.to_pos([0, line]);
|
let line_start = self.text.to_pos([0, coord[1]]);
|
||||||
|
|
||||||
let prev_indent = self.text.indent_of_line(line).to_vec();
|
let prev_indent = self
|
||||||
let next_indent = self.text.indent_of_line(line + 1).to_vec();
|
.text
|
||||||
|
.indent_of_line(coord[1])
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.take(coord[0] as usize)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let next_indent = self.text.indent_of_line(coord[1] + 1).to_vec();
|
||||||
|
|
||||||
let (close_block, extra_indent, trailing_indent, base_indent) = if let Some(last_pos) =
|
let (close_block, extra_indent, trailing_indent, base_indent) = if let Some(last_pos) =
|
||||||
cursor
|
cursor
|
||||||
|
|
|
||||||
|
|
@ -277,6 +277,7 @@ pub struct Terminal<'a> {
|
||||||
stdout: StdoutLock<'a>,
|
stdout: StdoutLock<'a>,
|
||||||
size: [u16; 2],
|
size: [u16; 2],
|
||||||
fb: [Framebuffer; 2],
|
fb: [Framebuffer; 2],
|
||||||
|
bell: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Terminal<'a> {
|
impl<'a> Terminal<'a> {
|
||||||
|
|
@ -304,6 +305,7 @@ impl<'a> Terminal<'a> {
|
||||||
stdout: io::stdout().lock(),
|
stdout: io::stdout().lock(),
|
||||||
size: [size.columns, size.rows],
|
size: [size.columns, size.rows],
|
||||||
fb: [Framebuffer::default(), Framebuffer::default()],
|
fb: [Framebuffer::default(), Framebuffer::default()],
|
||||||
|
bell: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let hook = panic::take_hook();
|
let hook = panic::take_hook();
|
||||||
|
|
@ -322,6 +324,10 @@ impl<'a> Terminal<'a> {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ring_bell(&mut self) {
|
||||||
|
self.bell = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, render: impl FnOnce(&mut Rect)) {
|
pub fn update(&mut self, render: impl FnOnce(&mut Rect)) {
|
||||||
// Reset framebuffer
|
// Reset framebuffer
|
||||||
if self.fb[0].size != self.size {
|
if self.fb[0].size != self.size {
|
||||||
|
|
@ -337,6 +343,11 @@ impl<'a> Terminal<'a> {
|
||||||
|
|
||||||
self.stdout
|
self.stdout
|
||||||
.sync_update(|stdout| {
|
.sync_update(|stdout| {
|
||||||
|
if self.bell {
|
||||||
|
self.bell = false;
|
||||||
|
stdout.queue(style::Print('\x07')).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
let mut cursor_pos = [0, 0];
|
let mut cursor_pos = [0, 0];
|
||||||
let mut fg = Color::Reset;
|
let mut fg = Color::Reset;
|
||||||
let mut bg = Color::Reset;
|
let mut bg = Color::Reset;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ impl Input {
|
||||||
.or_else(|| e.to_select_all())
|
.or_else(|| e.to_select_all())
|
||||||
.or_else(|| e.to_indent())
|
.or_else(|| e.to_indent())
|
||||||
.or_else(|| e.to_mouse(self.last_area))
|
.or_else(|| e.to_mouse(self.last_area))
|
||||||
|
.or_else(|| e.to_undo_redo())
|
||||||
}) {
|
}) {
|
||||||
Some(Action::Char(c)) => {
|
Some(Action::Char(c)) => {
|
||||||
if c == '\x08' {
|
if c == '\x08' {
|
||||||
|
|
@ -92,6 +93,7 @@ impl Input {
|
||||||
}
|
}
|
||||||
Some(Action::Indent(forward)) => {
|
Some(Action::Indent(forward)) => {
|
||||||
buffer.indent(cursor_id, forward);
|
buffer.indent(cursor_id, forward);
|
||||||
|
self.refocus(buffer, cursor_id);
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(Action::GotoLine(line)) => {
|
Some(Action::GotoLine(line)) => {
|
||||||
|
|
@ -101,6 +103,7 @@ impl Input {
|
||||||
}
|
}
|
||||||
Some(Action::SelectToken) => {
|
Some(Action::SelectToken) => {
|
||||||
buffer.select_token_cursor(cursor_id);
|
buffer.select_token_cursor(cursor_id);
|
||||||
|
self.refocus(buffer, cursor_id);
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(Action::SelectAll) => {
|
Some(Action::SelectAll) => {
|
||||||
|
|
@ -109,8 +112,23 @@ impl Input {
|
||||||
}
|
}
|
||||||
Some(Action::Mouse(MouseAction::Click, pos)) => {
|
Some(Action::Mouse(MouseAction::Click, pos)) => {
|
||||||
buffer.goto_cursor(cursor_id, [self.focus[0] + pos[0], self.focus[1] + pos[1]]);
|
buffer.goto_cursor(cursor_id, [self.focus[0] + pos[0], self.focus[1] + pos[1]]);
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
}
|
||||||
|
Some(Action::Undo) => {
|
||||||
|
if buffer.undo() {
|
||||||
self.refocus(buffer, cursor_id);
|
self.refocus(buffer, cursor_id);
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
|
} else {
|
||||||
|
Ok(Resp::handled(Some(Event::Bell)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Action::Redo) => {
|
||||||
|
if buffer.redo() {
|
||||||
|
self.refocus(buffer, cursor_id);
|
||||||
|
Ok(Resp::handled(None))
|
||||||
|
} else {
|
||||||
|
Ok(Resp::handled(Some(Event::Bell)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => Err(event),
|
_ => Err(event),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl Element<()> for Prompt {
|
||||||
Some(Action::Cancel) => Ok(Resp::end(None)),
|
Some(Action::Cancel) => Ok(Resp::end(None)),
|
||||||
Some(Action::Go) => match self.parse_action() {
|
Some(Action::Go) => match self.parse_action() {
|
||||||
Ok(action) => {
|
Ok(action) => {
|
||||||
self.buffer.clear();
|
self.buffer.reset();
|
||||||
Ok(Resp::end(Some(action.into())))
|
Ok(Resp::end(Some(action.into())))
|
||||||
}
|
}
|
||||||
Err(err) => Ok(Resp::handled(Some(
|
Err(err) => Ok(Resp::handled(Some(
|
||||||
|
|
@ -303,7 +303,7 @@ impl Opener {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_string(&mut self, s: &str) {
|
fn set_string(&mut self, s: &str) {
|
||||||
self.buffer.clear();
|
self.buffer.reset();
|
||||||
self.buffer.enter(self.cursor_id, s.chars());
|
self.buffer.enter(self.cursor_id, s.chars());
|
||||||
self.update_completions();
|
self.update_completions();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ impl Element<()> for Root {
|
||||||
fn handle(&mut self, state: &mut State, mut event: Event) -> Result<Resp<()>, Event> {
|
fn handle(&mut self, state: &mut State, mut event: Event) -> Result<Resp<()>, Event> {
|
||||||
// Pass the event down through the list of tasks until we meet one that can handle it
|
// Pass the event down through the list of tasks until we meet one that can handle it
|
||||||
let mut task_idx = self.tasks.len();
|
let mut task_idx = self.tasks.len();
|
||||||
let action = loop {
|
let event = loop {
|
||||||
task_idx = match task_idx.checked_sub(1) {
|
task_idx = match task_idx.checked_sub(1) {
|
||||||
Some(task_idx) => task_idx,
|
Some(task_idx) => task_idx,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -77,7 +77,7 @@ impl Element<()> for Root {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle 'top-level' actions
|
// Handle 'top-level' actions
|
||||||
if let Some(action) = action.and_then(|e| {
|
if let Some(action) = event.as_ref().and_then(|e| {
|
||||||
e.to_action(|e| {
|
e.to_action(|e| {
|
||||||
e.to_open_prompt()
|
e.to_open_prompt()
|
||||||
.or_else(|| e.to_cancel())
|
.or_else(|| e.to_cancel())
|
||||||
|
|
@ -126,6 +126,8 @@ impl Element<()> for Root {
|
||||||
.map(|r| r.into_can_end());
|
.map(|r| r.into_can_end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some(event) = event {
|
||||||
|
return Err(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root element swallows all other events
|
// Root element swallows all other events
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue