mirror of
https://github.com/extrawurst/gitui.git
synced 2024-11-21 15:02:40 +03:00
Multiline TextEdit cleanups (#2053)
* change commit default binding * animated gif of multiline text edit * update changelog * fix style of default text * textinput should not need to know about its users * fix branch create popup
This commit is contained in:
parent
b9a2e131f2
commit
825935ba4f
10
CHANGELOG.md
10
CHANGELOG.md
@ -7,10 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
|
||||
** multiline text editor **
|
||||
|
||||
![multiline editor](assets/multiline-texteditor.gif)
|
||||
|
||||
### Breaking Change
|
||||
The Commit message popup now supports multiline editing! Inserting a **newline** defaults to `enter`. This comes with a new default to confirm the commit message (`ctrl+d`).
|
||||
Both commands can be overwritten via `newline` and `commit` in the key bindings. see [KEY_CONFIG](./KEY_CONFIG.md) on how.
|
||||
These defaults require some adoption from existing users but feel more natural to new users.
|
||||
|
||||
### Added
|
||||
* `theme.ron` now supports customizing line break symbol ([#1894](https://github.com/extrawurst/gitui/issues/1894))
|
||||
* add confirmation for dialog for undo commit [[@TeFiLeDo](https://github.com/TeFiLeDo)] ([#1912](https://github.com/extrawurst/gitui/issues/1912))
|
||||
* support `prepare-commit-msg` hook ([#1873](https://github.com/extrawurst/gitui/issues/1873))
|
||||
* support for new-line in text-input (e.g. commit message editor) [[@pm100]](https://github/pm100) ([#1662](https://github.com/extrawurst/gitui/issues/1662)).
|
||||
|
||||
### Changed
|
||||
* do not allow tag when `tag.gpgsign` enabled [[@TeFiLeDo](https://github.com/TeFiLeDo)] ([#1915](https://github.com/extrawurst/gitui/pull/1915))
|
||||
|
BIN
assets/multiline-texteditor.gif
Normal file
BIN
assets/multiline-texteditor.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
@ -512,7 +512,7 @@ impl Component for CommitComponent {
|
||||
|
||||
if self.is_visible() || force_all {
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::commit_enter(&self.key_config),
|
||||
strings::commands::commit_submit(&self.key_config),
|
||||
self.can_commit(),
|
||||
true,
|
||||
));
|
||||
@ -566,57 +566,67 @@ impl Component for CommitComponent {
|
||||
|
||||
fn event(&mut self, ev: &Event) -> Result<EventState> {
|
||||
if self.is_visible() {
|
||||
if self.input.event(ev)?.is_consumed() {
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
|
||||
if let Event::Key(e) = ev {
|
||||
if key_match(e, self.key_config.keys.commit)
|
||||
&& self.can_commit()
|
||||
{
|
||||
try_or_popup!(
|
||||
self,
|
||||
"commit error:",
|
||||
self.commit()
|
||||
);
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.toggle_verify,
|
||||
) && self.can_commit()
|
||||
{
|
||||
self.toggle_verify();
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.commit_amend,
|
||||
) && self.can_amend()
|
||||
{
|
||||
self.amend()?;
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.open_commit_editor,
|
||||
) {
|
||||
self.queue.push(
|
||||
InternalEvent::OpenExternalEditor(None),
|
||||
);
|
||||
self.hide();
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.commit_history_next,
|
||||
) {
|
||||
if let Some(msg) = self
|
||||
.options
|
||||
.borrow()
|
||||
.commit_msg(self.commit_msg_history_idx)
|
||||
let input_consumed =
|
||||
if key_match(e, self.key_config.keys.commit)
|
||||
&& self.can_commit()
|
||||
{
|
||||
self.input.set_text(msg);
|
||||
self.commit_msg_history_idx += 1;
|
||||
}
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.toggle_signoff,
|
||||
) {
|
||||
self.signoff_commit();
|
||||
try_or_popup!(
|
||||
self,
|
||||
"commit error:",
|
||||
self.commit()
|
||||
);
|
||||
true
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.toggle_verify,
|
||||
) && self.can_commit()
|
||||
{
|
||||
self.toggle_verify();
|
||||
true
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.commit_amend,
|
||||
) && self.can_amend()
|
||||
{
|
||||
self.amend()?;
|
||||
true
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.open_commit_editor,
|
||||
) {
|
||||
self.queue.push(
|
||||
InternalEvent::OpenExternalEditor(None),
|
||||
);
|
||||
self.hide();
|
||||
true
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.commit_history_next,
|
||||
) {
|
||||
if let Some(msg) = self
|
||||
.options
|
||||
.borrow()
|
||||
.commit_msg(self.commit_msg_history_idx)
|
||||
{
|
||||
self.input.set_text(msg);
|
||||
self.commit_msg_history_idx += 1;
|
||||
}
|
||||
true
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.toggle_signoff,
|
||||
) {
|
||||
self.signoff_commit();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !input_consumed {
|
||||
self.input.event(ev)?;
|
||||
}
|
||||
|
||||
// stop key event propagation
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
|
@ -23,19 +23,25 @@ use std::cell::Cell;
|
||||
use std::cell::OnceCell;
|
||||
use std::convert::From;
|
||||
use tui_textarea::{CursorMove, Input, Key, Scrolling, TextArea};
|
||||
|
||||
///
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum InputType {
|
||||
Singleline,
|
||||
Multiline,
|
||||
Password,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum SelectionState {
|
||||
Selecting,
|
||||
NotSelecting,
|
||||
SelectionEndPending,
|
||||
}
|
||||
|
||||
type TextAreaComponent = TextArea<'static>;
|
||||
|
||||
///
|
||||
pub struct TextInputComponent {
|
||||
title: String,
|
||||
default_msg: String,
|
||||
@ -75,6 +81,7 @@ impl TextInputComponent {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub const fn with_input_type(
|
||||
mut self,
|
||||
input_type: InputType,
|
||||
@ -135,18 +142,24 @@ impl TextInputComponent {
|
||||
.split('\n')
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
|
||||
self.textarea = Some({
|
||||
let style =
|
||||
self.theme.text(self.selected.unwrap_or(true), false);
|
||||
let mut text_area = TextArea::new(lines);
|
||||
if self.input_type == InputType::Password {
|
||||
text_area.set_mask_char('*');
|
||||
}
|
||||
|
||||
text_area
|
||||
.set_cursor_line_style(self.theme.text(true, false));
|
||||
text_area.set_placeholder_text(self.default_msg.clone());
|
||||
text_area.set_placeholder_style(style);
|
||||
text_area.set_style(style);
|
||||
text_area.set_placeholder_style(
|
||||
self.theme
|
||||
.text(self.selected.unwrap_or_default(), false),
|
||||
);
|
||||
text_area.set_style(
|
||||
self.theme.text(self.selected.unwrap_or(true), false),
|
||||
);
|
||||
|
||||
if !self.embed {
|
||||
text_area.set_block(
|
||||
Block::default()
|
||||
@ -241,6 +254,361 @@ impl TextInputComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines, clippy::unnested_or_patterns)]
|
||||
fn process_inputs(ta: &mut TextArea<'_>, input: &Input) -> bool {
|
||||
match input {
|
||||
Input {
|
||||
key: Key::Char(c),
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.insert_char(*c);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Tab,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.insert_tab();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('h'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Backspace,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.delete_char();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('d'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Delete,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.delete_next_char();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('k'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.delete_line_by_end();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('j'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.delete_line_by_head();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('w'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('h'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Backspace,
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.delete_word();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Delete,
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('d'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.delete_next_word();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('n'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Down,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Down);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('p'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Up,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Up);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('f'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Right,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Forward);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('b'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Left,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Back);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('a'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input { key: Key::Home, .. }
|
||||
| Input {
|
||||
key: Key::Left | Key::Char('b'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Head);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('e'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input { key: Key::End, .. }
|
||||
| Input {
|
||||
key: Key::Right | Key::Char('f'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::End);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('<'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Up | Key::Char('p'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Top);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('>'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Down | Key::Char('n'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Bottom);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('f'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Right,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::WordForward);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('b'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Left,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::WordBack);
|
||||
true
|
||||
}
|
||||
|
||||
Input {
|
||||
key: Key::Char(']'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('n'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Down,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::ParagraphForward);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('['),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('p'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Up,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::ParagraphBack);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('u'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.undo();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('r'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.redo();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('y'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.paste();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('v'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::PageDown, ..
|
||||
} => {
|
||||
ta.scroll(Scrolling::PageDown);
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('v'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::PageUp, ..
|
||||
} => {
|
||||
ta.scroll(Scrolling::PageUp);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawableComponent for TextInputComponent {
|
||||
@ -297,419 +665,50 @@ impl Component for TextInputComponent {
|
||||
)
|
||||
.order(1),
|
||||
);
|
||||
|
||||
//TODO: we might want to show the textarea specific commands here
|
||||
|
||||
visibility_blocking(self)
|
||||
}
|
||||
|
||||
// the fixes this clippy wants make the code much harder to follow
|
||||
#[allow(clippy::unnested_or_patterns)]
|
||||
// it just has to be this big
|
||||
#[allow(clippy::too_many_lines)]
|
||||
|
||||
fn event(&mut self, ev: &Event) -> Result<EventState> {
|
||||
let input = Input::from(ev.clone());
|
||||
self.should_select(&input);
|
||||
if let Some(ta) = &mut self.textarea {
|
||||
if let Event::Key(e) = ev {
|
||||
let modified = if let Event::Key(e) = ev {
|
||||
if key_match(e, self.key_config.keys.exit_popup) {
|
||||
self.hide();
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
|
||||
// for a multi line box we want to allow the user to enter new lines
|
||||
// so test for what might be a different enter to mean 'ok do it'
|
||||
if self.input_type == InputType::Multiline {
|
||||
if key_match(e, self.key_config.keys.commit) {
|
||||
// means pass it back up to the caller to handle
|
||||
return Ok(EventState::NotConsumed);
|
||||
}
|
||||
} else if key_match(e, self.key_config.keys.enter) {
|
||||
// ditto - we dont want it
|
||||
return Ok(EventState::NotConsumed);
|
||||
if key_match(e, self.key_config.keys.newline)
|
||||
&& self.input_type == InputType::Multiline
|
||||
{
|
||||
ta.insert_newline();
|
||||
true
|
||||
} else {
|
||||
Self::process_inputs(ta, &input)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// here all 'known' special keys for any textinput call are filtered out
|
||||
|
||||
if key_match(e, self.key_config.keys.toggle_verify)
|
||||
|| key_match(e, self.key_config.keys.commit_amend)
|
||||
|| key_match(
|
||||
e,
|
||||
self.key_config.keys.open_commit_editor,
|
||||
) || key_match(
|
||||
e,
|
||||
self.key_config.keys.commit_history_next,
|
||||
) {
|
||||
return Ok(EventState::NotConsumed);
|
||||
}
|
||||
|
||||
/*
|
||||
here we do key handling rather than passing it to textareas input function
|
||||
- so that we know which keys were handled and which were not
|
||||
- to get fine control over what each key press does
|
||||
- allow for key mapping based off key config....
|
||||
but in fact the original textinput ignored all key bindings, up,down,right,....
|
||||
so they are also ignored here
|
||||
|
||||
*/
|
||||
|
||||
// was the text buffer changed?
|
||||
|
||||
let modified =
|
||||
if key_match(e, self.key_config.keys.newline)
|
||||
&& self.input_type == InputType::Multiline
|
||||
{
|
||||
ta.insert_newline();
|
||||
true
|
||||
} else {
|
||||
match input {
|
||||
Input {
|
||||
key: Key::Char(c),
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.insert_char(c);
|
||||
true
|
||||
}
|
||||
|
||||
Input {
|
||||
key: Key::Tab,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.insert_tab();
|
||||
true
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('h'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Backspace,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.delete_char(),
|
||||
Input {
|
||||
key: Key::Char('d'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Delete,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.delete_next_char(),
|
||||
Input {
|
||||
key: Key::Char('k'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.delete_line_by_end(),
|
||||
Input {
|
||||
key: Key::Char('j'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.delete_line_by_head(),
|
||||
Input {
|
||||
key: Key::Char('w'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('h'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Backspace,
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
} => ta.delete_word(),
|
||||
Input {
|
||||
key: Key::Delete,
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('d'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
} => ta.delete_next_word(),
|
||||
Input {
|
||||
key: Key::Char('n'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Down,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Down);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('p'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Up,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Up);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('f'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Right,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Forward);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('b'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Left,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Back);
|
||||
false
|
||||
}
|
||||
// normally picked up earlier as 'amend'
|
||||
Input {
|
||||
key: Key::Char('a'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input { key: Key::Home, .. }
|
||||
| Input {
|
||||
key: Key::Left | Key::Char('b'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Head);
|
||||
false
|
||||
}
|
||||
// normally picked up earlier as 'invoke editor'
|
||||
Input {
|
||||
key: Key::Char('e'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input { key: Key::End, .. }
|
||||
| Input {
|
||||
key: Key::Right | Key::Char('f'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::End);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('<'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Up | Key::Char('p'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Top);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('>'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Down | Key::Char('n'),
|
||||
ctrl: true,
|
||||
alt: true,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::Bottom);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('f'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Right,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(
|
||||
CursorMove::WordForward,
|
||||
);
|
||||
false
|
||||
}
|
||||
|
||||
Input {
|
||||
key: Key::Char('b'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Left,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(CursorMove::WordBack);
|
||||
false
|
||||
}
|
||||
|
||||
Input {
|
||||
key: Key::Char(']'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('n'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Down,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(
|
||||
CursorMove::ParagraphForward,
|
||||
);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('['),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Char('p'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::Up,
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => {
|
||||
ta.move_cursor(
|
||||
CursorMove::ParagraphBack,
|
||||
);
|
||||
false
|
||||
}
|
||||
Input {
|
||||
key: Key::Char('u'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.undo(),
|
||||
Input {
|
||||
key: Key::Char('r'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.redo(),
|
||||
Input {
|
||||
key: Key::Char('y'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
} => ta.paste(),
|
||||
Input {
|
||||
key: Key::Char('v'),
|
||||
ctrl: true,
|
||||
alt: false,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::PageDown, ..
|
||||
} => {
|
||||
ta.scroll(Scrolling::PageDown);
|
||||
false
|
||||
}
|
||||
|
||||
Input {
|
||||
key: Key::Char('v'),
|
||||
ctrl: false,
|
||||
alt: true,
|
||||
..
|
||||
}
|
||||
| Input {
|
||||
key: Key::PageUp, ..
|
||||
} => {
|
||||
ta.scroll(Scrolling::PageUp);
|
||||
false
|
||||
}
|
||||
_ => return Ok(EventState::NotConsumed),
|
||||
}
|
||||
};
|
||||
if modified {
|
||||
self.msg.take();
|
||||
}
|
||||
}
|
||||
if self.select_state
|
||||
== SelectionState::SelectionEndPending
|
||||
{
|
||||
ta.cancel_selection();
|
||||
self.select_state = SelectionState::NotSelecting;
|
||||
}
|
||||
return Ok(EventState::Consumed);
|
||||
|
||||
if modified {
|
||||
self.msg.take();
|
||||
return Ok(EventState::Consumed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(EventState::NotConsumed)
|
||||
}
|
||||
|
||||
/*
|
||||
visible maps to textarea Option
|
||||
None = > not visible
|
||||
|
@ -211,8 +211,8 @@ impl Default for KeysList {
|
||||
view_submodule_parent: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()),
|
||||
update_submodule: GituiKeyEvent::new(KeyCode::Char('u'), KeyModifiers::empty()),
|
||||
commit_history_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL),
|
||||
commit: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
|
||||
newline: GituiKeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL),
|
||||
commit: GituiKeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL),
|
||||
newline: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -964,10 +964,12 @@ pub mod commands {
|
||||
CMD_GROUP_COMMIT_POPUP,
|
||||
)
|
||||
}
|
||||
pub fn commit_enter(key_config: &SharedKeyConfig) -> CommandText {
|
||||
pub fn commit_submit(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Commit [{}]",
|
||||
"Do Commit [{}]",
|
||||
key_config.get_hint(key_config.keys.commit),
|
||||
),
|
||||
"commit (available when commit message is non-empty)",
|
||||
|
Loading…
Reference in New Issue
Block a user