]> code.delx.au - gnu-emacs-elpa/blobdiff - swiper.el
swiper.el (swiper-mc): Remove interactive
[gnu-emacs-elpa] / swiper.el
index 216a50a0e39a2995d4c3d568779a9394220b1763..90e25b5f39c200e0aef2549d8648ea5bfecee2a9 100644 (file)
--- a/swiper.el
+++ b/swiper.el
@@ -4,7 +4,7 @@
 
 ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
 ;; URL: https://github.com/abo-abo/swiper
-;; Version: 0.6.0
+;; Version: 0.7.0
 ;; Package-Requires: ((emacs "24.1"))
 ;; Keywords: matching
 
   '((t (:inherit isearch-fail)))
   "Face for `swiper' matches modulo 3.")
 
-(define-obsolete-face-alias 'swiper-minibuffer-match-face-1
-    'ivy-minibuffer-match-face-1 "0.6.0")
-
-(define-obsolete-face-alias 'swiper-minibuffer-match-face-2
-    'ivy-minibuffer-match-face-2 "0.6.0")
-
-(define-obsolete-face-alias 'swiper-minibuffer-match-face-3
-    'ivy-minibuffer-match-face-3 "0.6.0")
-
-(define-obsolete-face-alias 'swiper-minibuffer-match-face-4
-    'ivy-minibuffer-match-face-4 "0.6.0")
-
 (defface swiper-line-face
   '((t (:inherit highlight)))
   "Face for current `swiper' line.")
@@ -81,7 +69,9 @@
                           swiper-match-face-2
                           swiper-match-face-3
                           swiper-match-face-4)
-  "List of `swiper' faces for group matches.")
+  "List of `swiper' faces for group matches."
+  :group 'ivy-faces
+  :type 'list)
 
 (defcustom swiper-min-highlight 2
   "Only highlight matches for regexps at least this long."
       (user-error "Should only be called in the minibuffer through `swiper-map'")
     (let* ((enable-recursive-minibuffers t)
            (from (ivy--regex ivy-text))
-           (to (query-replace-read-to from "Query replace" t)))
-      (delete-minibuffer-contents)
-      (ivy-set-action (lambda (_)
-                        (with-ivy-window
-                          (move-beginning-of-line 1)
-                          (perform-replace from to
-                                           t t nil))))
+           (to (minibuffer-with-setup-hook
+                   (lambda ()
+                     (setq minibuffer-default
+                           (if (string-match "\\`\\\\_<\\(.*\\)\\\\_>\\'" ivy-text)
+                               (match-string 1 ivy-text)
+                             ivy-text)))
+                 (read-from-minibuffer (format "Query replace %s with: " from)))))
       (swiper--cleanup)
-      (exit-minibuffer))))
+      (ivy-exit-with-action
+       (lambda (_)
+         (with-ivy-window
+           (move-beginning-of-line 1)
+           (perform-replace from to
+                            t t nil)))))))
 
 (defvar avy-background)
 (defvar avy-all-windows)
                                 (forward-line))
                               cands)))))
            (candidate (unwind-protect
-                          (prog2
-                              (avy--make-backgrounds
-                               (append (avy-window-list)
-                                       (list  (ivy-state-window ivy-last))))
-                              (if (eq avy-style 'de-bruijn)
-                                  (avy-read-de-bruijn
-                                   candidates avy-keys)
-                                (avy-read (avy-tree candidates avy-keys)
-                                          #'avy--overlay-post
-                                          #'avy--remove-leading-chars))
-                            (avy-push-mark))
+                           (prog2
+                               (avy--make-backgrounds
+                                (append (avy-window-list)
+                                        (list (ivy-state-window ivy-last))))
+                               (if (eq avy-style 'de-bruijn)
+                                   (avy-read-de-bruijn
+                                    candidates avy-keys)
+                                 (avy-read (avy-tree candidates avy-keys)
+                                           #'avy--overlay-post
+                                           #'avy--remove-leading-chars))
+                             (avy-push-mark))
                         (avy--done))))
       (if (window-minibuffer-p (cdr candidate))
           (progn
 (declare-function mc/create-fake-cursor-at-point "ext:multiple-cursors-core")
 (declare-function multiple-cursors-mode "ext:multiple-cursors-core")
 
-;;;###autoload
 (defun swiper-mc ()
-  (interactive)
   (unless (require 'multiple-cursors nil t)
     (error "multiple-cursors isn't installed"))
   (let ((cands (nreverse ivy--old-cands)))
     (unless (string= ivy-text "")
-      (ivy-set-action
+      (ivy-exit-with-action
        (lambda (_)
          (let (cand)
            (while (setq cand (pop cands))
              (swiper--action cand)
              (when cands
                (mc/create-fake-cursor-at-point))))
-         (multiple-cursors-mode 1)))
-      (setq ivy-exit 'done)
-      (exit-minibuffer))))
+         (multiple-cursors-mode 1))))))
 
 (defun swiper-recenter-top-bottom (&optional arg)
   "Call (`recenter-top-bottom' ARG)."
   (with-ivy-window
     (recenter-top-bottom arg)))
 
+(defvar swiper-font-lock-exclude
+  '(package-menu-mode
+    gnus-summary-mode
+    gnus-article-mode
+    gnus-group-mode
+    emms-playlist-mode
+    emms-stream-mode
+    erc-mode
+    org-agenda-mode
+    dired-mode
+    jabber-chat-mode
+    elfeed-search-mode
+    elfeed-show-mode
+    fundamental-mode
+    Man-mode
+    woman-mode
+    mu4e-view-mode
+    mu4e-headers-mode
+    help-mode
+    debbugs-gnu-mode
+    occur-mode
+    occur-edit-mode
+    bongo-mode
+    bongo-library-mode
+    bongo-playlist-mode
+    eww-mode
+    twittering-mode
+    vc-dir-mode
+    rcirc-mode
+    sauron-mode
+    w3m-mode)
+  "List of major-modes that are incompatible with font-lock-ensure.")
+
+(defun swiper-font-lock-ensure-p ()
+  "Return non-nil if we should font-lock-ensure."
+  (or (derived-mode-p 'magit-mode)
+              (bound-and-true-p magit-blame-mode)
+              (memq major-mode swiper-font-lock-exclude)))
+
 (defun swiper-font-lock-ensure ()
   "Ensure the entired buffer is highlighted."
-  (unless (or (derived-mode-p 'magit-mode)
-              (bound-and-true-p magit-blame-mode)
-              (memq major-mode '(package-menu-mode
-                                 gnus-summary-mode
-                                 gnus-article-mode
-                                 gnus-group-mode
-                                 emms-playlist-mode
-                                 emms-stream-mode
-                                 erc-mode
-                                 org-agenda-mode
-                                 dired-mode
-                                 jabber-chat-mode
-                                 elfeed-search-mode
-                                 elfeed-show-mode
-                                 fundamental-mode
-                                 Man-mode
-                                 woman-mode
-                                 mu4e-view-mode
-                                 mu4e-headers-mode
-                                 help-mode
-                                 debbugs-gnu-mode
-                                 occur-mode
-                                 occur-edit-mode
-                                 bongo-mode
-                                 eww-mode
-                                 w3m-mode)))
-    (unless (> (buffer-size) 100000)
+  (unless (swiper-font-lock-ensure-p)
+    (unless (or (> (buffer-size) 100000) (null font-lock-mode))
       (if (fboundp 'font-lock-ensure)
           (font-lock-ensure)
         (with-no-warnings (font-lock-fontify-buffer))))))
   "Store the current candidates format spec.")
 
 (defvar swiper--width nil
-  "Store the amount of digits needed for the longest line nubmer.")
+  "Store the number of digits needed for the longest line nubmer.")
 
 (defvar swiper-use-visual-line nil
   "When non-nil, use `line-move' instead of `forward-line'.")
 
+(declare-function outline-show-all "outline")
+
 (defun swiper--candidates (&optional numbers-width)
   "Return a list of this buffer lines.
 
-NUMBERS-WIDTH, when specified, is used for line numbers width
-spec, instead of calculating it as the log of the buffer line
-count."
-  (setq swiper-use-visual-line
-        (and (not (eq major-mode 'org-mode))
-             visual-line-mode
-             (< (buffer-size) 20000)))
+NUMBERS-WIDTH, when specified, is used for width spec of line
+numbers; replaces calculating the width from buffer line count."
+  (if (and visual-line-mode
+           ;; super-slow otherwise
+           (< (buffer-size) 20000))
+      (progn
+        (when (eq major-mode 'org-mode)
+          (require 'outline)
+          (if (fboundp 'outline-show-all)
+              (outline-show-all)
+            (with-no-warnings
+              (show-all))))
+        (setq swiper-use-visual-line t))
+    (setq swiper-use-visual-line nil))
   (let ((n-lines (count-lines (point-min) (point-max))))
     (unless (zerop n-lines)
       (setq swiper--width (or numbers-width
@@ -273,13 +287,18 @@ count."
                         " "
                         (replace-regexp-in-string
                          "\t" "    "
-                         (buffer-substring
-                          (point)
-                          (if swiper-use-visual-line
+                         (if swiper-use-visual-line
+                             (buffer-substring
                               (save-excursion
-                                (end-of-visual-line)
+                                (beginning-of-visual-line)
                                 (point))
+                              (save-excursion
+                                (end-of-visual-line)
+                                (point)))
+                           (buffer-substring
+                            (point)
                             (line-end-position)))))))
+              (remove-text-properties 0 (length str) '(field) str)
               (put-text-property 0 1 'display
                                  (format swiper--format-spec
                                          (cl-incf line-number))
@@ -296,35 +315,89 @@ count."
   "`isearch' with an overview.
 When non-nil, INITIAL-INPUT is the initial search pattern."
   (interactive)
-  (swiper--ivy initial-input))
+  (swiper--ivy (swiper--candidates) initial-input))
+
+(declare-function string-trim-right "subr-x")
+
+(defun swiper-occur (&optional revert)
+  "Generate a custom occur buffer for `swiper'.
+When REVERT is non-nil, regenerate the current *ivy-occur* buffer."
+  (let* ((buffer (ivy-state-buffer ivy-last))
+         (fname (propertize
+                 (with-ivy-window
+                   (if (buffer-file-name buffer)
+                       (file-name-nondirectory
+                        (buffer-file-name buffer))
+                     (buffer-name buffer)))
+                 '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)))
+                 (if (null revert)
+                     ivy--old-cands
+                   (setq ivy--old-re nil)
+                   (let ((ivy--regex-function 'swiper--re-builder))
+                     (ivy--filter
+                      (progn (string-match "\"\\(.*\\)\"" (buffer-name))
+                             (match-string 1 (buffer-name)))
+                      (with-current-buffer buffer
+                        (swiper--candidates))))))))
+    (unless (eq major-mode 'ivy-occur-grep-mode)
+      (ivy-occur-grep-mode)
+      (font-lock-mode -1))
+    (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))
+    (goto-char (point-min))
+    (forward-line 4)))
+
+(ivy-set-occur 'swiper 'swiper-occur)
 
 (declare-function evil-jumper--set-jump "ext:evil-jumper")
 
+(defvar swiper--current-line nil)
+(defvar swiper--current-match-start nil)
+
 (defun swiper--init ()
   "Perform initialization common to both completion methods."
+  (setq swiper--current-line nil)
+  (setq swiper--current-match-start nil)
   (setq swiper--opoint (point))
   (when (bound-and-true-p evil-jumper-mode)
     (evil-jumper--set-jump)))
 
 (defun swiper--re-builder (str)
   "Transform STR into a swiper regex.
-This is the regex used in the minibuffer, since the candidates
-there have line numbers. In the buffer, `ivy--regex' should be used."
-  (cond
-    ((equal str "")
-     "")
-    ((equal str "^")
-     (setq ivy--subexps 0)
-     ".")
-    ((string-match "^\\^" str)
-     (setq ivy--old-re "")
-     (let ((re (ivy--regex-plus (substring str 1))))
-       (if (zerop ivy--subexps)
-           (prog1 (format "^ ?\\(%s\\)" re)
-             (setq ivy--subexps 1))
-         (format "^ %s" re))))
-    (t
-     (ivy--regex-plus str))))
+This is the regex used in the minibuffer where candidates have
+line numbers. For the buffer, use `ivy--regex' instead."
+  (replace-regexp-in-string
+   "\t" "    "
+   (cond
+     ((equal str "")
+      "")
+     ((equal str "^")
+      (setq ivy--subexps 0)
+      ".")
+     ((string-match "^\\^" str)
+      (setq ivy--old-re "")
+      (let ((re (ivy--regex-plus (substring str 1))))
+        (if (zerop ivy--subexps)
+            (prog1 (format "^ ?\\(%s\\)" re)
+              (setq ivy--subexps 1))
+          (format "^ %s" re))))
+     (t
+      (ivy--regex-plus str)))))
 
 (defvar swiper-history nil
   "History for `swiper'.")
@@ -332,36 +405,39 @@ there have line numbers. In the buffer, `ivy--regex' should be used."
 (defvar swiper-invocation-face nil
   "The face at the point of invocation of `swiper'.")
 
-(defun swiper--ivy (&optional initial-input)
-  "`isearch' with an overview using `ivy'.
+(defun swiper--ivy (candidates &optional initial-input)
+  "Select one of CANDIDATES and move there.
 When non-nil, INITIAL-INPUT is the initial search pattern."
   (interactive)
   (swiper--init)
   (setq swiper-invocation-face
         (plist-get (text-properties-at (point)) 'face))
-  (let ((candidates (swiper--candidates))
-        (preselect
-         (if (bound-and-true-p visual-line-mode)
+  (let ((preselect
+         (if swiper-use-visual-line
              (count-screen-lines
               (point-min)
               (save-excursion (beginning-of-visual-line) (point)))
            (1- (line-number-at-pos))))
-        (minibuffer-allow-text-properties t))
+        (minibuffer-allow-text-properties t)
+        res)
     (unwind-protect
-         (ivy-read
-          "Swiper: "
-          candidates
-          :initial-input initial-input
-          :keymap swiper-map
-          :preselect preselect
-          :require-match t
-          :update-fn #'swiper--update-input-ivy
-          :unwind #'swiper--cleanup
-          :action #'swiper--action
-          :re-builder #'swiper--re-builder
-          :history 'swiper-history
-          :caller 'swiper)
-      (when (null ivy-exit)
+         (and
+          (setq res
+                (ivy-read
+                 "Swiper: "
+                 candidates
+                 :initial-input initial-input
+                 :keymap swiper-map
+                 :preselect preselect
+                 :require-match t
+                 :update-fn #'swiper--update-input-ivy
+                 :unwind #'swiper--cleanup
+                 :action #'swiper--action
+                 :re-builder #'swiper--re-builder
+                 :history 'swiper-history
+                 :caller 'swiper))
+          (point))
+      (unless res
         (goto-char swiper--opoint)))))
 
 (defun swiper-toggle-face-matching ()
@@ -393,7 +469,7 @@ Matched candidates should have `swiper-invocation-face'."
 
 (defun swiper--ensure-visible ()
   "Remove overlays hiding point."
-  (let ((overlays (overlays-at (point)))
+  (let ((overlays (overlays-at (1- (point))))
         ov expose)
     (while (setq ov (pop overlays))
       (if (and (invisible-p (overlay-get ov 'invisible))
@@ -416,28 +492,38 @@ Matched candidates should have `swiper-invocation-face'."
   (with-ivy-window
     (swiper--cleanup)
     (when (> (length ivy--current) 0)
-      (let* ((re (funcall ivy--regex-function ivy-text))
+      (let* ((re (replace-regexp-in-string
+                  "    " "\t"
+                  (funcall ivy--regex-function ivy-text)))
              (re (if (stringp re) re (caar re)))
              (str (get-text-property 0 'display ivy--current))
              (num (if (string-match "^[0-9]+" str)
                       (string-to-number (match-string 0 str))
                     0)))
-        (goto-char (point-min))
-        (when (cl-plusp num)
-          (goto-char (point-min))
-          (if swiper-use-visual-line
-              (line-move (1- num))
-            (forward-line (1- num)))
-          (if (and (equal ivy-text "")
-                   (>= swiper--opoint (line-beginning-position))
-                   (<= swiper--opoint (line-end-position)))
-              (goto-char swiper--opoint)
-            (re-search-forward re (line-end-position) t))
-          (isearch-range-invisible (line-beginning-position)
-                                   (line-end-position))
-          (unless (and (>= (point) (window-start))
-                       (<= (point) (window-end (ivy-state-window ivy-last) t)))
-            (recenter)))
+        (unless (eq this-command 'ivy-yank-word)
+          (when (cl-plusp num)
+            (unless (if swiper--current-line
+                        (eq swiper--current-line num)
+                      (eq (line-number-at-pos) num))
+              (goto-char (point-min))
+              (if swiper-use-visual-line
+                  (line-move (1- num))
+                (forward-line (1- num))))
+            (if (and (equal ivy-text "")
+                     (>= swiper--opoint (line-beginning-position))
+                     (<= swiper--opoint (line-end-position)))
+                (goto-char swiper--opoint)
+              (if (eq swiper--current-line num)
+                  (when swiper--current-match-start
+                    (goto-char swiper--current-match-start))
+                (setq swiper--current-line num))
+              (when (re-search-forward re (line-end-position) t)
+                (setq swiper--current-match-start (match-beginning 0))))
+            (isearch-range-invisible (line-beginning-position)
+                                     (line-end-position))
+            (unless (and (>= (point) (window-start))
+                         (<= (point) (window-end (ivy-state-window ivy-last) t)))
+              (recenter))))
         (swiper--add-overlays re)))))
 
 (defun swiper--add-overlays (re &optional beg end wnd)
@@ -493,24 +579,32 @@ 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)
-          (push-mark swiper--opoint t)
-          (message "Mark saved where search started"))))))
+  (let ((ln (1- (read (or (get-text-property 0 'display 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 (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 ()
@@ -554,6 +648,8 @@ Run `swiper' for those buffers."
                          (buffer-list)))
             :action 'swiper-multi-action-2
             :unwind #'swiper--cleanup
+            :update-fn (lambda ()
+                         (swiper-multi-action-2 ivy--current))
             :caller 'swiper-multi))
 
 (defun swiper--multi-candidates (buffers)