]> code.delx.au - gnu-emacs-elpa/blob - packages/all/all.el
Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
[gnu-emacs-elpa] / packages / all / all.el
1 ;;; all.el --- Edit all lines matching a given regexp
2
3 ;; Copyright (C) 1985-1987,1992,1994,2011-2012 Free Software Foundation, Inc.
4
5 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6 ;; Version: 1.0
7 ;; Keywords: matching
8
9 ;; LCD Archive Entry:
10 ;; all|Per Abrahamsen|abraham@dina.kvl.dk|
11 ;; Edit all lines matching a given regexp|
12 ;; $Date: 1997/03/04 10:29:42 $|$Revision: 5.2 $|~/misc/all.Z|
13
14 ;; This program is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 3, or (at your option)
17 ;; any later version.
18 ;;
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.
23 ;;
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/>.
26
27 ;;; Commentary:
28
29 ;; Just like occur, except that changes in the *All* buffer are
30 ;; propagated to the original buffer.
31
32 ;; You can no longer use mouse-2 to find a match in the original file,
33 ;; since the default definition of mouse is too useful.
34 ;; However, `C-c C-c' still works.
35
36 ;; Line numbers are not listed in the *All* buffer.
37
38 ;; Ok, it is _not_ just like occur.
39
40 ;; Some limitations:
41
42 ;; - Undo in the *All* buffer is an ordinary change in the original.
43 ;; - Changes to the original buffer are not reflected in the *All* buffer.
44 ;; - A single change in the *All* buffer must be limited to a single match.
45
46 ;;; Code:
47
48 (defvar all-mode-map
49 (let ((map (make-sparse-keymap)))
50 (define-key map "\C-c\C-c" 'all-mode-goto)
51 map))
52
53 (defvar all-buffer nil)
54 (make-variable-buffer-local 'all-buffer)
55
56 (define-derived-mode all-mode fundamental-mode "All"
57 "Major mode for output from \\[all].
58
59 All changes made in this buffer will be propagated to the buffer where
60 you ran \\[all].
61
62 Press \\[all-mode-goto] to go to the same spot in the original buffer."
63 (add-hook 'before-change-functions 'all-before-change-function nil 'local)
64 (add-hook 'after-change-functions 'all-after-change-function nil 'local))
65
66 (defun all-mode-find (pos)
67 ;; Find position in original buffer corresponding to POS.
68 (let ((overlay (all-mode-find-overlay pos)))
69 (if overlay
70 (+ (marker-position (overlay-get overlay 'all-marker))
71 (- pos (overlay-start overlay))))))
72
73 (defun all-mode-find-overlay (pos)
74 ;; Find the overlay containing POS.
75 (let ((overlays (overlays-at pos)))
76 (while (and overlays (null (overlay-get (car overlays) 'all-marker)))
77 (setq overlays (cdr overlays)))
78 (car-safe overlays)))
79
80 (defun all-mode-goto ()
81 "Move point to the corresponding position in the original buffer."
82 (interactive)
83 (let ((pos (all-mode-find (point))))
84 (if pos
85 (pop-to-buffer all-buffer)
86 (error "This text is not from the original buffer"))
87 (goto-char pos)))
88
89 (defvar all-initialization-p nil)
90
91 (defun all-before-change-function (from to)
92 ;; Check that change is legal.
93 (and all-buffer
94 (not all-initialization-p)
95 (let ((start (all-mode-find-overlay from))
96 (end (all-mode-find-overlay to)))
97 (not (and start (eq start end))))
98 (error "Changes should be limited to a single text piece")))
99
100 (defun all-after-change-function (from to length)
101 ;; Propagate changes from *All* buffer.
102 (and all-buffer
103 (null all-initialization-p)
104 (let ((buffer (current-buffer))
105 (pos (all-mode-find from)))
106 (if pos
107 (with-current-buffer all-buffer
108 (save-excursion
109 (goto-char pos)
110 (delete-region pos (+ pos length))
111 (insert-buffer-substring buffer from to)))))))
112
113 ;;;###autoload
114 (defun all (regexp &optional nlines)
115 "Show all lines in the current buffer containing a match for REGEXP.
116
117 If a match spreads across multiple lines, all those lines are shown.
118
119 Each line is displayed with NLINES lines before and after, or -NLINES
120 before if NLINES is negative.
121 NLINES defaults to `list-matching-lines-default-context-lines'.
122 Interactively it is the prefix arg.
123
124 The lines are shown in a buffer named `*All*'.
125 Any changes made in that buffer will be propagated to this buffer."
126 (interactive
127 (list (let* ((default (car regexp-history)))
128 (read-string
129 (if default
130 (format
131 "Edit lines matching regexp (default `%s'): " default)
132 "Edit lines matching regexp: ")
133 nil 'regexp-history default))
134 current-prefix-arg))
135 (setq nlines (if nlines (prefix-numeric-value nlines)
136 list-matching-lines-default-context-lines))
137 (let ((all-initialization-p t)
138 (buffer (current-buffer))
139 (prevend nil)
140 (prevstart nil)
141 (prevpos (point-min)))
142 (with-output-to-temp-buffer "*All*"
143 (with-current-buffer standard-output
144 (all-mode)
145 (setq all-buffer buffer)
146 (insert "Lines matching ")
147 (prin1 regexp)
148 (insert " in buffer " (buffer-name buffer) ?. ?\n)
149 (insert "--------\n"))
150 (if (eq buffer standard-output)
151 (goto-char (point-max)))
152 (save-excursion
153 (goto-char (point-min))
154 ;; Find next match, but give up if prev match was at end of buffer.
155 (while (and (not (= prevpos (point-max)))
156 (re-search-forward regexp nil t))
157 (goto-char (match-beginning 0))
158 (beginning-of-line)
159 (setq prevpos (point))
160 (goto-char (match-end 0))
161 (let* ((start (save-excursion
162 (goto-char (match-beginning 0))
163 (forward-line (if (< nlines 0) nlines (- nlines)))
164 (point)))
165 (end (save-excursion
166 (goto-char (match-end 0))
167 (if (> nlines 0)
168 (forward-line (1+ nlines))
169 (forward-line 1))
170 (point))))
171 (cond ((null prevend)
172 (setq prevstart start
173 prevend end))
174 ((> start prevend)
175 (all-insert prevstart prevend regexp nlines)
176 (setq prevstart start
177 prevend end))
178 (t
179 (setq prevend end)))))
180 (if prevend
181 (all-insert prevstart prevend regexp nlines))))))
182
183 (defun all-insert (start end regexp nlines)
184 ;; Insert match.
185 (let ((marker (copy-marker start))
186 (buffer (current-buffer)))
187 (with-current-buffer standard-output
188 (let ((from (point))
189 to)
190 (insert-buffer-substring buffer start end)
191 (setq to (point))
192 (overlay-put (make-overlay from to) 'all-marker marker)
193 (goto-char from)
194 (while (re-search-forward regexp to t)
195 (put-text-property (match-beginning 0) (match-end 0)
196 'face 'match))
197 (goto-char to)
198 (if (> nlines 0)
199 (insert "--------\n"))))))
200
201 (provide 'all)
202
203 ;;; all.el ends here