重新定义general.el(无意义造轮第二弹)

用上home brew的use-package和genereal,我的内心终于平静了。现在配置的核心工具,包加载,包管理和按键绑定都不再需要下载外来包实现,感觉配置完整了不少(独立自主?)。

这回的luna-def-key我加入了些自己的想法,不完全是低配版general-define-key。首先像:keymaps:prefix这些修饰不再是修饰整个general-define-key,而只修饰他们之后的按键定义,例子:

general-define-key需要三个form:

(general-define-key
 :keymaps 'map1
 "m" #'fn1)
(general-define-key
 :keymaps 'map2
 "m" #'fn2)
(general-define-key
 :prefix "C-x"
 "h" #'fn3)

luna-def-key可以全都写在一起:

(luna-def-key
 :keymaps 'map1
 :prefix "C-c"
 "m" #'fn1
 :keymaps 'map2
 "m" #'fn2
 :keymaps nil
 :prefix "C-x"
 "h" #'fn3)

可能没什么大不了,但是我感觉挺有趣的,我也喜欢能把按键定义写在一起。

其次,general.el的definer功能我用preset功能代替:

general.el:

(general-create-definer my-definer
  :prefix "C-x"
  :keymaps 'override)
(my-definer
 "m" #'fn1)

luna-key.el:

(luna-key-def-preset :leader
  :prefix "C-x"
  :keymaps 'override)
(luna-def-key
 :leader
 "m" #'fn1)

preset就像一个简易macro,展开成定义的那些修饰。总之效果就是一个luan-def-key搞定一切。


完整实现只要一百多行,对比general.el 2700+行:

(require 'pcase)

(defvar luna-key-preset-alist nil
  "Stores defined presets.")

(defvar luna-key-override-mode-map (make-sparse-keymap)
  "One map to rule them all.")

(defvar luna-key-override-map-alist
  `(luna-key-override-mode . ,luna-key-override-mode-map)
  "Alist put into `emulation-mode-map-alists' to override all other maps.")

(defvar luna-key-postponed-alist nil
  "An alist of (map . ((key def) (key def))).
When we define a key in a map that’s not defined yet, we push the
definition to this alist. When a new file is loaded, we check for
each map and see if we can now define the bindings for that map.")

(defun luna-key-define-postponed-binding (_)
  "Define postponed bindings in ‘luna-key-postponed-alist’."
  (dolist (map (mapcar #'car luna-key-postponed-alist))
    (when (boundp map)
      (pcase-dolist (`(,key ,def)
                     (alist-get map luna-key-postponed-alist))
        (define-key (symbol-value map) key def))
      (setf (alist-get map luna-key-postponed-alist) nil))))

(define-minor-mode luna-key-override-mode
  "Minor mode used to activate our override keymap."
  :global t
  :lighter ""
  :keymap 'luna-key-override-mode-map)

(defun luna-key-def-preset (preset &rest args)
  "Define PRESET as ARGS.

ARGS can be anything valid in `luna-def-key'.

If you define :leader as

    (luna-key-def-preset :leader
     :keymaps 'override
     :prefix \"C-SPC\")

Then

    (luna-def-key
     :leader
     KEY DEF)

is equivalent to

    (luna-def-key
     :keymaps 'override
     :prefix \"C-SPC\"
     KEY DEF)"
  (declare (indent 1))
  (setf (alist-get preset luna-key-preset-alist) args))

(defun luna-key-normalize (prefix key)
  "Normalize KEY and PREFIX with `kbd' and combine them.
However, if KEY is [remap ...] or [t], don’t prepend PREFIX to it."
  ;; Normalize KEY and PREFIX with `kbd'.
  (if (stringp key) (setq key (kbd key)))
  (if (and prefix (stringp prefix)) (setq prefix (kbd prefix)))
  ;; Result of ‘kbd’ can be either string or vector,
  ;; now we normalize KEY and PREFIX to vectors.
  (if (stringp key) (setq key (string-to-vector key)))
  (if (and prefix (stringp prefix))
      (setq prefix (string-to-vector prefix)))
  ;; When do we simply return KEY without PREFIX: either PREFIX is
  ;; nil, or KEY has special meaning ([remap ...] or [t]).
  (if (or (not prefix) (and (vectorp key) (memq (aref key 0) '(t remap))))
      key
    (vconcat prefix key)))

(defun luna-key-define (key def map-list prefix)
  "Define KEY to DEF.
Define KEY in all the maps in MAP-LIST, using PREFIX as prefix. 
MAP-LIST and PREFIX can be nil.
If MAP-LIST is nil, only define in `global-map'."
  (let ((map-list (or map-list (list 'global-map)))
        (key (luna-key-normalize prefix key))
        ;; If DEF is (STRING . DEFN), we use STRING as it’s description.
        (desc (car-safe def)))
    (when desc
      (with-eval-after-load 'which-key
        (which-key-add-key-based-replacements
          (key-description key) desc)))
    (dolist (map map-list)
      (let ((map (if (eq map 'override) 'luna-key-override-mode-map map)))
        (if (boundp map)
            (define-key (symbol-value map) key def)
          (push (list key def)
                (alist-get map luna-key-postponed-alist))
          (add-hook 'after-load-functions
                    #'luna-key-define-postponed-binding))))))

(defun luna-def-key (&rest args)
  "Define key.

The :keymaps and :prefix modifiers specifies the keymaps and
prefix key for KEY DEF pairs below them. Modifiers only affect
the definitions after them, and their effect lasts until another
modifier overrides them. For example, to define KEY1 in MAP1 and
KEY2 in MAP2:

  (luna-def-key
   :keymaps 'MAP1
   KEY1 DEF1
   :keymaps 'MAP2n
   KEY2 DEF2)

Unlike in `define-key', MAP is the symbol of a keymap, rather than
the keymap itself. MAP can also be nil, which is interpreted as
`global-map', or 'override, which is interpreted as the override
keymap defined by luna-key, or a list of these three forms. KEY
and DEF can be anything that `define-key' accepts. `kbd' is
automatically added to KEY but not DEF.

You can also use preset modifiers defined by `luna-key-def-preset'.
They are basically macros that expand to other modifiers.

Use :clear to reset all modifier effects. :--- is an alias for :clear.

ARGS.

\(fn [:keymaps MAPS] [:prefix PREFIX] [:clear] KEY DEF ...)"
  (luna-key-override-mode)
  (condition-case nil
      (let (arg map-list prefix)
        (while args
          (setq arg (pop args))
          (pcase arg
            (:keymaps
             (let ((map (pop args)))
               (cond ((symbolp map) (setq map-list (list map)))
                     ((proper-list-p map) (setq map-list map))
                     (t (error "Invalid argument for :keymaps command: %s"
                               map)))))
            (:prefix
             (setq prefix (pop args)))
            ((or :clear :---) (setq prefix nil
                                    map-list nil))
            ((pred keywordp)
             (when-let ((preset (alist-get arg luna-key-preset-alist)))
               (setq args (append preset args))))
            (_ (let ((key arg)
                     (def (pop args)))
                 (luna-key-define key def map-list prefix))))))
    (setting-constant (error "Not enough arguments"))))
9 个赞

lisper 人人如龙。。。

3 个赞

不如说生命不息折腾不止 :joy:

哇!膜了!不过这儿下面的那个 luna-def-key 是怎么知道是 C-x 的那个的呢?是通过 :leader / :leader2 这样来确定的嘛?

(luna-key-def-preset :leader
  :prefix "C-x"
  :keymaps 'override)
(luna-def-key
 :leader
 "m" #'fn1)
(luna-def-key
 :leader
 "m" #'fn1)
;; 就等于
(luna-def-key
 :prefix "C-x"
 :keymaps 'override
 "m" #'fn1)

没太明白你具体问的是啥,不过preset的实现很直接: 首先luna-key-def-preset就是单纯把定义的修饰扔到一个alist里:

(defun luna-key-def-preset (preset &rest args)
  (setf (alist-get preset luna-key-preset-alist) args))

然后在luna-def-key里,程序一个接一个地读接受到的参数,如果遇到了preset,就把它里面存的一堆东西扔到待处理的参数列表里。结果上就是把:preset展开成了里面存的东西:

(defun luna-def-key (&rest args)
  ...
  (while args
    (setq arg (pop args))
    ...
    (pcase arg
      ...
      ((pred keywordp)
       (when-let ((preset (alist-get arg luna-key-preset-alist)))
         (setq args (append preset args)))))
    ...
    ))

就是吧。比如我习惯把文件操作的快捷键都放在 f 之下,就定义一个 :file-leader,然后:

(luna-def-key
 :file-leader
 "d" #'duplicate-file)

我也没用 general.el,自己写了一个 kemap!(或许应该起个更好的名字)。

我的侧重点是解决依赖关系:

;; 全局

(keymap!
 :autoload foo
 :prefix "C-c"
 "x"    #'foo-function)
;; 特定 mode

(keymap!
 :autoload foo
 :prefix "C-c"
 :mode abc-mode
 "x"    #'foo-function)
;; 全局 + 特定 mode

(keymap!
 :autoload (foo bar)
 :prefix "C-c"
 "x"    #'foo-function
 (:mode abc-mode
  "x"    #'bar-function))
1 个赞

好w

紫薯布丁

很有趣,我绑定按键和加载包的配置完全分开,autoload也是在加载包的地方设置的。每个配置文件大概是这么个结构:

;;; Key

(luna-def-key
;; 所有包的按键绑定
...)

;;; Packages

(load-package
...)

(load-package
...)

对了,有 完整的函数/宏/包 嘛?

想抄!

什么叫抄,叫复用,复用不是抄,程序员的事能叫抄吗?

现在用着general.el。虽然我没看懂,但是还是要点赞。

好w 复用w

自主可控 XD

2 个赞

能不能分享你的map!学习一下

1 个赞