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

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

[elpa] externals/denote 6bb6da0882 1/6: feat: project.el integration & b


From: ELPA Syncer
Subject: [elpa] externals/denote 6bb6da0882 1/6: feat: project.el integration & backlinks refactoring with xref
Date: Mon, 21 Nov 2022 22:57:30 -0500 (EST)

branch: externals/denote
commit 6bb6da088211324cdd1ad35396c2988ccb2eccb7
Author: Noboru Ota <me@nobiot.com>
Commit: Protesilaos Stavrou <info@protesilaos.com>

    feat: project.el integration & backlinks refactoring with xref
    
    The main intent of the patch is as follows:
    
    - To simplify the implementations of functions related to backlinks that
      rely on the Xref library
    
    - To add minimal implementations to integrate Denote with project.el
    
      This is necessary for simplification of Xref/backlinks mentioned
      above.
    
      It has given an opportunity to refactor 'denote-file-prompt' to let
      you search notes under all sub-directories of 'denote-directory'.  The
      original 'read-file-name' could only search within only single
      'denote-directory'.
    
      This enhances the following commands: 'denote-open-or-create',
      'denote-link', 'denote-link-or-create', and 'denote-link-ol-complete'.
    
      The project.el integration also lets users set something like this
      below in their configuration.  This way, they can generically use
      project.el facilities for Denote such as 'project-find-file' and
      'project-find-regexp' without a version management tool (e.g. Git)
    
          (add-hook 'project-find-functions #'denote-project-find)
    
    Following is more detail of changes for each function:
    
    * denote.el
    
    (denote-file-prompt): Refactored
    
    - To use the same completion function as 'project-find-file' does.
    
    - The main benefit is that it can let you search notes under all
      sub-directories of 'denote-directory'.  The original 'read-file-name'
      could only search within only single 'denote-directory'
    
    - Note the impact on commands that use 'denote-file-prompt';
      i.e. 'denote-open-or-create', 'denote-link', 'denote-link-or-create',
      and 'denote-link-ol-complete'
    
    - One minor annoyance may be that the prompt now requires a confirmation
      if the user enters text that does not match any of the candidate and
      tries to exit
    
    - 'denote--title-history' is directly updated from the minibuffer
      completion function.  This eliminates the need for
      'denote--push-extracted-title-to-history'.  The original used
      'file-name-history' as the intermediate storage of titles, which are
      not really file names, thus resulted in polluting the history for file
      names
    
    (denote--retrieve-xrefs): Removed
    
    - No longer used. It was only used by 'denote--retrieve-process-grep',
      which has now been removed.
    
    (denote--retrieve-files-in-xrefs): Refactored
    
    - To take IDENTIFIER as the argument.  No change to the returned
      values. This is for this function to be compatible with the removal of
      'denote--retrieve-xrefs'.  Retrieving files do not need to use the
      intermediate xref-alist, which is a duplicate of work.  This change
      lets this function directly retrieve file name (group) from xrefs (not
      xref-alist) with using Xref public methods
    
    (denote--retrieve-process-grep): Removed
    
    - No longer used. It was only used by 'denote-link--prepare-backlinks'
      to retrieve xref-alist for the purpose of creating the backlinks
      buffer.  Creation of the backlinks buffer has now been refactored to
      get closer to the built-in Xref.  See 'denote-link-find-backlink' and
      'denote-link--prepare-backlinks'
    
    (denote--push-extracted-title-to-history): Removed
    
    - No longer used. See commit message fro 'denote-file-prompt' above
    
    (denote-open-or-create): Refactored
    
    - To not use 'denote--push-extracted-title-to-history', which has been
      removed
    
    (denote-link-find-backlink): Refactored
    
    - To not use 'denote--retrieve-xrefs', which has been removed
    
    (denote-link-or-create): Refactored
    
    - To not use 'denote--push-extracted-title-to-history', which has been
      removed
    
    (denote-backlinks-mode): Refactored
    
    - To locally set 'denote-project-find'. This is be compatible with the
      new project.el integration.  The change is necessary for the backlinks
      buffer to correctly revert by identifying the denote project root
    
    (denote-link--prepare-backlinks): Refactored
    
    - To set the correct revert function with using the new way
    
    (denote-link-backlinks): Refactored
    
    - To use 'xref--show-xrefs' to create and show backlinks buffer.  The
      double-hyphen in the name 'xref--show-xrefs' suggests it is a private
      function; however, in the current development branch of Xref, there is
      a new public function 'xref-show-xrefs'.  It is a wrapper function
      with the same arguments used in this patch, thus we are further
      aligning Denote with the direction of Xref development
    
    (project-root): (project-files): (denote-project-find): Added
    
    - These are Denote specific implementations of generic functions defined
      in project.el and a function for a hook to let Denote use them.  This
      patch contains many built-in Xref functions, which rely on project.el
      to identify the "root" of the project.  Denote explicitly defines its
      root with 'denote-directory'. These newly created methods and function
      connect Denote and project.el generic.  This way, users without a
      version management tool (e.g. Git) for their notes can benefit from
      project.el.  See one of such major benefits with 'denote-file-prompt'
      and the related Denote commands above.
    
    - This change also let users set something like this below in their
      configuration to generically use project.el facilities such as
      'project-find-file'.
    
       (add-hook 'project-find-functions #'denote-project-find)
    
    * denote-org-dblock.el
    
    (org-dblock-write:denote-backlinks): Refactored
    
    -  To align with the change in 'denote--retrieve-files-in-xrefs' as
      above
---
 denote-org-dblock.el |   3 +-
 denote.el            | 155 ++++++++++++++++++++++++++-------------------------
 2 files changed, 80 insertions(+), 78 deletions(-)

diff --git a/denote-org-dblock.el b/denote-org-dblock.el
index ce1026ab65..0f10456a1d 100644
--- a/denote-org-dblock.el
+++ b/denote-org-dblock.el
@@ -139,8 +139,7 @@ Used by `org-dblock-update' with PARAMS provided by the 
dynamic block."
 Used by `org-dblock-update' with PARAMS provided by the dynamic block."
   (when-let* ((file (buffer-file-name))
               (id (denote-retrieve-filename-identifier file))
-              (files (denote--retrieve-files-in-xrefs
-                      (denote--retrieve-process-grep id))))
+              (files (delete file (denote--retrieve-files-in-xrefs id)))))
     (insert (denote-link--prepare-links files file nil))
     (join-line))) ;; remove trailing empty line
 
diff --git a/denote.el b/denote.el
index a6c26fe7d1..b472d207d7 100644
--- a/denote.el
+++ b/denote.el
@@ -719,10 +719,13 @@ whatever matches `denote-excluded-directories-regexp'."
 (defun denote-file-prompt (&optional initial-text)
   "Prompt for file with identifier in variable `denote-directory'.
 With optional INITIAL-TEXT, use it to prepopulate the minibuffer."
-  (read-file-name "Select note: " (denote-directory) nil nil initial-text
-                  (lambda (f)
-                    (or (denote-file-has-identifier-p f)
-                        (denote-file-directory-p f)))))
+  (let* ((project-find-functions #'denote-project-find)
+         (project (project-current nil (denote-directory)))
+         (dirs (list (project-root project)))
+         (all-files (project-files project dirs))
+         (completion-ignore-case read-file-name-completion-ignore-case))
+    (funcall project-read-file-name-function
+             "Select note: " all-files nil 'denote--title-history 
initial-text)))
 
 (define-obsolete-function-alias
   'denote--retrieve-read-file-prompt
@@ -1245,50 +1248,16 @@ Run `denote-desluggify' on title if the extraction is 
sucessful."
       title
     (denote-retrieve-filename-title file)))
 
-(defun denote--retrieve-xrefs (identifier &optional file)
-  "Return xrefs of IDENTIFIER in variable `denote-directory'.
-The xrefs are returned as an alist of the form:
-
-    ((GROUP . (XREF ...)) ...)
-
-GROUP is an absolute file name as retrieved by Xref facility.
-
-When FILE is present, remove its GROUP from the alist."
-  (let ((alist
-         (xref--alistify
-          (xref-matches-in-files identifier (denote-directory-text-only-files))
-          (lambda (x)
-            (xref-location-group (xref-item-location x))))))
-    (if file (assoc-delete-all file alist) alist)))
-
-(defun denote--retrieve-files-in-xrefs (xref-alist)
-  "Return sorted, deduplicated file names from XREF-ALIST."
+(defun denote--retrieve-files-in-xrefs (identifier)
+  "Return sorted, deduplicated file names from IDENTIFIER."
   (sort
-   (delete-dups (mapcar #'car xref-alist))
+   (delete-dups
+    (mapcar #'xref-location-group
+            (mapcar #'xref-match-item-location
+                    (xref-matches-in-files identifier
+                                           
(denote-directory-text-only-files)))))
    #'string-lessp))
 
-(defun denote--retrieve-process-grep (identifier)
-  "Process lines matching IDENTIFIER and return list of xref-alist.
-
-The alist is of the form ((GROUP . (XREF ...)) ...).
-
-The alist excludes GROUP for the file that current buffer is
-visiting so that only its backlinks are colleced.
-
-In addition, GROUP is a transformed to filename relative to
-variable `denote-directory', which is the string displayed in the
-backlinks' buffer."
-  ;;; This `mapcar' form is doing what function `xref--analyze' would
-  ;;; do.  `xref--analyze' can be flexibly configured but is not used
-  ;;; directly here because it assumes that the current directory is in
-  ;;; a "project" as defined in project.el.  For Denote, this is not the
-  ;;; case (at least as at the time of this writing).
-  (mapcar
-   (lambda (xref)
-     (cons (denote-get-file-name-relative-to-denote-directory (car xref))
-           (cdr xref)))
-   (denote--retrieve-xrefs identifier (buffer-file-name))))
-
 ;;;; New note
 
 ;;;;; Common helpers for new notes
@@ -1726,13 +1695,6 @@ set to \\='(template title keywords)."
     (string-match (denote-directory) title)
     (substring title (match-end 0))))
 
-(defun denote--push-extracted-title-to-history ()
-  "Add `denote--extract-title-from-file-history' to `denote--title-history'."
-  (when-let* ((last-input (denote--extract-title-from-file-history))
-              ((not (string-empty-p last-input)))
-              ((not (string-blank-p last-input))))
-    (push last-input denote--title-history)))
-
 ;;;###autoload
 (defun denote-open-or-create (target)
   "Visit TARGET file in variable `denote-directory'.
@@ -1747,7 +1709,6 @@ note's actual title.  At the `denote-title-prompt' type
   (interactive (list (denote-file-prompt)))
   (if (file-exists-p target)
       (find-file target)
-    (denote--push-extracted-title-to-history)
     (call-interactively #'denote)))
 
 ;;;###autoload
@@ -2635,9 +2596,7 @@ Like `denote-link-find-file', but select backlink to 
follow."
   (interactive)
   (if-let* ((file (buffer-file-name))
             (id (denote-retrieve-filename-identifier file))
-            (files
-             (denote--retrieve-files-in-xrefs
-              (denote--retrieve-xrefs id (buffer-file-name)))))
+            (files (delete file (denote--retrieve-files-in-xrefs id))))
       (find-file
        (denote-get-path-by-id
         (denote-extract-id-from-string
@@ -2694,7 +2653,6 @@ file's title.  This has the same meaning as in 
`denote-link'."
   (interactive (list (denote-file-prompt) current-prefix-arg))
   (if (file-exists-p target)
       (denote-link target id-only)
-    (denote--push-extracted-title-to-history)
     (call-interactively #'denote-link-after-creating)))
 
 (defalias 'denote-link-to-existing-or-new-note (symbol-function 
'denote-link-or-create))
@@ -2823,24 +2781,37 @@ nil)."
 (define-derived-mode denote-backlinks-mode xref--xref-buffer-mode "Backlinks"
   "Major mode for backlinks buffers."
   (unless denote-backlinks-show-context
-    (font-lock-add-keywords nil denote-faces-file-name-keywords-for-backlinks 
t)))
+    (font-lock-add-keywords nil denote-faces-file-name-keywords-for-backlinks 
t))
+  (add-hook 'project-find-functions #'denote-project-find nil t))
 
 (make-obsolete-variable 'denote-backlink-mode 'denote-backlinks-mode "0.6.0")
 
-(defun denote-link--prepare-backlinks (id xref-alist &optional title)
-  "Create backlinks' buffer for ID using XREF-ALIST.
-Use optional TITLE for a prettier heading."
-  (let ((inhibit-read-only t)
-        (buf (format "*denote-backlinks to %s*" id))
-        (file (buffer-file-name)))
+(defun denote-link--prepare-backlinks (fetcher _alist)
+  "Create backlinks' buffer for the current note.
+FETCHER is a function that fetches a list of xrefs.  It is called
+with `funcall' with no argument like `xref--fetcher'.
+
+In the case of `denote', `apply-partially' is used to create a
+function that has already applied another function to multiple
+arguments.
+
+ALIST is not used in favour of using
+`denote-link-backlinks-display-buffer-action'."
+  (let* ((inhibit-read-only t)
+         (file (buffer-file-name))
+         (file-type (denote-filetype-heuristics file))
+         (id (denote-retrieve-filename-identifier file))
+         (buf (format "*denote-backlinks to %s*" id))
+         (xref-alist (xref--analyze (funcall fetcher))))
     (with-current-buffer (get-buffer-create buf)
       (setq-local default-directory (denote-directory))
       (erase-buffer)
+      (setq overlay-arrow-position nil)
       (denote-backlinks-mode)
       (goto-char (point-min))
-      (when-let* ((title)
-                  (heading (format "Backlinks to %S (%s)" title id))
-                  (l (length heading)))
+      (when-let*  ((title (denote-retrieve-title-value file file-type))
+                   (heading (format "Backlinks to %S (%s)" title id))
+                   (l (length heading)))
         (insert (format "%s\n%s\n\n" heading (make-string l ?-))))
       (if denote-backlinks-show-context
           (xref--insert-xrefs xref-alist)
@@ -2852,9 +2823,11 @@ Use optional TITLE for a prettier heading."
       (goto-char (point-min))
       (setq-local revert-buffer-function
                   (lambda (_ignore-auto _noconfirm)
-                    (when-let ((buffer-file-name file)
-                               (xref-alist (denote--retrieve-process-grep id)))
-                      (denote-link--prepare-backlinks id xref-alist title)))))
+                    (when-let ((buffer-file-name file))
+                      (denote-link--prepare-backlinks
+                       (apply-partially #'xref-matches-in-files id
+                                        (delete file 
(denote-directory-text-only-files)))
+                       nil)))))
     (denote-link--display-buffer buf)))
 
 ;;;###autoload
@@ -2875,12 +2848,14 @@ default, it will show up below the current window."
   (let ((file (buffer-file-name)))
     (when (denote-file-is-writable-and-supported-p file)
       (let* ((id (denote-retrieve-filename-identifier file))
-             (file-type (denote-filetype-heuristics file))
-             (title (denote-retrieve-title-value file file-type)))
-        (if-let ((xref-alist (denote--retrieve-process-grep id)))
-            (progn (xref--push-markers)
-                   (denote-link--prepare-backlinks id xref-alist title))
-          (user-error "No links to the current note"))))))
+             (xref-show-xrefs-function #'denote-link--prepare-backlinks)
+             (project-find-functions #'denote-project-find))
+        (xref--show-xrefs
+         (apply-partially #'xref-matches-in-files id
+                          ;; remove the current buffer file from the
+                          ;; backlinks
+                          (delete file (denote-directory-text-only-files)))
+         nil)))))
 
 (defalias 'denote-link-show-backlinks-buffer (symbol-function 
'denote-link-backlinks))
 
@@ -3181,5 +3156,33 @@ Consult the manual for template samples."
 (make-obsolete 'denote-migrate-old-org-filetags nil "1.1.0")
 (make-obsolete 'denote-migrate-old-markdown-yaml-tags nil "1.1.0")
 
+
+;;;; project.el integration
+;;   This is also used by xref integration
+
+(cl-defmethod project-root ((project (head denote)))
+  "Denote's implementation of `project-root' method from `project'.
+Return current variable `denote-directory' as the root of the
+current denote PROJECT."
+  (cdr project))
+
+(cl-defmethod project-files ((_project (head denote)) &optional _dirs)
+  "Denote's implementation of `project-files' method from `project'.
+Return all files that have an identifier for the current denote
+PROJECT.  The return value may thus include file types that are
+not implied by `denote-file-type'.  To limit the return value to
+text files, use the function `denote-directory-text-only-files'."
+  (denote-directory-files))
+
+(defun denote-project-find (dir)
+  "Return project instance if DIR is part of variable `denote-directory'.
+The format of project instance is aligned with `project-try-vc'
+defined in `project'."
+  (let ((dir (expand-file-name dir)) ; canonicalize current directory name
+        (root (denote-directory)))
+    (when (or (file-equal-p dir root) ; currently at `denote-directory'
+              (string-prefix-p root dir)) ; or its subdirectory
+      (cons 'denote root))))
+
 (provide 'denote)
 ;;; denote.el ends here



reply via email to

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