parent
0eb35a0b08
commit
a5dccf2fb5
|
@ -13,6 +13,7 @@
|
|||
centered-cursor
|
||||
dos
|
||||
emoji-cheat-sheet
|
||||
evil-escape
|
||||
evil-org-mode
|
||||
evil-plugins
|
||||
helm-rcirc
|
||||
|
@ -51,6 +52,13 @@
|
|||
(use-package emoji-cheat-sheet
|
||||
:commands emoji-cheat-sheet))
|
||||
|
||||
(defun spacemacs/init-evil-escape ()
|
||||
(use-package evil-escape
|
||||
:init
|
||||
(evil-escape-mode)
|
||||
:config
|
||||
(spacemacs//hide-lighter evil-escape-mode)))
|
||||
|
||||
(defun spacemacs/init-evil-org-mode ()
|
||||
(use-package evil-org
|
||||
:commands evil-org-mode
|
||||
|
|
48
spacemacs/extensions/evil-escape/README.md
Normal file
48
spacemacs/extensions/evil-escape/README.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
# evil-escape
|
||||
|
||||
Customizable key sequence to escape from insert state and everything else in
|
||||
Emacs.
|
||||
|
||||
Press `fd` quickly to:
|
||||
|
||||
- escape from all evil states to normal state
|
||||
- escape from evil-lisp-state to normal state
|
||||
- abort evil ex command
|
||||
- quit minibuffer
|
||||
- abort isearch
|
||||
- quit magit buffers
|
||||
- quit help buffers
|
||||
- hide neotree buffer
|
||||
|
||||
And more to come !
|
||||
|
||||
Contributions to support more buffers are _very welcome_:
|
||||
**Escape Everything !**
|
||||
|
||||
## Install
|
||||
|
||||
The package _will be available soon_ in [MELPA][].
|
||||
|
||||
If you have MELPA in `package-archives`, use
|
||||
|
||||
M-x package-install RET evil-escape RET
|
||||
|
||||
If you don't, open `evil-escape.el` in Emacs and call
|
||||
`package-install-from-buffer`.
|
||||
|
||||
## Usage
|
||||
|
||||
To toggle the `evil-escape` mode globally:
|
||||
|
||||
M-x evil-escape-mode
|
||||
|
||||
## Customization
|
||||
|
||||
Open the customization group buffer:
|
||||
|
||||
M-x customize-group RET evil-escape RET
|
||||
|
||||
There you can change the key sequence to your desire.
|
||||
The default value is `fd`.
|
||||
|
||||
[MELPA]: http://melpa.org/
|
217
spacemacs/extensions/evil-escape/evil-escape.el
Normal file
217
spacemacs/extensions/evil-escape/evil-escape.el
Normal file
|
@ -0,0 +1,217 @@
|
|||
;;; evil-escape.el --- Customizable key sequence to escape from insert state and everything else.
|
||||
|
||||
;; Copyright (C) 2014 syl20bnr
|
||||
;;
|
||||
;;;; Author: Sylvain Benner <sylvain.benner@gmail.com>
|
||||
;; Keywords: convenience editing evil
|
||||
;; Created: 22 Oct 2014
|
||||
;; Version: 1.0
|
||||
;; Package-Requires: ((emacs "24") (evil "1.0.9") (key-chord "0.6"))
|
||||
;; URL: https://github.com/syl20bnr/evil-escape
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Press `fd` quickly to:
|
||||
;; - escape from all evil states to normal state
|
||||
;; - escape from evil-lisp-state to normal state
|
||||
;; - abort evil ex command
|
||||
;; - quit minibuffer
|
||||
;; - abort isearch
|
||||
;; - quit magit buffers
|
||||
;; - quit help buffers
|
||||
;; - hide neotree buffer
|
||||
;; And more to come !
|
||||
|
||||
;; The key sequence can be customized with the variable
|
||||
;; `evil-escape-key-sequence`
|
||||
|
||||
;; More information in the readme of the repository:
|
||||
;; https://github.com/syl20bnr/evil-escape
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'evil)
|
||||
(require 'key-chord)
|
||||
|
||||
(defgroup evil-escape nil
|
||||
"Key sequence to escape insert state and everything else."
|
||||
:prefix "evil-escape-"
|
||||
:group 'evil)
|
||||
|
||||
(defvar evil-escape-key-sequence (kbd "fd")
|
||||
"Two keys sequence to return to normal state.")
|
||||
(defcustom evil-escape-key-sequence (kbd "fd")
|
||||
"Two keys sequence to escape from insert state."
|
||||
:type 'key-sequence
|
||||
:group 'evil-escape)
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode evil-escape-mode
|
||||
"Buffer-local minor mode to escape insert state and everythin else
|
||||
with a key sequence."
|
||||
:lighter (:eval (concat " " evil-escape-key-sequence))
|
||||
:group 'evil
|
||||
:global t
|
||||
(if evil-escape-mode
|
||||
(progn
|
||||
(key-chord-mode 1)
|
||||
(evil-escape--define-keys)
|
||||
(message "evil-escape enabled, press \"%s\" to escape from anything."
|
||||
evil-escape-key-sequence))
|
||||
(evil-escape--undefine-keys)))
|
||||
|
||||
(defun evil-escape--define-keys ()
|
||||
"Set the key bindings to escape _everything!_"
|
||||
;; use key-chord whenever it is possible
|
||||
;; evil states
|
||||
(key-chord-define evil-insert-state-map evil-escape-key-sequence 'evil-normal-state)
|
||||
(key-chord-define evil-emacs-state-map evil-escape-key-sequence
|
||||
'(lambda () (interactive)
|
||||
(cond ((string-match "magit" (symbol-name major-mode))
|
||||
(setq unread-command-events (listify-key-sequence "q")))
|
||||
(t evil-normal-state))))
|
||||
(key-chord-define evil-visual-state-map evil-escape-key-sequence 'evil-exit-visual-state)
|
||||
(key-chord-define evil-motion-state-map evil-escape-key-sequence
|
||||
'(lambda () (interactive)
|
||||
(cond ((eq 'help-mode major-mode) (quit-window))
|
||||
((eq 'neotree-mode major-mode) (neotree-hide))
|
||||
(t (evil-normal-state)))))
|
||||
;; lisp state if installed
|
||||
(eval-after-load 'evil-lisp-state
|
||||
'(key-chord-define evil-lisp-state-map evil-escape-key-sequence 'evil-normal-state))
|
||||
;; mini-buffer
|
||||
(key-chord-define minibuffer-local-map evil-escape-key-sequence 'abort-recursive-edit)
|
||||
;; evil ex command
|
||||
(key-chord-define evil-ex-completion-map evil-escape-key-sequence 'abort-recursive-edit)
|
||||
;; key-chord does not work with isearch, use evil-escape implementation
|
||||
(evil-escape-define-escape isearch-mode-map isearch-abort
|
||||
:insert t
|
||||
:delete t
|
||||
:insert-func evil-escape--isearch-insert-func
|
||||
:delete-func isearch-delete-char))
|
||||
|
||||
(defun evil-escape--undefine-keys ()
|
||||
"Unset the key bindings defined in `evil-escape--define-keys'."
|
||||
;; bulk undefine
|
||||
(dolist (map '(evil-insert-state-map
|
||||
evil-emacs-state-map
|
||||
evil-visual-state-map
|
||||
evil-motion-state-map
|
||||
minibuffer-local-map
|
||||
evil-ex-completion-map))
|
||||
(key-chord-define (eval map) evil-escape-key-sequence nil))
|
||||
;; lisp state if installed
|
||||
(eval-after-load 'evil-lisp-state
|
||||
'(key-chord-define evil-lisp-state-map evil-escape-key-sequence nil))
|
||||
;; isearch
|
||||
(define-key isearch-mode-map (kbd "f") 'isearch-printing-char))
|
||||
|
||||
(defmacro evil-escape-define-escape (map command &rest properties)
|
||||
"Define an escape in MAP keymap by executing COMMAND.
|
||||
|
||||
`:insert BOOL'
|
||||
If BOOL is not nil the first character of the escape sequence is inserted
|
||||
in the buffer using `:insert-func' if the buffer is not read-only.
|
||||
|
||||
`:delete BOOL'
|
||||
If BOOL is not nil the first character is deleted using `:delete-func' if
|
||||
the escape sequence succeeded.
|
||||
|
||||
`:shadowed BOOL'
|
||||
BOOL not nil indicates that the first key of the sequence shadows a
|
||||
function. This function is looked-up from `evil-motion-state-map'.
|
||||
Whenever the escape sequence does not succeed and BOOL is not nil
|
||||
the shadowed function is called.
|
||||
|
||||
`:insert-func FUNCTION'
|
||||
Specify the insert function to call when inserting the first key.
|
||||
|
||||
`:delete-func FUNCTION'
|
||||
Specify the delete function to call when deleting the first key."
|
||||
(let* ((first-key (elt evil-escape-key-sequence 0))
|
||||
(fkeystr (char-to-string first-key))
|
||||
(insertp (plist-get properties :insert))
|
||||
(deletep (plist-get properties :delete))
|
||||
(insert-func (plist-get properties :insert-func))
|
||||
(delete-func (plist-get properties :delete-func)))
|
||||
`(progn
|
||||
(define-key ,map ,fkeystr
|
||||
(lambda () (interactive)
|
||||
(evil-escape--escape
|
||||
,evil-escape-key-sequence
|
||||
',(if (plist-get properties :shadowed) (lookup-key evil-motion-state-map fkeystr))
|
||||
,insertp
|
||||
,deletep
|
||||
',command
|
||||
',insert-func
|
||||
',delete-func))))))
|
||||
|
||||
(defun evil-escape--default-insert-func (key)
|
||||
"Insert KEY in current buffer if not read only."
|
||||
(let* ((insertp (not buffer-read-only)))
|
||||
(insert key)))
|
||||
|
||||
(defun evil-escape--isearch-insert-func (key)
|
||||
"Insert KEY in current buffer if not read only."
|
||||
(isearch-printing-char))
|
||||
|
||||
(defun evil-escape--default-delete-func ()
|
||||
"Delete char in current buffer if not read only."
|
||||
(let* ((insertp (not buffer-read-only)))
|
||||
(delete-char -1)))
|
||||
|
||||
(evil-define-command evil-escape--escape
|
||||
(keys shadowed-func insert? delete? callback &optional insert-func delete-func)
|
||||
"Execute the passed CALLBACK using KEYS. KEYS is a cons cell of 2 characters.
|
||||
|
||||
If the first key insertion shadowed a function then pass the shadowed function
|
||||
in SHADOWED-FUNC and it will be executed if the key sequence was not
|
||||
successfull.
|
||||
|
||||
If INSERT? is not nil then the first key pressed is inserted using the function
|
||||
INSERT-FUNC.
|
||||
|
||||
If DELETE? is not nil then the first key is deleted using the function
|
||||
DELETE-FUNC when calling CALLBACK. "
|
||||
:repeat nil
|
||||
(let* ((modified (buffer-modified-p))
|
||||
(insertf (if insert-func
|
||||
insert-func 'evil-escape--default-insert-func))
|
||||
(deletef (if delete-func
|
||||
delete-func 'evil-escape--default-delete-func))
|
||||
(fkey (elt keys 0))
|
||||
(fkeystr (char-to-string fkey))
|
||||
(skey (elt keys 1)))
|
||||
(if insert? (funcall insertf fkey))
|
||||
(let* ((evt (read-event nil nil key-chord-two-keys-delay)))
|
||||
(cond
|
||||
((null evt)
|
||||
(unless (eq 'insert evil-state)
|
||||
(if shadowed-func (call-interactively shadowed-func))))
|
||||
((and (integerp evt)
|
||||
(char-equal evt skey))
|
||||
;; remove the f character
|
||||
(if delete? (funcall deletef))
|
||||
(set-buffer-modified-p modified)
|
||||
(funcall callback))
|
||||
(t ; otherwise
|
||||
(setq unread-command-events
|
||||
(append unread-command-events (list evt)))
|
||||
(if shadowed-func (call-interactively shadowed-func)))))))
|
||||
|
||||
(provide 'evil-escape)
|
|
@ -77,6 +77,7 @@
|
|||
json-mode
|
||||
ledger-mode
|
||||
less-css-mode
|
||||
key-chord
|
||||
magit
|
||||
magit-gitflow
|
||||
markdown-mode
|
||||
|
@ -219,118 +220,6 @@ determine the state to enable when escaping from the insert state.")
|
|||
(defadvice evil-lisp-state (before spacemacs/evil-lisp-state activate)
|
||||
"Advice to keep track of the last base state."
|
||||
(setq spacemacs-last-base-state 'lisp))
|
||||
|
||||
(defun spacemacs/escape-state-default-insert-func (key)
|
||||
"Insert KEY in current isearch minibuffer."
|
||||
(let* ((insertp (not buffer-read-only)))
|
||||
(insert key)))
|
||||
|
||||
(defun spacemacs/escape-state-isearch-insert-func (key)
|
||||
"Insert KEY in current buffer if not read only."
|
||||
(isearch-printing-char))
|
||||
|
||||
(defun spacemacs/escape-state-term-insert-func (key)
|
||||
"Insert KEY in current term buffer."
|
||||
(term-send-raw))
|
||||
|
||||
(defun spacemacs/escape-state-default-delete-func ()
|
||||
"Delete char in current buffer if not read only."
|
||||
(let* ((insertp (not buffer-read-only)))
|
||||
(delete-char -1)))
|
||||
|
||||
(evil-define-command spacemacs/escape-state
|
||||
(keys shadowed insert? delete? callback &optional insert-func delete-func)
|
||||
"Allows to execute the passed CALLBACK using KEYS. KEYS is a cons cell
|
||||
of 2 characters.
|
||||
|
||||
If INSERT? is not nil then the first key pressed is inserted using the function
|
||||
INSERT-FUNC.
|
||||
|
||||
If DELETE? is not nil then the first key is deleted using the function
|
||||
DELETE-FUNC when calling CALLBACK.
|
||||
"
|
||||
:repeat change
|
||||
(let* ((modified (buffer-modified-p))
|
||||
(insertf
|
||||
(if insert-func
|
||||
insert-func 'spacemacs/escape-state-default-insert-func))
|
||||
(deletef
|
||||
(if delete-func
|
||||
delete-func 'spacemacs/escape-state-default-delete-func))
|
||||
(fkey (car keys))
|
||||
(fkeystr (char-to-string fkey))
|
||||
(skey (cdr keys)))
|
||||
(if insert? (funcall insertf fkey))
|
||||
(let* ((evt (read-event nil nil spacemacs-normal-state-sequence-delay)))
|
||||
(cond
|
||||
((null evt)
|
||||
(unless (eq 'insert evil-state)
|
||||
(if shadowed (call-interactively shadowed))))
|
||||
((and (integerp evt)
|
||||
(char-equal evt skey))
|
||||
;; remove the f character
|
||||
(if delete? (funcall deletef))
|
||||
(set-buffer-modified-p modified)
|
||||
(funcall callback))
|
||||
(t ; otherwise
|
||||
(setq unread-command-events
|
||||
(append unread-command-events (list evt)))
|
||||
(if shadowed (call-interactively shadowed)))))))
|
||||
;; easier toggle for emacs-state
|
||||
(evil-set-toggle-key "s-`")
|
||||
;; escape state with a better key sequence than ESC
|
||||
(let* ((seq spacemacs-normal-state-sequence)
|
||||
(key (char-to-string (car spacemacs-normal-state-sequence)))
|
||||
(shadowed (lookup-key evil-motion-state-map key)))
|
||||
;; 'fd' triggers to escape from a state to the base state
|
||||
(global-set-key key `(lambda () (interactive)
|
||||
(spacemacs/escape-state ',seq nil nil nil 'keyboard-quit)))
|
||||
(mapc (lambda (map)
|
||||
(define-key (eval map) key
|
||||
`(lambda () (interactive)
|
||||
(spacemacs/escape-state ',seq nil t nil 'abort-recursive-edit))))
|
||||
'(minibuffer-local-map
|
||||
minibuffer-local-ns-map
|
||||
minibuffer-local-completion-map
|
||||
minibuffer-local-must-match-map
|
||||
minibuffer-local-isearch-map))
|
||||
(define-key isearch-mode-map key
|
||||
`(lambda () (interactive)
|
||||
(spacemacs/escape-state
|
||||
',seq nil t t 'isearch-abort 'spacemacs/escape-state-isearch-insert-func
|
||||
'isearch-delete-char)))
|
||||
(define-key evil-ex-completion-map key
|
||||
`(lambda () (interactive)
|
||||
(spacemacs/escape-state
|
||||
',seq nil t nil 'abort-recursive-edit nil 'evil-ex-delete-backward-char)))
|
||||
;; Note: we keep emacs state untouched in order to always have a
|
||||
;; fallback for modes that uses the `f' key (ie. magit-gitflow)
|
||||
(define-key evil-insert-state-map key
|
||||
`(lambda () (interactive)
|
||||
(let ((insertf (if (eq 'term-mode major-mode)
|
||||
'spacemacs/escape-state-term-insert-func)))
|
||||
(spacemacs/escape-state
|
||||
',seq nil t t (intern (format "evil-%s-state" spacemacs-last-base-state)) insertf))))
|
||||
(define-key evil-visual-state-map key
|
||||
`(lambda () (interactive)
|
||||
(spacemacs/escape-state ',seq ',shadowed nil nil 'evil-exit-visual-state)))
|
||||
(define-key evil-motion-state-map key
|
||||
`(lambda () (interactive)
|
||||
(let ((exit-func (cond ((eq 'help-mode major-mode)
|
||||
'quit-window)
|
||||
((eq 'neotree-mode major-mode)
|
||||
'neotree-hide)
|
||||
(t 'evil-normal-state))))
|
||||
(spacemacs/escape-state ',seq ',shadowed nil nil exit-func))))
|
||||
(eval-after-load 'evil-lisp-state
|
||||
`(define-key evil-lisp-state-map ,key
|
||||
(lambda () (interactive)
|
||||
(spacemacs/escape-state ',seq ',shadowed nil nil 'evil-normal-state))))
|
||||
(eval-after-load "helm-mode"
|
||||
`(define-key helm-map ,key
|
||||
(lambda () (interactive)
|
||||
(spacemacs/escape-state ',seq nil t nil 'helm-keyboard-quit)))))
|
||||
|
||||
;; manage the base state target when leaving the insert state
|
||||
(define-key evil-insert-state-map [escape]
|
||||
(lambda () (interactive)
|
||||
|
|
Loading…
Reference in a new issue