1 ;;; loccur.el --- Perform an occur-like folding in current buffer
3 ;; Copyright (C) 2009-2015 Alexey Veretennikov
5 ;; Author: Alexey Veretennikov <alexey dot veretennikov at gmail dot com>
9 ;; URL: https://github.com/fourier/loccur
10 ;; Compatibility: GNU Emacs 23.x, GNU Emacs 24.x
12 ;; This file is NOT part of GNU Emacs.
14 ;; This program is free software; you can redistribute it and/or
15 ;; modify it under the terms of the GNU General Public License
16 ;; as published by the Free Software Foundation; either version 2
17 ;; of the License, or (at your option) any later version.
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
29 ;; Add the following to your .emacs file:
32 ;; ;; defines shortcut for loccur of the current word
33 ;; (define-key global-map [(control o)] 'loccur-current)
34 ;; ;; defines shortcut for the interactive loccur command
35 ;; (define-key global-map [(control meta o)] 'loccur)
36 ;; ;; defines shortcut for the loccur of the previously found word
37 ;; (define-key global-map [(control shift o)] 'loccur-previous-match)
40 ;; Using with smooth-scrolling.el sometimes
41 ;; gives unexpected jumps in loccur mode
48 ;; + Preparation for GNU ELPA submission. Removed contributions
49 ;; without signed papers
50 ;; + added loccur-face - face to highlight text, by default isearch
53 ;; + Added custom option loccur-jump-beginning-of-line; removed some
57 ;; + Default value is taken from prompt instead of an edit area
58 ;; (thanks to Nathaniel Flath)
61 ;; + Added highlighting of the matched strings
62 ;; + Now inserts selected region to the prompt
63 ;; + Added defun for applying last found regexp(loccur-previous-match)
64 ;; + Added intangible property together with invisibility
74 "Perform an occur-like folding in current buffer."
77 ;; should be defined before define-minor-mode
78 (defvar loccur-mode-map
79 (let ((map (make-sparse-keymap)))
80 (define-key map (kbd "RET") '(lambda () (interactive) (loccur nil)))
81 ;; redefine Ctrl+Up/Down to Up/Down, since it looks like some problem
82 ;; with backward-paragraph and forward-paragraph with invisible overlays
83 (define-key map (kbd "<C-up>") 'previous-line)
84 (define-key map (kbd "<C-down>") 'next-line)
86 "Keymap for the variable `loccur-mode'.")
89 (define-minor-mode loccur-mode
90 "Minor mode for navigating through the file.
91 Hides all lines without matches like `occur' does, but without opening
94 :keymap loccur-mode-map
96 (loccur-1 loccur-current-search)
97 (loccur-remove-overlays)
101 '((t (:inherit isearch)))
106 (defconst loccur-overlay-invisible-property-name 'loccur-invisible-overlay
107 "Property name of the overlay for all invisible text.")
109 (defconst loccur-overlay-visible-property-name 'loccur-visible-overlay
110 "Property name of the overlay for all visible text.")
112 (defcustom loccur-jump-beginning-of-line nil
113 "Set cursor to the beginning of the line when the loccur function is called.
118 (defcustom loccur-highlight-matching-regexp t
119 "If set to nil, do not highlight matching words.
124 (defvar loccur-history nil
125 "History of previously searched expressions for the prompt.")
127 (defvar-local loccur-last-match nil
130 (defvar-local loccur-overlay-list nil
131 "A list of currently active overlays.")
133 (defvar-local loccur-current-search nil
134 "The expression to search in the current active mode.")
136 (defun loccur-current ()
137 "Call `loccur' for the current word."
139 (loccur (current-word)))
142 (defun loccur-previous-match ()
143 "Call `loccur' for the previously found word."
145 (loccur loccur-last-match))
147 (defun loccur-no-highlight (regex)
148 "Perform search like loccur, but temporary removing match highlight.
149 REGEX is regexp to search"
153 (list (read-string "Loccur: " (loccur-prompt) 'loccur-history))))
154 (let ((loccur-highlight-matching-regexp nil))
157 (defun loccur-toggle-highlight (&optional arg)
158 "Toggle the highlighting of the match.
159 Optional argument ARG if t turn highlight on, off otherwise."
161 (setq loccur-highlight-matching-regexp (not loccur-highlight-matching-regexp))
163 (dolist (ovl loccur-overlay-list)
164 (when (overlay-get ovl loccur-overlay-visible-property-name)
165 (overlay-put ovl 'face (if loccur-highlight-matching-regexp 'loccur-face nil))))))
167 (defun loccur (regex)
168 "Perform a simple grep in current buffer.
170 This command hides all lines from the current buffer except those
171 containing the regular expression REGEX. A second call of the function
176 (list (read-string "Loccur: " (loccur-prompt) 'loccur-history))))
178 (= (length regex) 0))
180 ;; remove current search and turn off loccur mode
181 ;; to allow to call `loccur' multiple times
182 (setf loccur-current-search nil)
184 ;; otherwise do as usual
185 ;; if the regex argument is not equal to previous search
186 (when (not (string-equal regex loccur-current-search))
187 (cl-pushnew regex loccur-history)
188 (setf loccur-current-search regex)
190 (when loccur-jump-beginning-of-line
191 (beginning-of-line))))) ; optionally jump to the beginning of line
194 (defun loccur-prompt ()
195 "Return the default value of the prompt.
197 Default value for prompt is a current word or active region(selection),
198 if its size is 1 line"
200 (if (and transient-mark-mode
202 (let ((pos1 (region-beginning))
204 ;; Check if the start and the of an active region is on
206 (if (= (line-number-at-pos pos1)
207 (line-number-at-pos pos2))
208 (buffer-substring-no-properties pos1 pos2)))
213 (defun loccur-1 (regex)
214 "Implementation of the `loccur' functionality.
216 REGEX is an argument to `loccur'."
217 (let* ((buffer-matches (loccur-find-matches regex))
218 (ovl-bounds (loccur-create-overlay-bounds-btw-lines buffer-matches)))
219 (setq loccur-overlay-list
220 (loccur-create-invisible-overlays ovl-bounds))
222 (setq loccur-overlay-list
223 (append loccur-overlay-list
224 (loccur-create-highlighted-overlays buffer-matches)))
225 (setq loccur-last-match regex)
228 (defun loccur-create-highlighted-overlays (buffer-matches)
229 "Create the list of overlays for BUFFER-MATCHES."
231 (mapcar (lambda (match)
235 (current-buffer) t nil))
238 (overlay-put ovl loccur-overlay-visible-property-name t)
239 (when loccur-highlight-matching-regexp
240 (overlay-put ovl 'face 'loccur-face)))
244 (defun loccur-create-invisible-overlays (ovl-bounds)
245 "Create a list of invisible overlays by given OVL-BOUNDS."
247 (mapcar (lambda (bnd)
251 (current-buffer) t nil))
254 (overlay-put ovl loccur-overlay-invisible-property-name t)
255 (overlay-put ovl 'invisible t)
256 ;; force intangible property if invisible property
257 ;; does not automatically set it
258 (overlay-put ovl 'intangible t))
262 (defun loccur-remove-overlays ()
263 "Remove all overlays."
264 (remove-overlays (point-min) (point-max) loccur-overlay-visible-property-name t)
265 (remove-overlays (point-min) (point-max) loccur-overlay-invisible-property-name t)
266 (setq loccur-overlay-list nil))
269 (defun loccur-create-overlay-bounds-btw-lines (buffer-matches)
270 "Create a list of overlays between matched lines BUFFER-MATCHES."
271 (let ((prev-end (point-min))
274 (mapcar (lambda (line)
275 (let ((beginning (car line)))
276 (unless ( = (- beginning prev-end) 1)
277 (let ((ovl-end (1- beginning)))
278 (push (list prev-end ovl-end) overlays)))
279 (setq prev-end (nth 3 line))))
281 (push (list (1+ prev-end) (point-max)) overlays)
282 (setq overlays (nreverse overlays)))))
285 (defun loccur-find-matches (regex)
286 "Find all occurences in the current buffer for given REGEX.
288 Returns a list of 4-number tuples, specifying begnning of the line,
289 1st match begin of a line, 1st match end of a line, end of a line
292 ;; Go to the beginnig of buffer
293 (goto-char (point-min))
294 ;; Set initial values for variables
301 (setq curpoint (point))
302 ;; if something found
303 (when (setq endpoint (re-search-forward regex nil t))
305 (let ((found-begin (match-beginning 0))
306 (found-end (match-end 0)))
307 ;; Get the start and the and of the matching line
308 ;; and store it to the overlays array
309 (goto-char found-begin)
310 (setq endpoint (line-end-position))
311 (push (list (line-beginning-position)
315 ;; maybe add some code to highlight matches like in occur-mode?
316 ;; goto the end of line for any case
317 (goto-char endpoint))
319 (setq lines (nreverse lines)))))
326 ;;; loccur.el ends here