]> code.delx.au - gnu-emacs-elpa/blob - packages/vlf/vlf-search.el
* packages/vlf: Break into components.
[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 (defun vlf-re-search (regexp count backward batch-step)
31 "Search for REGEXP COUNT number of times forward or BACKWARD.
32 BATCH-STEP is amount of overlap between successive chunks."
33 (if (<= count 0)
34 (error "Count must be positive"))
35 (let* ((case-fold-search t)
36 (match-chunk-start vlf-start-pos)
37 (match-chunk-end vlf-end-pos)
38 (match-start-pos (+ vlf-start-pos (position-bytes (point))))
39 (match-end-pos match-start-pos)
40 (to-find count)
41 (reporter (make-progress-reporter
42 (concat "Searching for " regexp "...")
43 (if backward
44 (- vlf-file-size vlf-end-pos)
45 vlf-start-pos)
46 vlf-file-size)))
47 (vlf-with-undo-disabled
48 (unwind-protect
49 (catch 'end-of-file
50 (if backward
51 (while (not (zerop to-find))
52 (cond ((re-search-backward regexp nil t)
53 (setq to-find (1- to-find)
54 match-chunk-start vlf-start-pos
55 match-chunk-end vlf-end-pos
56 match-start-pos (+ vlf-start-pos
57 (position-bytes
58 (match-beginning 0)))
59 match-end-pos (+ vlf-start-pos
60 (position-bytes
61 (match-end 0)))))
62 ((zerop vlf-start-pos)
63 (throw 'end-of-file nil))
64 (t (let ((batch-move (- vlf-start-pos
65 (- vlf-batch-size
66 batch-step))))
67 (vlf-move-to-batch
68 (if (< match-start-pos batch-move)
69 (- match-start-pos vlf-batch-size)
70 batch-move) t))
71 (goto-char (if (< match-start-pos
72 vlf-end-pos)
73 (or (byte-to-position
74 (- match-start-pos
75 vlf-start-pos))
76 (point-max))
77 (point-max)))
78 (progress-reporter-update
79 reporter (- vlf-file-size
80 vlf-start-pos)))))
81 (while (not (zerop to-find))
82 (cond ((re-search-forward regexp nil t)
83 (setq to-find (1- to-find)
84 match-chunk-start vlf-start-pos
85 match-chunk-end vlf-end-pos
86 match-start-pos (+ vlf-start-pos
87 (position-bytes
88 (match-beginning 0)))
89 match-end-pos (+ vlf-start-pos
90 (position-bytes
91 (match-end 0)))))
92 ((= vlf-end-pos vlf-file-size)
93 (throw 'end-of-file nil))
94 (t (let ((batch-move (- vlf-end-pos batch-step)))
95 (vlf-move-to-batch
96 (if (< batch-move match-end-pos)
97 match-end-pos
98 batch-move) t))
99 (goto-char (if (< vlf-start-pos match-end-pos)
100 (or (byte-to-position
101 (- match-end-pos
102 vlf-start-pos))
103 (point-min))
104 (point-min)))
105 (progress-reporter-update reporter
106 vlf-end-pos)))))
107 (progress-reporter-done reporter))
108 (set-buffer-modified-p nil)
109 (if backward
110 (vlf-goto-match match-chunk-start match-chunk-end
111 match-end-pos match-start-pos
112 count to-find)
113 (vlf-goto-match match-chunk-start match-chunk-end
114 match-start-pos match-end-pos
115 count to-find))))))
116
117 (defun vlf-goto-match (match-chunk-start match-chunk-end
118 match-pos-start
119 match-pos-end
120 count to-find)
121 "Move to MATCH-CHUNK-START MATCH-CHUNK-END surrounding \
122 MATCH-POS-START and MATCH-POS-END.
123 According to COUNT and left TO-FIND, show if search has been
124 successful. Return nil if nothing found."
125 (if (= count to-find)
126 (progn (vlf-move-to-chunk match-chunk-start match-chunk-end)
127 (goto-char (or (byte-to-position (- match-pos-start
128 vlf-start-pos))
129 (point-max)))
130 (message "Not found")
131 nil)
132 (let ((success (zerop to-find)))
133 (if success
134 (vlf-update-buffer-name)
135 (vlf-move-to-chunk match-chunk-start match-chunk-end))
136 (let* ((match-end (or (byte-to-position (- match-pos-end
137 vlf-start-pos))
138 (point-max)))
139 (overlay (make-overlay (byte-to-position
140 (- match-pos-start
141 vlf-start-pos))
142 match-end)))
143 (overlay-put overlay 'face 'match)
144 (unless success
145 (goto-char match-end)
146 (message "Moved to the %d match which is last"
147 (- count to-find)))
148 (unwind-protect (sit-for 3)
149 (delete-overlay overlay))
150 t))))
151
152 (defun vlf-re-search-forward (regexp count)
153 "Search forward for REGEXP prefix COUNT number of times.
154 Search is performed chunk by chunk in `vlf-batch-size' memory."
155 (interactive (if (vlf-no-modifications)
156 (list (read-regexp "Search whole file"
157 (if regexp-history
158 (car regexp-history)))
159 (or current-prefix-arg 1))))
160 (vlf-re-search regexp count nil (/ vlf-batch-size 8)))
161
162 (defun vlf-re-search-backward (regexp count)
163 "Search backward for REGEXP prefix COUNT number of times.
164 Search is performed chunk by chunk in `vlf-batch-size' memory."
165 (interactive (if (vlf-no-modifications)
166 (list (read-regexp "Search whole file backward"
167 (if regexp-history
168 (car regexp-history)))
169 (or current-prefix-arg 1))))
170 (vlf-re-search regexp count t (/ vlf-batch-size 8)))
171
172 (defun vlf-goto-line (n)
173 "Go to line N. If N is negative, count from the end of file."
174 (interactive (if (vlf-no-modifications)
175 (list (read-number "Go to line: "))))
176 (let ((start-pos vlf-start-pos)
177 (end-pos vlf-end-pos)
178 (pos (point))
179 (success nil))
180 (unwind-protect
181 (if (< 0 n)
182 (progn (vlf-beginning-of-file)
183 (goto-char (point-min))
184 (setq success (vlf-re-search "[\n\C-m]" (1- n)
185 nil 0)))
186 (vlf-end-of-file)
187 (goto-char (point-max))
188 (setq success (vlf-re-search "[\n\C-m]" (- n) t 0)))
189 (if success
190 (message "Onto line %s" n)
191 (vlf-move-to-chunk start-pos end-pos)
192 (goto-char pos)))))
193
194 (provide 'vlf-search)
195
196 ;;; vlf-search.el ends here