diff --git a/init.example.el b/init.example.el index 0b8f82495..f03e1169f 100644 --- a/init.example.el +++ b/init.example.el @@ -17,6 +17,7 @@ (doom! :input ;;chinese ;;japanese + ;;layout ; auie,ctsrnm is the superior home row :completion company ; the ultimate code completion backend diff --git a/modules/input/layout/+bepo.el b/modules/input/layout/+bepo.el new file mode 100644 index 000000000..b31afe121 --- /dev/null +++ b/modules/input/layout/+bepo.el @@ -0,0 +1,112 @@ +;;; input/layout/+bepo.el -*- lexical-binding: t; -*- + +;; NOTE: the evaluation loads the whole autoload/bepo.el file but it doesn't really matter as other +;; functions are eagerly called in this block +;; NOTE: since this file is loaded before $DOOMDIR/config.el, the cr-rotation-style variable +;; if not default needs to be set up in $DOOMDIR/init.el +(fset 'doom-bepo--evil-collection-hook + (doom-bepo-rotate-collection-keymaps-h-builder doom-bepo-cr-rotation-style)) +(add-hook 'evil-collection-setup-hook #'doom-bepo--evil-collection-hook) + +;; Highlight non breaking spaces as error in prog modes only +;; TODO: this variable is defined in a file called xdisp.c Will that work in non-X builds ? +(setq nobreak-char-display t) +(set-face-attribute 'nobreak-space nil :underline t) + +(add-transient-hook! 'doom-init-modules-hook + ;; "ts" would be a little too common for an evil escape sequence + (setq evil-escape-key-sequence "gq") + (setq avy-keys '(?a ?u ?i ?e ?, ?c ?t ?s ?r ?n) + lispy-avy-keys '(?a ?u ?i ?e ?, ?c ?t ?s ?r ?n ?m ?b ?é ?p ?o ?è ?v ?d ?l ?j ?z)) + ;; :ui window-select settings, ignoring +numbers flag for now + (after! ace-window + (setq aw-keys '(?a ?u ?i ?e ?, ?c ?t ?s ?r ?n))) + (after! switch-window + (setq switch-window-shortcut-style 'qwerty + switch-window-qwerty-shortcuts '("a" "u" "i" "e" "," "c" "t" "s" "r"))) + + (doom-bepo-rotate-ts-bare-keymap '(read-expression-map)) + (doom-bepo-rotate-bare-keymap '(evil-window-map) doom-bepo-cr-rotation-style) + + (map! :i "C-t" #'+default-newline + (:when (featurep! :editor multiple-cursors) + :prefix "gz" + :nv "t" #'evil-mc-make-cursor-move-next-line + :nv "s" #'evil-mc-make-cursor-move-prev-line + ;; the old toggle mapping (t) is made available both on "T" for mnemonics and + ;; "j" as a "classic" rotation + :nv "T" #'+multiple-cursors/evil-mc-toggle-cursors + :nv "j" #'+multiple-cursors/evil-mc-toggle-cursors) + (:when (featurep! :ui popup) + :n "C-$" #'+popup/toggle + :n "C-#" #'+popup/raise)) + (map! + :leader + :desc "Window" "é" 'evil-window-map + (:when (featurep! :ui popup) + :desc "Toggle last popup" "#" #'+popup/toggle) + (:when (featurep! :ui workspaces) + :desc "Switch buffer" "«" #'switch-to-buffer) + :desc "Switch to last buffer" "$" #'evil-switch-to-windows-last-buffer + (:when (featurep! :ui workspaces) + (:prefix-map ("TAB" . "workspace") + :desc "Switch to last workspace" "$" #'+workspace/other + :desc "Next workspace" ")" #'+workspace/switch-right + :desc "Previous workspace" "(" #'+workspace/switch-left)) + (:prefix-map ("b" . "buffer") + :desc "Previous buffer" "(" #'previous-buffer + :desc "Next buffer" ")" #'next-buffer) + (:prefix-map ("c" . "code") + :desc "Jump to documentation" "S" #'+lookup/documentation) + (:prefix-map ("g" . "git") + (:when (featurep! :ui vc-gutter) + :desc "Jump to next hunk" ")" #'git-gutter:next-hunk + :desc "Jump to previous hunk" "(" #'git-gutter:previous-hunk)) + (:prefix-map ("p" . "project") + :desc "Browse other project" "»" #'doom/browse-in-other-project)) + (after! treemacs + (doom-bepo-rotate-ts-bare-keymap '(evil-treemacs-state-map))) + (after! (:or helm ivy) + (doom-bepo-rotate-bare-keymap +default-minibuffer-maps doom-bepo-cr-rotation-style)) + (after! company + (doom-bepo-rotate-bare-keymap '(company-active-map company-search-map) doom-bepo-cr-rotation-style)) + (after! helm + (doom-bepo-rotate-bare-keymap '(helm-map) doom-bepo-cr-rotation-style)) + (after! general + (doom-bepo-rotate-evil-keymap doom-bepo-cr-rotation-style)) + (after! evil-snipe + (doom-bepo--evil-collection-hook + nil + '(evil-snipe-local-mode-map evil-snipe-override-local-mode-map))) + (after! lispyville + ;; <> en direct + (general-translate-key '(normal motion) 'lispyville-mode-map + "«" "<" + "»" ">")) + (after! (evil magit evil-magit) + (doom-bepo-rotate-ts-bare-keymap + '(magit-mode-map + magit-diff-section-base-map + magit-staged-section-map + magit-unstaged-section-map + magit-untracked-section-map)) + ;; Without this, "s" is mapped to 'magit-delete-thing (the old "k" for "kill") and + ;; takes precedence over the evil command to go up one line + (map! :map magit-mode-map "s" nil) + (doom-bepo--evil-collection-hook + nil + '(magit-mode-map + magit-cherry-mode-map + magit-mode-map + magit-blob-mode-map + magit-diff-mode-map + magit-log-mode-map + magit-log-select-mode-map + magit-reflog-mode-map + magit-status-mode-map + magit-file-mode-map + magit-log-read-revs-map + magit-process-mode-map + magit-refs-mode-map))) + (after! evil-easymotion + (doom-bepo-rotate-bare-keymap '(evilem-map) doom-bepo-cr-rotation-style))) diff --git a/modules/input/layout/README.org b/modules/input/layout/README.org new file mode 100644 index 000000000..b36ec7b4b --- /dev/null +++ b/modules/input/layout/README.org @@ -0,0 +1,90 @@ +#+TITLE: input/layout +#+DATE: Jun 29, 2020 +#+SINCE: v3.0 +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] + - [[#bépo][Bépo]] + - [[#leaving-mnemonics-alone-when-possible][Leaving mnemonics alone when possible]] + - [[#possible-contributions][Possible contributions]] +- [[#configuration][Configuration]] + - [[#bépo-1][Bépo]] +- [[#troubleshooting][Troubleshooting]] + - [[#how-to-deactivate-the-new-bindings-and-go-back-to-the-old-ones-][How to deactivate the new bindings and go back to the old ones ?]] + +* Description +This module provides barebones support for using Doom with evil-mode with non-qwerty layouts. + +** Maintainers ++ @gagbo (Author) + +** Module Flags ++ =+bepo= Enables modifications for the BÉPO layout (customized with version 1.1 in mind) + +** Plugins +None + +* Prerequisites +This module should only be active if evil is enabled. It uses a general.el utility function, and +the hooks provided by evil-collection to make the necessary changes. + +* Features +# An in-depth list of features, how to use them, and their dependencies. +** Bépo +Support for the bépo layout includes: +- Setting Avy keys to the correct home row keys +- Changing navigation keys to =ctsr= + + old =t= is mapped to =j= + + old =s= is mapped to =k= (i.e. staging in the magit status buffer is done with =k=) + + See [[*Configuration][Configuration]] to see where old =c= and =r= functions + are remapped +- Bind =<>= functions to =«»= keys when possible +- Bind =[]= functions to =()= keys when possible +- Bind =é= key to =w= functions when possible +- Bind =è= key to useful functions when possible +- Bind =`~= functions to =$#= keys when possible + +*** Leaving mnemonics alone when possible +Exchanging =hjkl= to =ctsr= has the effect of destroying a few mnemonics: the +change operator becomes =l= for example, or the window split becomes =SPC é k=. + +The module tries to limit those changes to the minimum, especially in special +buffers. A concrete example is magit. + +As the =magit: project= buffer (obtained with =magit-status=) does not need +left-right navigation, keys =c=, =r=, =h=, and =l= keep their "expected" bindings, +while =t=, =s=, =j=, and =k= are flipped: +- checking the log from a magit buffer is still on =l= +- staging a file/region has been moved to =k= + +*** Possible contributions +A nice addition in the future might be to have all the normal mode bindings that +start with =g= start with =,= instead to avoid the curl on these common +bindings. This is *not* implemented for the time being. + +* Configuration +** Bépo +=doom-bepo-cr-rotation-style= controls whether: +- =qwerty-c= functions are mapped on =bépo-l= key, and =qwerty-r= functions on + =bépo-h= key (='ergodis=), or +- =qwerty-c= functions are mapped on =bépo-h= key, and =qwerty-r= functions on + =bépo-l= key (='strict=) +='strict= would be the logical choice but the =c= functions are used more often +than the =r= ones so [[https://bepo.fr/wiki/Vim#Principe][Ergodis]] advises to +actually put all the =c= functions on the key that does not need a curl. + +* Troubleshooting +# Common issues and their solution, or places to look for help. +** How to deactivate the new bindings and go back to the old ones ? +If you are learning a new layout you might want to go back to tho old one to +"get work done". Sadly the only way is to comment out the module, run =doom +sync= and restart emacs. + +Restoring the session =SPC q l= by default helps to lower the impact of the +restart. diff --git a/modules/input/layout/autoload/bepo.el b/modules/input/layout/autoload/bepo.el new file mode 100644 index 000000000..83ba06287 --- /dev/null +++ b/modules/input/layout/autoload/bepo.el @@ -0,0 +1,268 @@ +;;; input/keymaps/autoload/bepo.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun doom-bepo-rotate-ts-bare-keymap (keymaps) + "Rotate [jk] with [ts] in KEYMAP." + (dolist (keymap keymaps) + (general-translate-key nil keymap + "t" "j" + "T" "J" + "s" "k" + "S" "K" + "j" "t" + "J" "T" + "k" "s" + "K" "S" + "C-t" "C-j" + "C-s" "C-k" + "C-j" "C-t" + "C-k" "C-s" + "M-t" "M-j" + "M-s" "M-k" + "M-j" "M-t" + "M-k" "M-s" + "C-S-t" "C-S-j" + "C-S-s" "C-S-k" + "C-S-j" "C-S-t" + "C-S-k" "C-S-s" + "M-S-t" "M-S-j" + "M-S-s" "M-S-k" + "M-S-j" "M-S-t" + "M-S-k" "M-S-s"))) + +;;;###autoload +(defun doom-bepo-rotate-é-quotes-bare-keymap (keymaps) + "Rotate [w<>] with [é«»] in KEYMAP." + (dolist (keymap keymaps) + (general-translate-key nil keymap + "é" "w" + "É" "W" + "«" "<" + "»" ">" + "C-é" "C-w" + "C-«" "C-<" + "C-»" "C->" + "M-é" "M-w" + "M-«" "M-<" + "M-»" "M->" + "C-S-é" "C-S-w" + "C-S-«" "C-S-<" + "C-S-»" "C-S->" + "M-S-é" "M-S-w" + "M-S-«" "M-S-<" + "M-S-»" "M-S->"))) + +;;;###autoload +(defun doom-bepo-rotate-cr-bare-keymap (keymaps &optional style) + "Rotate [hl] with [cr] in KEYMAP. + +If STYLE is nil or 'ergodis, the old 'c' bindings will be mapped on 'l' and the old 'r' on 'h'. +Otherwise if STYLE is 'strict, the old 'c' bindings will be mapped on 'h' and the old 'r' on 'l'. +Undefined behaviour in other cases, for forward compatibility." + (let ((style (or style 'ergodis))) + (dolist (keymap keymaps) + (progn + (general-translate-key nil keymap + "c" "h" + "C" "H" + "r" "l" + "R" "L" + "C-c" "C-h" + "C-r" "C-l" + "M-c" "M-h" + "M-r" "M-l" + "C-S-c" "C-S-h" + "C-S-r" "C-S-l" + "M-S-c" "M-S-h" + "M-S-r" "M-S-l") + (cond ((eq style 'ergodis) + (general-translate-key nil keymap + "h" "r" + "H" "R" + "l" "c" + "L" "C" + "C-h" "C-r" + "C-l" "C-c" + "M-h" "M-r" + "M-l" "M-c" + "C-S-h" "C-S-r" + "C-S-l" "C-S-c" + "M-S-h" "M-S-r" + "M-S-l" "M-S-c")) + (t + (general-translate-key nil keymap + "h" "c" + "H" "C" + "l" "r" + "L" "R" + "C-h" "C-c" + "C-l" "C-r" + "M-h" "M-c" + "M-l" "M-r" + "C-S-h" "C-S-c" + "C-S-l" "C-S-r" + "M-S-h" "M-S-c" + "M-S-l" "M-S-r"))))))) + +;;;###autoload +(defun doom-bepo-rotate-bare-keymap (keymaps &optional cr-style) + "Rotate [hjklw<>] with [ctsré«»] in KEYMAP. +See `doom-bepo-cr-rotation-style' for the meaning of CR-STYLE" + (doom-bepo-rotate-cr-bare-keymap keymaps cr-style) + (doom-bepo-rotate-ts-bare-keymap keymaps) + (doom-bepo-rotate-é-quotes-bare-keymap keymaps)) + +;;;###autoload +(defun doom-bepo-rotate-evil-keymap (&optional cr-style) + "Remap evil-{normal,operator,motion,...}-state-map + to be more natural with Bépo keyboard layout. +See `doom-bepo-cr-rotation-style' for the meaning of CR-STYLE." + (general-translate-key nil '(normal motion visual) + "c" "h" + "C" "H" + "t" "j" + "T" "J" + "s" "k" + "S" "K" + "r" "l" + "R" "L" + "j" "t" + "J" "T" + "k" "s" + "K" "S") + (cond ((eq cr-style 'ergodis) + (general-translate-key nil '(normal motion visual) + "h" "r" + "H" "R" + "l" "c" + "L" "C")) + (t + (general-translate-key nil '(normal motion visual) + "h" "c" + "H" "C" + "l" "r" + "L" "R"))) + + (general-translate-key nil '(insert) + "C-c" "C-h" + "C-C" "C-H" + "C-t" "C-j" + "C-T" "C-J" + "C-s" "C-k" + "C-S" "C-K" + "C-r" "C-l" + "C-R" "C-L" + "C-j" "C-t" + "C-J" "C-T" + "C-k" "C-s" + "C-K" "C-S") + (cond ((eq cr-style 'ergodis) + (general-translate-key nil '(insert) + "C-h" "C-r" + "C-H" "C-R" + "C-l" "C-c" + "C-L" "C-C")) + (t + (general-translate-key nil '(insert) + "C-h" "C-c" + "C-H" "C-C" + "C-l" "C-r" + "C-L" "C-R"))) + + + ;; <> as direct access + (general-translate-key nil '(normal motion) + "«" "<" + "»" ">") + + ;; " è replaces ^0 to go at BOL + (general-translate-key nil '(normal motion) + "è" "^" + "È" "0") + + ;; [W] -> [É] + ;; [C-W] -> [W] + (general-translate-key nil '(normal motion operator) + "é" "w" + "É" "W" + "w" "C-w" + "W" "C-w C-w")) + +;;;###autoload +(defun doom-bepo-rotate-collection-keymaps-h-builder (cr-style) + "Build a hook that remaps evil-collection customizations to be more natural + with Bépo keyboard layout, according to CR-STYLE (see `doom-bepo-cr-rotation-style')." + (let* ((cr-style (or cr-style 'ergodis)) + (doom-bepo-hook (lambda (_mode mode-keymaps &rest _rest) + (dolist (keymap mode-keymaps) + (general-translate-key '(normal motion visual) keymap + "c" "h" + "C" "H" + "t" "j" + "T" "J" + "s" "k" + "S" "K" + "r" "l" + "R" "L" + "j" "t" + "J" "T" + "k" "s" + "K" "S") + (cond ((eq cr-style 'ergodis) + (general-translate-key '(normal motion visual) keymap + "h" "r" + "H" "R" + "l" "c" + "L" "C")) + (t + (general-translate-key '(normal motion visual) keymap + "h" "c" + "H" "C" + "l" "r" + "L" "R"))) + + + (general-translate-key '(insert) keymap + "C-c" "C-h" + "C-C" "C-H" + "C-t" "C-j" + "C-T" "C-J" + "C-s" "C-k" + "C-S" "C-K" + "C-r" "C-l" + "C-R" "C-L" + "C-j" "C-t" + "C-J" "C-T" + "C-k" "C-s" + "C-K" "C-S") + (cond ((eq cr-style 'ergodis) + (general-translate-key '(insert) keymap + "C-h" "C-r" + "C-H" "C-R" + "C-l" "C-c" + "C-L" "C-C")) + (t + (general-translate-key '(insert) keymap + "C-h" "C-c" + "C-H" "C-C" + "C-l" "C-r" + "C-L" "C-R"))) + + ;; <> en direct + (general-translate-key '(normal motion visual) keymap + "«" "<" + "»" ">") + + ;; è pour aller au début de ligne + (general-translate-key '(normal motion visual) keymap + "è" "^" + "È" "0") + + ;; [W] -> [É] + ;; [C-W] -> [W] + (general-translate-key '(normal motion operator visual) keymap + "é" "w" + "É" "W" + "w" "C-w" + "W" "C-w C-w"))))) + doom-bepo-hook)) diff --git a/modules/input/layout/config.el b/modules/input/layout/config.el new file mode 100644 index 000000000..2e8d667dd --- /dev/null +++ b/modules/input/layout/config.el @@ -0,0 +1,14 @@ +;;; input/keymaps/config.el -*- lexical-binding: t; -*- + +(defvar doom-bepo-cr-rotation-style 'ergodis + "Modify this variable in your $DOOMDIR/init.el +Style of binding rotation for the cr keys. +If 'ergodis, then the module maps the old 'c' bindings to 'l' and the old 'r' to 'h', as +the 'change' function is used more often and 'l' is easier to reach than 'h' in bépo. + +If 'strict, the module does a normal swap and 'c' bindings go to 'h', 'r' bindings go to 'l'. + +In all cases, 'h' functions go to 'c' and 'l' ones go to 'r' so the navigation keys still feel vim-like.") + +(when (featurep! +bepo) + (load! "+bepo"))