branch: externals/vertico
commit 7fbaeaf06b988db85e9f38e2d410376997d493c5
Author: Daniel Mendler <m...@daniel-mendler.de>
Commit: Daniel Mendler <m...@daniel-mendler.de>

    Add sorting by history for files
    
    cc @clemera, https://github.com/raxod502/selectrum/issues/502
---
 minicomp.el | 63 ++++++++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 41 insertions(+), 22 deletions(-)

diff --git a/minicomp.el b/minicomp.el
index 27b3006..98c1ca6 100644
--- a/minicomp.el
+++ b/minicomp.el
@@ -90,6 +90,9 @@
 (defvar-local minicomp--history-hash nil
   "History hash table.")
 
+(defvar-local minicomp--history-dir nil
+  "Directory of `minicomp--history-hash'.")
+
 (defvar-local minicomp--candidates-ov nil
   "Overlay showing the candidates.")
 
@@ -120,24 +123,41 @@
       (and (= (cdr x) (cdr y))
            (string< (car x) (car y)))))
 
-(defun minicomp--sort (candidates)
-  "Sort CANDIDATES by history position, length and alphabetically."
-  (unless minicomp--history-hash
-    ;; History disabled if `minibuffer-history-variable' eq `t'.
-    (let ((list (and (not (eq minibuffer-history-variable t))
-                     (symbol-value minibuffer-history-variable)))
-          (hist-idx 0))
-      (setq minicomp--history-hash (make-hash-table :test #'equal
-                                                    :size (length list)))
-      ;; Store the history position first in a hashtable in order to
-      ;; allow O(1) history lookup.
-      (dolist (elem list)
-        (unless (gethash elem minicomp--history-hash)
-          (puthash elem hist-idx minicomp--history-hash))
-        (setq hist-idx (1+ hist-idx)))))
-  ;; Decorate each candidate with (hist-idx<<13) + length. This way we sort 
first by hist-idx and
-  ;; then by length. We assume that the candidates are shorter than 2**13 
characters and that the
-  ;; history is shorter than 2**16 entries.
+(defun minicomp--sort (input candidates)
+  "Sort CANDIDATES by history position, length and alphabetically, given 
current INPUT."
+  ;; Store the history position first in a hashtable in order to allow O(1) 
history lookup. File
+  ;; names get special treatment. In principle, completion tables with 
boundaries should also get
+  ;; special treatment, but files are the most important.
+  (let ((index 0)
+        ;; History disabled if `minibuffer-history-variable' eq `t'.
+        (hist (and (not (eq minibuffer-history-variable t))
+                   (symbol-value minibuffer-history-variable))))
+    (if (eq minibuffer-history-variable 'file-name-history)
+      (let* ((dir (expand-file-name (substitute-in-file-name
+                                     (or (file-name-directory input)
+                                         default-directory))))
+             (adir (abbreviate-file-name dir)))
+        (unless (equal minicomp--history-dir dir)
+          (setq minicomp--history-hash (make-hash-table :test #'equal :size 
(length hist))
+                minicomp--history-dir dir)
+          (dolist (elem hist)
+            (when-let (file (cond
+                             ((string-prefix-p dir elem) (substring elem 
(length dir)))
+                             ((string-prefix-p adir elem) (substring elem 
(length adir)))))
+              (when-let (slash (string-match-p "/" file))
+                (setq file (substring file 0 (1+ slash))))
+              (unless (gethash file minicomp--history-hash)
+                (puthash file index minicomp--history-hash)))
+            (setq index (1+ index)))))
+      (unless minicomp--history-hash
+        (setq minicomp--history-hash (make-hash-table :test #'equal :size 
(length hist)))
+        (dolist (elem hist)
+          (unless (gethash elem minicomp--history-hash)
+            (puthash elem index minicomp--history-hash))
+          (setq index (1+ index))))))
+  ;; Decorate each candidate with (index<<13) + length. This way we sort first 
by index and then by
+  ;; length. We assume that the candidates are shorter than 2**13 characters 
and that the history is
+  ;; shorter than 2**16 entries.
   (let ((cand candidates))
     (while cand
       (setcar cand (cons (car cand)
@@ -181,10 +201,9 @@
     (setq total (length all)
           all (if (> total minicomp-sort-threshold)
                   all
-                (funcall
-                 (or (completion-metadata-get metadata 'display-sort-function)
-                     #'minicomp--sort)
-                 all)))
+                (if-let (sort (completion-metadata-get metadata 
'display-sort-function))
+                    (funcall sort all)
+                  (minicomp--sort input all))))
     (when-let* ((def (cond
                       ((stringp (car-safe minibuffer-default)) (car 
minibuffer-default))
                       ((stringp minibuffer-default) minibuffer-default)))

Reply via email to