All Projects → fgeller → Fingers.el

fgeller / Fingers.el

Modal editing minor mode for Emacs

Projects that are alternatives of or similar to Fingers.el

Emacs Solidity
The official solidity-mode for EMACS
Stars: ✭ 120 (+135.29%)
Mutual labels:  emacs, emacs-mode
Org Msg
OrgMsg is a GNU/Emacs global minor mode mixing up Org mode and Message mode to compose and reply to emails in a Outlook HTML friendly style.
Stars: ✭ 153 (+200%)
Mutual labels:  emacs, emacs-mode
Jq Mode
Emacs major mode for editing jq queries.
Stars: ✭ 70 (+37.25%)
Mutual labels:  emacs, emacs-mode
Webpaste.el
webpaste.el can paste whole buffers or parts of buffers to several pastebin-like services and supports failover if one service fails.
Stars: ✭ 67 (+31.37%)
Mutual labels:  emacs, emacs-mode
Writegood Mode
Minor mode for Emacs to improve English writing
Stars: ✭ 369 (+623.53%)
Mutual labels:  emacs, emacs-mode
Live Py Plugin
Live coding in Python with PyCharm, Emacs, Sublime Text, or even a browser
Stars: ✭ 222 (+335.29%)
Mutual labels:  emacs, emacs-mode
Psc Ide Emacs
Emacs integration for PureScript's psc-ide tool.
Stars: ✭ 130 (+154.9%)
Mutual labels:  emacs, emacs-mode
Nyan Mode
Nyan Cat for Emacs! Nyanyanyanyanyanyanyanyanyan!
Stars: ✭ 590 (+1056.86%)
Mutual labels:  emacs, emacs-mode
Swift Mode
Emacs support for Apple's Swift programming language.
Stars: ✭ 308 (+503.92%)
Mutual labels:  emacs, emacs-mode
Zoom
Fixed and automatic balanced window layout for Emacs
Stars: ✭ 252 (+394.12%)
Mutual labels:  emacs, emacs-mode
Deft
Deft for Emacs
Stars: ✭ 521 (+921.57%)
Mutual labels:  emacs, emacs-mode
Clj Refactor.el
A collection of Clojure refactoring functions for Emacs
Stars: ✭ 694 (+1260.78%)
Mutual labels:  emacs, emacs-mode
Mix Format.el
Emacs package to format Elixir code in Emacs with elixir-mode
Stars: ✭ 46 (-9.8%)
Mutual labels:  emacs
Ikeysnail
iKeySnail provides fully-configurable hardware keyboard functionalities for web browsing on iOS (iPadOS)
Stars: ✭ 48 (-5.88%)
Mutual labels:  emacs
Cfrs
Child Frame Read String
Stars: ✭ 46 (-9.8%)
Mutual labels:  emacs
Elisp Lint
Basic linting for Emacs Lisp
Stars: ✭ 45 (-11.76%)
Mutual labels:  emacs
Graphql.el
GraphQL utilities
Stars: ✭ 50 (-1.96%)
Mutual labels:  emacs
Paredit Cheatsheet
A new, scalable source document for the Paredit Cheatsheet available as a png on the Emacs wiki
Stars: ✭ 48 (-5.88%)
Mutual labels:  emacs
Etrace
Emacs Lisp Latency Tracing for the Chromium Catapult Trace Event Format
Stars: ✭ 45 (-11.76%)
Mutual labels:  emacs
Apib Mode
Emacs API Blueprint major mode
Stars: ✭ 44 (-13.73%)
Mutual labels:  emacs-mode
  • fingers.el

    Modal text editing for Emacs: A collection of key bindings for navigation and text manipulation.

    [[https://melpa.org/#/fingers][file:https://melpa.org/packages/fingers-badge.svg]]

    Cheat sheet:

    [[https://raw.githubusercontent.com/fgeller/fingers.el/master/images/cheatsheet.png][file:https://raw.githubusercontent.com/fgeller/fingers.el/master/images/cheatsheet.png]]

** Installation

You can install this package via [[http://melpa.milkbox.net:1337/#/][MELPA]] or add this repository manually to your =load-path=. Load the library in your preferred way, for example:

#+begin_src emacs-lisp (require 'fingers) #+end_src

For Qwerty users, add the following to change the main bindings:

#+begin_src emacs-lisp (require 'fingers-qwerty) (setq fingers-region-specifiers fingers-qwerty-region-specifiers) (setq fingers-keyboard-layout-mapper 'fingers-workman-to-qwerty) (fingers-reset-bindings) #+end_src

Refer to =fingers-qwerty.el= and =fingers-neo.el= for more information about the mappings.

Next you should bind the function =global-fingers-mode= to enable and disable =fingers-mode= globally. You can bind this function to any convenient key sequence. For example, I use [[http://www.emacswiki.org/emacs/KeyChord][key-chord]] to toggle it via =oe=, where =jk= would be better suited for Qwerty users:

#+begin_src emacs-lisp
  (key-chord-define-global "oe" 'global-fingers-mode)
#+end_src

Continue reading for more information on the available bindings.

** Recent change

  • [2015-08-09 Sun]: Added =fingers-replace-with-yank= bound to =V= by default.

  • [2015-03-21 Sat]: Added + and - bindings to increment and decrement number under point.

  • [2015-03-21 Sat]: Switched bindings for region specifiers "line" and "till line end"

  • [2015-03-07 Sat]: Search prefixes and bindings for repition changed: =uu= / =pp= to start searching, =U= / =P= to repeat search.

  • [2015-03-07 Sat]: Added bindings for =universal-argument=, and commands to pop local and global marks.

  • [2015-03-07 Sat]: Added hook for customizing key-bindings. Simplifies =eval-buffer= to test new bindings while maintaining custom bindings.

** Details

=fingers-mode= is a global minor mode that introduces key bindings for navigation and text manipulation. It relies on modal editing to reduce the usage of modifiers like Control and Meta. It introduces a new keymap to trigger commands without the need for modifiers and can easily be toggled to fall back to inserting text as usually in Emacs.

=fingers-mode= is activated for every buffer except the minibuffer. While this works best for me, you might want to exclude additional major modes like =magit=:

#+begin_src emacs-lisp (add-to-list 'fingers-mode-excluded-major-modes 'magit-status-mode) #+end_src

The following tables describe the bindings when =fingers-mode= is active. The commands are sometimes abbreviated for formatting, you can always use =C-h k= to get the actual binding. The tables are based on the [[http://www.workmanlayout.com/blog/][Workman]] keyboard layout, but there are mappings available for [[https://en.wikipedia.org/wiki/QWERTY][Qwerty]] and [[http://www.neo-layout.org/][Neo]]. The mappings modify the main =fingers-mode-map= where the layout is based on ease of key presses rather than mnemonics. The bindings available in the =x-map= and =c-map= are usually mnemonics and based on the standard Emacs bindings, so they aren't modified by the mappings by default.

While =fingers-mode= aims to offer convenient bindings for most cases, it does not modify the underlying bindings. You can always fall back to regular bindings like =C-n= for moving to the next line.

*** Left Hand: Text manipulation

Available bindings on the left hand (Workman layout):

[[https://raw.githubusercontent.com/fgeller/fingers.el/master/images/text_manipulation.png][file:https://raw.githubusercontent.com/fgeller/fingers.el/master/images/text_manipulation.png]]

The home row binds common killing =t=, copying =T= and there are also
functions to add a pair of surrounding strings =a= or remove them =s=, similar to
paredit's splice command. These commands expect a region as an argument that
can either be active prior to triggering the command or be selected
afterwards. Regular navigation commands on the right hand can be used to
make a selection but also a set of bindings on the left hand allow to
quickly specify a region. There are bindings available for selecting the
char =v=, word =h=, symbol =t= or line =g= under point as well as the region
between =s= or with an enclosing pair =a=. Upper case versions trigger
selections with surrounding whitespace or from point till line end for =G=.

The following table lists the region specifiers on the left hand (Workman layout):

[[https://raw.githubusercontent.com/fgeller/fingers.el/master/images/region_specifiers.png][file:https://raw.githubusercontent.com/fgeller/fingers.el/master/images/region_specifiers.png]]

The available pairs are currently =()=, ={}=, =[]=, =<>=, "", `` and
''. Additionally, for pair selection you can double press the key for a
command and =fingers-mode= will identify the next pair that starts left of
point. For example, if =|= represents point and =[]= the active region:

#+begin_src text
  def greet(): Unit = {
    println("Hello, |world")
  }
#+end_src

To remove the surrounding double quotes, you can use =s=. It will prompt for
a starting character of the pair you are trying to remove. You can use =s"=
to effectively remove the string delimiters (splice):

#+begin_src text
  def greet(): Unit = {
    println(|Hello, world)
  }
#+end_src

Pressing =ss= will yield the same result, as the double quote to the left
of point is the first character that is identified as the start of a pair:

#+begin_src text
  def greet(): Unit = {
    println(|Hello, world)
  }
#+end_src

You can use =t= to kill a region. The command expects either a region
specifier or a navigation command (for example, next line). In the above
snippet pressing =tss= will yield:

#+begin_src text
  def greet(): Unit = {
    println(|)
  }
#+end_src

The first =s= is a region selector (between pair) and the second =s= causes
=fingers-mode= to look to the left for the first starting character of a
supported pair. In this case, the =(= is interpreted as the start of a pair
and everything until the matching parenthesis is killed. Now, you can select the
function body explicitly via =ta{=:

#+begin_src text
  def greet(): Unit = |
#+end_src

The double key press is simply looking to the left of point for the next
character that is the start of a known pair, it does not look whether the
character has a well balanced matching end character. Selecting a region
based on the pairs =()=, ={}=, =[]= and =<>= will attempt to find the
matching end character. For example:

#+begin_src text
  (defun hello-there ()
    (interactive)
    (message "1 + |1 + 2 + 3 = %s" (+ 1 1 2 3)))
#+end_src

Pressing =ts(= will yield:

#+begin_src text
  (defun hello-there ()
    (interactive)
    (|))
#+end_src

Or for:

#+begin_src text
  (defun hello-there| ()
    (interactive)
    (message "1 + 1 + 2 + 3 = %s" (+ 1 1 2 3)))
#+end_src

Pressing =ta(= will kill the entire function definition and yield:

#+begin_src text
  |
#+end_src

Notice that the =a= is a region specifier similar to =s=, but that includes
the surrounding pair. Many of the region specifiers have an upper case
analog that includes the surrounding whitespace. For example, pressing =taa=
for the following snippet:

#+begin_src text
  (defun hello-there ()
    (interactive)
    (mess|age "1 + 1 + 2 + 3 = %s" (+ 1 1 2 3)))
#+end_src

Removes the contents and the surrounding =()= pair:

#+begin_src text
  (defun hello-there ()
    (interactive)
    |)
#+end_src

Pressing =tAA= would clean up the whitespace and yield:

#+begin_src text
  (defun hello-there ()
    (interactive)|)
#+end_src

Notice that the same region specifiers work for marking as well, bound by
default to =SPC=. Pressing =SPCaa= for the above snippet yields the
following active region:

#+begin_src text
  [(defun hello-there ()
    (interactive))]
#+end_src

Where =]= also denotes point. Alternatively, pressing =SPCh= for the
following snippet:

#+begin_src text
  (defun he|llo-there ()
    (interactive))
#+end_src

Yields the active region:

#+begin_src text
  (defun [hello]-there ()
    (interactive))
#+end_src

Where pressing =SPCT= (that's =SPC= followed by =T=) would yield:

#+begin_src text
  (defun[ hello-there ]()
    (interactive))
#+end_src

=T= causes the selection of the symbol =hello-there= plus surrounding
whitespace.

Any navigation command can be used to manually define the active
region. For example, pressing =SPCG= for the following snippet:

#+begin_src text
  (defun |hello-there ()
    (interactive))
#+end_src

Activates a region from point till end of line:

#+begin_src text
  (defun [hello-there ()]
    (interactive))
#+end_src

Pressing =SPC'= has the same effect, where ='= is the navigation command to
move point to then end of the line.

Active regions can be used as input to the commands to kill a region or
enclose it with a pair. For example, pressing =t= with the acitve region in
the above snippet yields:

#+begin_src text
  (defun |
    (interactive))
#+end_src

So pressing any of =SPC't=, =SPCGt=, =t'=, =tG= has the same effect.

Here's a demo for some of the examples above:

[[https://raw.githubusercontent.com/fgeller/fingers.el/master/images/fingers-mode.gif][file:https://raw.githubusercontent.com/fgeller/fingers.el/master/images/fingers-mode.gif]]

All of these manipulation commands are text based rather than identifying
syntactic components in the buffer. The goal are generally applicable
commands for text manipulation, rather than major-mode specific ones.

While many of these bindings are specific to =fingers-mode=, many common
bindings are easily available as well. Bindings that are prefixed by =C-x=
or =C-c= are available by pressing =x= or =c= respectively. For example, to
save the current buffer, you can press =xs= rather than =C-x C-s=.  Modify
=fingers-x-bindings= and =fingers-c-bindings= if a common binding for either
is missing. In addition, similar to god-mode, =g= and =G= bind meta prefixes
=M-= and =C-M-= respectively. So pressing =g;= is like pressing =M-;= and
commonly triggers =comment-dwim=.

The =universal-argument= is bound to =b= by default to easily toggle between
different modes for commands. For example, pressing =w= will join the current
line to the previous one, pressing =bw= will join the next line to the current
one.

*** Right Hand: Navigation

Available bindings on the right hand (Workman layout), prefixs are color coded::

[[https://raw.githubusercontent.com/fgeller/fingers.el/master/images/navigation.png][file:https://raw.githubusercontent.com/fgeller/fingers.el/master/images/navigation.png]]

Regular cursor motion is available on the home row via bindings that mirror
Vim's =hjkl= for left, down, up and right plus additional bindings for
jumping to the beginning and end of the current line respectively. Upper
case variants increase the jump range. For example: =n= triggers =left-char=
and =N= triggers =backward-word=, or =y= to jump to the beginning of the
line, =Y= to jump to the beginning of the buffer.

The top row introduces several prefixes to make use of registers and
isearch. For registers, you can store a point in register =a= by pressing
=fna= and return to it by pressing =ffa=. Supplying a prefix works as
regularly. To store the current window configuration in =b= you can use =C-u
ffb= and to restore it =ffb=.

Middle and ring finger start prefixes for searching down =u= and up =p=. To
start a search from point forward, press =uu= and enter the search string
(=pp= for backwards search). For example, pressing =uuwhite= for the
following snippet:

#+begin_src emacs-lisp
  (defvar fing|ers-region-specifiers
    '((char . ?v)
      (char-and-whitespace . ?V)
      (line . ?G)
      (line-rest . ?g)
      (word . ?h)
      (word-and-whitespace . ?H)
      (symbol . ?t)
      (symbol-and-whitespace . ?T)
      (between-whitespace . ?c)
      (with-surrounding-whitespace . ?C)
      (inside-pair . ?s)
      (with-pair . ?a)
      (with-pair-and-whitespace . ?A))
    "Mapping from region type to identifier key")

  (defun fingers-region-specifier (type)
    (cdr (assoc type fingers-region-specifiers)))
#+end_src

Will move point and highlight the occurrences of =white= (denoted by =[]=
where the first =]= is also point):

#+begin_src emacs-lisp
  (defvar fingers-region-specifiers
    '((char . ?v)
      (char-and-[white]space . ?V)
      (line . ?G)
      (line-rest . ?g)
      (word . ?h)
      (word-and-[white]space . ?H)
      (symbol . ?t)
      (symbol-and-[white]space . ?T)
      (between-[white]space . ?c)
      (with-surrounding-[white]space . ?C)
      (inside-pair . ?s)
      (with-pair . ?a)
      (with-pair-and-[white]space . ?A))
    "Mapping from region type to identifier key")

  (defun fingers-region-specifier (type)
    (cdr (assoc type fingers-region-specifiers)))
#+end_src

Exit isearch via =RET= and continue searching downward via =U= or upward
via =P=. Alternatively you can press =uo= to trigger =occur= for the
current search string =white=.

Additionally you can use =ut= and =pt= to jump to the next or previous
occurrence of the symbol under point. For jumping to occurrences of the word
under point you can use =uh= and =ph= respectively. Pressing =ut= in the
original snippet:

#+begin_src emacs-lisp
  (defvar finge|rs-region-specifiers
    '((char . ?v)
      (char-and-whitespace . ?V)
      (line . ?G)
      (line-rest . ?g)
      (word . ?h)
      (word-and-whitespace . ?H)
      (symbol . ?t)
      (symbol-and-whitespace . ?T)
      (between-whitespace . ?c)
      (with-surrounding-whitespace . ?C)
      (inside-pair . ?s)
      (with-pair . ?a)
      (with-pair-and-whitespace . ?A))
    "Mapping from region type to identifier key")

  (defun fingers-region-specifier (type)
    (cdr (assoc type fingers-region-specifiers)))
#+end_src

Will move point to the next occurrence of the symbol
=fingers-region-specifiers=:

#+begin_src emacs-lisp
  (defvar fingers-region-specifiers
    '((char . ?v)
      (char-and-whitespace . ?V)
      (line . ?G)
      (line-rest . ?g)
      (word . ?h)
      (word-and-whitespace . ?H)
      (symbol . ?t)
      (symbol-and-whitespace . ?T)
      (between-whitespace . ?c)
      (with-surrounding-whitespace . ?C)
      (inside-pair . ?s)
      (with-pair . ?a)
      (with-pair-and-whitespace . ?A))
    "Mapping from region type to identifier key")

  (defun fingers-region-specifier (type)
    (cdr (assoc type |fingers-region-specifiers)))
#+end_src

Pressing =uo= would trigger =occur= and show you all of the occurrences of
the last symbol or word you jumped to via =ut=/=pt= or =uh=/=ph=.

*** Mappings

=fingers-mode= has defaults that I tuned for the Workman layout, but
currently there are mappings available for the Qwerty and the Neo
layout. You can use =fingers-qwerty.el= and =fingers-neo.el= as templates to
add mappings for a different layout.

The Qwerty mappings have one difference to the Workman bindings: The
bindings for =m= and =c= on the Workman layout are switched so that the
common prefix =C-c= is in the usual place. More specifically, pressing =c=
for the Qwerty layout will trigger the bindings in =fingers-mode-c-map= and
pressing =v= will trigger macro related commands that are bound to =m= on
the Workman layout.

** Extensions

*** Third party libraries

=fingers-mode= has no external requirements, it only loads =thingatpt= which
is bundled with GNU Emacs. But I personally use several extensions for which
I either use unbound keys or replace existing bindings. For example, I
replace the built-in functionality for =query-replace= with
[[https://github.com/syohex/emacs-anzu][anzu]]'s version that offers
immediate visual feedback:

#+begin_src emacs-lisp
  (define-key fingers-mode-map (kbd "r") 'anzu-query-replace)
  (define-key fingers-mode-map (kbd "R") 'anzu-query-replace-regexp)
#+end_src

Or I use [[https://github.com/emacs-helm/helm][helm]] to replace =find-file=
or =execute-extended-command= via:

#+begin_src emacs-lisp
  (define-key fingers-mode-x-map (kbd "f") 'helm-find-files)
  (define-key fingers-mode-x-map (kbd "x") 'helm-M-x)
#+end_src

You can find more of my personal customizations
[[https://github.com/fgeller/emacs.d/blob/master/fingers.org][here]].

*** Visual feedback

 You can use the following snippet to color the mode-line to indicate
 whether =fingers-mode= is active:

 #+begin_src emacs-lisp
   (defun fingers-mode-visual-toggle ()
     (let ((faces-to-toggle '(mode-line mode-line-inactive))
           (enabled-color (if terminal-p "gray" "#e8e8e8"))
           (disabled-color (if terminal-p "green" "#a1b56c")))
       (cond (fingers-mode
              (mapcar (lambda (face) (set-face-background face enabled-color))
                      faces-to-toggle))
             (t
              (mapcar (lambda (face) (set-face-background face disabled-color))
                      faces-to-toggle)))))

   (add-hook 'fingers-mode-hook 'fingers-mode-visual-toggle)
 #+end_src

** References

=fingers-mode= is based on excellent ideas found in [[https://github.com/jyp/boon][boon]] and [[https://github.com/chrisdone/god-mode][god-mode]].

Compared to =god-mode=, =fingers-mode= is a bigger step away from the usual key bindings in Emacs. Both share the =M-= and =M-C-= prefix via =g= and =G=, and the common bindings for the =C-x= and =C-c= prefix are accessible via =x= and =c= respectively. =fingers-mode= also bundles several text manipulation commands and introduces new bindings for these and for navigation commands.

=fingers-mode= is very similarly to =boon= with a couple of details that are different (in no particular order):

- =fingers-mode= has no external dependencies. This means that the package
  is standalone, but also that some of the text manipulation commands might
  not accomodate specific cases. More specifically, =fingers-mode= does not
  rely on the excellent =expand-region=, which introduces selection helpers
  specific to major modes. Instead, the goal are simple and easily
  understandable defaults that are applicable to all text.

- Navigation commands are bound a little differently: Most navigation is on
  the home row for =fingers-mode=, rather than split acroos home and top
  row. =fingers-mode= also uses VIM-like keys (hjkl for left, down, up and
  right) on home row but in the default position, not shifted to the
  left. The search commands are available on the right side as well and
  there are some helpers to jump to the next or previous occurrence of a
  word or symbol.

- Several of the bindings for manipulation commands are different as well,
  but I imagine they are mostly specific to personal taste and usage
  frequency.

- No dependency on a specific keyboard layout. Some mappings are included,
  and adding one should be straight-forward. The mappings are currently only
  for the main bindings, not the bindings behind the =c= and =x= prefix
  which are following mnemonics as the original ones. For example, =xs=
  still triggers the save command or =xv= is still a prefix for version
  control related commands.

Compared to both, =fingers-mode= is by default active for every mode, except the minibuffer. I prefer this consistency, but you can customize this to exclude modes like =dired= and =magit=, similarly to =boon= and =god-mode=.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].