New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserving sort order of candidates #1294

Closed
narendraj9 opened this Issue Nov 19, 2017 · 11 comments

Comments

Projects
None yet
3 participants
@narendraj9

narendraj9 commented Nov 19, 2017

I would like to preserve the order of candidates if I have set ivy-sort-matching-fns and ivy-sort-funs-alist (not the exact variable names) to nil for the specific command. Currently, it doesn't happen. I switched from helm to ivy and in helm that was the default and I think it should be. What are your thoughts? @abo-abo

@abo-abo

This comment has been minimized.

Owner

abo-abo commented Nov 19, 2017

Can you give a concrete example? It's not fully clear to me.

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 19, 2017

For example:

(defconst hledger-jcompletions '("balancesheet"
                                 "daily"
                                 "incomestatement"
                                 "overall"
                                 "stats"
                                 "activity"
                                 "print"
                                 "accounts"
                                 "balance"
                                 "register")
  "Commands that can be passed to `hledger-jdo` function defined below.")
(defun hledger-run-command (command)
  "Run an hledger COMMAND."
  (interactive (list (completing-read "jdo> "
                                      hledger-jcompletions)))
                                 ..

With

(push '(hledger-run-command) ivy-sort-matches-functions-alist)
(push '(hledger-run-command) ivy-sort-functions-alist)

The order of completions isn't kept the same as we have in the list provided. It's modified. @abo-abo

@abo-abo

This comment has been minimized.

Owner

abo-abo commented Nov 19, 2017

Can't reproduce with make plain and running this code in scratch:

(defconst hledger-jcompletions '("balancesheet"
                                 "daily"
                                 "incomestatement"
                                 "overall"
                                 "stats"
                                 "activity"
                                 "print"
                                 "accounts"
                                 "balance"
                                 "register")
  "Commands that can be passed to `hledger-jdo` function defined below.")
(completing-read "jdo> " hledger-jcompletions)

The collection is unsorted by default, keeping the same order as the list.

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 19, 2017

Here is what I have in my configuration:

(use-package ivy
  :demand t
  :ensure t
  :diminish ivy-mode
  :preface
  (defun ivy--sort-by-len (name candidates)
    "Sort CANDIDATES based on similarity of their length with NAME."
    (let ((name-len (length name))
          (candidates-count (length candidates)))
      (if (< 500 candidates-count)
          candidates
        (seq-sort-by #'length
                     (lambda (a b)
                       (< (abs (- name-len a))
                          (abs (- name-len b))))
                     candidates))))

  :bind (("C-x b" . ivy-switch-buffer)
         ("C-x B" . ivy-switch-buffer-other-window)
         ("M-H"   . ivy-resume))

  :config
  (bind-keys :map ivy-minibuffer-map
             ("C-r" . ivy-previous-line-or-history)
             ("M-r" . ivy-reverse-i-search))

  (setq ivy-initial-inputs-alist nil
        ivy-format-function #'ivy-format-function-arrow
        ivy-re-builders-alist '((t . ivy--regex-ignore-order))
        ivy-use-virtual-buffers t)

  ;; Change the default sort function to rank matches according to similarity
  ;; with input text.
  (setf (alist-get 't ivy-sort-matches-functions-alist)
        #'ivy--sort-by-len)
  ;; No sorting for `counsel-yank-pop' as candidates are already sorted.
  (push '(counsel-yank-pop) ivy-sort-matches-functions-alist)
  (push '(hledger-run-command) ivy-sort-matches-functions-alist)
  (push '(hledger-run-command) ivy-sort-functions-alist)

  (ivy-mode +1)

  (use-package ivy-hydra
    :bind (:map ivy-minibuffer-map
                ("M-o" . ivy-dispatching-done-hydra))
    :ensure t)

  (use-package ivy-rich
    :ensure t
    :config
    (ivy-set-display-transformer 'ivy-switch-buffer
                                 'ivy-rich-switch-buffer-transformer)
    (setq ivy-virtual-abbreviate 'full
          ivy-rich-switch-buffer-align-virtual-buffer t
          ivy-rich-path-style 'abbrev))

  (use-package swiper
    :ensure t
    :bind (("C-. C-s" . swiper))
    :commands swiper-from-isearch
    :init
    (bind-key "C-." #'swiper-from-isearch isearch-mode-map)
    :config
    (bind-keys :map swiper-map
               ("M-%" . swiper-query-replace)
               ("M-h" . swiper-avy)))

  (use-package counsel
    :ensure t
    :bind (("M-x"       . counsel-M-x)
           ("C-x 8 RET" . counsel-unicode-char)
           ("C-c r"     . counsel-recentf)
           ("C-. C-r"   . counsel-ag)
           ("M-y"       . counsel-yank-pop)

           :map minibuffer-local-map
           ("M-r" . counsel-minibuffer-history)

           :map ctl-quote-map
           ("c p" . counsel-linux-app))
    :config
    (setq counsel-yank-pop-separator
          (format "\n%s\n" (make-string 60 ?┅))))

  (use-package counsel-osx-app
    :doc
    "Keep this after `counsel' so that the key binding is
     overridden only on OSX."
    :ensure t
    :if (eq system-type 'darwin)
    :bind (:map ctl-quote-map
                ("c p" . counsel-osx-app))))

Could any of the above be causing it? Thanks for the help! :)

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 19, 2017

For some reason, (push '(hledger-run-command) ivy-sort-matches-functions-alist) isn't doing what it should. Am I doing something wrong here? @abo-abo

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 19, 2017

I have found the problem. It's happening because :caller isn't correct when completing-read is called. Hence, we use the default sorting functions for `hledger-run-command'.

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 19, 2017

(defun ivy--sort (name candidates)
  "Re-sort candidates by NAME.
All CANDIDATES are assumed to match NAME."
  (let ((key (or (ivy-state-caller ivy-last)
                 (when (functionp (ivy-state-collection ivy-last))
                   (ivy-state-collection ivy-last))
                 this-command))                          ;;; <------------------------ Added
        fun)
    (cond ((and ivy--flx-featurep
                (eq ivy--regex-function 'ivy--regex-fuzzy))
           (ivy--flx-sort name candidates))
          ((setq fun (cdr (or (assoc key ivy-sort-matches-functions-alist)
                              (assoc t ivy-sort-matches-functions-alist))))
           (funcall fun name candidates))
          (t
           candidates))))

Making this change achieves what I want. Is there a reason this wasn't made? @abo-abo

@abo-abo abo-abo closed this in 215922e Nov 19, 2017

@abo-abo

This comment has been minimized.

Owner

abo-abo commented Nov 19, 2017

Added this-command. Please confirm if it's fixed for you.

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 19, 2017

@abo-abo You are amazing! Thanks :)

@narendraj9

This comment has been minimized.

narendraj9 commented Nov 20, 2017

@abo-abo The issue isn't fixed yet. This fixes it #1297

@basil-conto

This comment has been minimized.

Contributor

basil-conto commented Aug 7, 2018

The issue isn't fixed yet. This fixes it #1297

#1621, which supplanted #1297, has landed, so in theory this issue should be fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment