From f6836214ee60e8c53f9f51ed7f56d77bd14c7800 Mon Sep 17 00:00:00 2001 From: jixiuf Date: Tue, 13 Feb 2024 23:52:23 +0800 Subject: [PATCH] rewrite search related code with isearch --- meow-beacon.el | 29 ++++++----- meow-command.el | 135 +++++++++++++++++------------------------------- meow-core.el | 6 +++ meow-face.el | 5 -- meow-util.el | 116 ++++++++++++++++++++++++++++++++++++----- meow-visual.el | 54 +++---------------- 6 files changed, 179 insertions(+), 166 deletions(-) diff --git a/meow-beacon.el b/meow-beacon.el index d11cdb1..329c39d 100644 --- a/meow-beacon.el +++ b/meow-beacon.el @@ -235,20 +235,23 @@ MATCH is the search regexp." (meow--narrow-secondary-selection) (let ((orig-end (region-end)) (orig-beg (region-beginning)) - (back (meow--direction-backward-p))) + (back (meow--direction-backward-p)) + (inhibit-redisplay t) + (isearch-state (isearch--get-state)) + (isearch-wrap-pause nil)) ; never wrap, just stop at the last match. (save-mark-and-excursion (goto-char (point-min)) - (let ((case-fold-search nil)) - (while (re-search-forward match nil t) - (unless (or (= orig-end (point)) - (= orig-beg (point))) - (let ((match (match-data))) - (meow--beacon-add-overlay-at-region - '(select . visit) - (car match) - (cadr match) - back))))) - (setq meow--beacon-overlays (reverse meow--beacon-overlays)))))) + (while (meow--search nil match nil t) + (unless (or (= orig-end (point)) + (= orig-beg (point))) + (let ((match isearch-match-data)) + (meow--beacon-add-overlay-at-region + '(select . visit) + (car match) + (cadr match) + back)))) + (setq meow--beacon-overlays (reverse meow--beacon-overlays))) + (isearch--set-state isearch-state)))) (defun meow--beacon-count-lines (beg end) "Count selected lines from BEG to END." @@ -393,7 +396,7 @@ MATCH is the search regexp." ((word) (if (not (eq 'expand ex)) (meow--add-beacons-for-word) (meow--add-beacons-for-match (meow--beacon-region-words-to-match)))) - ((visit) (meow--add-beacons-for-match (car regexp-search-ring))) + ((visit) (meow--add-beacons-for-match nil)) ((line) (meow--add-beacons-for-line)) ((join) (meow--add-beacons-for-join)) ((find) (meow--add-beacons-for-find)) diff --git a/meow-command.el b/meow-command.el index ec56b82..85aae41 100644 --- a/meow-command.el +++ b/meow-command.el @@ -129,9 +129,8 @@ This command supports `meow-selection-command-fallback'." (interactive) (meow--with-selection-fallback (meow--execute-kbd-macro meow--kbd-exchange-point-and-mark) - (if (member last-command - '(meow-visit meow-search meow-mark-symbol meow-mark-word)) - (meow--highlight-regexp-in-buffer (car regexp-search-ring)) + (unless (member last-command + '(meow-visit meow-search meow-mark-symbol meow-mark-word)) (meow--maybe-highlight-num-positions)))) ;;; Buffer @@ -749,14 +748,16 @@ Use negative argument to create a backward selection." (interactive "p") (let* ((bounds (bounds-of-thing-at-point 'word)) (beg (car bounds)) - (end (cdr bounds))) + (end (cdr bounds)) + (backward (< n 0))) (when beg (thread-first (meow--make-selection '(expand . word) beg end) - (meow--select (< n 0))) + (meow--select backward)) (let ((search (format "\\<%s\\>" (regexp-quote (buffer-substring-no-properties beg end))))) - (meow--push-search search) - (meow--highlight-regexp-in-buffer search))))) + (save-mark-and-excursion + (goto-char (if backward end beg)) + (meow--search backward search nil nil t)))))) (defun meow-mark-symbol (n) "Mark current symbol under cursor. @@ -765,14 +766,16 @@ This command works similar to `meow-mark-word'." (interactive "p") (let* ((bounds (bounds-of-thing-at-point 'symbol)) (beg (car bounds)) - (end (cdr bounds))) + (end (cdr bounds)) + (backward (< n 0))) (when beg (thread-first (meow--make-selection '(expand . word) beg end) - (meow--select (< n 0))) + (meow--select backward)) (let ((search (format "\\_<%s\\_>" (regexp-quote (buffer-substring-no-properties beg end))))) - (meow--push-search search) - (meow--highlight-regexp-in-buffer search))))) + (save-mark-and-excursion + (goto-char (if backward end beg)) + (meow--search backward search nil nil t)))))) (defun meow--forward-symbol-1 () (when (forward-symbol 1) @@ -1315,49 +1318,32 @@ with UNIVERSAL ARGUMENT, search both side." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun meow-search (arg) - " Search and select with the car of current `regexp-search-ring'. - -If the contents of selection doesn't match the regexp, will push it to `regexp-search-ring' before searching. + "Search for the next occurrence by `isearch-repeat' or search for +the current active region. To search backward, use \\[negative-argument]." (interactive "P") - ;; Test if we add current region as search target. - (when (and (region-active-p) - (let ((search (car regexp-search-ring))) - (or (not search) - (not (string-match-p - (format "^%s$" search) - (buffer-substring-no-properties (region-beginning) (region-end))))))) - (meow--push-search (regexp-quote (buffer-substring-no-properties (region-beginning) (region-end))))) - (when-let ((search (car regexp-search-ring))) - (let ((reverse (xor (meow--with-negative-argument-p arg) (meow--direction-backward-p))) - (case-fold-search nil)) - (if (or (if reverse - (re-search-backward search nil t 1) - (re-search-forward search nil t 1)) - ;; Try research from buffer beginning/end - ;; if we are already at the last/first matched - (save-mark-and-excursion - ;; Recalculate search indicator - (meow--clean-search-indicator-state) - (goto-char (if reverse (point-max) (point-min))) - (if reverse - (re-search-backward search nil t 1) - (re-search-forward search nil t 1)))) - (let* ((m (match-data)) - (marker-beg (car m)) - (marker-end (cadr m)) - (beg (if reverse (marker-position marker-end) (marker-position marker-beg))) - (end (if reverse (marker-position marker-beg) (marker-position marker-end)))) - (thread-first - (meow--make-selection '(select . visit) beg end) - (meow--select)) - (if reverse - (message "Reverse search: %s" search) - (message "Search: %s" search)) - (meow--ensure-visible)) - (message "Searching %s failed" search)) - (meow--highlight-regexp-in-buffer search)))) + (let ((reverse (xor (meow--with-negative-argument-p arg) + (if (region-active-p) + (meow--direction-backward-p) + (not isearch-forward)))) + match region mark point) + (when (region-active-p) + (setq region (buffer-substring-no-properties + (region-beginning)(region-end))) + (setq mark (mark)) + (setq point (point)) + (deactivate-mark) + (when (not(meow--search-match region)) + (setq match (regexp-quote region)))) + (when (and (not match) (string-empty-p isearch-string)) + (user-error "Failed to Search: please make a selection to search for")) + (meow--search reverse match) + ;; if failed reactivate origin region + (when (and region (not isearch-success)) + (set-mark mark) + (goto-char point)) + isearch-success)) (defun meow-pop-search () "Searching for the previous target." @@ -1366,21 +1352,9 @@ To search backward, use \\[negative-argument]." (message "current search is: %s" (car regexp-search-ring)) (meow--cancel-selection))) -(defun meow--visit-point (text reverse) - "Return the point of text for visit command. -Argument TEXT current search text. -Argument REVERSE if selection is reversed." - (let ((func (if reverse #'re-search-backward #'re-search-forward)) - (func-2 (if reverse #'re-search-forward #'re-search-backward)) - (case-fold-search nil)) - (save-mark-and-excursion - (or (funcall func text nil t 1) - (funcall func-2 text nil t 1))))) - (defun meow-visit (arg) "Read a string from minibuffer, then find and select it. -The input will be pushed into `regexp-search-ring'. So \\[meow-search] can be used for further searching with the same condition. A list of words and symbols in the current buffer will be provided for completion. @@ -1391,25 +1365,10 @@ the words and symbols in the current buffer. To search backward, use \\[negative-argument]." (interactive "P") (let* ((reverse arg) - (pos (point)) (text (meow--prompt-symbol-and-words (if arg "Visit backward: " "Visit: ") - (point-min) (point-max))) - (visit-point (meow--visit-point text reverse))) - (if visit-point - (let* ((m (match-data)) - (marker-beg (car m)) - (marker-end (cadr m)) - (beg (if (> pos visit-point) (marker-position marker-end) (marker-position marker-beg))) - (end (if (> pos visit-point) (marker-position marker-beg) (marker-position marker-end)))) - (thread-first - (meow--make-selection '(select . visit) beg end) - (meow--select)) - (meow--push-search text) - (meow--ensure-visible) - (meow--highlight-regexp-in-buffer text) - (setq meow--dont-remove-overlay t)) - (message "Visit: %s failed" text)))) + (point-min) (point-max)))) + (meow--search reverse text))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; THING @@ -1624,20 +1583,19 @@ Argument ARG if not nil, switching in a new window." Use negative argument for backward application." (interactive "P") - (let ((s (car regexp-search-ring)) - (case-fold-search nil) + ;;do not wrap when there are no more matches + (let ((isearch-wrap-pause nil) + (isearch-state (isearch--get-state)) (back (meow--with-negative-argument-p arg))) (meow--wrap-collapse-undo - (while (if back - (re-search-backward s nil t) - (re-search-forward s nil t)) + (while (meow--search back nil nil t) (thread-first (meow--make-selection '(select . visit) (if back (point) - (match-beginning 0)) + (car isearch-match-data)) (if back - (match-end 0) + (cadr isearch-match-data) (point))) (meow--select)) (let ((ov (make-overlay (region-beginning) (region-end)))) @@ -1648,7 +1606,8 @@ Use negative argument for backward application." (if back (goto-char (min (point) (overlay-start ov))) (goto-char (max (point) (overlay-end ov)))) - (delete-overlay ov)))))))) + (delete-overlay ov))))) + (isearch--set-state isearch-state)))) (defun meow-start-kmacro () "Start kmacro. diff --git a/meow-core.el b/meow-core.el index a4a862e..4aa5884 100644 --- a/meow-core.el +++ b/meow-core.el @@ -171,11 +171,14 @@ there's no chance for meow to call an init function." (add-hook 'window-state-change-functions #'meow--on-window-state-change) (add-hook 'minibuffer-setup-hook #'meow--minibuffer-setup) (add-hook 'pre-command-hook 'meow--highlight-pre-command) + + (add-hook 'post-command-hook 'meow--highlight-post-command) (add-hook 'post-command-hook 'meow--maybe-toggle-beacon-state) (add-hook 'suspend-hook 'meow--on-exit) (add-hook 'suspend-resume-hook 'meow--update-cursor) (add-hook 'kill-emacs-hook 'meow--on-exit) (add-hook 'desktop-after-read-hook 'meow--init-buffers) + (add-hook 'lazy-count-update-hook #'meow--lazy-count-hook) (meow--enable-shims) ;; meow-esc-mode fix ESC in TUI @@ -201,11 +204,14 @@ there's no chance for meow to call an init function." (remove-hook 'window-state-change-functions #'meow--on-window-state-change) (remove-hook 'minibuffer-setup-hook #'meow--minibuffer-setup) (remove-hook 'pre-command-hook 'meow--highlight-pre-command) + (remove-hook 'post-command-hook 'meow--highlight-post-command) (remove-hook 'post-command-hook 'meow--maybe-toggle-beacon-state) (remove-hook 'suspend-hook 'meow--on-exit) (remove-hook 'suspend-resume-hook 'meow--update-cursor) (remove-hook 'kill-emacs-hook 'meow--on-exit) (remove-hook 'desktop-after-read-hook 'meow--init-buffers) + (remove-hook 'lazy-count-update-hook #'meow--lazy-count-hook) + (meow--disable-shims) (meow--remove-modeline-indicator) (when meow-use-cursor-position-hack diff --git a/meow-face.el b/meow-face.el index bf18ac6..3955d74 100644 --- a/meow-face.el +++ b/meow-face.el @@ -152,11 +152,6 @@ "Indicator for region direction." :group 'meow) -(defface meow-search-highlight - '((t (:inherit lazy-highlight))) - "Search target highlight." - :group 'meow) - (defface meow-position-highlight-number '((((class color) (background dark)) (:inherit default)) diff --git a/meow-util.el b/meow-util.el index b92e3c5..76dd383 100644 --- a/meow-util.el +++ b/meow-util.el @@ -26,6 +26,7 @@ (require 'cl-lib) (require 'seq) (require 'color) +(require 'isearch) (require 'meow-var) (require 'meow-keymap) @@ -400,14 +401,6 @@ Looks up the state in meow-replace-state-name-list" (upcase (event-basic-type e)) (event-basic-type e))) -(defun meow--ensure-visible () - (let ((overlays (overlays-at (1- (point)))) - ov expose) - (while (setq ov (pop overlays)) - (if (and (invisible-p (overlay-get ov 'invisible)) - (setq expose (overlay-get ov 'isearch-open-invisible))) - (funcall expose ov))))) - (defun meow--minibuffer-setup () (local-set-key (kbd "") #'meow-minibuffer-quit) (setq-local meow-normal-mode nil) @@ -576,11 +569,24 @@ that bound to DEF. Otherwise, return DEF." (undo-amalgamate-change-group ,handle)) (cancel-change-group ,handle)))))) +(defun meow--highlight-post-command () + (let ((remove t)) + (cond + ((member this-command '(isearch-exit meow-search)) + (setq remove nil)) + (isearch-mode + (setq remove nil)) + ((and isearch-match-data + (eq (current-buffer) (nth 2 isearch-match-data)) + (<= (car isearch-match-data) (point) + (cadr isearch-match-data))) + (setq remove nil))) + (when remove + (meow--remove-match-highlights) + (meow--remove-search-highlight)))) + (defun meow--highlight-pre-command () - (unless (member this-command '(meow-search)) - (meow--remove-match-highlights)) - (meow--remove-expand-highlights) - (meow--remove-search-highlight)) + (meow--remove-expand-highlights)) (defun meow--remove-fake-cursor (rol) (when (overlayp rol) @@ -696,5 +702,91 @@ that bound to DEF. Otherwise, return DEF." ((null meow-keypad-leader-dispatch) (alist-get 'leader meow-keymap-alist)))) +(defun meow--search (reverse &optional match not-regexp + inhibit-lazy-highlight-and-count not-repeat) + "Search for the next occurrence of MATCH using isearch. +If found, move point to the end of the occurrence,and return point. + +REVERSE - If non-nil, the search direction is backward. Otherwise, it is forward. +MATCH - The string to search for, if nil `isearch-repeat' is called. +NOT-REGEXP - If non-nil, do a regular string search instead. +INHIBIT-LAZY-HIGHLIGHT-AND-COUNT - If non-nil disable lazy highlighting and +lazy counting features. +NOT-REPEAT - do not use `isearch-repeat' even MATCH equals to last `isearch-string'." + (interactive "P") + (when (and (string-equal match isearch-string) + (equal isearch-regexp (not not-regexp)) + (not not-repeat)) + (setq match nil)) + (let ((inhibit-redisplay t) + ;; we call `isearch-lazy-highlight-new-loop' at the end + ;; of function,so set `isearch-lazy-count' and `isearch-lazy-highlight' + ;; to nil is costless. + (isearch-lazy-count nil) + (isearch-lazy-highlight nil)) + (if (not match) + ;; if MATCH is nil, just call `isearch-repeat' + (isearch-repeat (if reverse 'backward 'forward)) + (if reverse + (isearch-backward-regexp not-regexp t) + (isearch-forward-regexp not-regexp t)) + (isearch-process-search-string + match + (mapconcat 'isearch-text-char-description match ""))) + (isearch-update) + (isearch-done)) + ;; highlight after isearch-done + ;; M-x:lazy-highlight-cleanup to cleanup highlight + (when (and isearch-success + (not inhibit-lazy-highlight-and-count)) + (isearch-lazy-highlight-new-loop) + (meow--post-isearch-function)) + isearch-success) + +(defun meow--search-match (match) + "Check if MATCH can be matched by the current `isearch-string'." + (when (and match + (not (string-empty-p match)) + (not (string-empty-p isearch-string))) + (save-match-data + (save-mark-and-excursion + (let ((case-fold-search isearch-case-fold-search) + (search-invisible isearch-invisible)) + (with-temp-buffer + (insert match) + (when isearch-forward + (goto-char (point-min))) + (and (isearch-search-string isearch-string nil t) + (string-equal (match-string 0) match)))))))) + +(defun meow--lazy-count-hook () + (when (meow-normal-mode-p) + (save-mark-and-excursion + (meow--remove-search-indicator) + (when isearch-lazy-count-current + (meow--show-indicator (point) (isearch-lazy-count-format))) + ;; If the current search hits one, then there is no need to highlight it. + (when (number-or-marker-p isearch-success) + (dolist (ov isearch-lazy-highlight-overlays) + (let ((ov-start (overlay-start ov)) + (ov-end (overlay-end ov))) + ;; Check if point `isearch-success' is within the overlay region. + (when (and (<= ov-start isearch-success) (>= ov-end isearch-success)) + (overlay-put ov 'priority 0)))))))) + +(defun meow--post-isearch-function () + (unless isearch-mode-end-hook-quit + (when (and isearch-success isearch-match-data) + (let ((beg (car isearch-match-data)) + (end (cadr isearch-match-data)) + (selection (meow--selection-type))) + ;; if a selection already exists there, do not create the visit selection + (unless selection + (thread-first + (meow--make-selection '(select . visit) + beg + (if isearch-forward end isearch-other-end)) + (meow--select (not isearch-forward)))))))) + (provide 'meow-util) ;;; meow-util.el ends here diff --git a/meow-visual.el b/meow-visual.el index e1f4eff..a08f78e 100644 --- a/meow-visual.el +++ b/meow-visual.el @@ -35,9 +35,6 @@ (defvar meow--expand-overlays nil "Overlays used to highlight expand hints in buffer.") -(defvar meow--match-overlays nil - "Overlays used to highlight matches in buffer.") - (defvar meow--search-indicator-overlay nil "Overlays used to display search indicator in current line.") @@ -46,9 +43,6 @@ Value is a list of (last-regexp last-pos idx cnt).") -(defvar meow--dont-remove-overlay nil - "Indicate we should prevent removing overlay for once.") - (defvar meow--highlight-timer nil "Timer for highlight cleaner.") @@ -57,8 +51,7 @@ Value is a list of (last-regexp last-pos idx cnt).") (setq meow--expand-overlays nil)) (defun meow--remove-match-highlights () - (mapc #'delete-overlay meow--match-overlays) - (setq meow--match-overlays nil)) + (lazy-highlight-cleanup t)) (defun meow--remove-search-highlight () (when meow--search-indicator-overlay @@ -72,53 +65,18 @@ Value is a list of (last-regexp last-pos idx cnt).") (meow--remove-search-highlight) (meow--clean-search-indicator-state)) -(defun meow--show-indicator (pos idx cnt) +(defun meow--show-indicator (pos msg) (goto-char pos) (goto-char (line-end-position)) + (setq msg (string-trim-right msg)) (if (= (point) (point-max)) (let ((ov (make-overlay (point) (point)))) - (overlay-put ov 'after-string (propertize (format " [%d/%d]" idx cnt) 'face 'meow-search-indicator)) + (overlay-put ov 'after-string (propertize (format " [%s] " msg) 'face 'meow-search-indicator)) (setq meow--search-indicator-overlay ov)) - (let ((ov (make-overlay (point) (1+ (point))))) - (overlay-put ov 'display (propertize (format " [%d/%d] \n" idx cnt) 'face 'meow-search-indicator)) + (let ((ov (make-overlay (point) (1+ (point))))) + (overlay-put ov 'display (propertize (format " [%s]\n" msg) 'face 'meow-search-indicator)) (setq meow--search-indicator-overlay ov)))) -(defun meow--highlight-match () - (let ((beg (match-beginning 0)) - (end (match-end 0))) - (unless (cl-find-if (lambda (it) - (overlay-get it 'meow)) - (overlays-at beg)) - (let ((ov (make-overlay beg end))) - (overlay-put ov 'face 'meow-search-highlight) - (overlay-put ov 'priority 0) - (overlay-put ov 'meow t) - (push ov meow--match-overlays))))) - -(defun meow--highlight-regexp-in-buffer (regexp) - "Highlight all regexp in this buffer." - (when (and (meow-normal-mode-p) - (region-active-p)) - (meow--remove-expand-highlights) - (let* ((cnt 0) - (idx 0) - (pos (region-end)) - (hl-start (max (point-min) (- (point) 3000))) - (hl-end (min (point-max) (+ (point) 3000)))) - (setq meow--expand-nav-function nil) - (setq meow--visual-command this-command) - (save-mark-and-excursion - (meow--remove-search-indicator) - (let ((case-fold-search nil)) - (goto-char (point-min)) - (while (re-search-forward regexp (point-max) t) - (cl-incf cnt) - (when (<= (match-beginning 0) pos (match-end 0)) - (setq idx cnt)) - (when (<= hl-start (point) hl-end) - (meow--highlight-match))) - (meow--show-indicator pos idx cnt)))))) - (defun meow--format-full-width-number (n) (alist-get n meow-full-width-number-position-chars))