Compare commits

..

5 Commits

Author SHA1 Message Date
Dustin Farris
8a61cf35f8 fix!(capture): move plain capture point past properties drawer
- Fix org-roam-capture--adjust-point-for-capture-type to properly handle
  cases where point is positioned after head content but not at a heading
- Remove redundant location-type logic that incorrectly assumed point > 1
  means we're at a heading (file+head can position point after keywords)
- Restore call to adjust-point-for-capture-type that was removed in ed94524
- Add comprehensive tests for plain capture ordering and assertion error fix
- Refactor adjust-point-for-capture-type for better readability

Amend: ed94524964
2025-09-23 13:45:18 -07:00
Dustin Farris
b7483a1df5 test: add tests for link replacement optimization
Added integration tests for the roam link replacement functionality
to validate the performance optimization that limits the scope of
link replacement by searching specifically for "[[roam:" instead
of any org link bracket pattern. The tests ensure:

1. Special regex characters in buffer content don't break the
   search functionality when using search-forward
2. Only roam: links are processed, not other link types like
   file: or https:

These tests validate the optimization maintains correctness while
improving performance by avoiding unnecessary replacement attempts
on non-roam links.

Ref: fc8638759b
Ref: 89dfaef38b
2025-09-23 07:13:35 -07:00
Dustin Farris
2ac1760620 test: clean up demotable.org
This test had been leaving demotable.org in a modified state which was
getting picked up by git requiring devs to manually reset it before
committing other changes.
2025-09-22 18:24:35 -07:00
Jethro Kuan
ed94524964 (feat!)capture: change id creation to headline on entry-type capture-templates 2025-09-22 18:24:35 -07:00
Liam Hupfer
89dfaef38b Fix link replacement optimization
We are searching for a string literal for speed, so don’t treat it as a
regexp.

Fixes: fc8638759b ("feat: Limit link replacement scope")
Fixes: #2529
2025-07-01 00:28:11 -05:00
17 changed files with 957 additions and 293 deletions

View File

@@ -1,7 +1,6 @@
---
# * docs.yml --- Build the documentation and publish to Github Pages
name: Docs
name: "Docs"
on:
push:
branches:
@@ -10,19 +9,23 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Install deps
run: sudo apt install texinfo
- name: Install deps
run: |
sudo apt-get install texinfo
- name: Build docs
run: make html
- name: Build docs
continue-on-error: false
run: make html
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@releases/v4
with:
token: ${{ secrets.ACCESS_TOKEN }}
branch: gh-pages # The branch the action should deploy to.
folder: doc
clean: true
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to.
FOLDER: doc # The folder the action should deploy.
CLEAN: true

View File

@@ -1,4 +1,7 @@
---
# * test.yml --- Test Emacs packages using makem.sh on GitHub Actions
# https://github.com/alphapapa/makem.sh
# Based on Steve Purcell's examples at
# <https://github.com/purcell/setup-emacs/blob/master/.github/workflows/test.yml>,
# <https://github.com/purcell/package-lint/blob/master/.github/workflows/test.yml>.
@@ -20,7 +23,7 @@
# * Code:
name: CI
name: "CI"
on:
pull_request:
push:
@@ -44,10 +47,10 @@ jobs:
with:
version: ${{ matrix.emacs_version }}
- name: Install Eldev
uses: emacs-eldev/setup-eldev@v1
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install Eldev
run: curl -fsSL https://raw.github.com/org-roam/org-roam/master/github-eldev | sh
- name: Install dependencies
run: make prepare
@@ -57,28 +60,6 @@ jobs:
- name: Test
run: make test
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install deps
run: sudo apt install texinfo
- name: Build docs
run: make html
- name: Upload
uses: actions/upload-artifact@v4
with:
name: Docs
if-no-files-found: error
# TODO: optimize images
path: |
doc/
!doc/images/
# Local Variables:
# eval: (outline-minor-mode)
# End:

View File

@@ -1,5 +1,8 @@
# Changelog
## 2.3.2
- [#2056](https://github.com/org-roam/org-roam/pull/2056) capture: IDs are now created for entries in capture templates
## 2.3.1 (2025-06-26)
* (fix): Use correct type specifications to suppress warnings by @okomestudio in https://github.com/org-roam/org-roam/pull/2522

View File

@@ -1,7 +1,6 @@
.PHONY: clean
clean:
eldev clean all
$(MAKE) -C doc clean
.PHONY: prepare
prepare:
@@ -15,17 +14,16 @@ lint:
test:
eldev -C --unstable -T test
.PHONY: docs
docs:
$(MAKE) -C doc
make -C doc all
.PHONY: html
html:
$(MAKE) -C doc html
make -C doc html-dir
.PHONY: install
install: install-docs
.PHONY: install-docs
install-docs:
$(MAKE) -C doc install
install-docs: docs
make -C doc install-docs
install-info: info
make -C doc install-info

View File

@@ -151,9 +151,12 @@ In both of the cases you will need to ensure that you have all the required
dependencies. These include:
- dash
- f
- s
- org (9.6 is the minimum required version!)
- emacsql
- magit-section
- filenotify-recursive
After installing the package, you will need to properly setup `load-path` to the
package:

107
default.mk Normal file
View File

@@ -0,0 +1,107 @@
TOP := $(dir $(lastword $(MAKEFILE_LIST)))
## User options ######################################################
#
# You can override these settings in "config.mk" or on the command
# line.
#
# You might also want to set LOAD_PATH. If you do, then it must
# contain "-L .".
#
# If you don't do so, then the default is set in the "Load-Path"
# section below. The default assumes that all dependencies are
# installed either at "../<DEPENDENCY>", or when using package.el
# at "ELPA_DIR/<DEPENDENCY>-<HIGHEST-VERSION>".
sharedir ?= $(HOME)/.local/share
lispdir ?= $(sharedir)/emacs/site-lisp/org-roam
infodir ?= $(sharedir)/info
docdir ?= $(sharedir)/doc/org-roam
statsdir ?= $(TOP)/doc/stats
CP ?= install -p -m 644
MKDIR ?= install -p -m 755 -d
RMDIR ?= rm -rf
TAR ?= tar
SED ?= sed
EMACSBIN ?= emacs
BATCH = $(EMACSBIN) -Q --batch $(LOAD_PATH)
INSTALL_INFO ?= $(shell command -v ginstall-info || printf install-info)
MAKEINFO ?= makeinfo
MANUAL_HTML_ARGS ?= --css-ref /assets/page.css
## Files #############################################################
PKG = org-roam
PACKAGES = org-roam
TEXIPAGES = $(addsuffix .texi,$(PACKAGES))
INFOPAGES = $(addsuffix .info,$(PACKAGES))
HTMLFILES = $(addsuffix .html,$(PACKAGES))
HTMLDIRS = $(PACKAGES)
PDFFILES = $(addsuffix .pdf,$(PACKAGES))
EPUBFILES = $(addsuffix .epub,$(PACKAGES))
ELS = org-roam.el
ELS += org-roam-capture.el
ELS += org-roam-compat.el
ELS += org-roam-db.el
ELS += org-roam-mode.el
ELS += org-roam-node.el
ELS += org-roam-utils.el
ELS += extensions/org-roam-dailies.el
ELS += extensions/org-roam-graph.el
ELS += extensions/org-roam-overlay.el
ELS += extensions/org-roam-protocol.el
ELCS = $(ELS:.el=.elc)
ELMS = org-roam.el $(filter-out $(addsuffix .el,$(PACKAGES)),$(ELS))
ELGS = org-roam-autoloads.el org-roam-version.el
## Versions ##########################################################
VERSION ?= $(shell test -e $(TOP).git && git describe --tags --abbrev=0 | cut -c2-)
EMACS_VERSION = 26.1
EMACSOLD := $(shell $(BATCH) --eval \
"(and (version< emacs-version \"$(EMACS_VERSION)\") (princ \"true\"))")
ifeq "$(EMACSOLD)" "true"
$(error At least version $(EMACS_VERSION) of Emacs is required)
endif
## Load-Path #########################################################
ifndef LOAD_PATH
ELPA_DIR ?= $(HOME)/.emacs.d/elpa
SYSTYPE := $(shell $(EMACSBIN) -Q --batch --eval "(princ system-type)")
ifeq ($(SYSTYPE), windows-nt)
CYGPATH := $(shell cygpath --version 2>/dev/null)
endif
LOAD_PATH = -L $(TOP)
# When making changes here, then don't forget to adjust "Makefile",
# ".travis.yml", ".github/ISSUE_TEMPLATE/bug_report.md",
# `magit-emacs-Q-command' and the "Installing from the Git Repository"
# info node accordingly. Also don't forget to "rgrep \b<pkg>\b".
endif # ifndef LOAD_PATH
ifndef ORG_LOAD_PATH
ORG_LOAD_PATH = $(LOAD_PATH)
ORG_LOAD_PATH += -L $(TOP)../ox-texinfo-plus
ORG_LOAD_PATH += -L $(TOP)../org-mode/contrib/lisp
ORG_LOAD_PATH += -L $(TOP)../org-mode/lisp
endif
## Publish ###########################################################
PUBLISH_TARGETS ?= html html-dir pdf epub
DOCBOOK_XSL ?= /usr/share/xml/docbook/stylesheet/docbook-xsl/epub/docbook.xsl
EPUBTRASH = epub.xml META-INF OEBPS

37
doc/AUTHORS.md Normal file
View File

@@ -0,0 +1,37 @@
Authors
=======
The following people have contributed to Org-Roam.
Names below are sorted alphabetically.
Author
------
- Jethro Kuan <jethrokuan95@gmail.com>
Maintainers
----------
- Jethro Kuan <jethrokuan95@gmail.com>
- Leo Vivier <leo.vivier+dev@gmail.com>
Contributors
------------
- Alexey Shmalko <rasen.dubi@gmail.com>
- James Ravn <james@r-vn.org>
- Jethro Kuan <jethrokuan95@gmail.com>
- Johann Klähn <johann@jklaehn.de>
- Josh English <josh@joshenglish.com>
- Jürgen Hötzel <juergen@archlinux.org>
- Langston Barrett <langston.barrett@gmail.com>
- Leo Vivier <leo.vivier+dev@gmail.com>
- Michael Glaesemann <grzm@seespotcode.net>
- Michael Herold <github@michaeljherold.com>
- Noboru <noboru.ota@gmail.com>
- N V <44036031+progfolio@users.noreply.github.com>
- Rafael Accácio Nogueira <raccacio@poli.ufrj.br>
- Roland Coeurjoly <rolandcoeurjoly@gmail.com>
- Sayan <dit7ya@users.noreply.github.com>
- Tim Quelch <tim@tquelch.com>

View File

@@ -1,94 +1,127 @@
-include ../config.mk
include ../default.mk
## User options ######################################################
#
# You can override these settings in "config.mk" or on the command
# line.
###################################################################
MANUAL_HTML_ARGS = --css-ref assets/page.css
sharedir ?= $(HOME)/.local/share
infodir ?= $(sharedir)/info
.PHONY: texi install clean AUTHORS.md stats
EMACS ?= emacs
BATCH = $(EMACS) -Q --batch
INSTALL_INFO ?= $(shell command -v ginstall-info || printf install-info)
MAKEINFO ?= makeinfo
MANUAL_HTML_ARGS ?= --css-ref assets/page.css
## Files #############################################################
PKG = org-roam
PACKAGES = org-roam
INFOPAGES = $(addsuffix .info,$(PACKAGES))
HTMLFILES = $(addsuffix .html,$(PACKAGES))
## Versions ##########################################################
EMACS_VERSION = 26.1
EMACSOLD := $(shell $(BATCH) --eval \
"(and (version< emacs-version \"$(EMACS_VERSION)\") (princ \"true\"))")
ifeq "$(EMACSOLD)" "true"
$(error At least version $(EMACS_VERSION) of Emacs is required)
endif
######################################################################
.PHONY: default
default: info
all: info
## Build #############################################################
.PHONY: info
info: dir
dir: $(INFOPAGES)
@printf "Generating dir\n"
@echo $^ | xargs -n 1 $(INSTALL_INFO) --dir=$@
info: $(INFOPAGES) dir
html: $(HTMLFILES)
pdf: $(PDFFILES)
epub: $(EPUBFILES)
%.info: %.texi
@printf "Generating $@\n"
@$(MAKEINFO) --no-split $< -o $@
.PHONY: html
html: $(HTMLFILES)
mv $(PKG).html manual.html
dir: org-roam.info
@printf "Generating dir\n"
@echo $^ | xargs -n 1 $(INSTALL_INFO) --dir=$@
%.html: %.texi
@printf "Generating $@\n"
@$(MAKEINFO) --html --no-split $(MANUAL_HTML_ARGS) $<
html-dir:
@$(MAKEINFO) --html --no-split $(MANUAL_HTML_ARGS) org-roam.texi
mv org-roam.html manual.html
%.pdf: %.texi
@printf "Generating $@\n"
@texi2pdf --clean $< > /dev/null
%.epub: %.texi
@printf "Generating $@\n"
@$(MAKEINFO) --docbook $< -o epub.xml
@xsltproc $(DOCBOOK_XSL) epub.xml 2> /dev/null
@echo "application/epub+zip" > mimetype
@zip -X --quiet --recurse-paths -0 $@ mimetype
@zip -X --quiet --recurse-paths -9 --no-dir-entries $@ META-INF OEBPS
@$(RMDIR) $(EPUBTRASH)
## Install ###########################################################
install: install-info install-docs
install-docs: install-info
@$(MKDIR) $(DESTDIR)$(docdir)
$(CP) AUTHORS.md $(DESTDIR)$(docdir)
install-info: info
@$(MKDIR) $(DESTDIR)$(infodir)
$(CP) $(INFOPAGES) $(DESTDIR)$(infodir)
## Clean #############################################################
clean:
@printf "Cleaning doc/*...\n"
@$(RMDIR) dir $(INFOPAGES) $(HTMLFILES) $(HTMLDIRS) $(PDFFILES)
@$(RMDIR) $(EPUBFILES) $(EPUBTRASH)
## Release management ################################################
ORG_ARGS = --batch -Q $(ORG_LOAD_PATH)
ORG_ARGS += -l ox-extra -l ox-texinfo+
ORG_ARGS += --eval "(or (require 'org-man nil t) (require 'ol-man))"
ORG_EVAL = --eval "(ox-extras-activate '(ignore-headlines))"
ORG_EVAL += --eval "(setq indent-tabs-mode nil)"
ORG_EVAL += --eval "(setq org-src-preserve-indentation nil)"
ORG_EVAL += --funcall org-texinfo-export-to-texinfo
# NOTE: %.org is not a prerequisite since package archives cant generate texi
# from org. texi is generated on org save and committed to repo
%.texi:
@echo "Generating $@"
@$(BATCH) $*.org $(ORG_EVAL)
# This target first bumps version strings in the Org source. The
# necessary tools might be missing so other targets do not depend
# on this target and it has to be run explicitly when appropriate.
#
# AMEND=t make texi Update manual to be amended to HEAD.
# VERSION=N make texi Update manual for release.
#
texi:
@$(EMACSBIN) $(ORG_ARGS) $(PKG).org $(ORG_EVAL)
@printf "\n" >> $(PKG).texi
@rm -f $(PKG).texi~
## Install ###########################################################
stats:
@printf "Generating statistics\n"
@gitstats -c style=/assets/stats.css -c max_authors=999 $(TOP) $(statsdir)
.PHONY: install
install: install-info
authors: AUTHORS.md
.PHONY: install-info
install-info: info
install -p -m 755 -d $(DESTDIR)$(infodir)
install -p -m 644 $(INFOPAGES) $(DESTDIR)$(infodir)
AUTHORS.md:
@printf "Generating AUTHORS.md..."
@test -e $(TOP).git \
&& (printf "$$AUTHORS_HEADER\n" > $@ \
&& git log --pretty=format:'- %aN <%aE>' | sort -u >> $@ \
&& printf "done\n" ; ) \
|| printf "FAILED (non-fatal)\n"
## Clean #############################################################
# Templates ##########################################################
.PHONY: clean
clean:
@echo "Cleaning doc/*..."
@rm -rf dir $(INFOPAGES) $(HTMLFILES)
define AUTHORS_HEADER
Authors
=======
## Release management ################################################
The following people have contributed to Org-Roam.
# Run VERSION=N make -C doc release-texi to explicitly set version when releasing
.PHONY: release-texi
release-texi:
VERSION=$(VERSION) $(MAKE) -B $(PKG).texi
Names below are sorted alphabetically.
Author
------
- Jethro Kuan <jethrokuan95@gmail.com>
Maintainers
----------
- Jethro Kuan <jethrokuan95@gmail.com>
- Leo Vivier <leo.vivier+dev@gmail.com>
Contributors
------------
endef
export AUTHORS_HEADER

View File

@@ -1,25 +1,23 @@
#+title: Org-roam User Manual
#+author: Jethro Kuan
#+email: jethrokuan95@gmail.com
#+date: 2020-{{{year}}}
#+date: 2020-2025
#+language: en
#+texinfo_deffn: t
#+texinfo_dir_category: Emacs
#+texinfo_dir_name: Org-roam: (org-roam).
#+texinfo_dir_title: Org-roam: (org-roam).
#+texinfo_dir_desc: Roam Research for Emacs.
#+subtitle: for {{{version}}}
#+subtitle: for version 2.3.1
#+options: H:4 num:3 toc:nil creator:t ':t
#+property: header-args :eval never
#+macro: version (eval (concat "version " (or (getenv "VERSION") (if-let* ((tag (ignore-errors (car (process-lines "git" "describe" "--tags" "--dirty=-devel" "--abbrev=0"))))) (replace-regexp-in-string "^v" "" tag) "<unknown>"))))
#+macro: year (eval (format-time-string "%Y"))
#+texinfo: @noindent
This manual is for Org-roam {{{version}}}.
This manual is for Org-roam version 2.3.1.
#+BEGIN_QUOTE
Copyright (C) 2020-{{{year}}} Jethro Kuan <jethrokuan95@gmail.com> and the
Org-roam contributors
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
You can redistribute this document and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either
@@ -222,6 +220,8 @@ You now have Org-roam installed. However, you don't necessarily have the
dependencies that it requires. These include:
- dash
- f
- s
- org
- emacsql
- magit-section
@@ -378,7 +378,7 @@ Node selection is achieved via the ~completing-read~ interface, typically
through ~org-roam-node-read~. The presentation of these nodes are governed by
~org-roam-node-display-template~.
- Variable: org-roam-node-display-template ::
- Variable: org-roam-node-display-template
Configures display formatting for Org-roam node.
@@ -458,7 +458,7 @@ additionally trying to process these links. Use
~org-roam-db-extra-links-elements~ to specify which additional Org AST element
types to consider.
- Variable: org-roam-db-extra-links-elements ::
- Variable: org-roam-db-extra-links-elements
The list of Org element types to include for parsing by Org-roam.
@@ -472,7 +472,7 @@ property drawers. For example, we would not want ~ROAM_REFS~ links to be
self-referential. Hence, to exclude specific keys, we use
~org-roam-db-extra-links-exclude-keys~.
- Variable: org-roam-db-extra-links-exclude-keys ::
- Variable: org-roam-db-extra-links-exclude-keys
Keys to ignore when mapping over links.
@@ -491,7 +491,7 @@ However, depending on how large your Org files are, database updating can be a
slow operation. You can disable the automatic updating of the database by
setting ~org-roam-db-update-on-save~ to ~nil~.
- Variable: org-roam-db-update-on-save ::
- Variable: org-roam-db-update-on-save
If t, update the Org-roam database upon saving the file. Disable this if your
files are large and updating the database is slow.
@@ -513,14 +513,14 @@ two main commands to use here:
To bring up a buffer that tracks the current node at point, call ~M-x
org-roam-buffer-toggle~.
- Function: org-roam-buffer-toggle ::
- Function: org-roam-buffer-toggle
Toggle display of the ~org-roam-buffer~.
To bring up a buffer that's dedicated for a specific node, call ~M-x
org-roam-buffer-display-dedicated~.
- Function: org-roam-buffer-display-dedicated ::
- Function: org-roam-buffer-display-dedicated
Launch node dedicated Org-roam buffer without visiting the node itself.
@@ -649,12 +649,12 @@ To assign an alias to a node, add the "ROAM_ALIASES" property to the node:
Alternatively, Org-roam provides some functions to add or remove aliases.
- Function: org-roam-alias-add alias ::
- Function: org-roam-alias-add alias
Add ALIAS to the node at point. When called interactively, prompt for the
alias to add.
- Function: org-roam-alias-remove ::
- Function: org-roam-alias-remove
Remove an alias from the node at point.
@@ -696,12 +696,12 @@ key and a URL at the same time.
Org-roam also provides some functions to add or remove refs.
- Function: org-roam-ref-add ref ::
- Function: org-roam-ref-add ref
Add REF to the node at point. When called interactively, prompt for the
ref to add.
- Function: org-roam-ref-remove ::
- Function: org-roam-ref-remove
Remove a ref from the node at point.
@@ -798,7 +798,7 @@ to ~t~:
(setq org-roam-completion-everywhere t)
#+end_src
- Variable: org-roam-completion-everywhere ::
- Variable: org-roam-completion-everywhere
When non-nil, provide link completion matching outside of Org links.
@@ -1028,14 +1028,14 @@ If you're using the [[https://formulae.brew.sh/formula/emacs][Emacs Homebrew for
a) Add option `-c` to `emacsclient` in the script, and start emacs from command line with `emacs --daemon`
#+begin_src emacs-lisp
#+begin_src emacs-lisp
on open location this_URL
set EC to "/usr/local/bin/emacsclient -c --no-wait "
set filePath to quoted form of this_URL
do shell script EC & filePath & " &> /dev/null &"
tell application "Emacs" to activate
end open location
#+end_src
#+end_src
b) Add `(server-start)` in .emacs (in this case you do not need option `-c` for `emacsclient` in the script, and you do not need to start emacs with `emacs --daemon`
@@ -1129,7 +1129,7 @@ generating images using [[https://graphviz.org/][Graphviz]]. The graph can also
The entry point to graph creation is ~org-roam-graph~.
- Function: org-roam-graph & optional arg node ::
- Function: org-roam-graph & optional arg node
Build and display a graph for NODE.
ARG may be any of the following values:
@@ -1138,7 +1138,7 @@ ARG may be any of the following values:
- ~integer~ an integer argument ~N~ will show the graph for the connected
components to node up to ~N~ steps away.
- User Option: org-roam-graph-executable ::
- User Option: org-roam-graph-executable
Path to the graphing executable (in this case, Graphviz). Set this if Org-roam
is unable to find the Graphviz executable on your system.
@@ -1146,7 +1146,7 @@ ARG may be any of the following values:
You may also choose to use ~neato~ in place of ~dot~, which generates a more
compact graph layout.
- User Option: org-roam-graph-viewer ::
- User Option: org-roam-graph-viewer
Org-roam defaults to using Firefox (located on PATH) to view the SVG, but you
may choose to set it to:
@@ -1172,22 +1172,22 @@ Graphviz provides many options for customizing the graph output, and Org-roam
supports some of them. See https://graphviz.gitlab.io/_pages/doc/info/attrs.html
for customizable options.
- User Option: org-roam-graph-filetype ::
- User Option: org-roam-graph-filetype
The file type to generate for graphs. This defaults to ~"svg"~.
- User Option: org-roam-graph-extra-config ::
- User Option: org-roam-graph-extra-config
Extra options passed to graphviz for the digraph (The "G" attributes).
Example: ~'~(("rankdir" . "LR"))~
- User Option: org-roam-graph-node-extra-config ::
- User Option: org-roam-graph-node-extra-config
An alist of options to style the nodes.
The car of the alist node type such as ~"id"~, or ~"http"~. The cdr of the
list is another alist of Graphviz node options (the "N" attributes).
- User Option: org-roam-graph-edge-extra-config ::
- User Option: org-roam-graph-edge-extra-config
Extra options for edges in the graphviz output (The "E" attributes).
Example: ~'(("dir" . "back"))~
@@ -1201,11 +1201,11 @@ Org-journal with ~org-roam-dailies~.
For ~org-roam-dailies~ to work, you need to define two variables:
- Variable: org-roam-dailies-directory ::
- Variable: ~org-roam-dailies-directory~
Path to daily-notes. This path is relative to ~org-roam-directory~.
- Variable: org-roam-dailies-capture-templates ::
- Variable: ~org-roam-dailies-capture-templates~
Capture templates for daily-notes in Org-roam.
@@ -1227,31 +1227,31 @@ See [[*The Templating System][The Templating System]] for creating new templates
~org-roam-dailies~ provides these interactive functions:
- Function: org-roam-dailies-capture-today &optional goto ::
- Function: ~org-roam-dailies-capture-today~ &optional goto
Create an entry in the daily note for today.
When ~goto~ is non-nil, go to the note without creating an entry.
- Function: org-roam-dailies-goto-today ::
- Function: ~org-roam-dailies-goto-today~
Find the daily note for today, creating it if necessary.
There are variants of those commands for ~-yesterday~ and ~-tomorrow~:
- Function: org-roam-dailies-capture-yesterday n &optional goto ::
- Function: ~org-roam-dailies-capture-yesterday~ n &optional goto
Create an entry in the daily note for yesterday.
With numeric argument ~n~, use the daily note ~n~ days in the past.
- Function: org-roam-dailies-goto-yesterday ::
- Function: ~org-roam-dailies-goto-yesterday~
With numeric argument N, use the daily-note N days in the future.
There are also commands which allow you to use Emacss ~calendar~ to find the date
- Function: org-roam-dailies-capture-date ::
- Function: ~org-roam-dailies-capture-date~
Create an entry in the daily note for a date using the calendar.
@@ -1260,21 +1260,21 @@ There are also commands which allow you to use Emacss ~calendar~ to find the
With a 'C-u' prefix or when ~goto~ is non-nil, go the note without
creating an entry.
- Function: org-roam-dailies-goto-date ::
- Function: ~org-roam-dailies-goto-date~
Find the daily note for a date using the calendar, creating it if necessary.
Prefer past dates, unless ~prefer-future~ is non-nil.
- Function: org-roam-dailies-find-directory ::
- Function: ~org-roam-dailies-find-directory~
Find and open ~org-roam-dailies-directory~.
- Function: org-roam-dailies-goto-previous-note ::
- Function: ~org-roam-dailies-goto-previous-note~
When in an daily-note, find the previous one.
- Function: org-roam-dailies-goto-next-note ::
- Function: ~org-roam-dailies-goto-next-note~
When in an daily-note, find the next one.
** org-roam-export
@@ -1664,12 +1664,13 @@ that extensions/customizations are robust to change, extensions should only use
The node interface is cleanly defined using ~cl-defstruct~. The primary
method to access nodes is ~org-roam-node-at-point~ and ~org-roam-node-read~:
- Function: org-roam-node-at-point &optional assert ::
- Function: org-roam-node-at-point &optional assert
Return the node at point. If ASSERT, throw an error if there is no node at
point.
- Function: org-roam-node-read &optional initial-input filter-fn sort-fn require-match ::
- Function: org-roam-node-read &optional initial-input filter-fn sort-fn
require-match
Read and return an `org-roam-node'.
INITIAL-INPUT is the initial minibuffer prompt value. FILTER-FN
@@ -1704,7 +1705,7 @@ Org-roam applies some patching over Org's capture system to smooth out the user
experience, and sometimes it is desirable to use Org-roam's capturing system
instead. The exposed function to be used in extensions is ~org-roam-capture-~:
- Function: org-roam-capture- &key goto keys node info props templates ::
- Function: org-roam-capture- &key goto keys node info props templates
Main entry point.
GOTO and KEYS correspond to `org-capture' arguments.
@@ -1734,8 +1735,7 @@ When GOTO is non-nil, go the note without creating an entry."
:END:
#+BEGIN_QUOTE
Copyright (C) 2020-{{{year}}} Jethro Kuan <jethrokuan95@gmail.com> and the
Org-roam contributors
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@gmail.com>
You can redistribute this document and/or modify it under the terms
of the GNU General Public License as published by the Free Software
@@ -1796,9 +1796,9 @@ an Org file code block (like =#+BEGIN_SRC emacs-lisp=).
# Local Variables:
# eval: (require 'ol-info)
# eval: (require 'ox-texinfo+ nil t)
# eval: (auto-fill-mode +1)
# after-save-hook: org-texinfo-export-to-info
# after-save-hook: (lambda nil (progn (require 'ox-texinfo nil t) (org-texinfo-export-to-info)))
# indent-tabs-mode: nil
# org-src-preserve-indentation: nil
# org-list-allow-alphabetical: t
# End:

View File

@@ -8,8 +8,7 @@
@copying
@quotation
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@@gmail.com> and the
Org-roam contributors
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@@gmail.com>
You can redistribute this document and/or modify it under the terms
of the GNU General Public License as published by the Free Software
@@ -32,7 +31,7 @@ General Public License for more details.
@finalout
@titlepage
@title Org-roam User Manual
@subtitle for version 2.3.1-devel
@subtitle for version 2.3.1
@author Jethro Kuan
@page
@vskip 0pt plus 1filll
@@ -45,11 +44,10 @@ General Public License for more details.
@noindent
This manual is for Org-roam version 2.3.1-devel.
This manual is for Org-roam version 2.3.1.
@quotation
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@@gmail.com> and the
Org-roam contributors
Copyright (C) 2020-2025 Jethro Kuan <jethrokuan95@@gmail.com>
You can redistribute this document and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either
@@ -435,6 +433,10 @@ dependencies that it requires. These include:
@item
dash
@item
f
@item
s
@item
org
@item
emacsql
@@ -624,7 +626,10 @@ Node selection is achieved via the @code{completing-read} interface, typically
through @code{org-roam-node-read}. The presentation of these nodes are governed by
@code{org-roam-node-display-template}.
@defvar org-roam-node-display-template
@itemize
@item
Variable: org-roam-node-display-template
Configures display formatting for Org-roam node.
Patterns of form ``$@{field-name:length@}'' are interpolated based
@@ -653,7 +658,7 @@ as many characters as possible and will be aligned accordingly.
A closure can also be assigned to this variable in which case the
closure is evaluated and the return value is used as the
template. The closure must evaluate to a valid template string.
@end defvar
@end itemize
If you're using a vertical completion framework, such as Ivy and Selectrum,
Org-roam supports the generation of an aligned, tabular completion interface.
@@ -715,27 +720,33 @@ additionally trying to process these links. Use
@code{org-roam-db-extra-links-elements} to specify which additional Org AST element
types to consider.
@defvar org-roam-db-extra-links-elements
@itemize
@item
Variable: org-roam-db-extra-links-elements
The list of Org element types to include for parsing by Org-roam.
By default, when parsing Org's AST, links within keywords and
property drawers are not parsed as links. Sometimes however, it
is desirable to parse and cache these links (e.g. hiding links in
a property drawer).
@end defvar
@end itemize
Additionally, one may want to ignore certain keys from being excluded within
property drawers. For example, we would not want @code{ROAM_REFS} links to be
self-referential. Hence, to exclude specific keys, we use
@code{org-roam-db-extra-links-exclude-keys}.
@defvar org-roam-db-extra-links-exclude-keys
@itemize
@item
Variable: org-roam-db-extra-links-exclude-keys
Keys to ignore when mapping over links.
The car of the association list is the Org element type (e.g. keyword). The
cdr is a list of case-insensitive strings to exclude from being treated as
links.
@end defvar
@end itemize
@node When to cache
@section When to cache
@@ -749,8 +760,11 @@ However, depending on how large your Org files are, database updating can be a
slow operation. You can disable the automatic updating of the database by
setting @code{org-roam-db-update-on-save} to @code{nil}.
@defvar org-roam-db-update-on-save
@end defvar
@itemize
@item
Variable: org-roam-db-update-on-save
@end itemize
If t, update the Org-roam database upon saving the file. Disable this if your
files are large and updating the database is slow.
@@ -776,16 +790,22 @@ new node at point.
To bring up a buffer that tracks the current node at point, call @code{M-x
org-roam-buffer-toggle}.
@defun org-roam-buffer-toggle
@itemize
@item
Function: org-roam-buffer-toggle
Toggle display of the @code{org-roam-buffer}.
@end defun
@end itemize
To bring up a buffer that's dedicated for a specific node, call @code{M-x
org-roam-buffer-display-dedicated}.
@defun org-roam-buffer-display-dedicated
@itemize
@item
Function: org-roam-buffer-display-dedicated
Launch node dedicated Org-roam buffer without visiting the node itself.
@end defun
@end itemize
@menu
* Navigating the Org-roam Buffer::
@@ -958,14 +978,18 @@ To assign an alias to a node, add the ``ROAM@math{_ALIASES}'' property to the no
Alternatively, Org-roam provides some functions to add or remove aliases.
@defun org-roam-alias-add alias
@itemize
@item
Function: org-roam-alias-add alias
Add ALIAS to the node at point. When called interactively, prompt for the
alias to add.
@end defun
@defun org-roam-alias-remove
@item
Function: org-roam-alias-remove
Remove an alias from the node at point.
@end defun
@end itemize
@node Tags
@section Tags
@@ -1007,14 +1031,18 @@ key and a URL at the same time.
Org-roam also provides some functions to add or remove refs.
@defun org-roam-ref-add ref
@itemize
@item
Function: org-roam-ref-add ref
Add REF to the node at point. When called interactively, prompt for the
ref to add.
@end defun
@defun org-roam-ref-remove
@item
Function: org-roam-ref-remove
Remove a ref from the node at point.
@end defun
@end itemize
@node Citations
@chapter Citations
@@ -1127,8 +1155,11 @@ to @code{t}:
(setq org-roam-completion-everywhere t)
@end lisp
@defvar org-roam-completion-everywhere
@end defvar
@itemize
@item
Variable: org-roam-completion-everywhere
@end itemize
When non-nil, provide link completion matching outside of Org links.
@node Encryption
@@ -1427,6 +1458,7 @@ If you're using the @uref{https://formulae.brew.sh/formula/emacs, Emacs Homebrew
@enumerate
@item
Add option `-c` to `emacsclient` in the script, and start emacs from command line with `emacs --daemon`
@end enumerate
@lisp
on open location this_URL
@@ -1437,6 +1469,7 @@ on open location this_URL
end open location
@end lisp
@enumerate
@item
Add `(server-start)` in .emacs (in this case you do not need option `-c` for `emacsclient` in the script, and you do not need to start emacs with `emacs --daemon`
@end enumerate
@@ -1541,8 +1574,11 @@ generating images using @uref{https://graphviz.org/, Graphviz}. The graph can al
The entry point to graph creation is @code{org-roam-graph}.
@defun org-roam-graph & optional arg node
@end defun
@itemize
@item
Function: org-roam-graph & optional arg node
@end itemize
Build and display a graph for NODE@.
ARG may be any of the following values:
@@ -1554,15 +1590,19 @@ ARG may be any of the following values:
components to node up to @code{N} steps away.
@end itemize
@defopt org-roam-graph-executable
@itemize
@item
User Option: org-roam-graph-executable
Path to the graphing executable (in this case, Graphviz). Set this if Org-roam
is unable to find the Graphviz executable on your system.
You may also choose to use @code{neato} in place of @code{dot}, which generates a more
compact graph layout.
@end defopt
@defopt org-roam-graph-viewer
@item
User Option: org-roam-graph-viewer
Org-roam defaults to using Firefox (located on PATH) to view the SVG, but you
may choose to set it to:
@@ -1584,7 +1624,7 @@ the second option to set the browser and network file path:
(let ((org-roam-graph-viewer "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"))
(org-roam-graph--open (concat "file://///wsl$/Ubuntu" file)))))
@end lisp
@end defopt
@end itemize
@menu
* Graph Options::
@@ -1597,25 +1637,31 @@ Graphviz provides many options for customizing the graph output, and Org-roam
supports some of them. See @uref{https://graphviz.gitlab.io/_pages/doc/info/attrs.html}
for customizable options.
@defopt org-roam-graph-filetype
The file type to generate for graphs. This defaults to @code{"svg"}.
@end defopt
@itemize
@item
User Option: org-roam-graph-filetype
The file type to generate for graphs. This defaults to @code{"svg"}.
@item
User Option: org-roam-graph-extra-config
@defopt org-roam-graph-extra-config
Extra options passed to graphviz for the digraph (The ``G'' attributes).
Example: @code{'~(("rankdir" . "LR"))}
@end defopt
@defopt org-roam-graph-node-extra-config
@item
User Option: org-roam-graph-node-extra-config
An alist of options to style the nodes.
The car of the alist node type such as @code{"id"}, or @code{"http"}. The cdr of the
list is another alist of Graphviz node options (the ``N'' attributes).
@end defopt
@defopt org-roam-graph-edge-extra-config
@item
User Option: org-roam-graph-edge-extra-config
Extra options for edges in the graphviz output (The ``E'' attributes).
Example: @code{'(("dir" . "back"))}
@end defopt
@end itemize
@node org-roam-dailies
@section org-roam-dailies
@@ -1633,13 +1679,17 @@ Org-journal with @code{org-roam-dailies}.
For @code{org-roam-dailies} to work, you need to define two variables:
@defvar org-roam-dailies-directory
Path to daily-notes. This path is relative to @code{org-roam-directory}.
@end defvar
@itemize
@item
Variable: @code{org-roam-dailies-directory}
Path to daily-notes. This path is relative to @code{org-roam-directory}.
@item
Variable: @code{org-roam-dailies-capture-templates}
@defvar org-roam-dailies-capture-templates
Capture templates for daily-notes in Org-roam.
@end defvar
@end itemize
Here is a sane default configuration:
@@ -1660,56 +1710,71 @@ See @ref{The Templating System} for creating new templates.
@code{org-roam-dailies} provides these interactive functions:
@defun org-roam-dailies-capture-today &optional goto
@itemize
@item
Function: @code{org-roam-dailies-capture-today} &optional goto
Create an entry in the daily note for today.
When @code{goto} is non-nil, go to the note without creating an entry.
@end defun
@defun org-roam-dailies-goto-today
@item
Function: @code{org-roam-dailies-goto-today}
Find the daily note for today, creating it if necessary.
@end defun
@end itemize
There are variants of those commands for @code{-yesterday} and @code{-tomorrow}:
@defun org-roam-dailies-capture-yesterday n &optional goto
@itemize
@item
Function: @code{org-roam-dailies-capture-yesterday} n &optional goto
Create an entry in the daily note for yesterday.
With numeric argument @code{n}, use the daily note @code{n} days in the past.
@end defun
@defun org-roam-dailies-goto-yesterday
@item
Function: @code{org-roam-dailies-goto-yesterday}
With numeric argument N, use the daily-note N days in the future.
@end defun
@end itemize
There are also commands which allow you to use Emacss @code{calendar} to find the date
@defun org-roam-dailies-capture-date
@itemize
@item
Function: @code{org-roam-dailies-capture-date}
Create an entry in the daily note for a date using the calendar.
Prefer past dates, unless @code{prefer-future} is non-nil.
With a 'C-u' prefix or when @code{goto} is non-nil, go the note without
creating an entry.
@end defun
@defun org-roam-dailies-goto-date
@item
Function: @code{org-roam-dailies-goto-date}
Find the daily note for a date using the calendar, creating it if necessary.
Prefer past dates, unless @code{prefer-future} is non-nil.
@end defun
@defun org-roam-dailies-find-directory
@item
Function: @code{org-roam-dailies-find-directory}
Find and open @code{org-roam-dailies-directory}.
@end defun
@defun org-roam-dailies-goto-previous-note
@item
Function: @code{org-roam-dailies-goto-previous-note}
When in an daily-note, find the previous one.
@end defun
@defun org-roam-dailies-goto-next-note
@item
Function: @code{org-roam-dailies-goto-next-note}
When in an daily-note, find the next one.
@end defun
@end itemize
@node org-roam-export
@section org-roam-export
@@ -2218,12 +2283,17 @@ that extensions/customizations are robust to change, extensions should only use
The node interface is cleanly defined using @code{cl-defstruct}. The primary
method to access nodes is @code{org-roam-node-at-point} and @code{org-roam-node-read}:
@defun org-roam-node-at-point &optional assert
@itemize
@item
Function: org-roam-node-at-point &optional assert
Return the node at point. If ASSERT, throw an error if there is no node at
point.
@end defun
@defun org-roam-node-read &optional initial-input filter-fn sort-fn require-match
@item
Function: org-roam-node-read &optional initial-input filter-fn sort-fn
require-match
Read and return an `org-roam-node'.
INITIAL-INPUT is the initial minibuffer prompt value. FILTER-FN
is a function to filter out nodes: it takes a single argument (an
@@ -2232,7 +2302,7 @@ filtered out.
SORT-FN is a function to sort nodes. See @code{org-roam-node-read-sort-by-file-mtime}
for an example sort function.
If REQUIRE-MATCH, the minibuffer prompt will require a match.
@end defun
@end itemize
Once you obtain the node, you can use the accessors for the node, e.g.
@code{org-roam-node-id} or @code{org-roam-node-todo}.
@@ -2259,14 +2329,17 @@ Org-roam applies some patching over Org's capture system to smooth out the user
experience, and sometimes it is desirable to use Org-roam's capturing system
instead. The exposed function to be used in extensions is @code{org-roam-capture-}:
@defun org-roam-capture- &key goto keys node info props templates
@itemize
@item
Function: org-roam-capture- &key goto keys node info props templates
Main entry point.
GOTO and KEYS correspond to `org-capture' arguments.
INFO is a plist for filling up Org-roam's capture templates.
NODE is an `org-roam-node' construct containing information about the node.
PROPS is a plist containing additional Org-roam properties for each template.
TEMPLATES is a list of org-roam templates.
@end defun
@end itemize
An example of an extension using @code{org-roam-capture-} is @code{org-roam-dailies}
itself:
@@ -2348,5 +2421,5 @@ When GOTO is non-nil, go the note without creating an entry."
@printindex vr
Emacs 30.1.90 (Org mode 9.7.25)
Emacs 30.1 (Org mode 9.7.29)
@bye

View File

@@ -339,11 +339,12 @@ In this case, interactive selection will be bypassed."
(when goto (run-hooks 'org-roam-dailies-find-file-hook)))
(add-hook 'org-roam-capture-preface-hook #'org-roam-dailies--override-capture-time-h)
(defun org-roam-dailies--override-capture-time-h ()
"Override the `:default-time' with the time from `:override-default-time'."
(prog1 nil
(when (org-roam-capture--get :override-default-time)
(org-capture-put :default-time (org-roam-capture--get :override-default-time)))))
(when (org-roam-capture--get :override-default-time)
(org-capture-put :default-time (org-roam-capture--get :override-default-time)))
nil)
;;; Bindings
(defvar org-roam-dailies-map (make-sparse-keymap)

11
github-eldev Executable file
View File

@@ -0,0 +1,11 @@
#! /bin/sh
set -e
ELDEV_BIN_DIR=~/.local/bin
# `$GITHUB_PATH' is a magic file which contents is translated to environment variable `$PATH'.
echo "$ELDEV_BIN_DIR" >> $GITHUB_PATH
mkdir -p $ELDEV_BIN_DIR
curl -fsSL https://raw.githubusercontent.com/doublep/eldev/f111d19cda305e5e8fcb70a5675b87173041cb68/bin/eldev > $ELDEV_BIN_DIR/eldev
chmod a+x $ELDEV_BIN_DIR/eldev

View File

@@ -466,22 +466,23 @@ processing by `org-capture'.
Note: During the capture process this function is run by
`org-capture-set-target-location', as a (function ...) based
capture target."
(let ((id (cond ((run-hook-with-args-until-success 'org-roam-capture-preface-hook))
(t (org-roam-capture--setup-target-location)))))
(org-roam-capture--adjust-point-for-capture-type)
(let ((template (org-capture-get :template)))
(when (stringp template)
(org-capture-put
:template
(org-roam-capture--fill-template template))))
(org-roam-capture--put :id id)
(org-roam-capture--put :finalize (or (org-capture-get :finalize)
(org-roam-capture--get :finalize)))))
(if-let ((id (run-hook-with-args-until-success 'org-roam-capture-preface-hook)))
(org-roam-capture--put :id id)
(org-roam-capture--setup-target-location)
;; Adjust point for plain captures to skip past metadata (e.g. properties drawer)
(org-roam-capture--adjust-point-for-capture-type))
(let ((template (org-capture-get :template)))
(when (stringp template)
(org-capture-put
:template
(org-roam-capture--fill-template template))))
(org-roam-capture--put :finalize (or (org-capture-get :finalize)
(org-roam-capture--get :finalize))))
(defun org-roam-capture--setup-target-location ()
"Initialize the buffer, and goto the location of the new capture.
Return the ID of the location."
(let (p new-file-p)
"Initialize the buffer, and goto the location of the new capture."
(let ((target-entry-p t)
p new-file-p id)
(pcase (org-roam-capture--get-target)
(`(file ,path)
(setq path (org-roam-capture--target-truepath path)
@@ -489,7 +490,8 @@ Return the ID of the location."
(when new-file-p (org-roam-capture--put :new-file path))
(set-buffer (org-capture-target-buffer path))
(widen)
(setq p (goto-char (point-min))))
(setq p (goto-char (point-min))
target-entry-p nil))
(`(file+olp ,path ,olp)
(setq path (org-roam-capture--target-truepath path)
new-file-p (org-roam-capture--new-file-p path))
@@ -505,9 +507,12 @@ Return the ID of the location."
(set-buffer (org-capture-target-buffer path))
(when new-file-p
(org-roam-capture--put :new-file path)
(insert (org-roam-capture--fill-template head 'ensure-newline)))
(insert (org-roam-capture--fill-template head 'ensure-newline))
(setq p (point-max)))
(widen)
(setq p (goto-char (point-min))))
(unless new-file-p
(setq p (goto-char (point-min))))
(setq target-entry-p nil))
(`(file+head+olp ,path ,head ,olp)
(setq path (org-roam-capture--target-truepath path)
new-file-p (org-roam-capture--new-file-p path))
@@ -569,17 +574,45 @@ Return the ID of the location."
(user-error "No node with title or id \"%s\"" title-or-id))))
(set-buffer (org-capture-target-buffer (org-roam-node-file node)))
(goto-char (org-roam-node-point node))
(setq p (org-roam-node-point node)))))
(setq p (org-roam-node-point node)
target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p))))))
;; Setup `org-id' for the current capture target and return it back to the
;; caller.
(save-excursion
(goto-char p)
(if-let ((id (org-entry-get p "ID")))
(setf (org-roam-node-id org-roam-capture--node) id)
(org-entry-put p "ID" (org-roam-node-id org-roam-capture--node)))
(prog1
(org-id-get)
(run-hooks 'org-roam-capture-new-node-hook)))))
;; Unless it's an entry type, then we want to create an ID for the entry instead
(pcase (org-capture-get :type)
('entry
(advice-add #'org-capture-place-entry :after #'org-roam-capture--create-id-for-entry)
(org-roam-capture--put :new-node-p t)
(setq id (org-roam-node-id org-roam-capture--node)))
(_
(save-excursion
(goto-char p)
(unless (org-entry-get p "ID")
(org-roam-capture--put :new-node-p t))
(setq id (or (org-entry-get p "ID")
(org-roam-node-id org-roam-capture--node)))
(setf (org-roam-node-id org-roam-capture--node) id)
(org-entry-put p "ID" id))))
(org-roam-capture--put :id id)
(org-roam-capture--put :target-entry-p target-entry-p)
(advice-add #'org-capture-place-template :before #'org-roam-capture--set-target-entry-p-a)
(advice-add #'org-capture-place-template :after #'org-roam-capture-run-new-node-hook-a)))
(defun org-roam-capture--set-target-entry-p-a (_)
"Correct `:target-entry-p' in Org-capture template based on `:target.'."
(org-capture-put :target-entry-p (org-roam-capture--get :target-entry-p))
(advice-remove #'org-capture-place-template #'org-roam-capture--set-target-entry-p-a))
(defun org-roam-capture-run-new-node-hook-a (_)
"Advice to run after the Org-capture template is placed."
(when (org-roam-capture--get :new-node-p)
(run-hooks 'org-roam-capture-new-node-hook))
(advice-remove #'org-capture-place-template #'org-roam-capture--place-template-a))
(defun org-roam-capture--create-id-for-entry ()
"Create the ID for the new entry."
(org-entry-put (point) "ID" (org-roam-capture--get :id))
(advice-remove #'org-capture-place-entry #'org-roam-capture--create-id-for-entry))
(defun org-roam-capture--get-target ()
"Get the current capture :target for the capture template in use."
@@ -657,27 +690,17 @@ POS is the current position of point (an integer) inside the
currently active capture buffer, where the adjustment should
start to begin from. If it's nil, then it will default to
the current value of `point'."
(or pos (setq pos (point)))
(goto-char pos)
(let ((location-type (if (= pos 1) 'beginning-of-file 'heading-at-point)))
(and (eq location-type 'heading-at-point)
(cl-assert (org-at-heading-p)))
(pcase (org-capture-get :type)
(`plain
(cl-case location-type
(beginning-of-file
(if (org-capture-get :prepend)
(let ((el (org-element-at-point)))
(while (and (not (eobp))
(memq (org-element-type el)
'(drawer property-drawer keyword comment comment-block horizontal-rule)))
(goto-char (org-element-property :end el))
(setq el (org-element-at-point))))
(goto-char (org-entry-end-position))))
(heading-at-point
(if (org-capture-get :prepend)
(org-end-of-meta-data t)
(goto-char (org-entry-end-position))))))))
(goto-char (or pos (point)))
(pcase (org-capture-get :type)
(`plain
(if (org-capture-get :prepend)
(let ((el (org-element-at-point)))
(while (and (not (eobp))
(memq (org-element-type el)
'(drawer property-drawer keyword comment comment-block horizontal-rule)))
(goto-char (org-element-property :end el))
(setq el (org-element-at-point))))
(goto-char (org-entry-end-position)))))
(point))
;;; Capture implementation

View File

@@ -810,10 +810,9 @@ Assumes that the cursor was put where the link is."
(defun org-roam-link-replace-all ()
"Replace all \"roam:\" links in buffer with \"id:\" links."
(interactive)
(let ((org-roam-link-prefix (concat "[[" org-roam-link-type ":")))
(org-with-point-at 1
(while (re-search-forward org-roam-link-prefix nil t)
(org-roam-link-replace-at-point)))))
(org-with-point-at 1
(while (search-forward (concat "[[" org-roam-link-type ":") nil t)
(org-roam-link-replace-at-point))))
(add-hook 'org-roam-find-file-hook #'org-roam--replace-roam-links-on-save-h)
(defun org-roam--replace-roam-links-on-save-h ()

View File

@@ -58,6 +58,330 @@
(org-roam-capture--fill-template (lambda () "foo"))
:to-equal "foo")))
(describe "org-roam-capture entry-type ID creation"
(it "creates ID for entry-type captures"
(let* ((temp-dir (make-temp-file "org-roam-test" t))
(test-file (expand-file-name "test.org" temp-dir))
(org-roam-directory temp-dir)
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
(org-roam-capture--info (make-hash-table :test 'equal))
capture-id)
(unwind-protect
(progn
;; Create initial file content
(with-temp-file test-file
(insert "#+TITLE: Test File\n\n* Parent Heading\n:PROPERTIES:\n:ID: parent-id\n:END:\n"))
;; Mock org-capture context and get-target
(cl-letf* (((symbol-function 'org-capture-get)
(lambda (prop)
(pcase prop
(:type 'entry)
(_ nil))))
((symbol-function 'org-roam-capture--get-target)
(lambda () `(file ,test-file))))
;; Call the setup function
(with-current-buffer (find-file-noselect test-file)
(org-roam-capture--setup-target-location)
(setq capture-id (org-roam-capture--get :id)))
;; Verify ID was created and stored for entry type
(expect capture-id :not :to-be nil)
(expect (org-roam-capture--get :new-node-p) :to-be t)))
(delete-directory temp-dir t))))
(it "creates ID at target for non-entry-type captures"
(let* ((temp-dir (make-temp-file "org-roam-test" t))
(test-file (expand-file-name "test-plain.org" temp-dir))
(org-roam-directory temp-dir)
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
(org-roam-capture--info (make-hash-table :test 'equal))
capture-id)
(unwind-protect
(progn
;; Create initial empty file
(with-temp-file test-file
(insert "#+TITLE: Test\n"))
;; Mock org-capture context for plain type
(cl-letf* (((symbol-function 'org-capture-get)
(lambda (prop)
(pcase prop
(:type 'plain)
(_ nil))))
((symbol-function 'org-roam-capture--get-target)
(lambda () `(file ,test-file))))
;; Call the setup function
(with-current-buffer (find-file-noselect test-file)
(org-roam-capture--setup-target-location)
(setq capture-id (org-roam-capture--get :id)))
;; For non-entry types, ID should be created at the target location
(expect capture-id :not :to-be nil)
(expect (org-roam-capture--get :new-node-p) :to-be t)))
(delete-directory temp-dir t)))))
(describe "org-roam-capture advice functions"
:var ((org-roam-capture--info))
(before-each
(setq org-roam-capture--info (make-hash-table :test 'equal)))
(it "org-roam-capture--create-id-for-entry creates and removes advice"
(cl-letf* ((entry-id nil)
((symbol-function 'org-entry-put)
(lambda (pom prop val)
(when (string= prop "ID")
(setq entry-id val))))
((symbol-function 'org-roam-capture--get)
(lambda (prop)
(if (eq prop :id)
"test-id-123"
nil))))
;; Add the advice
(advice-add #'org-capture-place-entry :after #'org-roam-capture--create-id-for-entry)
;; Call the function
(org-roam-capture--create-id-for-entry)
;; Verify ID was set and advice removed
(expect entry-id :to-equal "test-id-123")
(expect (advice-member-p #'org-roam-capture--create-id-for-entry
#'org-capture-place-entry)
:to-be nil)))
(it "org-roam-capture--set-target-entry-p-a sets and removes advice"
(cl-letf* ((captured-value nil)
((symbol-function 'org-capture-put)
(lambda (prop val)
(when (eq prop :target-entry-p)
(setq captured-value val))))
((symbol-function 'org-roam-capture--get)
(lambda (prop)
(if (eq prop :target-entry-p)
t
nil))))
;; Add the advice
(advice-add #'org-capture-place-template :before #'org-roam-capture--set-target-entry-p-a)
;; Call the function
(org-roam-capture--set-target-entry-p-a nil)
;; Verify value was set and advice removed
(expect captured-value :to-be t)
(expect (advice-member-p #'org-roam-capture--set-target-entry-p-a
#'org-capture-place-template)
:to-be nil)))
(it "org-roam-capture-run-new-node-hook-a runs hook when new node"
(let ((hook-ran nil))
(cl-letf* (((symbol-function 'org-roam-capture--get)
(lambda (prop)
(if (eq prop :new-node-p)
t
nil))))
;; Add test hook
(add-hook 'org-roam-capture-new-node-hook
(lambda () (setq hook-ran t)))
;; Add the advice
(advice-add #'org-capture-place-template :after #'org-roam-capture-run-new-node-hook-a)
;; Call the function
(org-roam-capture-run-new-node-hook-a nil)
;; Verify hook ran
(expect hook-ran :to-be t)
;; Clean up
(remove-hook 'org-roam-capture-new-node-hook
(lambda () (setq hook-ran t)))))))
(describe "org-roam-capture target-entry-p detection"
(it "detects entry target for file+olp"
(let* ((temp-dir (make-temp-file "org-roam-test" t))
(test-file (expand-file-name "test-olp.org" temp-dir))
(org-roam-directory temp-dir)
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
(org-roam-capture--info (make-hash-table :test 'equal)))
(unwind-protect
(progn
(with-temp-file test-file
(insert "* Level 1\n** Level 2\n"))
(cl-letf* (((symbol-function 'org-roam-capture--get-target)
(lambda () `(file+olp ,test-file ("Level 1" "Level 2"))))
((symbol-function 'org-capture-get)
(lambda (prop) nil)))
(with-current-buffer (find-file-noselect test-file)
(org-roam-capture--setup-target-location)
(expect (org-roam-capture--get :target-entry-p) :to-be t))))
(delete-directory temp-dir t))))
(it "detects non-entry target for file"
(let* ((temp-dir (make-temp-file "org-roam-test" t))
(test-file (expand-file-name "test-file.org" temp-dir))
(org-roam-directory temp-dir)
(org-roam-capture--node (org-roam-node-create :id (org-id-new)))
(org-roam-capture--info (make-hash-table :test 'equal)))
(unwind-protect
(progn
(with-temp-file test-file
(insert "#+TITLE: Test\n"))
(cl-letf* (((symbol-function 'org-roam-capture--get-target)
(lambda () `(file ,test-file)))
((symbol-function 'org-capture-get)
(lambda (prop) nil)))
(with-current-buffer (find-file-noselect test-file)
(org-roam-capture--setup-target-location)
(expect (org-roam-capture--get :target-entry-p) :to-be nil))))
(delete-directory temp-dir t)))))
(describe "org-roam-capture plain type ordering"
:var ((temp-dir) (org-roam-directory) (org-roam-db-location))
(before-each
(setq temp-dir (make-temp-file "org-roam-test" t))
(setq org-roam-directory temp-dir)
(setq org-roam-db-location (expand-file-name "org-roam.db" temp-dir))
(org-roam-db-sync))
(after-each
(delete-directory temp-dir t))
(it "places properties drawer before captured content for plain type with file target"
(let* ((test-file (expand-file-name "test-plain-file.org" temp-dir))
(test-content "Test plain :target file")
(node (org-roam-node-create :title "Test Plain"))
(org-roam-capture--node node)
(org-roam-capture--info (make-hash-table :test 'equal)))
;; Call the setup directly to simulate capture without user interaction
(cl-letf* (((symbol-function 'org-capture-get)
(lambda (prop)
(pcase prop
(:type 'plain)
(:target-file test-file)
(_ nil))))
((symbol-function 'org-roam-capture--get-target)
(lambda () `(file ,test-file))))
;; Run the setup and insert content
(with-current-buffer (find-file-noselect test-file)
(org-roam-capture--setup-target-location)
(org-roam-capture--adjust-point-for-capture-type)
(insert test-content)
(save-buffer)))
;; Read the created file and check its structure
(with-temp-buffer
(insert-file-contents test-file)
(let ((buffer-content (buffer-string)))
;; The expected format is:
;; :PROPERTIES:
;; :ID: some-id
;; :END:
;; Test plain :target file
;; Check that properties come first
(expect buffer-content
:to-match
(rx bol ":PROPERTIES:"))
;; Verify ordering: properties, then content
(let ((props-pos (string-match ":PROPERTIES:" buffer-content))
(end-pos (string-match ":END:" buffer-content))
(content-pos (string-match (regexp-quote test-content) buffer-content)))
(expect props-pos :to-be 0)
(expect end-pos :not :to-be nil)
(expect end-pos :to-be-greater-than props-pos)
(expect content-pos :not :to-be nil)
(expect content-pos :to-be-greater-than end-pos))))))
(it "correctly orders buffer elements for plain type with file+head target"
(let* ((test-file (expand-file-name "test-plain-file-head.org" temp-dir))
(test-content "Test plain :target file+head")
(node (org-roam-node-create :title "plain file+head"))
(org-roam-capture--node node)
(org-roam-capture--info (make-hash-table :test 'equal)))
;; Populate capture info with title for template expansion
(puthash :title "plain file+head" org-roam-capture--info)
;; Call the setup directly to simulate capture without user interaction
(cl-letf* (((symbol-function 'org-capture-get)
(lambda (prop)
(pcase prop
(:type 'plain)
(:target-file test-file)
(_ nil))))
((symbol-function 'org-roam-capture--get-target)
(lambda () `(file+head ,test-file "#+title: ${title}\n"))))
;; Run the setup and insert content
(with-current-buffer (find-file-noselect test-file)
(org-roam-capture--setup-target-location)
(org-roam-capture--adjust-point-for-capture-type)
(insert test-content)
(save-buffer)))
;; Read the created file and check its structure
(with-temp-buffer
(insert-file-contents test-file)
(let ((buffer-content (buffer-string)))
;; The actual format according to org-mode property syntax is:
;; :PROPERTIES:
;; :ID: some-id
;; :END:
;; #+title: plain file+head
;; Test plain :target file+head
;;
;; This is correct - buffer-level properties must be at the top
;; Check that properties come first
(expect buffer-content
:to-match
(rx bol ":PROPERTIES:"))
;; Verify ordering: properties are at the top
(let ((props-pos (string-match ":PROPERTIES:" buffer-content))
(end-pos (string-match ":END:" buffer-content)))
;; Properties drawer should be first
(expect props-pos :to-be 0)
(expect end-pos :not :to-be nil)
(expect end-pos :to-be-greater-than props-pos))))))
(it "tests org-roam-capture--adjust-point-for-capture-type behavior"
;; Simple test to verify the fix for assertion error
(with-temp-buffer
(org-mode)
(insert "#+title: Test\n")
(goto-char (point-max))
;; Mock org-capture-get to return plain type
(cl-letf (((symbol-function 'org-capture-get)
(lambda (prop) (when (eq prop :type) 'plain))))
;; Document current state that previously caused assertion error
(let ((point-before (point))
(at-heading-before (org-at-heading-p)))
;; This should no longer trigger assertion error with our fix
(org-roam-capture--adjust-point-for-capture-type)
(expect point-before :to-be-greater-than 1)
(expect at-heading-before :to-be nil))))))
(provide 'test-org-roam-capture)
;;; test-org-roam-capture.el ends here

View File

@@ -0,0 +1,56 @@
;;; test-org-roam-link-replace.el --- Tests for Org-roam link replacement -*- lexical-binding: t; -*-
;; Copyright (C) 2025
;; Package-Requires: ((buttercup))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Tests for commits fc86387 and 89dfaef - link replacement optimization
;;; Code:
(require 'buttercup)
(require 'org-roam)
(require 'org-roam-node)
(describe "org-roam-link-replace-all optimization"
(before-all
(setq org-roam-directory (expand-file-name "tests/roam-files")
org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory))
(org-roam-db-sync))
(after-all
(org-roam-db--close)
(delete-file org-roam-db-location))
(it "only processes roam: links, not other bracket links"
(with-temp-buffer
(org-mode)
(insert "[[file:test.org][File]]\n[[roam:Foo]]\n[[https://example.com][Web]]")
(let ((replace-count 0)
(original-fn (symbol-function 'org-roam-link-replace-at-point)))
;; Wrap the original function to count calls
(cl-letf (((symbol-function 'org-roam-link-replace-at-point)
(lambda ()
(cl-incf replace-count)
(funcall original-fn))))
(org-roam-link-replace-all)
;; Should only be called once, for the roam: link
(expect replace-count :to-equal 1)
(expect (buffer-string) :to-match "\\[\\[id:.*\\]\\[Foo\\]\\]"))))))
(provide 'test-org-roam-link-replace)
;;; test-org-roam-link-replace.el ends here

View File

@@ -77,10 +77,22 @@
(cd root-directory))
(it "demotes an entire org buffer"
(find-file "tests/roam-files/demoteable.org" nil)
(org-roam-demote-entire-buffer)
(expect (buffer-substring-no-properties (point) (point-max))
:to-equal "* Demoteable\n:PROPERTIES:\n:ID: 97bf31cf-dfee-45d8-87a5-2ae0dabc4734\n:END:\n\n** Demoteable h1\n\n*** Demoteable child\n")))
(let* ((test-file "tests/roam-files/demoteable.org")
(buf (find-file-noselect test-file))
;; Store the original content before any modifications
(original-content (with-current-buffer buf
(buffer-substring-no-properties (point-min) (point-max)))))
(unwind-protect
(with-current-buffer buf
(org-roam-demote-entire-buffer)
(expect (buffer-substring-no-properties (point) (point-max))
:to-equal "* Demoteable\n:PROPERTIES:\n:ID: 97bf31cf-dfee-45d8-87a5-2ae0dabc4734\n:END:\n\n** Demoteable h1\n\n*** Demoteable child\n"))
;; Always restore the original content
(with-current-buffer buf
(erase-buffer)
(insert original-content)
(save-buffer)
(kill-buffer buf))))))
(describe "org-roam--h1-count"
(after-each