]> code.delx.au - gnu-emacs-elpa/blobdiff - packages/swiper/ivy.el
Merge commit 'efa18eca10e5a0e05043f872cf9945842bb3a034' from swiper
[gnu-emacs-elpa] / packages / swiper / ivy.el
index 78a50a3c7197a45ff1ec791f9eca3e5f24c8545c..2802f078fd92e798ec38f23bbc9532c6cdb9792d 100644 (file)
   '((t (:inherit highlight)))
   "Face used by Ivy for highlighting first match.")
 
+(defface ivy-subdir
+  '((t (:weight bold)))
+  "Face used by Ivy for highlighting subdirs in the alternatives.")
+
 (defcustom ivy-height 10
   "Number of lines for the minibuffer window."
   :type 'integer)
@@ -89,6 +93,8 @@ Only \"./\" and \"../\" apply here. They appear in reverse order."
     (define-key map (kbd "M-n") 'ivy-next-history-element)
     (define-key map (kbd "M-p") 'ivy-previous-history-element)
     (define-key map (kbd "C-g") 'minibuffer-keyboard-quit)
+    (define-key map (kbd "C-v") 'ivy-scroll-up-command)
+    (define-key map (kbd "M-v") 'ivy-scroll-down-command)
     map)
   "Keymap used in the minibuffer.")
 
@@ -147,14 +153,25 @@ When non-nil, it should contain one %d.")
   "Exit the minibuffer with the selected candidate."
   (interactive)
   (delete-minibuffer-contents)
-  (if (zerop ivy--length)
-      (when (memq ivy-require-match '(nil confirm confirm-after-completion))
-        (insert ivy-text)
-        (setq ivy-exit 'done))
-    (if ivy--directory
-        (insert (expand-file-name ivy--current ivy--directory))
-      (insert ivy--current))
-    (setq ivy-exit 'done))
+  (cond (ivy--directory
+         (insert
+          (cond ((string= ivy-text "")
+                 (if (equal ivy--current "./")
+                     ivy--directory
+                   ivy--current))
+                ((zerop ivy--length)
+                 (expand-file-name ivy-text ivy--directory))
+                (t
+                 (expand-file-name ivy--current ivy--directory))))
+         (setq ivy-exit 'done))
+        ((zerop ivy--length)
+         (when (memq ivy-require-match
+                     '(nil confirm confirm-after-completion))
+           (insert ivy-text)
+           (setq ivy-exit 'done)))
+        (t
+         (insert ivy--current)
+         (setq ivy-exit 'done)))
   (exit-minibuffer))
 
 (defun ivy-alt-done ()
@@ -167,6 +184,7 @@ When non-nil, it should contain one %d.")
            (ivy-done))
 
           ((and ivy--directory
+                (plusp ivy--length)
                 (file-directory-p
                  (setq dir (expand-file-name
                             ivy--current ivy--directory))))
@@ -186,6 +204,18 @@ When non-nil, it should contain one %d.")
   (interactive)
   (setq ivy--index (1- ivy--length)))
 
+(defun ivy-scroll-up-command ()
+  "Scroll the candidates upward by the minibuffer height."
+  (interactive)
+  (setq ivy--index (min (+ ivy--index ivy-height)
+                        (1- ivy--length))))
+
+(defun ivy-scroll-down-command ()
+  "Scroll the candidates downward by the minibuffer height."
+  (interactive)
+  (setq ivy--index (max (- ivy--index ivy-height)
+                        0)))
+
 (defun ivy-next-line (&optional arg)
   "Move cursor vertically down ARG candidates."
   (interactive "p")
@@ -259,6 +289,21 @@ On error (read-only), call `ivy-on-del-error-function'."
        (when ivy-on-del-error-function
          (funcall ivy-on-del-error-function))))))
 
+(defun ivy-sort-file-function-default (x y)
+  "Compare two files X and Y.
+Prioritize directories."
+  (if (get-text-property 0 'dirp x)
+      (if (get-text-property 0 'dirp y)
+          (string< x y)
+        t)
+    (if (get-text-property 0 'dirp y)
+        nil
+      (string< x y))))
+
+(defvar ivy-sort-file-function 'ivy-sort-file-function-default
+  "The function that compares file names.
+It should take two string arguments and return nil and non-nil.")
+
 (defun ivy--sorted-files (dir)
   "Return the list of files in DIR.
 Directories come first."
@@ -266,16 +311,12 @@ Directories come first."
          (seq (all-completions "" 'read-file-name-internal)))
     (if (equal dir "/")
         seq
-      (setq seq (cl-sort
-                 (delete "./" (delete "../" seq))
-                 (lambda (x y)
-                   (if (file-directory-p x)
-                       (if (file-directory-p y)
-                           (string< x y)
-                         t)
-                     (if (file-directory-p y)
-                         nil
-                       (string< x y))))))
+      (setq seq (delete "./" (delete "../" seq)))
+      (when (eq ivy-sort-file-function 'ivy-sort-file-function-default)
+        (setq seq (mapcar (lambda (x)
+                            (propertize x 'dirp (string-match-p "/$" x)))
+                          (delete "./" (delete "../" seq)))))
+      (setq seq (cl-sort seq ivy-sort-file-function))
       (dolist (dir ivy-extra-directories)
         (push dir seq))
       seq)))
@@ -417,10 +458,11 @@ Turning on Ivy mode will set `completing-read-function' to
            (lambda (x)
              (string-match initial-input x))
            candidates)))
-  (cl-position-if
-   (lambda (x)
-     (string-match preselect x))
-   candidates))
+  (or (cl-position preselect candidates :test 'equal)
+      (cl-position-if
+       (lambda (x)
+         (string-match preselect x))
+       candidates)))
 
 ;;* Implementation
 ;;** Regex
@@ -537,13 +579,21 @@ NAME is a string of words separated by spaces that is used to
 build a regex.
 CANDIDATES is a list of strings."
   (let* ((re (ivy--regex name))
-         (cands (if (and (equal re ivy--old-re)
-                         ivy--old-cands)
-                    ivy--old-cands
-                  (ignore-errors
-                    (cl-remove-if-not
-                     (lambda (x) (string-match re x))
-                     candidates))))
+         (cands (cond ((and (equal re ivy--old-re)
+                            ivy--old-cands)
+                       ivy--old-cands)
+                      ((and ivy--old-re
+                            (not (equal ivy--old-re ""))
+                            (eq 0 (cl-search ivy--old-re re)))
+                       (ignore-errors
+                         (cl-remove-if-not
+                          (lambda (x) (string-match re x))
+                          ivy--old-cands)))
+                      (t
+                       (ignore-errors
+                         (cl-remove-if-not
+                          (lambda (x) (string-match re x))
+                          candidates)))))
          (tail (nthcdr ivy--index ivy--old-cands))
          (ww (window-width))
          idx)
@@ -566,6 +616,12 @@ CANDIDATES is a list of strings."
              (end (min (+ start (1- ivy-height)) ivy--length))
              (cands (cl-subseq cands start end))
              (index (min ivy--index half-height (1- (length cands)))))
+        (when ivy--directory
+          (setq cands (mapcar (lambda (x)
+                                (if (string-match-p "/$" x)
+                                    (propertize x 'face 'ivy-subdir)
+                                  x))
+                              cands)))
         (setq ivy--current (copy-sequence (nth index cands)))
         (setf (nth index cands)
               (ivy--add-face ivy--current 'ivy-current-match))