abo-abo / swiper

Ivy - a generic completion frontend for Emacs, Swiper - isearch with an overview, and more. Oh, man!
https://oremacs.com/swiper/
2.3k stars 338 forks source link

swiper-mc leaves a cursor behind #1304

Open jwiegley opened 6 years ago

jwiegley commented 6 years ago

When I use swiper to search for a word like "use-package" in my init.el, and then hit M-c, I properly get a cursor for every search hit, but I also get another cursor, not colored black like the multiple-cursors, and located somewhere near (but not exactly where) I started the search. It's not on a search hit. But all the operations I know do with multiple-cursors also happen at this stray cursor.

My relevant configuration is:

(use-package ivy
  :demand t
  :diminish ivy-mode
  :load-path "site-lisp/site-ivy/swiper"
  :bind (("C-x b" . ivy-switch-buffer)
         ("C-x B" . ivy-switch-buffer-other-window)
         ("M-H"   . ivy-resume))
  :commands (ivy-mode ivy-read ivy-completing-read)
  :init
  (defun my-ivy-completing-read (&rest args)
    (let ((ivy-sort-functions-alist '((t . nil))))
      (apply 'ivy-completing-read args)))

  :config
  (setq ivy-initial-inputs-alist nil
        ivy-re-builders-alist '((t . ivy--regex-ignore-order)))

  (ivy-mode 1)

  (ivy-set-occur 'ivy-switch-buffer 'ivy-switch-buffer-occur)

  (bind-key "C-r" #'ivy-previous-line-or-history ivy-minibuffer-map)
  (bind-key "M-r" #'ivy-reverse-i-search ivy-minibuffer-map)

  (use-package ivy-hydra
    :demand t)

  (use-package ivy-rich
    :demand t
    :load-path "site-lisp/site-ivy/ivy-rich"
    :config
    (ivy-set-display-transformer 'ivy-switch-buffer
                                 'ivy-rich-switch-buffer-transformer)
    (setq ivy-virtual-abbreviate 'full
          ivy-rich-switch-buffer-align-virtual-buffer t)
    (setq ivy-rich-path-style 'abbrev))

  (use-package swiper
    :demand t
    :load-path "site-lisp/ivy/swiper"
    :bind (("C-s" . swiper)
           ("C-. C-s" . swiper)
           ("C-. C-r" . swiper))
    :commands swiper-from-isearch
    :init
    (bind-key "C-." #'swiper-from-isearch isearch-mode-map)
    :config
    (bind-key "M-y" #'yank swiper-map)
    (bind-key "M-%" #'swiper-query-replace swiper-map)
    (bind-key "M-h" #'swiper-avy swiper-map)
    (bind-key "M-c" #'swiper-mc swiper-map))

  (use-package counsel
    :demand t
    :diminish counsel-mode
    :bind (("M-x"     . counsel-M-x)
           ("C-h f"   . counsel-describe-function)
           ("C-h v"   . counsel-describe-variable)
           ("C-h e l" . counsel-find-library)
           ("C-h e u" . counsel-unicode-char))
    :commands counsel-minibuffer-history
    :init
    (define-key minibuffer-local-map (kbd "M-r")
      'counsel-minibuffer-history)
    :config
    (counsel-mode 1)

    (use-package emacs-counsel-gtags
      :disabled t
      :load-path "site-lisp/site-ivy/emacs-counsel-gtags")))

(use-package multiple-cursors
  :load-path "site-lisp/multiple-cursors"
  :bind (("C-. c"   . mc/edit-lines)
         ("C-'"     . mc/edit-lines)
         ("C->"     . mc/mark-next-like-this)
         ("C-<"     . mc/mark-previous-like-this)
         ("C-c C-<" . mc/mark-all-like-this)

         ("C-c m n" . mc/insert-numbers)
         ("C-c m l" . mc/insert-letters)
         ("C-c m s" . mc/sort-regions)
         ("C-c m R" . mc/reverse-regions)
         ("C-c m r" . set-rectangular-region-anchor)))
habamax commented 6 years ago

I have the same problem with the similar setup.

habamax commented 6 years ago

I actually tried once again and narrowed misbehaviour.

tldr; If you have matched string upwards (not showed in the window) then additional cursor appears.

Steps to reproduce:

  1. bind swiper-mc to M-c
  2. open scratch buffer
  3. input 100 lines of "hello": F3 hello <CR> F4 C-u 100 F4 (it is important that number of generated lines > number of visible lines)

Happy path (all ok)

  1. goto beginning of buffer M-S-<
  2. Swiper for hello
  3. Call swiper-mc with M-c
  4. ALL GOOD

Bug path

  1. goto end of buffer M-S->
  2. Swiper for hello
  3. Call swiper-mc with M-c

Result: additional cursor is created: image

habamax commented 6 years ago

So there is a function swiper--action which is called for each candidate on swiper-mc.

If you comment out

              (swiper--current-window-start
               (set-window-start (selected-window) swiper--current-window-start))

then it no additional cursor is created but cursor is on first matched string.

(defun swiper--action (x)
  "Goto line X."
  (let ((ln (1- (read (or (get-text-property 0 'swiper-line-number x)
                          (and (string-match ":\\([0-9]+\\):.*\\'" x)
                               (match-string-no-properties 1 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 swiper--point-min)
        (funcall (if swiper-use-visual-line
                     #'line-move
                   #'forward-line)
                 ln)
        (when (and (re-search-forward re (line-end-position) t) swiper-goto-start-of-match)
          (goto-char (match-beginning 0)))
        (swiper--ensure-visible)
        (cond (swiper-action-recenter
               (recenter))
              ;; HERE ;; (swiper--current-window-start
              ;; HERE ;; (set-window-start (selected-window) swiper--current-window-start))
              )
        (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)
        ;; integration with evil-mode's search
        (when (bound-and-true-p evil-mode)
          (when (eq evil-search-module 'isearch)
            (setq isearch-string ivy-text))
          (when (eq evil-search-module 'evil-search)
            (add-to-history 'evil-ex-search-history re)
            (setq evil-ex-search-pattern (list re t t))
            (setq evil-ex-search-direction 'forward)
            (when evil-ex-search-persistent-highlight
              (evil-ex-search-activate-highlight evil-ex-search-pattern))))))))
habamax commented 6 years ago

And the temporary fix for the problem is to define a new function and bind it instead of swiper-mc

for ex:

  (use-package swiper
    :bind (:map swiper-map
                ("M-c" . haba/swiper-mc-fixed))
    :init
    (bind-key "C-." #'swiper-from-isearch isearch-mode-map)
    :config
    (defun haba/swiper-mc-fixed()
      (interactive)
      (setq swiper--current-window-start nil)
      (swiper-mc)))

Anyway for multiple cursors it is not that important to stay close to the point where swiper starts searching. With the func above active cursor is on first swiper match.