关于 embark ,大家有什么小技巧或者 hack 吗?

embark 挺长时间了,深感其对效率提升之大。因此想深入研究一下,看看它有什么奇技淫巧,但是发现除了 FIFTEEN WAYS TO USE EMBARK ,论坛关于 embark 的讨论并不是很多,因此开帖来请教一下大家。

抛砖引玉,我先来分享一下我的一些配置:

  1. consult-org-heading
  (defun consult-heading-insert-backlink (target)
    (let* ((marker (plist-get
                    (text-properties-at 0 target)
                    'consult--candidate))
           (headline-name (substring (org-no-properties target)
                                     0 -1))
           (headline-id (save-excursion
                          (with-current-buffer
                              (marker-buffer marker)
                            (goto-char marker)
                            (org-id-get-create)))))
      (org-insert-link
	   nil (concat "id:" headline-id) headline-name)))

  (embark-define-keymap embark-org-heading-map
    "Keymap for Embark heading actions."
    ("i" embark-insert)
    ("b" consult-heading-insert-backlink)
    ("w" embark-copy-as-kill)
    ("q" embark-toggle-quit)
    ("E" embark-export)
    ("S" embark-collect)
    ("L" embark-live)
    ("B" embark-become)
    ("A" embark-act-all)
    ("C-s" embark-isearch)
    ("SPC" mark)
    ("DEL" delete-region))

  (add-to-list 'embark-keymap-alist '(consult-org-heading . embark-org-heading-map))

主要增加了"快速插入 headline 链接" 的功能, 效果如下

ezgif.com-gif-maker

  1. org-roam support
 (defun org-roam-backlinks-query* (NODE)
  "Gets the backlinks of NODE with `org-roam-db-query'."
  (org-roam-db-query
   [:select [source dest]
		    :from links
		    :where (= dest $s1)
		    :and (= type "id")]
   (org-roam-node-id NODE)))

(defun org-roam-backlinks-p (SOURCE NODE)
  "Predicate function that checks if NODE is a backlink of SOURCE."
  (let* ((source-id (org-roam-node-id SOURCE))
	     (backlinks (org-roam-backlinks-query* SOURCE))
	     (id (org-roam-node-id NODE))
	     (id-list (list id source-id)))
    (member id-list backlinks)))

(defun org-roam-backlinks--read-node-backlinks (source)
  "Runs `org-roam-node-read' on the backlinks of SOURCE.
 The predicate used as `org-roam-node-read''s filter-fn is
 `org-roam-backlinks-p'."
  (org-roam-node-read nil (apply-partially #'org-roam-backlinks-p source)))

(defun org-roam-backlinks-node-read (entry)
  "Read a NODE and run `org-roam-backlinks--read-node-backlinks'."
  (let* ((node (get-text-property 0 'node entry))
         (backlink (org-roam-backlinks--read-node-backlinks node)))
    (find-file (org-roam-node-file backlink))))

(embark-define-keymap embark-org-roam-map
  "Keymap for Embark org roam actions."
  ("i" org-roam-node-insert)
  ("s" embark-collect)
  ("b" eli-org-roam-backlinks-node-read))
  
  (add-to-list 'embark-keymap-alist '(org-roam-node . embark-org-roam-map))

org-roam-node-find 界面,可以使用 embark 来查看所选节点的反向链接节点和插入此节点,也可以批量操作节点。和节点有关操作用统一入口 org-roam-node-find 就能解决,减少记忆和快捷键负担。

edit: 根据reddit 的一篇帖子优化了 embark 代码

不知道大家还有什么小技巧或者 hack ?

18 个赞

插入图片我一般有两种方式:一是用 org-download 截图,二是从本地目录选择插入。由于我的本地图片文件名都是日期格式,所以有一个不方便的地方就是不好确定目标图片,所以结合 embark 和 posframe 做了个图片预览:

(require 'posframe)
(require 'embark)
(defun eli-image-preview (&rest _args)
  (let* ((target (embark--targets))
         (file-path (plist-get (car target) :target))
         (name (file-name-nondirectory file-path))
         (mode (assoc-default name auto-mode-alist #'string-match)))
    (posframe-hide-all)
    (when (memq mode '(image-mode))
      (with-current-buffer (get-buffer-create "*image*")
        (setq inhibit-read-only t)
        (erase-buffer)
        (insert-file-contents file-path)
        (set-auto-mode-0 mode))
      (when (posframe-workable-p)
        (posframe-show "*image*"
                       :poshandler #'posframe-poshandler-frame-center)))))

(defun eli-select-images ()
  (interactive)
  (let ((default-directory "/path-to-images/"))
    (call-interactively 'find-file)))

(advice-add 'eli-select-images
            :before (lambda ()
                      (add-hook 'post-command-hook #'eli-image-preview)))

(add-hook 'minibuffer-exit-hook
          (lambda ()
            (remove-hook 'post-command-hook #'eli-image-preview)
            (posframe-delete-all)))

这个函数还可以扩展,变成预览所有类型的文件。

效果如下: Peek 2022-09-29 10-44

18 个赞

好实用的功能,点赞

  (add-to-list 'embark-keymap-alist '(consult-org-heading . embark-org-heading-map))

这个对 consult-org-heading 的确起作用了, 但为啥对下面这个就完全不起作用呢? 执行 consult-outline 时 embark 菜单里还是没有 b 这个快捷键?

  (add-to-list 'embark-keymap-alist '(consult-outline . embark-org-heading-map))

embark 的原理是根据光标附近对象的类型来选择对应的操作集,embark-keymap-alist 中的元素是 (对象类型 . 对象类型对应的操作集), 而不是特定的命令。比如说在 buffer 里面,光标下是 link ,那就去 embark-keymap-alist 里面找有没有对应的操作,如果没有就用默认的。(当然这是最基本逻辑,还有数据传递之类的细节)

而在 minibuffer 里面,candidates 的类型是由 metadata 里的 category(Programmed Completion )设置的,在 consult 里面,由 consult--read 中的 CATEGORY参数来设置。

consult-outline:category 是默认的 consult-locationembark-keymap-alist 中找不到就会用默认的操作集。

哦哦, 可是我执行了下面这一行以后执行 consult-outline, embark 菜单中还是找不到 embark-org-heading-map 定义的动作, 这是为啥呢?

(add-to-list 'embark-keymap-alist '(consult-location . embark-org-heading-map))

因为它被 override 了,见变量 embark-default-action-overrides

((imenu . consult-imenu)
 (consult-info . embark-consult-info)
 (consult-man . embark-consult-man)
 ((file . consult-locate)
  . find-file)
 ((file . consult-find)
  . find-file)
 (consult-grep . embark-consult-goto-grep)
 (consult-location . embark-consult-goto-location))

当然 embark-org-heading-map 里的函数可能不适用于 consult-location 的对象,因为你平时用 consult-line 之类的操作,其候选的对象也是这个,所以要注意不在 org-mode 中,不是 headline 等情况,还是挺复杂的,总之不推荐修改

感谢, 可是,

(setq embark-default-action-overrides (assoc-delete-all 'consult-location embark-default-action-overrides))

我都从中去除了 consult-location, 还是不行 :sob:

我不知道你的具体配置,但是我这里是可以的,建议给个最小可复现配置

  (defun consult-outline-insert-heading (target)
    (let* ((marker (plist-get
                    (text-properties-at 0 target)
                    'consult--candidate))
           (headline-name (substring (org-no-properties target)
                                     2 nil)))
      (org-insert-link nil headline-name)))

  (with-eval-after-load 'embark
    (define-key embark-general-map (kbd "h") #'consult-outline-insert-heading))

我这样设置的,也可以用。

谢谢您~ 时隔几个月终于破案了, 原因是我用的最新版的 embark, 在 embark-org.el 中已经定义了 embark-org-heading-map, 而我也用 defvar-keymap 定义, 在已经存在的情况下就是不起作用的, 我把 embark-org-heading-map 重命名为 my/embark-org-heading-map 后, 问题解决.

1 个赞

最近也在玩 embark, 想请教个用法,我现在有一堆 pod 名字,现在我想检查每个pod的日志里是否出现了,error 日志,如果出现了则输出pod的名字。我现在通过 embark-collect 收集到了一个buffer。

变成了这个样子,那么我如何提取 pod 名字,并执行接下来的操作呢?

我想问, 如何快速确定一个命令的 :category 呢? 就比如 consult-org-heading 这个例子还好, 我在 consult 包里找到它的定义即可, 但比如随便一个命令, 比如 cd 命令, 我怎么能知道它的 category 是什么呢? 我在意这个问题是我想要对 cd 命令添加动作, 就是把某个变量设定为 cd 的补全结果.

哦, 没事, 解决了, marginalia 包的 marginalia-cycle 能显示出这个信息.

可以贴出您更新后的配置参考一下吗?我重命名了之后,还是无法插入link

; 根据 target 类型返回 target 对应的 marker
(defun my/embark-get-marker(target)
       (let* ((embark-plist (text-properties-at 0 target)) (embark-type (car embark-plist)))
         (cond
          ((eq embark-type 'consult-org--heading) (plist-get embark-plist 'consult--candidate))
          ((eq embark-type 'consult--outline-level) (car (plist-get embark-plist 'consult-location)))
         )
                                     ))
; 根据 target 类型返回 target 对应的 heading
(defun my/embark-headname(target)
    (let* ((embark-plist (text-properties-at 0 target)) (embark-type (car embark-plist)))
    (cond
     ((eq embark-type 'consult-org--heading) (substring (org-no-properties target)
                                                        0 -1))
     ((eq embark-type 'consult--outline-level) (replace-regexp-in-string "^\*+ " "" (org-no-properties target)) )
     )
    )
  )
(defun embark-heading-insert-link (target)
  (let* ((marker (my/embark-get-marker target))
         (headline-name (my/embark-headname target))
           (headline-id (save-excursion
                            (marker-buffer marker)
                          (goto-char marker)
                          (org-id-get-create))))
      (org-insert-link
	   nil (concat "id:" headline-id) headline-name)))
(defvar-keymap my/embark-org-heading-map
  :doc "Keymap for Embark heading actions."
  :parent embark-org-heading-map
"l" 'embark-heading-insert-link
  )

(add-to-list 'embark-keymap-alist '(consult-org-heading . my/embark-org-heading-map))

我删了一些与链接无关的行, 可能会影响运行, 意思就是这个意思

1 个赞

感谢回复,用您的配置,可以正常使用了。