branch: externals/denote commit 570d9ac0bde3923961e69200614094e2b7bc482e Author: Protesilaos Stavrou <i...@protesilaos.com> Commit: Protesilaos Stavrou <i...@protesilaos.com>
Make all links contingent on denote-file-types This consolidates all link formatting and link extraction/recognition in the variable 'denote-file-types'. We now have the ability to control centrally how file types will behave for all linking commands and their ancillary functions. With this and other changes from earlier today (see excerpt of Git log further below), the 'denote-file-types' provides the means to extend Denote to support ANY FILE TYPE: how it produces its front matter, the way to retrieve front matter values, the pattern to format keywords, link format+extraction/ecognition, et cetera. The doc string of 'denote-file-types' covers all the technicalities. This is an advanced feature of Denote. It is only intended for experienced users. A relevant request for such a feature can be found in issue 86 on the GitHub mirror: <https://github.com/protesilaos/denote/issues/86>. I believe I have tested everything, though we have plenty of time ahead of us to test things before the release of version 1.2.0 of Denote (tentative target is the end of November). * * * Relevant commits: * 2afd07d 2022-10-30 07:03:58 +0200 Protesilaos Stavrou: Make public all variables for link in context * a553003 2022-10-30 06:50:27 +0200 Protesilaos Stavrou: Make link format variables public * 9957eaa 2022-10-30 06:39:04 +0200 Protesilaos Stavrou: Clarify denote-link--format-markdown doc string * 4c7bfe2 2022-10-30 06:34:53 +0200 Protesilaos Stavrou: Make date format functions public * dec6367 2022-10-30 06:20:38 +0200 Protesilaos Stavrou: Make denote-file-types read a :date-function * a93d9f2 2022-10-30 06:22:30 +0200 Protesilaos Stavrou: Clarify denote-file-type doc string * 14f6919 2022-10-30 05:46:23 +0200 Protesilaos Stavrou: Refine denote-file-types documentation * a214901 2022-10-30 05:44:23 +0200 Protesilaos Stavrou: Refine how denote-file-type-prompt gets candidates --- README.org | 8 +++++ denote.el | 117 ++++++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 86 insertions(+), 39 deletions(-) diff --git a/README.org b/README.org index b28e9c5bee..62dd35c088 100644 --- a/README.org +++ b/README.org @@ -2677,6 +2677,14 @@ might change them without further notice. the keywords' value from the front matter. It performs the reverse of the =:keywords-value-function=. + 10. =:link= is a string, or variable holding a string, that + specifies the format of a link. See the variables + ~denote-org-link-format~, ~denote-md-link-format~. + + 11. =:link-in-context-regexp= is a regular expression that is used + to match the aforementioned link format. See the variables + ~denote-org-link-in-context-regexp~, ~denote-md-link-in-context-regexp~. + If ~denote-file-type~ is nil, we use the first element of this list for new note creation. The default is ~org~. diff --git a/denote.el b/denote.el index a0ea191684..9301246f94 100644 --- a/denote.el +++ b/denote.el @@ -883,7 +883,9 @@ Consult the `denote-file-types' for how this is used." :title-value-reverse-function denote-trim-whitespace :keywords-key-regexp "^#\\+filetags\\s-*:" :keywords-value-function denote-format-keywords-for-org-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter) + :keywords-value-reverse-function denote-extract-keywords-from-front-matter + :link denote-org-link-format + :link-in-context-regexp denote-org-link-in-context-regexp) (markdown-yaml :extension ".md" :date-function denote-date-rfc3339 @@ -893,7 +895,9 @@ Consult the `denote-file-types' for how this is used." :title-value-reverse-function denote-trim-whitespace-then-quotes :keywords-key-regexp "^tags\\s-*:" :keywords-value-function denote-format-keywords-for-md-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter) + :keywords-value-reverse-function denote-extract-keywords-from-front-matter + :link denote-md-link-format + :link-in-context-regexp denote-md-link-in-context-regexp) (markdown-toml :extension ".md" :date-function denote-date-rfc3339 @@ -903,7 +907,9 @@ Consult the `denote-file-types' for how this is used." :title-value-reverse-function denote-trim-whitespace-then-quotes :keywords-key-regexp "^tags\\s-*=" :keywords-value-function denote-format-keywords-for-md-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter) + :keywords-value-reverse-function denote-extract-keywords-from-front-matter + :link denote-md-link-format + :link-in-context-regexp denote-md-link-in-context-regexp) (text :extension ".txt" :date-function denote-date-iso-8601 @@ -913,7 +919,9 @@ Consult the `denote-file-types' for how this is used." :title-value-reverse-function denote-trim-whitespace :keywords-key-regexp "^tags\\s-*:" :keywords-value-function denote-format-keywords-for-text-front-matter - :keywords-value-reverse-function denote-extract-keywords-from-front-matter)) + :keywords-value-reverse-function denote-extract-keywords-from-front-matter + :link denote-org-link-format + :link-in-context-regexp denote-org-link-in-context-regexp)) "Alist of `denote-file-type' and their format properties. Each element is of the form (SYMBOL . PROPERTY-LIST). SYMBOL is @@ -959,6 +967,14 @@ PROPERTY-LIST is a plist that consists of 8 elements: retrieve the keywords' value from the front matter. It performs the reverse of the `:keywords-value-function'. +- `:link' is a string, or variable holding a string, that + specifies the format of a link. See the variables + `denote-org-link-format', `denote-md-link-format'. + +- `:link-in-context-regexp' is a regular expression that is used + to match the aforementioned link format. See the variables + `denote-org-link-in-context-regexp',`denote-md-link-in-context-regexp'. + If `denote-file-type' is nil, we use the first element of this list for new note creation. The default is `org'.") @@ -1019,6 +1035,18 @@ list for new note creation. The default is `org'.") (alist-get file-type denote-file-types) :keywords-value-reverse-function)) +(defun denote--link-format (file-type) + "Return link format extension based on FILE-TYPE." + (plist-get + (alist-get file-type denote-file-types) + :link)) + +(defun denote--link-in-context-regexp (file-type) + "Return link regexp in context based on FILE-TYPE." + (plist-get + (alist-get file-type denote-file-types) + :link-in-context-regexp)) + (defun denote--extensions () "Return all extensions in `denote-file-types'." (delete-dups @@ -2446,34 +2474,33 @@ The format of such links is `denote-md-link-format'.") "Regexp to match an identifier-only link in its context. The format of such links is `denote-id-only-link-format'." ) -(defun denote-link--file-type-format (current-file id-only) - "Return link format based on CURRENT-FILE format. +(defun denote-link--file-type-format (file-type id-only) + "Return link format based on FILE-TYPE. With non-nil ID-ONLY, use the generic link format without a -title." +title. + +Fall back to `denote-org-link-format'." ;; Includes backup files. Maybe we can remove them? - (let ((current-file-ext (file-name-extension current-file))) - (cond - (id-only denote-id-only-link-format) - ((string= current-file-ext "md") - denote-md-link-format) - ;; Plain text also uses [[denote:ID][TITLE]] - (t denote-org-link-format)))) - -(defun denote-link--file-type-regexp (file) - "Return link regexp based on FILE format." - (pcase (file-name-extension file) - ("md" denote-md-link-in-context-regexp) - (_ denote-org-link-in-context-regexp))) - -(defun denote-link--format-link (file pattern &optional description) - "Prepare link to FILE using PATTERN. + (cond + (id-only denote-id-only-link-format) + ((when-let ((link (denote--link-format file-type))) + link)) + ;; Plain text also uses [[denote:ID][TITLE]] + (t denote-org-link-format))) + +(defun denote-link--format-link (file format &optional description) + "Prepare link to FILE using FORMAT. If DESCRIPTION is non-nil, use it as link description instead of -FILE's title." +FILE's title. + +FORMAT is the symbol of a variable that specifies a string. See +the `:link' property of `denote-file-types'." (let* ((file-id (denote-retrieve-filename-identifier file)) + (fm (if (symbolp format) (symbol-value format) format)) (file-type (denote-filetype-heuristics file)) - (file-title (unless (string= pattern denote-id-only-link-format) + (file-title (unless (string= fm denote-id-only-link-format) (or description (denote--retrieve-title-or-filename file file-type))))) - (format pattern file-id file-title))) + (format fm file-id file-title))) ;;;###autoload (defun denote-link (target &optional id-only) @@ -2499,11 +2526,12 @@ whitespace-only), insert an ID-ONLY link." (buffer-substring-no-properties beg end)))) (delete-region beg end) selected-text)) - (id-only (or id-only (string-empty-p description)))) + (identifier-only (or id-only (string-empty-p description))) + (file-type (denote-filetype-heuristics (buffer-file-name)))) (insert (denote-link--format-link target - (denote-link--file-type-format (buffer-file-name) id-only) + (denote-link--file-type-format file-type identifier-only) description)) (unless (derived-mode-p 'org-mode) (make-button beg (point) 'type 'denote-link-button)))) @@ -2523,9 +2551,10 @@ whitespace-only), insert an ID-ONLY link." (defun denote-link--expand-identifiers (regexp) "Expend identifiers matching REGEXP into file paths." (let ((files (denote-directory-files)) - (found-files)) + (rx (if (symbolp regexp) (symbol-value regexp) regexp)) + found-files) (dolist (file files) - (dolist (i (denote-link--collect-identifiers regexp)) + (dolist (i (denote-link--collect-identifiers rx)) (when (string-prefix-p i (file-name-nondirectory file)) (push file found-files)))) found-files)) @@ -2546,7 +2575,9 @@ whitespace-only), insert an ID-ONLY link." (defun denote-link-find-file () "Use minibuffer completion to visit linked file." (interactive) - (if-let* ((regexp (denote-link--file-type-regexp (buffer-file-name))) + (if-let* ((current-file (buffer-file-name)) + (file-type (denote-filetype-heuristics current-file)) + (regexp (denote--link-in-context-regexp file-type)) (files (denote-link--expand-identifiers regexp))) (find-file (denote-get-path-by-id @@ -2853,11 +2884,14 @@ inserts links with just the identifier." (list (read-regexp "Insert links matching REGEX: " nil 'denote-link--add-links-history) current-prefix-arg)) - (let ((current-file (buffer-file-name))) - (if-let ((files (delete current-file (denote-directory-files-matching-regexp regexp))) + (let* ((current-file (buffer-file-name)) + (file-type (denote-filetype-heuristics current-file))) + (if-let ((files (assoc-delete-all + current-file + (denote-directory-files-matching-regexp regexp))) (beg (point))) (progn - (insert (denote-link--prepare-links files current-file id-only)) + (insert (denote-link--prepare-links files file-type id-only)) (denote-link-buttonize-buffer beg (point))) (message "No links matching `%s'" regexp)))) @@ -2876,14 +2910,16 @@ inserts links with just the identifier." (read-regexp "Insert links matching REGEX: " nil 'denote-link--add-links-history) current-prefix-arg)) (let* ((current-file (buffer-file-name)) - (current-id (denote-link--file-type-regexp current-file)) + (file-type (denote-filetype-heuristics current-file)) + (current-id (denote--link-in-context-regexp file-type)) (linked-files (denote-link--expand-identifiers current-id))) - (if-let* ((found-files (delete current-file - (denote-directory-files-matching-regexp regexp))) + (if-let* ((found-files (assoc-delete-all + current-file + (denote-directory-files-matching-regexp regexp))) (final-files (seq-difference found-files linked-files)) (beg (point))) (progn - (insert (denote-link--prepare-links final-files current-file id-only)) + (insert (denote-link--prepare-links final-files file-type id-only)) (denote-link-buttonize-buffer beg (point))) (message "No links matching `%s' that aren't yet present in the current buffer" regexp)))) @@ -2945,7 +2981,10 @@ This command is meant to be used from a Dired buffer." (user-error "No note files to link to") (when (y-or-n-p (format "Create links at point in %s?" buffer)) (with-current-buffer buffer - (insert (denote-link--prepare-links files (buffer-file-name) id-only)) + (insert (denote-link--prepare-links + files + (denote-filetype-heuristics (buffer-file-name)) + id-only)) (denote-link-buttonize-buffer))))) ;;;;; Register `denote:' custom Org hyperlink