;; - Support multi-buffer `search'.
;; - Add regexp completion style and use it for `search' completion.
-;; - Add replace support.
;; - Isearch-to-search and vice versa.
;; - Search non-matches.
;; - Highlight subgroups in matches.
(defface search-highlight
'((t :inherit highlight :foreground "black"))
- "Foo.")
-
-(defun search--go-to (target)
- "Go to TARGET."
- (let ((pos (string-to-number target)))
- (unless (zerop pos) (goto-char pos))))
+ "Face for highlighting matches for current input during \\[search].")
(defun search-read-target (&optional beg end re-or-fn)
"Prompt for \\[search] target between BEG and END matching RE-OR-FN."
(let* ((buffer (current-buffer))
(beg (or beg (point-min)))
(end (or end (point-max)))
- (sfn (if (functionp re-or-fn) re-or-fn
- (let ((r (or re-or-fn (read-regexp "Search regular expression"))))
- (lambda () (re-search-forward r end t)))))
+ (reg nil)
+ (sfn (if (functionp re-or-fn)
+ (prog1 re-or-fn (setq reg "match"))
+ (setq reg (or re-or-fn (read-regexp "Search regular expression")))
+ (lambda () (re-search-forward reg end t))))
(ovs nil)
(ovz nil)
(cur nil)
- (trs nil))
+ (trs nil)
+ (tab (make-hash-table))
+ (ind 0)
+ (rep nil))
(deactivate-mark)
(save-excursion
(goto-char beg)
(setq done t)
(if (<= (point) pos)
(forward-char)
- (push (format "%d:%d:%s"
- (match-beginning 0)
- (match-end 0)
- (match-string 0))
- trs)
+ (push (format "%d:%s" (cl-incf ind) (match-string 0)) trs)
+ (puthash ind
+ (list (copy-marker (match-beginning 0))
+ (copy-marker (match-end 0)))
+ tab)
(push (make-overlay (match-beginning 0)
(match-end 0))
ovs)
(overlay-put (car ovs) 'search t)
(overlay-put (car ovs) 'priority '(nil . 1)))
(setq pos (point))))))
+ (setq trs (nreverse trs))
(unwind-protect
- (progn
- (minibuffer-with-setup-hook
- (lambda ()
- (setq minibuffer-action
- (cons
- (lambda (c)
- (with-selected-window (minibuffer-selected-window)
- (unless (search--go-to c) (user-error "Invalid search target"))
- (when (overlayp cur) (overlay-put cur 'face 'lazy-highlight))
- (setq cur (seq-some
- (lambda (ov) (and (overlay-get ov 'search) ov))
- (overlays-at (point))))
- (overlay-put cur 'face 'isearch)))
- "search"))
- (let ((hook-fn
- (lambda (input)
- (unless (string-empty-p input)
- (mapc #'delete-overlay ovz)
- (setq ovz nil)
- (with-current-buffer buffer
- (dolist (ov ovs)
- (save-excursion
- (goto-char (overlay-start ov))
- (let ((r (regexp-quote input))
- (e (overlay-end ov)))
- (while (re-search-forward r e t)
- (push (make-overlay (match-beginning 0)
- (match-end 0))
- ovz)
- (overlay-put (car ovz) 'face 'search-highlight)
- (overlay-put (car ovz) 'search-input t)
- (overlay-put (car ovz) 'priority '(nil . 10)))))))))))
- (add-hook 'minibuffer-new-completion-input-hook
- (lambda () (funcall hook-fn (caar completion-history)))
- nil t)
- (add-hook 'completion-setup-hook
- (lambda () (funcall hook-fn (minibuffer-contents)))
- nil t)))
- (completing-read
- "Search: "
- (completion-table-with-metadata
- (nreverse trs)
- `((category . search)
- (group-function
- . ,(lambda (string &optional transform)
- (when transform (nth 2 (string-split string ":"))))))))))
+ (minibuffer-with-setup-hook
+ (lambda ()
+ (setq minibuffer-action
+ (cons
+ (lambda (c)
+ (with-selected-window
+ (or (get-buffer-window buffer) (display-buffer buffer))
+ (goto-char (car (gethash (string-to-number c) tab)))
+ (when (overlayp cur) (overlay-put cur 'face 'lazy-highlight))
+ (setq cur (seq-some
+ (lambda (ov) (and (overlay-get ov 'search) ov))
+ (overlays-at (point))))
+ (overlay-put cur 'face 'isearch)))
+ "search"))
+ (setq minibuffer-alternative-action
+ (cons
+ (lambda (c)
+ (if-let ((n (string-to-number c))
+ (d (gethash n tab)))
+ (with-selected-window
+ (or (get-buffer-window buffer) (display-buffer buffer))
+ (when (overlayp cur) (overlay-put cur 'face 'lazy-highlight))
+ (set-match-data d)
+ (let ((ov (seq-some
+ (lambda (ov) (and (overlay-get ov 'search) ov))
+ (overlays-at (match-beginning 0)))))
+ (unless rep
+ (overlay-put ov 'face 'isearch)
+ (goto-char (match-beginning 0))
+ (setq rep (query-replace-read-to reg "Replace" t)))
+ (setq ovs (delq ov ovs))
+ (delete-overlay ov))
+ (setq trs (delete c trs))
+ (remhash n tab)
+ (replace-match rep))
+ (user-error "Already replaced")))
+ "replace"))
+ (let ((hook-fn
+ (lambda (input)
+ (unless (string-empty-p input)
+ (mapc #'delete-overlay ovz)
+ (setq ovz nil)
+ (with-current-buffer buffer
+ (dolist (ov ovs)
+ (save-excursion
+ (goto-char (overlay-start ov))
+ (let ((r (regexp-quote input))
+ (e (overlay-end ov)))
+ (while (re-search-forward r e t)
+ (push (make-overlay (match-beginning 0)
+ (match-end 0))
+ ovz)
+ (overlay-put (car ovz) 'face 'search-highlight)
+ (overlay-put (car ovz) 'search-input t)
+ (overlay-put (car ovz) 'priority '(nil . 10))
+ (overlay-put (car ovz) 'evaporate t))))))))))
+ (add-hook 'minibuffer-new-completion-input-hook
+ (lambda () (funcall hook-fn (caar completion-history)))
+ nil t)
+ (add-hook 'completion-setup-hook
+ (lambda () (funcall hook-fn (minibuffer-contents)))
+ nil t)))
+ (gethash (string-to-number
+ (completing-read
+ "Search: "
+ (completion-table-with-metadata
+ (completion-table-dynamic (lambda (_) trs))
+ `((category . search)
+ (group-function
+ . ,(lambda (string &optional transform)
+ (when transform (nth 1 (string-split string ":")))))))
+ nil t))
+ tab))
(mapc #'delete-overlay ovs)
(mapc #'delete-overlay ovz))))
;;;###autoload
-(defun search (target)
- "Search for TARGET."
+(defun search (beg end)
+ "Go to and pulse region starting at BEG and ending at END."
(interactive
- (list
- (save-excursion (search-read-target (use-region-beginning) (use-region-end)))))
+ (save-excursion (search-read-target (use-region-beginning) (use-region-end))))
(push-mark)
- (unless (search--go-to target) (user-error "Invalid search target"))
- (seq-let (beg end _) (split-string target ":")
- (pulse-momentary-highlight-region (string-to-number beg)
- (string-to-number end)
- 'isearch)))
+ (goto-char beg)
+ (pulse-momentary-highlight-region beg end 'isearch))
;;;###autoload
-(defun search-lines (target)
- "Search for TARGET line."
+(defun search-lines (beg end)
+ "Go to and pulse line starting at BEG and ending at END."
(interactive
- (list (save-excursion
- (search-read-target (use-region-beginning) (use-region-end) ".*"))))
- (search target))
+ (save-excursion (search-read-target (use-region-beginning) (use-region-end) ".*")))
+ (search beg end))
(provide 'search)
;;; refactor.el ends here