jwiegley / use-package

A use-package declaration for simplifying your .emacs
https://jwiegley.github.io/use-package
GNU General Public License v3.0
4.38k stars 260 forks source link

bind and bind-keymap question [maybe feature request] #757

Closed Ergus closed 5 years ago

Ergus commented 5 years ago

Hi I have a question related with prefix and bind-keymaps.

I want to know if is possible somehow (simple) in use-package to associate a prefix to a keymap (or a different concept, lets call it prefix/context) in order to associate just single letters to commands within the context.

For example if I want that all the counsel commands start with C-c c (plus a letter) I could make that the autoload for counsel will be for the prefix C-c c instead for a command itself, so the user experience will be better because the package will start loading some miliseconds before the keybind is finished

But also the user only needs to specify shorter binds within this context (a . counsel-ag) (g . counsel-grep) and so on, because the prefix will be implicit as otherwise the commands binds (the single letters) will be disabled, and in the global map will be only the C-c c.

It is already possible to emulate partially this just putting the binds with same prefix in the global map, but maybe use-package could simplify it in a more elegant way?

As a plus it will be possible to add a name for the context/prefix that can be used by which-key. What we do now with:

:init
`(which-key-add-key-based-replacements "C-c c" "counsel")`
rprimus commented 5 years ago

Thu Apr 4 11:14:48 BST 2019

@Ergus Have a look at: https://github.com/abo-abo/hydra

Ergus commented 5 years ago

Yes I know hydra, but I was thinking in something simpler (no repeat needed like in hydra) that could be supported in use-package, because sometimes hydra has issues for some commands and is hard to configure for new ussers.

for example something like this:

(use-package counsel
  :diminish
  :bind ( ("C-x C-f" . counsel-find-file)
         ("C-c c a" . counsel-ag)
         ("C-c c i" . counsel-imenu)
         ("C-c c g" . counsel-grep)
         ("C-c c t" . counsel-git)
             ("C-c c r" . counsel-rg)            ; like git grep
         ("C-c c r" . counsel-git-grep)
         ("C-c c l" . counsel-locate))
  :init
  (which-key-add-key-based-replacements "C-c c" "counsel")
  :config
  (counsel-mode t))

could be simplified in something like:

(use-package counsel
    :diminish
    :bind ( ("C-x C-f" . counsel-find-file)
            ("C-c c" . newname "counsel"
            ("a" . counsel-ag)
            ("i" . counsel-imenu)
            ("g" . counsel-grep)
            ("t" . counsel-git)
                ("r" . counsel-rg)
            ("r" . counsel-git-grep)
            ("l" . counsel-locate)))
  :config
  (counsel-mode t))

The syntax is more clear and will produce a final environment easier to remember for the user. With a grouped autoload, And with no need to go for the complexity in hydra.

dangom commented 5 years ago

Here are my magit and projectile use-package declarations, which may help you achieve what you want for counsel. It integrates out of the box with which-key.

;; Keymap style similar to Spacemacs default keymap.
(define-prefix-command 'leader-map)
(defvar leader-map-key "M-m")

(use-package magit
  :defer 2
  :bind (:map leader-map
              :prefix "g"  ; This will create a prefix "M-m g" for magit.
              :prefix-map magit-map
              :prefix-docstring "Git related"
              ("s" . magit-status)
              ("i" . magit-init)
              ("S" . magit-stage-file)
              ("U" . magit-unstage-file)
              ("b" . magit-blame-addition))
  :init
  (setq magit-no-message '("Turning on magit-auto-revert-mode..."))
  :config
  (setq magit-diff-refine-hunk t)
  (setq magit-stage-all-confirm nil)
  (setq magit-unstage-all-confirm nil)
  :hook (magit-mode . visual-line-mode))

(use-package projectile
  :config (projectile-mode +1)
  :bind-keymap ("C-c p" . projectile-command-map))

And remember that use-package is only a macro wrapping (amongst others) bind-key.el. Reading through bind-key.el code will tell you what is currently possible with use-package.

Ergus commented 5 years ago

Hi @dangom :

Thanks for the config, I had a similar approach already. But I know that use-package is syntax sugar for other packages, and is exactly for that reason why I am asking for such a functionality.

Because it fixes perfectly with the use-package philosophy, and shouldn't really add excessive complexity to what is already implemented. For example, your code for magit is a bit longer than using bind-keymap. But also in this case magit and counsel has their own map, but what happens with packages that has many functions but not their own map but the user want's to group them. The user will need to create a new one. Finally it will be needed to add some code for which-key. That for a new emacs users takes some months (o years) to understand all those packages (or even know that the functionalities exists, because everything is in the manual, but the manual is HUGE and there are more than 4000 packages right now to look around in elpa+melpa)

The point is that use-package could help with this, centralizing and organizing the functionalities (and with the nice documentation); specially for new user's config (I am always thinking in the new users because the learning curve with emacs was VERY hard for me on the beginning).

dangom commented 5 years ago

No, in my case I created a new sub-prefix "g" for magit within my M-m prefix, and use-package then bound the separate commands into this "g" subprefix, which I called magit-map (which interplays nicely with which-key). There was no magit-map before.

This is essentially what you wanted to achieve, except you'd change prefix to "C-c c". Here's what you are looking for:

(use-package counsel
  :bind (("C-x C-f" . counsel-find-file)
         :prefix "C-c c"
         :prefix-map counsel-map
         :prefix-docstring "Counsel"
         ("a" . counsel-ag)
         ("i" . counsel-imenu)
         ("g" . counsel-grep)
         ("t" . counsel-git)
             ("r" . counsel-rg)
         ("r" . counsel-git-grep)
         ("l" . counsel-locate)))
Ergus commented 5 years ago

Ok, Now I see. Then an example like yours would be nice to be in the use-package documentation. Literally like the last one. With prefix, prefix-map and prefix docstring. The only missing is the which-key part, but it is just one extra line, so, I can live with that. Very thanks.

fikovnik commented 5 years ago

I might be missing something, but from the comments I still do not really understand how to make it work without manually calling the which-key-add-key-based-replacements for both the keymap and the individual bindings.

Concretely, I tried the following:

(use-package iqa
  :bind (:prefix "C-c e"
         :prefix-map my-emacs-helper-map
         :prefix-docstring "Emacs" 
         ("e" . iqa-find-user-init-file)
         ...
         ("c" . fikovnik/sync-configuration-and-compile)))

When I press C-c I would like to see e → +Emacs instead of e → +prefix. Similarly, when I press C-c e, I would like to see c → sync & compile instead of c → fikovnik/sync-configuration-and-compile.

To do that I add:

  :init
  (which-key-add-key-based-replacements "C-c e" "Emacs")
  (which-key-add-key-based-replacements "C-c e c" "sync & compile")

But this way I have to keep two binding in sync. I was wondering if there is a better to do this? A way to attach description for the key binding (e.g. ("e" . '("sync & compile" . fikovnik/sync-configuration-and-compile))) for which I could then write a function that would call the which-key-add-key-based-replacement. Thank you!

dangom commented 5 years ago

I don't know why you see e -> +prefix. I see the map name at my end. Maybe you see +prefix because you already have other stuff bound under C-c e which does not come from your use-package declaration.

Additionally, it seems that adding support for the syntax you suggest would either add which-key as a dependency to use-package, which makes 0 sense, or require writing a use-package extension (see use-package-hydra for an example).

I would try Reddit or Emacs Exchange for alternative solutions. Maybe you have more luck over there.

fikovnik commented 5 years ago

Thank you. I will look into use-package extensions, that looks like a way to go.