]> code.delx.au - gnu-emacs-elpa/blob - packages/vlf/vlf.el
Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs/elpa
[gnu-emacs-elpa] / packages / vlf / vlf.el
1 ;;; vlf.el --- View Large Files -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2006, 2012-2014 Free Software Foundation, Inc.
4
5 ;; Version: 1.4
6 ;; Keywords: large files, utilities
7 ;; Maintainer: Andrey Kotlarski <m00naticus@gmail.com>
8 ;; Authors: 2006 Mathias Dahl <mathias.dahl@gmail.com>
9 ;; 2012 Sam Steingold <sds@gnu.org>
10 ;; 2013-2014 Andrey Kotlarski <m00naticus@gmail.com>
11 ;; URL: https://github.com/m00natic/vlfi
12
13 ;; This file is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; This file is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to
25 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29 ;; This package provides the M-x vlf command, which visits part of
30 ;; large file without loading it entirely. The buffer uses VLF mode,
31 ;; which provides several commands for moving around, searching,
32 ;; comparing and editing selected part of file.
33 ;; To have it offered when opening large files:
34 ;; (require 'vlf-integrate)
35
36 ;; This package was inspired by a snippet posted by Kevin Rodgers,
37 ;; showing how to use `insert-file-contents' to extract part of a
38 ;; file.
39
40 ;;; Code:
41
42 (require 'vlf-base)
43
44 (autoload 'vlf-write "vlf-write" "Write current chunk to file." t)
45 (autoload 'vlf-re-search-forward "vlf-search"
46 "Search forward for REGEXP prefix COUNT number of times." t)
47 (autoload 'vlf-re-search-backward "vlf-search"
48 "Search backward for REGEXP prefix COUNT number of times." t)
49 (autoload 'vlf-goto-line "vlf-search" "Go to line." t)
50 (autoload 'vlf-occur "vlf-occur"
51 "Make whole file occur style index for REGEXP." t)
52 (autoload 'vlf-toggle-follow "vlf-follow"
53 "Toggle continuous chunk recenter around current point." t)
54 (autoload 'vlf-stop-follow "vlf-follow" "Stop continuous recenter." t)
55 (autoload 'vlf-ediff-buffers "vlf-ediff"
56 "Run batch by batch ediff over VLF buffers." t)
57
58 (defvar vlf-mode-map
59 (let ((map (make-sparse-keymap)))
60 (define-key map "n" 'vlf-next-batch)
61 (define-key map "p" 'vlf-prev-batch)
62 (define-key map " " 'vlf-next-batch-from-point)
63 (define-key map "+" 'vlf-change-batch-size)
64 (define-key map "-"
65 (lambda () "Decrease vlf batch size by factor of 2."
66 (interactive)
67 (vlf-change-batch-size t)))
68 (define-key map "s" 'vlf-re-search-forward)
69 (define-key map "r" 'vlf-re-search-backward)
70 (define-key map "o" 'vlf-occur)
71 (define-key map "[" 'vlf-beginning-of-file)
72 (define-key map "]" 'vlf-end-of-file)
73 (define-key map "j" 'vlf-jump-to-chunk)
74 (define-key map "l" 'vlf-goto-line)
75 (define-key map "e" 'vlf-ediff-buffers)
76 (define-key map "f" 'vlf-toggle-follow)
77 (define-key map "g" 'vlf-revert)
78 map)
79 "Keymap for `vlf-mode'.")
80
81 (defvar vlf-prefix-map
82 (let ((map (make-sparse-keymap)))
83 (define-key map "\C-c\C-v" vlf-mode-map)
84 map)
85 "Prefixed keymap for `vlf-mode'.")
86
87 (define-minor-mode vlf-mode
88 "Mode to browse large files in."
89 :lighter " VLF"
90 :group 'vlf
91 :keymap vlf-prefix-map
92 (if vlf-mode
93 (progn
94 (set (make-local-variable 'require-final-newline) nil)
95 (add-hook 'write-file-functions 'vlf-write nil t)
96 (set (make-local-variable 'revert-buffer-function)
97 'vlf-revert)
98 (make-local-variable 'vlf-batch-size)
99 (setq vlf-file-size (vlf-get-file-size buffer-file-truename)
100 vlf-start-pos 0
101 vlf-end-pos 0)
102 (let* ((pos (position-bytes (point)))
103 (start (* (/ pos vlf-batch-size) vlf-batch-size)))
104 (goto-char (byte-to-position (- pos start)))
105 (vlf-move-to-batch start)))
106 (kill-local-variable 'revert-buffer-function)
107 (vlf-stop-follow)
108 (when (or (not large-file-warning-threshold)
109 (< vlf-file-size large-file-warning-threshold)
110 (y-or-n-p (format "Load whole file (%s)? "
111 (file-size-human-readable
112 vlf-file-size))))
113 (kill-local-variable 'require-final-newline)
114 (remove-hook 'write-file-functions 'vlf-write t)
115 (let ((pos (+ vlf-start-pos (position-bytes (point)))))
116 (vlf-with-undo-disabled
117 (insert-file-contents buffer-file-name t nil nil t))
118 (goto-char (byte-to-position pos)))
119 (rename-buffer (file-name-nondirectory buffer-file-name) t))))
120
121 ;;;###autoload
122 (defun vlf (file)
123 "View Large FILE in batches.
124 You can customize number of bytes displayed by customizing
125 `vlf-batch-size'.
126 Return newly created buffer."
127 (interactive "fFile to open: ")
128 (let ((vlf-buffer (generate-new-buffer "*vlf*")))
129 (set-buffer vlf-buffer)
130 (set-visited-file-name file)
131 (set-buffer-modified-p nil)
132 (vlf-mode 1)
133 (switch-to-buffer vlf-buffer)
134 vlf-buffer))
135
136 (defun vlf-next-batch (append)
137 "Display the next batch of file data.
138 When prefix argument is supplied and positive
139 jump over APPEND number of batches.
140 When prefix argument is negative
141 append next APPEND number of batches to the existing buffer."
142 (interactive "p")
143 (vlf-verify-size)
144 (let* ((end (min (+ vlf-end-pos (* vlf-batch-size (abs append)))
145 vlf-file-size))
146 (start (if (< append 0)
147 vlf-start-pos
148 (- end vlf-batch-size))))
149 (vlf-move-to-chunk start end)))
150
151 (defun vlf-prev-batch (prepend)
152 "Display the previous batch of file data.
153 When prefix argument is supplied and positive
154 jump over PREPEND number of batches.
155 When prefix argument is negative
156 append previous PREPEND number of batches to the existing buffer."
157 (interactive "p")
158 (if (zerop vlf-start-pos)
159 (error "Already at BOF"))
160 (let* ((start (max 0 (- vlf-start-pos (* vlf-batch-size (abs prepend)))))
161 (end (if (< prepend 0)
162 vlf-end-pos
163 (+ start vlf-batch-size))))
164 (vlf-move-to-chunk start end)))
165
166 ;; scroll auto batching
167 (defadvice scroll-up (around vlf-scroll-up
168 activate compile)
169 "Slide to next batch if at end of buffer in `vlf-mode'."
170 (if (and vlf-mode (pos-visible-in-window-p (point-max)))
171 (progn (vlf-next-batch 1)
172 (goto-char (point-min)))
173 ad-do-it))
174
175 (defadvice scroll-down (around vlf-scroll-down
176 activate compile)
177 "Slide to previous batch if at beginning of buffer in `vlf-mode'."
178 (if (and vlf-mode (pos-visible-in-window-p (point-min)))
179 (progn (vlf-prev-batch 1)
180 (goto-char (point-max)))
181 ad-do-it))
182
183 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
184 ;;; utilities
185
186 (defun vlf-change-batch-size (decrease)
187 "Change the buffer-local value of `vlf-batch-size'.
188 Normally, the value is doubled;
189 with the prefix argument DECREASE it is halved."
190 (interactive "P")
191 (vlf-set-batch-size (if decrease (/ vlf-batch-size 2)
192 (* vlf-batch-size 2))))
193
194 (defun vlf-set-batch-size (size)
195 "Set batch to SIZE bytes and update chunk."
196 (interactive (list (read-number "Size in bytes: " vlf-batch-size)))
197 (setq vlf-batch-size size)
198 (vlf-move-to-batch vlf-start-pos))
199
200 (defun vlf-beginning-of-file ()
201 "Jump to beginning of file content."
202 (interactive)
203 (vlf-move-to-batch 0))
204
205 (defun vlf-end-of-file ()
206 "Jump to end of file content."
207 (interactive)
208 (vlf-verify-size)
209 (vlf-move-to-batch vlf-file-size))
210
211 (defun vlf-revert (&optional _ignore-auto noconfirm)
212 "Revert current chunk. Ignore _IGNORE-AUTO.
213 Ask for confirmation if NOCONFIRM is nil."
214 (interactive)
215 (when (or noconfirm
216 (yes-or-no-p (format "Revert buffer from file %s? "
217 buffer-file-name)))
218 (set-buffer-modified-p nil)
219 (vlf-move-to-chunk-2 vlf-start-pos vlf-end-pos)))
220
221 (defun vlf-jump-to-chunk (n)
222 "Go to to chunk N."
223 (interactive "nGoto to chunk: ")
224 (vlf-move-to-batch (* (1- n) vlf-batch-size)))
225
226 (defun vlf-no-modifications ()
227 "Ensure there are no buffer modifications."
228 (if (buffer-modified-p)
229 (error "Save or discard your changes first")
230 t))
231
232 (defun vlf-move-to-batch (start &optional minimal)
233 "Move to batch determined by START.
234 Adjust according to file start/end and show `vlf-batch-size' bytes.
235 When given MINIMAL flag, skip non important operations."
236 (vlf-verify-size)
237 (let* ((start (max 0 start))
238 (end (min (+ start vlf-batch-size) vlf-file-size)))
239 (if (= vlf-file-size end) ; re-adjust start
240 (setq start (max 0 (- end vlf-batch-size))))
241 (vlf-move-to-chunk start end minimal)))
242
243 (defun vlf-next-batch-from-point ()
244 "Display batch of file data starting from current point."
245 (interactive)
246 (let ((start (+ vlf-start-pos (position-bytes (point)) -1)))
247 (vlf-move-to-chunk start (+ start vlf-batch-size)))
248 (goto-char (point-min)))
249
250 (provide 'vlf)
251
252 ;;; vlf.el ends here