1
0
mirror of https://github.com/m00natic/vlfi.git synced 2025-11-14 22:05:36 +00:00

19 Commits
0.8 ... 0.9

Author SHA1 Message Date
Andrey Kotlarski
b943008eca Bump version and update README. 2013-06-09 02:34:52 +03:00
Andrey Kotlarski
4589c25d96 Return to current position after occur ending with quit. 2013-05-07 15:43:52 +03:00
Andrey Kotlarski
c827c3e186 Prevent standard save procedure invocation in case user postpones
saving.
2013-05-04 23:53:17 +03:00
Andrey Kotlarski
36d2ed4d09 Update README. 2013-05-02 14:25:39 +03:00
Andrey Kotlarski
ae775f88f9 Extend vlfi-goto-line to count lines from the end with negative
argument.
2013-05-02 14:18:41 +03:00
Andrey Kotlarski
1ac1eece40 Turn vlfi-get-file-size to function. 2013-05-02 12:04:12 +03:00
Andrey Kotlarski
67732485d8 Optimize a bit goto line. 2013-05-01 02:18:37 +03:00
Andrey Kotlarski
3d652fe71d Minor documentation fixes. 2013-05-01 02:02:28 +03:00
Andrey Kotlarski
7bfe665524 More secure chunk decode adjustment and minor fixes. 2013-05-01 01:51:36 +03:00
Colin Marquardt
28255a2aa2 Correctly print MB (for older emacsen). 2013-04-23 19:15:43 +03:00
Andrey Kotlarski
fbe081417c Fix occur indexing not to skip last chunk. 2013-04-23 00:53:54 +03:00
Andrey Kotlarski
fd9c258fc8 Use permanent buffer local write hook and update README. 2013-04-17 14:46:50 +03:00
Andrey Kotlarski
a63ea7a5d0 Grammar fixes. 2013-04-17 13:19:37 +03:00
Andrey Kotlarski
fbc3a37ce5 Add tip for ability to change major mode and more section hierarchy. 2013-04-17 13:11:40 +03:00
Andrey Kotlarski
eaf85e5cff Add key-binding to vlfi-mode-map in vlfi-edit-mode-map. 2013-04-17 13:00:37 +03:00
Andrey Kotlarski
e4d886a3e3 Add check if VLFI buffer has been modified before occur jumping to new
chunk.
2013-04-17 12:49:29 +03:00
Andrey Kotlarski
b8cc34422e Add more detailed usage details. 2013-04-17 12:33:04 +03:00
Andrey Kotlarski
a5fec57e32 Mostly documentation and commentary added. 2013-04-17 11:55:02 +03:00
Andrey Kotlarski
70719b0917 Add vlfi-occur-show command for showing match but still staying in
occur buffer.
2013-04-17 11:53:49 +03:00
2 changed files with 190 additions and 70 deletions

View File

@@ -18,4 +18,88 @@ following improvements:
- newly added content is acknowledged if file has changed size
meanwhile
- automatic scrolling of batches
- vlfi is added as an option when opening large files
- VLFI is added as an option when opening large files
GNU Emacs 23 and 24 are supported.
* Overview and tips
M-x vlfi PATH-TO-FILE
** Unicode
Emacs' Unicode support is leveraged so you'll not see bare bytes but
characters decoded as if file is normally opened. This holds for
editing, search and indexing.
** 32-bit GNU Emacs
Regular Emacs integers are used, so if you use 32-bit Emacs without
bignum support and have really huge file (with size beyond the maximum
integer value), VLFI will probably not quite work.
** Memory control
*vlfi-batch-size* bounds the memory used for all operations.
** Special mode
VLFI is derived from special-mode and keeps all its properties. For
example you can directly press digits to enter prefix arguments.
** Changing major mode
You can (temporarily) change major mode to whatever you like (for
example hexl-mode). Saving will insert contents as intended. You can
return to *vlfi-mode* too.
* Detail usage
** Controlling batch size
*+* and *-* control current batch size by factors of 2.
You can also set by hand local variable *vlfi-batch-size* and then
refresh with *g*.
** Move around
*M-PgUp* and *M-PgDn* move chunk by chunk. With positive prefix
argument they move prefix number of batches. With negative - append
prefix number of batches.
*[* and *]* take you to the beginning and end of file respectively.
*j* jumps to given chunk. To see where you are in file and how many chunks
there are (using the current batch size), look at the bracketed part
of the buffer name, batch size is also there - at the end.
** Search whole file
*s* and *r* search forward and backward respectively over the whole
file. This is done chunk by chunk so if you have really huge file -
you'd better set somewhat bigger batch size beforehand.
** Occur over whole file
*o* builds index for given regular expression just like occur-mode.
It does this chunk by chunk over the whole file. Note that even if
you prematurely stop it with *C-g*, it will still show index of what's
found so far.
** Jump to line
*l* jumps to given line in file. This is done by searching from the
beginning, so again the bigger current batch size, the quicker. With
negative argument, lines are counted from the end of file.
** Edit
*e* enters VLFI in edit mode. If editing doesn't change size of
the chunk, only this chunk is saved. Otherwise the remaining part of
the file is adjusted chunk by chunk, so again you'd better have bigger
current batch size. If chunk has been expanded the memory used is
#+BEGIN_EXAMPLE
(batch size + difference to the original chunk size) x 2
#+END_EXAMPLE

174
vlfi.el
View File

@@ -2,7 +2,7 @@
;; Copyright (C) 2006, 2012, 2013 Free Software Foundation, Inc.
;; Version: 0.8
;; Version: 0.9
;; Keywords: large files, utilities
;; Authors: 2006 Mathias Dahl <mathias.dahl@gmail.com>
;; 2012 Sam Steingold <sds@gnu.org>
@@ -26,11 +26,11 @@
;;; Commentary:
;; This package provides the M-x vlfi command, which visits part of a
;; large file in a read-only buffer without visiting the entire file.
;; large file without loading the entire file.
;; The buffer uses VLFI mode, which defines several commands for
;; moving around, searching and editing selected chunk of file.
;; moving around, searching and editing selected part of file.
;; This package is an improved fork of the vlf.el package.
;; This package is upgraded version of the vlf.el package.
;;; Code:
@@ -47,8 +47,7 @@
;;; Keep track of file position.
(defvar vlfi-start-pos 0
"Absolute position of the visible chunk start.")
(defvar vlfi-end-pos vlfi-batch-size
"Absolute position of the visible chunk end.")
(defvar vlfi-end-pos 0 "Absolute position of the visible chunk end.")
(defvar vlfi-file-size 0 "Total size of presented file.")
(defvar vlfi-mode-map
@@ -76,7 +75,8 @@
(setq buffer-read-only t)
(set-buffer-modified-p nil)
(buffer-disable-undo)
(add-hook 'write-contents-functions 'vlfi-write)
(make-local-variable 'write-file-functions)
(add-hook 'write-file-functions 'vlfi-write)
(make-local-variable 'revert-buffer-function)
(setq revert-buffer-function 'vlfi-revert)
(make-local-variable 'vlfi-batch-size)
@@ -96,10 +96,10 @@ buffer. You can customize number of bytes displayed by customizing
`vlfi-batch-size'."
(interactive "fFile to open: ")
(with-current-buffer (generate-new-buffer "*vlfi*")
(vlfi-mode)
(setq buffer-file-name file
vlfi-file-size (vlfi-get-file-size file))
(vlfi-insert-file)
(vlfi-mode)
(switch-to-buffer (current-buffer))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -166,11 +166,11 @@ OP-TYPE specifies the file operation being performed over FILENAME."
(goto-char (point-max)))
ad-do-it))
;; non recent Emacs
;; non-recent Emacs
(unless (fboundp 'file-size-human-readable)
(defun file-size-human-readable (file-size)
"Print FILE-SIZE in MB."
(format "%.1fMB" (/ file-size 1024.0))))
(format "%.1fMB" (/ file-size 1048576.0))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; utilities
@@ -200,9 +200,9 @@ with the prefix argument DECREASE it is halved."
"Update the current buffer name."
(rename-buffer (vlfi-format-buffer-name) t))
(defmacro vlfi-get-file-size (file)
(defun vlfi-get-file-size (file)
"Get size in bytes of FILE."
`(nth 7 (file-attributes ,file)))
(nth 7 (file-attributes file)))
(defun vlfi-insert-file (&optional from-end)
"Insert first chunk of current file contents in current buffer.
@@ -349,15 +349,17 @@ When given MINIMAL flag, skip non important operations."
(defun vlfi-adjust-chunk ()
"Adjust chunk beginning until content can be properly decoded.
Return number of bytes moved back for this to happen."
(let ((shift 0))
(let ((shift 0)
(chunk-size (- vlfi-end-pos vlfi-start-pos)))
(while (and (not (zerop vlfi-start-pos))
(< shift 3)
(/= (- vlfi-end-pos vlfi-start-pos)
(< shift 4)
(/= chunk-size
(length (encode-coding-region
(point-min) (point-max)
buffer-file-coding-system t))))
(setq shift (1+ shift)
vlfi-start-pos (1- vlfi-start-pos))
vlfi-start-pos (1- vlfi-start-pos)
chunk-size (1+ chunk-size))
(let ((inhibit-read-only t))
(erase-buffer)
(insert-file-contents buffer-file-name nil
@@ -368,8 +370,10 @@ Return number of bytes moved back for this to happen."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; search
(defun vlfi-re-search (regexp count backward)
"Search for REGEXP COUNT number of times forward or BACKWARD."
(defun vlfi-re-search (regexp count backward batch-step)
"Search for REGEXP COUNT number of times forward or BACKWARD.
BATCH-STEP is amount of overlap between successive chunks."
(assert (< 0 count))
(let* ((match-chunk-start vlfi-start-pos)
(match-chunk-end vlfi-end-pos)
(match-start-pos (+ vlfi-start-pos (position-bytes (point))))
@@ -380,8 +384,7 @@ Return number of bytes moved back for this to happen."
(if backward
(- vlfi-file-size vlfi-end-pos)
vlfi-start-pos)
vlfi-file-size))
(batch-step (/ vlfi-batch-size 8))) ; amount of chunk overlap
vlfi-file-size)))
(unwind-protect
(catch 'end-of-file
(if backward
@@ -437,7 +440,7 @@ Return number of bytes moved back for this to happen."
(or (byte-to-position
(- match-end-pos
vlfi-start-pos))
(point-max))
(point-min))
(point-min)))
(progress-reporter-update reporter
vlfi-end-pos)))))
@@ -492,7 +495,7 @@ Search is performed chunk by chunk in `vlfi-batch-size' memory."
(if regexp-history
(car regexp-history)))
(or current-prefix-arg 1)))
(vlfi-re-search regexp count nil))
(vlfi-re-search regexp count nil (/ vlfi-batch-size 8)))
(defun vlfi-re-search-backward (regexp count)
"Search backward for REGEXP prefix COUNT number of times.
@@ -501,21 +504,26 @@ Search is performed chunk by chunk in `vlfi-batch-size' memory."
(if regexp-history
(car regexp-history)))
(or current-prefix-arg 1)))
(vlfi-re-search regexp count t))
(vlfi-re-search regexp count t (/ vlfi-batch-size 8)))
(defun vlfi-goto-line (n)
"Go to line N."
"Go to line N. If N is negative, count from the end of file."
(interactive "nGo to line: ")
(let ((start-pos vlfi-start-pos)
(end-pos vlfi-end-pos)
(pos (point))
(success nil))
(unwind-protect
(progn (vlfi-beginning-of-file)
(goto-char (point-min))
(setq success (vlfi-re-search-forward "[\n\C-m]"
(1- n))))
(unless success
(if (< 0 n)
(progn (vlfi-beginning-of-file)
(goto-char (point-min))
(setq success (vlfi-re-search "[\n\C-m]" (1- n)
nil 0)))
(vlfi-end-of-file)
(goto-char (point-max))
(setq success (vlfi-re-search "[\n\C-m]" (- n) t 0)))
(if success
(message "Onto line %s" n)
(vlfi-move-to-chunk start-pos end-pos)
(goto-char pos)))))
@@ -528,6 +536,7 @@ Search is performed chunk by chunk in `vlfi-batch-size' memory."
(define-key map "p" 'vlfi-occur-prev-match)
(define-key map "\C-m" 'vlfi-occur-visit)
(define-key map [mouse-1] 'vlfi-occur-visit)
(define-key map "o" 'vlfi-occur-show)
map)
"Keymap for command `vlfi-occur-mode'.")
@@ -552,12 +561,27 @@ Search is performed chunk by chunk in `vlfi-batch-size' memory."
(goto-char (or (previous-single-property-change (point) 'face)
(point-max)))))
(defun vlfi-occur-show (&optional event)
"Visit current `vlfi-occur' link in a vlfi buffer but stay in the \
occur buffer. If original VLFI buffer has been killed,
open new VLFI session each time.
EVENT may hold details of the invocation."
(interactive (list last-nonmenu-event))
(let ((occur-buffer (if event
(window-buffer (posn-window
(event-end event)))
(current-buffer))))
(vlfi-occur-visit event)
(pop-to-buffer occur-buffer)))
(defun vlfi-occur-visit (&optional event)
"Visit current `vlfi-occur' link in a vlfi buffer.
The same for mouse EVENT."
If original VLFI buffer has been killed,
open new VLFI session each time.
EVENT may hold details of the invocation."
(interactive (list last-nonmenu-event))
(when event
(switch-to-buffer (window-buffer (posn-window (event-end event))))
(set-buffer (window-buffer (posn-window (event-end event))))
(goto-char (posn-point (event-end event))))
(let* ((pos (point))
(pos-relative (- pos (line-beginning-position) 1))
@@ -566,20 +590,27 @@ The same for mouse EVENT."
(let ((chunk-start (get-char-property pos 'chunk-start))
(chunk-end (get-char-property pos 'chunk-end))
(buffer (get-char-property pos 'buffer))
(match-pos (or (get-char-property pos 'match-pos)
(+ (get-char-property pos 'line-pos)
pos-relative))))
(unless (buffer-live-p buffer)
(let ((occur-buffer (current-buffer)))
(setq buffer (vlfi file))
(switch-to-buffer occur-buffer)))
(match-pos (+ (get-char-property pos 'line-pos)
pos-relative)))
(or (buffer-live-p buffer)
(let ((occur-buffer (current-buffer)))
(setq buffer (vlfi file))
(switch-to-buffer occur-buffer)))
(pop-to-buffer buffer)
(vlfi-move-to-chunk chunk-start chunk-end)
(set-buffer buffer)
(goto-char match-pos)))))
(if (buffer-modified-p)
(cond ((and (= vlfi-start-pos chunk-start)
(= vlfi-end-pos chunk-end))
(goto-char match-pos))
((y-or-n-p "VLFI buffer has been modified. \
Really jump to new chunk? ")
(vlfi-move-to-chunk chunk-start chunk-end)
(goto-char match-pos)))
(vlfi-move-to-chunk chunk-start chunk-end)
(goto-char match-pos))))))
(defun vlfi-occur (regexp)
"Make occur style index for REGEXP."
"Make whole file occur style index for REGEXP.
Prematurely ending indexing will still show what's found so far."
(interactive (list (read-regexp "List lines matching regexp"
(if regexp-history
(car regexp-history)))))
@@ -588,9 +619,9 @@ The same for mouse EVENT."
(pos (point)))
(vlfi-beginning-of-file)
(goto-char (point-min))
(vlfi-build-occur regexp)
(vlfi-move-to-chunk start-pos end-pos)
(goto-char pos)))
(unwind-protect (vlfi-build-occur regexp)
(vlfi-move-to-chunk start-pos end-pos)
(goto-char pos))))
(defun vlfi-build-occur (regexp)
"Build occur style index for REGEXP."
@@ -607,19 +638,20 @@ The same for mouse EVENT."
(line-regexp (concat "\\(?5:[\n\C-m]\\)\\|\\(?10:"
regexp "\\)"))
(batch-step (/ vlfi-batch-size 8))
(end-of-file nil)
(reporter (make-progress-reporter
(concat "Building index for " regexp "...")
vlfi-start-pos vlfi-file-size)))
(unwind-protect
(progn
(while (/= vlfi-end-pos vlfi-file-size)
(while (not end-of-file)
(if (re-search-forward line-regexp nil t)
(progn
(setq match-end-pos (+ vlfi-start-pos
(position-bytes
(match-end 0))))
(if (match-string 5)
(setq line (1+ line)
(setq line (1+ line) ; line detected
last-line-pos (point))
(let* ((chunk-start vlfi-start-pos)
(chunk-end vlfi-end-pos)
@@ -628,8 +660,8 @@ The same for mouse EVENT."
(line-text (buffer-substring
line-pos (line-end-position))))
(with-current-buffer occur-buffer
(unless (= line last-match-line)
(insert "\n:")
(unless (= line last-match-line) ;new match line
(insert "\n:") ; insert line number
(let* ((overlay-pos (1- (point)))
(overlay (make-overlay
overlay-pos
@@ -638,7 +670,7 @@ The same for mouse EVENT."
(propertize
(number-to-string line)
'face 'shadow)))
(insert (propertize line-text
(insert (propertize line-text ; insert line
'file file
'buffer vlfi-buffer
'chunk-start chunk-start
@@ -650,29 +682,31 @@ The same for mouse EVENT."
line))))
(setq last-match-line line
total-matches (1+ total-matches))
(let ((line-start (+ (line-beginning-position)
1))
(let ((line-start (1+
(line-beginning-position)))
(match-pos (match-beginning 10)))
(add-text-properties
(add-text-properties ; mark match
(+ line-start match-pos (- last-line-pos))
(+ line-start (match-end 10)
(- last-line-pos))
(list 'face 'match 'match-pos match-pos
(list 'face 'match
'help-echo
(format "Move to match %d"
total-matches))))))))
(let ((batch-move (- vlfi-end-pos batch-step)))
(vlfi-move-to-batch (if (< batch-move match-end-pos)
match-end-pos
batch-move) t))
(goto-char (if (< vlfi-start-pos match-end-pos)
(or (byte-to-position (- match-end-pos
vlfi-start-pos))
(point-min))
(point-min)))
(setq last-match-line 0
last-line-pos (point-min))
(progress-reporter-update reporter vlfi-end-pos)))
(setq end-of-file (= vlfi-end-pos vlfi-file-size))
(unless end-of-file
(let ((batch-move (- vlfi-end-pos batch-step)))
(vlfi-move-to-batch (if (< batch-move match-end-pos)
match-end-pos
batch-move) t))
(goto-char (if (< vlfi-start-pos match-end-pos)
(or (byte-to-position (- match-end-pos
vlfi-start-pos))
(point-min))
(point-min)))
(setq last-match-line 0
last-line-pos (line-beginning-position))
(progress-reporter-update reporter vlfi-end-pos))))
(progress-reporter-done reporter))
(if (zerop total-matches)
(progn (with-current-buffer occur-buffer
@@ -686,7 +720,7 @@ The same for mouse EVENT."
in file: %s" total-matches line regexp file)
'face 'underline))
(set-buffer-modified-p nil)
(forward-char)
(forward-char 2)
(vlfi-occur-mode))
(display-buffer occur-buffer)))))
@@ -698,6 +732,7 @@ in file: %s" total-matches line regexp file)
(set-keymap-parent map text-mode-map)
(define-key map "\C-c\C-c" 'vlfi-write)
(define-key map "\C-c\C-q" 'vlfi-discard-edit)
(define-key map "\C-v" vlfi-mode-map)
map)
"Keymap for command `vlfi-edit-mode'.")
@@ -712,6 +747,7 @@ or \\[vlfi-discard-edit] to discard changes.")))
(defun vlfi-discard-edit ()
"Discard edit and refresh chunk from file."
(interactive)
(set-buffer-modified-p nil)
(vlfi-move-to-chunk vlfi-start-pos vlfi-end-pos)
(vlfi-mode)
(message "Switched to VLFI mode."))
@@ -739,8 +775,8 @@ Save anyway? ")))
(t (vlfi-file-shift-forward (- size-change))))
(vlfi-move-to-chunk vlfi-start-pos vlfi-end-pos)
(goto-char pos))
(vlfi-mode)
t))
(vlfi-mode))
t)
(defun vlfi-file-shift-back (size-change)
"Shift file contents SIZE-CHANGE bytes back."