Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Allow to configure additional vi mode Escape mapping #760

Open
fogine opened this issue Jul 25, 2017 · 13 comments
Open

[Feature] Allow to configure additional vi mode Escape mapping #760

fogine opened this issue Jul 25, 2017 · 13 comments

Comments

@fogine
Copy link

fogine commented Jul 25, 2017

Description

GNU read line allows to globally map jj key sequence to ESC for quick insert mode -> command mode transition.
I'm not aware of any global configuration for prompt_toolkit.

Your environment

> pgcli --version  

Version: 1.7.0

> uname -a  

Linux myuser 4.11.9-1-ARCH #1 SMP PREEMPT Wed Jul 5 18:23:08 CEST 2017 x86_64 GNU/Linux

Currently I've patched the key_bindings.py like so:

diff --git a/pgcli/key_bindings.py b/pgcli/key_bindings.py                                                                                                                                                                                    
index 646e6f7..b623c62 100644
--- a/pgcli/key_bindings.py
+++ b/pgcli/key_bindings.py
@@ -2,7 +2,8 @@ import logging
 from prompt_toolkit.enums import EditingMode
 from prompt_toolkit.keys import Keys
 from prompt_toolkit.key_binding.manager import KeyBindingManager
-from prompt_toolkit.filters import Condition
+from prompt_toolkit.key_binding.input_processor import KeyPress
+from prompt_toolkit.filters import Condition, ViInsertMode
 from .filters import HasSelectedCompletion
 
 _logger = logging.getLogger(__name__)
@@ -22,6 +23,14 @@ def pgcli_bindings(get_vi_mode_enabled, set_vi_mode_enabled):
         enable_search=True,
         enable_abort_and_exit_bindings=True)
 
+    @key_binding_manager.registry.add_binding('j', 'j', filter=ViInsertMode())
+    def _(event):
+        """
+        Typing 'jj' in Insert mode, should go back to navigation mode.
+        """
+        _logger.debug('Detected jj keys.')
+        event.cli.input_processor.feed(KeyPress(Keys.Escape))
+
     @key_binding_manager.registry.add_binding(Keys.F2)
     def _(event):
         """
@arturbalabanov
Copy link

arturbalabanov commented Apr 27, 2018

Can we do a more general solution to that -- to enable any arbitrary key bindings (in vi mode or not) from a config file? PtPython allows it but its config file is a python file, so you can simply put your mappings as they are like in the proposed solution. In pgcli's case I am not sure where would be the place of these mappings though.

I'll be happy to implement it when I have time as soon as we agree on how would the user specify their key bindings.

@cswingle
Copy link

cswingle commented Oct 6, 2018

The new version uses prompt_toolkit 2.0, which no longer uses KeyBindingManager. Any idea on how to hack key_bindings.py to allow for custom vi mode key bindings? Or does the 2.0 version of promt_toolkit have a readline-style configuration for vi mode?

@cswingle
Copy link

cswingle commented Oct 6, 2018

Answering my own question, here's a patch that adds a jk binding to switch to vi normal/navigation mode. It's got no testing to see if the user is even in vi mode, so use at your own risk.

diff --git a/pgcli/key_bindings.py b/pgcli/key_bindings.py
index f1eaaa39..0e21904c 100644
--- a/pgcli/key_bindings.py
+++ b/pgcli/key_bindings.py
@@ -4,6 +4,7 @@ import logging
 from prompt_toolkit.enums import EditingMode
 from prompt_toolkit.key_binding import KeyBindings
 from prompt_toolkit.filters import completion_is_selected
+from prompt_toolkit.key_binding.vi_state import InputMode

 _logger = logging.getLogger(__name__)

@@ -12,6 +13,12 @@ def pgcli_bindings(pgcli):
     """Custom key bindings for pgcli."""
     kb = KeyBindings()

+    @kb.add('j', 'k')
+    def _(event):
+        """vi Normal mode."""
+        _logger.debug('Detected jk keystroke.')
+        event.cli.vi_state.input_mode = InputMode.NAVIGATION
+
     @kb.add('f2')
     def _(event):
         """Enable/Disable SmartCompletion Mode."""

@j-bennet
Copy link
Contributor

j-bennet commented Oct 6, 2018

@jonathanslenders , do you have any advice here?

@aste4
Copy link

aste4 commented Nov 24, 2019

Any updates on the progress of this feature? The above patch seems to work as a temporary fix.

I found this project a few hours ago and I am loving it!

@rightaway
Copy link

Would maintainers be interested in merging this patch?

@amjith
Copy link
Member

amjith commented Apr 22, 2020

Providing a way to override specific features in pgcli is one thing, but overriding how vi keybindings are handled in pgcli seems sufficiently obscure that I'm not sure it is worth adding complexity to the code base.

I wonder if we can have a prompt-toolkit level configuration that will apply to all apps that use prompt-toolkit as readline replacement. @jonathanslenders Thoughts?

@jonathanslenders
Copy link
Contributor

jonathanslenders commented Apr 22, 2020

This is a feature that can be configured in ptpython.

Ideally if you have a Python configuration file, it should be possible to define a function in that file (lets call it def custom_bindings(), which returns a prompt_toolkit.key_binding.Keybindings object.

These key bindings can then be merged into the main pgcli key bindings like this:
https://python-prompt-toolkit.readthedocs.io/en/master/pages/advanced_topics/key_bindings.html#merging-key-bindings

The configuration function should then look something like this:

from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.input_processor import KeyPress
from prompt_toolkit.filters import vi_insert_mode

def create_key_bindings():
    bindings = KeyBindings()

    @bindings.add('j', 'j', filter=vi_insert_mode)
    def esc(event):
        event.cli.input_processor.feed(KeyPress(Keys.Escape))

    return bindings

In the ptpython config, you can see that the key bindings are registered in a KeyBindings object that we already have. It was a decision I made back then, but I think it's not really better. Something else you can see in the ptpython config is that the key bindings have access to the "repl" object. That's the main application object. That's useful, because then the custom bindings can change about anything to the application.

One question maybe for everyone: would it make sense to have a global .prompt_toolkit.config.py file or something like this, were these custom bindings could be configured for every prompt_toolkit application. Similar to .inputrc for all GNU readline applications? (edit: of course, key bindings in the global configuration file don't have access to application specific objects.)

@cswingle
Copy link

@jonathanslenders

One question maybe for everyone: would it make sense to have a global .prompt_toolkit.config.py file or something like this, were these custom bindings could be configured for every prompt_toolkit application. Similar to .inputrc for all GNU readline applications?

For my part that would be optimal, and I think it's pretty likely that anyone going to the trouble to configure a vi_insert_mode key combination for one application is going to want it for every application using prompt_toolkit, just like the way it works with readline.

@fogine
Copy link
Author

fogine commented Apr 22, 2020

Yes, global configuration would be great as I patch multiple projects that use prompt_toolkit, eg. mycli as well..

@rightaway
Copy link

@jonathanslenders This function would be in its own standalone file?

And in which file do I put the merging function from the link?

@rightaway
Copy link

Could anyone please explain how to get this to work?

@caticoa3
Copy link

caticoa3 commented Jul 9, 2020

You'll need to edit key_binding.py

After modifying the above examples (btw thanks everyone for sharing those snippets), the following worked for me:
The changes appear to be necessary due to updates to the prompt-toolkit library.

diff --git a/pgcli/key_bindings.py b/pgcli/key_bindings.py
index 23174b6..9437a83 100644
--- a/pgcli/key_bindings.py
+++ b/pgcli/key_bindings.py
@@ -1,7 +1,10 @@
 import logging
 from prompt_toolkit.enums import EditingMode
+from prompt_toolkit.keys import Keys
 from prompt_toolkit.key_binding import KeyBindings
+from prompt_toolkit.key_binding.key_processor import KeyPress
 from prompt_toolkit.filters import (
+    ViInsertMode,
     completion_is_selected,
     is_searching,
     has_completions,
@@ -124,4 +127,12 @@ def pgcli_bindings(pgcli):
         """Move down in history."""
         event.current_buffer.history_forward(count=event.arg)

+    @kb.add("k", "j", filter=ViInsertMode())
+    def _(event):
+        """
+        Typing 'kj' in Insert mode, should go back to navigation mode.
+        """
+        _logger.debug('Detected kj keys.')
+        event.cli.key_processor.feed(KeyPress(Keys.Escape))
+
     return kb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants