Skip to content

Commit

Permalink
Allow user to define custom states (meow-edit#158)
Browse files Browse the repository at this point in the history
* Allow user to define custom states (meow-edit#155)

* Generalized macro to produce define-key helpers

Defines a macro meow-generate-define-key which produces a helper function
meow-STATE-define-key. This macro is then applied to all keymaps.
This commit also introduces the variable meow-keymap-alist to keep track of
states to keymaps.

* Generalize state disabling

Ensure that when normal, insert, and beacon modes are entered, they disable all
modes that may conflict with them, including custom modes. We also add
meow-state-alist to keep track of all modes.

* Generalize meow--update-cursor

Use a list of conditions instead of a long cond statement.
This ensures you can easily add your own conditions and functions.

* Generalize meow--switch-state

Modify meow--switch-state such that if the state being switched to is not
caught by the case statement, it is looked up in an alist of custom modes.

* Generalize meow--current-state

This uses the previously defined variable meow-state-alist to generalize the
meow--current-state function.

* Add function to define custom state

Please see docstrings. Adds several macros to help the user easily define
custom states.

* Autoload meow-define-state to prevent errors on startup

* Fix entry function macro and add to meow-define-state

* Make keymap generator take customization options

Suppression option and sparsity options added

* Improve variable declarations

Fixed docstrings and made indentation look nicer. Also,
removes meow-custom-state-alist in favor of two alists that should always
mirror each other. Adds meow--register-state to update both lists at once.

* Change meow-escape-or-normal-modal to always work

Previously, it only switched you to normal if you were
either in insert mode or in fundamental mode.

* Indenting consistency

My indenter got mad

* Fix wrong variable usage

* Generalize meow--render-indicator

Custom states are now added to meow-replace-state-name-list.
meow--render-indicator is rewritten to be much shorter.

* Documentation

* Rewrite meow--switch-state and extract updating to init functions.

* Change macros to functions and remove evals

* Remove entry function

* Make meow-keymap-alist use symbol keys

* Rewrite cursor updating

Remove code blocks from meow-update-cursor-functions-alist and
make defuns for each one.

* Make meow--define-state-keymap accept an override keymap

* Documentation

* Disable normal mode before saving origin commands in motion

* Variable docstring tweak

Changes variable documentation for meow-keymap-alist and
meow-update-cursor-functions-alist.

* Make keypad mode remember custom states.

* Rewrite meow--motion-init to save origin command properly

* setting previous state before keypad is enabled

* remove meow-mode-state-alist

* improve getting current state & disabling current state

* create default helper functions explictly

* Update CUSTOMIZATIONS.org

* fix error when leaving beacon state

* fix init in fundamental-mode

* fixes

* change define-state syntax

* Factor out keymap generation

* Documentation for syntax changes

* Loop over motion state keymap directly and remove meow--motion-overwrite-keys

* Move meow-keymap-alist and make its values keymaps

* Add and use meow-define-keys to define keybindings

* update docs for meow-define-keys

* Inline mode init functions

* Docs

* Declare indenting on define-keys and define-state

* fix indent spec for meow-define-state, update docs

* fix unrenamed meow-intern-string

* define existing states with meow-define-state

* update changelog

Co-authored-by: tianshu <doglooksgood@gmail.com>
Co-authored-by: eshrh <esrh@gatech.edu>
Co-authored-by: Fredrik Bergroth <fbergroth@gmail.com>
  • Loading branch information
4 people authored Jan 8, 2022
1 parent b04e2ea commit 0d63977
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 231 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Master (Unreleased)

### Enhancements
* [#155](https://github.com/meow-edit/meow/pull/155) [#166](https://github.com/meow-edit/meow/pull/166) [#158](https://github.com/meow-edit/meow/pull/158) Add `meow-define-state` and `meow-register-state` to allow user define custom state.
* Remap `describe-key` to `meow-describe-key` which handles the dispatched keybinds.
* Allow leader in beacon state(still can not switch to keypad).
* [#164](https://github.com/meow-edit/meow/issues/164) Add fallback support for meta & control-meta prefix in keypad.
Expand Down
102 changes: 92 additions & 10 deletions CUSTOMIZATIONS.org
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,41 @@

* Helper Functions

** meow-normal-define-key
** meow-define-keys

Define key in NORMAL state.
Define key bindings in a state.

#+begin_src emacs-lisp
(meow-normal-define-key
;; bind command
(meow-define-keys
;; state
'normal

;; bind to a command
'("a" . meow-append)
;; bind keymap

;; bind to a keymap
(cons "h" help-map)
;; bind key translation
'("x" . "C-x C-x")
...)

;; bind to a keybinding
'("x" . "C-x C-x"))
#+end_src

** meow-normal-define-key

Similar to ~meow-define-keys~. Define key in NORMAL state.

** meow-leader-define-key

Like ~meow-normal-define-key~, but for leader keymap.
Similar to ~meow-define-keys~. Define key in leader keymap.

The default keybindings in leader keymap:
- ~x~, ~c~, ~h~, ~m~ and ~g~ is bound to ~meow-keypad-start~, which can start KEYPAD state.
- ~SPC~ will execute the original command which bound to ~SPC~.

** meow-motion-overwrite-define-key

Use like ~meow-motion-overwrite-define-key~.
Similar to ~meow-define-keys~. Define key in MOTION state.

Meow will remap overwrited command to a keybinding with *HYPER* modifier.

For example, if you define ~j~ as ~C-N~ , the original command on ~j~ will be bound to ~H-j~.
Expand Down Expand Up @@ -58,6 +67,44 @@ Register a thing which can be used for ~meow-beginning/end/inner/bounds-of-thing

Check function's documentation for usage examples.

** meow-define-state
Define a custom state.

Example usage:

#+begin_src emacs-lisp
(setq meow-paren-keymap (make-keymap))
(meow-define-state paren
"meow state for interacting with smartparens"
:lighter " [P]"
:keymap meow-paren-keymap)

;; meow-define-state creates the variable
(setq meow-cursor-type-paren 'hollow)

(meow-define-keys 'paren
'("<escape>" . meow-normal-mode)
'("l" . sp-forward-sexp)
'("h" . sp-backward-sexp)
'("j" . sp-down-sexp)
'("k" . sp-up-sexp)
'("n" . sp-forward-slurp-sexp)
'("b" . sp-forward-barf-sexp)
'("v" . sp-backward-barf-sexp)
'("c" . sp-backward-slurp-sexp)
'("u" . meow-undo))
#+end_src

This function generates several new objects named based on the NAME parameter passed
in. See the function’s docstring for a list of them.

Similarly to =define-minor-mode=, your last parameter to =meow-define-state= may be
a single lisp form that is run every time the internal minor mode is entered
and exited.

If you already have a minor mode that you just need to register with meow, then
see the documentation for the internal function =meow-register-state=.

* Variables

** meow-mode-state-list
Expand Down Expand Up @@ -238,3 +285,38 @@ For examples:
"C-x C-v" will remap the occupied j to C-x C-v j.
"C-M-" will remap the occupied j to C-M-j.
#+end_example

** meow-state-mode-alist
Association list of symbols of meow states to their corresponding mode functions.

** meow-update-cursor-functions-alist

Association list of predicates to functions.

This list is used to update the cursor type and face. The first value whose
predicate evaluates to true will have its corresponding key run. This key
should use meow--set-cursor-type and meow--set-cursor-color to update the cursor.

You may customize this list for more complex modifications to the cursor.
For instance, to change the face of the insert cursor to a hollow cursor only
in org-mode, use

#+BEGIN_SRC emacs-lisp
(defun meow--update-cursor-custom ()
(progn
(meow--set-cursor-type 'hollow)
(meow--set-cursor-color 'meow-insert-cursor)))
(add-to-list 'meow-update-cursor-functions-alist
'((lambda () (and (meow-insert-mode-p)
(eq major-mode 'org-mode)))
. meow--update-cursor-custom))
#+END_SRC
Note that the both the car and cdr must be functions.

However, for simple changes to the insert cursor it would be sufficient to
change the variable =meow-cursor-type-insert=.

** meow-keymap-alist

Association list of symbols to their corresponding keymaps. Used
to generate =meow-*-define-key= helpers.
2 changes: 1 addition & 1 deletion meow-beacon.el
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Non-nil BACKWARD means backward direction."
inside)
(meow--switch-state 'beacon)
(meow--beacon-update-overlays))
((and (meow-beacon-mode-p))
((meow-beacon-mode-p)
(if inside
(meow--beacon-update-overlays)
(meow--beacon-remove-overlays)
Expand Down
4 changes: 2 additions & 2 deletions meow-command.el
Original file line number Diff line number Diff line change
Expand Up @@ -1373,15 +1373,15 @@ Argument ARG if not nil, switching in a new window."
(when overwrite-mode
(overwrite-mode -1))
(meow--switch-state 'normal))
((eq major-mode 'fundamental-mode)
(t
(meow--switch-state 'normal))))

(defun meow-motion-origin-command ()
"Execute the original command bound in special mode."
(interactive)
(let ((key (meow--parse-input-event last-input-event)))
(when-let* ((rebind-key (meow--get-origin-command key)))
(meow--execute-kbd-macro rebind-key))))
(meow--execute-kbd-macro rebind-key))))

(defun meow-eval-last-exp ()
"Eval last sexp."
Expand Down
144 changes: 48 additions & 96 deletions meow-core.el
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
;;; Code:

(require 'cl-lib)
(require 'subr-x)

(require 'meow-util)
(require 'meow-command)
Expand All @@ -32,48 +33,60 @@
(require 'meow-esc)
(require 'meow-shims)
(require 'meow-beacon)
(require 'meow-helpers)

;;;###autoload
(define-minor-mode meow-insert-mode
"Meow Insert state."
:init-value nil
(meow-define-state insert
"Meow INSERT state minor mode."
:lighter " [I]"
:keymap meow-insert-state-keymap
(meow--insert-init))
:face meow-insert-indicator
(if meow-insert-mode
(run-hooks 'meow-insert-enter-hook)
(when (and meow--insert-pos
meow-select-on-change
(not (= (point) meow--insert-pos)))
(thread-first
(meow--make-selection '(select . transient) meow--insert-pos (point))
(meow--select)))
(run-hooks 'meow-insert-exit-hook)
(setq-local meow--insert-pos nil)))

;;;###autoload
(define-minor-mode meow-normal-mode
"Meow Normal state."
:init-value nil
(meow-define-state normal
"Meow NORMAL state minor mode."
:lighter " [N]"
:keymap meow-normal-state-keymap
(meow--normal-init))

;;;###autoload
(define-minor-mode meow-keypad-mode
"Meow keypad state."
:init-value nil
:lighter " [K]"
;; use overriding-local-map for highest keymap priority
;; so KEYPAD won't be affected by overlays' keymap
:keymap meow-keypad-state-keymap
(meow--keypad-init))
:face meow-normal-indicator)

;;;###autoload
(define-minor-mode meow-motion-mode
"Meow motion state."
:init-value nil
(meow-define-state motion
"Meow MOTION state minor mode."
:lighter " [M]"
:keymap meow-motion-state-keymap
(meow--motion-init))
:face meow-motion-indicator)

;;;###autoload
(define-minor-mode meow-beacon-mode
"Meow cursor state."
:init-value nil
:lighter " [C]"
(meow-define-state keypad
"Meow KEYPAD state minor mode."
:lighter " [K]"
:keymap meow-keypad-state-keymap
:face meow-keypad-indicator
(when meow-keypad-mode
(setq meow--prefix-arg current-prefix-arg
meow--keypad-keys nil
meow--use-literal nil
meow--use-meta nil
meow--use-both nil)))

(meow-define-state beacon
"Meow BEACON state minor mode."
:lighter " [B]"
:keymap meow-beacon-state-keymap
(meow--beacon-init))
:face meow-beacon-indicator
(if meow-beacon-mode
(progn
(setq meow--beacon-backup-hl-line (bound-and-true-p hl-line-mode))
(meow--cancel-selection)
(hl-line-mode -1))
(when meow--beacon-backup-hl-line
(hl-line-mode 1))))

;;;###autoload
(define-minor-mode meow-mode
Expand Down Expand Up @@ -103,66 +116,6 @@ This minor mode is used by meow-global-mode, should not be enabled directly."
(meow--global-enable)
(meow--global-disable)))

(defun meow--normal-init ()
"Init normal state."
(when meow-normal-mode
(when (bound-and-true-p meow-insert-mode) (meow-insert-mode -1))
(when (bound-and-true-p meow-motion-mode) (meow-motion-mode -1))
(when (bound-and-true-p meow-beacon-mode) (meow-beacon-mode -1))))

(defun meow--insert-init ()
"Init insert state."
(if meow-insert-mode
(progn
(meow-normal-mode -1)
(meow-motion-mode -1)
(run-hooks 'meow-insert-enter-hook))
(when (and meow--insert-pos
meow-select-on-change
(not (= (point) meow--insert-pos)))
(thread-first
(meow--make-selection '(select . transient) meow--insert-pos (point))
(meow--select)))
(run-hooks 'meow-insert-exit-hook)
(setq-local meow--insert-pos nil)))

(defun meow--motion-init ()
"Init motion state."
(when meow-motion-mode
(when (bound-and-true-p meow-insert-mode) (meow-insert-mode -1))))

(defun meow--keypad-init ()
"Init keypad state.
We have to remember previous state, so that we can restore it."
(when meow-keypad-mode
(cond
((meow-motion-mode-p)
(setq meow--keypad-previous-state 'motion)
(meow-motion-mode -1))
((meow-normal-mode-p)
(setq meow--keypad-previous-state 'normal)
(meow-normal-mode -1)))
(setq meow--prefix-arg current-prefix-arg
;; meow--keypad-this-command nil
meow--keypad-keys nil
meow--use-literal nil
meow--use-meta nil
meow--use-both nil)))

(defun meow--beacon-init ()
"Init cursor state."
(if meow-beacon-mode
(progn
(setq meow--beacon-backup-hl-line (bound-and-true-p hl-line-mode))
(meow--cancel-selection)
(meow-normal-mode -1)
(meow-insert-mode -1)
(hl-line-mode -1))
(meow-normal-mode 1)
(when meow--beacon-backup-hl-line
(hl-line-mode 1))))

(defun meow--enable ()
"Enable Meow.
Expand All @@ -186,6 +139,7 @@ an init function."
;; if MOTION is specified
((apply #'derived-mode-p (mapcar #'car (alist-get 'motion state-to-modes)))
(meow-normal-mode -1)
(setq meow--current-state nil)
(meow--save-origin-commands)
(meow-motion-mode 1))

Expand All @@ -197,8 +151,8 @@ an init function."
((progn
;; Disable meow-normal-mode, we need test the command name bound to a single letter key.
(meow-normal-mode -1)
(let ((meow-normal-mode nil)
(cmd (key-binding "a")))
(setq meow--current-state nil)
(let ((cmd (key-binding "a")))
(and
(commandp cmd)
(symbolp cmd)
Expand All @@ -212,9 +166,7 @@ an init function."

(defun meow--disable ()
"Disable Meow."
(meow-normal-mode -1)
(meow-insert-mode -1)
(meow-motion-mode -1))
(mapc (lambda (state-mode) (funcall (cdr state-mode) -1)) meow-state-mode-alist))

(defun meow--global-enable ()
"Enable meow globally."
Expand Down
Loading

0 comments on commit 0d63977

Please sign in to comment.