-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathvlf.el
364 lines (324 loc) · 13.7 KB
/
vlf.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
;;; vlf.el --- View Large Files -*- lexical-binding: t -*-
;; Copyright (C) 2006, 2012-2015 Free Software Foundation, Inc.
;; Version: 1.7
;; Keywords: large files, utilities
;; Maintainer: Andrey Kotlarski <[email protected]>
;; Authors: 2006 Mathias Dahl <[email protected]>
;; 2012 Sam Steingold <[email protected]>
;; 2013-2015 Andrey Kotlarski <[email protected]>
;; URL: https://github.com/m00natic/vlfi
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This package provides the M-x vlf command, which visits part of
;; large file without loading it entirely. The buffer uses VLF mode,
;; which provides several commands for moving around, searching,
;; comparing and editing selected part of file.
;; To have it offered when opening large files:
;; (require 'vlf-setup)
;; This package was inspired by a snippet posted by Kevin Rodgers,
;; showing how to use `insert-file-contents' to extract part of a
;; file.
;;; Code:
(require 'vlf-base)
(defcustom vlf-before-batch-functions nil
"Hook that runs before multiple batch operations.
One argument is supplied that specifies current action. Possible
values are: `write', `ediff', `occur', `search', `goto-line'."
:group 'vlf :type 'hook)
(defcustom vlf-after-batch-functions nil
"Hook that runs after multiple batch operations.
One argument is supplied that specifies current action. Possible
values are: `write', `ediff', `occur', `search', `goto-line'."
:group 'vlf :type 'hook)
(defcustom vlf-batch-size-remote 1024
"Defines size (in bytes) of a batch of file data when accessed remotely."
:group 'vlf :type 'integer)
(defvar hexl-bits)
(autoload 'vlf-write "vlf-write" "Write current chunk to file." t)
(autoload 'vlf-re-search-forward "vlf-search"
"Search forward for REGEXP prefix COUNT number of times." t)
(autoload 'vlf-re-search-backward "vlf-search"
"Search backward for REGEXP prefix COUNT number of times." t)
(autoload 'vlf-goto-line "vlf-search" "Go to line." t)
(autoload 'vlf-query-replace "vlf-search"
"Query replace regexp over whole file." t)
(autoload 'vlf-occur "vlf-occur"
"Make whole file occur style index for REGEXP." t)
(autoload 'vlf-toggle-follow "vlf-follow"
"Toggle continuous chunk recenter around current point." t)
(autoload 'vlf-stop-follow "vlf-follow" "Stop continuous recenter." t)
(autoload 'vlf-ediff-buffers "vlf-ediff"
"Run batch by batch ediff over VLF buffers." t)
(defvar vlf-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "n" 'vlf-next-batch)
(define-key map "p" 'vlf-prev-batch)
(define-key map " " 'vlf-next-batch-from-point)
(define-key map "+" 'vlf-change-batch-size)
(define-key map "-"
(lambda () "Decrease vlf batch size by factor of 2."
(interactive)
(vlf-change-batch-size t)))
(define-key map "s" 'vlf-re-search-forward)
(define-key map "r" 'vlf-re-search-backward)
(define-key map "%" 'vlf-query-replace)
(define-key map "o" 'vlf-occur)
(define-key map "[" 'vlf-beginning-of-file)
(define-key map "]" 'vlf-end-of-file)
(define-key map "j" 'vlf-jump-to-chunk)
(define-key map "l" 'vlf-goto-line)
(define-key map "e" 'vlf-ediff-buffers)
(define-key map "f" 'vlf-toggle-follow)
(define-key map "g" 'vlf-revert)
map)
"Keymap for `vlf-mode'.")
(defvar vlf-prefix-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-v" vlf-mode-map)
map)
"Prefixed keymap for `vlf-mode'.")
(define-minor-mode vlf-mode
"Mode to browse large files in."
:group 'vlf :keymap vlf-prefix-map
:lighter (:eval (format " VLF[%d/%d](%s)"
(ceiling vlf-end-pos vlf-batch-size)
(ceiling vlf-file-size vlf-batch-size)
(file-size-human-readable vlf-file-size)))
(cond (vlf-mode
(set (make-local-variable 'require-final-newline) nil)
(add-hook 'write-file-functions 'vlf-write nil t)
(set (make-local-variable 'revert-buffer-function)
'vlf-revert)
(make-local-variable 'vlf-batch-size)
(setq vlf-file-size (vlf-get-file-size buffer-file-truename)
vlf-start-pos 0
vlf-end-pos 0)
(let* ((pos (position-bytes (point)))
(start (* (/ pos vlf-batch-size) vlf-batch-size)))
(goto-char (byte-to-position (- pos start)))
(vlf-move-to-batch start))
(add-hook 'after-change-major-mode-hook 'vlf-keep-alive t t)
(vlf-keep-alive))
((or (not large-file-warning-threshold)
(< vlf-file-size large-file-warning-threshold)
(y-or-n-p (format "Load whole file (%s)? "
(file-size-human-readable
vlf-file-size))))
(kill-local-variable 'revert-buffer-function)
(vlf-stop-follow)
(kill-local-variable 'require-final-newline)
(remove-hook 'write-file-functions 'vlf-write t)
(remove-hook 'after-change-major-mode-hook
'vlf-keep-alive t)
(if (derived-mode-p 'hexl-mode)
(let ((line (/ (1+ vlf-start-pos) hexl-bits))
(pos (point)))
(if (consp buffer-undo-list)
(setq buffer-undo-list nil))
(vlf-with-undo-disabled
(let ((inhibit-read-only t))
(insert-file-contents-literally buffer-file-name
t nil nil t)
(hexlify-buffer)))
(set-buffer-modified-p nil)
(goto-char (point-min))
(forward-line line)
(forward-char pos))
(let ((pos (+ vlf-start-pos (position-bytes (point))))
(inhibit-read-only t))
(vlf-with-undo-disabled
(insert-file-contents buffer-file-name t nil nil t))
(goto-char (byte-to-position pos)))))
(t (setq vlf-mode t))))
(defun vlf-keep-alive ()
"Keep `vlf-mode' on major mode change."
(if (derived-mode-p 'hexl-mode)
(set (make-local-variable 'revert-buffer-function) 'vlf-revert))
(setq vlf-mode t))
;;;###autoload
(defun vlf (file &optional minimal)
"View Large FILE in batches. When MINIMAL load just a few bytes.
You can customize number of bytes displayed by customizing
`vlf-batch-size'.
Return newly created buffer."
(interactive (list (read-file-name "File to open: ") nil))
(let ((vlf-buffer (generate-new-buffer "*vlf*")))
(set-buffer vlf-buffer)
(set-visited-file-name file)
(set-buffer-modified-p nil)
(cond (minimal
(set (make-local-variable 'vlf-batch-size) 1024))
((file-remote-p file)
(set (make-local-variable 'vlf-batch-size) vlf-batch-size-remote)))
(vlf-mode 1)
(when minimal ;restore batch size to default value
(kill-local-variable 'vlf-batch-size)
(make-local-variable 'vlf-batch-size))
(switch-to-buffer vlf-buffer)
vlf-buffer))
(defun vlf-next-batch (append)
"Display the next batch of file data.
When prefix argument is supplied and positive
jump over APPEND number of batches.
When prefix argument is negative
append next APPEND number of batches to the existing buffer."
(interactive "p")
(vlf-verify-size)
(vlf-tune-load (if (derived-mode-p 'hexl-mode)
'(:hexl :raw)
'(:insert :encode)))
(let* ((end (min (+ vlf-end-pos (* vlf-batch-size (abs append)))
vlf-file-size))
(start (if (< append 0)
vlf-start-pos
(- end vlf-batch-size))))
(vlf-move-to-chunk start end)))
(defun vlf-prev-batch (prepend)
"Display the previous batch of file data.
When prefix argument is supplied and positive
jump over PREPEND number of batches.
When prefix argument is negative
append previous PREPEND number of batches to the existing buffer."
(interactive "p")
(if (zerop vlf-start-pos)
(error "Already at BOF"))
(vlf-tune-load (if (derived-mode-p 'hexl-mode)
'(:hexl :raw)
'(:insert :encode)))
(let* ((start (max 0 (- vlf-start-pos (* vlf-batch-size (abs prepend)))))
(end (if (< prepend 0)
vlf-end-pos
(+ start vlf-batch-size))))
(vlf-move-to-chunk start end)))
;; scroll auto batching
(defadvice scroll-up (around vlf-scroll-up
activate compile)
"Slide to next batch if at end of buffer in `vlf-mode'."
(if (and vlf-mode (pos-visible-in-window-p (point-max)))
(progn (vlf-next-batch 1)
(goto-char (point-min)))
ad-do-it))
(defadvice scroll-down (around vlf-scroll-down
activate compile)
"Slide to previous batch if at beginning of buffer in `vlf-mode'."
(if (and vlf-mode (pos-visible-in-window-p (point-min)))
(progn (vlf-prev-batch 1)
(goto-char (point-max)))
ad-do-it))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; hexl mode integration
(eval-after-load "hexl"
'(progn
(defadvice hexl-save-buffer (around vlf-hexl-save
activate compile)
"Prevent hexl save if `vlf-mode' is active."
(if vlf-mode
(vlf-write)
ad-do-it))
(defadvice hexl-scroll-up (around vlf-hexl-scroll-up
activate compile)
"Slide to next batch if at end of buffer in `vlf-mode'."
(if (and vlf-mode (pos-visible-in-window-p (point-max))
(or (not (numberp arg)) (< 0 arg)))
(progn (vlf-next-batch 1)
(goto-char (point-min)))
ad-do-it))
(defadvice hexl-scroll-down (around vlf-hexl-scroll-down
activate compile)
"Slide to previous batch if at beginning of buffer in `vlf-mode'."
(if (and vlf-mode (pos-visible-in-window-p (point-min)))
(progn (vlf-prev-batch 1)
(goto-char (point-max)))
ad-do-it))
(defadvice hexl-mode-exit (around vlf-hexl-mode-exit
activate compile)
"Exit `hexl-mode' gracefully in case `vlf-mode' is active."
(if (and vlf-mode (not (buffer-modified-p)))
(vlf-with-undo-disabled
(erase-buffer)
ad-do-it
(vlf-move-to-chunk-2 vlf-start-pos vlf-end-pos))
ad-do-it))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; utilities
(defun vlf-change-batch-size (decrease)
"Change the buffer-local value of `vlf-batch-size'.
Normally, the value is doubled;
with the prefix argument DECREASE it is halved."
(interactive "P")
(vlf-set-batch-size (if decrease (/ vlf-batch-size 2)
(* vlf-batch-size 2))))
(defun vlf-set-batch-size (size)
"Set batch to SIZE bytes and update chunk."
(interactive
(list (read-number "Size in bytes: "
(vlf-tune-optimal-load
(if (derived-mode-p 'hexl-mode)
'(:hexl :raw)
'(:insert :encode))))))
(setq vlf-batch-size size)
(vlf-move-to-batch vlf-start-pos))
(defun vlf-beginning-of-file ()
"Jump to beginning of file content."
(interactive)
(vlf-tune-load (if (derived-mode-p 'hexl-mode)
'(:hexl :raw)
'(:insert :encode)))
(vlf-move-to-batch 0))
(defun vlf-end-of-file ()
"Jump to end of file content."
(interactive)
(vlf-verify-size)
(vlf-tune-load (if (derived-mode-p 'hexl-mode)
'(:hexl :raw)
'(:insert :encode)))
(vlf-move-to-batch vlf-file-size))
(defun vlf-revert (&optional _auto noconfirm)
"Revert current chunk. Ignore _AUTO.
Ask for confirmation if NOCONFIRM is nil."
(interactive)
(when (or noconfirm
(yes-or-no-p (format "Revert buffer from file %s? "
buffer-file-name)))
(set-buffer-modified-p nil)
(vlf-move-to-chunk-2 vlf-start-pos vlf-end-pos)))
(defun vlf-jump-to-chunk (n)
"Go to to chunk N."
(interactive "nGoto to chunk: ")
(vlf-tune-load (if (derived-mode-p 'hexl-mode)
'(:hexl :raw)
'(:insert :encode)))
(vlf-move-to-batch (* (1- n) vlf-batch-size)))
(defun vlf-no-modifications ()
"Ensure there are no buffer modifications."
(if (buffer-modified-p)
(error "Save or discard your changes first")
t))
(defun vlf-move-to-batch (start)
"Move to batch determined by START.
Adjust according to file start/end and show `vlf-batch-size' bytes."
(vlf-verify-size)
(let* ((start (max 0 start))
(end (min (+ start vlf-batch-size) vlf-file-size)))
(if (= vlf-file-size end) ; re-adjust start
(setq start (max 0 (- end vlf-batch-size))))
(vlf-move-to-chunk start end)))
(defun vlf-next-batch-from-point ()
"Display batch of file data starting from current point."
(interactive)
(let ((start (+ vlf-start-pos (position-bytes (point)) -1)))
(vlf-move-to-chunk start (+ start vlf-batch-size)))
(goto-char (point-min)))
(provide 'vlf)
;;; vlf.el ends here