------------------------------------------------------------ revno: 422 revision-id: monn...@iro.umontreal.ca-20130722054131-81jwcig1lslrhj22 parent: monn...@iro.umontreal.ca-20130722052538-s8ram528f7plz997 author: Andrey Kotlarski <m00nati...@gmail.com> committer: Stefan Monnier <monn...@iro.umontreal.ca> branch nick: elpa timestamp: Mon 2013-07-22 01:41:31 -0400 message: * packages/vlf/vlf.el: Version 0.6 (vlf-mode): Setup revert and file write. (vlf-format-buffer-name): Change format to indicate the chunk numbers. (vlf-insert-file): Remove unused arg `file'. (vlf-beginning-of-file, vlf-end-of-file, vlf-jump-to-chunk): New commands. (vlf-mode-map): Use them. Add a `j' binding. (vlf-revert): New function. (vlf-next-batch, vlf-prev-batch, vlf-move-to-batch, vlf-move-to-chunk): Set modtime. Better preserve point. (vlf-file-shift-back, vlf-shift-batch, vlf-file-shift-forward) (vlf-shift-batches): New functions. (vlf-write): Use them when size of saved chunks has changed. Pay attention to modtimes. modified: packages/vlf/vlf.el vlf.el-20120614203028-urlm47rgs71aoaqu-2
=== modified file 'packages/vlf/vlf.el' --- a/packages/vlf/vlf.el 2013-07-22 05:25:38 +0000 +++ b/packages/vlf/vlf.el 2013-07-22 05:41:31 +0000 @@ -2,7 +2,7 @@ ;; Copyright (C) 2006, 2012, 2013 Free Software Foundation, Inc. -;; Version: 0.5 +;; Version: 0.6 ;; Keywords: large files, utilities ;; Authors: 2006 Mathias Dahl <mathias.d...@gmail.com> ;; 2012 Sam Steingold <s...@gnu.org> @@ -64,13 +64,10 @@ (vlf-change-batch-size t))) (define-key map "s" 'vlf-re-search-forward) (define-key map "r" 'vlf-re-search-backward) - (define-key map "]" (lambda () "Jump to end of file content." - (interactive) - (vlf-insert-file buffer-file-name t))) - (define-key map "[" (lambda () "Jump to beginning of file content." - (interactive) - (vlf-insert-file buffer-file-name))) + (define-key map "[" 'vlf-beginning-of-file) + (define-key map "]" 'vlf-end-of-file) (define-key map "e" 'vlf-edit-mode) + (define-key map "j" 'vlf-jump-to-chunk) map) "Keymap for `vlf-mode'.") @@ -79,6 +76,8 @@ (setq buffer-read-only t) (set-buffer-modified-p nil) (buffer-disable-undo) + (add-hook 'write-contents-functions 'vlf-write) + (setq revert-buffer-function 'vlf-revert) (make-local-variable 'vlf-batch-size) (put 'vlf-batch-size 'permanent-local t) (make-local-variable 'vlf-start-pos) @@ -88,6 +87,75 @@ (make-local-variable 'vlf-file-size) (put 'vlf-file-size 'permanent-local t)) +;;;###autoload +(defun vlf (file &optional from-end) + "View Large FILE. With FROM-END prefix, view from the back. +Batches of the file data from FILE will be displayed in a read-only +buffer. You can customize number of bytes displayed by customizing +`vlf-batch-size'." + (interactive "fFile to open: \nP") + (with-current-buffer (generate-new-buffer "*vlf*") + (setq buffer-file-name file + vlf-file-size (nth 7 (file-attributes file))) + (vlf-insert-file from-end) + (vlf-mode) + (switch-to-buffer (current-buffer)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; integration with other packages + +;;;###autoload +(defun dired-vlf (from-end) + "In Dired, visit the file on this line in VLF mode. +With FROM-END prefix, view from the back." + (interactive "P") + (vlf (dired-get-file-for-visit) from-end)) + +;;;###autoload +(eval-after-load "dired" + '(define-key dired-mode-map "V" 'dired-vlf)) + +;;;###autoload +(defun vlf-if-file-too-large (size op-type &optional filename) + "If file SIZE larger than `large-file-warning-threshold', \ +allow user to view file with `vlf', open it normally or abort. +OP-TYPE specifies the file operation being performed over FILENAME." + (and large-file-warning-threshold size + (> size large-file-warning-threshold) + (let ((char nil)) + (while (not (memq (setq char + (read-event + (propertize + (format + "File %s is large (%s): \ +%s normally (o), %s with vlf (v) or abort (a)" + (if filename + (file-name-nondirectory filename) + "") + (file-size-human-readable size) + op-type op-type) + 'face 'minibuffer-prompt))) + '(?o ?O ?v ?V ?a ?A)))) + (cond ((memq char '(?o ?O))) + ((memq char '(?v ?V)) + (vlf filename nil) + (error "")) + ((memq char '(?a ?A)) + (error "Aborted")))))) + +;; hijack `abort-if-file-too-large' +;;;###autoload +(fset 'abort-if-file-too-large 'vlf-if-file-too-large) + +;; 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)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; utilities + (defun vlf-change-batch-size (decrease) "Change the buffer-local value of `vlf-batch-size'. Normally, the value is doubled; @@ -102,16 +170,50 @@ (defun vlf-format-buffer-name () "Return format for vlf buffer name." - (format "%s(%s)[%.2f%%%%](%d)" + (format "%s(%s)[%d/%d](%d)" (file-name-nondirectory buffer-file-name) (file-size-human-readable vlf-file-size) - (/ (* 100 vlf-end-pos) (float vlf-file-size)) + (/ vlf-end-pos vlf-batch-size) + (/ vlf-file-size vlf-batch-size) vlf-batch-size)) (defun vlf-update-buffer-name () "Update the current buffer name." (rename-buffer (vlf-format-buffer-name) t)) +(defun vlf-insert-file (&optional from-end) + "Insert first chunk of current file contents in current buffer. +With FROM-END prefix, start from the back." + (if from-end + (setq vlf-start-pos (max 0 (- vlf-file-size vlf-batch-size)) + vlf-end-pos vlf-file-size) + (setq vlf-start-pos 0 + vlf-end-pos (min vlf-batch-size vlf-file-size))) + (vlf-move-to-chunk vlf-start-pos vlf-end-pos)) + +(defun vlf-beginning-of-file () + "Jump to beginning of file content." + (interactive) + (vlf-insert-file)) + +(defun vlf-end-of-file () + "Jump to end of file content." + (interactive) + (vlf-insert-file t)) + +(defun vlf-revert (&rest args) + "Revert current chunk. Ignore ARGS." + (ignore args) + (vlf-move-to-chunk vlf-start-pos vlf-end-pos)) + +(defun vlf-jump-to-chunk (n) + "Go to to chunk N." + (interactive "nGoto to chunk: ") + (vlf-move-to-batch (* (1- n) vlf-batch-size))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; batch movement + (defun vlf-next-batch (append) "Display the next batch of file data. When prefix argument is supplied and positive @@ -121,7 +223,7 @@ (interactive "p") (let ((end (+ vlf-end-pos (* vlf-batch-size (abs append))))) - (when (< vlf-file-size end) ; re-check file size + (when (< vlf-file-size end) ; re-check file size (setq vlf-file-size (nth 7 (file-attributes buffer-file-name))) (cond ((= vlf-end-pos vlf-file-size) (error "Already at EOF")) @@ -134,13 +236,13 @@ (goto-char (point-max)) (setq vlf-start-pos (- end vlf-batch-size)) (erase-buffer)) - (insert-file-contents buffer-file-name nil - (if do-append - vlf-end-pos - vlf-start-pos) + (insert-file-contents buffer-file-name nil (if do-append + vlf-end-pos + vlf-start-pos) end) (goto-char pos)) (setq vlf-end-pos end)) + (set-visited-file-modtime) (set-buffer-modified-p nil) (vlf-update-buffer-name)) @@ -168,6 +270,7 @@ vlf-end-pos)) (goto-char (- (point-max) pos)) (setq vlf-start-pos start)) + (set-visited-file-modtime) (set-buffer-modified-p nil) (vlf-update-buffer-name)) @@ -181,92 +284,36 @@ (nth 7 (file-attributes buffer-file-name)) vlf-end-pos (min vlf-end-pos vlf-file-size) vlf-start-pos (max 0 (- vlf-end-pos vlf-batch-size)))) - (let ((inhibit-read-only t)) + (let ((inhibit-read-only t) + (pos (point))) (erase-buffer) (insert-file-contents buffer-file-name nil - vlf-start-pos vlf-end-pos)) + vlf-start-pos vlf-end-pos) + (goto-char pos)) + (set-visited-file-modtime) (set-buffer-modified-p nil) (vlf-update-buffer-name)) (defun vlf-move-to-chunk (start end) "Move to chunk determined by START END." - (if (< vlf-file-size end) ; re-check file size + (if (< vlf-file-size end) ; re-check file size (setq vlf-file-size (nth 7 (file-attributes buffer-file-name)))) (setq vlf-start-pos (max 0 start) vlf-end-pos (min end vlf-file-size)) - (let ((inhibit-read-only t)) + (let ((inhibit-read-only t) + (pos (point))) (erase-buffer) (insert-file-contents buffer-file-name nil - vlf-start-pos vlf-end-pos)) + vlf-start-pos vlf-end-pos) + (goto-char pos)) + (set-visited-file-modtime) (set-buffer-modified-p nil) (vlf-update-buffer-name)) -(defun vlf-insert-file (file &optional from-end) - "Insert first chunk of FILE contents in current buffer. -With FROM-END prefix, start from the back." - (if from-end - (setq vlf-start-pos (max 0 (- vlf-file-size vlf-batch-size)) - vlf-end-pos vlf-file-size) - (setq vlf-start-pos 0 - vlf-end-pos (min vlf-batch-size vlf-file-size))) - (vlf-move-to-chunk vlf-start-pos vlf-end-pos)) - -;;;###autoload -(defun vlf (file &optional from-end) - "View Large FILE. With FROM-END prefix, view from the back. -Batches of the file data from FILE will be displayed in a read-only -buffer. You can customize number of bytes displayed by customizing -`vlf-batch-size'." - (interactive "fFile to open: \nP") - (with-current-buffer (generate-new-buffer "*vlf*") - (setq buffer-file-name file - vlf-file-size (nth 7 (file-attributes file))) - (vlf-insert-file file from-end) - (vlf-mode) - (switch-to-buffer (current-buffer)))) - -;;;###autoload -(defun dired-vlf (from-end) - "In Dired, visit the file on this line in VLF mode. -With FROM-END prefix, view from the back." - (interactive "P") - (vlf (dired-get-file-for-visit) from-end)) - -;;;###autoload -(eval-after-load "dired" - '(define-key dired-mode-map "V" 'dired-vlf)) - -;;;###autoload -(defun vlf-if-file-too-large (size op-type &optional filename) - "If file SIZE larger than `large-file-warning-threshold', \ -allow user to view file with `vlf', open it normally or abort. -OP-TYPE specifies the file operation being performed over FILENAME." - (and large-file-warning-threshold size - (> size large-file-warning-threshold) - (let ((char nil)) - (while (not (memq (setq char - (read-event - (propertize - (format "File %s is large (%s): \ -%s normally (o), %s with vlf (v) or abort (a)" - (file-name-nondirectory filename) - (file-size-human-readable size) - op-type op-type) - 'face 'minibuffer-prompt))) - '(?o ?O ?v ?V ?a ?A)))) - (cond ((memq char '(?o ?O))) - ((memq char '(?v ?V)) - (vlf filename nil) - (error "")) - ((memq char '(?a ?A)) - (error "Aborted")))))) - -;;; hijack `abort-if-file-too-large' -;;;###autoload -(fset 'abort-if-file-too-large 'vlf-if-file-too-large) - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; search + (defun vlf-re-search (regexp count backward) "Search for REGEXP COUNT number of times forward or BACKWARD." (let* ((match-start-pos (+ vlf-start-pos (point))) @@ -355,7 +402,8 @@ (delete-overlay overlay))))) (defun vlf-re-search-forward (regexp count) - "Search forward for REGEXP prefix COUNT number of times." + "Search forward for REGEXP prefix COUNT number of times. +Search is performed chunk by chunk in `vlf-batch-size' memory." (interactive (list (read-regexp "Search whole file" (if regexp-history (car regexp-history)) @@ -364,7 +412,8 @@ (vlf-re-search regexp count nil)) (defun vlf-re-search-backward (regexp count) - "Search backward for REGEXP prefix COUNT number of times." + "Search backward for REGEXP prefix COUNT number of times. +Search is performed chunk by chunk in `vlf-batch-size' memory." (interactive (list (read-regexp "Search whole file backward" (if regexp-history (car regexp-history)) @@ -372,7 +421,9 @@ (or current-prefix-arg 1))) (vlf-re-search regexp count t)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; editing + (defvar vlf-edit-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map text-mode-map) @@ -389,16 +440,6 @@ "Editing: Type \\[vlf-write] to write chunk \ or \\[vlf-discard-edit] to discard changes."))) -(defun vlf-write () - "Write current chunk to file. May overwrite existing content." - (interactive) - (when (or (= (buffer-size) (- vlf-end-pos vlf-start-pos)) - (y-or-n-p "Changed size of original chunk. \ -End of chunk will be garbled. Continue? ")) - (write-region nil nil buffer-file-name vlf-start-pos) - (vlf-move-to-chunk vlf-start-pos vlf-end-pos) - (vlf-mode))) - (defun vlf-discard-edit () "Discard edit and refresh chunk from file." (interactive) @@ -406,6 +447,119 @@ (vlf-mode) (message "Switched to VLF mode.")) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; saving + +(defun vlf-write () + "Write current chunk to file. Always return true to disable save. +If changing size of chunk shift remaining file content." + (interactive) + (when (and (derived-mode-p 'vlf-mode) + (buffer-modified-p) + (or (verify-visited-file-modtime) + (y-or-n-p "File has changed since visited or saved. \ +Save anyway? "))) + (let ((size-change (- vlf-end-pos vlf-start-pos + (length (encode-coding-region + (point-min) (point-max) + buffer-file-coding-system t)))) + (pos (point))) + (cond ((zerop size-change) + (write-region nil nil buffer-file-name vlf-start-pos t)) + ((< 0 size-change) + (vlf-file-shift-back size-change)) + (t (vlf-file-shift-forward (- size-change)))) + (goto-char pos)) + (vlf-move-to-chunk vlf-start-pos vlf-end-pos) + (vlf-mode) + t)) + +(defun vlf-file-shift-back (size-change) + "Shift file contents SIZE-CHANGE bytes back." + (let ((coding-system buffer-file-coding-system)) + (write-region nil nil buffer-file-name vlf-start-pos t) + (setq buffer-file-coding-system nil) + (let ((read-start-pos vlf-end-pos) + (reporter (make-progress-reporter "Adjusting file content" + vlf-end-pos + vlf-file-size))) + (while (vlf-shift-batch read-start-pos (- read-start-pos + size-change)) + (setq read-start-pos (+ read-start-pos vlf-batch-size)) + (progress-reporter-update reporter read-start-pos)) + ;; pad end with space + (erase-buffer) + (insert-char 32 size-change) + (write-region nil nil buffer-file-name (- vlf-file-size + size-change) t) + (progress-reporter-done reporter)) + (setq buffer-file-coding-system coding-system))) + +(defun vlf-shift-batch (read-pos write-pos) + "Read `vlf-batch-size' bytes from READ-POS and write them \ +back at WRITE-POS. Return nil if EOF is reached, t otherwise." + (erase-buffer) + (setq vlf-file-size (nth 7 (file-attributes buffer-file-name))) + (let ((read-end (+ read-pos vlf-batch-size))) + (insert-file-contents-literally buffer-file-name nil + read-pos + (min vlf-file-size read-end)) + (write-region nil nil buffer-file-name write-pos t) + (< read-end vlf-file-size))) + +(defun vlf-file-shift-forward (size-change) + "Shift file contents SIZE-CHANGE bytes forward. +Done by saving content up front and then writing previous batch." + (let ((vlf-buffer (current-buffer)) + (temp-buffer (generate-new-buffer (concat " " + (buffer-name)))) + (coding-system buffer-file-coding-system)) + (let ((file buffer-file-name)) + (set-buffer temp-buffer) + (setq buffer-file-name file)) + (set-buffer vlf-buffer) + (let ((read-buffer temp-buffer) + (write-buffer vlf-buffer) + (size (+ vlf-batch-size size-change)) + (read-pos vlf-end-pos) + (write-pos vlf-start-pos) + swap-buffer + (reporter (make-progress-reporter "Adjusting file content" + vlf-start-pos + vlf-file-size))) + (while (vlf-shift-batches size read-buffer read-pos + write-buffer write-pos) + (setq swap-buffer read-buffer + read-buffer write-buffer + write-buffer swap-buffer + write-pos (+ read-pos size-change) + read-pos (+ read-pos size)) + (progress-reporter-update reporter write-pos)) + (progress-reporter-done reporter)) + (kill-buffer temp-buffer) + (set-buffer vlf-buffer) + (setq buffer-file-coding-system coding-system))) + +(defun vlf-shift-batches (size read-buffer read-pos + write-buffer write-pos) + "Read SIZE bytes in READ-BUFFER starting from READ-POS. +Then write contents of WRITE-BUFFER to buffer file at WRITE-POS. +Return nil if EOF is reached, t otherwise." + (let* ((file-size (nth 7 (file-attributes buffer-file-name))) + (read-more (< read-pos file-size))) + (when read-more + ;; read + (set-buffer read-buffer) + (erase-buffer) + (setq buffer-file-coding-system nil) + (insert-file-contents-literally buffer-file-name nil read-pos + (min file-size (+ read-pos + size)))) + ;; write + (set-buffer write-buffer) + (write-region nil nil buffer-file-name write-pos t) + read-more)) + (provide 'vlf) ;;; vlf.el ends here