From ca8ba42ec41377b733a035b11735e317f1783679 Mon Sep 17 00:00:00 2001 From: Andrey Kotlarski Date: Mon, 15 Apr 2013 00:57:10 +0300 Subject: [PATCH] Fix position handling to correctly deal with multibyte characters. --- vlfi.el | 151 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 60 deletions(-) diff --git a/vlfi.el b/vlfi.el index 8dc8559..85d3262 100644 --- a/vlfi.el +++ b/vlfi.el @@ -252,7 +252,7 @@ When prefix argument is negative (setq end vlfi-file-size)))) (let ((inhibit-read-only t) (do-append (< append 0)) - (pos (point))) + (pos (position-bytes (point)))) (if do-append (goto-char (point-max)) (setq vlfi-start-pos (- end vlfi-batch-size)) @@ -261,9 +261,9 @@ When prefix argument is negative vlfi-end-pos vlfi-start-pos) end) - (goto-char pos)) - (setq vlfi-end-pos end)) - (vlfi-adjust-chunk) + (setq vlfi-end-pos end) + (goto-char (or (byte-to-position (+ pos (vlfi-adjust-chunk))) + (point-max))))) (set-visited-file-modtime) (set-buffer-modified-p nil) (vlfi-update-buffer-name)) @@ -281,7 +281,8 @@ When prefix argument is negative (start (max 0 (- vlfi-start-pos (* vlfi-batch-size (abs prepend))))) (do-prepend (< prepend 0)) - (pos (- (point-max) (point)))) + (pos (- (position-bytes (point-max)) + (position-bytes (point))))) (if do-prepend (goto-char (point-min)) (setq vlfi-end-pos (+ start vlfi-batch-size)) @@ -291,8 +292,10 @@ When prefix argument is negative vlfi-start-pos vlfi-end-pos)) (setq vlfi-start-pos start) - (vlfi-adjust-chunk) - (goto-char (- (point-max) pos))) + (setq pos (+ pos (vlfi-adjust-chunk))) + (goto-char (or (byte-to-position (- (position-bytes (point-max)) + pos)) + (point-max)))) (set-visited-file-modtime) (set-buffer-modified-p nil) (vlfi-update-buffer-name)) @@ -308,12 +311,12 @@ When given MINIMAL flag, skip non important operations." vlfi-end-pos (min vlfi-end-pos vlfi-file-size) vlfi-start-pos (max 0 (- vlfi-end-pos vlfi-batch-size)))) (let ((inhibit-read-only t) - (pos (point))) + (pos (position-bytes (point)))) (erase-buffer) (insert-file-contents buffer-file-name nil vlfi-start-pos vlfi-end-pos) - (vlfi-adjust-chunk) - (goto-char pos)) + (goto-char (or (byte-to-position (+ pos (vlfi-adjust-chunk))) + (point-max)))) (set-buffer-modified-p nil) (unless minimal (set-visited-file-modtime) @@ -327,41 +330,43 @@ When given MINIMAL flag, skip non important operations." (setq vlfi-start-pos (max 0 start) vlfi-end-pos (min end vlfi-file-size)) (let ((inhibit-read-only t) - (pos (point))) + (pos (position-bytes (point)))) (erase-buffer) (insert-file-contents buffer-file-name nil vlfi-start-pos vlfi-end-pos) - (vlfi-adjust-chunk) - (goto-char pos)) + (goto-char (or (byte-to-position (+ pos (vlfi-adjust-chunk))) + (point-max)))) (set-buffer-modified-p nil) (unless minimal (set-visited-file-modtime) (vlfi-update-buffer-name))) (defun vlfi-adjust-chunk () - "Adjust chunk beginning until content can be properly decoded." - (or (zerop vlfi-start-pos) - (let ((pos (point))) - (while (/= (- vlfi-end-pos vlfi-start-pos) - (length (encode-coding-region - (point-min) (point-max) - buffer-file-coding-system t))) - - (setq pos (1- pos) - vlfi-start-pos (1- vlfi-start-pos)) - (let ((inhibit-read-only t)) - (erase-buffer) - (insert-file-contents buffer-file-name nil - vlfi-start-pos vlfi-end-pos))) - (set-buffer-modified-p nil) - (goto-char pos)))) + "Adjust chunk beginning until content can be properly decoded. +Return number of bytes moved back for this to happen." + (let ((shift 0)) + (while (and (not (zerop vlfi-start-pos)) + (/= (- vlfi-end-pos vlfi-start-pos) + (length (encode-coding-region + (point-min) (point-max) + buffer-file-coding-system t)))) + (setq shift (1+ shift) + vlfi-start-pos (1- vlfi-start-pos)) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert-file-contents buffer-file-name nil + vlfi-start-pos vlfi-end-pos))) + (set-buffer-modified-p nil) + shift)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; search (defun vlfi-re-search (regexp count backward) "Search for REGEXP COUNT number of times forward or BACKWARD." - (let* ((match-start-pos (+ vlfi-start-pos (point))) + (let* ((match-chunk-start vlfi-start-pos) + (match-chunk-end vlfi-end-pos) + (match-start-pos (+ vlfi-start-pos (position-bytes (point)))) (match-end-pos match-start-pos) (to-find count) (search-reporter (make-progress-reporter @@ -377,10 +382,14 @@ When given MINIMAL flag, skip non important operations." (while (not (zerop to-find)) (cond ((re-search-backward regexp nil t) (setq to-find (1- to-find) + match-chunk-start vlfi-start-pos + match-chunk-end vlfi-end-pos match-start-pos (+ vlfi-start-pos - (match-beginning 0)) + (position-bytes + (match-beginning 0))) match-end-pos (+ vlfi-start-pos - (match-end 0)))) + (position-bytes + (match-end 0))))) ((zerop vlfi-start-pos) (throw 'end-of-file nil)) (t (let ((batch-move (- vlfi-start-pos @@ -392,18 +401,24 @@ When given MINIMAL flag, skip non important operations." batch-move) t)) (goto-char (if (< match-start-pos vlfi-end-pos) - (- match-start-pos - vlfi-start-pos) + (or (byte-to-position + (- match-start-pos + vlfi-start-pos)) + (point-max)) (point-max))) (progress-reporter-update search-reporter vlfi-start-pos)))) (while (not (zerop to-find)) (cond ((re-search-forward regexp nil t) (setq to-find (1- to-find) + match-chunk-start vlfi-start-pos + match-chunk-end vlfi-end-pos match-start-pos (+ vlfi-start-pos - (match-beginning 0)) + (position-bytes + (match-beginning 0))) match-end-pos (+ vlfi-start-pos - (match-end 0)))) + (position-bytes + (match-end 0))))) ((= vlfi-end-pos vlfi-file-size) (throw 'end-of-file nil)) (t (let ((batch-move (- vlfi-end-pos batch-step))) @@ -412,39 +427,56 @@ When given MINIMAL flag, skip non important operations." match-end-pos batch-move) t)) (goto-char (if (< vlfi-start-pos match-end-pos) - (- match-end-pos vlfi-start-pos) + (or (byte-to-position + (- match-end-pos + vlfi-start-pos)) + (point-max)) (point-min))) (progress-reporter-update search-reporter vlfi-end-pos))))) (progress-reporter-done search-reporter)) (if backward - (vlfi-goto-match match-end-pos match-start-pos + (vlfi-goto-match match-chunk-start match-chunk-end + match-end-pos match-start-pos count to-find) - (vlfi-goto-match match-start-pos match-end-pos + (vlfi-goto-match match-chunk-start match-chunk-end + match-start-pos match-end-pos count to-find))))) -(defun vlfi-goto-match (match-pos-start match-pos-end count to-find) - "Move to chunk surrounding MATCH-POS-START and MATCH-POS-END. +(defun vlfi-goto-match (match-chunk-start match-chunk-end + 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 successful. Return nil if nothing found." - (let ((success (zerop to-find))) - (if success - (vlfi-update-buffer-name) - (vlfi-move-to-batch (- match-pos-start (/ vlfi-batch-size 2)))) - (let* ((match-end (- match-pos-end vlfi-start-pos)) - (overlay (make-overlay (- match-pos-start vlfi-start-pos) - match-end))) - (overlay-put overlay 'face 'region) - (or success (goto-char match-end)) - (prog1 (cond (success t) - ((< to-find count) - (message "Moved to the %d match which is last" - (- count to-find)) - t) - (t (message "Not found") - nil)) + (if (= count to-find) + (progn (vlfi-move-to-chunk match-chunk-start match-chunk-end) + (goto-char (or (byte-to-position (- match-pos-start + vlfi-start-pos)) + (point-max))) + (message "Not found") + nil) + (let ((success (zerop to-find))) + (if success + (vlfi-update-buffer-name) + (vlfi-move-to-chunk match-chunk-start match-chunk-end)) + (let* ((match-end (or (byte-to-position (- match-pos-end + vlfi-start-pos)) + (point-max))) + (overlay (make-overlay (byte-to-position + (- match-pos-start + vlfi-start-pos)) + match-end))) + (overlay-put overlay 'face 'region) + (unless success + (goto-char match-end) + (message "Moved to the %d match which is last" + (- count to-find))) (sit-for 0.1) - (delete-overlay overlay))))) + (delete-overlay overlay) + t)))) (defun vlfi-re-search-forward (regexp count) "Search forward for REGEXP prefix COUNT number of times. @@ -515,8 +547,7 @@ or \\[vlfi-discard-edit] to discard changes."))) "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 'vlfi-mode) - (buffer-modified-p) + (when (and (buffer-modified-p) (or (verify-visited-file-modtime) (y-or-n-p "File has changed since visited or saved. \ Save anyway? ")))