Support more control over input buffer

This PR adds a new message: `UpdateInputBuffer: InputOperation`

This makes it possible to perform cursor based input operations without
needing input from the keyboard.
This commit is contained in:
Arijit Basu 2021-11-05 14:52:52 +05:30 committed by Arijit Basu
parent b45a553a0c
commit 05c2f7aa68
3 changed files with 104 additions and 23 deletions

View File

@ -210,9 +210,25 @@ Go to the next path visited.
Follow the symlink under focus to its actual location. Follow the symlink under focus to its actual location.
### { UpdateInputBuffer = [Input Opertaion][71] }
**YAML:** `BufferInput: Input Operation`
Update the input buffer using cursor based operations.
**YAML Example:** `UpdateInputBuffer: GoToPreviousWord`
**Lua Example:** `{ UpdateInputBuffer = "GoToPreviousWord" }`
### "UpdateInputBufferFromKey"
**YAML:** `UpdateInputBufferFromKey`
Update the input buffer from the key read from keyboard input.
### { BufferInput = "string" } ### { BufferInput = "string" }
**YAML:** `BufferInput(String)` **YAML:** `BufferInput: string`
Append/buffer the given string into the input buffer. Append/buffer the given string into the input buffer.
@ -790,6 +806,24 @@ Write the application state to a file, without quitting. Also helpful for debugg
Terminate the application with a non-zero return code. Terminate the application with a non-zero return code.
## InputOperation
Cursor based input operation can be one of the following:
- { SetCursor = int }
- { InsertCharacter = str }
- "GoToPreviousCharacter"
- "GoToNextCharacter"
- "GoToPreviousWord"
- "GoToNextWord"
- "GoToStart"
- "GoToEnd"
- "DeletePreviousCharacter"
- "DeleteNextCharacter"
- "DeletePreviousWord"
- "DeleteNextWord"
- "DeleteLine"
## Lua Function Calls ## Lua Function Calls
xplr allows users to define lua functions using the `xplr.fn.custom` Lua API. xplr allows users to define lua functions using the `xplr.fn.custom` Lua API.
@ -1286,3 +1320,4 @@ xplr.config.modes.builtin.default.key_bindings.on_key.space = {
[68]: #loc [68]: #loc
[69]: #paths [69]: #paths
[70]: #history-1 [70]: #history-1
[71]: #inputoperation

View File

@ -1,7 +1,7 @@
use crate::config::Config; use crate::config::Config;
use crate::config::Mode; use crate::config::Mode;
use crate::explorer; use crate::explorer;
use crate::input::Key; use crate::input::{InputOperation, Key};
use crate::lua; use crate::lua;
use crate::permissions::Permissions; use crate::permissions::Permissions;
use crate::ui::Layout; use crate::ui::Layout;
@ -777,6 +777,11 @@ pub enum ExternalMsg {
/// Follow the symlink under focus to its actual location. /// Follow the symlink under focus to its actual location.
FollowSymlink, FollowSymlink,
/// Update the input buffer using cursor based operations.
///
/// **Example:** `UpdateInputBuffer: GoToPreviousWord`
UpdateInputBuffer(InputOperation),
/// Update the input buffer from given key /// Update the input buffer from given key
UpdateInputBufferFromKey, UpdateInputBufferFromKey,
@ -1481,6 +1486,9 @@ impl App {
ExternalMsg::LastVisitedPath => self.last_visited_path(), ExternalMsg::LastVisitedPath => self.last_visited_path(),
ExternalMsg::NextVisitedPath => self.next_visited_path(), ExternalMsg::NextVisitedPath => self.next_visited_path(),
ExternalMsg::FollowSymlink => self.follow_symlink(), ExternalMsg::FollowSymlink => self.follow_symlink(),
ExternalMsg::UpdateInputBuffer(op) => {
self.update_input_buffer(op)
}
ExternalMsg::UpdateInputBufferFromKey => { ExternalMsg::UpdateInputBufferFromKey => {
self.update_input_buffer_from_key(key) self.update_input_buffer_from_key(key)
} }
@ -1886,18 +1894,21 @@ impl App {
} }
} }
fn update_input_buffer_from_key( fn update_input_buffer(mut self, op: InputOperation) -> Result<Self> {
mut self,
key: Option<Key>,
) -> Result<Self> {
if let Some(req) = key.and_then(|k| k.to_input_request()) {
if let Some(buf) = self.input.as_mut() { if let Some(buf) = self.input.as_mut() {
buf.handle(req); buf.handle(op.into());
}
} }
Ok(self) Ok(self)
} }
fn update_input_buffer_from_key(self, key: Option<Key>) -> Result<Self> {
if let Some(op) = key.and_then(|k| k.to_input_operation()) {
self.update_input_buffer(op)
} else {
Ok(self)
}
}
fn buffer_input(mut self, input: &str) -> Result<Self> { fn buffer_input(mut self, input: &str) -> Result<Self> {
if let Some(buf) = self.input.as_mut() { if let Some(buf) = self.input.as_mut() {
buf.handle(InputRequest::GoToEnd); buf.handle(InputRequest::GoToEnd);

View File

@ -208,27 +208,25 @@ impl std::fmt::Display for Key {
} }
impl Key { impl Key {
pub fn to_input_request(&self) -> Option<InputRequest> { pub fn to_input_operation(&self) -> Option<InputOperation> {
use InputRequest::*; use InputOperation::*;
use Key::*; use Key::*;
match self { match self {
Backspace => Some(DeletePrevChar), Backspace => Some(DeletePreviousCharacter),
Delete => Some(DeleteNextChar), Delete => Some(DeleteNextCharacter),
Tab => Some(InsertChar('\t')), Tab => Some(InsertCharacter('\t')),
Space => Some(InsertChar(' ')), Space => Some(InsertCharacter(' ')),
Left => Some(GoToPrevChar), Left => Some(GoToPreviousCharacter),
CtrlLeft => Some(GoToPrevWord), CtrlLeft => Some(GoToPreviousWord),
Right => Some(GoToNextChar), Right => Some(GoToNextCharacter),
CtrlRight => Some(GoToNextWord), CtrlRight => Some(GoToNextWord),
CtrlU => Some(DeleteLine), CtrlU => Some(DeleteLine),
CtrlW => Some(DeletePrevWord), CtrlW => Some(DeletePreviousWord),
CtrlDelete => Some(DeleteNextWord), CtrlDelete => Some(DeleteNextWord),
CtrlA => Some(GoToStart), CtrlA => Some(GoToStart),
CtrlE => Some(GoToEnd), CtrlE => Some(GoToEnd),
Enter => Some(Submit), key => key.to_char().map(InsertCharacter),
Esc => Some(Escape),
key => key.to_char().map(InsertChar),
} }
} }
@ -726,3 +724,40 @@ impl PartialOrd for Key {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum InputOperation {
SetCursor(usize),
InsertCharacter(char),
GoToPreviousCharacter,
GoToNextCharacter,
GoToPreviousWord,
GoToNextWord,
GoToStart,
GoToEnd,
DeletePreviousCharacter,
DeleteNextCharacter,
DeletePreviousWord,
DeleteNextWord,
DeleteLine,
}
impl Into<InputRequest> for InputOperation {
fn into(self) -> InputRequest {
match self {
Self::SetCursor(i) => InputRequest::SetCursor(i),
Self::InsertCharacter(c) => InputRequest::InsertChar(c),
Self::GoToPreviousCharacter => InputRequest::GoToPrevChar,
Self::GoToNextCharacter => InputRequest::GoToNextChar,
Self::GoToPreviousWord => InputRequest::GoToPrevWord,
Self::GoToNextWord => InputRequest::GoToNextWord,
Self::GoToStart => InputRequest::GoToStart,
Self::GoToEnd => InputRequest::GoToEnd,
Self::DeletePreviousCharacter => InputRequest::DeletePrevChar,
Self::DeleteNextCharacter => InputRequest::DeleteNextChar,
Self::DeletePreviousWord => InputRequest::DeletePrevWord,
Self::DeleteNextWord => InputRequest::DeleteNextWord,
Self::DeleteLine => InputRequest::DeleteLine,
}
}
}