X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/0cdc945e2cdc9e1f7a72ed03639cff7edecc12ea..f0c48b253ea7903357d73eba6cc6ba3051ff8962:/packages/vlf/vlf.el diff --git a/packages/vlf/vlf.el b/packages/vlf/vlf.el index 28b65f186..e6c34e641 100644 --- a/packages/vlf/vlf.el +++ b/packages/vlf/vlf.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2006, 2012, 2013 Free Software Foundation, Inc. -;; Version: 0.9.1 +;; Version: 1.1 ;; Keywords: large files, utilities ;; Maintainer: Andrey Kotlarski ;; Authors: 2006 Mathias Dahl @@ -60,13 +60,12 @@ (defvar vlf-file-size 0 "Total size of presented file.") (put 'vlf-file-size 'permanent-local t) -(defvar vlf-encode-size 0 "Size in bytes of current batch decoded.") -(put 'vlf-encode-size 'permanent-local t) - (defvar vlf-mode-map - (let ((map (make-sparse-keymap))) - (define-key map [M-next] 'vlf-next-batch) - (define-key map [M-prior] 'vlf-prev-batch) + (let ((map-prefix (make-sparse-keymap)) + (map (make-sparse-keymap))) + (define-key map [next] 'vlf-next-batch) + (define-key map [prior] 'vlf-prev-batch) + (define-key map "n" 'vlf-next-batch-from-point) (define-key map "+" 'vlf-change-batch-size) (define-key map "-" (lambda () "Decrease vlf batch size by factor of 2." @@ -77,38 +76,57 @@ (define-key map "o" 'vlf-occur) (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) (define-key map "l" 'vlf-goto-line) - map) + (define-key map "g" 'vlf-refresh) + (define-key map-prefix "\C-c\C-v" map) + map-prefix) "Keymap for `vlf-mode'.") -(define-derived-mode vlf-mode special-mode "VLF" +(define-minor-mode vlf-mode "Mode to browse large files in." - (setq buffer-read-only t) - (set-buffer-modified-p nil) - (buffer-disable-undo) - (add-hook 'write-file-functions 'vlf-write nil t) - (make-local-variable 'revert-buffer-function) - (setq revert-buffer-function 'vlf-revert) - (make-local-variable 'vlf-batch-size) - (make-local-variable 'vlf-start-pos) - (make-local-variable 'vlf-end-pos) - (make-local-variable 'vlf-file-size) - (make-local-variable 'vlf-encode-size)) + :lighter " VLF" + :group 'vlf + :keymap vlf-mode-map + (if vlf-mode + (progn + (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) + (set (make-local-variable 'vlf-start-pos) -1) + (make-local-variable 'vlf-end-pos) + (set (make-local-variable 'vlf-file-size) + (vlf-get-file-size buffer-file-name)) + (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))) + (kill-local-variable 'revert-buffer-function) + (when (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 'require-final-newline) + (remove-hook 'write-file-functions 'vlf-write t) + (let ((pos (+ vlf-start-pos (position-bytes (point))))) + (vlf-with-undo-disabled + (insert-file-contents buffer-file-name t nil nil t)) + (goto-char (byte-to-position pos))) + (rename-buffer (file-name-nondirectory buffer-file-name) t)))) ;;;###autoload (defun vlf (file) - "View Large FILE. -Batches of the file data from FILE will be displayed in a read-only -buffer. You can customize number of bytes displayed by customizing + "View Large FILE in batches. +You can customize number of bytes displayed by customizing `vlf-batch-size'." (interactive "fFile to open: ") (with-current-buffer (generate-new-buffer "*vlf*") (set-visited-file-name file) - (vlf-mode) - (setq vlf-file-size (vlf-get-file-size buffer-file-name)) - (vlf-insert-file) + (set-buffer-modified-p nil) + (vlf-mode 1) (switch-to-buffer (current-buffer)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -160,17 +178,15 @@ OP-TYPE specifies the file operation being performed over FILENAME." (defadvice scroll-up (around vlf-scroll-up activate compile) "Slide to next batch if at end of buffer in `vlf-mode'." - (if (and (derived-mode-p 'vlf-mode) - (eobp)) + (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 (derived-mode-p 'vlf-mode) - (bobp)) + "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)) @@ -191,22 +207,18 @@ Normally, the value is doubled; with the prefix argument DECREASE it is halved." (interactive "P") (setq vlf-batch-size (if decrease - (/ vlf-batch-size 2) - (* vlf-batch-size 2))) + (/ vlf-batch-size 2) + (* vlf-batch-size 2))) (vlf-move-to-batch vlf-start-pos)) -(defun vlf-format-buffer-name () - "Return format for vlf buffer name." - (format "%s(%s)[%d/%d](%d)" - (file-name-nondirectory buffer-file-name) - (file-size-human-readable 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)) + (rename-buffer (format "%s(%d/%d)[%s]" + (file-name-nondirectory buffer-file-name) + (/ vlf-end-pos vlf-batch-size) + (/ vlf-file-size vlf-batch-size) + (file-size-human-readable vlf-batch-size)) + t)) (defun vlf-get-file-size (file) "Get size in bytes of FILE." @@ -221,12 +233,13 @@ with the prefix argument DECREASE it is halved." (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)) + (let ((start 0) + (end vlf-batch-size)) + (if from-end + (setq start (- vlf-file-size vlf-batch-size) + end vlf-file-size) + (setq end (min vlf-batch-size vlf-file-size))) + (vlf-move-to-chunk start end))) (defun vlf-beginning-of-file () "Jump to beginning of file content." @@ -244,13 +257,28 @@ Ask for confirmation if NOCONFIRM is nil." (if (or noconfirm (yes-or-no-p (format "Revert buffer from file %s? " buffer-file-name))) - (vlf-move-to-chunk vlf-start-pos vlf-end-pos))) + (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-move-to-batch (* (1- n) vlf-batch-size))) +(defmacro vlf-with-undo-disabled (&rest body) + "Execute BODY with temporarily disabled undo." + `(let ((undo-enabled (not (eq buffer-undo-list t)))) + (if undo-enabled + (buffer-disable-undo)) + (unwind-protect (progn ,@body) + (if undo-enabled + (buffer-enable-undo))))) + +(defun vlf-no-modifications () + "Ensure there are no buffer modifications." + (if (buffer-modified-p) + (error "Save or discard your changes first") + t)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; batch movement @@ -262,26 +290,13 @@ When prefix argument is negative append next APPEND number of batches to the existing buffer." (interactive "p") (vlf-verify-size) - (let ((end (min (+ vlf-end-pos (* vlf-batch-size + (let* ((end (min (+ vlf-end-pos (* vlf-batch-size (abs append))) - vlf-file-size))) - (let ((inhibit-read-only t) - (do-append (< append 0)) - (pos (position-bytes (point)))) - (if do-append - (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) - end) - (setq vlf-end-pos end) - (goto-char (or (byte-to-position (+ pos (vlf-adjust-chunk))) - (point-max))))) - (set-visited-file-modtime) - (set-buffer-modified-p nil) - (vlf-update-buffer-name)) + 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. @@ -292,92 +307,190 @@ When prefix argument is negative (interactive "p") (if (zerop vlf-start-pos) (error "Already at BOF")) - (vlf-verify-size) - (let ((inhibit-read-only t) - (start (max 0 (- vlf-start-pos (* vlf-batch-size + (let* ((start (max 0 (- vlf-start-pos (* vlf-batch-size (abs prepend))))) - (do-prepend (< prepend 0)) - (pos (- (position-bytes (point-max)) - (position-bytes (point))))) - (if do-prepend - (goto-char (point-min)) - (setq vlf-end-pos (min (+ start vlf-batch-size) - vlf-file-size)) - (erase-buffer)) - (insert-file-contents buffer-file-name nil start - (if do-prepend - vlf-start-pos - vlf-end-pos)) - (setq vlf-start-pos start - pos (+ pos (vlf-adjust-chunk))) - (goto-char (or (byte-to-position (- (position-bytes (point-max)) - pos)) - (point-max)))) - (set-visited-file-modtime) - (set-buffer-modified-p nil) - (vlf-update-buffer-name)) + (end (if (< prepend 0) + vlf-end-pos + (+ start vlf-batch-size)))) + (vlf-move-to-chunk start end))) (defun vlf-move-to-batch (start &optional minimal) "Move to batch determined by START. Adjust according to file start/end and show `vlf-batch-size' bytes. When given MINIMAL flag, skip non important operations." (vlf-verify-size) - (setq vlf-start-pos (max 0 start) - vlf-end-pos (min (+ vlf-start-pos vlf-batch-size) - vlf-file-size)) - (if (= vlf-file-size vlf-end-pos) ; re-check file size - (setq vlf-start-pos (max 0 (- vlf-end-pos vlf-batch-size)))) - (let ((inhibit-read-only t) - (pos (position-bytes (point)))) - (erase-buffer) - (insert-file-contents buffer-file-name nil - vlf-start-pos vlf-end-pos) - (goto-char (or (byte-to-position (+ pos (vlf-adjust-chunk))) - (point-max)))) - (set-buffer-modified-p nil) - (set-visited-file-modtime) - (or minimal(vlf-update-buffer-name))) + (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 minimal))) + +(defun vlf-next-batch-from-point () + "Display batch of file data starting from current point." + (interactive) + (vlf-move-to-batch (+ vlf-start-pos (position-bytes (point)) -1)) + (goto-char (point-min))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; primitive chunk operations (defun vlf-move-to-chunk (start end &optional minimal) "Move to chunk determined by START END. -When given MINIMAL flag, skip non important operations." - (vlf-verify-size) +When given MINIMAL flag, skip non important operations. +If same as current chunk is requested, do nothing." + (unless (and (= start vlf-start-pos) + (= end vlf-end-pos)) + (vlf-verify-size) + (if (vlf-move-to-chunk-1 start end) + (or minimal (vlf-update-buffer-name))))) + +(defun vlf-move-to-chunk-1 (start end) + "Move to chunk determined by START END keeping as much edits if any. +Return t if move hasn't been canceled." + (let ((modified (buffer-modified-p)) + (start (max 0 start)) + (end (min end vlf-file-size)) + (edit-end (+ (position-bytes (point-max)) vlf-start-pos))) + (cond + ((and (= start vlf-start-pos) (= end edit-end)) + (unless modified + (vlf-move-to-chunk-2 start end) + t)) + ((or (<= edit-end start) (<= end vlf-start-pos)) + (when (or (not modified) + (y-or-n-p "Chunk modified, are you sure? ")) ;full chunk renewal + (set-buffer-modified-p nil) + (vlf-move-to-chunk-2 start end) + t)) + ((or (and (<= start vlf-start-pos) (<= edit-end end)) + (not modified) + (y-or-n-p "Chunk modified, are you sure? ")) + (let ((pos (+ (position-bytes (point)) vlf-start-pos)) + (shift-start 0) + (shift-end 0) + (inhibit-read-only t)) + (cond ((< end edit-end) + (let* ((del-pos (1+ (byte-to-position + (- end vlf-start-pos)))) + (del-len (length (encode-coding-region + del-pos (point-max) + buffer-file-coding-system + t)))) + (setq end (- vlf-end-pos del-len)) + (vlf-with-undo-disabled + (delete-region del-pos (point-max))))) + ((< edit-end end) + (let ((edit-end-pos (point-max))) + (goto-char edit-end-pos) + (vlf-with-undo-disabled + (insert-file-contents buffer-file-name nil + vlf-end-pos end) + (setq shift-end (cdr (vlf-adjust-chunk + vlf-end-pos end nil t + edit-end-pos))))))) + (cond ((< vlf-start-pos start) + (let* ((del-pos (1+ (byte-to-position + (- start vlf-start-pos)))) + (del-len (length (encode-coding-region + (point-min) del-pos + buffer-file-coding-system + t)))) + (setq start (+ vlf-start-pos del-len)) + (vlf-with-undo-disabled + (delete-region (point-min) del-pos)))) + ((< start vlf-start-pos) + (let ((edit-end-pos (point-max))) + (goto-char edit-end-pos) + (vlf-with-undo-disabled + (insert-file-contents buffer-file-name nil + start vlf-start-pos) + (setq shift-start (car + (vlf-adjust-chunk start + vlf-start-pos + t nil + edit-end-pos))) + (goto-char (point-min)) + (insert (delete-and-extract-region edit-end-pos + (point-max))))))) + (setq vlf-start-pos (- start shift-start) + vlf-end-pos (+ end shift-end)) + (goto-char (or (byte-to-position (- pos vlf-start-pos)) + (point-max)))) + (set-buffer-modified-p modified) + t)))) + +(defun vlf-move-to-chunk-2 (start end) + "Unconditionally move to chunk determined by START END." (setq vlf-start-pos (max 0 start) vlf-end-pos (min end vlf-file-size)) (let ((inhibit-read-only t) (pos (position-bytes (point)))) - (erase-buffer) - (insert-file-contents buffer-file-name nil - vlf-start-pos vlf-end-pos) - (goto-char (or (byte-to-position (+ pos (vlf-adjust-chunk))) - (point-max)))) + (vlf-with-undo-disabled + (erase-buffer) + (insert-file-contents buffer-file-name nil + vlf-start-pos vlf-end-pos) + (let ((shifts (vlf-adjust-chunk vlf-start-pos vlf-end-pos t + t))) + (setq vlf-start-pos (- vlf-start-pos (car shifts)) + vlf-end-pos (+ vlf-end-pos (cdr shifts))) + (goto-char (or (byte-to-position (+ pos (car shifts))) + (point-max)))))) (set-buffer-modified-p nil) - (set-visited-file-modtime) - (or minimal (vlf-update-buffer-name))) - -(defun vlf-adjust-chunk () - "Adjust chunk beginning until content can be properly decoded. -Set `vlf-encode-size' to size of buffer when encoded. -Return number of bytes moved back for this to happen." - (let ((shift 0) - (chunk-size (- vlf-end-pos vlf-start-pos))) - (while (and (< shift 4) - (< 4 (abs (- chunk-size - (setq vlf-encode-size + (set-visited-file-modtime)) + +(defun vlf-adjust-chunk (start end &optional adjust-start adjust-end + position) + "Adjust chunk at absolute START to END till content can be\ +properly decoded. ADJUST-START determines if trying to prepend bytes\ + to the beginning, ADJUST-END - append to the end. +Use buffer POSITION as start if given. +Return number of bytes moved back for proper decoding and number of +bytes added to the end." + (let ((shift-start 0) + (shift-end 0)) + (if adjust-start + (let ((position (or position (point-min))) + (chunk-size (- end start))) + (while (and (not (zerop start)) + (< shift-start 4) + (< 4 (abs (- chunk-size (length (encode-coding-region - (point-min) (point-max) + position (point-max) buffer-file-coding-system t)))))) - (not (zerop vlf-start-pos))) - (setq shift (1+ shift) - vlf-start-pos (1- vlf-start-pos) - chunk-size (1+ chunk-size)) - (let ((inhibit-read-only t)) - (erase-buffer) - (insert-file-contents buffer-file-name nil - vlf-start-pos vlf-end-pos))) - (set-buffer-modified-p nil) - shift)) + (setq shift-start (1+ shift-start) + start (1- start) + chunk-size (1+ chunk-size)) + (delete-region position (point-max)) + (goto-char position) + (insert-file-contents buffer-file-name nil start end)))) + (if adjust-end + (cond ((vlf-partial-decode-shown-p) ;remove raw bytes from end + (goto-char (point-max)) + (while (eq (char-charset (preceding-char)) 'eight-bit) + (setq shift-end (1- shift-end)) + (delete-char -1))) + ((< end vlf-file-size) ;add bytes until new character is displayed + (let ((position (or position (point-min))) + (expected-size (buffer-size))) + (while (and (progn + (setq shift-end (1+ shift-end) + end (1+ end)) + (delete-region position (point-max)) + (goto-char position) + (insert-file-contents buffer-file-name + nil start end) + (< end vlf-file-size)) + (= expected-size (buffer-size)))))))) + (cons shift-start shift-end))) + +(defun vlf-partial-decode-shown-p () + "Determine if partial decode codes are displayed. +This seems to be the case with GNU/Emacs before 24.4." + (cond ((< emacs-major-version 24) t) + ((< 24 emacs-major-version) nil) + (t ;; TODO: use (< emacs-minor-version 4) after 24.4 release + (string-lessp emacs-version "24.3.5")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; search @@ -385,8 +498,10 @@ Return number of bytes moved back for this to happen." (defun vlf-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 vlf-start-pos) + (if (<= count 0) + (error "Count must be positive")) + (let* ((case-fold-search t) + (match-chunk-start vlf-start-pos) (match-chunk-end vlf-end-pos) (match-start-pos (+ vlf-start-pos (position-bytes (point)))) (match-end-pos match-start-pos) @@ -397,78 +512,80 @@ BATCH-STEP is amount of overlap between successive chunks." (- vlf-file-size vlf-end-pos) vlf-start-pos) vlf-file-size))) - (unwind-protect - (catch 'end-of-file - (if backward - (while (not (zerop to-find)) - (cond ((re-search-backward regexp nil t) - (setq to-find (1- to-find) - match-chunk-start vlf-start-pos - match-chunk-end vlf-end-pos - match-start-pos (+ vlf-start-pos - (position-bytes - (match-beginning 0))) - match-end-pos (+ vlf-start-pos - (position-bytes - (match-end 0))))) - ((zerop vlf-start-pos) - (throw 'end-of-file nil)) - (t (let ((batch-move (- vlf-start-pos - (- vlf-batch-size - batch-step)))) - (vlf-move-to-batch - (if (< match-start-pos batch-move) - (- match-start-pos vlf-batch-size) - batch-move) t)) - (goto-char (if (< match-start-pos - vlf-end-pos) - (or (byte-to-position - (- match-start-pos - vlf-start-pos)) - (point-max)) - (point-max))) - (progress-reporter-update - reporter (- vlf-file-size - vlf-start-pos))))) - (while (not (zerop to-find)) - (cond ((re-search-forward regexp nil t) - (setq to-find (1- to-find) - match-chunk-start vlf-start-pos - match-chunk-end vlf-end-pos - match-start-pos (+ vlf-start-pos - (position-bytes - (match-beginning 0))) - match-end-pos (+ vlf-start-pos - (position-bytes - (match-end 0))))) - ((= vlf-end-pos vlf-file-size) - (throw 'end-of-file nil)) - (t (let ((batch-move (- vlf-end-pos batch-step))) - (vlf-move-to-batch - (if (< batch-move match-end-pos) - match-end-pos - batch-move) t)) - (goto-char (if (< vlf-start-pos match-end-pos) - (or (byte-to-position - (- match-end-pos - vlf-start-pos)) - (point-min)) - (point-min))) - (progress-reporter-update reporter - vlf-end-pos))))) - (progress-reporter-done reporter)) - (if backward - (vlf-goto-match match-chunk-start match-chunk-end + (vlf-with-undo-disabled + (unwind-protect + (catch 'end-of-file + (if backward + (while (not (zerop to-find)) + (cond ((re-search-backward regexp nil t) + (setq to-find (1- to-find) + match-chunk-start vlf-start-pos + match-chunk-end vlf-end-pos + match-start-pos (+ vlf-start-pos + (position-bytes + (match-beginning 0))) + match-end-pos (+ vlf-start-pos + (position-bytes + (match-end 0))))) + ((zerop vlf-start-pos) + (throw 'end-of-file nil)) + (t (let ((batch-move (- vlf-start-pos + (- vlf-batch-size + batch-step)))) + (vlf-move-to-batch + (if (< match-start-pos batch-move) + (- match-start-pos vlf-batch-size) + batch-move) t)) + (goto-char (if (< match-start-pos + vlf-end-pos) + (or (byte-to-position + (- match-start-pos + vlf-start-pos)) + (point-max)) + (point-max))) + (progress-reporter-update + reporter (- vlf-file-size + vlf-start-pos))))) + (while (not (zerop to-find)) + (cond ((re-search-forward regexp nil t) + (setq to-find (1- to-find) + match-chunk-start vlf-start-pos + match-chunk-end vlf-end-pos + match-start-pos (+ vlf-start-pos + (position-bytes + (match-beginning 0))) + match-end-pos (+ vlf-start-pos + (position-bytes + (match-end 0))))) + ((= vlf-end-pos vlf-file-size) + (throw 'end-of-file nil)) + (t (let ((batch-move (- vlf-end-pos batch-step))) + (vlf-move-to-batch + (if (< batch-move match-end-pos) + match-end-pos + batch-move) t)) + (goto-char (if (< vlf-start-pos match-end-pos) + (or (byte-to-position + (- match-end-pos + vlf-start-pos)) + (point-min)) + (point-min))) + (progress-reporter-update reporter + vlf-end-pos))))) + (progress-reporter-done reporter)) + (set-buffer-modified-p nil) + (if backward + (vlf-goto-match match-chunk-start match-chunk-end match-end-pos match-start-pos count to-find) - (vlf-goto-match match-chunk-start match-chunk-end + (vlf-goto-match match-chunk-start match-chunk-end match-start-pos match-end-pos - count to-find))))) + count to-find)))))) (defun vlf-goto-match (match-chunk-start match-chunk-end - match-pos-start - match-pos-end - count to-find) + match-pos-start + match-pos-end + count to-find) "Move to MATCH-CHUNK-START MATCH-CHUNK-END surrounding \ MATCH-POS-START and MATCH-POS-END. According to COUNT and left TO-FIND, show if search has been @@ -496,31 +613,34 @@ successful. Return nil if nothing found." (goto-char match-end) (message "Moved to the %d match which is last" (- count to-find))) - (sit-for 0.1) - (delete-overlay overlay) + (unwind-protect (sit-for 3) + (delete-overlay overlay)) t)))) (defun vlf-re-search-forward (regexp count) "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))) - (or current-prefix-arg 1))) + (interactive (if (vlf-no-modifications) + (list (read-regexp "Search whole file" + (if regexp-history + (car regexp-history))) + (or current-prefix-arg 1)))) (vlf-re-search regexp count nil (/ vlf-batch-size 8))) (defun vlf-re-search-backward (regexp count) "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))) - (or current-prefix-arg 1))) + (interactive (if (vlf-no-modifications) + (list (read-regexp "Search whole file backward" + (if regexp-history + (car regexp-history))) + (or current-prefix-arg 1)))) (vlf-re-search regexp count t (/ vlf-batch-size 8))) (defun vlf-goto-line (n) "Go to line N. If N is negative, count from the end of file." - (interactive "nGo to line: ") + (interactive (if (vlf-no-modifications) + (list (read-number "Go to line: ")))) (let ((start-pos vlf-start-pos) (end-pos vlf-end-pos) (pos (point)) @@ -530,7 +650,7 @@ Search is performed chunk by chunk in `vlf-batch-size' memory." (progn (vlf-beginning-of-file) (goto-char (point-min)) (setq success (vlf-re-search "[\n\C-m]" (1- n) - nil 0))) + nil 0))) (vlf-end-of-file) (goto-char (point-max)) (setq success (vlf-re-search "[\n\C-m]" (- n) t 0))) @@ -547,6 +667,7 @@ Search is performed chunk by chunk in `vlf-batch-size' memory." (define-key map "n" 'vlf-occur-next-match) (define-key map "p" 'vlf-occur-prev-match) (define-key map "\C-m" 'vlf-occur-visit) + (define-key map "\M-\r" 'vlf-occur-visit-new-buffer) (define-key map [mouse-1] 'vlf-occur-visit) (define-key map "o" 'vlf-occur-show) map) @@ -586,10 +707,16 @@ EVENT may hold details of the invocation." (vlf-occur-visit event) (pop-to-buffer occur-buffer))) +(defun vlf-occur-visit-new-buffer () + "Visit `vlf-occur' link in new vlf buffer." + (interactive) + (let ((current-prefix-arg t)) + (vlf-occur-visit))) + (defun vlf-occur-visit (&optional event) "Visit current `vlf-occur' link in a vlf buffer. -If original VLF buffer has been killed, -open new VLF session each time. +With prefix argument or if original VLF buffer has been killed, +open new VLF session. EVENT may hold details of the invocation." (interactive (list last-nonmenu-event)) (when event @@ -601,24 +728,25 @@ EVENT may hold details of the invocation." (if file (let ((chunk-start (get-char-property pos 'chunk-start)) (chunk-end (get-char-property pos 'chunk-end)) - (buffer (get-char-property pos 'buffer)) + (vlf-buffer (get-char-property pos 'buffer)) + (occur-buffer (current-buffer)) (match-pos (+ (get-char-property pos 'line-pos) pos-relative))) - (or (buffer-live-p buffer) - (let ((occur-buffer (current-buffer))) - (setq buffer (vlf file)) - (switch-to-buffer occur-buffer))) - (pop-to-buffer buffer) - (if (buffer-modified-p) - (cond ((and (= vlf-start-pos chunk-start) - (= vlf-end-pos chunk-end)) - (goto-char match-pos)) - ((y-or-n-p "VLF buffer has been modified. \ -Really jump to new chunk? ") - (vlf-move-to-chunk chunk-start chunk-end) - (goto-char match-pos))) - (vlf-move-to-chunk chunk-start chunk-end) - (goto-char match-pos)))))) + (cond (current-prefix-arg + (setq vlf-buffer (vlf file)) + (switch-to-buffer occur-buffer)) + ((not (buffer-live-p vlf-buffer)) + (or (catch 'found + (dolist (buf (buffer-list)) + (set-buffer buf) + (and vlf-mode (equal file buffer-file-name) + (setq vlf-buffer buf) + (throw 'found t)))) + (setq vlf-buffer (vlf file))) + (switch-to-buffer occur-buffer))) + (pop-to-buffer vlf-buffer) + (vlf-move-to-chunk chunk-start chunk-end) + (goto-char match-pos))))) (defun vlf-occur (regexp) "Make whole file occur style index for REGEXP. @@ -626,18 +754,32 @@ 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))))) - (let ((start-pos vlf-start-pos) - (end-pos vlf-end-pos) - (pos (point))) - (vlf-beginning-of-file) - (goto-char (point-min)) - (unwind-protect (vlf-build-occur regexp) - (vlf-move-to-chunk start-pos end-pos) - (goto-char pos)))) - -(defun vlf-build-occur (regexp) - "Build occur style index for REGEXP." - (let ((line 1) + (if (buffer-modified-p) ;use temporary buffer not to interfere with modifications + (let ((vlf-buffer (current-buffer)) + (file buffer-file-name) + (batch-size vlf-batch-size)) + (with-temp-buffer + (setq buffer-file-name file) + (set-buffer-modified-p nil) + (set (make-local-variable 'vlf-batch-size) batch-size) + (vlf-mode 1) + (goto-char (point-min)) + (vlf-with-undo-disabled + (vlf-build-occur regexp vlf-buffer)))) + (let ((start-pos vlf-start-pos) + (end-pos vlf-end-pos) + (pos (point))) + (vlf-beginning-of-file) + (goto-char (point-min)) + (vlf-with-undo-disabled + (unwind-protect (vlf-build-occur regexp (current-buffer)) + (vlf-move-to-chunk start-pos end-pos) + (goto-char pos)))))) + +(defun vlf-build-occur (regexp vlf-buffer) + "Build occur style index for REGEXP over VLF-BUFFER." + (let ((case-fold-search t) + (line 1) (last-match-line 0) (last-line-pos (point-min)) (file buffer-file-name) @@ -645,7 +787,7 @@ Prematurely ending indexing will still show what's found so far." (match-end-pos (+ vlf-start-pos (position-bytes (point)))) (occur-buffer (generate-new-buffer (concat "*VLF-occur " (file-name-nondirectory - buffer-file-name) + buffer-file-name) "*"))) (line-regexp (concat "\\(?5:[\n\C-m]\\)\\|\\(?10:" regexp "\\)")) @@ -667,7 +809,6 @@ Prematurely ending indexing will still show what's found so far." last-line-pos (point)) (let* ((chunk-start vlf-start-pos) (chunk-end vlf-end-pos) - (vlf-buffer (current-buffer)) (line-pos (line-beginning-position)) (line-text (buffer-substring line-pos (line-end-position)))) @@ -709,8 +850,8 @@ Prematurely ending indexing will still show what's found so far." (unless end-of-file (let ((batch-move (- vlf-end-pos batch-step))) (vlf-move-to-batch (if (< batch-move match-end-pos) - match-end-pos - batch-move) t)) + match-end-pos + batch-move) t)) (goto-char (if (< vlf-start-pos match-end-pos) (or (byte-to-position (- match-end-pos vlf-start-pos)) @@ -720,6 +861,7 @@ Prematurely ending indexing will still show what's found so far." last-line-pos (line-beginning-position)) (progress-reporter-update reporter vlf-end-pos)))) (progress-reporter-done reporter)) + (set-buffer-modified-p nil) (if (zerop total-matches) (progn (with-current-buffer occur-buffer (set-buffer-modified-p nil)) @@ -728,7 +870,7 @@ Prematurely ending indexing will still show what's found so far." (with-current-buffer occur-buffer (goto-char (point-min)) (insert (propertize - (format "%d matches from %d lines for \"%s\" \ + (format "%d matches in %d lines for \"%s\" \ in file: %s" total-matches line regexp file) 'face 'underline)) (set-buffer-modified-p nil) @@ -739,30 +881,11 @@ in file: %s" total-matches line regexp file) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; editing -(defvar vlf-edit-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map text-mode-map) - (define-key map "\C-c\C-c" 'vlf-write) - (define-key map "\C-c\C-q" 'vlf-discard-edit) - (define-key map "\C-v" vlf-mode-map) - map) - "Keymap for command `vlf-edit-mode'.") - -(define-derived-mode vlf-edit-mode vlf-mode "VLF[edit]" - "Major mode for editing large file chunks." - (setq buffer-read-only nil) - (buffer-enable-undo) - (message (substitute-command-keys - "Editing: Type \\[vlf-write] to write chunk \ -or \\[vlf-discard-edit] to discard changes."))) - -(defun vlf-discard-edit () +(defun vlf-refresh () "Discard edit and refresh chunk from file." (interactive) (set-buffer-modified-p nil) - (vlf-move-to-chunk vlf-start-pos vlf-end-pos) - (vlf-mode) - (message "Switched to VLF mode.")) + (vlf-move-to-chunk-2 vlf-start-pos vlf-end-pos)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; saving @@ -776,39 +899,36 @@ If changing size of chunk, shift remaining file content." (y-or-n-p "File has changed since visited or saved. \ Save anyway? "))) (let ((pos (point)) - (size-change (- vlf-encode-size - (setq vlf-encode-size - (length (encode-coding-region - (point-min) (point-max) - buffer-file-coding-system - t)))))) + (size-change (- vlf-end-pos vlf-start-pos + (length (encode-coding-region + (point-min) (point-max) + buffer-file-coding-system t))))) (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)))) - (vlf-move-to-chunk vlf-start-pos vlf-end-pos) - (goto-char pos)) - (vlf-mode)) + (vlf-move-to-chunk-2 vlf-start-pos vlf-end-pos) + (goto-char pos))) t) (defun vlf-file-shift-back (size-change) "Shift file contents SIZE-CHANGE bytes back." (write-region nil nil buffer-file-name vlf-start-pos t) - (buffer-disable-undo) (let ((read-start-pos vlf-end-pos) (coding-system-for-write 'no-conversion) (reporter (make-progress-reporter "Adjusting file content..." vlf-end-pos vlf-file-size))) - (while (vlf-shift-batch read-start-pos (- read-start-pos + (vlf-with-undo-disabled + (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) - (vlf-verify-size) - (insert-char 32 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) + (vlf-verify-size) + (insert-char 32 size-change)) (write-region nil nil buffer-file-name (- vlf-file-size size-change) t) (progress-reporter-done reporter))) @@ -828,26 +948,26 @@ back at WRITE-POS. Return nil if EOF is reached, t otherwise." (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." - (buffer-disable-undo) - (let ((size (+ vlf-batch-size size-change)) + (let ((read-size (max (/ vlf-batch-size 2) size-change)) (read-pos vlf-end-pos) (write-pos vlf-start-pos) (reporter (make-progress-reporter "Adjusting file content..." vlf-start-pos vlf-file-size))) - (when (vlf-shift-batches size read-pos write-pos t) - (setq write-pos (+ read-pos size-change) - read-pos (+ read-pos size)) - (progress-reporter-update reporter write-pos) - (let ((coding-system-for-write 'no-conversion)) - (while (vlf-shift-batches size read-pos write-pos nil) - (setq write-pos (+ read-pos size-change) - read-pos (+ read-pos size)) - (progress-reporter-update reporter write-pos)))) + (vlf-with-undo-disabled + (when (vlf-shift-batches read-size read-pos write-pos t) + (setq write-pos (+ read-pos size-change) + read-pos (+ read-pos read-size)) + (progress-reporter-update reporter write-pos) + (let ((coding-system-for-write 'no-conversion)) + (while (vlf-shift-batches read-size read-pos write-pos nil) + (setq write-pos (+ read-pos size-change) + read-pos (+ read-pos read-size)) + (progress-reporter-update reporter write-pos))))) (progress-reporter-done reporter))) -(defun vlf-shift-batches (size read-pos write-pos hide-read) - "Append SIZE bytes of file starting at READ-POS. +(defun vlf-shift-batches (read-size read-pos write-pos hide-read) + "Append READ-SIZE bytes of file starting at READ-POS. Then write initial buffer content to file at WRITE-POS. If HIDE-READ is non nil, temporarily hide literal read content. Return nil if EOF is reached, t otherwise." @@ -858,8 +978,8 @@ Return nil if EOF is reached, t otherwise." (when read-more (goto-char end-write-pos) (insert-file-contents-literally buffer-file-name nil read-pos - (min vlf-file-size (+ read-pos - size)))) + (min vlf-file-size + (+ read-pos read-size)))) ;; write (if hide-read ; hide literal region if user has to choose encoding (narrow-to-region start-write-pos end-write-pos))