X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/fcec6530dc02bcb0a24a5d9fb27630e7fa8358f5..dc89a3ce43357ef755a295c05b0b3bce63c65c8c:/packages/vlf/vlf.el diff --git a/packages/vlf/vlf.el b/packages/vlf/vlf.el index 6816f6f3f..2e3c9a1b8 100644 --- a/packages/vlf/vlf.el +++ b/packages/vlf/vlf.el @@ -1,11 +1,14 @@ -;;; vlf.el --- View Large Files +;;; vlf.el --- View Large Files -*- lexical-binding: t -*- -;; Copyright (C) 2006, 2012 Free Software Foundation, Inc. +;; Copyright (C) 2006, 2012-2014 Free Software Foundation, Inc. -;; Version: 0.2 +;; Version: 1.5 ;; Keywords: large files, utilities +;; Maintainer: Andrey Kotlarski ;; Authors: 2006 Mathias Dahl ;; 2012 Sam Steingold +;; 2013-2014 Andrey Kotlarski +;; 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 @@ -23,123 +26,297 @@ ;; Boston, MA 02111-1307, USA. ;;; Commentary: -;; -;; After reading the Nth post on Gnu Emacs Help about Viewing Large -;; Files in Emacs, it itched so much that I decided to make a try. It -;; helped quite a lot when Kevin Rodgers posted a snippet on how to -;; use `insert-file-contents' to extract part of a file. +;; 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-integrate) + +;; 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: -(defgroup vlf nil - "View Large Files in Emacs." - :prefix "vlf-" - :group 'files) +(defgroup vlf nil "View Large Files in Emacs." + :prefix "vlf-" :group 'files) + +(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 1024 - "Defines how large each batch of file data is (in bytes)." - :type 'integer - :group 'vlf) +(require 'vlf-base) -;; Keep track of file position. -(defvar vlf-start-pos) -(defvar vlf-end-pos) -(defvar vlf-file-size) +(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-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 [M-next] 'vlf-next-batch) - (define-key map [M-prior] 'vlf-prev-batch) - (define-key map (kbd "C-+") 'vlf-change-batch-size) + (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 "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'.") -(define-derived-mode vlf-mode special-mode "VLF" +(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." - (setq buffer-read-only t) - (set-buffer-modified-p nil) - (make-local-variable 'vlf-batch-size) - (make-local-variable 'vlf-start-pos) - (make-local-variable 'vlf-file-size)) + :lighter " VLF" :group 'vlf :keymap vlf-prefix-map + (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) + (let ((hexl (derived-mode-p 'hexl-mode))) + (if hexl (hexl-mode-exit)) + (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))) + (if hexl (hexl-mode))) + (rename-buffer (file-name-nondirectory buffer-file-name) t)) + (t (setq vlf-mode t)))) -(defun vlf-change-batch-size (decrease) - "Change the buffer-local value of `vlf-batch-size'. -Normally, the value is doubled; -with the prefix argument it is halved." - (interactive "P") - (or (assq 'vlf-batch-size (buffer-local-variables)) - (error "%s is not local in this buffer" 'vlf-batch-size)) - (setq vlf-batch-size - (if decrease - (/ vlf-batch-size 2) - (* vlf-batch-size 2))) - (vlf-update-buffer-name)) - -(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-start-pos vlf-end-pos vlf-batch-size)) - -(defun vlf-update-buffer-name () - "Update the current buffer name." - (rename-buffer (vlf-format-buffer-name) 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) + "View Large FILE in batches. +You can customize number of bytes displayed by customizing +`vlf-batch-size'. +Return newly created buffer." + (interactive "fFile to open: ") + (let ((vlf-buffer (generate-new-buffer "*vlf*"))) + (set-buffer vlf-buffer) + (set-visited-file-name file) + (set-buffer-modified-p nil) + (vlf-mode 1) + (switch-to-buffer vlf-buffer) + vlf-buffer)) (defun vlf-next-batch (append) "Display the next batch of file data. -Append to the existing buffer when the prefix argument is supplied." - (interactive "P") - (when (= vlf-end-pos vlf-file-size) - (error "Already at EOF")) - (let ((inhibit-read-only t) - (end (min vlf-file-size (+ vlf-end-pos vlf-batch-size)))) - (goto-char (point-max)) - ;; replacing `erase-buffer' with replace arg to `insert-file-contents' - ;; hangs emacs - (unless append (erase-buffer)) - (insert-file-contents buffer-file-name nil vlf-end-pos end) - (unless append - (setq vlf-start-pos vlf-end-pos)) - (setq vlf-end-pos end) - (set-buffer-modified-p nil) - (vlf-update-buffer-name))) +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) + (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. -Prepend to the existing buffer when the prefix argument is supplied." +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")) + (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 +(defun vlf-hexl-before (&optional operation) + "Temporarily disable `hexl-mode' for OPERATION." + (when (derived-mode-p 'hexl-mode) + (hexl-mode-exit) + (set (make-local-variable 'vlf-restore-hexl-mode) operation))) + +(defun vlf-hexl-after (&optional operation) + "Re-enable `hexl-mode' if active before OPERATION." + (when (and (boundp 'vlf-restore-hexl-mode) + (eq vlf-restore-hexl-mode operation)) + (hexl-mode) + (kill-local-variable 'vlf-restore-hexl-mode))) + +(add-hook 'vlf-before-batch-functions 'vlf-hexl-before) +(add-hook 'vlf-after-batch-functions 'vlf-hexl-after) +(add-hook 'vlf-before-chunk-update 'vlf-hexl-before) +(add-hook 'vlf-after-chunk-update 'vlf-hexl-after) + +(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)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; 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") - (when (= vlf-start-pos 0) - (error "Already at BOF")) - (let ((inhibit-read-only t) - (start (max 0 (- vlf-start-pos vlf-batch-size)))) - (goto-char (point-min)) - (unless prepend (erase-buffer)) - (insert-file-contents buffer-file-name nil start vlf-start-pos) - (unless prepend - (setq vlf-end-pos vlf-start-pos)) - (setq vlf-start-pos start) + (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-batch-size))) + (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-move-to-batch 0)) + +(defun vlf-end-of-file () + "Jump to end of file content." + (interactive) + (vlf-verify-size) + (vlf-move-to-batch vlf-file-size)) + +(defun vlf-revert (&optional _ignore-auto noconfirm) + "Revert current chunk. Ignore _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-update-buffer-name))) + (vlf-move-to-chunk-2 vlf-start-pos vlf-end-pos))) -(defun vlf (file) - "View a Large File in Emacs. -FILE is the file to open. -Batches of the file data from FILE will be displayed in a - read-only buffer. -You can customize the number of bytes to - display by customizing `vlf-batch-size'." - (interactive "fFile to open: ") - (with-current-buffer (generate-new-buffer "*vlf*") - (setq buffer-file-name file - vlf-start-pos 0 - vlf-end-pos vlf-batch-size - vlf-file-size (nth 7 (file-attributes file))) - (vlf-update-buffer-name) - (insert-file-contents buffer-file-name nil - vlf-start-pos vlf-end-pos nil) - (vlf-mode) - (display-buffer (current-buffer)))) +(defun vlf-jump-to-chunk (n) + "Go to to chunk N." + (interactive "nGoto to chunk: ") + (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 &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) + (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) + (let ((start (+ vlf-start-pos (position-bytes (point)) -1))) + (vlf-move-to-chunk start (+ start vlf-batch-size))) + (goto-char (point-min))) (provide 'vlf)