]> code.delx.au - gnu-emacs-elpa/blob - packages/vlf/vlf-search.el
Merge commit '4709fc4530da4ddfd29b910763c801292b228f69' from diff-hl
[gnu-emacs-elpa] / packages / vlf / vlf-search.el
1 ;;; vlf-search.el --- Search functionality for VLF -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2014 Free Software Foundation, Inc.
4
5 ;; Keywords: large files, search
6 ;; Author: Andrey Kotlarski <m00naticus@gmail.com>
7 ;; URL: https://github.com/m00natic/vlfi
8
9 ;; This file is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
13
14 ;; This file is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to
21 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25 ;; This package provides search utilities for dealing with large files
26 ;; in constant memory.
27
28 ;;; Code:
29
30 (require 'vlf)
31
32 (defun vlf-re-search (regexp count backward batch-step)
33 "Search for REGEXP COUNT number of times forward or BACKWARD.
34 BATCH-STEP is amount of overlap between successive chunks."
35 (if (<= count 0)
36 (error "Count must be positive"))
37 (run-hook-with-args 'vlf-before-batch-functions 'search)
38 (let* ((tramp-verbose (if (boundp 'tramp-verbose)
39 (min tramp-verbose 2)))
40 (case-fold-search t)
41 (match-chunk-start vlf-start-pos)
42 (match-chunk-end vlf-end-pos)
43 (match-start-pos (+ vlf-start-pos (position-bytes (point))))
44 (match-end-pos match-start-pos)
45 (to-find count)
46 (font-lock font-lock-mode)
47 (reporter (make-progress-reporter
48 (concat "Searching for " regexp "...")
49 (if backward
50 (- vlf-file-size vlf-end-pos)
51 vlf-start-pos)
52 vlf-file-size)))
53 (font-lock-mode 0)
54 (vlf-with-undo-disabled
55 (unwind-protect
56 (catch 'end-of-file
57 (if backward
58 (while (not (zerop to-find))
59 (cond ((re-search-backward regexp nil t)
60 (setq to-find (1- to-find)
61 match-chunk-start vlf-start-pos
62 match-chunk-end vlf-end-pos
63 match-start-pos (+ vlf-start-pos
64 (position-bytes
65 (match-beginning 0)))
66 match-end-pos (+ vlf-start-pos
67 (position-bytes
68 (match-end 0)))))
69 ((zerop vlf-start-pos)
70 (throw 'end-of-file nil))
71 (t (let ((batch-move (- vlf-start-pos
72 (- vlf-batch-size
73 batch-step))))
74 (vlf-move-to-batch
75 (if (< match-start-pos batch-move)
76 (- match-start-pos vlf-batch-size)
77 batch-move) t))
78 (goto-char (if (< match-start-pos
79 vlf-end-pos)
80 (or (byte-to-position
81 (- match-start-pos
82 vlf-start-pos))
83 (point-max))
84 (point-max)))
85 (progress-reporter-update
86 reporter (- vlf-file-size
87 vlf-start-pos)))))
88 (while (not (zerop to-find))
89 (cond ((re-search-forward regexp nil t)
90 (setq to-find (1- to-find)
91 match-chunk-start vlf-start-pos
92 match-chunk-end vlf-end-pos
93 match-start-pos (+ vlf-start-pos
94 (position-bytes
95 (match-beginning 0)))
96 match-end-pos (+ vlf-start-pos
97 (position-bytes
98 (match-end 0)))))
99 ((= vlf-end-pos vlf-file-size)
100 (throw 'end-of-file nil))
101 (t (let ((batch-move (- vlf-end-pos batch-step)))
102 (vlf-move-to-batch
103 (if (< batch-move match-end-pos)
104 match-end-pos
105 batch-move) t))
106 (goto-char (if (< vlf-start-pos match-end-pos)
107 (or (byte-to-position
108 (- match-end-pos
109 vlf-start-pos))
110 (point-min))
111 (point-min)))
112 (progress-reporter-update reporter
113 vlf-end-pos)))))
114 (progress-reporter-done reporter))
115 (set-buffer-modified-p nil)
116 (if font-lock (font-lock-mode 1))
117 (if backward
118 (vlf-goto-match match-chunk-start match-chunk-end
119 match-end-pos match-start-pos
120 count to-find)
121 (vlf-goto-match match-chunk-start match-chunk-end
122 match-start-pos match-end-pos
123 count to-find))
124 (run-hook-with-args 'vlf-after-batch-functions 'search)))))
125
126 (defun vlf-goto-match (match-chunk-start match-chunk-end
127 match-pos-start
128 match-pos-end
129 count to-find)
130 "Move to MATCH-CHUNK-START MATCH-CHUNK-END surrounding\
131 MATCH-POS-START and MATCH-POS-END.
132 According to COUNT and left TO-FIND, show if search has been
133 successful. Return nil if nothing found."
134 (if (= count to-find)
135 (progn (vlf-move-to-chunk match-chunk-start match-chunk-end)
136 (goto-char (or (byte-to-position (- match-pos-start
137 vlf-start-pos))
138 (point-max)))
139 (message "Not found")
140 nil)
141 (let ((success (zerop to-find)))
142 (if success
143 (vlf-update-buffer-name)
144 (vlf-move-to-chunk match-chunk-start match-chunk-end))
145 (let* ((match-end (or (byte-to-position (- match-pos-end
146 vlf-start-pos))
147 (point-max)))
148 (overlay (make-overlay (byte-to-position
149 (- match-pos-start
150 vlf-start-pos))
151 match-end)))
152 (overlay-put overlay 'face 'match)
153 (unless success
154 (goto-char match-end)
155 (message "Moved to the %d match which is last"
156 (- count to-find)))
157 (unwind-protect (sit-for 3)
158 (delete-overlay overlay))
159 t))))
160
161 (defun vlf-re-search-forward (regexp count)
162 "Search forward for REGEXP prefix COUNT number of times.
163 Search is performed chunk by chunk in `vlf-batch-size' memory."
164 (interactive (if (vlf-no-modifications)
165 (list (read-regexp "Search whole file"
166 (if regexp-history
167 (car regexp-history)))
168 (or current-prefix-arg 1))))
169 (vlf-re-search regexp count nil (/ vlf-batch-size 8)))
170
171 (defun vlf-re-search-backward (regexp count)
172 "Search backward for REGEXP prefix COUNT number of times.
173 Search is performed chunk by chunk in `vlf-batch-size' memory."
174 (interactive (if (vlf-no-modifications)
175 (list (read-regexp "Search whole file backward"
176 (if regexp-history
177 (car regexp-history)))
178 (or current-prefix-arg 1))))
179 (vlf-re-search regexp count t (/ vlf-batch-size 8)))
180
181 (defun vlf-goto-line (n)
182 "Go to line N. If N is negative, count from the end of file."
183 (interactive (if (vlf-no-modifications)
184 (list (read-number "Go to line: "))))
185 (run-hook-with-args 'vlf-before-batch-functions 'goto-line)
186 (vlf-verify-size)
187 (let ((tramp-verbose (if (boundp 'tramp-verbose)
188 (min tramp-verbose 2)))
189 (start-pos vlf-start-pos)
190 (end-pos vlf-end-pos)
191 (pos (point))
192 (font-lock font-lock-mode)
193 (success nil))
194 (font-lock-mode 0)
195 (unwind-protect
196 (if (< 0 n)
197 (let ((start 0)
198 (end (min vlf-batch-size vlf-file-size))
199 (reporter (make-progress-reporter
200 (concat "Searching for line "
201 (number-to-string n) "...")
202 0 vlf-file-size))
203 (inhibit-read-only t))
204 (setq n (1- n))
205 (vlf-with-undo-disabled
206 (while (and (< (- end start) n)
207 (< n (- vlf-file-size start)))
208 (erase-buffer)
209 (insert-file-contents-literally buffer-file-name
210 nil start end)
211 (goto-char (point-min))
212 (while (re-search-forward "[\n\C-m]" nil t)
213 (setq n (1- n)))
214 (vlf-verify-size)
215 (setq start end
216 end (min vlf-file-size
217 (+ start vlf-batch-size)))
218 (progress-reporter-update reporter start))
219 (when (< n (- vlf-file-size end))
220 (vlf-move-to-chunk-2 start end)
221 (goto-char (point-min))
222 (setq success (vlf-re-search "[\n\C-m]" n nil 0)))))
223 (let ((start (max 0 (- vlf-file-size vlf-batch-size)))
224 (end vlf-file-size)
225 (reporter (make-progress-reporter
226 (concat "Searching for line -"
227 (number-to-string n) "...")
228 0 vlf-file-size))
229 (inhibit-read-only t))
230 (setq n (- n))
231 (vlf-with-undo-disabled
232 (while (and (< (- end start) n) (< n end))
233 (erase-buffer)
234 (insert-file-contents-literally buffer-file-name nil
235 start end)
236 (goto-char (point-max))
237 (while (re-search-backward "[\n\C-m]" nil t)
238 (setq n (1- n)))
239 (setq end start
240 start (max 0 (- end vlf-batch-size)))
241 (progress-reporter-update reporter
242 (- vlf-file-size end)))
243 (when (< n end)
244 (vlf-move-to-chunk-2 start end)
245 (goto-char (point-max))
246 (setq success (vlf-re-search "[\n\C-m]" n t 0))))))
247 (if font-lock (font-lock-mode 1))
248 (unless success
249 (vlf-with-undo-disabled
250 (vlf-move-to-chunk-2 start-pos end-pos))
251 (vlf-update-buffer-name)
252 (goto-char pos)
253 (message "Unable to find line"))
254 (run-hook-with-args 'vlf-after-batch-functions 'goto-line))))
255
256 (provide 'vlf-search)
257
258 ;;; vlf-search.el ends here