Skip to content
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
Closed

Preserving sort order of candidates #1294

narendraj9 opened this issue Nov 19, 2017 · 11 comments

Comments

@narendraj9
Copy link

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
Copy link
Owner

abo-abo commented Nov 19, 2017

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

@narendraj9
Copy link
Author

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
Copy link
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
Copy link
Author

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
Copy link
Author

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
Copy link
Author

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
Copy link
Author

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
Copy link
Owner

abo-abo commented Nov 19, 2017

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

@narendraj9
Copy link
Author

@abo-abo You are amazing! Thanks :)

@narendraj9
Copy link
Author

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

@basil-conto
Copy link
Collaborator

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants