├── README.md └── swiper-helm.el /README.md: -------------------------------------------------------------------------------- 1 | ## Swiper 2 | 3 | Package for GNU Emacs that gives you an overview as you search for a regex 4 | 5 | ![swiper.png](http://oremacs.com/download/swiper.png) 6 | 7 | The package uses the `helm` back end for the overview. 8 | 9 | ## Screenshots 10 | 11 | ### One 12 | 13 | ![swiper-1.png](http://oremacs.com/download/swiper-1.png) 14 | 15 | ### Two 16 | 17 | ![swiper-2.png](http://oremacs.com/download/swiper-2.png) 18 | 19 | ### Three 20 | 21 | ![swiper-3.png](http://oremacs.com/download/swiper-3.png) 22 | -------------------------------------------------------------------------------- /swiper-helm.el: -------------------------------------------------------------------------------- 1 | ;;; swiper-helm.el --- Helm version of Swiper. -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2015 Oleh Krehel 4 | 5 | ;; Author: Oleh Krehel 6 | ;; URL: https://github.com/abo-abo/swiper-helm 7 | ;; Version: 0.1.0 8 | ;; Package-Requires: ((emacs "24.1") (swiper "0.1.0") (helm "1.5.3")) 9 | ;; Keywords: matching 10 | 11 | ;; This file is not part of GNU Emacs 12 | 13 | ;; This file is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 3, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; For a full copy of the GNU General Public License 24 | ;; see . 25 | 26 | ;;; Commentary: 27 | ;; 28 | ;; This package gives an overview of the current regex search 29 | ;; candidates. The search regex can be split into groups with a 30 | ;; space. Each group is highlighted with a different face. 31 | ;; 32 | ;; The overview back end is `helm'. 33 | ;; 34 | ;; It can double as a quick `regex-builder', although only single 35 | ;; lines will be matched. 36 | 37 | ;;; Code: 38 | 39 | (require 'swiper) 40 | (require 'helm) 41 | 42 | (defgroup swiper-helm nil 43 | "`isearch' with an overview using `helm'." 44 | :group 'matching 45 | :prefix "swiper-helm-") 46 | 47 | (defvar swiper-helm-keymap 48 | (let ((map (make-sparse-keymap))) 49 | (define-key map (kbd "C-s") 'helm-next-line) 50 | (define-key map (kbd "C-r") 'helm-previous-line) 51 | map) 52 | "Allows you to go to next and previous hit isearch-style.") 53 | 54 | (defun swiper-helm-default-display-buffer (buf &optional _resume) 55 | "Display BUF buffer." 56 | (when (one-window-p) 57 | (split-window-vertically)) 58 | (other-window 1) 59 | (switch-to-buffer buf)) 60 | 61 | (defcustom swiper-helm-display-function 'swiper-helm-default-display-buffer 62 | "The function that switches to the window where helm should be." 63 | :type 'function) 64 | 65 | (defvar swiper--anchor nil 66 | "A line number to which the search should be anchored.") 67 | 68 | (defvar swiper--len 0 69 | "The last length of input for which an anchoring was made.") 70 | 71 | (defun swiper--helm-init () 72 | "Perform initialization common to both completion methods." 73 | (setq swiper--opoint (point)) 74 | (setq swiper--len 0) 75 | (setq swiper--anchor (line-number-at-pos)) 76 | (when (bound-and-true-p evil-jumper-mode) 77 | (evil-jumper--set-jump))) 78 | 79 | ;;;###autoload 80 | (defun swiper-helm (&optional initial-input) 81 | "`isearch' with an overview using `helm'. 82 | When non-nil, INITIAL-INPUT is the initial search pattern." 83 | (interactive) 84 | (require 'helm) 85 | (require 'helm-multi-match) 86 | (swiper--helm-init) 87 | (setq ivy-last 88 | (make-ivy-state 89 | :window (selected-window))) 90 | (unwind-protect 91 | (let ((helm-display-function swiper-helm-display-function) 92 | helm-candidate-number-limit) 93 | (helm :sources 94 | `((name . ,(buffer-name)) 95 | (init . (lambda () 96 | (add-hook 'helm-move-selection-after-hook 97 | #'swiper--update-sel) 98 | (add-hook 'helm-update-hook 99 | #'swiper--update-input-helm) 100 | (add-hook 'helm-after-update-hook 101 | #'swiper--reanchor))) 102 | (match-strict . (lambda (x) 103 | (ignore-errors 104 | (string-match (ivy--regex helm-input) x)))) 105 | (candidates . ,(swiper--helm-candidates)) 106 | (filtered-candidate-transformer 107 | helm-fuzzy-highlight-matches) 108 | (action . swiper--action-helm)) 109 | :keymap (make-composed-keymap 110 | swiper-helm-keymap 111 | helm-map) 112 | :input initial-input 113 | :preselect 114 | (format "^%d " swiper--anchor) 115 | :buffer "*swiper*")) 116 | ;; cleanup 117 | (remove-hook 'helm-move-selection-after-hook #'swiper--update-sel) 118 | (remove-hook 'helm-update-hook #'swiper--update-input-helm) 119 | (remove-hook 'helm-after-update-hook #'swiper--reanchor) 120 | (swiper--cleanup))) 121 | 122 | (defun swiper--update-input-helm () 123 | "Update selection." 124 | (swiper--cleanup) 125 | (let ((swiper--window (ivy-state-window ivy-last))) 126 | (with-selected-window swiper--window 127 | (swiper--add-overlays 128 | (ivy--regex helm-input) 129 | (window-start swiper--window) 130 | (window-end swiper--window t))) 131 | (when (/= (length helm-input) swiper--len) 132 | (setq swiper--len (length helm-input)) 133 | (swiper--reanchor)))) 134 | 135 | (defun swiper--binary (beg end) 136 | "Find anchor between BEG and END." 137 | (if (<= (- end beg) 10) 138 | (let ((min 1000) 139 | n 140 | ln 141 | d) 142 | (goto-char (point-min)) 143 | (forward-line (1- beg)) 144 | (while (< beg end) 145 | (beginning-of-line) 146 | (setq n (read (current-buffer))) 147 | (when (< (setq d (abs (- n swiper--anchor))) min) 148 | (setq min d) 149 | (setq ln beg)) 150 | (cl-incf beg) 151 | (forward-line 1)) 152 | (goto-char (point-min)) 153 | (when ln 154 | (forward-line (1- ln)))) 155 | (let ((mid (+ beg (/ (- end beg) 2)))) 156 | (goto-char (point-min)) 157 | (forward-line mid) 158 | (beginning-of-line) 159 | (let ((n (read (current-buffer)))) 160 | (if (> n swiper--anchor) 161 | (swiper--binary beg mid) 162 | (swiper--binary mid end)))))) 163 | 164 | (defun swiper--update-sel () 165 | "Update selection." 166 | (let* ((re (ivy--regex helm-input)) 167 | (str (buffer-substring-no-properties 168 | (line-beginning-position) 169 | (line-end-position))) 170 | (num (if (string-match "^[0-9]+" str) 171 | (string-to-number (match-string 0 str)) 172 | 0)) 173 | pt) 174 | (with-ivy-window 175 | (when (> (length re) 0) 176 | (goto-char (point-min)) 177 | (forward-line (1- num)) 178 | (when (re-search-forward re (point-max) t) 179 | (setq pt (match-beginning 0))) 180 | (when pt 181 | (helm-persistent-action-display-window) 182 | (goto-char pt) 183 | (recenter) 184 | (swiper--update-input-helm))) 185 | (let ((ov (make-overlay 186 | (line-beginning-position) 187 | (1+ (line-end-position))))) 188 | (overlay-put ov 'face 'swiper-line-face) 189 | (push ov swiper--overlays))))) 190 | 191 | (defun swiper--reanchor () 192 | "Move to a valid match closest to `swiper--anchor'." 193 | (with-selected-window (helm-window) 194 | (goto-char (point-min)) 195 | (if (re-search-forward (format "^%d " swiper--anchor) nil t) 196 | nil 197 | (forward-line 1) 198 | (swiper--binary 2 (1+ (count-lines (point) (point-max))))) 199 | (when (> (count-lines (point-min) (point-max)) 1) 200 | (forward-line -1) 201 | (helm-next-line 1)))) 202 | 203 | (defun swiper--action-helm (x) 204 | "Goto line X." 205 | (if (null x) 206 | (user-error "No candidates") 207 | (goto-char (point-min)) 208 | (forward-line (1- (read x))) 209 | (re-search-forward 210 | (ivy--regex helm-input) 211 | (line-end-position) 212 | t) 213 | (swiper--ensure-visible) 214 | (when (/= (point) swiper--opoint) 215 | (unless (and transient-mark-mode 216 | mark-active) 217 | (push-mark swiper--opoint t) 218 | (message 219 | "Mark saved where search started")))) 220 | (recenter)) 221 | 222 | (defun swiper-helm-from-isearch () 223 | "Invoke `swiper-helm' from isearch." 224 | (interactive) 225 | (let ((query (if isearch-regexp 226 | isearch-string 227 | (regexp-quote isearch-string)))) 228 | (isearch-exit) 229 | (swiper-helm query))) 230 | 231 | (defun swiper--helm-candidates () 232 | "Return a list of this buffer lines." 233 | (let ((n-lines (count-lines (point-min) (point-max)))) 234 | (unless (zerop n-lines) 235 | (setq swiper--width (1+ (floor (log n-lines 10)))) 236 | (setq swiper--format-spec 237 | (format "%%-%dd %%s" swiper--width)) 238 | (let ((line-number 0) 239 | candidates) 240 | (save-excursion 241 | (goto-char (point-min)) 242 | (swiper-font-lock-ensure) 243 | (while (< (point) (point-max)) 244 | (push (format swiper--format-spec 245 | (cl-incf line-number) 246 | (buffer-substring 247 | (line-beginning-position) 248 | (line-end-position))) 249 | candidates) 250 | (forward-line 1)) 251 | (nreverse candidates)))))) 252 | 253 | (provide 'swiper-helm) 254 | 255 | ;;; swiper-helm.el ends here 256 | --------------------------------------------------------------------------------