[Top][All Lists]

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

[elpa] externals/marginalia ee24a23 2/2: Add annotator caching

From: ELPA Syncer
Subject: [elpa] externals/marginalia ee24a23 2/2: Add annotator caching
Date: Mon, 26 Jul 2021 04:57:14 -0400 (EDT)

branch: externals/marginalia
commit ee24a2390fb472a549224ae4a084958b4132ea82
Author: Daniel Mendler <mail@daniel-mendler.de>
Commit: Daniel Mendler <mail@daniel-mendler.de>

    Add annotator caching
    * Helps when scrolling in incremental UIs
    * Does not help much when filtering
    * Does not help in non-incremental UIs
    Since Marginalia is a major performance offender, I decided to add the 
    here for now. Alternatively the caching layer can be pushed to the affected
    completion UIs, in particular Vertico. However the `affixation-function` is 
    problem then, since then one has to check the list of candidates first 
    the list of already cached candidates and then call the 
    once on the new candidates, in order to make only a single call to the
    `affixation-function`. Hopefully we can replace the `affixation-function` 
by an
    improved `decoration-function` soon.
 marginalia.el | 52 +++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/marginalia.el b/marginalia.el
index f9524aa..e94e515 100644
--- a/marginalia.el
+++ b/marginalia.el
@@ -257,6 +257,14 @@ determine it."
 ;;;; Marginalia mode
+(defvar-local marginalia--cache nil
+  "The cache, pair of list and hashtable.")
+(defvar marginalia--cache-size 100
+  "Size of the cache, set to 0 to disable the cache.
+Disabling the cache is useful on non-incremental UIs like default completion or
+for performance profiling of the annotators.")
 (defvar marginalia--separator "    "
   "Field separator.")
@@ -784,9 +792,11 @@ looking for a regexp that matches the prompt."
   "Setup annotator context with completion METADATA around BODY."
   (declare (indent 1))
   (let ((w (make-symbol "w"))
+        (c (make-symbol "c"))
         (o (make-symbol "o")))
     ;; Take the window width of the current window (minibuffer window!)
     `(let ((marginalia--metadata ,metadata)
+           (,c marginalia--cache)
            (,w (window-width))
            ;; Compute marginalia-align-offset. If the right-fringe-width is
            ;; zero, use an additional offset of 1 by default! See
@@ -798,13 +808,40 @@ looking for a regexp that matches the prompt."
        ;; Otherwise it would probably suffice to only change the current 
        ;; We need the `selected-window' fallback for Embark Occur.
        (with-selected-window (or (minibuffer-selected-window) 
-         (let ((marginalia-truncate-width (min (/ ,w 2) 
+         (let ((marginalia--cache ,c) ;; Take the cache from the minibuffer
+               (marginalia-truncate-width (min (/ ,w 2) 
                (marginalia-align-offset (or marginalia-align-offset ,o))
                (marginalia--separator (if (>= ,w 
marginalia-separator-threshold) "    " " "))
                (marginalia--margin (when (>= ,w (+ marginalia-margin-min 
                                      (make-string (- ,w 
marginalia-margin-threshold) 32))))
+(defun marginalia--cache-reset ()
+  "Reset the cache."
+  (when marginalia--cache
+    (setq marginalia--cache (and (> marginalia--cache-size 0)
+                                 (cons nil (make-hash-table :test #'equal
+                                                            :size 
+(defun marginalia--cached (fun key)
+  "Cached application of function FUN with KEY.
+The cache keeps around the last `marginalia--cache-size' computed annotations.
+The cache is mainly useful when scrolling in completion UIs like Vertico or
+  (if marginalia--cache
+      (let ((ht (cdr marginalia--cache)))
+        (or (gethash key ht)
+            (let ((val (funcall fun key)))
+              (setcar marginalia--cache (cons key (car marginalia--cache)))
+              (puthash key val ht)
+              (when (>= (hash-table-count ht) marginalia--cache-size)
+                (let ((end (last (car marginalia--cache) 2)))
+                  (remhash (cadr end) ht)
+                  (setcdr end nil)))
+              val)))
+    (funcall fun key)))
 (defun marginalia--completion-metadata-get (metadata prop)
   "Meant as :before-until advice for `completion-metadata-get'.
 METADATA is the metadata.
@@ -816,7 +853,7 @@ PROP is the property which is looked up."
                  (annotate (marginalia--annotator cat)))
        (lambda (cand)
          (marginalia--context metadata
-           (funcall annotate cand)))))
+           (marginalia--cached annotate cand)))))
      ;; We do want the advice triggered for `completion-metadata-get'.
      ;; Return wrapper around `annotation-function'.
@@ -824,7 +861,7 @@ PROP is the property which is looked up."
                  (annotate (marginalia--annotator cat)))
        (lambda (cands)
          (marginalia--context metadata
-           (mapcar (lambda (x) (list x "" (or (funcall annotate x) ""))) 
+           (mapcar (lambda (x) (list x "" (or (marginalia--cached annotate x) 
""))) cands)))))
      ;; Find the completion category by trying each of our classifiers.
      ;; Store the metadata for `marginalia-classify-original-category'.
@@ -834,14 +871,18 @@ PROP is the property which is looked up."
 (defun marginalia--minibuffer-setup ()
   "Setup minibuffer for `marginalia-mode'.
 Remember `this-command' for `marginalia-classify-by-command-name'."
-  (setq marginalia--this-command this-command))
+  (setq marginalia--cache t marginalia--this-command this-command)
+  (marginalia--cache-reset))
 (defun marginalia--base-position (completions)
   "Record the base position of COMPLETIONS."
   ;; NOTE: As a small optimization track the base position only for file 
   ;; since `marginalia--full-candidate' is only used for files as of now.
   (when minibuffer-completing-file-name
-    (setq marginalia--base-position (or (cdr (last completions)) 0)))
+    (let ((base (or (cdr (last completions)) 0)))
+      (unless (= marginalia--base-position base)
+        (marginalia--cache-reset)
+        (setq marginalia--base-position base))))
@@ -879,6 +920,7 @@ Remember `this-command' for 
           (setq cat (assq cat marginalia-annotator-registry))
           (unless cat
             (user-error "Marginalia: No annotators found"))
+          (marginalia--cache-reset)
           (setcdr cat (append (cddr cat) (list (cadr cat))))
           ;; When the builtin annotator is selected and no builtin function is 
available, skip to
           ;; the next annotator. Note that we cannot use 
`completion-metadata-get' to access the

reply via email to

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