[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] 01/04: * packages/vlf/vlf.el: Add recenter around chunk functiona
From: |
Andrey Kotlarski |
Subject: |
[elpa] 01/04: * packages/vlf/vlf.el: Add recenter around chunk functionality. |
Date: |
Sat, 01 Feb 2014 20:08:37 +0000 |
m00natic pushed a commit to branch master
in repository elpa.
commit bccb5d68b5143a9c453b094f066a3fdda6939e44
Author: Andrey Kotlarski <address@hidden>
Date: Sat Feb 1 18:45:15 2014 +0200
* packages/vlf/vlf.el: Add recenter around chunk functionality.
(vlf-follow-timer): New variable.
(vlf-partial-decode-shown, vlf-min-chunk-size): New constants.
(vlf-with-undo-disabled): Restore previous undo list.
(vlf-shift-undo-list): New function.
(vlf-mode): Stop follow timer if active.
("etags"): Don't apply automatically VLF over TAGS files.
(vlf-move-to-chunk, vlf-move-to-chunk-1, vlf-move-to-chunk-2):
Return number of bytes added to beginning and end for proper
decoding.
(vlf-move-to-chunk-1): Adjust undo list when chunk start has
changed. Ignore moving by just a few bytes.
(vlf-adjust-start, vlf-adjust-end, vlf-insert-content-safe): New
functions.
(vlf-adjust-chunk): Use them. Rename to `vlf-insert-file-contents'.
(vlf-recenter): New function.
(vlf-stop-following, vlf-start-following): New commands.
---
packages/vlf/vlf.el | 454 +++++++++++++++++++++++++++++++++++++--------------
1 files changed, 331 insertions(+), 123 deletions(-)
diff --git a/packages/vlf/vlf.el b/packages/vlf/vlf.el
index c33e11c..42b1c27 100644
--- a/packages/vlf/vlf.el
+++ b/packages/vlf/vlf.el
@@ -74,6 +74,21 @@ Possible values are: nil to never use it;
(defvar vlf-file-size 0 "Total size of presented file.")
(put 'vlf-file-size 'permanent-local t)
+(defvar vlf-follow-timer nil
+ "Contains timer and it's repeat interval if vlf buffer is set to\
+continuously recenter.")
+(put 'vlf-follow-timer 'permanent-local t)
+
+(defconst vlf-partial-decode-shown
+ (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")))
+ "Indicates whether partial decode codes are displayed.")
+
+(defconst vlf-min-chunk-size 16
+ "Minimal number of bytes that can be properly decoded.")
+
(defvar vlf-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "n" 'vlf-next-batch)
@@ -103,12 +118,72 @@ Possible values are: nil to never use it;
(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))
+ `(let ((undo-list buffer-undo-list))
+ (setq buffer-undo-list t)
(unwind-protect (progn ,@body)
- (if undo-enabled
- (buffer-enable-undo)))))
+ (setq buffer-undo-list undo-list))))
+
+(defun vlf-shift-undo-list (n)
+ "Shift undo list element regions by N."
+ (or (eq buffer-undo-list t)
+ (setq buffer-undo-list
+ (nreverse
+ (let ((min (point-min))
+ undo-list)
+ (catch 'end
+ (dolist (el buffer-undo-list undo-list)
+ (push
+ (cond
+ ((null el) nil)
+ ((numberp el) (let ((pos (+ el n)))
+ (if (< pos min)
+ (throw 'end undo-list)
+ pos)))
+ (t (let ((head (car el)))
+ (cond ((numberp head)
+ (let ((beg (+ head n)))
+ (if (< beg min)
+ (throw 'end undo-list)
+ (cons beg (+ (cdr el) n)))))
+ ((stringp head)
+ (let* ((pos (cdr el))
+ (positive (< 0 pos))
+ (new (+ (abs pos) n)))
+ (if (< new min)
+ (throw 'end undo-list)
+ (cons head (if positive
+ new
+ (- new))))))
+ ((null head)
+ (let ((beg (+ (nth 3 el) n)))
+ (if (< beg min)
+ (throw 'end undo-list)
+ (cons
+ nil
+ (cons
+ (cadr el)
+ (cons
+ (nth 2 el)
+ (cons beg
+ (+ (cddr
+ (cddr el)) n))))))))
+ ((and (eq head 'apply)
+ (numberp (cadr el)))
+ (let ((beg (+ (nth 2 el) n)))
+ (if (< beg min)
+ (throw 'end undo-list)
+ (cons
+ 'apply
+ (cons
+ (cadr el)
+ (cons
+ beg
+ (cons
+ (+ (nth 3 el) n)
+ (cons (nth 4 el)
+ (cdr (last el))))))))))
+ (t el)))))
+ undo-list))))))))
(define-minor-mode vlf-mode
"Mode to browse large files in."
@@ -126,11 +201,13 @@ Possible values are: nil to never use it;
(vlf-get-file-size buffer-file-truename))
(set (make-local-variable 'vlf-start-pos) 0)
(set (make-local-variable 'vlf-end-pos) 0)
+ (set (make-local-variable 'vlf-follow-timer) nil)
(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)
+ (vlf-stop-following)
(when (or (not large-file-warning-threshold)
(< vlf-file-size large-file-warning-threshold)
(y-or-n-p (format "Load whole file (%s)? "
@@ -251,6 +328,22 @@ OP-TYPE specifies the file operation being performed over
FILENAME."
((memq char '(?a ?A))
(error "Aborted"))))))))
+;; never apply VLF over TAGS files
+;;;###autoload
+(eval-after-load "etags"
+ '(progn
+ (defadvice tags-verify-table (around vlf-tags-verify-table
+ compile activate)
+ "Temporarily disable `vlf-mode'."
+ (let ((vlf-application nil))
+ ad-do-it))
+
+ (defadvice tag-find-file-of-tag-noselect
+ (around vlf-tag-find-file-of-tag compile activate)
+ "Temporarily disable `vlf-mode'."
+ (let ((vlf-application nil))
+ ad-do-it))))
+
;; scroll auto batching
(defadvice scroll-up (around vlf-scroll-up
activate compile)
@@ -407,158 +500,228 @@ When given MINIMAL flag, skip non important 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.
-If same as current chunk is requested, do nothing."
+If same as current chunk is requested, do nothing.
+Return number of bytes moved back for proper decoding and number of
+bytes added to the end."
(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)))))
+ (let ((shifts (vlf-move-to-chunk-1 start end)))
+ (and shifts (not minimal)
+ (vlf-update-buffer-name))
+ shifts)))
(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)))
+Return number of bytes moved back for proper decoding and number of
+bytes added to the end."
+ (let* ((modified (buffer-modified-p))
+ (start (max 0 start))
+ (end (min end vlf-file-size))
+ (edit-end (if modified
+ (+ vlf-start-pos
+ (length (encode-coding-region
+ (point-min) (point-max)
+ buffer-file-coding-system t)))
+ vlf-end-pos)))
(cond
((and (= start vlf-start-pos) (= end edit-end))
- (unless modified
- (vlf-move-to-chunk-2 start end)
- t))
+ (or modified (vlf-move-to-chunk-2 start end)))
((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))
+ (vlf-move-to-chunk-2 start end)))
((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 (- (if (zerop vlf-end-pos)
- vlf-file-size
- 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 start (- start shift-start))
- (goto-char (or (byte-to-position (- pos start))
- (byte-to-position (- pos vlf-start-pos))
- (point-max)))
- (setq vlf-start-pos start
- vlf-end-pos (+ end shift-end)))
- (set-buffer-modified-p modified)
- t))))
+ (let ((shift-start 0)
+ (shift-end 0))
+ (let ((pos (+ (position-bytes (point)) vlf-start-pos))
+ (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 (- (if (zerop vlf-end-pos)
+ vlf-file-size
+ vlf-end-pos)
+ del-len))
+ (vlf-with-undo-disabled
+ (delete-region del-pos (point-max)))))
+ ((< edit-end end)
+ (if (and (not vlf-partial-decode-shown)
+ (< (- end vlf-end-pos) 4))
+ (setq end vlf-end-pos)
+ (vlf-with-undo-disabled
+ (setq shift-end (cdr (vlf-insert-file-contents
+ vlf-end-pos end nil t
+ (point-max))))))))
+ (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))
+ (vlf-shift-undo-list (- 1 del-pos))))
+ ((< start vlf-start-pos)
+ (if (and (not vlf-partial-decode-shown)
+ (< (- vlf-start-pos start) 4))
+ (setq start vlf-start-pos)
+ (let ((edit-end-pos (point-max)))
+ (vlf-with-undo-disabled
+ (setq shift-start (car (vlf-insert-file-contents
+ start vlf-start-pos
+ t nil edit-end-pos)))
+ (goto-char (point-min))
+ (insert (delete-and-extract-region
+ edit-end-pos (point-max))))
+ (vlf-shift-undo-list (- (point-max) edit-end-pos))))))
+ (setq start (- start shift-start))
+ (goto-char (or (byte-to-position (- pos start))
+ (byte-to-position (- pos vlf-start-pos))
+ (point-max)))
+ (setq vlf-start-pos start
+ vlf-end-pos (+ end shift-end)))
+ (set-buffer-modified-p modified)
+ (cons shift-start shift-end))))))
(defun vlf-move-to-chunk-2 (start end)
- "Unconditionally move to chunk determined by START END."
+ "Unconditionally move to chunk determined by START END.
+Return number of bytes moved back for proper decoding and number of
+bytes added to the 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))))
- (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))
+ (let (shifts)
+ (let ((inhibit-read-only t)
+ (pos (position-bytes (point))))
+ (vlf-with-undo-disabled
+ (erase-buffer)
+ (setq shifts (vlf-insert-file-contents vlf-start-pos
+ vlf-end-pos t t)
+ 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))
+ (point-max)))))
+ (set-buffer-modified-p nil)
+ (setq buffer-undo-list nil)
+ (set-visited-file-modtime)
+ shifts))
-(defun vlf-adjust-chunk (start end &optional adjust-start adjust-end
- position)
+(defun vlf-insert-file-contents (start end adjust-start adjust-end
+ &optional 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."
+ (setq adjust-start (and adjust-start (not (zerop start)))
+ adjust-end (and adjust-end (< end vlf-file-size))
+ position (or position (point-min)))
(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
- position (point-max)
- buffer-file-coding-system
- t))))))
- (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))))
+ (setq shift-start (vlf-adjust-start start end position
+ adjust-end)
+ start (- start shift-start))
+ (setq shift-end (vlf-insert-content-safe start end position)
+ end (+ end shift-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))))))))
+ (setq shift-end (+ shift-end
+ (vlf-adjust-end start end position))))
(cons shift-start shift-end)))
+(defun vlf-adjust-start (start end position adjust-end)
+ "Adjust chunk beginning at absolute START to END till content can\
+be properly decoded. Use buffer POSITION as start.
+ADJUST-END is non-nil if end would be adjusted later.
+Return number of bytes moved back for proper decoding."
+ (let* ((min-end (min end (+ start vlf-min-chunk-size)))
+ (chunk-size (- min-end start))
+ (strict (and (not adjust-end) (= min-end end)))
+ (shift (vlf-insert-content-safe start min-end position t)))
+ (setq start (- start shift))
+ (while (and (not (zerop start))
+ (< shift 3)
+ (let ((diff (- chunk-size
+ (length
+ (encode-coding-region
+ position (point-max)
+ buffer-file-coding-system t)))))
+ (cond (strict (not (zerop diff)))
+ (vlf-partial-decode-shown
+ (or (< diff -3) (< 0 diff)))
+ (t (or (< diff 0) (< 3 diff))))))
+ (setq shift (1+ shift)
+ start (1- start)
+ chunk-size (1+ chunk-size))
+ (delete-region position (point-max))
+ (insert-file-contents buffer-file-name nil start min-end))
+ (unless (= min-end end)
+ (delete-region position (point-max))
+ (insert-file-contents buffer-file-name nil start end))
+ shift))
+
+(defun vlf-adjust-end (start end position)
+ "Adjust chunk end at absolute START to END till content can be\
+properly decoded starting at POSITION.
+Return number of bytes added for proper decoding."
+ (let ((shift 0))
+ (if vlf-partial-decode-shown
+ (let ((new-pos (max position
+ (- (point-max) vlf-min-chunk-size))))
+ (if (< position new-pos)
+ (setq start (+ start (length (encode-coding-region
+ position new-pos
+ buffer-file-coding-system
+ t)))
+ position new-pos))))
+ (let ((chunk-size (- end start)))
+ (goto-char (point-max))
+ (while (and (< shift 3)
+ (< end vlf-file-size)
+ (or (eq (char-charset (preceding-char)) 'eight-bit)
+ (/= chunk-size
+ (length (encode-coding-region
+ position (point-max)
+ buffer-file-coding-system t)))))
+ (setq shift (1+ shift)
+ end (1+ end)
+ chunk-size (1+ chunk-size))
+ (delete-region position (point-max))
+ (insert-file-contents buffer-file-name nil start end)
+ (goto-char (point-max))))
+ shift))
+
+(defun vlf-insert-content-safe (start end position &optional shift-start)
+ "Insert file content from absolute START to END of file at\
+POSITION. Adjust start if SHIFT-START is non nil, end otherwise.
+Clean up if no characters are inserted."
+ (goto-char position)
+ (let ((shift 0))
+ (while (and (< shift 3)
+ (zerop (cadr (insert-file-contents buffer-file-name
+ nil start end)))
+ (if shift-start
+ (not (zerop start))
+ (< end vlf-file-size)))
+ ;; TODO: this seems like regression after Emacs 24.3
+ (message "Buffer content may be broken")
+ (setq shift (1+ shift))
+ (if shift-start
+ (setq start (1- start))
+ (setq end (1+ end)))
+ (delete-region position (point-max)))
+ shift))
+
(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."
@@ -568,6 +731,51 @@ This seems to be the case with GNU/Emacs before 24.4."
(string-lessp emacs-version "24.3.5"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; follow point
+
+(defun vlf-recenter (vlf-buffer)
+ "Recenter chunk around current point in VLF-BUFFER."
+ (and vlf-follow-timer
+ (eq (current-buffer) vlf-buffer)
+ (or (pos-visible-in-window-p (point-min))
+ (pos-visible-in-window-p (point-max)))
+ (let ((current-pos (+ vlf-start-pos (position-bytes (point))))
+ (half-batch (/ vlf-batch-size 2)))
+ (if (buffer-modified-p)
+ (progn
+ (let ((edit-end (+ (position-bytes (point-max))
+ vlf-start-pos)))
+ (vlf-move-to-chunk (min vlf-start-pos
+ (- current-pos half-batch))
+ (max edit-end
+ (+ current-pos half-batch))))
+ (goto-char (byte-to-position (- current-pos
+ vlf-start-pos))))
+ (vlf-move-to-batch (- current-pos half-batch))
+ (and (< half-batch current-pos)
+ (< half-batch (- vlf-file-size current-pos))
+ (goto-char (byte-to-position (- current-pos
+ vlf-start-pos))))))))
+
+(defun vlf-stop-following ()
+ "Stop continuous recenter."
+ (interactive)
+ (when vlf-follow-timer
+ (cancel-timer (car vlf-follow-timer))
+ (setq vlf-follow-timer nil)))
+
+(defun vlf-start-following (interval)
+ "Continuously recenter chunk around point every INTERVAL seconds."
+ (interactive "nNumber of seconds: ")
+ (when vlf-mode
+ (vlf-stop-following)
+ (setq vlf-follow-timer (cons (run-with-idle-timer interval interval
+ 'vlf-recenter
+ (current-buffer))
+ interval))
+ (add-hook 'kill-buffer-hook 'vlf-stop-following nil t)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; search
(defun vlf-re-search (regexp count backward batch-step)