- 1 前言
- 2 Org 文件里的链接和反链改造
- 2.1 背景
- 2.2 org-super-links
- 2.3 org-super-links 的不足
- 2.4 改造 org-super-links 插件
- 2.5 最终效果
- 2.6 最终生成的代码
- 3 结语
1 前言 🔗
在大模型时代,一切皆有可能。对于 Emacs 同样如此,大模型拉低学习了 Emacs 的门槛,我们通过大模型可以做到以前我们敢想不敢做,或敢做没时间做,或有时间不会做的事。
今天继续这个新的系列,跟大家分享在大模型时代,我们可以如何“玩” Emacs,我们应该怎么用大模型来满足自己对 Emacs 使用的需求。
2 Org 文件里的链接和反链改造 🔗
2.1 背景 🔗
我通过 Org mode 和 Denote 包来构筑我的知识体系。Org mode 有一个很好的功能就是链接,他可以让你在某一个 Org 文件内,增加链接到另外一个文件的某个标题行,这样能让知识链接成网,长期坚持,好处多多。
Org mode 自带一个 org-store-link
命令,可以将当前位置记录下来,然后通过 org-insert-link
命令插入到其他的 Org 文件:
![](/ox-hugo/gif_20240608_210018.gif)
但这个功能有一个不够完善的地方,就是它的链接是单向的,我们从上面那个例子可以看到在文档的最末,链接到了 第二级
这个标题行,但是在 第二级
标题行,我们并不知道这个标题行被链接到哪些地方。
2.2 org-super-links 🔗
org-super-links 就是用来完善这个需求的。有了这个插件,我们可以通过 org-super-links-store-link
来存储某个位置,然后通过 org-super-links-insert-link
来插入链接:
![](/ox-hugo/gif_20240610_202750.gif)
2.3 org-super-links 的不足 🔗
通过 org-super-links
插件确实能够在我们插入某个链接时,在源文件的标题行以 drawer
来显示反链信息。
![](/ox-hugo/img_20240610_203327.png)
对我来说,如果能在插入的地方所在的标题行也以 drawer
的方式显示插入链接信息,那就更加完美了,相当于在插入和被插入的标题行,都在标题行下以 drawer
的方式来记录链接信息,我们无论在哪,都能很方便的看到彼此的双向信息。这就是本次实验的初衷。
2.4 改造 org-super-links 插件 🔗
这一次的改造,跟之前几次不同,我们换了一种方式,我们将 org-super-links
插件的主体源代码全部 copy 复制到 ChatGPT 中,得益于 GPT-4o 强大的窗口 token 数,整个插件的源代码都可以被 ChatGPT 阅读,并基于它的理解,我们进行相关的改造。
![](/ox-hugo/img_20240610_204030.png)
接着,就要逐步地清晰地描述自己的需求:
![](/ox-hugo/img_20240610_204115.png)
接着是不断的测试,并将错误情况告诉 ChatGPT,让它不断的调整代码,以及加入这段最重要的需求描述:
![](/ox-hugo/img_20240610_204355.png)
2.5 最终效果 🔗
- 通过
org-super-links-store-link
来存储源文件的标题行位置 - 在目标位置,通过
org-super-links-insert-link
来插入- 当目标位置在一个标题行上,以
drawer
的方式来记录链接信息,同时在源文件标题行以drawer
方式记录反链信息 - 当目标位置不在标题行上,即在正文中,那不仅仅以
drawer
的方式来记录链接信息,同时在光标处插入该链接信息,并且在源文件标题行以drawer
方式记录反链信息
- 当目标位置在一个标题行上,以
- 这样,所有的链接和反链信息都可以在源文件标题行和目的文件标题行的
drawer
清晰的看到,并且不妨碍在正文中的链接
![](/ox-hugo/gif_20240610_204612.gif)
2.6 最终生成的代码 🔗
;; 为了实现在存储位置时带上文件和标题行的信息,进行了如下的魔改
(defun org-super-links-store-link (&optional GOTO KEYS)
"Store a point to the register for use in function `org-super-links-insert-link'.
This is primarily intended to be called before `org-capture', but
could possibly even be used to replace `org-store-link' IF
function `org-super-links-insert-link' is used to replace `org-insert-link'. This
has not been thoroughly tested outside of links to/form org files.
GOTO and KEYS are unused."
(interactive "P")
(ignore GOTO)
(ignore KEYS)
(save-excursion
(let ((c1 (make-marker))
(headline (org-get-heading t t t t))) ;; 获取标题行
(set-marker c1 (point) (current-buffer))
(set-register ?^ (cons c1 headline)) ;; 存储标记和标题行
(message "Link copied with headline: %s" headline))))
(defun org-super-links-insert-link ()
"Insert a super link from the register."
(interactive)
(let* ((target (get-register ?^)))
(if target
(let ((marker (car target))
(headline (cdr target)))
(org-super-links--insert-link marker headline)
(set-register ?^ nil))
(message "No link to insert!"))))
(defun org-super-links--insert-link (target headline &optional no-forward)
"Insert link to marker TARGET at current `point`, and create backlink to here.
Include HEADLINE in the link.
If NO-FORWARD is non-nil skip creating the forward link. Currently only used when converting a link."
(let* ((source (point-marker))
(source-headline (org-get-heading t t t t))
(source-link (org-super-links-links-action source 'org-super-links-pre-link-hook))
(target-link (org-super-links-links-action target 'org-super-links-pre-backlink-hook))
(source-formatted-link (org-super-links-link-builder source-link))
(target-formatted-link (org-super-links-link-builder target-link))
(source-file (file-relative-name (buffer-file-name (marker-buffer source))
(file-name-directory (buffer-file-name (marker-buffer target)))))
(target-file (buffer-file-name (marker-buffer target)))
(source-link (format "file:%s::%s" source-file source-headline))
(target-link (format "file:%s::%s" target-file headline)))
(with-current-buffer (marker-buffer target)
(save-excursion
(goto-char (marker-position target))
(when (derived-mode-p 'org-mode)
(org-super-links-insert-backlink source-link source-headline))))
(unless no-forward
(with-current-buffer (marker-buffer source)
(save-excursion
(goto-char (marker-position source))
(if (org-at-heading-p)
(org-super-links-insert-relatedlink target-link headline)
(org-super-links-insert-inline-link target-link headline source-headline)))))))
(defun org-super-links-insert-inline-link (target-link headline source-headline)
"Insert TARGET-LINK with HEADLINE at point and add related drawer at current heading."
;; (insert (org-super-links-link-prefix))
(org-insert-link nil target-link headline)
(insert (org-super-links-link-postfix))
(org-back-to-heading)
(org-super-links-insert-relatedlink target-link headline))
(defun org-super-links-insert-backlink (link desc)
"Insert backlink to LINK with DESC.
Where the backlink is placed is determined by the variable `org-super-links-backlink-into-drawer`."
(let* ((org-log-into-drawer (org-super-links-backlink-into-drawer))
(description (org-super-links-default-description-formatter link desc))
(beg (org-log-beginning t)))
(goto-char beg)
(insert (org-super-links-backlink-prefix))
(insert (org-link-make-string link description))
(insert (org-super-links-backlink-postfix))
(org-indent-region beg (point))))
(defun org-super-links-insert-relatedlink (link desc)
"Insert LINK with DESC into related drawer."
(let* ((org-log-into-drawer (org-super-links-related-into-drawer))
(drawer-name (or org-log-into-drawer "RELATED"))
(beg (org-log-beginning t)))
(goto-char beg)
(unless (org-at-heading-p)
(org-back-to-heading t))
(org-narrow-to-subtree)
(let ((drawer-beg (re-search-forward (format ":%s:" drawer-name) nil t)))
(if drawer-beg
(progn
(goto-char drawer-beg)
(forward-line)
(insert (org-super-links-link-prefix))
(org-insert-link nil link desc)
(insert (org-super-links-link-postfix) "\n")
(org-indent-region drawer-beg (point)))
(goto-char (point-max))
(insert (format ":%s:\n" drawer-name))
(insert (org-super-links-link-prefix))
(org-insert-link nil link desc)
(insert (org-super-links-link-postfix) "\n")
(insert (format ":%s:\n" "END"))
(org-indent-region beg (point))))
(widen)))
(defun org-super-links-related-into-drawer ()
"Name of the related drawer, as a string, or nil.
This is the value of `org-super-links-related-into-drawer`. However, if the
current entry has or inherits a RELATED_INTO_DRAWER property, it will
be used instead of the default value."
(let ((p (org-entry-get nil "RELATED_INTO_DRAWER" 'inherit t)))
(cond ((equal p "nil") nil)
((equal p "t") org-super-links-related-drawer-default-name)
((stringp p) p)
(p org-super-links-related-drawer-default-name)
((stringp org-super-links-related-into-drawer) org-super-links-related-into-drawer)
(org-super-links-related-into-drawer org-super-links-related-drawer-default-name))))
3 结语 🔗
今天这个案例,是另外一种思路,我们将整个插件的源代码通过提示词一起给到大模型,由大模型做整体的理解和记忆,然后基于整体源代码,提出我们的需求、进行调试,最终达到我们的目标。
后续,将会继续分享几个案例。
- org 文件导出为 HTML 的 css 改造
- …
我也会持续的使用 ChatGPT 和 Emacs,持续分享我的心得,感谢您的阅读。