【求教】hydra 与 use-package 结合

已解决,见21楼,感谢各位的帮助和指点


需求: lazy load emms(从而提高启动速度),在我按下 s-c m 后加载 emms ( s-c 是我 hydra 的前缀)

目前代码:

emms,我的理解是:

  • init 是 emacs 启动时就会运行的,因此会定义 hydra-media-player/body
  • 当 hydra-media-player/body 这个 command 被调用时,运行 config 的内容,加载 emms-setup 等
(use-package emms
  :commands (hydra-media-player/body)
  :init
  (defhydra hydra-media-player ()
    "emms"
    ("p" emms-pause               "pause/resume")
    ("l" emms-play-playlist       "playlist")
    ("f" emms-play-file           "song")
    ("d" emms-play-directory-tree "directory")
    (">" emms-next                "next")
    ("<" emms-previous            "previous")
    ("s" emms-stop                "stop")
    ("q" nil                      "quit"))

  :config
  (require 'emms-setup)
  (emms-standard)
  (emms-default-players)
  )

hydra:

(defhydra hydra-wenshan-emacs ()
  "Hydra"
  ("m" hydra-media-player/body "media player" :exit t)
  ("s" aweshell-new "eshell" :exit t)
  ("c" compile "compile" :exit t)
  ("r" recompile "re-compile" :exit t)
  ("q" nil "quit"))

(global-set-key (kbd "s-c") 'hydra-wenshan-emacs/body)

问题: s-c m 后能看到 hydra-media-player 的那些选项,但按任意选项,都会出现类似 “Wrong type argument: commandp, emms-play-directory-tree” 的错误,求指点

一种办法是把 hydra-media-player 里面用到的命令都加到 commands 里:

(use-package emms
  :commands (emms-play-directory-tree)
  :init
  (defhydra hydra-media-player ()
    "emms"
    ("p" emms-pause               "pause/resume")
    ("l" emms-play-playlist       "playlist")
    ("f" emms-play-file           "song")
    ("d" emms-play-directory-tree "directory")
    (">" emms-next                "next")
    ("<" emms-previous            "previous")
    ("s" emms-stop                "stop")
    ("q" nil                      "quit"))

  :config
  (require 'emms-setup)
  (emms-standard)
  (emms-default-players)
  )

但这样代码有重复,请问有更简洁的写法吗?

这里应该并不会

1 个赞

你可能需要这个插件:

1 个赞

问下s-c这个绑定的是什么 (global-set-key (kbd "s-c") 'hydra-wenshan-emacs/body),C-c是ctrl+c,M-c是alt+c,“s-c”还以为是shift+c,一试发现不对,shift+…也是我的hydra前缀

Win-c

2 个赞

原来是这样,我还以为终于遇到有人用shift开头的快捷键了

总之,问题原因就是 xxx/body 调用之时,emms-* 函数必须就绪。

你可以简单地再写一个函数绑定 “m” 键,在这个函数里面 (require 'emms) (xxx/body)

补充:也可以加到 :pre 中,参考 GitHub - abo-abo/hydra: make Emacs bindings that stick around

1 个赞

或许你可以使用

(defhydra hydra-media-player (:body-pre
                              (progn 
                                (require 'emms-setup)
                                (emms-standard)
                                (emms-default-players)))
  ...)

或者直接使用(advice-add 'hydra-media-player/body :before ...)

1 个赞

它的subroutine pretty-hydra 更通用, 局限性质更小,而且可以在其之上建立更加强大的use-package 的key。

这个错误一般都是函数不是 interactive的。我没有使用 emms,用下面这个简单测试了下是没有问题的。

(defhydra hydra-test2 ()
  "test2"
  ("l" counsel-load-theme "load theme")
  ("q" nil                      "quit"))

(defhydra hydra-test ()
  "Hydra"
  ("m" hydra-test2/body "test" :exit t)
  ("q" nil "quit"))

(global-set-key (kbd "s-c") 'hydra-test/body)

最近可好?有没有受到疫情影响?

多谢推荐,浏览了一下 README,好像不太能达到我想要的效果诶

s for super

对,我的理解好像是有问题。

根据 use-package 的 README:

When you use the :commands keyword, it creates autoloads for those commands and defers loading of the module until they are used.

=> 所以 :commands (hydra-media-player/body) 使得:当 hydra-media-player/body 被调用时,加载 emms 这个 module

Similarly, :config can be used to execute code after a package is loaded. In cases where loading is done lazily (see more about autoloading below), this execution is deferred until after the autoload occurs

=> 所以当 emms 加载完后,会运行 config 里面的内容


合起来,我的理解是:当 hydra-media-player/body 被调用 → 加载 emms 这个 module → 运行 config 里面的内容

而 config 里面的内容会使得 emms-play-directory-tree 变成可用的

  (require 'emms-setup)
  (emms-standard)
  (emms-default-players)

但不知道这个理解是在哪错了。

嗯,确实是这个原因

你的意思是类似于这样吗?

(defhydra hydra-wenshan-emacs ()
  "Hydra"
  ("m" (lambda ()
         (interactive)
         (require 'emms-setup)
         (emms-standard)
         (emms-default-players)
         (hydra-media-player/body)) "media player" :exit t)
  ("p" hydra-projectile/body "project interaction" :exit t)
  ("s" aweshell-new "eshell" :exit t)
  ("c" compile "compile" :exit t)
  ("r" recompile "re-compile" :exit t)
  ("q" nil "quit"))

这样确实可以,但会导致每次按 m 都运行一遍 (require 'emms-setup)... 那段代码

嗯,这样做是没问题的,我的问题似乎是个 use-package 有关,即 hydra-media-player/body 被调用时,emms-setup 那段代码没有被运行,从而导致 emms-play… 这些 commands 没有加载

挺好的,多谢周冲兄关心啊,你可还好?

澳洲也慢慢 lock down 了,最近都是在家工作。

这边还行,现在除了餐饮业之外,其他行业都渐渐恢复了