Yevgnen/ivy-rich

Magit intergration

EgorDuplensky opened this issue · 9 comments

I am implementing ivy-rich support for magit's ivy commands (mostly revision selection).
Would such pull request be acceptable for the project or I better to create a separate package?

Can you point me to which ivy commands for magit?

Magit does not really have ivy commands. It uses ivy-completing-read (see magit-completing-read) to form a command on the fly.
Basically I am creating counsel wrappers for those magit commands which eventually call ivy-completing-read (like magit-checkout) and then customizing them using ivy-rich framework. I haven't find a better way to do this.

The main obstacle here is that, for example, magit forms the list of the candidates for magit-checkout in scope of magit-checkout itself, it does not really have a function like magit-get-list-branches-to-checkout which I could use for counsel wrapper I am creating. So I have to call few internal magit functions (basically by coping 90% of magit-checkout code) which most likely are not supposed to be called from outside.

There is an example:
(defun magit-list-other-branch-or-commit ()
      (let* ((current (magit-get-current-branch))
             (atpoint (magit-branch-or-commit-at-point))
             (exclude current)
             (default (or (and (not (equal atpoint exclude))
                               (not (and (not current)
                                         (magit-rev-equal atpoint "HEAD")))
                               atpoint)
                          (and (not (equal current exclude)) current)
                          (magit-get-previous-branch))))
                          (let ((result (delete exclude (magit-list-refnames))))
                            (if atpoint (cons atpoint result) result)
                          )
      ))

   (defun magit-get-current-commit-sha-short (rev)
      (magit-rev-parse "--short" rev))

   (defun magit-comming-message (rev)
      (magit-rev-format "%s" rev))

   (defun magit-branch-or-commit-at-point-save ()
      (let (atpoint (magit-branch-or-commit-at-point))
        (if atpoint atpoint "")
        ))


    (defun counsel-magit-checkout ()
      "Forward to `describe-function'."
      (interactive)
      (ivy-read "Switch to branch: "
                (magit-list-other-branch-or-commit)
                :action 'magit-checkout
                :caller 'counsel-magit-checkout))

    ;; counsel-magit-checkout
    (:columns
     ((ivy-rich-candidate (:width 30))
      (magit-get-current-commit-sha-short (:face magit-hash))
      (magit-get-upstream-branch-save (:face font-lock-comment-face))
      (magit-comming-message (:face font-lock-comment-face))))

    (transient-replace-suffix 'magit-branch "b"
      '("b" "branch/revision" counsel-magit-checkout))
As you can see there is a lot of extra logic besides ivy-rich itself.

I see. It seems you must first ivyfy a Magit command first. Although ivy-rich support something like

(setq ivy-rich-display-transformers-list
          (plist-put
           ivy-rich-display-transformers-list
           'execute-extended-command
           'ivy-rich--counsel-M-x-transformer))

where execute-extended-command use the internal completing-read in read-extended-command. But it does not work directly with Magit and I currently have no idea of it due to my limited Elisp.

Sorry for delay. I think I have found a way to define transformers without extra wrappers.
Could you please help with few issues I faced:

  1. The resulting ivy-rich buffer is quite slow. As far as I understand ivy refreshes all candidates upon each action, including, for example, ivy-next-line, and since git interaction is not completely free, it takes noticeable time (like 0.5 second) to get tag+branch+sha+commit_message for 10 candidates.
    And the only way to mitigate this is to cache, right?
  2. Is there any way to use a lisp function or variable as a transformer to avoid code duplication?
    For example (which is not working):
(defun my-counsel-M-x-transformer ()
    '(:columns
     ((counsel-M-x-transformer (:width 0.4))
      (ivy-rich-counsel-function-docstring (:face font-lock-doc-face))))

(setq ivy-rich-display-transformers-list '(counsel-M-x
                                           (my-counsel-M-x-transformer)
  )
There is an example of _magit-checkout_ command

ivy-rich-magit-example

Hi, I'm not sure if there's a way to speed up. Caching is definitely a possible way to do it. How does your transformer look like?

About the reusing the transformer, you can take a look of this.

magit transformers
     (defun magit-get-current-commit-sha-short (rev)
      (magit-rev-parse "--short" rev))

    (defun magit-commit-message (rev)
      (magit-rev-format "%s" rev))

    (defun magit-get-branch-and-upstream (rev)
      (let ((branch (magit-get-upstream-branch rev)))
        (concat (propertize rev 'face 'default) (when branch
                      (put-text-property 0 (length branch) 'face 'magit-branch-remote-head branch)
                      (format " %s" branch)))
        ))

    (defun magit-get-current-tag-save (rev)
      (let ((tag (magit-git-str "describe" "--tags" "--exact-match" rev)))
        (if tag tag "")
        ))

    (setq ivy-rich-display-transformers-list (append ivy-rich-display-transformers-list
                                                     '(magit-checkout
                                                       (:columns
                                                        (
                                                         ;;(ivy-rich-candidate (:width 30))
                                                         (magit-get-branch-and-upstream (:width 0.35))
                                                         (magit-get-current-commit-sha-short (:width 11 :face magit-hash))
                                                         (magit-get-current-tag-save (:width 10 :face magit-tag))
                                                         (magit-commit-message (:width 0.35 :face font-lock-comment-face))
                                                         )
                                                        )
                                                       magit-branch-and-checkout
                                                       (:columns
                                                        (
                                                         ;; (ivy-rich-candidate (:width 30))
                                                         (magit-get-branch-and-upstream (:width 0.35))
                                                         (magit-get-current-commit-sha-short (:width 11 :face magit-hash))
                                                         (magit-get-current-tag-save (:width 10 :face magit-tag))
                                                         (magit-commit-message (:width 0.35 :face font-lock-comment-face))
                                                         )
                                                        )
                                                       magit-branch-create
                                                       (:columns
                                                        (
                                                         ;; (ivy-rich-candidate (:width 30))
                                                         (magit-get-branch-and-upstream (:width 0.35))
                                                         (magit-get-current-commit-sha-short (:width 11 :face magit-hash))
                                                         (magit-get-current-tag-save (:width 10 :face magit-tag))
                                                         (magit-commit-message (:width 0.35 :face font-lock-comment-face))
                                                         )
                                                        )
                                                       )
                                                     ))

    (ivy-rich-set-display-transformer)
These are for 'b b', 'b c', and 'b n' magit actions

I use magit-get-branch-and-upstream instead of ivy-rich-candidate to have branch displayed in magit style, like branch[upstream]. In the first version I had a separate column for upstream branch.

Not relevant anymore