diff --git a/doc/DOCUMENTATION.org b/doc/DOCUMENTATION.org index a05abeced927..5d1453b39298 100644 --- a/doc/DOCUMENTATION.org +++ b/doc/DOCUMENTATION.org @@ -36,6 +36,7 @@ - [[#setting-configuration-layers-variables][Setting configuration layers variables]] - [[#excluding-packages][Excluding packages]] - [[#hooks][Hooks]] + - [[#binding-keys][Binding keys]] - [[#custom-variables][Custom variables]] - [[#main-principles][Main principles]] - [[#evil][Evil]] @@ -542,6 +543,58 @@ configuration at the beginning and end of =Spacemacs= loading process. loading. - =dotspacemacs/config= is triggered at the very end of =Spacemacs= loading. +*** Binding keys +Key sequences are bound to commands in Emacs in various keymaps. The most basic +map is the global-map. Setting a key binding the global-map uses the function +=global-set-key= as follows (to the command =forward-char= in this case). + +#+begin_src emacs-lisp + (global-set-key (kbd "C-]") 'forward-char) +#+end_src + +The =kbd= macro accepts a string describing a key sequence. The global-map is +often shadowed by other maps. For example, evil-mode defines keymaps that target +states (or modes in vim terminology). Here is an example that creates the same +binding as above but only in insert state (=define-key= is a built-in function. +Evil-mode has its own functions for defining keys). + +#+begin_src emacs-lisp + (define-key evil-insert-state-map (kbd "C-]") 'forward-char) +#+end_src + +Perhaps most importantly for spacemacs is the use of the evil-leader package, +which binds keys to the evil-leader keymap. This is where most of the spacemacs +bindings live. There are two related commands from this package which are used +as follows. + +#+begin_src emacs-lisp + (evil-leader/set-key "C-]" 'forward-char) + (evil-leader/set-key-for-mode 'emacs-lisp-mode "C-]" 'forward-char) +#+end_src + +These functions use a macro like =kbd= to translate the key sequences for you. +The second function, =evil-leader/set-key-for-mode=, binds the key only in the +specified mode. The second key binding would not be in effect in =org-mode= for +example. + +Finally, one should be aware of prefix keys. Essentially, all keymaps can be +nested. Nested keymaps are used extensively in spacemacs, and in vanilla Emacs +for that matter. For example, ~SPC a~ points to key bindings for "applications", +like ~SPC ac~ for =calc-dispatch=. Nesting bindings is easy. + +#+begin_src emacs-lisp + (spacemacs/declare-prefix "]" "bracket-prefix") + (evil-leader/set-key "]]" 'double-bracket-command) +#+end_src + +The first line declares ~SPC ]~ to be a prefix and the second binds the key +sequence ~SPC ]]~ to the corresponding command. The first line is actually +unnecessary to create the prefix, but it will give your new prefix a name that +key-discovery tools can use (e.g., which-key). + +There is much more to say about bindings keys, but these are the basics. Keys +can be bound in your =~/.spacemacs= file or in individual layers. + *** Custom variables Custom variables configuration from =M-x customize-group= which are automatically saved by Emacs are stored at the end of your =~/.spacemacs= file. diff --git a/spacemacs/funcs.el b/spacemacs/funcs.el index 18f7771092da..95c690dc57b6 100644 --- a/spacemacs/funcs.el +++ b/spacemacs/funcs.el @@ -79,24 +79,38 @@ "Declare a prefix PREFIX. PREFIX is a string describing a key sequence. NAME is a symbol name used as the prefix command. LONG-NAME if given is stored in `spacemacs/prefix-command-alist'." - (let ((command (intern (concat spacemacs/prefix-command-string name))) - (full-prefix-vim (listify-key-sequence (kbd (concat dotspacemacs-leader-key " " prefix)))) - (full-prefix-emacs (listify-key-sequence (kbd (concat dotspacemacs-emacs-leader-key " " prefix))))) + (let* ((command (intern (concat spacemacs/prefix-command-string name))) + (full-prefix (concat dotspacemacs-leader-key " " prefix)) + (full-prefix-emacs (concat dotspacemacs-emacs-leader-key " " prefix)) + (full-prefix-lst (listify-key-sequence (kbd full-prefix))) + (full-prefix-emacs-lst (listify-key-sequence + (kbd full-prefix-emacs)))) ;; define the prefix command only if it does not already exist (unless long-name (setq long-name name)) - (unless (lookup-key evil-leader--default-map prefix) - (define-prefix-command command) - (evil-leader/set-key prefix command) - (push (cons full-prefix-vim long-name) spacemacs/prefix-titles) - (push (cons full-prefix-emacs long-name) spacemacs/prefix-titles)))) - -(defun spacemacs/declare-prefix-for-mode (mode prefix name) + (if (fboundp 'which-key-declare-prefixes) + (which-key-declare-prefixes + full-prefix-emacs (cons name long-name) + full-prefix (cons name long-name)) + (unless (lookup-key evil-leader--default-map prefix) + (define-prefix-command command) + (evil-leader/set-key prefix command) + (push (cons full-prefix-lst long-name) spacemacs/prefix-titles) + (push (cons full-prefix-emacs-lst long-name) spacemacs/prefix-titles))))) + +(defun spacemacs/declare-prefix-for-mode (mode prefix name &optional long-name) "Declare a prefix PREFIX. MODE is the mode in which this prefix command should be added. PREFIX is a string describing a key sequence. NAME is a symbol name used as the prefix command." - (let ((command (intern (concat spacemacs/prefix-command-string name)))) - (define-prefix-command command) - (evil-leader/set-key-for-mode mode prefix command))) + (let ((command (intern (concat spacemacs/prefix-command-string name))) + (full-prefix (concat dotspacemacs-leader-key " " prefix)) + (full-prefix-emacs (concat dotspacemacs-emacs-leader-key " " prefix))) + (unless long-name (setq long-name name)) + (if (fboundp 'which-key-declare-prefixes-for-mode) + (which-key-declare-prefixes-for-mode mode + full-prefix-emacs (cons name long-name) + full-prefix (cons name long-name)) + (define-prefix-command command) + (evil-leader/set-key-for-mode mode prefix command)))) (defun spacemacs/activate-major-mode-leader () "Bind major mode key map to `dotspacemacs-major-mode-leader-key'." diff --git a/spacemacs/packages.el b/spacemacs/packages.el index 3eafed9c193c..ab11c30cee94 100644 --- a/spacemacs/packages.el +++ b/spacemacs/packages.el @@ -3699,18 +3699,27 @@ one of `l' or `r'." (which-key-add-key-based-replacements (concat leader-key " m") "major mode commands" (concat leader-key " " dotspacemacs-command-key) "M-x")) + (if (fboundp 'which-key-declare-prefixes) + (which-key-declare-prefixes + dotspacemacs-leader-key '("root" . "Spacemacs root") + dotspacemacs-emacs-leader-key '("root" . "Spacemacs root") + (concat dotspacemacs-leader-key " m") + '("major-mode-cmd" . "Major mode commands") + (concat dotspacemacs-emacs-leader-key " m") + '("major-mode-cmd" . "Major mode commands")) + ;; no need to use this after everyone updates which-key + (setq which-key-prefix-title-alist + `((,(listify-key-sequence + (kbd (concat dotspacemacs-leader-key " m"))) . "Major mode commands") + (,(listify-key-sequence + (kbd (concat dotspacemacs-emacs-leader-key " m"))) . "Major mode commands") + (,(listify-key-sequence + (kbd dotspacemacs-leader-key)) . "Spacemacs root") + (,(listify-key-sequence + (kbd dotspacemacs-emacs-leader-key)) . "Spacemacs root"))) + (nconc which-key-prefix-title-alist spacemacs/prefix-titles)) ;; disable special key handling for spacemacs, since it can be ;; disorienting if you don't understand it - (setq which-key-prefix-title-alist - `((,(listify-key-sequence - (kbd (concat dotspacemacs-leader-key " m"))) . "Major mode commands") - (,(listify-key-sequence - (kbd (concat dotspacemacs-emacs-leader-key " m"))) . "Major mode commands") - (,(listify-key-sequence - (kbd dotspacemacs-leader-key)) . "Spacemacs root") - (,(listify-key-sequence - (kbd dotspacemacs-emacs-leader-key)) . "Spacemacs root"))) - (nconc which-key-prefix-title-alist spacemacs/prefix-titles) (setq which-key-special-keys nil which-key-use-C-h-for-paging t which-key-echo-keystrokes 0.02