文件尽量不改名,如果需要改名在文件头部声明。
使用 id 链接有什么劣势吗?
有个功能可以隐藏properties这些,看不习惯你可以操作一下。我在org-roam 的论坛看到的。
Org-roam 已经支持在 Emacs 29 中使用内置的 sqlite,需要安装 emacsql-sqlite-builtin ,这个包也可以通过 melpa安装 并设置:
(use-package org-roam
:custom
(org-roam-database-connector 'sqlite-builtin)
)
使用内置 sqlite 的好处:启动 org-roam 速度更快了,首次加载都是秒开的感觉,使用过程也更加顺滑了😄。
这是我的个人配置
也可以参考这里:
这次更新 org-roam 配置的时候才发现,(setq org-roam-v2-ack t)
这个配置也不需要了。
不用emacs 29, 在以前的版本有办法加速一开始的sqlite的加载吗?
我是在windows上用的, 而且还是org-roam v1.
求教!
不用 Emacs 29 没法用这个选项,因为 sqlite 在 29 才内置支持的。
建议你先升级到 V2,有升级向导。安装好 org-roam 最新版本后,执行 M-x, org-roam-migrate-wizard
会引导你进行数据迁移(建议迁移前先备份好 V1 的数据)。
那还是算了, 我完全不需要v2中的特性, 而且已经习惯v1了.
不过org-roam的内核本来就不大, 我准备自己维护了.
V1 用的习惯的话,确实没必要折腾了。用的舒服才是最重要的
之前v1的时候用过一段时间,但基本都是记流水帐了。现在就只用text mode记,然后grep一顿搜,简单粗暴。我觉得真正想在pieces间建立联系只有写成essay这一条路。。好工具不如烂笔头
其实Zettelkasten方法的核心是写而不是记,建议阅读介绍Zettelkasten方法的书 How to Take Smart Notes
那你可以试试之光的 xeft,我也弃用 roam 了
org-super-links 试一下,直接引用块,也比较方便。但我确实是直接写 essay 来积累知识。因为比较显性、可结构化的,只需要做索引和查资料就行,不需要专门记录和分析。所以我看大部分用 Notion 自建站的博客,都觉得…
嗯 其实很多类似的包我都试过了,包括zk,deft,orgsuperlinks,但用一个包有些时候就不得不得按它的逻辑来适配自己的工作流,最后发现其实很多需求对于干活而言都是虚假的。所以我决定从最朴素的搜索逻辑开始,写一些自己的小函数满足需求,这样才能慢慢地真正掌握emacs,而不是被一些fancy的功能牵着鼻子走。当然我觉得这些包都是极好的学习资源,用一用然后抄几个自己真正用的到的函数。包的强大引入的复杂性和找到自己需要的理解的操作,这是tradeoff得权衡。emacs本身最大的魅力就是帮助你真正理解和操控自己使用的工具
之光的xeft感觉不错啊,谢谢推荐,我试试看。我之前感觉org roam太重,加上自己还 hack了太多东西,一直想换个轻量级的,试用了zk等一堆包,都有一些局限性不够灵活还是没换掉
我感觉super-link更成熟一些,zk最大的优点是desk以及检索方便,但因为过度依赖动态的搜索,无法自动更新文件里面的链接,所以有时候反而误导用户以为当前的文件只有一个链接。我个人很喜欢zk这种轻量级的方法,最终放弃的另一个原因是zk对于org的融合有点割裂,虽然能用org-id作为链接,但zk的做法跟org-id没啥关系,我get-id后还是得手动处理。私以为zk其实是希望统一所有的文本格式都能处理,因此直接放弃了org mode的许多特性转而换用自己的方法
同时,zk这种纯搜索的方法在大量文件的情况下也不如roam或者xeft之类的数据库方法
谢谢分享zk使用经验。zk不能自动更新链接,且不能很好与orgmode融合的话,使用的意义就不是很大了。
org-super-link 我只看了简介。
目前我用org-roam的唯一理由就是双链,以及未来的可扩展性。它的缺点就是总是有点小毛病,虽然作者更新很快,但总给人一种不稳定的感觉。
另,能分享一下你的方法,或者代码吗?谢谢
关于org-roam的代码我大概分为4个部分
- 一些基础设置
- 对于标题链接的显示
- org-roam 和 org-agenda 的结合
- citar 文献操作的结合
其中2是从doom emacs 拿出来的, 3是org- roam论坛一位大佬的方法,4是org-roam作者分享他记录文献的流程,我从中学了不少我能用的方法
我很粗略的写了一点代码是干啥的,因为大部分都是我重组或者抄的,如果你感兴趣,建议去找找原代码出处,我有空可能会写一篇详细的工作流例如录一些动图来介绍这些方法。
- 首先是基础的设置,这里感谢论坛大佬的提醒,我也使用了built-in 的sqlite
(setq org-roam-database-connector 'sqlite-builtin
org-roam-mode-section-functions (list #'org-roam-backlinks-section
#'org-roam-reflinks-section
;; #'org-roam-unlinked-references-section
)
org-roam-directory "~/Documents/emacs/orgmode/roam/"
org-roam-dailies-directory "~/Documents/emacs/orgmode/roam"
org-roam-db-gc-threshold most-positive-fixnum)
;; 使用侧边栏而不是完整buffer
(add-to-list 'display-buffer-alist
'("\\*org-roam\\*"
(display-buffer-in-side-window)
(side . right)
(slot . 0)
(window-width . 0.25)
(window-parameters . ((no-other-window . t)
(no-delete-other-windows . t)))))
- 标题链接,org-roam默认会把所有的标题链接和文件链接视为同一级,这样有时会因此重复索引或者不直观的特点,我从doom里面抄了一堆代码, 效果如第四个node那样(2021-0815是文件一级,而具体的标题一级是后面的链接)
代码
;; Codes blow are used to general a hierachy for title nodes that under a file
(cl-defmethod org-roam-node-doom-filetitle ((node org-roam-node))
"Return the value of \"#+title:\" (if any) from file that NODE resides in.
If there's no file-level title in the file, return empty string."
(or (if (= (org-roam-node-level node) 0)
(org-roam-node-title node)
(org-roam-get-keyword "TITLE" (org-roam-node-file node)))
""))
(cl-defmethod org-roam-node-doom-hierarchy ((node org-roam-node))
"Return hierarchy for NODE, constructed of its file title, OLP and direct title.
If some elements are missing, they will be stripped out."
(let ((title (org-roam-node-title node))
(olp (org-roam-node-olp node))
(level (org-roam-node-level node))
(filetitle (org-roam-node-doom-filetitle node))
(separator (propertize " > " 'face 'shadow)))
(cl-case level
;; node is a top-level file
(0 filetitle)
;; node is a level 1 heading
(1 (concat (propertize filetitle 'face '(shadow italic))
separator title))
;; node is a heading with an arbitrary outline path
(t (concat (propertize filetitle 'face '(shadow italic))
separator (propertize (string-join olp " > ") 'face '(shadow italic))
separator title)))))
(setq org-roam-node-display-template (concat "${type:15} ${doom-hierarchy:80} " (propertize "${tags:*}" 'face 'org-tag)))
- org-roam agenda结合 这些代码的效果是,会对所有有TODO的文件动态添加到agenda列表。因为是用了org-roam的数据库,所以速度很快。我没细看代码,大概的原理是,当用户添加TODO的时候,会自动给文件打一个project的tag,如果改文件所有TODO都没有了,则益处project的tag,然后会用数据库读取包含project tag的文件添加到agenda文件列表
如果你打算使用这个功能,建议去关注org-roam论坛中该代码的作者,他在长期维护(从org-roam v1 一直维护到现在的v2)
(defun vulpea-project-p ()
"Return non-nil if current buffer has any todo entry.
TODO entries marked as done are ignored, meaning the this
function returns nil if current buffer contains only completed
tasks."
(seq-find ; (3)
(lambda (type)
(eq type 'todo))
(org-element-map ; (2)
(org-element-parse-buffer 'headline) ; (1)
'headline
(lambda (h)
(org-element-property :todo-type h)))))
(defun vulpea-project-update-tag ()
"Update PROJECT tag in the current buffer."
(when (and (not (active-minibuffer-window))
(vulpea-buffer-p))
(save-excursion
(goto-char (point-min))
(let* ((tags (vulpea-buffer-tags-get))
(original-tags tags))
(if (vulpea-project-p)
(setq tags (cons "project" tags))
(setq tags (remove "project" tags)))
;; cleanup duplicates
(setq tags (seq-uniq tags))
;; update tags if changed
(when (or (seq-difference tags original-tags)
(seq-difference original-tags tags))
(apply #'vulpea-buffer-tags-set tags))))))
(defun vulpea-buffer-p ()
"Return non-nil if the currently visited buffer is a note."
(and buffer-file-name
(string-prefix-p
(expand-file-name (file-name-as-directory org-roam-directory))
(file-name-directory buffer-file-name))))
(defun vulpea-project-files ()
"Return a list of note files containing 'project' tag." ;
(seq-uniq
(seq-map
#'car
(org-roam-db-query
[:select [nodes:file]
:from tags
:left-join nodes
:on (= tags:node-id nodes:id)
:where (like tag (quote "%\"project\"%"))]))))
(defun vulpea-agenda-files-update (&rest _)
"Update the value of `org-agenda-files'."
(setq org-agenda-files (vulpea-project-files)))
(add-hook 'find-file-hook #'vulpea-project-update-tag)
(add-hook 'before-save-hook #'vulpea-project-update-tag)
(advice-add 'org-agenda :before #'vulpea-agenda-files-update)
(advice-add 'org-todo-list :before #'vulpea-agenda-files-update)
;; functions borrowed from `vulpea' library
;; https://github.com/d12frosted/vulpea/blob/6a735c34f1f64e1f70da77989e9ce8da7864e5ff/vulpea-buffer.el
(defun vulpea-buffer-tags-get ()
"Return filetags value in current buffer."
(vulpea-buffer-prop-get-list "filetags" "[ :]"))
(defun vulpea-buffer-tags-set (&rest tags)
"Set TAGS in current buffer.
If filetags value is already set, replace it."
(if tags
(vulpea-buffer-prop-set
"filetags" (concat ":" (string-join tags ":") ":"))
(vulpea-buffer-prop-remove "filetags")))
(defun vulpea-buffer-tags-add (tag)
"Add a TAG to filetags in current buffer."
(let* ((tags (vulpea-buffer-tags-get))
(tags (append tags (list tag))))
(apply #'vulpea-buffer-tags-set tags)))
(defun vulpea-buffer-tags-remove (tag)
"Remove a TAG from filetags in current buffer."
(let* ((tags (vulpea-buffer-tags-get))l
(tags (delete tag tags)))
(apply #'vulpea-buffer-tags-set tags)))
(defun vulpea-buffer-prop-set (name value)
"Set a file property called NAME to VALUE in buffer file.
If the property is already set, replace its value."
(setq name (downcase name))
(org-with-point-at 1
(let ((case-fold-search t))
(if (re-search-forward (concat "^#\\+" name ":\\(.*\\)")
(point-max) t)
(replace-match (concat "#+" name ": " value) 'fixedcase)
(while (and (not (eobp))
(looking-at "^[#:]"))
(if (save-excursion (end-of-line) (eobp))
(progn
(end-of-line)
(insert "\n"))
(forward-line)
(beginning-of-line)))
(insert "#+" name ": " value "\n")))))
(defun vulpea-buffer-prop-set-list (name values &optional separators)
"Set a file property called NAME to VALUES in current buffer.
VALUES are quoted and combined into single string using
`combine-and-quote-strings'.
If SEPARATORS is non-nil, it should be a regular expression
matching text that separates, but is not part of, the substrings.
If nil it defaults to `split-string-default-separators', normally
\"[ \f\t\n\r\v]+\", and OMIT-NULLS is forced to t.
If the property is already set, replace its value."
(vulpea-buffer-prop-set
name (combine-and-quote-strings values separators)))
(defun vulpea-buffer-prop-get (name)
"Get a buffer property called NAME as a string."
(org-with-point-at 1
(when (re-search-forward (concat "^#\\+" name ": \\(.*\\)")
(point-max) t)
(buffer-substring-no-properties
(match-beginning 1)
(match-end 1)))))
(defun vulpea-buffer-prop-get-list (name &optional separators)
"Get a buffer property NAME as a list using SEPARATORS.
If SEPARATORS is non-nil, it should be a regular expression
matching text that separates, but is not part of, the substrings.
If nil it defaults to `split-string-default-separators', normally
\"[ \f\t\n\r\v]+\", and OMIT-NULLS is forced to t."
(let ((value (vulpea-buffer-prop-get name)))
(when (and value (not (string-empty-p value)))
(split-string-and-unquote value separators))))
(defun vulpea-buffer-prop-remove (name)
"Remove a buffer property called NAME."
(org-with-point-at 1
(when (re-search-forward (concat "\\(^#\\+" name ":.*\n?\\)")
(point-max) t)
(replace-match ""))))
- 对于 citar文献的融合
鉴于这里是一种方法而不是形式,我建议你搜一下 org-roam作者写的那一篇关于他如何记录笔记和科研的方法,理解思路然后再阅读代码
首先是 citar部分,可以从bib文件一键生成node和索引,包括文献名和文献文件的链接。
(setup citar
(:option org-cite-global-bibliography '("~/Documents/emacs/orgmode/bibliography/better_zotero_bib.bib")
org-cite-insert-processor 'citar
org-cite-follow-processor 'citar
org-cite-activate-processor 'citar
citar-bibliography org-cite-global-bibliography))
;; borrowed from https://jethrokuan.github.io/org-roam-guide/ as a method for insert notes for reference
(defun lewis/org-roam-node-from-cite (keys-entries)
(interactive (list (citar-select-ref :multiple nil :rebuild-cache t)))
(let ((title (citar--format-entry-no-widths (cdr keys-entries)
"${author editor}::${title}")))
(org-roam-capture- :templates
'(("r" "reference" plain "%?" :if-new
(file+head "reference/${citekey}.org"
":PROPERTIES:
:ROAM_REFS: [cite:@${citekey}]
:END:
,#+title: ${title}\n* Action notes\n* Idea notes\n* Sealed notes")
:unnarrowed t))
:info (list :citekey (car keys-entries))
:node (org-roam-node-create :title title)
:props '(:finalize find-file))))
然后是添加了新的一列,你可以简单理解为独立的一组tag,这组tag是根据文件路径显示的
(cl-defmethod org-roam-node-type ((node org-roam-node))
"Return the TYPE of NODE."
(condition-case nil
(file-name-nondirectory
(directory-file-name
(file-name-directory
(file-relative-name (org-roam-node-file node) org-roam-directory))))
(error "")))
效果就是前面的reference或者article分类。
上面的代码可能看起来有点乱,我今晚也在忙别的写的仓促。你可以在我的配置里 GitHub - nowislewis/nowisemacs: A full-blown emacs configuration framework with easy abstraction 找到更为系统的组织形式
我是用org-mode管理所有的配置,都在里面的一个 init.org里。说实话因为转了borg管理包,你不一定能在init.org看到所有的包,有疑问欢迎提出,我入门的时受过很多人的帮助,也很乐意分享任何我能有的一点知识