From f26f94505b31526076323c985c3aa2de458723ab Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Fri, 5 Jun 2020 17:17:27 -0400 Subject: [PATCH 1/3] Support wgrep in ivy-occur buffers generated from ivy-xref Ivy has built-in support for wgrep-mode (https://github.com/mhayashi1120/Emacs-wgrep) in ivy-occur buffers, assuming wgrep is already installed. However, in order for wgrep to work the occur buffer needs to be in a specific format. This adds a function that tells Ivy how to format the ivy-xref occur buffer, allowing wgrep to work. However, it is currently subtly broken - although the wgrep edits are successfully applied, entries in the occur buffer that have been edited can no longer be clicked on. This seems to be because after wgrep has edited the line, the underlying (candidate . location) list that ivy-xref-show-xrefs relies on to jump to the occurence is set to nil. I haven't figured out how to fix this yet... --- ivy-xref.el | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ivy-xref.el b/ivy-xref.el index 8a0dc5a..dcb7e37 100644 --- a/ivy-xref.el +++ b/ivy-xref.el @@ -66,8 +66,8 @@ file (file-name-nondirectory file)) (if (integerp line) - (format ":%d: " line) - ": ")) + (format ":%d:" line) + ":")) 'face 'compilation-info) (progn (when ivy-xref-remove-text-properties @@ -76,6 +76,13 @@ (push `(,candidate . ,location) collection)))) (nreverse collection))) +(defun ivy-xref-show-xrefs-occur (&optional cands) + "Generate a custom occur buffer for `ivy-xref-show-xrefs'" + (unless (eq major-mode 'ivy-occur-grep-mode) + (ivy-occur-grep-mode) + (setq default-directory (ivy-state-directory ivy-last))) + (swiper--occur-insert-lines (mapcar #'counsel--normalize-grep-match cands))) + ;;;###autoload (defun ivy-xref-show-xrefs (fetcher alist) "Show the list of xrefs returned by FETCHER and ALIST via ivy." @@ -113,8 +120,9 @@ (unless done (switch-to-buffer orig-buf) (goto-char orig-pos))) + :occur #'ivy-xref-show-xrefs-occur :caller 'ivy-xref-show-xrefs)) - ;; honor the contact of xref--show-xref-buffer by returning its original + ;; honor the contract of xref--show-xref-buffer by returning its original ;; return value buffer)) From a54db7f361c9a273da92adc8e1b15e6944cb9f4a Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Fri, 5 Jun 2020 22:16:49 -0400 Subject: [PATCH 2/3] Add :occur keyword correctly --- ivy-xref.el | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ivy-xref.el b/ivy-xref.el index dcb7e37..fa86948 100644 --- a/ivy-xref.el +++ b/ivy-xref.el @@ -76,13 +76,6 @@ (push `(,candidate . ,location) collection)))) (nreverse collection))) -(defun ivy-xref-show-xrefs-occur (&optional cands) - "Generate a custom occur buffer for `ivy-xref-show-xrefs'" - (unless (eq major-mode 'ivy-occur-grep-mode) - (ivy-occur-grep-mode) - (setq default-directory (ivy-state-directory ivy-last))) - (swiper--occur-insert-lines (mapcar #'counsel--normalize-grep-match cands))) - ;;;###autoload (defun ivy-xref-show-xrefs (fetcher alist) "Show the list of xrefs returned by FETCHER and ALIST via ivy." @@ -120,7 +113,6 @@ (unless done (switch-to-buffer orig-buf) (goto-char orig-pos))) - :occur #'ivy-xref-show-xrefs-occur :caller 'ivy-xref-show-xrefs)) ;; honor the contract of xref--show-xref-buffer by returning its original ;; return value @@ -140,5 +132,15 @@ Will jump to the definition if only one is found." (cons (cons 'fetched-xrefs xrefs) alist)))))) +(defun ivy-xref-show-xrefs-occur (&optional cands) + "Generate a custom occur buffer for `ivy-xref-show-xrefs'" + (unless (eq major-mode 'ivy-occur-grep-mode) + (ivy-occur-grep-mode) + (setq default-directory (ivy-state-directory ivy-last))) + (swiper--occur-insert-lines (mapcar #'counsel--normalize-grep-match cands))) + +(ivy-configure 'ivy-xref-show-xrefs + :occur #'ivy-xref-show-xrefs-occur) + (provide 'ivy-xref) ;;; ivy-xref.el ends here From 9082aa2860baeb7915bc130c2e925b06bb17d4d7 Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Sat, 6 Jun 2020 16:20:11 -0400 Subject: [PATCH 3/3] Ensure that occur items can still be acted on after wgrep --- ivy-xref.el | 68 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/ivy-xref.el b/ivy-xref.el index fa86948..2531483 100644 --- a/ivy-xref.el +++ b/ivy-xref.el @@ -59,6 +59,7 @@ (let* ((line (xref-location-line location)) (file (xref-location-group location)) (candidate + (counsel--normalize-grep-match (concat (propertize (concat @@ -72,7 +73,7 @@ (progn (when ivy-xref-remove-text-properties (set-text-properties 0 (length summary) nil summary)) - summary)))) + summary))))) (push `(,candidate . ,location) collection)))) (nreverse collection))) @@ -88,7 +89,7 @@ ;; Emacs 27 (or (assoc-default 'fetched-xrefs alist) (funcall fetcher)) - fetcher)) + fetcher)) (buffer (xref--show-xref-buffer fetcher alist))) (quit-window) (let ((orig-buf (current-buffer)) @@ -97,18 +98,22 @@ (ivy-read "xref: " (ivy-xref-make-collection xrefs) :require-match t :action (lambda (candidate) - (setq done (eq 'ivy-done this-command)) - (condition-case err - (let* ((marker (xref-location-marker (cdr candidate))) - (buf (marker-buffer marker))) - (with-current-buffer buffer - (select-window - ;; function signature changed in - ;; 2a973edeacefcabb9fd8024188b7e167f0f9a9b6 - (if (version< emacs-version "26.0.90") - (xref--show-pos-in-buf marker buf t) - (xref--show-pos-in-buf marker buf))))) - (user-error (message (error-message-string err))))) + (let ((candidate (or candidate + (with-current-buffer (ivy--find-occur-buffer) + (get-text-property (line-beginning-position) + 'ivy-xref-candidate))))) + (setq done (eq 'ivy-done this-command)) + (condition-case err + (let* ((marker (xref-location-marker (cdr candidate))) + (buf (marker-buffer marker))) + (with-current-buffer buffer + (select-window + ;; function signature changed in + ;; 2a973edeacefcabb9fd8024188b7e167f0f9a9b6 + (if (version< emacs-version "26.0.90") + (xref--show-pos-in-buf marker buf t) + (xref--show-pos-in-buf marker buf))))) + (user-error (message (error-message-string err)))))) :unwind (lambda () (unless done (switch-to-buffer orig-buf) @@ -132,12 +137,45 @@ Will jump to the definition if only one is found." (cons (cons 'fetched-xrefs xrefs) alist)))))) +(defun ivy-xref--occur-insert-lines (cands) + "Insert CANDS into `ivy-occur' buffer." + (font-lock-mode -1) + (dolist (cand cands) + (let ((cand-list (assoc cand (ivy-state-collection ivy-last) #'string=))) + (setq cand + (if (string-match "\\`\\(.*:[0-9]+:\\)\\(.*\\)\\'" cand) + (let ((file-and-line (match-string 1 cand)) + (grep-line (match-string 2 cand))) + (concat + (propertize file-and-line 'face 'ivy-grep-info) + (ivy--highlight-fuzzy grep-line))) + (ivy--highlight-fuzzy (copy-sequence cand)))) + (add-text-properties + 0 (length cand) + `(mouse-face + highlight + help-echo "mouse-1: call ivy-action" + ivy-xref-candidate ,cand-list) + cand) + (insert (if (ivy--starts-with-dotslash cand) "" " ") + cand ?\n)))) + +(defun ivy-xref--occur-make-buffer (cands) + (let ((inhibit-read-only t)) + ;; Need precise number of header lines for `wgrep' to work. + (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" + default-directory)) + (insert (format "%d candidates:\n" (length cands))) + (ivy-xref--occur-insert-lines cands) + (goto-char (point-min)) + (forward-line 4))) + (defun ivy-xref-show-xrefs-occur (&optional cands) "Generate a custom occur buffer for `ivy-xref-show-xrefs'" (unless (eq major-mode 'ivy-occur-grep-mode) (ivy-occur-grep-mode) (setq default-directory (ivy-state-directory ivy-last))) - (swiper--occur-insert-lines (mapcar #'counsel--normalize-grep-match cands))) + (ivy-xref--occur-make-buffer cands)) (ivy-configure 'ivy-xref-show-xrefs :occur #'ivy-xref-show-xrefs-occur)