diff --git a/meow-command.el b/meow-command.el index 0f3b0b0..04b1790 100644 --- a/meow-command.el +++ b/meow-command.el @@ -1349,18 +1349,45 @@ Argument REVERSE if selection is reversed." 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. -To search for regexp instead, set `meow-visit-sanitize-completion' to nil. -In that case, completions will be provided in regexp form, but also covering +When `meow-prompter' is `completion' (the default), a list of +words and symbols in the current buffer will be provided for +completion. To search for a regexp instead, set +`meow-visit-sanitize-completion' to nil. In that case, +completions will be provided in regexp form, but also covering the words and symbols in the current buffer. +When `meow-prompter' is `buffer-highlight', matches are +highlighted in the current buffer from point until then end of +the visible text. When `meow-visit-sanitize-completion' is +non-nil, only whole words and symbols are highlighted, and the +input is treated literally. When `meow-beacon-mode' is active +and point is inside the secondary selection, matches back to the +start or end of the selection are highlighted, depending on whether +`meow-visit' is searching backward or forward. + 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))) + (prompt (if reverse "Visit backward: " "Visit: ")) + (text (pcase meow-visit-prompter + ('buffer-highlight + (apply #'meow--prompt-buffer-highlight + prompt + (let* ((ov-start (overlay-start mouse-secondary-overlay)) + (ov-end (overlay-end mouse-secondary-overlay)) + (use-sec (and (meow-beacon-mode-p) + (secondary-selection-exist-p) + (>= pos ov-start) + (< pos ov-end)))) + (if reverse + (list (window-start (selected-window)) + (if use-sec ov-end pos)) + (list (if use-sec ov-start pos) + (window-end (selected-window))))))) + ((or 'completion _) + (meow--prompt-symbol-and-words prompt + (point-min) (point-max))))) (visit-point (meow--visit-point text reverse))) (if visit-point (let* ((m (match-data)) diff --git a/meow-util.el b/meow-util.el index 4d581c6..3560a38 100644 --- a/meow-util.el +++ b/meow-util.el @@ -290,6 +290,67 @@ Looks up the state in meow-replace-state-name-list" (regexp-quote selected)) selected)))) +(defun meow--prompt-buffer-highlight (prompt beg end) + "PROMPT from the minibuffer while highlighting matches between BEG and END. + +When `meow-visit-sanitize-completion' is non-nil, matches are +literal instead of regexps and only words and symbols are +matched. + +Highlights are only visible in the selected window." + (let ((current-buffer (current-buffer)) + (selected-window (selected-window)) + (timer) + (overlays) + (prompter-minibuffer)) + (cl-labels ((transform-text (txt &optional quote-regexp) + (cond (meow-visit-sanitize-completion + ;; The below regexp was made using the below call to + ;; `rx', which has gained features since Emacs 26.3. + ;; + ;;(rx (or (seq symbol-start (literal txt) symbol-end)) + ;; (seq word-start (literal txt) word-end)) + (let ((qtd (regexp-quote txt))) + (concat "\\_<\\(?:" qtd "\\)\\_>\\|\\<\\(?:" qtd "\\)\\>"))) + (quote-regexp (regexp-quote txt)) + (t txt))) + (delete-overlays () + (mapc #'delete-overlay overlays) + (setq overlays nil)) + (highlight-matches () + ;; Before highlighting the minibuffer contents, we need to + ;; check that we are still in the prompter's minibuffer, not + ;; some other minibuffer. + (when (or (not enable-recursive-minibuffers) + (equal prompter-minibuffer (current-buffer))) + (delete-overlays) + (let ((regexp (transform-text (minibuffer-contents)))) + (unless (string-empty-p regexp) + (save-excursion + (with-current-buffer current-buffer + (goto-char beg) + (while (ignore-error invalid-regexp + ;; If the user is still typing the regexp it + ;; might not be valid. In that case, we + ;; treat it as valid but not matching. + (re-search-forward regexp end t)) + (let ((ov (make-overlay (match-beginning 0) + (match-end 0)))) + (overlay-put ov 'face 'lazy-highlight) + ;; Same priority as Isearch lazy-highlight. + (overlay-put ov 'priority 1000) + (overlay-put ov 'window selected-window) + (push ov overlays))))))))) + (make-hl-timer () + (run-with-idle-timer 0.01 'repeat #'highlight-matches))) + (unwind-protect + (minibuffer-with-setup-hook + (lambda () (setq timer (make-hl-timer) + prompter-minibuffer (current-buffer))) + (transform-text (read-from-minibuffer prompt) t)) + (cancel-timer timer) + (delete-overlays))))) + (defun meow--on-window-state-change (&rest _args) "Update cursor style after switching window." (meow--update-cursor) diff --git a/meow-var.el b/meow-var.el index f6b364d..5f310cd 100644 --- a/meow-var.el +++ b/meow-var.el @@ -153,8 +153,29 @@ This will affect how selection is displayed." :group 'meow :type '(repeat function)) +(defcustom meow-visit-prompter 'completion + "How `meow-visit' should prompt for a target. + +- `completion' (the default) means to present a lists of regexps + in the minibuffer, respecting the value of + `meow-visit-sanitize-completion'. + +- `buffer-highlight' means to highlight in the buffer the region + matching the text in the minibuffer, whether after or before + point. If point is within the secondary selection and + `meow-beacon-mode' is active, then matches within the + secondary selection are also highlighted. When + `meow-visit-sanitize-completion' is non-nil, this treats the + input literally and only matches whole words and symbols. + +Any other value is treated as `completion'." + :group 'meow + :type '(choice (const :tag "Show regexps in the minibuffer" completion) + (const :tag "Highlight text in the buffer" buffer-highlight))) + (defcustom meow-visit-collect-min-length 1 - "Minimal length when collecting symbols for `meow-visit'." + "Minimal length when collecting symbols for `meow-visit' in the default prompter. +See also `meow-visit-prompter'." :group 'meow :type 'integer)