mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
@ -34,7 +34,6 @@
|
|||||||
;; (company-org-roam-init)
|
;; (company-org-roam-init)
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(require 'cl-lib)
|
(require 'cl-lib)
|
||||||
(require 'company)
|
(require 'company)
|
||||||
(require 'org-roam)
|
(require 'org-roam)
|
||||||
@ -89,7 +88,7 @@ The string match is case-insensitive."
|
|||||||
Entries with no title do not appear in the completions."
|
Entries with no title do not appear in the completions."
|
||||||
(let ((dir (file-truename org-roam-directory))
|
(let ((dir (file-truename org-roam-directory))
|
||||||
(ht (make-hash-table :test #'equal)))
|
(ht (make-hash-table :test #'equal)))
|
||||||
(dolist (row (org-roam-sql [:select [titles file] :from titles]))
|
(dolist (row (org-roam-db-query [:select [titles file] :from titles]))
|
||||||
(let ((titles (car row))
|
(let ((titles (car row))
|
||||||
(file (cadr row)))
|
(file (cadr row)))
|
||||||
(dolist (title titles)
|
(dolist (title titles)
|
||||||
@ -133,7 +132,7 @@ COMMAND and ARG are as per the documentation of `company-backends'."
|
|||||||
"Conditional enabling of the `company-org-roam' backend."
|
"Conditional enabling of the `company-org-roam' backend."
|
||||||
(when (org-roam--org-roam-file-p (buffer-file-name (buffer-base-buffer)))
|
(when (org-roam--org-roam-file-p (buffer-file-name (buffer-base-buffer)))
|
||||||
(setq-local company-backends
|
(setq-local company-backends
|
||||||
(cons'company-org-roam company-backends))))
|
(cons 'company-org-roam company-backends))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun company-org-roam-init ()
|
(defun company-org-roam-init ()
|
||||||
|
306
org-roam-capture.el
Normal file
306
org-roam-capture.el
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
;;; org-roam-capture.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 1.0.0-rc1
|
||||||
|
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite "1.0.0"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the
|
||||||
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;; Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; This library provides capture functionality for org-roam
|
||||||
|
;;; Code:
|
||||||
|
;;;; Library Requires
|
||||||
|
(require 'org-capture)
|
||||||
|
(require 'dash)
|
||||||
|
(require 's)
|
||||||
|
|
||||||
|
;; Declarations
|
||||||
|
(defvar org-roam-encrypt-files)
|
||||||
|
(defvar org-roam-directory)
|
||||||
|
(declare-function org-roam--file-path-from-id "org-roam")
|
||||||
|
(declare-function org-roam--get-ref-path-completions "org-roam")
|
||||||
|
(declare-function org-roam--format-link "org-roam")
|
||||||
|
|
||||||
|
(defvar org-roam-capture--file-name-default "%<%Y%m%d%H%M%S>"
|
||||||
|
"The default file name format for Org-roam templates.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture--header-default "#+TITLE: ${title}\n"
|
||||||
|
"The default capture header for Org-roam templates.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture--file-path nil
|
||||||
|
"The file path for the Org-roam capture.
|
||||||
|
This variable is set during the Org-roam capture process.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture--info nil
|
||||||
|
"An alist of additional information passed to the Org-roam template.
|
||||||
|
This variable is populated dynamically, and is only non-nil
|
||||||
|
during the Org-roam capture process.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture--context nil
|
||||||
|
"A symbol, that reflects the context for obtaining the exact point in a file.
|
||||||
|
This variable is populated dynamically, and is only active during
|
||||||
|
an Org-roam capture process.
|
||||||
|
|
||||||
|
The `title' context is used in `org-roam-insert' and
|
||||||
|
`org-roam-find-file', where the capture process is triggered upon
|
||||||
|
trying to create a new file without that `title'.
|
||||||
|
|
||||||
|
The `ref' context is used by `org-roam-protocol', where the
|
||||||
|
capture process is triggered upon trying to find or create a new
|
||||||
|
note with the given `ref'.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture--in-process nil
|
||||||
|
"Boolean tracking whether Org-roam captures are in-process.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture-additional-template-props nil
|
||||||
|
"Additional props to be added to the Org-roam template.")
|
||||||
|
|
||||||
|
(defconst org-roam-capture--template-keywords '(:file-name :head)
|
||||||
|
"Keywords used in `org-roam-capture-templates' specific to Org-roam.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture-templates
|
||||||
|
'(("d" "default" plain (function org-roam-capture--get-point)
|
||||||
|
"%?"
|
||||||
|
:file-name "%<%Y%m%d%H%M%S>-${slug}"
|
||||||
|
:head "#+TITLE: ${title}\n"
|
||||||
|
:unnarrowed t))
|
||||||
|
"Capture templates for Org-roam.
|
||||||
|
The capture templates are an extension of
|
||||||
|
`org-capture-templates', and the documentation there also
|
||||||
|
applies.
|
||||||
|
|
||||||
|
`org-capture-templates' are extended in 3 ways:
|
||||||
|
|
||||||
|
1. Template expansion capabilities are extended with additional custom syntax.
|
||||||
|
See `org-roam-capture--fill-template' for more details.
|
||||||
|
|
||||||
|
2. The `:file-name' key is added, which expands to the file-name
|
||||||
|
of the note if it creates a new file. This file-name is
|
||||||
|
relative to `org-roam-directory', and is without the
|
||||||
|
file-extension.
|
||||||
|
|
||||||
|
3. The `:head' key is added, which contains the template that is
|
||||||
|
inserted on initial creation (added only once). This is where
|
||||||
|
insertion of any note metadata should go.")
|
||||||
|
|
||||||
|
(defvar org-roam-capture-ref-templates
|
||||||
|
'(("r" "ref" plain (function org-roam-capture--get-point)
|
||||||
|
""
|
||||||
|
:file-name "${slug}"
|
||||||
|
:head "#+TITLE: ${title}
|
||||||
|
#+ROAM_KEY: ${ref}\n"
|
||||||
|
:unnarrowed t))
|
||||||
|
"The Org-roam templates used during a capture from the roam-ref protocol.
|
||||||
|
Details on how to specify for the template is given in `org-roam-capture-templates'.")
|
||||||
|
|
||||||
|
(defun org-roam-capture--get (keyword)
|
||||||
|
"Gets the value for KEYWORD from the `org-roam-capture-template'."
|
||||||
|
(plist-get (plist-get org-capture-plist :org-roam) keyword))
|
||||||
|
|
||||||
|
(defun org-roam-capture--put (&rest stuff)
|
||||||
|
"Puts properties from STUFF into the `org-roam-capture-template'."
|
||||||
|
(let ((p (plist-get org-capture-plist :org-roam)))
|
||||||
|
(while stuff
|
||||||
|
(setq p (plist-put p
|
||||||
|
(pop stuff) (pop stuff))))
|
||||||
|
(setq org-capture-plist
|
||||||
|
(plist-put org-capture-plist :org-roam p))))
|
||||||
|
|
||||||
|
(defun org-roam-capture--fill-template (str &optional info)
|
||||||
|
"Expands the template STR, returning the string.
|
||||||
|
This is an extension of org-capture's template expansion.
|
||||||
|
|
||||||
|
First, it expands ${var} occurrences in STR, using the INFO alist.
|
||||||
|
If there is a ${var} with no matching var in the alist, the value
|
||||||
|
of var is prompted for via `completing-read'.
|
||||||
|
|
||||||
|
Next, it expands the remaining template string using
|
||||||
|
`org-capture-fill-template'."
|
||||||
|
(-> str
|
||||||
|
(s-format (lambda (key)
|
||||||
|
(or (s--aget info key)
|
||||||
|
(completing-read (format "%s: " key ) nil))) nil)
|
||||||
|
(org-capture-fill-template)))
|
||||||
|
|
||||||
|
(defun org-roam-capture--find-file-h ()
|
||||||
|
"Opens the newly created template file.
|
||||||
|
This is added as a hook to `org-capture-after-finalize-hook'."
|
||||||
|
(when-let ((file-path (org-roam-capture--get :file-path)))
|
||||||
|
(unless org-note-abort
|
||||||
|
(find-file file-path)))
|
||||||
|
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--find-file-h))
|
||||||
|
|
||||||
|
(defun org-roam-capture--insert-link-h ()
|
||||||
|
"Insert the link into the original buffer, after the capture process is done.
|
||||||
|
This is added as a hook to `org-capture-after-finalize-hook'."
|
||||||
|
(when (and (not org-note-abort)
|
||||||
|
(eq (org-roam-capture--get :capture-fn)
|
||||||
|
'org-roam-insert))
|
||||||
|
(when-let ((region (org-roam-capture--get :region))) ;; Remove previously selected text.
|
||||||
|
(delete-region (car region) (cdr region)))
|
||||||
|
(insert (org-roam--format-link (org-roam-capture--get :file-path)
|
||||||
|
(org-roam-capture--get :link-description))))
|
||||||
|
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--insert-link-h))
|
||||||
|
|
||||||
|
(defun org-roam-capture--save-file-maybe-h ()
|
||||||
|
"Save the file conditionally.
|
||||||
|
The file is saved if the original value of :no-save is not t and
|
||||||
|
`org-note-abort' is not t. It is added to
|
||||||
|
`org-capture-after-finalize-hook'."
|
||||||
|
(cond
|
||||||
|
((and (org-roam-capture--get :new-file)
|
||||||
|
org-note-abort)
|
||||||
|
(with-current-buffer (org-capture-get :buffer)
|
||||||
|
(set-buffer-modified-p nil)
|
||||||
|
(kill-buffer)))
|
||||||
|
((and (not (org-roam-capture--get :orig-no-save))
|
||||||
|
(not org-note-abort))
|
||||||
|
(with-current-buffer (org-capture-get :buffer)
|
||||||
|
(save-buffer))))
|
||||||
|
(remove-hook 'org-capture-after-finalize-hook #'org-roam-capture--save-file-maybe-h))
|
||||||
|
|
||||||
|
(defun org-roam-capture--new-file ()
|
||||||
|
"Return the path to the new file during an Org-roam capture.
|
||||||
|
|
||||||
|
This function reads the file-name attribute of the currently
|
||||||
|
active Org-roam template.
|
||||||
|
|
||||||
|
If the file path already exists, it throw an error.
|
||||||
|
|
||||||
|
Else, to insert the header content in the file, `org-capture'
|
||||||
|
prepends the `:head' property of the Org-roam capture template.
|
||||||
|
|
||||||
|
To prevent the creation of a new file if the capture process is
|
||||||
|
aborted, we do the following:
|
||||||
|
|
||||||
|
1. Save the original value of the capture template's :no-save.
|
||||||
|
|
||||||
|
2. Set the capture template's :no-save to t.
|
||||||
|
|
||||||
|
3. Add a function on `org-capture-after-finalize-hook' that saves
|
||||||
|
the file if the original value of :no-save is not t and
|
||||||
|
`org-note-abort' is not t."
|
||||||
|
(let* ((name-templ (or (org-roam-capture--get :file-name)
|
||||||
|
org-roam-capture--file-name-default))
|
||||||
|
(new-id (s-trim (org-roam-capture--fill-template
|
||||||
|
name-templ
|
||||||
|
org-roam-capture--info)))
|
||||||
|
(file-path (org-roam--file-path-from-id new-id))
|
||||||
|
(roam-head (or (org-roam-capture--get :head)
|
||||||
|
org-roam-capture--header-default))
|
||||||
|
(org-template (org-capture-get :template))
|
||||||
|
(roam-template (concat roam-head org-template)))
|
||||||
|
(when (file-exists-p file-path)
|
||||||
|
(error (format "File exists at %s, aborting" file-path)))
|
||||||
|
(org-roam-capture--put :orig-no-save (org-capture-get :no-save)
|
||||||
|
:new-file t)
|
||||||
|
(org-capture-put :template
|
||||||
|
;; Fixes org-capture-place-plain-text throwing 'invalid search bound'
|
||||||
|
;; when both :unnarowed t and "%?" is missing from the template string;
|
||||||
|
;; may become unnecessary when the upstream bug is fixed
|
||||||
|
(if (s-contains-p "%?" roam-template)
|
||||||
|
roam-template
|
||||||
|
(concat roam-template "%?"))
|
||||||
|
:type 'plain
|
||||||
|
:no-save t)
|
||||||
|
file-path))
|
||||||
|
|
||||||
|
(defun org-roam-capture--expand-template ()
|
||||||
|
"Expand capture template with information from `org-roam-capture--info'."
|
||||||
|
(org-capture-put :template
|
||||||
|
(s-format (org-capture-get :template)
|
||||||
|
(lambda (key)
|
||||||
|
(or (s--aget org-roam-capture--info key)
|
||||||
|
(completing-read (format "%s: " key ) nil))) nil)))
|
||||||
|
|
||||||
|
(defun org-roam-capture--get-point ()
|
||||||
|
"Return exact point to file for org-capture-template.
|
||||||
|
The file to use is dependent on the context:
|
||||||
|
|
||||||
|
If the search is via title, it is assumed that the file does not
|
||||||
|
yet exist, and Org-roam will attempt to create new file.
|
||||||
|
|
||||||
|
If the search is via ref, it is matched against the Org-roam database.
|
||||||
|
If there is no file with that ref, a file with that ref is created.
|
||||||
|
|
||||||
|
This function is used solely in Org-roam's capture templates: see
|
||||||
|
`org-roam-capture-templates'."
|
||||||
|
(let ((file-path (pcase org-roam-capture--context
|
||||||
|
('title
|
||||||
|
(org-roam-capture--new-file))
|
||||||
|
('ref
|
||||||
|
(let ((completions (org-roam--get-ref-path-completions))
|
||||||
|
(ref (cdr (assoc 'ref org-roam-capture--info))))
|
||||||
|
(or (cdr (assoc ref completions))
|
||||||
|
(org-roam-capture--new-file))))
|
||||||
|
(_ (error "Invalid org-roam-capture-context")))))
|
||||||
|
(org-roam-capture--expand-template)
|
||||||
|
(org-roam-capture--put :file-path file-path)
|
||||||
|
(while org-roam-capture-additional-template-props
|
||||||
|
(let ((prop (pop org-roam-capture-additional-template-props))
|
||||||
|
(val (pop org-roam-capture-additional-template-props)))
|
||||||
|
(org-roam-capture--put prop val)))
|
||||||
|
(set-buffer (org-capture-target-buffer file-path))
|
||||||
|
(widen)
|
||||||
|
(goto-char (point-max))))
|
||||||
|
|
||||||
|
(defun org-roam-capture--cleanup-h ()
|
||||||
|
"Cleans up after an Org-roam capture process."
|
||||||
|
(setq org-roam-capture--in-process nil))
|
||||||
|
|
||||||
|
(defun org-roam-capture--convert-template (template)
|
||||||
|
"Convert TEMPLATE from Org-roam syntax to `org-capture-templates' syntax."
|
||||||
|
(let* ((copy (copy-tree template))
|
||||||
|
converted
|
||||||
|
org-roam-plist
|
||||||
|
key
|
||||||
|
val)
|
||||||
|
;;put positional args on converted template
|
||||||
|
(dotimes (_ 5)
|
||||||
|
(push (pop copy) converted))
|
||||||
|
(while (setq key (pop copy)
|
||||||
|
val (pop copy))
|
||||||
|
(if (member key org-roam-capture--template-keywords)
|
||||||
|
(progn
|
||||||
|
(push val org-roam-plist)
|
||||||
|
(push key org-roam-plist))
|
||||||
|
(push key converted)
|
||||||
|
(push val converted)))
|
||||||
|
(append (nreverse converted) `(:org-roam ,org-roam-plist))))
|
||||||
|
|
||||||
|
(defun org-roam-capture (&optional goto keys)
|
||||||
|
"Create a new file, and return the path to the edited file.
|
||||||
|
The templates are defined at `org-roam-capture-templates'. The
|
||||||
|
GOTO and KEYS argument have the same functionality as
|
||||||
|
`org-capture'."
|
||||||
|
(let ((org-capture-templates (mapcar #'org-roam-capture--convert-template org-roam-capture-templates)))
|
||||||
|
(when (= (length org-capture-templates) 1)
|
||||||
|
(setq keys (caar org-capture-templates)))
|
||||||
|
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--save-file-maybe-h)
|
||||||
|
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--cleanup-h 10)
|
||||||
|
(setq org-roam-capture--in-process t)
|
||||||
|
(org-capture goto keys)))
|
||||||
|
|
||||||
|
(provide 'org-roam-capture)
|
||||||
|
|
||||||
|
;;; org-roam-capture.el ends here
|
119
org-roam-completion.el
Normal file
119
org-roam-completion.el
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
;;; org-roam-completion.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 1.0.0-rc1
|
||||||
|
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite "1.0.0"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the
|
||||||
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;; Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; This library provides completion for org-roam.
|
||||||
|
;;; Code:
|
||||||
|
;;;; Library Requires
|
||||||
|
(require 'cl-lib)
|
||||||
|
(require 's)
|
||||||
|
|
||||||
|
(defvar helm-pattern)
|
||||||
|
(declare-function helm "ext:helm")
|
||||||
|
(declare-function helm-build-sync-source "ext:helm-source" (name &rest args) t)
|
||||||
|
|
||||||
|
(defcustom org-roam-completion-system 'default
|
||||||
|
"The completion system to be used by `org-roam'."
|
||||||
|
:type '(radio
|
||||||
|
(const :tag "Default" default)
|
||||||
|
(const :tag "Ido" ido)
|
||||||
|
(const :tag "Ivy" ivy)
|
||||||
|
(const :tag "Helm" helm)
|
||||||
|
(function :tag "Custom function"))
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-completion-fuzzy-match nil
|
||||||
|
"Whether to fuzzy match Org-roam's completion candidates."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defun org-roam-completion--helm-candidate-transformer (candidates _source)
|
||||||
|
"Transforms CANDIDATES for Helm-based completing read.
|
||||||
|
SOURCE is not used."
|
||||||
|
(let ((prefix (propertize "[?] "
|
||||||
|
'face 'helm-ff-prefix)))
|
||||||
|
(cons (propertize helm-pattern
|
||||||
|
'display (concat prefix helm-pattern))
|
||||||
|
candidates)))
|
||||||
|
|
||||||
|
(cl-defun org-roam-completion--completing-read (prompt choices &key
|
||||||
|
require-match initial-input
|
||||||
|
action)
|
||||||
|
"Present a PROMPT with CHOICES and optional INITIAL-INPUT.
|
||||||
|
If REQUIRE-MATCH is t, the user must select one of the CHOICES.
|
||||||
|
Return user choice."
|
||||||
|
(let (res)
|
||||||
|
(setq res
|
||||||
|
(cond
|
||||||
|
((eq org-roam-completion-system 'ido)
|
||||||
|
(let ((candidates (mapcar #'car choices)))
|
||||||
|
(ido-completing-read prompt candidates nil require-match initial-input)))
|
||||||
|
((eq org-roam-completion-system 'default)
|
||||||
|
(completing-read prompt choices nil require-match initial-input))
|
||||||
|
((eq org-roam-completion-system 'ivy)
|
||||||
|
(if (fboundp 'ivy-read)
|
||||||
|
(ivy-read prompt choices
|
||||||
|
:initial-input initial-input
|
||||||
|
:require-match require-match
|
||||||
|
:action (prog1 action
|
||||||
|
(setq action nil))
|
||||||
|
:caller 'org-roam--completing-read
|
||||||
|
:re-builder (if org-roam-completion-fuzzy-match 'ivy--regex-fuzzy
|
||||||
|
'regexp-quote))
|
||||||
|
(user-error "Please install ivy from \
|
||||||
|
https://github.com/abo-abo/swiper")))
|
||||||
|
((eq org-roam-completion-system 'helm)
|
||||||
|
(unless (and (fboundp 'helm)
|
||||||
|
(fboundp 'helm-build-sync-source))
|
||||||
|
(user-error "Please install helm from \
|
||||||
|
https://github.com/emacs-helm/helm"))
|
||||||
|
(let ((source (helm-build-sync-source prompt
|
||||||
|
:candidates (mapcar #'car choices)
|
||||||
|
:filtered-candidate-transformer
|
||||||
|
(and (not require-match)
|
||||||
|
#'org-roam-completion--helm-candidate-transformer)
|
||||||
|
:fuzzy-match org-roam-completion-fuzzy-match))
|
||||||
|
(buf (concat "*org-roam "
|
||||||
|
(s-downcase (s-chop-suffix ":" (s-trim prompt)))
|
||||||
|
"*")))
|
||||||
|
(or (helm :sources source
|
||||||
|
:action (if action
|
||||||
|
(prog1 action
|
||||||
|
(setq action nil))
|
||||||
|
#'identity)
|
||||||
|
:prompt prompt
|
||||||
|
:input initial-input
|
||||||
|
:buffer buf)
|
||||||
|
(keyboard-quit))))))
|
||||||
|
(if action
|
||||||
|
(funcall action res)
|
||||||
|
res)))
|
||||||
|
|
||||||
|
(provide 'org-roam-completion)
|
||||||
|
|
||||||
|
;;; org-roam-completion.el ends here
|
364
org-roam-db.el
Normal file
364
org-roam-db.el
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
;;; org-roam-db.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 1.0.0-rc1
|
||||||
|
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite "1.0.0"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the
|
||||||
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;; Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; This library is provides the underlying database api to org-roam
|
||||||
|
;;
|
||||||
|
;;; Code:
|
||||||
|
;;;; Library Requires
|
||||||
|
(require 'emacsql)
|
||||||
|
(require 'emacsql-sqlite)
|
||||||
|
(require 'org-roam-macs)
|
||||||
|
|
||||||
|
(defvar org-roam-directory)
|
||||||
|
(defvar org-roam-verbose)
|
||||||
|
|
||||||
|
(declare-function org-roam--extract-titles "org-roam")
|
||||||
|
(declare-function org-roam--extract-ref "org-roam")
|
||||||
|
(declare-function org-roam--extract-links "org-roam")
|
||||||
|
(declare-function org-roam--maybe-update-buffer "org-roam")
|
||||||
|
(declare-function org-roam--list-files "org-roam")
|
||||||
|
|
||||||
|
;;;; Options
|
||||||
|
(defcustom org-roam-db-location nil
|
||||||
|
"Location of the Org-roam database.
|
||||||
|
If this is non-nil, the Org-roam sqlite database is saved here.
|
||||||
|
|
||||||
|
It is the user's responsibility to set this correctly, especially
|
||||||
|
when used with multiple Org-roam instances."
|
||||||
|
:type 'string
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defconst org-roam-db--version 1)
|
||||||
|
(defconst org-roam-db--sqlite-available-p
|
||||||
|
(with-demoted-errors "Org-roam initialization: %S"
|
||||||
|
(emacsql-sqlite-ensure-binary)
|
||||||
|
t))
|
||||||
|
|
||||||
|
(defvaralias 'org-roam--db-connection 'org-roam-db--connection)
|
||||||
|
(make-obsolete-variable 'org-roam--db-connection 'org-roam-db--connection "2020/03/28")
|
||||||
|
(defvar org-roam-db--connection (make-hash-table :test #'equal)
|
||||||
|
"Database connection to Org-roam database.")
|
||||||
|
|
||||||
|
|
||||||
|
;;;; Core Functions
|
||||||
|
(defalias 'org-roam--get-db 'org-roam-db--get)
|
||||||
|
(make-obsolete 'org-roam--get-db 'org-roam-db--get "2020/03/28")
|
||||||
|
(defun org-roam-db--get ()
|
||||||
|
"Return the sqlite db file."
|
||||||
|
(interactive "P")
|
||||||
|
(or org-roam-db-location
|
||||||
|
(expand-file-name "org-roam.db" org-roam-directory)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun org-roam-db--get-connection ()
|
||||||
|
"Return the database connection, if any."
|
||||||
|
(gethash (file-truename org-roam-directory)
|
||||||
|
org-roam-db--connection))
|
||||||
|
|
||||||
|
(defun org-roam-db ()
|
||||||
|
"Entrypoint to the Org-roam sqlite database.
|
||||||
|
Initializes and stores the database, and the database connection.
|
||||||
|
Performs a database upgrade when required."
|
||||||
|
(unless (and (org-roam-db--get-connection)
|
||||||
|
(emacsql-live-p (org-roam-db--get-connection)))
|
||||||
|
(let* ((db-file (org-roam-db--get))
|
||||||
|
(init-db (not (file-exists-p db-file))))
|
||||||
|
(make-directory (file-name-directory db-file) t)
|
||||||
|
(let ((conn (emacsql-sqlite db-file)))
|
||||||
|
(set-process-query-on-exit-flag (emacsql-process conn) nil)
|
||||||
|
(puthash (file-truename org-roam-directory)
|
||||||
|
conn
|
||||||
|
org-roam-db--connection)
|
||||||
|
(when init-db
|
||||||
|
(org-roam-db--init conn))
|
||||||
|
(let* ((version (caar (emacsql conn "PRAGMA user_version")))
|
||||||
|
(version (org-roam-db--maybe-update conn version)))
|
||||||
|
(cond
|
||||||
|
((> version org-roam-db--version)
|
||||||
|
(emacsql-close conn)
|
||||||
|
(user-error
|
||||||
|
"The Org-roam database was created with a newer Org-roam version. "
|
||||||
|
"You need to update the Org-roam package"))
|
||||||
|
((< version org-roam-db--version)
|
||||||
|
(emacsql-close conn)
|
||||||
|
(error "BUG: The Org-roam database scheme changed %s"
|
||||||
|
"and there is no upgrade path")))))))
|
||||||
|
(org-roam-db--get-connection))
|
||||||
|
|
||||||
|
;;;; Entrypoint: (org-roam-db-query)
|
||||||
|
(defalias 'org-roam-sql 'org-roam-db-query)
|
||||||
|
(make-obsolete 'org-roam-sql 'org-roam-db-query "2020/03/28")
|
||||||
|
(defun org-roam-db-query (sql &rest args)
|
||||||
|
"Run SQL query on Org-roam database with ARGS.
|
||||||
|
SQL can be either the emacsql vector representation, or a string."
|
||||||
|
(if (stringp sql)
|
||||||
|
(emacsql (org-roam-db) (apply #'format sql args))
|
||||||
|
(apply #'emacsql (org-roam-db) sql args)))
|
||||||
|
|
||||||
|
;;;; Schemata
|
||||||
|
(defconst org-roam-db--table-schemata
|
||||||
|
'((files
|
||||||
|
[(file :unique :primary-key)
|
||||||
|
(hash :not-null)
|
||||||
|
(last-modified :not-null)
|
||||||
|
])
|
||||||
|
|
||||||
|
(file-links
|
||||||
|
[(file-from :not-null)
|
||||||
|
(file-to :not-null)
|
||||||
|
(properties :not-null)])
|
||||||
|
|
||||||
|
(titles
|
||||||
|
[
|
||||||
|
(file :not-null)
|
||||||
|
titles])
|
||||||
|
|
||||||
|
(refs
|
||||||
|
[(ref :unique :not-null)
|
||||||
|
(file :not-null)])))
|
||||||
|
|
||||||
|
(defun org-roam-db--init (db)
|
||||||
|
"Initialize database DB with the correct schema and user version."
|
||||||
|
(emacsql-with-transaction db
|
||||||
|
(pcase-dolist (`(,table . ,schema) org-roam-db--table-schemata)
|
||||||
|
(emacsql db [:create-table $i1 $S2] table schema))
|
||||||
|
(emacsql db (format "PRAGMA user_version = %s" org-roam-db--version))))
|
||||||
|
|
||||||
|
(defun org-roam-db--maybe-update (db version)
|
||||||
|
"Upgrades the database schema for DB, if VERSION is old."
|
||||||
|
(emacsql-with-transaction db
|
||||||
|
'ignore
|
||||||
|
;; Do nothing now
|
||||||
|
version))
|
||||||
|
|
||||||
|
(defun org-roam-db--close (&optional db)
|
||||||
|
"Closes the database connection for database DB.
|
||||||
|
If DB is nil, closes the database connection for the database in
|
||||||
|
the current `org-roam-directory'."
|
||||||
|
(unless db
|
||||||
|
(setq db (org-roam-db--get-connection)))
|
||||||
|
(when (and db (emacsql-live-p db))
|
||||||
|
(emacsql-close db)))
|
||||||
|
|
||||||
|
(defun org-roam-db--close-all ()
|
||||||
|
"Closes all database connections made by Org-roam."
|
||||||
|
(dolist (conn (hash-table-values org-roam-db--connection))
|
||||||
|
(org-roam-db--close conn)))
|
||||||
|
|
||||||
|
;;;; Database API
|
||||||
|
;;;;; Initialization
|
||||||
|
(defun org-roam-db--initialized-p ()
|
||||||
|
"Whether the cache has been initialized."
|
||||||
|
(and (file-exists-p (org-roam-db--get))
|
||||||
|
(> (caar (org-roam-db-query [:select (funcall count) :from titles]))
|
||||||
|
0)))
|
||||||
|
|
||||||
|
(defun org-roam-db--ensure-built ()
|
||||||
|
"Ensures that Org-roam cache is built."
|
||||||
|
(unless (org-roam-db--initialized-p)
|
||||||
|
(error "[Org-roam] your cache isn't built yet! Please run org-roam-db-build-cache")))
|
||||||
|
|
||||||
|
;;;;; Clearing
|
||||||
|
(defalias 'org-roam--db-clear 'org-roam-db--clear)
|
||||||
|
(make-obsolete 'org-roam--db-clear 'org-roam-db--clear "2020/03/28")
|
||||||
|
(defun org-roam-db--clear ()
|
||||||
|
"Clears all entries in the caches."
|
||||||
|
(interactive)
|
||||||
|
(when (file-exists-p (org-roam-db--get))
|
||||||
|
(org-roam-db-query [:delete :from files])
|
||||||
|
(org-roam-db-query [:delete :from titles])
|
||||||
|
(org-roam-db-query [:delete :from file-links])
|
||||||
|
(org-roam-db-query [:delete :from refs])))
|
||||||
|
|
||||||
|
|
||||||
|
(defun org-roam-db--clear-file (&optional filepath)
|
||||||
|
"Remove any related links to the file at FILEPATH.
|
||||||
|
This is equivalent to removing the node from the graph."
|
||||||
|
(let* ((path (or filepath
|
||||||
|
(buffer-file-name)))
|
||||||
|
(file (file-truename path)))
|
||||||
|
(org-roam-db-query [:delete :from files
|
||||||
|
:where (= file $s1)]
|
||||||
|
file)
|
||||||
|
(org-roam-db-query [:delete :from file-links
|
||||||
|
:where (= file-from $s1)]
|
||||||
|
file)
|
||||||
|
(org-roam-db-query [:delete :from titles
|
||||||
|
:where (= file $s1)]
|
||||||
|
file)
|
||||||
|
(org-roam-db-query [:delete :from refs
|
||||||
|
:where (= file $s1)]
|
||||||
|
file)))
|
||||||
|
|
||||||
|
;;;;; Insertion
|
||||||
|
(defun org-roam-db--insert-links (links)
|
||||||
|
"Insert LINKS into the Org-roam cache."
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into file-links
|
||||||
|
:values $v1]
|
||||||
|
links))
|
||||||
|
|
||||||
|
(defun org-roam-db--insert-titles (file titles)
|
||||||
|
"Insert TITLES for a FILE into the Org-roam cache."
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into titles
|
||||||
|
:values $v1]
|
||||||
|
(list (vector file titles))))
|
||||||
|
|
||||||
|
(defun org-roam-db--insert-ref (file ref)
|
||||||
|
"Insert REF for FILE into the Org-roam cache."
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into refs
|
||||||
|
:values $v1]
|
||||||
|
(list (vector ref file))))
|
||||||
|
|
||||||
|
;;;;; Fetching
|
||||||
|
(defun org-roam-db--get-current-files ()
|
||||||
|
"Return a hash-table of file to the hash of its file contents."
|
||||||
|
(let* ((current-files (org-roam-db-query [:select * :from files]))
|
||||||
|
(ht (make-hash-table :test #'equal)))
|
||||||
|
(dolist (row current-files)
|
||||||
|
(puthash (car row) (cadr row) ht))
|
||||||
|
ht))
|
||||||
|
|
||||||
|
(defun org-roam-db--get-titles (file)
|
||||||
|
"Return the titles of FILE from the cache."
|
||||||
|
(caar (org-roam-db-query [:select [titles] :from titles
|
||||||
|
:where (= file $s1)]
|
||||||
|
file
|
||||||
|
:limit 1)))
|
||||||
|
|
||||||
|
;;;;; Updating
|
||||||
|
(defun org-roam-db--update-titles ()
|
||||||
|
"Update the title of the current buffer into the cache."
|
||||||
|
(let ((file (file-truename (buffer-file-name))))
|
||||||
|
(org-roam-db-query [:delete :from titles
|
||||||
|
:where (= file $s1)]
|
||||||
|
file)
|
||||||
|
(org-roam-db--insert-titles file (org-roam--extract-titles))))
|
||||||
|
|
||||||
|
(defun org-roam-db--update-refs ()
|
||||||
|
"Update the ref of the current buffer into the cache."
|
||||||
|
(let ((file (file-truename (buffer-file-name))))
|
||||||
|
(org-roam-db-query [:delete :from refs
|
||||||
|
:where (= file $s1)]
|
||||||
|
file)
|
||||||
|
(when-let ((ref (org-roam--extract-ref)))
|
||||||
|
(org-roam-db--insert-ref file ref))))
|
||||||
|
|
||||||
|
(defun org-roam-db--update-cache-links ()
|
||||||
|
"Update the file links of the current buffer in the cache."
|
||||||
|
(let ((file (file-truename (buffer-file-name))))
|
||||||
|
(org-roam-db-query [:delete :from file-links
|
||||||
|
:where (= file-from $s1)]
|
||||||
|
file)
|
||||||
|
(when-let ((links (org-roam--extract-links)))
|
||||||
|
(org-roam-db--insert-links links))))
|
||||||
|
|
||||||
|
(defun org-roam-db--update-file (&optional file-path)
|
||||||
|
"Update Org-roam cache for FILE-PATH."
|
||||||
|
(let (buf)
|
||||||
|
(if file-path
|
||||||
|
(setq buf (find-file-noselect file-path))
|
||||||
|
(setq buf (current-buffer)))
|
||||||
|
(with-current-buffer buf
|
||||||
|
(save-excursion
|
||||||
|
(org-roam-db--update-titles)
|
||||||
|
(org-roam-db--update-refs)
|
||||||
|
(org-roam-db--update-cache-links)
|
||||||
|
(org-roam--maybe-update-buffer :redisplay t)))))
|
||||||
|
|
||||||
|
;;;;; org-roam-db-build-cache
|
||||||
|
(defalias 'org-roam-build-cache 'org-roam-db-build-cache)
|
||||||
|
(make-obsolete 'org-roam-build-cache 'org-roam-db-build-cache "2020/03/28")
|
||||||
|
(defun org-roam-db-build-cache ()
|
||||||
|
"Build the cache for `org-roam-directory'."
|
||||||
|
(interactive)
|
||||||
|
(org-roam-db--close) ;; Force a reconnect
|
||||||
|
(org-roam-db) ;; To initialize the database, no-op if already initialized
|
||||||
|
(let* ((org-roam-files (org-roam--list-files org-roam-directory))
|
||||||
|
(current-files (org-roam-db--get-current-files))
|
||||||
|
(time (current-time))
|
||||||
|
all-files all-links all-titles all-refs)
|
||||||
|
(dolist (file org-roam-files)
|
||||||
|
(org-roam--with-temp-buffer
|
||||||
|
(insert-file-contents file)
|
||||||
|
(let ((contents-hash (secure-hash 'sha1 (current-buffer))))
|
||||||
|
(unless (string= (gethash file current-files)
|
||||||
|
contents-hash)
|
||||||
|
(org-roam-db--clear-file file)
|
||||||
|
(setq all-files
|
||||||
|
(cons (vector file contents-hash time) all-files))
|
||||||
|
(when-let (links (org-roam--extract-links file))
|
||||||
|
(setq all-links (append links all-links)))
|
||||||
|
(let ((titles (org-roam--extract-titles)))
|
||||||
|
(setq all-titles (cons (vector file titles) all-titles)))
|
||||||
|
(when-let ((ref (org-roam--extract-ref)))
|
||||||
|
(setq all-refs (cons (vector ref file) all-refs))))
|
||||||
|
(remhash file current-files))))
|
||||||
|
(dolist (file (hash-table-keys current-files))
|
||||||
|
;; These files are no longer around, remove from cache...
|
||||||
|
(org-roam-db--clear-file file))
|
||||||
|
(when all-files
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into files
|
||||||
|
:values $v1]
|
||||||
|
all-files))
|
||||||
|
(when all-links
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into file-links
|
||||||
|
:values $v1]
|
||||||
|
all-links))
|
||||||
|
(when all-titles
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into titles
|
||||||
|
:values $v1]
|
||||||
|
all-titles))
|
||||||
|
(when all-refs
|
||||||
|
(org-roam-db-query
|
||||||
|
[:insert :into refs
|
||||||
|
:values $v1]
|
||||||
|
all-refs))
|
||||||
|
(let ((stats (list :files (length all-files)
|
||||||
|
:links (length all-links)
|
||||||
|
:titles (length all-titles)
|
||||||
|
:refs (length all-refs)
|
||||||
|
:deleted (length (hash-table-keys current-files)))))
|
||||||
|
(when org-roam-verbose
|
||||||
|
(message "files: %s, links: %s, titles: %s, refs: %s, deleted: %s"
|
||||||
|
(plist-get stats :files)
|
||||||
|
(plist-get stats :links)
|
||||||
|
(plist-get stats :titles)
|
||||||
|
(plist-get stats :refs)
|
||||||
|
(plist-get stats :deleted)))
|
||||||
|
stats)))
|
||||||
|
|
||||||
|
(provide 'org-roam-db)
|
||||||
|
|
||||||
|
;;; org-roam-db.el ends here
|
178
org-roam-graph.el
Normal file
178
org-roam-graph.el
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
;;; org-roam-graph.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 1.0.0-rc1
|
||||||
|
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite "1.0.0"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the
|
||||||
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;; Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; This library provides graphing functionality for org-roam.
|
||||||
|
;;
|
||||||
|
;;; Code:
|
||||||
|
(require 'xml) ;xml-escape-string
|
||||||
|
(require 's) ;s-truncate, s-replace
|
||||||
|
(require 'org-roam-macs)
|
||||||
|
|
||||||
|
;;;; Declarations
|
||||||
|
(defvar org-roam-directory)
|
||||||
|
(declare-function org-roam-db--ensure-built "org-roam-db")
|
||||||
|
(declare-function org-roam-db-query "org-roam-db")
|
||||||
|
(declare-function org-roam--path-to-slug "org-roam")
|
||||||
|
|
||||||
|
;;;; Options
|
||||||
|
(defcustom org-roam-graph-viewer (executable-find "firefox")
|
||||||
|
"Path to executable for viewing SVG."
|
||||||
|
:type 'string
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-graph-executable (executable-find "dot")
|
||||||
|
"Path to grapher executable."
|
||||||
|
:type 'string
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-grapher-extra-options nil
|
||||||
|
"Extra options when constructing the grapher graph.
|
||||||
|
Example:
|
||||||
|
'((\"rankdir\" . \"LR\"))"
|
||||||
|
:type '(alist)
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-graph-max-title-length 100
|
||||||
|
"Maximum length of titles in graph nodes."
|
||||||
|
:type 'number
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-graph-exclude-matcher nil
|
||||||
|
"Matcher for excluding nodes from the generated graph.
|
||||||
|
Any nodes and links for file paths matching this string is
|
||||||
|
excluded from the graph.
|
||||||
|
|
||||||
|
If value is a string, the string is the only matcher.
|
||||||
|
|
||||||
|
If value is a list, all file paths matching any of the strings
|
||||||
|
are excluded."
|
||||||
|
:type '(choice
|
||||||
|
(string :tag "Matcher")
|
||||||
|
(list :tag "Matchers"))
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
(defcustom org-roam-graph-node-shape "ellipse"
|
||||||
|
"Shape of graph nodes."
|
||||||
|
:type 'string
|
||||||
|
:group 'org-roam)
|
||||||
|
|
||||||
|
;;;; Functions
|
||||||
|
(defun org-roam-graph--expand-matcher (col &optional negate where)
|
||||||
|
"Return the exclusion regexp from `org-roam-graph-exclude-matcher'.
|
||||||
|
COL is the symbol to be matched against. if NEGATE, add :not to sql query.
|
||||||
|
set WHERE to true if WHERE query already exists."
|
||||||
|
(let ((matchers (cond ((null org-roam-graph-exclude-matcher)
|
||||||
|
nil)
|
||||||
|
((stringp org-roam-graph-exclude-matcher)
|
||||||
|
(cons (concat "%" org-roam-graph-exclude-matcher "%") nil))
|
||||||
|
((listp org-roam-graph-exclude-matcher)
|
||||||
|
(mapcar (lambda (m)
|
||||||
|
(concat "%" m "%"))
|
||||||
|
org-roam-graph-exclude-matcher))
|
||||||
|
(t
|
||||||
|
(error "Invalid org-roam-graph-exclude-matcher"))))
|
||||||
|
res)
|
||||||
|
(dolist (match matchers)
|
||||||
|
(if where
|
||||||
|
(push :and res)
|
||||||
|
(push :where res)
|
||||||
|
(setq where t))
|
||||||
|
(push col res)
|
||||||
|
(when negate
|
||||||
|
(push :not res))
|
||||||
|
(push :like res)
|
||||||
|
(push match res))
|
||||||
|
(nreverse res)))
|
||||||
|
|
||||||
|
(defun org-roam-graph--build ()
|
||||||
|
"Build the grapher string.
|
||||||
|
The Org-roam database titles table is read, to obtain the list of titles.
|
||||||
|
The file-links table is then read to obtain all directed links, and formatted
|
||||||
|
into a digraph."
|
||||||
|
(org-roam-db--ensure-built)
|
||||||
|
(org-roam--with-temp-buffer
|
||||||
|
(let* ((node-query `[:select [file titles]
|
||||||
|
:from titles
|
||||||
|
,@(org-roam-graph--expand-matcher 'file t)])
|
||||||
|
(nodes (org-roam-db-query node-query))
|
||||||
|
(edges-query `[:select :distinct [file-to file-from]
|
||||||
|
:from file-links
|
||||||
|
,@(org-roam-graph--expand-matcher 'file-to t)
|
||||||
|
,@(org-roam-graph--expand-matcher 'file-from t t)])
|
||||||
|
(edges (org-roam-db-query edges-query)))
|
||||||
|
(insert "digraph \"org-roam\" {\n")
|
||||||
|
(dolist (option org-roam-grapher-extra-options)
|
||||||
|
(insert (concat (car option)
|
||||||
|
"="
|
||||||
|
(cdr option)
|
||||||
|
";\n")))
|
||||||
|
(dolist (node nodes)
|
||||||
|
(let* ((file (xml-escape-string (car node)))
|
||||||
|
(title (or (caadr node)
|
||||||
|
(org-roam--path-to-slug file)))
|
||||||
|
(shortened-title (s-truncate org-roam-graph-max-title-length title)))
|
||||||
|
(insert
|
||||||
|
(format " \"%s\" [label=\"%s\", shape=%s, URL=\"org-protocol://roam-file?file=%s\", tooltip=\"%s\"];\n"
|
||||||
|
file
|
||||||
|
(s-replace "\"" "\\\"" shortened-title)
|
||||||
|
org-roam-graph-node-shape
|
||||||
|
(url-hexify-string file)
|
||||||
|
(xml-escape-string title)))))
|
||||||
|
(dolist (edge edges)
|
||||||
|
(insert (format " \"%s\" -> \"%s\";\n"
|
||||||
|
(xml-escape-string (car edge))
|
||||||
|
(xml-escape-string (cadr edge)))))
|
||||||
|
(insert "}")
|
||||||
|
(buffer-string))))
|
||||||
|
|
||||||
|
(defalias 'org-roam-show-graph 'org-roam-graph-show)
|
||||||
|
(make-obsolete 'org-roam-show-graph 'org-roam-graph-show "2020/03/28")
|
||||||
|
(defun org-roam-graph-show (&optional prefix)
|
||||||
|
"Generate and displays the Org-roam graph using `org-roam-graph-viewer'.
|
||||||
|
If PREFIX, then the graph is generated but the viewer is not invoked."
|
||||||
|
(interactive "P")
|
||||||
|
(declare (indent 0))
|
||||||
|
(unless org-roam-graph-executable
|
||||||
|
(user-error "Can't find %s executable. Please check if it is in your path"
|
||||||
|
org-roam-graph-executable))
|
||||||
|
(let ((temp-dot (expand-file-name "graph.dot" temporary-file-directory))
|
||||||
|
(temp-graph (expand-file-name "graph.svg" temporary-file-directory))
|
||||||
|
(graph (org-roam-graph--build)))
|
||||||
|
(with-temp-file temp-dot
|
||||||
|
(insert graph))
|
||||||
|
(call-process org-roam-graph-executable nil 0 nil temp-dot "-Tsvg" "-o" temp-graph)
|
||||||
|
(unless prefix
|
||||||
|
(if (and org-roam-graph-viewer (executable-find org-roam-graph-viewer))
|
||||||
|
(call-process org-roam-graph-viewer nil 0 nil temp-graph)
|
||||||
|
(view-file temp-graph)))))
|
||||||
|
|
||||||
|
|
||||||
|
(provide 'org-roam-graph)
|
||||||
|
|
||||||
|
;;; org-roam-graph.el ends here
|
48
org-roam-macs.el
Normal file
48
org-roam-macs.el
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
;;; org-roam-macs.el --- Roam Research replica with Org-mode -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright © 2020 Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
|
||||||
|
;; Author: Jethro Kuan <jethrokuan95@gmail.com>
|
||||||
|
;; URL: https://github.com/jethrokuan/org-roam
|
||||||
|
;; Keywords: org-mode, roam, convenience
|
||||||
|
;; Version: 1.0.0-rc1
|
||||||
|
;; Package-Requires: ((emacs "26.1") (dash "2.13") (f "0.17.2") (s "1.12.0") (org "9.3") (emacsql "3.0.0") (emacsql-sqlite "1.0.0"))
|
||||||
|
|
||||||
|
;; This file is NOT part of GNU Emacs.
|
||||||
|
|
||||||
|
;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the
|
||||||
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
;; Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; This library implements macros used throughout org-roam
|
||||||
|
;;
|
||||||
|
;;
|
||||||
|
;;; Code:
|
||||||
|
;;;; Library Requires
|
||||||
|
|
||||||
|
(defmacro org-roam--with-temp-buffer (&rest body)
|
||||||
|
"Execute BODY within a temp buffer.
|
||||||
|
Like `with-temp-buffer', but propagates `org-roam-directory'."
|
||||||
|
(declare (indent 0) (debug t))
|
||||||
|
(let ((current-org-roam-directory (make-symbol "current-org-roam-directory")))
|
||||||
|
`(let ((,current-org-roam-directory org-roam-directory))
|
||||||
|
(with-temp-buffer
|
||||||
|
(let ((org-roam-directory ,current-org-roam-directory))
|
||||||
|
,@body)))))
|
||||||
|
|
||||||
|
(provide 'org-roam-macs)
|
||||||
|
|
||||||
|
;;; org-roam-macs.el ends here
|
@ -35,23 +35,10 @@
|
|||||||
;; 2. "roam-ref": This protocol creates or opens a note with the given REF
|
;; 2. "roam-ref": This protocol creates or opens a note with the given REF
|
||||||
;;
|
;;
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(require 'org-protocol)
|
(require 'org-protocol)
|
||||||
(require 'org-roam)
|
(require 'org-roam)
|
||||||
|
|
||||||
(declare-function org-roam-find-ref "org-roam" (&optional info))
|
;;;; Functions
|
||||||
(declare-function org-roam--capture-get-point "org-roam" ())
|
|
||||||
|
|
||||||
(defvar org-roam-ref-capture-templates
|
|
||||||
'(("r" "ref" plain (function org-roam--capture-get-point)
|
|
||||||
""
|
|
||||||
:file-name "${slug}"
|
|
||||||
:head "#+TITLE: ${title}
|
|
||||||
#+ROAM_KEY: ${ref}\n"
|
|
||||||
:unnarrowed t))
|
|
||||||
"The Org-roam templates used during a capture from the roam-ref protocol.
|
|
||||||
Details on how to specify for the template is given in `org-roam-capture-templates'.")
|
|
||||||
|
|
||||||
(defun org-roam-protocol-open-ref (info)
|
(defun org-roam-protocol-open-ref (info)
|
||||||
"Process an org-protocol://roam-ref?ref= style url with INFO.
|
"Process an org-protocol://roam-ref?ref= style url with INFO.
|
||||||
|
|
||||||
@ -70,9 +57,9 @@ It opens or creates a note with the given ref.
|
|||||||
(error "No ref key provided"))
|
(error "No ref key provided"))
|
||||||
(when-let ((title (cdr (assoc 'title decoded-alist))))
|
(when-let ((title (cdr (assoc 'title decoded-alist))))
|
||||||
(push (cons 'slug (org-roam--title-to-slug title)) decoded-alist))
|
(push (cons 'slug (org-roam--title-to-slug title)) decoded-alist))
|
||||||
(let* ((org-roam-capture-templates org-roam-ref-capture-templates)
|
(let* ((org-roam-capture-templates org-roam-capture-ref-templates)
|
||||||
(org-roam--capture-context 'ref)
|
(org-roam-capture--context 'ref)
|
||||||
(org-roam--capture-info decoded-alist)
|
(org-roam-capture--info decoded-alist)
|
||||||
(template (cdr (assoc 'template decoded-alist))))
|
(template (cdr (assoc 'template decoded-alist))))
|
||||||
(raise-frame)
|
(raise-frame)
|
||||||
(org-roam-capture nil template)
|
(org-roam-capture nil template)
|
||||||
|
918
org-roam.el
918
org-roam.el
File diff suppressed because it is too large
Load Diff
@ -31,273 +31,280 @@
|
|||||||
(require 'org-roam)
|
(require 'org-roam)
|
||||||
(require 'dash)
|
(require 'dash)
|
||||||
|
|
||||||
(defun abs-path (file-path)
|
(defun org-roam-test-abs-path (file-path)
|
||||||
|
"Get absolute FILE-PATH from `org-roam-directory'."
|
||||||
(file-truename (expand-file-name file-path org-roam-directory)))
|
(file-truename (expand-file-name file-path org-roam-directory)))
|
||||||
|
|
||||||
(defun org-roam--test-find-new-file (path)
|
(defun org-roam-test-find-new-file (path)
|
||||||
(let ((path (abs-path path)))
|
"PATH."
|
||||||
|
(let ((path (org-roam-test-abs-path path)))
|
||||||
(make-directory (file-name-directory path) t)
|
(make-directory (file-name-directory path) t)
|
||||||
(find-file path)))
|
(find-file path)))
|
||||||
|
|
||||||
(defvar org-roam--tests-directory (file-truename (concat default-directory "tests/roam-files"))
|
(defvar org-roam-test-directory (file-truename (concat default-directory "tests/roam-files"))
|
||||||
"Directory containing org-roam test org files.")
|
"Directory containing org-roam test org files.")
|
||||||
|
|
||||||
(defun org-roam--test-init ()
|
(defun org-roam-test-init ()
|
||||||
(org-roam--db-close)
|
"."
|
||||||
(let ((original-dir org-roam--tests-directory)
|
(org-roam-db--close)
|
||||||
|
(let ((original-dir org-roam-test-directory)
|
||||||
(new-dir (expand-file-name (make-temp-name "org-roam") temporary-file-directory)))
|
(new-dir (expand-file-name (make-temp-name "org-roam") temporary-file-directory)))
|
||||||
(copy-directory original-dir new-dir)
|
(copy-directory original-dir new-dir)
|
||||||
(setq org-roam-directory new-dir)
|
(setq org-roam-directory new-dir)
|
||||||
(org-roam-mode +1)))
|
(org-roam-mode +1)))
|
||||||
|
|
||||||
;;; Tests
|
;;; Tests
|
||||||
(describe "org-roam-build-cache"
|
(describe "org-roam-db-build-cache"
|
||||||
(it "initializes correctly"
|
(it "initializes correctly"
|
||||||
(org-roam--test-init)
|
(org-roam-test-init)
|
||||||
(org-roam-build-cache)
|
(org-roam-db-build-cache)
|
||||||
|
|
||||||
;; Cache
|
;; Cache
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from files])) :to-be 8)
|
(expect (caar (org-roam-db-query [:select (funcall count) :from files])) :to-be 8)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from file-links])) :to-be 5)
|
(expect (caar (org-roam-db-query [:select (funcall count) :from file-links])) :to-be 5)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from titles])) :to-be 8)
|
(expect (caar (org-roam-db-query [:select (funcall count) :from titles])) :to-be 8)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from titles
|
(expect (caar (org-roam-db-query [:select (funcall count) :from titles
|
||||||
:where titles :is-null])) :to-be 2)
|
:where titles :is-null])) :to-be 2)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from refs])) :to-be 1)
|
(expect (caar (org-roam-db-query [:select (funcall count) :from refs])) :to-be 1)
|
||||||
|
|
||||||
;; TODO Test files
|
;; TODO Test files
|
||||||
|
|
||||||
;; Links -- File-from
|
;; Links -- File-from
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
|
(expect (caar (org-roam-db-query [:select (funcall count) :from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 1)
|
(org-roam-test-abs-path "foo.org"))) :to-be 1)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
|
(expect (caar (org-roam-db-query [:select (funcall count) :from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "nested/bar.org"))) :to-be 2)
|
(org-roam-test-abs-path "nested/bar.org"))) :to-be 2)
|
||||||
|
|
||||||
;; Links -- File-to
|
;; Links -- File-to
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
|
(expect (caar (org-roam-db-query [:select (funcall count) :from file-links
|
||||||
:where (= file-to $s1)]
|
:where (= file-to $s1)]
|
||||||
(abs-path "nested/foo.org"))) :to-be 1)
|
(org-roam-test-abs-path "nested/foo.org"))) :to-be 1)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
|
(expect (caar (org-roam-db-query [:select (funcall count) :from file-links
|
||||||
:where (= file-to $s1)]
|
:where (= file-to $s1)]
|
||||||
(abs-path "nested/bar.org"))) :to-be 1)
|
(org-roam-test-abs-path "nested/bar.org"))) :to-be 1)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count) :from file-links
|
(expect (caar (org-roam-db-query [:select (funcall count) :from file-links
|
||||||
:where (= file-to $s1)]
|
:where (= file-to $s1)]
|
||||||
(abs-path "unlinked.org"))) :to-be 0)
|
(org-roam-test-abs-path "unlinked.org"))) :to-be 0)
|
||||||
;; TODO Test titles
|
;; TODO Test titles
|
||||||
(expect (org-roam-sql [:select * :from titles])
|
(expect (org-roam-db-query [:select * :from titles])
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list (abs-path "alias.org")
|
(list (list (org-roam-test-abs-path "alias.org")
|
||||||
(list "t1" "a1" "a 2"))
|
(list "t1" "a1" "a 2"))
|
||||||
(list (abs-path "bar.org")
|
(list (org-roam-test-abs-path "bar.org")
|
||||||
(list "Bar"))
|
(list "Bar"))
|
||||||
(list (abs-path "foo.org")
|
(list (org-roam-test-abs-path "foo.org")
|
||||||
(list "Foo"))
|
(list "Foo"))
|
||||||
(list (abs-path "nested/bar.org")
|
(list (org-roam-test-abs-path "nested/bar.org")
|
||||||
(list "Nested Bar"))
|
(list "Nested Bar"))
|
||||||
(list (abs-path "nested/foo.org")
|
(list (org-roam-test-abs-path "nested/foo.org")
|
||||||
(list "Nested Foo"))
|
(list "Nested Foo"))
|
||||||
(list (abs-path "no-title.org") nil)
|
(list (org-roam-test-abs-path "no-title.org") nil)
|
||||||
(list (abs-path "web_ref.org") nil)
|
(list (org-roam-test-abs-path "web_ref.org") nil)
|
||||||
(list (abs-path "unlinked.org")
|
(list (org-roam-test-abs-path "unlinked.org")
|
||||||
(list "Unlinked"))))
|
(list "Unlinked"))))
|
||||||
|
|
||||||
(expect (org-roam-sql [:select * :from refs])
|
(expect (org-roam-db-query [:select * :from refs])
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list "https://google.com/" (abs-path "web_ref.org"))))
|
(list (list "https://google.com/" (org-roam-test-abs-path "web_ref.org"))))
|
||||||
|
|
||||||
;; Expect rebuilds to be really quick (nothing changed)
|
;; Expect rebuilds to be really quick (nothing changed)
|
||||||
(expect (org-roam-build-cache)
|
(expect (org-roam-db-build-cache)
|
||||||
:to-equal
|
:to-equal
|
||||||
(list :files 0 :links 0 :titles 0 :refs 0 :deleted 0))))
|
(list :files 0 :links 0 :titles 0 :refs 0 :deleted 0))))
|
||||||
|
|
||||||
(describe "org-roam-insert"
|
(describe "org-roam-insert"
|
||||||
(before-each
|
(before-each
|
||||||
(org-roam--test-init)
|
(org-roam-test-init)
|
||||||
(org-roam--db-clear)
|
(org-roam-db--clear)
|
||||||
(org-roam-build-cache))
|
(org-roam-db-build-cache))
|
||||||
|
|
||||||
(it "temp1 -> foo"
|
(it "temp1 -> foo"
|
||||||
(let ((buf (org-roam--test-find-new-file "temp1.org")))
|
(let ((buf (org-roam-test-find-new-file "temp1.org")))
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(with-simulated-input
|
(with-simulated-input
|
||||||
"Foo RET"
|
"Foo RET"
|
||||||
(org-roam-insert nil))))
|
(org-roam-insert nil))))
|
||||||
(expect (buffer-string) :to-match (regexp-quote "file:foo.org")))
|
(expect (buffer-string) :to-match (regexp-quote "file:foo.org")))
|
||||||
|
|
||||||
(it "temp2 -> nested/foo"
|
(it "temp2 -> nested/foo"
|
||||||
(let ((buf (org-roam--test-find-new-file "temp2.org")))
|
(let ((buf (org-roam-test-find-new-file "temp2.org")))
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(with-simulated-input
|
(with-simulated-input
|
||||||
"Nested SPC Foo RET"
|
"Nested SPC Foo RET"
|
||||||
(org-roam-insert nil))))
|
(org-roam-insert nil))))
|
||||||
(expect (buffer-string) :to-match (regexp-quote "file:nested/foo.org")))
|
(expect (buffer-string) :to-match (regexp-quote "file:nested/foo.org")))
|
||||||
|
|
||||||
(it "nested/temp3 -> foo"
|
(it "nested/temp3 -> foo"
|
||||||
(let ((buf (org-roam--test-find-new-file "nested/temp3.org")))
|
(let ((buf (org-roam-test-find-new-file "nested/temp3.org")))
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(with-simulated-input
|
(with-simulated-input
|
||||||
"Foo RET"
|
"Foo RET"
|
||||||
(org-roam-insert nil))))
|
(org-roam-insert nil))))
|
||||||
(expect (buffer-string) :to-match (regexp-quote "file:../foo.org")))
|
(expect (buffer-string) :to-match (regexp-quote "file:../foo.org")))
|
||||||
|
|
||||||
(it "a/b/temp4 -> nested/foo"
|
(it "a/b/temp4 -> nested/foo"
|
||||||
(let ((buf (org-roam--test-find-new-file "a/b/temp4.org")))
|
(let ((buf (org-roam-test-find-new-file "a/b/temp4.org")))
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(with-simulated-input
|
(with-simulated-input
|
||||||
"Nested SPC Foo RET"
|
"Nested SPC Foo RET"
|
||||||
(org-roam-insert nil))))
|
(org-roam-insert nil))))
|
||||||
(expect (buffer-string) :to-match (regexp-quote "file:../../nested/foo.org"))))
|
(expect (buffer-string) :to-match (regexp-quote "file:../../nested/foo.org"))))
|
||||||
|
|
||||||
(describe "rename file updates cache"
|
(describe "rename file updates cache"
|
||||||
(before-each
|
(before-each
|
||||||
(org-roam--test-init)
|
(org-roam-test-init)
|
||||||
(org-roam--db-clear)
|
(org-roam-db--clear)
|
||||||
(org-roam-build-cache))
|
(org-roam-db-build-cache))
|
||||||
|
|
||||||
(it "foo -> new_foo"
|
(it "foo -> new_foo"
|
||||||
(rename-file (abs-path "foo.org")
|
(rename-file (org-roam-test-abs-path "foo.org")
|
||||||
(abs-path "new_foo.org"))
|
(org-roam-test-abs-path "new_foo.org"))
|
||||||
;; Cache should be cleared of old file
|
;; Cache should be cleared of old file
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from titles
|
:from titles
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from refs
|
:from refs
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
|
|
||||||
;; Cache should be updated
|
;; Cache should be updated
|
||||||
(expect (org-roam-sql [:select [file-to]
|
(expect (org-roam-db-query [:select [file-to]
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "new_foo.org"))
|
(org-roam-test-abs-path "new_foo.org"))
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list (abs-path "bar.org"))))
|
(list (list (org-roam-test-abs-path "bar.org"))))
|
||||||
(expect (org-roam-sql [:select [file-from]
|
(expect (org-roam-db-query [:select [file-from]
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-to $s1)]
|
:where (= file-to $s1)]
|
||||||
(abs-path "new_foo.org"))
|
(org-roam-test-abs-path "new_foo.org"))
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list (abs-path "nested/bar.org"))))
|
(list (list (org-roam-test-abs-path "nested/bar.org"))))
|
||||||
|
|
||||||
;; Links are updated
|
;; Links are updated
|
||||||
(expect (with-temp-buffer
|
(expect (with-temp-buffer
|
||||||
(insert-file-contents (abs-path "nested/bar.org"))
|
(insert-file-contents (org-roam-test-abs-path "nested/bar.org"))
|
||||||
(buffer-string))
|
(buffer-string))
|
||||||
:to-match
|
:to-match
|
||||||
(regexp-quote "[[file:../new_foo.org][Foo]]")))
|
(regexp-quote "[[file:../new_foo.org][Foo]]")))
|
||||||
|
|
||||||
(it "foo -> foo with spaces"
|
(it "foo -> foo with spaces"
|
||||||
(rename-file (abs-path "foo.org")
|
(rename-file (org-roam-test-abs-path "foo.org")
|
||||||
(abs-path "foo with spaces.org"))
|
(org-roam-test-abs-path "foo with spaces.org"))
|
||||||
;; Cache should be cleared of old file
|
;; Cache should be cleared of old file
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from titles
|
:from titles
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from refs
|
:from refs
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
|
|
||||||
;; Cache should be updated
|
;; Cache should be updated
|
||||||
(expect (org-roam-sql [:select [file-to]
|
(expect (org-roam-db-query [:select [file-to]
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "foo with spaces.org"))
|
(org-roam-test-abs-path "foo with spaces.org"))
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list (abs-path "bar.org"))))
|
(list (list (org-roam-test-abs-path "bar.org"))))
|
||||||
(expect (org-roam-sql [:select [file-from]
|
(expect (org-roam-db-query [:select [file-from]
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-to $s1)]
|
:where (= file-to $s1)]
|
||||||
(abs-path "foo with spaces.org"))
|
(org-roam-test-abs-path "foo with spaces.org"))
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list (abs-path "nested/bar.org"))))
|
(list (list (org-roam-test-abs-path "nested/bar.org"))))
|
||||||
|
|
||||||
;; Links are updated
|
;; Links are updated
|
||||||
(expect (with-temp-buffer
|
(expect (with-temp-buffer
|
||||||
(insert-file-contents (abs-path "nested/bar.org"))
|
(insert-file-contents (org-roam-test-abs-path "nested/bar.org"))
|
||||||
(buffer-string))
|
(buffer-string))
|
||||||
:to-match
|
:to-match
|
||||||
(regexp-quote "[[file:../foo with spaces.org][Foo]]")))
|
(regexp-quote "[[file:../foo with spaces.org][Foo]]")))
|
||||||
|
|
||||||
(it "no-title -> meaningful-title"
|
(it "no-title -> meaningful-title"
|
||||||
(rename-file (abs-path "no-title.org")
|
(rename-file (org-roam-test-abs-path "no-title.org")
|
||||||
(abs-path "meaningful-title.org"))
|
(org-roam-test-abs-path "meaningful-title.org"))
|
||||||
;; File has no forward links
|
;; File has no forward links
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "no-title.org"))) :to-be 0)
|
(org-roam-test-abs-path "no-title.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "meaningful-title.org"))) :to-be 1)
|
(org-roam-test-abs-path "meaningful-title.org"))) :to-be 1)
|
||||||
|
|
||||||
;; Links are updated with the appropriate name
|
;; Links are updated with the appropriate name
|
||||||
(expect (with-temp-buffer
|
(expect (with-temp-buffer
|
||||||
(insert-file-contents (abs-path "meaningful-title.org"))
|
(insert-file-contents (org-roam-test-abs-path "meaningful-title.org"))
|
||||||
(buffer-string))
|
(buffer-string))
|
||||||
:to-match
|
:to-match
|
||||||
(regexp-quote "[[file:meaningful-title.org][meaningful-title]]")))
|
(regexp-quote "[[file:meaningful-title.org][meaningful-title]]")))
|
||||||
|
|
||||||
(it "web_ref -> hello"
|
(it "web_ref -> hello"
|
||||||
(expect (org-roam-sql
|
(expect (org-roam-db-query
|
||||||
[:select [file] :from refs
|
[:select [file] :from refs
|
||||||
:where (= ref $s1)]
|
:where (= ref $s1)]
|
||||||
"https://google.com/")
|
"https://google.com/")
|
||||||
:to-equal
|
:to-equal
|
||||||
(list (list (abs-path "web_ref.org"))))
|
(list (list (org-roam-test-abs-path "web_ref.org"))))
|
||||||
(rename-file (abs-path "web_ref.org")
|
(rename-file (org-roam-test-abs-path "web_ref.org")
|
||||||
(abs-path "hello.org"))
|
(org-roam-test-abs-path "hello.org"))
|
||||||
(expect (org-roam-sql
|
(expect (org-roam-db-query
|
||||||
[:select [file] :from refs
|
[:select [file] :from refs
|
||||||
:where (= ref $s1)]
|
:where (= ref $s1)]
|
||||||
"https://google.com/")
|
"https://google.com/")
|
||||||
:to-equal (list (list (abs-path "hello.org"))))
|
:to-equal (list (list (org-roam-test-abs-path "hello.org"))))
|
||||||
(expect (caar (org-roam-sql
|
(expect (caar (org-roam-db-query
|
||||||
[:select [ref] :from refs
|
[:select [ref] :from refs
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "web_ref.org")))
|
(org-roam-test-abs-path "web_ref.org")))
|
||||||
:to-equal nil)))
|
:to-equal nil)))
|
||||||
|
|
||||||
(describe "delete file updates cache"
|
(describe "delete file updates cache"
|
||||||
(before-each
|
(before-each
|
||||||
(org-roam--test-init)
|
(org-roam-test-init)
|
||||||
(org-roam--db-clear)
|
(org-roam-db--clear)
|
||||||
(org-roam-build-cache)
|
(org-roam-db-build-cache)
|
||||||
(sleep-for 1))
|
(sleep-for 1))
|
||||||
|
|
||||||
(it "delete foo"
|
(it "delete foo"
|
||||||
(delete-file (abs-path "foo.org"))
|
(delete-file (org-roam-test-abs-path "foo.org"))
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from titles
|
:from titles
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from refs
|
:from refs
|
||||||
:where (= file $s1)]
|
:where (= file $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0)
|
(org-roam-test-abs-path "foo.org"))) :to-be 0)
|
||||||
(expect (caar (org-roam-sql [:select (funcall count)
|
(expect (caar (org-roam-db-query [:select (funcall count)
|
||||||
:from file-links
|
:from file-links
|
||||||
:where (= file-from $s1)]
|
:where (= file-from $s1)]
|
||||||
(abs-path "foo.org"))) :to-be 0))
|
(org-roam-test-abs-path "foo.org"))) :to-be 0))
|
||||||
|
|
||||||
(it "delete web_ref"
|
(it "delete web_ref"
|
||||||
(expect (org-roam-sql [:select * :from refs])
|
(expect (org-roam-db-query [:select * :from refs])
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list (list "https://google.com/" (abs-path "web_ref.org"))))
|
(list (list "https://google.com/" (org-roam-test-abs-path "web_ref.org"))))
|
||||||
(delete-file (abs-path "web_ref.org"))
|
(delete-file (org-roam-test-abs-path "web_ref.org"))
|
||||||
(expect (org-roam-sql [:select * :from refs])
|
(expect (org-roam-db-query [:select * :from refs])
|
||||||
:to-have-same-items-as
|
:to-have-same-items-as
|
||||||
(list))))
|
(list))))
|
||||||
|
|
||||||
|
(provide 'test-org-roam)
|
||||||
|
|
||||||
|
;;; test-org-roam.el ends here
|
||||||
|
Reference in New Issue
Block a user