branch: master commit ded092351a7343e53504de5b7a62936bf95937f1 Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Customize ivy-occur per command with ivy-set-occur * ivy.el (ivy--occurs-list): New defvar. (ivy-set-occur): New API function for defining custom occur behavior. (ivy--occur-insert-lines): New defun. (ivy-occur): Simplify, move counsel-git-grep specific stuff away. * counsel.el (counsel-git-grep-occur): New defun. Sets up an occur buffer with a mode that derives from `grep-mode' that means that the error functions (`next-error' etc) etc are available. Also allows to edit the contents with `wgrep' by pressing "C-x C-q". * swiper.el (swiper-occur): New defun, similar to `counsel-git-grep-occur'. Except it turns off `font-lock-mode' to keep the original syntax-highlighted faces. (swiper--action): Update to make it work from ivy-occur buffers. --- counsel.el | 23 +++++++++++++++++ ivy.el | 80 ++++++++++++++++++++++++++++-------------------------------- swiper.el | 80 +++++++++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 117 insertions(+), 66 deletions(-) diff --git a/counsel.el b/counsel.el index d5eadbd..4432b61 100644 --- a/counsel.el +++ b/counsel.el @@ -451,6 +451,29 @@ INITIAL-INPUT can be given as the initial minibuffer input." :history 'counsel-git-grep-history :caller 'counsel-git-grep))) +(defun counsel-git-grep-occur () + "Generate a custom occur buffer for `counsel-git-grep'." + (ivy-occur-grep-mode) + (setq default-directory counsel--git-grep-dir) + (let ((cands (split-string + (shell-command-to-string + (format counsel-git-grep-cmd + (if (stringp ivy--old-re) + ivy--old-re + (caar ivy--old-re)))) + "\n" + 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--occur-insert-lines + (mapcar + (lambda (cand) (concat "./" cand)) + cands)))) + +(ivy-set-occur 'counsel-git-grep 'counsel-git-grep-occur) + (defcustom counsel-find-file-at-point nil "When non-nil, add file-at-point to the list of candidates." :type 'boolean diff --git a/ivy.el b/ivy.el index 86e0bac..3d844d3 100644 --- a/ivy.el +++ b/ivy.el @@ -2611,7 +2611,23 @@ buffer would modify `ivy-last'.") \\{ivy-occur-grep-mode-map}") -(defvar counsel-git-grep-cmd) +(defvar ivy--occurs-list nil + "A list of custom occur generators per command.") + +(defun ivy-set-occur (cmd occur) + "Assign CMD a custom OCCUR function." + (setq ivy--occurs-list + (plist-put ivy--occurs-list cmd occur))) + +(defun ivy--occur-insert-lines (cands) + (dolist (str cands) + (add-text-properties + 0 (length str) + `(mouse-face + highlight + help-echo "mouse-1: call ivy-action") + str) + (insert str "\n"))) (defun ivy-occur () "Stop completion and put the current matches into a new buffer. @@ -2623,51 +2639,29 @@ a mouse click will call the appropriate action for that candidate. There is no limit on the number of *ivy-occur* buffers." (interactive) - (let ((buffer - (generate-new-buffer - (format "*ivy-occur%s \"%s\"*" - (let (caller) - (if (setq caller (ivy-state-caller ivy-last)) - (concat " " (prin1-to-string caller)) - "")) - ivy-text))) - (do-grep (eq (ivy-state-caller ivy-last) 'counsel-git-grep))) + (let* ((caller (ivy-state-caller ivy-last)) + (occur-fn (plist-get ivy--occurs-list caller)) + (buffer + (generate-new-buffer + (format "*ivy-occur%s \"%s\"*" + (if caller + (concat " " (prin1-to-string caller)) + "") + ivy-text)))) (with-current-buffer buffer - (if do-grep - (progn - (setq ivy--old-cands - (split-string - (shell-command-to-string - (format counsel-git-grep-cmd - (if (stringp ivy--old-re) - ivy--old-re - (caar ivy--old-re)))) - "\n" - t)) - (ivy-occur-grep-mode)) - (ivy-occur-mode)) - (setf (ivy-state-text ivy-last) ivy-text) - (setq ivy-occur-last ivy-last) - (setq-local ivy--directory ivy--directory) (let ((inhibit-read-only t)) (erase-buffer) - (when do-grep - ;; Need precise number of header lines for `wgrep' to work. - (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" - (setq default-directory - counsel--git-grep-dir)))) - (insert (format "%d candidates:\n" (length ivy--old-cands))) - (dolist (cand ivy--old-cands) - (let ((str (if do-grep - (concat "./" cand) - (concat " " cand)))) - (add-text-properties - 0 (length str) - `(mouse-face - highlight - help-echo "mouse-1: call ivy-action") - str) - (insert str "\n"))))) + (if occur-fn + (funcall occur-fn) + (ivy-occur-mode) + (insert (format "%d candidates:\n" (length ivy--old-cands))) + (ivy--occur-insert-lines + (mapcar + (lambda (cand) (concat " " cand)) + ivy--old-cands)))) + (setf (ivy-state-text ivy-last) ivy-text) + (setq ivy-occur-last ivy-last) + (setq-local ivy--directory ivy--directory)) (ivy-exit-with-action `(lambda (_) (pop-to-buffer ,buffer))))) diff --git a/swiper.el b/swiper.el index 888b015..c183b1b 100644 --- a/swiper.el +++ b/swiper.el @@ -325,6 +325,36 @@ When non-nil, INITIAL-INPUT is the initial search pattern." (interactive) (swiper--ivy initial-input)) +(defun swiper-occur () + "Generate a custom occur buffer for `swiper'." + (ivy-occur-grep-mode) + (font-lock-mode -1) + (let* ((fname (propertize + (with-ivy-window + (file-name-nondirectory + (buffer-file-name))) + 'face + 'compilation-info)) + (cands (mapcar + (lambda (s) + (format "%s:%s:%s" + fname + (propertize + (string-trim-right + (get-text-property 0 'display s)) + 'face 'compilation-line-number) + (substring s 1))) + ivy--old-cands))) + (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" + default-directory)) + (insert (format "%d candidates:\n" (length cands))) + (ivy--occur-insert-lines + (mapcar + (lambda (cand) (concat "./" cand)) + cands)))) + +(ivy-set-occur 'swiper 'swiper-occur) + (declare-function evil-jumper--set-jump "ext:evil-jumper") (defvar swiper--current-line nil) @@ -537,29 +567,33 @@ WND, when specified is the window." (defun swiper--action (x) "Goto line X." - (if (null x) - (user-error "No candidates") - (with-ivy-window - (unless (equal (current-buffer) - (ivy-state-buffer ivy-last)) - (switch-to-buffer (ivy-state-buffer ivy-last))) - (goto-char (point-min)) - (funcall (if swiper-use-visual-line - #'line-move - #'forward-line) - (1- (read (get-text-property 0 'display x)))) - (re-search-forward - (ivy--regex ivy-text) (line-end-position) t) - (swiper--ensure-visible) - (when (/= (point) swiper--opoint) - (unless (and transient-mark-mode mark-active) - (when (eq ivy-exit 'done) - (push-mark swiper--opoint t) - (message "Mark saved where search started")))) - (add-to-history - 'regexp-search-ring - (ivy--regex ivy-text) - regexp-search-ring-max)))) + (let ((ln (1- (read (if (memq this-command '(ivy-occur-press)) + (when (string-match ":\\([0-9]+\\):.*\\'" x) + (match-string-no-properties 1 x)) + (get-text-property 0 'display x))))) + (re (ivy--regex ivy-text))) + (if (null x) + (user-error "No candidates") + (with-ivy-window + (unless (equal (current-buffer) + (ivy-state-buffer ivy-last)) + (switch-to-buffer (ivy-state-buffer ivy-last))) + (goto-char (point-min)) + (funcall (if swiper-use-visual-line + #'line-move + #'forward-line) + ln) + (re-search-forward re (line-end-position) t) + (swiper--ensure-visible) + (when (/= (point) swiper--opoint) + (unless (and transient-mark-mode mark-active) + (when (eq ivy-exit 'done) + (push-mark swiper--opoint t) + (message "Mark saved where search started")))) + (add-to-history + 'regexp-search-ring + re + regexp-search-ring-max))))) ;; (define-key isearch-mode-map (kbd "C-o") 'swiper-from-isearch) (defun swiper-from-isearch ()