From add010fd445417e559719ada5e22e732d67f3ae7 Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Thu, 15 Jun 2023 15:44:57 -0700 Subject: [PATCH] Label the `g` commands for which-key Cleaned up the commands that follow on the g key with labels, and I got rid of commands that I don't think is useful. This required changes through the repo, but things are looking good. I also changed the behavior of `e` and `E`, and `w` and `W` based on how I think that should be. --- ha-config.org | 242 +++++++++++++++++++++++++++------------ ha-org.org | 15 +-- ha-programming-elisp.org | 13 +-- ha-programming.org | 3 +- 4 files changed, 182 insertions(+), 91 deletions(-) diff --git a/ha-config.org b/ha-config.org index 1065db2..c196e1d 100644 --- a/ha-config.org +++ b/ha-config.org @@ -278,8 +278,8 @@ Why use [[https://gitlab.com/ideasman42/emacs-undo-fu][undo-fu]] instead of the #+end_src ** Evil-Specific Keybindings Can we change Evil at this point? Some tips: - - [[https://github.com/noctuid/evil-guide]] - - [[https://nathantypanski.com/blog/2014-08-03-a-vim-like-emacs-config.html]] + - [[https://github.com/noctuid/evil-guide][Evil Guide]] + - [[https://nathantypanski.com/blog/2014-08-03-a-vim-like-emacs-config.html][A Vim-like Emacs Configuration from Nathan Typanski]] - [[https://stackoverflow.com/questions/25542097/emacs-evil-mode-how-to-change-insert-state-to-emacs-state-automatically][Evil insert state is really Emacs?]] Real answer to that is to set [[help:evil-disable-insert-state-bindings][evil-disable-insert-state-bindings]] #+begin_src emacs-lisp @@ -295,29 +295,21 @@ Can we change Evil at this point? Some tips: evil-want-C-u-scroll nil evil-want-C-i-jump nil evil-escape-key-sequence "jk" - evil-escape-unordered-key-sequence t) + evil-escape-unordered-key-sequence t)) +#+end_src +The Escape key act like ~C-g~ and always go back to normal mode? +#+begin_src emacs-lisp + (use-package evil :config - ;; The Escape key act like C-g and always go back to normal mode? (global-set-key (kbd "") 'keyboard-escape-quit) + (evil-mode)) +#+end_src - ;; Underscores are part of a symbolic word in programming languages: - ;; Sure, I could use capital `W' and `B', but I often forget that. - (modify-syntax-entry ?_ "w") - - ;; Now that `evil-disable-insert-state-bindings' works to use Emacs - ;; keybindings in Evil's insert mode, we no longer need this code: - ;; (setq evil-insert-state-map (make-sparse-keymap)) - ;; (define-key evil-insert-state-map (kbd "") 'evil-normal-state) - - ;; Not a long-term VI user, so let's Emacsify some other keybindings: - (define-key evil-normal-state-map (kbd "C-b") 'scroll-up-command) - (define-key evil-normal-state-map (kbd "C-f") 'scroll-down-command) - (define-key evil-normal-state-map (kbd "C-p") 'previous-line) - (define-key evil-normal-state-map (kbd "C-n") 'next-line) - (define-key evil-normal-state-map (kbd "C-w") 'sp-kill-region) ; I have better window control - - ;; Even with the `evil-collections' (see below), some modes should be Emacs: +Even with the [[Evil Collection]], some modes should be Emacs: +#+begin_src emacs-lisp + (use-package evil + :config (dolist (mode '(custom-mode eshell-mode git-rebase-mode @@ -326,14 +318,47 @@ Can we change Evil at this point? Some tips: circe-chat-mode circe-query-mode vterm-mode)) - (add-to-list 'evil-emacs-state-modes mode)) - - (evil-mode)) + (add-to-list 'evil-emacs-state-modes mode))) #+end_src +I’m not a long term VI user, and I generally like /easy keys/, e.g. ~w~, have larger jumps, and /harder keys/, e.g. ~W~ (shifted), have smaller, fine-grained jumps. So I am switching these around: +#+begin_src emacs-lisp + (use-package evil + :config + (require 'evil-commands) + (evil-define-key '(normal visual motion operator) 'global + "w" 'evil-forward-WORD-begin + "W" 'evil-forward-word-begin + "e" 'evil-forward-WORD-end + "E" 'evil-forward-word-end) + + ;; The `b' key seems to need its own configuration setting: + (evil-define-key '(normal visual motion operator) 'global + "b" 'evil-backward-WORD-begin) + (evil-define-key '(normal visual motion operator) 'global + "B" 'evil-backward-word-begin) + ;; Note that evil-backward-word-end is on the `g e': + + ;; Not a long-term VI user, so let's Emacsify some other keybindings: + (evil-define-key '(normal visual motion operator) 'global + (kbd "C-b") 'scroll-up-command + (kbd "C-f") 'scroll-down-command + (kbd "C-p") 'previous-line + (kbd "C-n") 'next-line + ;; I have better window control: + (kbd "C-w") 'sp-kill-region)) +#+end_src +Testing: + - =word-subword-subword= + - =word_subword_subword= + This clever hack from [[https://manueluberti.eu//emacs/2022/10/16/back-last-edit/][Manuel Uberti]] got me finding these useful bindings: -- ~g ;~ :: [[help:goto-last-change][goto-last-change]] -- ~g ,~ :: [[help:goto-last-change-reverse][goto-last-change-reverse]] + - ~g ;~ :: [[help:goto-last-change][goto-last-change]] + - ~g ,~ :: [[help:goto-last-change-reverse][goto-last-change-reverse]] + +Keybindings I would like to use more: + - ~*~ :: jumps to the next instance of the word under point + - ~#~ :: jumps to the previous instance of the word under point While I’m pretty good with the VIM keybindings, I would like to play around with the [[https://evil.readthedocs.io/en/latest/extension.html#text-objects][text objects]] and how it compares to others (including the surround), for instance: - ~diw~ :: deletes a word, but can be anywhere in it, while ~de~ deletes to the end of the word. @@ -501,45 +526,6 @@ Using the key-chord project allows me to make Escape be on two key combo presses (key-chord-define-global "jk" 'evil-normal-state) (key-chord-define-global "JK" 'evil-normal-state)) #+end_src -*** Evil Lion -The [[https://github.com/edkolev/evil-lion][evil-lion]] package is a wrapper around Emacs’ [[help:align][align]] function. Just a little easier to use. Primary sequence is ~g a i p =~ to align along all the equal characters in the paragraph (block), or ~g a i b RET~ to use a built in rule to align (see below), or ~g a i b /~ to specify a regular expression, similar to [[help:align-regexp][align-regexp]]. - -#+begin_src emacs-lisp - (use-package evil-lion - :after evil - :bind (:map evil-normal-state-map - ("g a" . evil-lion-left) - ("g A" . evil-lion-right) - :map evil-visual-state-map - ("g a" . evil-lion-left) - ("g A" . evil-lion-right))) -#+end_src -Lion sounds like /align/ … get it? - -Where I like to align, is on variable assignments, e.g. -#+begin_src emacs-lisp :tangle no - (let ((foobar "Something something") - (a 42) - (very-long-var "odd string")) - ;; - ) -#+end_src - -If you press ~RETURN~ for the /character/ to align, =evil-lion= package simply calls the built-in [[help:align][align]] function. This function chooses a regular expression based on a list of /rules/, and aligning Lisp variables requires a complicated regular expression. Extend [[elisp:(describe-variable 'align-rules-list)][align-rules-list]]: -#+begin_src emacs-lisp - (use-package align - :straight (:type built-in) - :config - (add-to-list 'align-rules-list - `("lisp-assignments" - (regexp . ,(rx (group (one-or-more space)) - (or - (seq "\"" (zero-or-more any) "\"") - (one-or-more (not space))) - (one-or-more ")") (zero-or-more space) eol)) - (group . 1) - (modes . align-lisp-modes)))) -#+end_src ** General Leader Key Sequences The one thing that both Spacemacs and Doom taught me, is how much I like the /key sequences/ that begin with a leader key. In both of those systems, the key sequences begin in the /normal state/ with a space key. This means, while typing in /insert state/, I have to escape to /normal state/ and then hit the space. @@ -566,6 +552,69 @@ I'm not trying an experiment where specially-placed function keys on my fancy er :global-prefix "" :non-normal-prefix "S-SPC")) #+end_src +*** Relabel the G Keys +Can’t remember all the shortcuts on the ~g~ key, and =which-key= displays the entire function, so let’s /re-add/ those keybindings, but with labels. The ~g~ is extemely convenient, yet I realize that I will never use some of the default keybindings (like ~g m~ to go to the middle of the line? Too imprecise). So I am also going to delete some of them. + +#+begin_src emacs-lisp + (use-package evil + :general + (:states '(normal visual motion operator) + ;; These go into operator mode, so the key sequence, g U i o + ;; upper cases the symbol at point: + "g u" '("downcase" . evil-downcase) + "g U" '("upcase" . evil-case) + "g ~" '("invert case" . evil-invert-case) + + ;; Use this ALL the time: + "g ;" '("last change" . evil-goto-last-change) + "g d" '("goto def" . evil-goto-definition) + "g i" '("resume insert" . evil-insert-resume) + "g v" '("resume visual" . evil-visual-restore) + + "g g" '("goto first line" . evil-goto-first-line) + "g f" '("find file" . find-file-at-point) + "g q" '("fill para" . fill-paragraph) + "g Q" '("unfill para" . unfill-paragraph) + + "g e" '("← WORD end" . evil-backward-WORD-end) ; like b + "g E" '("← word end" . evil-backward-word-end) ; like B + "g w" '("→ WORD end" . evil-forward-WORD-end) + "g W" '("→ word end" . evil-forward-word-end) + + ;; Not sure how to use these two as they need text objs + "g n" '("next match" , evil-next-match) + "g N" '("prev match" , evil-previous-match) + + "g P" '("paste after" . evil-paste-before-cursor-after) + + ;; Let's clean out keybindings already in normal mode + ;; without the initial g: + "g #" nil ; evil-search-unbounded-word-backward + "g *" nil ; evil-search-unbounded-word-forward + "g ^" nil ; evil-first-non-blank + "g $" nil ; evil-end-of-line + "g _" nil ; evil-last-non-blank ... eh + "g 0" nil ; evil-beginning-of-line + "g &" nil ; evil-ex-repeat-global-substitute + "g 8" nil ; what-cursor-position + "g F" nil ; evil-find-file-at-point-with-line + "g J" nil ; evil-join-whitespace + "g I" nil ; evil-insert-0-line ... just use I + "g m" nil ; evil-middle-of-visual-line + "g M" nil ; evil-percentage-of-line ... middle? + "g T" nil ; tab-bar-switch-to-prev-tab + "g t" nil ; tab-bar-switch-to-next-tab + + "g j" nil ; This will be a major-mode-specific keybinding + "g k" nil + + (kbd "g ") nil + (kbd "g ") nil + (kbd "g ") nil + (kbd "g ") nil + (kbd "g ") nil + (kbd "g ") nil)) +#+end_src *** Top-Level Operations Let's try this general "space" prefix by defining some top-level operations, including hitting ~space~ twice to bring up the =M-x= collection of functions: #+begin_src emacs-lisp @@ -1120,6 +1169,7 @@ Since I tweaked the help menu, I craft my own menu: #+begin_src emacs-lisp (ha-leader "h" '(:ignore t :which-key "help") + "h ." '("cursor position" . what-cursor-position) "h a" '("apropos" . apropos-command) "h c" '("elisp cheatsheet" . shortdoc-display-group) "h e" '("errors" . view-echo-area-messages) @@ -1189,8 +1239,8 @@ The [[https://github.com/minad/consult][consult project]] aims to use libraries :general (:states 'normal - "gp" 'consult-yank-pop - "gs" 'consult-line)) + "gp" '("preview paste" . 'consult-yank-pop) + "gs" '("go to line" . 'consult-line))) #+end_src *** Consult for Projects One of the reasons that Consult hasn’t been too important to me, is that I often narrow my searching based on projectile. The [[https://gitlab.com/OlMon/consult-projectile][consult-projectile]] can help with this. @@ -1291,13 +1341,13 @@ I often use the Emacs commands, ~M-t~ and whatnot to exchange words and whatnot, evil-exchange-cancel-key (kbd "gX")) :general (:states 'normal - "g x" 'evil-exchange - "g X" 'evil-exchange-cancel + "g x" '("exchange" . 'evil-exchange) + "g X" '("cancel exchange" . 'evil-exchange-cancel) ;; What about a "normal mode" binding to regular emacs transpose? - "z x" 'transpose-words - "z X" 'transpose-sexps - "z T" 'transpose-lines) + "z w" '("transpose words" . transpose-words) + "z x" '("transpose sexps" . transpose-sexps) + "z k" '("transpose lines" . transpose-lines)) :config (evil-exchange-install)) #+end_src @@ -1318,12 +1368,54 @@ Notice that you can swap: - ~gx i p~ :: paragraphs - ~gx i x~ :: programming s-expressions between parens, braces, etc. - ~gx i l~ :: lines, with the [[Evil Text Object Line][line-based text object]] project installed +*** Evil Lion +The [[https://github.com/edkolev/evil-lion][evil-lion]] package is a wrapper around Emacs’ [[help:align][align]] function. Just a little easier to use. Primary sequence is ~g a i p =~ to align along all the equal characters in the paragraph (block), or ~g a i b RET~ to use a built in rule to align (see below), or ~g a i b /~ to specify a regular expression, similar to [[help:align-regexp][align-regexp]]. + +#+begin_src emacs-lisp + (use-package evil-lion + :after evil + :general + (:states '(normal visual) + "g a" '("lion ←" . evil-lion-left) + "g A" '("lion →" . evil-lion-right))) +#+end_src +Lion sounds like /align/ … get it? + +Where I like to align, is on variable assignments, e.g. +#+begin_src emacs-lisp :tangle no + (let ((foobar "Something something") + (a 42) + (very-long-var "odd string")) + ;; + ) +#+end_src + +If you press ~RETURN~ for the /character/ to align, =evil-lion= package simply calls the built-in [[help:align][align]] function. This function chooses a regular expression based on a list of /rules/, and aligning Lisp variables requires a complicated regular expression. Extend [[elisp:(describe-variable 'align-rules-list)][align-rules-list]]: +#+begin_src emacs-lisp + (use-package align + :straight (:type built-in) + :config + (add-to-list 'align-rules-list + `("lisp-assignments" + (regexp . ,(rx (group (one-or-more space)) + (or + (seq "\"" (zero-or-more any) "\"") + (one-or-more (not space))) + (one-or-more ")") (zero-or-more space) eol)) + (group . 1) + (modes . align-lisp-modes)))) +#+end_src *** Evil Commentary -The [[https://github.com/linktohack/evil-commentary][evil-commentary]] is a VI-like way of commenting text. Yeah, I typically type ~M-;~ to call Emacs’ originally functionality, but in this case, ~g c c~ comments out a line(s), and ~g c~ takes text objects and whatnot. For instance, ~g c $~ comments to the end of the line. +The [[https://github.com/linktohack/evil-commentary][evil-commentary]] is a VI-like way of commenting text. Yeah, I typically type ~M-;~ to call Emacs’ originally functionality, but in this case, ~g c c~ comments out a line(s), and ~g c~ comments text objects and whatnot. For instance, ~g c $~ comments to the end of the line. #+begin_src emacs-lisp (use-package evil-commentary - :config (evil-commentary-mode)) + :config (evil-commentary-mode) + + :general + (:states '(normal visual motion operator) + "g c" '("comments" . evil-commentary) + "g y" '("yank comment" . evil-commentary-yank))) #+end_src *** Evil Collection Dropping into Emacs state is better than pure Evil state for applications, however, [[https://github.com/emacs-evil/evil-collection][the evil-collection package]] creates a hybrid between the two, that I like. @@ -1541,7 +1633,7 @@ While I grew up on =Control S=, I am liking the /mental model/ associated with t :config (ha-leader "j" '("jump" . avy-goto-char-timer)) :general - (:states 'normal "go" 'avy-goto-char-timer) + (:states 'normal "go" '("avy goto" . avy-goto-char-timer)) :bind ("" . avy-goto-char-timer)) #+end_src diff --git a/ha-org.org b/ha-org.org index b3ef3f0..cef17a3 100644 --- a/ha-org.org +++ b/ha-org.org @@ -533,9 +533,9 @@ Global keybindings available to all file buffers: #+name: global-keybindings #+begin_src emacs-lisp :tangle no (ha-leader - "o l" '("store link" . org-store-link) + "o l" '("store link" . org-store-link) "o x" '("org capture" . org-capture) - "o c" '("clock out" . org-clock-out)) + "o c" '("clock out" . org-clock-out)) #+end_src Bindings specific to org files: @@ -543,9 +543,10 @@ Bindings specific to org files: #+begin_src emacs-lisp :tangle no (evil-define-key '(normal motion operator visual) org-mode-map - "gu" #'org-up-element - "gb" #'org-next-block - "gB" #'org-previous-block) + "gj" '("next heading" . #'org-forward-heading-same-level) + "gk" '("prev heading" . #'org-backward-heading-same-level) + "gb" '("next block" . #'org-next-block) + "gB" '("prev block" . #'org-previous-block)) (ha-local-leader :keymaps 'org-mode-map "e" '("exports" . org-export-dispatch) @@ -1084,8 +1085,8 @@ Trying out [[https://protesilaos.com/][Protesilaos Stavrou]]’s [[https://prote :general (:states 'normal - "g [" 'logos-backward-page-dwim - "g ]" 'logos-forward-page-dwim)) + "g [" '("back page" . logos-backward-page-dwim) + "g ]" '("next page" . logos-forward-page-dwim))) #+end_src * Technical Artifacts :noexport: Let's provide a name, to allow =require= to work: diff --git a/ha-programming-elisp.org b/ha-programming-elisp.org index baa7a3d..c09105a 100644 --- a/ha-programming-elisp.org +++ b/ha-programming-elisp.org @@ -113,14 +113,11 @@ My primary use-case is for its refactoring and other unique features. For instan #+begin_src emacs-lisp (use-package lispy :config - (evil-define-key 'normal emacs-lisp-mode-map - ;; Use C-SPC to mark single letter, as these seem to be nicer expansions: - "M-v" 'lispy-mark ; Mark entire s-expression - "g f" 'lispy-ace-paren - "g F" 'lispy-ace-symbol) - - (evil-define-key 'visual emacs-lisp-mode-map - "o" 'lispy-mark-symbol) ; Mark symbol since "o" doesn't do anything + (evil-define-key '(normal visual) lispyville-mode-map + ;; Jump to interesting places: + "gf" '("ace paren" . lispy-ace-paren) + "gF" '("ace symbol" . lispy-ace-symbol) + (kbd "M-v") '("mark s-exp" . lispy-mark)) ; Mark entire s-expression (ha-local-leader :keymaps '(emacs-lisp-mode-map lisp-mode-map) "r" '(:ignore t :which-key "refactor") diff --git a/ha-programming.org b/ha-programming.org index bc37452..0a9b926 100644 --- a/ha-programming.org +++ b/ha-programming.org @@ -99,7 +99,8 @@ Why use [[https://www.flycheck.org/][flycheck]] over the built-in =flymake=? Spe I’m interested in using [[https://devdocs.io/][devdocs]] instead, which is similar, but keeps it all /inside/ Emacs (and works on my Linux system). Two Emacs projects compete for this position. The Emacs [[https://github.com/astoff/devdocs.el][devdocs]] project is active, and seems to work well. Its advantage is a special mode for moving around the documentation. #+begin_src emacs-lisp (use-package devdocs - :general (:states 'normal "gD" 'devdocs-lookup) + :general (:states 'normal + "gD" '("devdocs" . devdocs-lookup)) :config (ha-local-leader :keymaps 'prog-mode-map