mirror of
https://github.com/org-roam/org-roam
synced 2025-08-01 12:17:21 -05:00
(internal): org-roam.el formatting (#687)
Organize org-roam.el: - Move functions so they are defined before they are referenced - Move defcustoms to appropriate section, sort alphabetically - Move interactive commands to appropriate section, autoload, sort. Should not change any behavior, but make review easier.
This commit is contained in:
736
org-roam.el
736
org-roam.el
@ -33,34 +33,38 @@
|
||||
;;
|
||||
;;
|
||||
;;; Code:
|
||||
;;;; Library Requires
|
||||
;;;; Dependencies
|
||||
(require 'org)
|
||||
(require 'org-element)
|
||||
(require 'ob-core) ;for org-babel-parse-header-arguments
|
||||
(eval-when-compile (require 'subr-x))
|
||||
(require 'dash)
|
||||
(require 's)
|
||||
(require 'f)
|
||||
(require 'ob-core) ;for org-babel-parse-header-arguments
|
||||
(require 'org-ref nil t) ; To detect cite: links
|
||||
(require 'cl-lib)
|
||||
(require 'dash)
|
||||
(require 'f)
|
||||
(require 's)
|
||||
(require 'seq)
|
||||
(eval-when-compile (require 'subr-x))
|
||||
|
||||
;;;; org-roam features
|
||||
;;;; Features
|
||||
(require 'org-roam-compat)
|
||||
(require 'org-roam-macs)
|
||||
(require 'org-roam-db)
|
||||
;; These features should be able to be loaded order independently.
|
||||
;; @TODO: implement something akin to `org-modules' that allows
|
||||
;; selectively loading different sets of features.
|
||||
;; ~NV [2020-05-22 Fri]
|
||||
(require 'org-roam-buffer)
|
||||
(require 'org-roam-capture)
|
||||
(require 'org-roam-graph)
|
||||
(require 'org-roam-completion)
|
||||
(require 'org-roam-capture)
|
||||
(require 'org-roam-dailies)
|
||||
(require 'org-roam-db)
|
||||
(require 'org-roam-doctor)
|
||||
(require 'org-roam-graph)
|
||||
|
||||
;; To detect cite: links
|
||||
(require 'org-ref nil t)
|
||||
;;;; Declarations
|
||||
(defvar org-ref-cite-types) ;; from org-ref-core.el
|
||||
(declare-function org-ref-split-and-strip-string "ext:org-ref-utils" (string))
|
||||
|
||||
;;;; Customizable Variables
|
||||
;;;; Customizable variables
|
||||
(defgroup org-roam nil
|
||||
"Roam Research replica in Org-mode."
|
||||
:group 'org
|
||||
@ -75,29 +79,15 @@
|
||||
|
||||
(defcustom org-roam-directory (expand-file-name "~/org-roam/")
|
||||
"Default path to Org-roam files.
|
||||
|
||||
All Org files, at any level of nesting, are considered part of the Org-roam."
|
||||
:type 'directory
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-link-title-format "%s"
|
||||
"The formatter used when inserting Org-roam links that use their title.
|
||||
Formatter may be a function that takes title as its only argument."
|
||||
:type '(choice
|
||||
(string :tag "String Format" "%s")
|
||||
(function :tag "Custom function"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-encrypt-files nil
|
||||
"Whether to encrypt new files. If true, create files with .gpg extension."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-verbose t
|
||||
"Echo messages that are not errors."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-file-extensions '("org")
|
||||
"Detected file extensions to include in the Org-roam ecosystem.
|
||||
The first item in the list is used as the default file extension.
|
||||
@ -107,6 +97,101 @@ ensure that."
|
||||
:type '(repeat string)
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-include-type-in-ref-path-completions nil
|
||||
"When t, include the type in ref-path completions.
|
||||
Note that this only affects interactive calls.
|
||||
See `org-roam--get-ref-path-completions' for details."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-index-file "index.org"
|
||||
"Path to the Org-roam index file.
|
||||
The path can be a string or a function. If it is a string, it
|
||||
should be the path (absolute or relative to `org-roam-directory')
|
||||
to the index file. If it is is a function, the function should
|
||||
return the path to the index file. Otherwise, the index is
|
||||
assumed to be a note in `org-roam-directory' whose title is
|
||||
'Index'."
|
||||
:type '(choice
|
||||
(string :tag "Path to index" "%s")
|
||||
(function :tag "Function to generate the path"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-link-title-format "%s"
|
||||
"The formatter used when inserting Org-roam links that use their title.
|
||||
Formatter may be a function that takes title as its only argument."
|
||||
:type '(choice
|
||||
(string :tag "String Format" "%s")
|
||||
(function :tag "Custom function"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-list-files-commands '(find rg)
|
||||
"Commands that will be used to find Org-roam files.
|
||||
|
||||
It should be a list of symbols or cons cells representing any of the following
|
||||
supported file search methods.
|
||||
|
||||
The commands will be tried in order until an executable for a command is found.
|
||||
The Elisp implementation is used if no command in the list is found.
|
||||
|
||||
`rg'
|
||||
Use ripgrep as the file search method.
|
||||
Example command: rg /path/to/dir/ --files -g \"*.org\" -g \"*.org.gpg\"
|
||||
|
||||
`find'
|
||||
Use find as the file search method.
|
||||
Example command:
|
||||
find /path/to/dir -type f \( -name \"*.org\" -o -name \"*.org.gpg\" \)
|
||||
|
||||
By default, `executable-find' will be used to look up the path to the
|
||||
executable. If a custom path is required, it can be specified together with the
|
||||
method symbol as a cons cell. For example: '(find (rg . \"/path/to/rg\"))."
|
||||
:type '(set (const :tag "find" find)
|
||||
(const :tag "rg" rg)))
|
||||
|
||||
(defcustom org-roam-tag-separator ","
|
||||
"String to use to separate tags when `org-roam-tag-sources' is non-nil."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-tag-sort nil
|
||||
"When non-nil, sort the tags in the completions.
|
||||
When t, sort the tags alphabetically, regardless of case.
|
||||
`org-roam-tag-sort' can also be a list of arguments to be applied
|
||||
to `cl-sort'. For example, these are the arguments used when
|
||||
`org-roam-tag-sort' is set to t:
|
||||
\('string-lessp :key 'downcase)
|
||||
Only relevant when `org-roam-tag-sources' is non-nil."
|
||||
:type '(choice
|
||||
(boolean)
|
||||
(list :tag "Arguments to cl-loop"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-tag-sources '(prop)
|
||||
"Sources to obtain tags from.
|
||||
|
||||
It should be a list of symbols representing any of the following
|
||||
extraction methods:
|
||||
|
||||
`prop'
|
||||
Extract tags from the #+ROAM_TAGS property.
|
||||
Tags are space delimited.
|
||||
Tags may contain spaces if they are double-quoted.
|
||||
e.g. #+ROAM_TAGS: tag \"tag with spaces\"
|
||||
|
||||
`all-directories'
|
||||
Extract sub-directories relative to `org-roam-directory'.
|
||||
That is, if a file is located at relative path foo/bar/file.org,
|
||||
the file will have tags \"foo\" and \"bar\".
|
||||
|
||||
`last-directory'
|
||||
Extract the last directory relative to `org-roam-directory'.
|
||||
That is, if a file is located at relative path foo/bar/file.org,
|
||||
the file will have tag \"bar\"."
|
||||
:type '(set (const :tag "#+ROAM_TAGS" prop)
|
||||
(const :tag "sub-directories" all-directories)
|
||||
(const :tag "parent directory" last-directory)))
|
||||
|
||||
(defcustom org-roam-title-sources '((title headline) alias)
|
||||
"The list of sources from which to retrieve a note title.
|
||||
Each element in the list is either:
|
||||
@ -135,61 +220,21 @@ space-delimited strings.
|
||||
(symbol)))
|
||||
:group 'org-roam)
|
||||
|
||||
(defcustom org-roam-tag-sources '(prop)
|
||||
"Sources to obtain tags from.
|
||||
|
||||
It should be a list of symbols representing any of the following
|
||||
extraction methods:
|
||||
|
||||
`prop'
|
||||
Extract tags from the #+ROAM_TAGS property.
|
||||
Tags are space delimited.
|
||||
Tags may contain spaces if they are double-quoted.
|
||||
e.g. #+ROAM_TAGS: tag \"tag with spaces\"
|
||||
|
||||
`all-directories'
|
||||
Extract sub-directories relative to `org-roam-directory'.
|
||||
That is, if a file is located at relative path foo/bar/file.org,
|
||||
the file will have tags \"foo\" and \"bar\".
|
||||
|
||||
`last-directory'
|
||||
Extract the last directory relative to `org-roam-directory'.
|
||||
That is, if a file is located at relative path foo/bar/file.org,
|
||||
the file will have tag \"bar\"."
|
||||
:type '(set (const :tag "#+ROAM_TAGS" prop)
|
||||
(const :tag "sub-directories" all-directories)
|
||||
(const :tag "parent directory" last-directory)))
|
||||
|
||||
(defcustom org-roam-list-files-commands '(find rg)
|
||||
"Commands that will be used to find Org-roam files.
|
||||
|
||||
It should be a list of symbols or cons cells representing any of the following
|
||||
supported file search methods.
|
||||
|
||||
The commands will be tried in order until an executable for a command is found.
|
||||
The Elisp implementation is used if no command in the list is found.
|
||||
|
||||
`rg'
|
||||
Use ripgrep as the file search method.
|
||||
Example command: rg /path/to/dir/ --files -g \"*.org\" -g \"*.org.gpg\"
|
||||
|
||||
`find'
|
||||
Use find as the file search method.
|
||||
Example command:
|
||||
find /path/to/dir -type f \( -name \"*.org\" -o -name \"*.org.gpg\" \)
|
||||
|
||||
By default, `executable-find' will be used to look up the path to the
|
||||
executable. If a custom path is required, it can be specified together with the
|
||||
method symbol as a cons cell. For example: '(find (rg . \"/path/to/rg\"))."
|
||||
:type '(set (const :tag "find" find)
|
||||
(const :tag "rg" rg)))
|
||||
(defcustom org-roam-verbose t
|
||||
"Echo messages that are not errors."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
;;;; Dynamic variables
|
||||
(defvar org-roam-last-window nil
|
||||
"Last window `org-roam' was called from.")
|
||||
|
||||
;;; Utilities
|
||||
;;;; General Utilities
|
||||
(defvar org-roam--org-link-file-bracket-re
|
||||
"\\[\\[file:\\(\\(?:[^][\\]\\|\\\\\\(?:\\\\\\\\\\)*[][]\\|\\\\+[^][]\\)+\\)]\\(?:\\[\\(\\(?:.\\|
|
||||
\\)+?\\)]\\)?]"
|
||||
"Matches a 'file:' link in double brackets.")
|
||||
|
||||
;;;; Utilities
|
||||
(defun org-roam--plist-to-alist (plist)
|
||||
"Return an alist of the property-value pairs in PLIST."
|
||||
(let (res)
|
||||
@ -248,7 +293,7 @@ Like `file-name-extension', but does not strip version number."
|
||||
"Return t if FILE is part of Org-roam system, nil otherwise.
|
||||
If FILE is not specified, use the current buffer's file-path."
|
||||
(if-let ((path (or file
|
||||
(buffer-file-name))))
|
||||
(buffer-file-name))))
|
||||
(save-match-data
|
||||
(and
|
||||
(org-roam--org-file-p path)
|
||||
@ -302,8 +347,8 @@ Use external shell commands if defined in `org-roam-list-files-commands'."
|
||||
exe (symbol-name cmd)))
|
||||
(wrong-type
|
||||
(signal 'wrong-type-argument
|
||||
`((consp symbolp)
|
||||
,wrong-type))))
|
||||
`((consp symbolp)
|
||||
,wrong-type))))
|
||||
(when path (cl-return)))
|
||||
(if path
|
||||
(let ((fn (intern (concat "org-roam--list-files-" exe))))
|
||||
@ -330,11 +375,6 @@ The search terminates when the first property is encountered."
|
||||
(push (cons prop p) res)))
|
||||
res))
|
||||
|
||||
(defvar org-roam--org-link-file-bracket-re
|
||||
"\\[\\[file:\\(\\(?:[^][\\]\\|\\\\\\(?:\\\\\\\\\\)*[][]\\|\\\\+[^][]\\)+\\)]\\(?:\\[\\(\\(?:.\\|
|
||||
\\)+?\\)]\\)?]"
|
||||
"Matches a 'file:' link in double brackets.")
|
||||
|
||||
(defun org-roam--expand-links (content path)
|
||||
"Crawl CONTENT for relative links and expand them.
|
||||
PATH should be the root from which to compute the relativity."
|
||||
@ -475,19 +515,6 @@ The final directory component is used as a tag."
|
||||
(let* ((prop (org-roam--extract-global-props '("ROAM_TAGS"))))
|
||||
(org-roam--parse-tags (cdr (assoc "ROAM_TAGS" prop)))))
|
||||
|
||||
(defcustom org-roam-tag-sort nil
|
||||
"When non-nil, sort the tags in the completions.
|
||||
When t, sort the tags alphabetically, regardless of case.
|
||||
`org-roam-tag-sort' can also be a list of arguments to be applied
|
||||
to `cl-sort'. For example, these are the arguments used when
|
||||
`org-roam-tag-sort' is set to t:
|
||||
\('string-lessp :key 'downcase)
|
||||
Only relevant when `org-roam-tag-sources' is non-nil."
|
||||
:type '(choice
|
||||
(boolean)
|
||||
(list :tag "Arguments to cl-loop"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defun org-roam--extract-tags (&optional file)
|
||||
"Extract tags from the current buffer.
|
||||
If file-path FILE, use it to determine the directory tags.
|
||||
@ -511,10 +538,28 @@ Tags are obtained via:
|
||||
`((booleanp (list symbolp …))
|
||||
,wrong-type))))))
|
||||
|
||||
(defun org-roam--ref-type-p (type)
|
||||
"Return t if the ref from current buffer is TYPE."
|
||||
(let ((current (car (org-roam--extract-ref))))
|
||||
(eq current type)))
|
||||
(defun org-roam--cite-prefix (ref)
|
||||
"Return the citation prefix of REF, or nil otherwise.
|
||||
The prefixes are defined in `org-ref-cite-types`.
|
||||
Examples:
|
||||
(org-roam--cite-prefix \"cite:foo\") -> \"cite:\"
|
||||
(org-roam--cite-prefix \"https://google.com\") -> nil"
|
||||
(when (require 'org-ref nil t)
|
||||
(seq-find
|
||||
(lambda (prefix) (s-prefix? prefix ref))
|
||||
(-map (lambda (type) (concat type ":"))
|
||||
org-ref-cite-types))))
|
||||
|
||||
(defun org-roam--ref-type (ref)
|
||||
"Determine the type of the REF from the prefix."
|
||||
(let* ((cite-prefix (org-roam--cite-prefix ref))
|
||||
(is-website (seq-some
|
||||
(lambda (prefix) (s-prefix? prefix ref))
|
||||
'("http" "https")))
|
||||
(type (cond (cite-prefix "cite")
|
||||
(is-website "website")
|
||||
(t "roam"))))
|
||||
type))
|
||||
|
||||
(defun org-roam--extract-ref ()
|
||||
"Extract the ref from current buffer and return the type and the key of the ref."
|
||||
@ -530,28 +575,10 @@ Tags are obtained via:
|
||||
(t ref))))
|
||||
(cons type key)))))
|
||||
|
||||
(defun org-roam--ref-type (ref)
|
||||
"Determine the type of the REF from the prefix."
|
||||
(let* ((cite-prefix (org-roam--cite-prefix ref))
|
||||
(is-website (seq-some
|
||||
(lambda (prefix) (s-prefix? prefix ref))
|
||||
'("http" "https")))
|
||||
(type (cond (cite-prefix "cite")
|
||||
(is-website "website")
|
||||
(t "roam"))))
|
||||
type))
|
||||
|
||||
(defun org-roam--cite-prefix (ref)
|
||||
"Return the citation prefix of REF, or nil otherwise.
|
||||
The prefixes are defined in `org-ref-cite-types`.
|
||||
Examples:
|
||||
(org-roam--cite-prefix \"cite:foo\") -> \"cite:\"
|
||||
(org-roam--cite-prefix \"https://google.com\") -> nil"
|
||||
(when (require 'org-ref nil t)
|
||||
(seq-find
|
||||
(lambda (prefix) (s-prefix? prefix ref))
|
||||
(-map (lambda (type) (concat type ":"))
|
||||
org-ref-cite-types))))
|
||||
(defun org-roam--ref-type-p (type)
|
||||
"Return t if the ref from current buffer is TYPE."
|
||||
(let ((current (car (org-roam--extract-ref))))
|
||||
(eq current type)))
|
||||
|
||||
;;;; Title/Path/Slug conversion
|
||||
(defun org-roam--path-to-slug (path)
|
||||
@ -581,7 +608,6 @@ Examples:
|
||||
(slug (-reduce-from #'cl-replace (strip-nonspacing-marks title) pairs)))
|
||||
(downcase slug))))
|
||||
|
||||
;;; Interactive Commands
|
||||
(defun org-roam--format-link-title (title)
|
||||
"Return the link title, given the file TITLE."
|
||||
(if (functionp org-roam-link-title-format)
|
||||
@ -602,61 +628,6 @@ Examples:
|
||||
target))
|
||||
description)))
|
||||
|
||||
(defun org-roam-insert (&optional lowercase completions filter-fn description)
|
||||
"Find an Org-roam file, and insert a relative org link to it at point.
|
||||
If LOWERCASE, downcase the title before insertion.
|
||||
COMPLETIONS is a list of completions to be used instead of
|
||||
`org-roam--get-title-path-completions`.
|
||||
FILTER-FN is the name of a function to apply on the candidates
|
||||
which takes as its argument an alist of path-completions.
|
||||
If DESCRIPTION is provided, use this as the link label. See
|
||||
`org-roam--get-title-path-completions' for details."
|
||||
(interactive "P")
|
||||
(let* ((region (and (region-active-p)
|
||||
;; following may lose active region, so save it
|
||||
(cons (region-beginning) (region-end))))
|
||||
(region-text (when region
|
||||
(buffer-substring-no-properties
|
||||
(car region) (cdr region))))
|
||||
(completions (--> (or completions
|
||||
(org-roam--get-title-path-completions))
|
||||
(if filter-fn
|
||||
(funcall filter-fn it)
|
||||
it)))
|
||||
(title-with-tags (org-roam-completion--completing-read "File: " completions
|
||||
:initial-input region-text))
|
||||
(res (cdr (assoc title-with-tags completions)))
|
||||
(title (or (plist-get res :title)
|
||||
title-with-tags))
|
||||
(target-file-path (plist-get res :path))
|
||||
(description (or description region-text title))
|
||||
(link-description (org-roam--format-link-title (if lowercase
|
||||
(downcase description)
|
||||
description))))
|
||||
(if (and target-file-path
|
||||
(file-exists-p target-file-path))
|
||||
(progn
|
||||
(when region ;; Remove previously selected text.
|
||||
(delete-region (car region) (cdr region)))
|
||||
(insert (org-roam--format-link target-file-path link-description)))
|
||||
(when (org-roam-capture--in-process-p)
|
||||
(user-error "Nested Org-roam capture processes not supported"))
|
||||
(let ((org-roam-capture--info `((title . ,title-with-tags)
|
||||
(slug . ,(org-roam--title-to-slug title-with-tags))))
|
||||
(org-roam-capture--context 'title))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--insert-link-h)
|
||||
(setq org-roam-capture-additional-template-props (list :region region
|
||||
:link-description link-description
|
||||
:capture-fn 'org-roam-insert))
|
||||
(org-roam--with-template-error 'org-roam-capture-templates
|
||||
(org-roam-capture--capture))))))
|
||||
|
||||
(defcustom org-roam-tag-separator ","
|
||||
"String to use to separate tags.
|
||||
Only relevant when `org-roam-tag-sources' is non-nil."
|
||||
:type 'string
|
||||
:group 'org-roam)
|
||||
|
||||
(defun org-roam--get-title-path-completions ()
|
||||
"Return an alist for completion.
|
||||
The car is the displayed title for completion, and the cdr is the
|
||||
@ -682,53 +653,6 @@ to the file."
|
||||
(v (list :path file-path :title title)))
|
||||
(push (cons k v) completions))))))))
|
||||
|
||||
(defun org-roam-find-file (&optional initial-prompt completions filter-fn)
|
||||
"Find and open an Org-roam file.
|
||||
INITIAL-PROMPT is the initial title prompt.
|
||||
COMPLETIONS is a list of completions to be used instead of
|
||||
`org-roam--get-title-path-completions`.
|
||||
FILTER-FN is the name of a function to apply on the candidates
|
||||
which takes as its argument an alist of path-completions. See
|
||||
`org-roam--get-title-path-completions' for details."
|
||||
(interactive)
|
||||
(let* ((completions (--> (or completions
|
||||
(org-roam--get-title-path-completions))
|
||||
(if filter-fn
|
||||
(funcall filter-fn it)
|
||||
it)))
|
||||
(title-with-tags (org-roam-completion--completing-read "File: " completions
|
||||
:initial-input initial-prompt))
|
||||
(res (cdr (assoc title-with-tags completions)))
|
||||
(file-path (plist-get res :path)))
|
||||
(if file-path
|
||||
(find-file file-path)
|
||||
(if (org-roam-capture--in-process-p)
|
||||
(user-error "Org-roam capture in process")
|
||||
(let ((org-roam-capture--info `((title . ,title-with-tags)
|
||||
(slug . ,(org-roam--title-to-slug title-with-tags))))
|
||||
(org-roam-capture--context 'title))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--find-file-h)
|
||||
(org-roam--with-template-error 'org-roam-capture-templates
|
||||
(org-roam-capture--capture)))))))
|
||||
|
||||
(defun org-roam-find-directory ()
|
||||
"Find and open `org-roam-directory'."
|
||||
(interactive)
|
||||
(find-file org-roam-directory))
|
||||
|
||||
(defcustom org-roam-index-file "index.org"
|
||||
"Path to the Org-roam index file.
|
||||
The path can be a string or a function. If it is a string, it
|
||||
should be the path (absolute or relative to `org-roam-directory')
|
||||
to the index file. If it is is a function, the function should
|
||||
return the path to the index file. Otherwise, the index is
|
||||
assumed to be a note in `org-roam-directory' whose title is
|
||||
'Index'."
|
||||
:type '(choice
|
||||
(string :tag "Path to index" "%s")
|
||||
(function :tag "Function to generate the path"))
|
||||
:group 'org-roam)
|
||||
|
||||
(defun org-roam--get-index-path ()
|
||||
"Return the path to the index in `org-roam-directory'.
|
||||
The path to the index can be defined in `org-roam-index-file'.
|
||||
@ -746,28 +670,7 @@ whose title is 'Index'."
|
||||
(concat (file-truename org-roam-directory) path)
|
||||
index)))
|
||||
|
||||
(defun org-roam-jump-to-index ()
|
||||
"Find the index file in `org-roam-directory'.
|
||||
The path to the index can be defined in `org-roam-index-file'.
|
||||
Otherwise, the function will look in your `org-roam-directory'
|
||||
for a note whose title is 'Index'. If it does not exist, the
|
||||
command will offer you to create one."
|
||||
(interactive)
|
||||
(let ((index (org-roam--get-index-path)))
|
||||
(if (and index
|
||||
(file-exists-p index))
|
||||
(find-file index)
|
||||
(when (y-or-n-p "Index file does not exist. Would you like to create it? ")
|
||||
(org-roam-find-file "Index")))))
|
||||
|
||||
;;;; org-roam-find-ref
|
||||
(defcustom org-roam-include-type-in-ref-path-completions nil
|
||||
"When t, include the type in ref-path completions.
|
||||
Note that this only affects interactive calls.
|
||||
See `org-roam--get-ref-path-completions' for details."
|
||||
:type 'boolean
|
||||
:group 'org-roam)
|
||||
|
||||
(defun org-roam--get-ref-path-completions (&optional interactive filter)
|
||||
"Return a alist of refs to absolute path of Org-roam files.
|
||||
When `org-roam-include-type-in-ref-path-completions' and
|
||||
@ -815,26 +718,6 @@ Return nil if the file does not exist."
|
||||
(file (plist-get (cdr (assoc ref completions)) :path)))
|
||||
(find-file file)))
|
||||
|
||||
(defun org-roam-find-ref (arg &optional filter)
|
||||
"Find and open an Org-roam file from a ref.
|
||||
ARG is used to forward interactive calls to
|
||||
`org-roam--get-ref-path-completions'
|
||||
FILTER can either be a string or a function:
|
||||
- If it is a string, it should be the type of refs to include as
|
||||
candidates (e.g. \"cite\" ,\"website\" ,etc.)
|
||||
- If it is a function, it should be the name of a function that
|
||||
takes three arguments: the type, the ref, and the file of the
|
||||
current candidate. It should return t if that candidate is to be
|
||||
included as a candidate."
|
||||
(interactive "p")
|
||||
(let* ((completions (org-roam--get-ref-path-completions arg filter))
|
||||
(ref (org-roam-completion--completing-read "Ref: "
|
||||
completions
|
||||
:require-match t))
|
||||
(file (-> (cdr (assoc ref completions))
|
||||
(plist-get :path))))
|
||||
(find-file file)))
|
||||
|
||||
(defun org-roam--get-roam-buffers ()
|
||||
"Return a list of buffers that are Org-roam files."
|
||||
(--filter (and (with-current-buffer it (derived-mode-p 'org-mode))
|
||||
@ -842,27 +725,11 @@ included as a candidate."
|
||||
(org-roam--org-roam-file-p (buffer-file-name it)))
|
||||
(buffer-list)))
|
||||
|
||||
(defun org-roam-switch-to-buffer ()
|
||||
"Switch to an existing Org-roam buffer."
|
||||
(interactive)
|
||||
(let* ((roam-buffers (org-roam--get-roam-buffers))
|
||||
(names-and-buffers (mapcar (lambda (buffer)
|
||||
(cons (or (org-roam--get-title-or-slug
|
||||
(buffer-file-name buffer))
|
||||
(buffer-name buffer))
|
||||
buffer))
|
||||
roam-buffers)))
|
||||
(unless roam-buffers
|
||||
(user-error "No roam buffers"))
|
||||
(when-let ((name (org-roam-completion--completing-read "Buffer: " names-and-buffers
|
||||
:require-match t)))
|
||||
(switch-to-buffer (cdr (assoc name names-and-buffers))))))
|
||||
|
||||
(defun org-roam--file-path-from-id (id)
|
||||
"The file path for an Org-roam file, with identifier ID."
|
||||
(file-truename
|
||||
(let* ((ext (or (car org-roam-file-extensions)
|
||||
"org"))
|
||||
"org"))
|
||||
(file (concat id "." ext)))
|
||||
(expand-file-name
|
||||
(if org-roam-encrypt-files
|
||||
@ -991,51 +858,7 @@ for Org-ref cite links."
|
||||
:link (format "file:%s" (abbreviate-file-name buffer-file-name))
|
||||
:description title))))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'org-roam 'org-roam-buffer-toggle-display)
|
||||
|
||||
;;; The global minor org-roam-mode
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode org-roam-mode
|
||||
"Minor mode for Org-roam.
|
||||
|
||||
This mode sets up several hooks, to ensure that the cache is updated on file
|
||||
changes, renames and deletes. It is also in charge of graceful termination of
|
||||
the database connection.
|
||||
|
||||
When called interactively, toggle `org-roam-mode'. with prefix
|
||||
ARG, enable `org-roam-mode' if ARG is positive, otherwise disable
|
||||
it.
|
||||
|
||||
When called from Lisp, enable `org-roam-mode' if ARG is omitted,
|
||||
nil, or positive. If ARG is `toggle', toggle `org-roam-mode'.
|
||||
Otherwise, behave as if called interactively."
|
||||
:lighter " Org-roam"
|
||||
:keymap (let ((map (make-sparse-keymap))) map)
|
||||
:group 'org-roam
|
||||
:require 'org-roam
|
||||
:global t
|
||||
(cond
|
||||
(org-roam-mode
|
||||
(add-hook 'find-file-hook #'org-roam--find-file-hook-function)
|
||||
(add-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
||||
(advice-add 'rename-file :after #'org-roam--rename-file-advice)
|
||||
(advice-add 'delete-file :before #'org-roam--delete-file-advice)
|
||||
(org-roam-db-build-cache))
|
||||
(t
|
||||
(remove-hook 'find-file-hook #'org-roam--find-file-hook-function)
|
||||
(remove-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
||||
(advice-remove 'rename-file #'org-roam--rename-file-advice)
|
||||
(advice-remove 'delete-file #'org-roam--delete-file-advice)
|
||||
(org-roam-db--close-all)
|
||||
;; Disable local hooks for all org-roam buffers
|
||||
(dolist (buf (org-roam--get-roam-buffers))
|
||||
(with-current-buffer buf
|
||||
(org-link-set-parameters "file" :face 'org-link)
|
||||
(remove-hook 'post-command-hook #'org-roam-buffer--update-maybe t)
|
||||
(remove-hook 'after-save-hook #'org-roam-db--update-file t))))))
|
||||
|
||||
(defun org-roam--find-file-hook-function ()
|
||||
"Called by `find-file-hook' when mode symbol `org-roam-mode' is on."
|
||||
(when (org-roam--org-roam-file-p)
|
||||
@ -1061,27 +884,27 @@ update with NEW-DESC."
|
||||
(find-file-noselect file))
|
||||
(save-excursion
|
||||
(let ((link-markers (org-element-map (org-element-parse-buffer) 'link
|
||||
(lambda (l)
|
||||
(let ((type (org-element-property :type l))
|
||||
(path (org-element-property :path l)))
|
||||
(when (and (equal "file" type)
|
||||
(string-equal (file-truename path)
|
||||
old-path))
|
||||
(set-marker (make-marker) (org-element-property :begin l))))))))
|
||||
(dolist (m link-markers)
|
||||
(goto-char m)
|
||||
(save-match-data
|
||||
(unless (org-in-regexp org-link-bracket-re 1)
|
||||
(user-error "No link at point"))
|
||||
(let* ((label (if (match-end 2)
|
||||
(match-string-no-properties 2)
|
||||
(org-link-unescape (match-string-no-properties 1))))
|
||||
(new-label (if (string-equal label old-desc)
|
||||
new-desc
|
||||
label)))
|
||||
(replace-match (org-link-make-string
|
||||
(concat "file:" (file-relative-name new-path (file-name-directory (buffer-file-name))))
|
||||
new-label)))))))
|
||||
(lambda (l)
|
||||
(let ((type (org-element-property :type l))
|
||||
(path (org-element-property :path l)))
|
||||
(when (and (equal "file" type)
|
||||
(string-equal (file-truename path)
|
||||
old-path))
|
||||
(set-marker (make-marker) (org-element-property :begin l))))))))
|
||||
(dolist (m link-markers)
|
||||
(goto-char m)
|
||||
(save-match-data
|
||||
(unless (org-in-regexp org-link-bracket-re 1)
|
||||
(user-error "No link at point"))
|
||||
(let* ((label (if (match-end 2)
|
||||
(match-string-no-properties 2)
|
||||
(org-link-unescape (match-string-no-properties 1))))
|
||||
(new-label (if (string-equal label old-desc)
|
||||
new-desc
|
||||
label)))
|
||||
(replace-match (org-link-make-string
|
||||
(concat "file:" (file-relative-name new-path (file-name-directory (buffer-file-name))))
|
||||
new-label)))))))
|
||||
(save-buffer)))
|
||||
|
||||
(defun org-roam--fix-relative-links (old-path)
|
||||
@ -1153,23 +976,49 @@ replaced links are made relative to the current buffer."
|
||||
(save-buffer)))
|
||||
(org-roam-db--update-file new-path)))))
|
||||
|
||||
;;;; Diagnostics
|
||||
;;;###autoload
|
||||
(defun org-roam-version (&optional message)
|
||||
"Return `org-roam' version.
|
||||
Interactively, or when MESSAGE is non-nil, show in the echo area."
|
||||
(interactive)
|
||||
(let* ((version
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally (locate-library "org-roam.el"))
|
||||
(goto-char (point-min))
|
||||
(save-match-data
|
||||
(if (re-search-forward "\\(?:;; Version: \\([^z-a]*?$\\)\\)" nil nil)
|
||||
(substring-no-properties (match-string 1))
|
||||
"N/A")))))
|
||||
(if (or message (called-interactively-p 'interactive))
|
||||
(message "%s" version)
|
||||
version)))
|
||||
(define-minor-mode org-roam-mode
|
||||
"Minor mode for Org-roam.
|
||||
|
||||
This mode sets up several hooks, to ensure that the cache is updated on file
|
||||
changes, renames and deletes. It is also in charge of graceful termination of
|
||||
the database connection.
|
||||
|
||||
When called interactively, toggle `org-roam-mode'. with prefix
|
||||
ARG, enable `org-roam-mode' if ARG is positive, otherwise disable
|
||||
it.
|
||||
|
||||
When called from Lisp, enable `org-roam-mode' if ARG is omitted,
|
||||
nil, or positive. If ARG is `toggle', toggle `org-roam-mode'.
|
||||
Otherwise, behave as if called interactively."
|
||||
:lighter " Org-roam"
|
||||
:keymap (let ((map (make-sparse-keymap))) map)
|
||||
:group 'org-roam
|
||||
:require 'org-roam
|
||||
:global t
|
||||
(cond
|
||||
(org-roam-mode
|
||||
(add-hook 'find-file-hook #'org-roam--find-file-hook-function)
|
||||
(add-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
||||
(advice-add 'rename-file :after #'org-roam--rename-file-advice)
|
||||
(advice-add 'delete-file :before #'org-roam--delete-file-advice)
|
||||
(org-roam-db-build-cache))
|
||||
(t
|
||||
(remove-hook 'find-file-hook #'org-roam--find-file-hook-function)
|
||||
(remove-hook 'kill-emacs-hook #'org-roam-db--close-all)
|
||||
(advice-remove 'rename-file #'org-roam--rename-file-advice)
|
||||
(advice-remove 'delete-file #'org-roam--delete-file-advice)
|
||||
(org-roam-db--close-all)
|
||||
;; Disable local hooks for all org-roam buffers
|
||||
(dolist (buf (org-roam--get-roam-buffers))
|
||||
(with-current-buffer buf
|
||||
(org-link-set-parameters "file" :face 'org-link)
|
||||
(remove-hook 'post-command-hook #'org-roam-buffer--update-maybe t)
|
||||
(remove-hook 'after-save-hook #'org-roam-db--update-file t))))))
|
||||
|
||||
;;; Interactive Commands
|
||||
;;;###autoload
|
||||
(defalias 'org-roam 'org-roam-buffer-toggle-display)
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-diagnostics ()
|
||||
@ -1187,5 +1036,160 @@ Interactively, or when MESSAGE is non-nil, show in the echo area."
|
||||
(insert (format "- Org: %s\n" (org-version nil 'full)))
|
||||
(insert (format "- Org-roam: %s" (org-roam-version)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-find-file (&optional initial-prompt completions filter-fn)
|
||||
"Find and open an Org-roam file.
|
||||
INITIAL-PROMPT is the initial title prompt.
|
||||
COMPLETIONS is a list of completions to be used instead of
|
||||
`org-roam--get-title-path-completions`.
|
||||
FILTER-FN is the name of a function to apply on the candidates
|
||||
which takes as its argument an alist of path-completions. See
|
||||
`org-roam--get-title-path-completions' for details."
|
||||
(interactive)
|
||||
(let* ((completions (--> (or completions
|
||||
(org-roam--get-title-path-completions))
|
||||
(if filter-fn
|
||||
(funcall filter-fn it)
|
||||
it)))
|
||||
(title-with-tags (org-roam-completion--completing-read "File: " completions
|
||||
:initial-input initial-prompt))
|
||||
(res (cdr (assoc title-with-tags completions)))
|
||||
(file-path (plist-get res :path)))
|
||||
(if file-path
|
||||
(find-file file-path)
|
||||
(if (org-roam-capture--in-process-p)
|
||||
(user-error "Org-roam capture in process")
|
||||
(let ((org-roam-capture--info `((title . ,title-with-tags)
|
||||
(slug . ,(org-roam--title-to-slug title-with-tags))))
|
||||
(org-roam-capture--context 'title))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--find-file-h)
|
||||
(org-roam--with-template-error 'org-roam-capture-templates
|
||||
(org-roam-capture--capture)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-find-directory ()
|
||||
"Find and open `org-roam-directory'."
|
||||
(interactive)
|
||||
(find-file org-roam-directory))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-find-ref (arg &optional filter)
|
||||
"Find and open an Org-roam file from a ref.
|
||||
ARG is used to forward interactive calls to
|
||||
`org-roam--get-ref-path-completions'
|
||||
FILTER can either be a string or a function:
|
||||
- If it is a string, it should be the type of refs to include as
|
||||
candidates (e.g. \"cite\" ,\"website\" ,etc.)
|
||||
- If it is a function, it should be the name of a function that
|
||||
takes three arguments: the type, the ref, and the file of the
|
||||
current candidate. It should return t if that candidate is to be
|
||||
included as a candidate."
|
||||
(interactive "p")
|
||||
(let* ((completions (org-roam--get-ref-path-completions arg filter))
|
||||
(ref (org-roam-completion--completing-read "Ref: "
|
||||
completions
|
||||
:require-match t))
|
||||
(file (-> (cdr (assoc ref completions))
|
||||
(plist-get :path))))
|
||||
(find-file file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-insert (&optional lowercase completions filter-fn description)
|
||||
"Find an Org-roam file, and insert a relative org link to it at point.
|
||||
If LOWERCASE, downcase the title before insertion.
|
||||
COMPLETIONS is a list of completions to be used instead of
|
||||
`org-roam--get-title-path-completions`.
|
||||
FILTER-FN is the name of a function to apply on the candidates
|
||||
which takes as its argument an alist of path-completions.
|
||||
If DESCRIPTION is provided, use this as the link label. See
|
||||
`org-roam--get-title-path-completions' for details."
|
||||
(interactive "P")
|
||||
(let* ((region (and (region-active-p)
|
||||
;; following may lose active region, so save it
|
||||
(cons (region-beginning) (region-end))))
|
||||
(region-text (when region
|
||||
(buffer-substring-no-properties (car region) (cdr region))))
|
||||
(completions (--> (or completions
|
||||
(org-roam--get-title-path-completions))
|
||||
(if filter-fn
|
||||
(funcall filter-fn it)
|
||||
it)))
|
||||
(title-with-tags (org-roam-completion--completing-read "File: " completions
|
||||
:initial-input region-text))
|
||||
(res (cdr (assoc title-with-tags completions)))
|
||||
(title (or (plist-get res :title)
|
||||
title-with-tags))
|
||||
(target-file-path (plist-get res :path))
|
||||
(description (or description region-text title))
|
||||
(link-description (org-roam--format-link-title (if lowercase
|
||||
(downcase description)
|
||||
description))))
|
||||
(if (and target-file-path
|
||||
(file-exists-p target-file-path))
|
||||
(progn
|
||||
(when region ;; Remove previously selected text.
|
||||
(delete-region (car region) (cdr region)))
|
||||
(insert (org-roam--format-link target-file-path link-description)))
|
||||
(when (org-roam-capture--in-process-p)
|
||||
(user-error "Nested Org-roam capture processes not supported"))
|
||||
(let ((org-roam-capture--info `((title . ,title-with-tags)
|
||||
(slug . ,(org-roam--title-to-slug title-with-tags))))
|
||||
(org-roam-capture--context 'title))
|
||||
(add-hook 'org-capture-after-finalize-hook #'org-roam-capture--insert-link-h)
|
||||
(setq org-roam-capture-additional-template-props (list :region region
|
||||
:link-description link-description
|
||||
:capture-fn 'org-roam-insert))
|
||||
(org-roam--with-template-error 'org-roam-capture-templates
|
||||
(org-roam-capture--capture))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-jump-to-index ()
|
||||
"Find the index file in `org-roam-directory'.
|
||||
The path to the index can be defined in `org-roam-index-file'.
|
||||
Otherwise, the function will look in your `org-roam-directory'
|
||||
for a note whose title is 'Index'. If it does not exist, the
|
||||
command will offer you to create one."
|
||||
(interactive)
|
||||
(let ((index (org-roam--get-index-path)))
|
||||
(if (and index
|
||||
(file-exists-p index))
|
||||
(find-file index)
|
||||
(when (y-or-n-p "Index file does not exist. Would you like to create it? ")
|
||||
(org-roam-find-file "Index")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-switch-to-buffer ()
|
||||
"Switch to an existing Org-roam buffer."
|
||||
(interactive)
|
||||
(let* ((roam-buffers (org-roam--get-roam-buffers))
|
||||
(names-and-buffers (mapcar (lambda (buffer)
|
||||
(cons (or (org-roam--get-title-or-slug
|
||||
(buffer-file-name buffer))
|
||||
(buffer-name buffer))
|
||||
buffer))
|
||||
roam-buffers)))
|
||||
(unless roam-buffers
|
||||
(user-error "No roam buffers"))
|
||||
(when-let ((name (org-roam-completion--completing-read "Buffer: " names-and-buffers
|
||||
:require-match t)))
|
||||
(switch-to-buffer (cdr (assoc name names-and-buffers))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-roam-version (&optional message)
|
||||
"Return `org-roam' version.
|
||||
Interactively, or when MESSAGE is non-nil, show in the echo area."
|
||||
(interactive)
|
||||
(let* ((version
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally (locate-library "org-roam.el"))
|
||||
(goto-char (point-min))
|
||||
(save-match-data
|
||||
(if (re-search-forward "\\(?:;; Version: \\([^z-a]*?$\\)\\)" nil nil)
|
||||
(substring-no-properties (match-string 1))
|
||||
"N/A")))))
|
||||
(if (or message (called-interactively-p 'interactive))
|
||||
(message "%s" version)
|
||||
version)))
|
||||
|
||||
(provide 'org-roam)
|
||||
;;; org-roam.el ends here
|
||||
|
Reference in New Issue
Block a user