Better newline behaviour
This commit is contained in:
parent
551b2816b3
commit
0912450513
4 changed files with 96 additions and 4 deletions
|
|
@ -34,6 +34,7 @@ pub enum Action {
|
|||
CommandStart(&'static str), // Start a new command
|
||||
GotoLine(isize), // Go to the specified file line
|
||||
SelectToken, // Fully select the token under the cursor
|
||||
SelectAll, // Fully select the entire input
|
||||
Save, // Save the current buffer
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +191,22 @@ impl RawEvent {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_select_all(&self) -> Option<Action> {
|
||||
if matches!(
|
||||
&self.0,
|
||||
TerminalEvent::Key(KeyEvent {
|
||||
code: KeyCode::Char('a'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
kind: KeyEventKind::Press,
|
||||
..
|
||||
})
|
||||
) {
|
||||
Some(Action::SelectAll)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_indent(&self) -> Option<Action> {
|
||||
if let TerminalEvent::Key(KeyEvent {
|
||||
code: c @ (KeyCode::Tab | KeyCode::BackTab),
|
||||
|
|
|
|||
61
src/state.rs
61
src/state.rs
|
|
@ -241,6 +241,14 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn select_all_cursor(&mut self, cursor_id: CursorId) {
|
||||
let Some(cursor) = self.cursors.get_mut(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
cursor.base = 0;
|
||||
cursor.pos = self.text.chars().len();
|
||||
}
|
||||
|
||||
fn indent_at(&mut self, mut pos: usize, forward: bool) {
|
||||
const TAB_ALIGN: usize = 4;
|
||||
|
||||
|
|
@ -385,6 +393,18 @@ impl Buffer {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn insert_after(&mut self, cursor_id: CursorId, chars: impl IntoIterator<Item = char>) {
|
||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
let old_cursor = *cursor;
|
||||
self.insert(old_cursor.pos, chars);
|
||||
let Some(cursor) = self.cursors.get_mut(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
*cursor = old_cursor;
|
||||
}
|
||||
|
||||
pub fn enter(&mut self, cursor_id: CursorId, chars: impl IntoIterator<Item = char>) {
|
||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||
return;
|
||||
|
|
@ -448,6 +468,47 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn newline(&mut self, cursor_id: CursorId) {
|
||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
let line_start = self.text.to_pos([0, self.text.to_coord(cursor.pos)[1]]);
|
||||
let is_block = if let Some(last_pos) = cursor
|
||||
.selection()
|
||||
.map_or(cursor.pos, |s| s.start)
|
||||
.checked_sub(1)
|
||||
&& let Some(last_char) = self.text.chars().get(last_pos)
|
||||
&& let Some((l, r)) = [('(', ')'), ('[', ']'), ('{', '}')]
|
||||
.iter()
|
||||
.find(|(l, _)| l == last_char)
|
||||
&& let next_pos = cursor.selection().map_or(cursor.pos, |s| s.end)
|
||||
&& let next_char = self.text.chars().get(next_pos)
|
||||
{
|
||||
Some((*r, next_char == Some(r)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.enter(cursor_id, ['\n']);
|
||||
|
||||
// Indent to same level as last line
|
||||
if let Some(chars) = self.text.chars().get(line_start..) {
|
||||
let indent = chars
|
||||
.iter()
|
||||
.take_while(|c| [' ', '\t'].contains(c))
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
self.enter(cursor_id, indent.iter().copied());
|
||||
// If the last character was the start of a block, perform an additional indent
|
||||
if let Some((r, is_complete)) = is_block {
|
||||
self.indent(cursor_id, true);
|
||||
// If the block was not already completed, complete it (TODO: make configurable!)
|
||||
let tail = if !is_complete { Some(r) } else { None };
|
||||
self.insert_after(cursor_id, core::iter::once('\n').chain(indent).chain(tail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_session(&mut self) -> CursorId {
|
||||
self.cursors.insert(Cursor::default())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ impl Input {
|
|||
.map(Action::Char)
|
||||
.or_else(|| e.to_move())
|
||||
.or_else(|| e.to_select_token())
|
||||
.or_else(|| e.to_select_all())
|
||||
.or_else(|| e.to_indent())
|
||||
}) {
|
||||
Some(Action::Char(c)) => {
|
||||
|
|
@ -69,6 +70,8 @@ impl Input {
|
|||
buffer.backspace(cursor_id);
|
||||
} else if c == '\x7F' {
|
||||
buffer.delete(cursor_id);
|
||||
} else if c == '\n' {
|
||||
buffer.newline(cursor_id);
|
||||
} else {
|
||||
buffer.enter(cursor_id, [c]);
|
||||
}
|
||||
|
|
@ -98,6 +101,10 @@ impl Input {
|
|||
buffer.select_token_cursor(cursor_id);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::SelectAll) => {
|
||||
buffer.select_all_cursor(cursor_id);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
_ => Err(event),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,10 +103,17 @@ impl Element<()> for Root {
|
|||
self.tasks
|
||||
.push(Task::Prompt(Prompt::new(&format!("{cmd} "))));
|
||||
}
|
||||
Action::Cancel => self.tasks.push(Task::Confirm(Confirm {
|
||||
label: Label("Are you sure you wish to quit? (y/n)".to_string()),
|
||||
action: Action::Quit,
|
||||
})),
|
||||
Action::Cancel => {
|
||||
let unsaved = state.buffers.values().filter(|b| b.unsaved).count();
|
||||
if unsaved == 0 {
|
||||
return Ok(Resp::end(None));
|
||||
} else {
|
||||
self.tasks.push(Task::Confirm(Confirm {
|
||||
label: Label(format!("Are you sure you wish to quit? (y/n). Note that {} files are unsaved!", unsaved)),
|
||||
action: Action::Quit,
|
||||
}));
|
||||
}
|
||||
}
|
||||
Action::Show(title, text) => self.tasks.push(Task::Show(Show {
|
||||
title,
|
||||
label: Label(text),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue