emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/denote 3df88a05d0 096/355: Make linking aware of file t


From: ELPA Syncer
Subject: [elpa] externals/denote 3df88a05d0 096/355: Make linking aware of file types (extends 299b897)
Date: Sun, 26 Jun 2022 23:58:03 -0400 (EDT)

branch: externals/denote
commit 3df88a05d0dea7590811b7468537df62ee564660
Author: Protesilaos Stavrou <info@protesilaos.com>
Commit: Protesilaos Stavrou <info@protesilaos.com>

    Make linking aware of file types (extends 299b897)
    
    I think this is a cool feature, especially the ability to read the
    relevant file in order to use the correct link format.
---
 README.org     |  36 ++++++++++++----
 denote-link.el | 129 +++++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 127 insertions(+), 38 deletions(-)

diff --git a/README.org b/README.org
index 661e31dd46..f308b471ca 100644
--- a/README.org
+++ b/README.org
@@ -225,20 +225,40 @@ Denote has a basic linking facility to quickly establish 
connections
 between notes.  The command ~denote-link~ prompts for a file name in the
 ~denote-directory~ (only regular files are considered, not directories).
 It then retrieves the path of the given note, inserts it at point using
-the link notation of Org, and creates a backlink entry in the target
-file.
+the appropriate link notation, and creates a backlink entry in the
+target file (again using the appropriate notation).
+
+What constitutes "appropriate link notation" depends on the file type of
+the given entry per ~denote-file-type~ 
([[#h:4e9c7512-84dc-4dfb-9fa9-e15d51178e5d][The file naming scheme]]).  For
+example when linking from an Org file to a Markdown file, the link in
+the former will follow Org syntax while the backlink in the latter will
+use that of Markdown.  Org links use =[[file:TARGET][DESCRIPTION]]=,
+those of Markdown are =[DESCRIPTION](file:TARGET)=, while for plain text
+we implement our own scheme of =<TYPE: TARGET> [DESCRIPTION]=, where
+=TYPE= is either =LINK= or =BACKLINK= (capitalization in the latter two
+is literal, because plain text lacks other means of emphasis).
+
+Plain text links can benefit from Emacs' notion of "future history",
+else its ability to read the thing at point for relevant commands.  With
+point over the =TARGET=, =M-x find-file= followed by =M-n= will fill the
+path to that file (this also works with point over just the identifier
+of a note).
 
 Backlinks are recorded at the end of a note under the heading with the
-title =Denote backlinks=.  Users should not edit this part manually: it
-is controlled by Denote, such as to delete duplicate links (in the
-future it might also handle stuff like alphabetic sorting).
+title =Denote backlinks=.  Users should not edit the note below this
+part manually: it is controlled by Denote, such as to delete duplicate
+links (in the future it might also handle stuff like alphabetic
+sorting).
+
+The section with the backlinks is formatted according to the note's file
+type.
 
 #+vindex: denote-link-insert-functions
 #+findex: denote-link-backlink
 The special hook ~denote-link-insert-functions~ is called after a link
-is created.  It accepts two arguments for the target file and the origin
-of the current link.  The function ~denote-link-backlink~ provides an
-example for advanced users.
+is created.  It accepts two arguments for the target file and the
+formatted backlink to the original file.  The function
+~denote-link-backlink~ provides an example for advanced users.
 
 #+findex: denote-link-clear-stale-backlinks
 Backlinks that no longer point to available notes can be removed from
diff --git a/denote-link.el b/denote-link.el
index e07d31e968..64619be262 100644
--- a/denote-link.el
+++ b/denote-link.el
@@ -24,7 +24,46 @@
 
 ;;; Commentary:
 ;;
-;; Links for denote.
+;; Denote has a basic linking facility to quickly establish connections
+;; between notes.  The command `denote-link' prompts for a file name in the
+;; `denote-directory' (only regular files are considered, not directories).
+;; It then retrieves the path of the given note, inserts it at point using
+;; the appropriate link notation, and creates a backlink entry in the
+;; target file (again using the appropriate notation).
+;;
+;; What constitutes "appropriate link notation" depends on the file type
+;; of the given entry per `denote-file-type' (see "The file naming
+;; scheme" in the manual).  For example when linking from an Org file to
+;; a Markdown file, the link in the former will follow Org syntax while
+;; the backlink in the latter will use that of Markdown.  Org links use
+;; `[[file:TARGET][DESCRIPTION]]', those of Markdown are
+;; `[DESCRIPTION](file:TARGET)', while for plain text we implement our
+;; own scheme of `<TYPE: TARGET> [DESCRIPTION]', where `TYPE' is either
+;; `LINK' or `BACKLINK' (capitalization in the latter two is literal,
+;; because plain text lacks other means of emphasis).
+;;
+;; Plain text links can benefit from Emacs' notion of "future history",
+;; else its ability to read the thing at point for relevant commands.  With
+;; point over the `TARGET', `M-x find-file' followed by `M-n' will fill the
+;; path to that file (this also works with point over just the identifier
+;; of a note).
+;;
+;; Backlinks are recorded at the end of a note under the heading with the
+;; title `Denote backlinks'.  Users should not edit the note below this
+;; part manually: it is controlled by Denote, such as to delete duplicate
+;; links (in the future it might also handle stuff like alphabetic
+;; sorting).
+;;
+;; The section with the backlinks is formatted according to the note's file
+;; type.
+;;
+;; The special hook `denote-link-insert-functions' is called after a link
+;; is created.  It accepts two arguments for the target file and the
+;; formatted backlink to the original file.  The function
+;; `denote-link-backlink' provides an example for advanced users.
+;;
+;; Backlinks that no longer point to available notes can be removed from
+;; the current buffer with the command `denote-link-clear-stale-backlinks'.
 
 ;;; Code:
 
@@ -39,55 +78,75 @@
 (defcustom denote-link-insert-functions
   (list #'denote-link-backlink)
   "Functions that run after `denote-link'.
-Each function accepts a TARGET-FILE and an ORIGIN-LINK argument.
-Both are supplied by `denote-link'."
+Each function accepts a TARGET file and a BACKLINK argument.
+Both are supplied by `denote-link'.  Advanced users are
+encouraged to study `denote-link-backlink' for how those
+arguments are used."
   :type 'hook
   :group 'denote-link)
 
 ;;;; Link to note
 
-(defun denote-link--find-key-value-pair (regexp)
-  "Produce a cons cell from REGEXP by searching the file."
+(defun denote-link--find-value (regexp)
+  "Return value from REGEXP by searching the file."
   (goto-char (point-min))
   (re-search-forward regexp)
-  (cons (match-string-no-properties 1)
-        (match-string-no-properties 2)))
+  (match-string-no-properties 3))
 
-(defconst denote-link--title-regexp "^\\(#\\+title:\\)[\s\t]+\\(.*\\)"
+(defconst denote-link--title-regexp "^\\(#\\+\\)?\\(title:\\)[\s\t]+\\(.*\\)"
   "Regular expression for title key and value.")
 
-(defconst denote-link--identifier-regexp 
"^\\(#\\+identifier:\\)[\s\t]+\\(.*\\)"
+(defconst denote-link--identifier-regexp 
"^\\(#\\+\\)?\\(identifier:\\)[\s\t]+\\(.*\\)"
   "Regular expression for filename key and value.")
 
-(defconst denote-link--link-format "[[file:%s][%s (%s)]]"
+(defconst denote-link--link-format-org "[[file:%s][%s (%s)]]"
   "Format of Org link to note.")
 
-(defconst denote-link--backlink-format "[[file:%s][backlink: %s (%s)]]"
+(defconst denote-link--backlink-format-org "[[file:%s][backlink: %s (%s)]]"
   "Format of Org backlink to note.")
 
+(defconst denote-link--link-format-md "[%2$s (%3$s)](file:%1$s)"
+  "Format of Markdown link to note.")
+
+(defconst denote-link--backlink-format-md "[backlink: %2$s (%3$s)](file:%1$s)"
+  "Format of Markdown backlink to note.")
+
+(defconst denote-link--link-format-txt "<LINK: %s> [NAME %s (%s)]"
+  "Format of plain text link to note.")
+
+(defconst denote-link--backlink-format-txt "BACKLINK: <%s> [NAME %s (%s)]"
+  "Format of plain text backlink to note.")
+
 (defconst denote-link--backlink-regexp "\\[\\[file:\\(.*?\\)\\]\\[backlink: 
\\(.*?\\) (\\(.*?\\))\\]\\]"
-  "Regexp of `denote-link--backlink-format'.")
+  "Regexp of `denote-link--backlink-format-org'.")
 
 (defun denote-link--retrieve-value (note regexp)
   "Return REGEXP value from NOTE."
   (let ((default-directory (denote-directory)))
     (with-temp-buffer
       (insert-file-contents-literally note)
-      (denote-link--find-key-value-pair regexp))))
+      (denote-link--find-value regexp))))
 
 (defun denote-link--read-file-prompt ()
   "Prompt for regular file in variable `denote-directory'."
   (read-file-name "Select note: " (denote-directory)
-                  nil t nil #'file-regular-p)) ; Includes backup files.  Maybe 
we can remove them?
-
-(defun denote-link--format-link (file &optional backlink)
-  "Format link to FILE.
+                  nil t nil #'file-regular-p))
+
+(defun denote-link--file-type-format (file &optional backlink)
+  "Return link pattern based on FILE format.
+With optional BACKLINK, return a backlink pattern"
+  (pcase (file-name-extension file)
+    ("md" (if backlink denote-link--backlink-format-md 
denote-link--link-format-md))
+    ("txt" (if backlink denote-link--backlink-format-txt 
denote-link--link-format-txt))
+    (_ (if backlink denote-link--backlink-format-org 
denote-link--link-format-org)))) ; Includes backup files.  Maybe we can remove 
them?
+
+(defun denote-link--format-link (file pattern)
+  "Prepare link to FILE using PATTERN.
 With optional BACKLINK, format it as a backlink."
   (let* ((dir (denote-directory))
-         (file-id (cdr (denote-link--retrieve-value file 
denote-link--identifier-regexp)))
+         (file-id (denote-link--retrieve-value file 
denote-link--identifier-regexp))
          (file-path (file-name-completion file-id dir))
-         (file-title (cdr (denote-link--retrieve-value file 
denote-link--title-regexp)))
-         (pattern (if backlink denote-link--backlink-format 
denote-link--link-format)))
+         (file-title (denote-link--retrieve-value file 
denote-link--title-regexp)))
     (format pattern file-path file-title file-id)))
 
 ;;;###autoload
@@ -95,28 +154,38 @@ With optional BACKLINK, format it as a backlink."
   "Create Org link to TARGET note in variable `denote-directory'.
 Run `denote-link-insert-functions' afterwards."
   (interactive (list (denote-link--read-file-prompt)))
-  (let* ((target-link (denote-link--format-link target))
-         (origin-link (denote-link--format-link (buffer-file-name) :backlink)))
-    (insert target-link)
-    (run-hook-with-args 'denote-link-insert-functions target origin-link)))
+  (let* ((origin (buffer-file-name))
+         (link (denote-link--format-link target (denote-link--file-type-format 
origin)))
+         (backlink (denote-link--format-link origin 
(denote-link--file-type-format target :backlink))))
+    (insert link)
+    (run-hook-with-args 'denote-link-insert-functions target backlink)))
 
 (defconst denote-link-backlink-heading "Denote backlinks"
   "String of the backlink's heading.
 This heading is appended to a file when another links to it.")
 
-(defun denote-link-backlink (target-file origin-link)
-  "Insert ORIGIN-LINK to TARGET-FILE."
+(defun denote-link--format-backlinks-heading (heading)
+  "Format HEADING for backlinks."
+  (let* ((ext (file-name-extension (buffer-file-name)))
+         (markup (if (string= ext "org") "*" "#"))
+         (warning "Do not edit past this line; this is for denote.el and 
related.")
+         (comment (if (string= ext "org")
+                      (format "# %s" warning)
+                    (format "<!-- %s -->" warning))))
+    (format "%s\n%s %s\n\n" comment markup heading)))
+
+(defun denote-link-backlink (target backlink)
+  "Insert BACKLINK to TARGET file."
   (let ((default-directory (denote-directory))
         (heading denote-link-backlink-heading)
         heading-point)
-    (with-current-buffer (find-file-noselect target-file)
+    (with-current-buffer (find-file-noselect target)
       (goto-char (point-max))
       (unless (save-excursion (setq heading-point (re-search-backward heading 
nil t)))
         (unless (denote--line-regexp-p 'empty 0)
           (newline))
-        (insert
-         (format "* %s\n%s\n\n" heading "# Do not edit; this is for denote.el 
and related")))
-      (insert (format "- %s\n" origin-link))
+        (insert (denote-link--format-backlinks-heading heading)))
+      (insert (format "- %s\n" backlink))
       ;; delete duplicate links
       (when heading-point
         (delete-duplicate-lines heading-point (point-max) nil nil t)))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]