1 ;;; vlf-ediff.el --- VLF ediff functionality -*- lexical-binding: t -*-
3 ;; Copyright (C) 2014 Free Software Foundation, Inc.
5 ;; Keywords: large files, compare, ediff
6 ;; Author: Andrey Kotlarski <m00naticus@gmail.com>
7 ;; URL: https://github.com/m00natic/vlfi
9 ;; This file is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; This file is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to
21 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
25 ;; This package provides ediff functionality for VLF managed buffers
26 ;; in face of the `vlf-ediff-buffers' and `vlf-ediff-files' commands.
33 (defvar vlf-ediff-session nil
34 "If non nil, specifies that ediff is done over VLF buffers.")
35 (make-variable-buffer-local 'vlf-ediff-session)
38 (defun vlf-ediff-buffers (buffer-A buffer-B)
39 "Run batch by batch ediff over VLF buffers BUFFER-A and BUFFER-B.
40 Batch size is determined by the size in BUFFER-A.
41 Requesting next or previous difference at the end or beginning
42 respectively of difference list, runs ediff over the adjacent chunks."
45 (list (setq bf (read-buffer "Buffer A to compare: "
46 (ediff-other-buffer "") t))
47 (read-buffer "Buffer B to compare: "
49 ;; realign buffers so that two visible bufs will be
51 (save-window-excursion (other-window 1))
52 (ediff-other-buffer bf))
55 (setq buffer-A (current-buffer)) ;names change, so reference by buffer object
56 (let ((batch-size vlf-batch-size))
58 (setq buffer-B (current-buffer))
59 (vlf-set-batch-size batch-size))
60 (ediff-buffers buffer-A buffer-B
61 '((lambda () (setq vlf-ediff-session t)
62 (vlf-ediff-next ediff-buffer-A ediff-buffer-B
67 (defun vlf-ediff-files (file-A file-B batch-size)
68 "Run batch by batch ediff over FILE-A and FILE-B.
69 Files are processed with VLF with BATCH-SIZE chunks.
70 Requesting next or previous difference at the end or beginning
71 respectively of difference list, runs ediff over the adjacent chunks."
73 (let ((dir-A (if ediff-use-last-dir
77 (list (setq f (ediff-read-file-name
80 (ediff-get-default-file-name)
82 (ediff-read-file-name "File B to compare"
84 (if ediff-use-last-dir
86 (file-name-directory f)))
90 (ediff-abbreviate-file-name
92 (file-name-nondirectory f)
94 (ediff-get-default-file-name f 1)))
95 (read-number "Batch size (in bytes): " vlf-batch-size))))
96 (let ((buffer-A (vlf file-A)))
98 (vlf-set-batch-size batch-size)
99 (let ((buffer-B (vlf file-B)))
100 (vlf-ediff-buffers buffer-A buffer-B))))
102 (defadvice ediff-next-difference (around vlf-ediff-next-difference
104 "Move to the next VLF chunk and search for difference if at the end\
106 (if (and vlf-ediff-session
107 (<= (1- ediff-number-of-differences)
108 ediff-current-difference))
109 (let ((buffer-A ediff-buffer-A)
110 (buffer-B ediff-buffer-B)
111 (ediff-buffer (current-buffer)))
113 (set-buffer buffer-A)
115 (set-buffer buffer-B)
117 (vlf-ediff-next buffer-A buffer-B ediff-buffer
119 (or (zerop ediff-number-of-differences)
120 (ediff-jump-to-difference 1)))
123 (defadvice ediff-previous-difference (around vlf-ediff-prev-difference
125 "Move to the previous VLF chunk and search for difference if at the\
126 beginning of difference list."
127 (if (and vlf-ediff-session
128 (<= ediff-current-difference 0))
129 (let ((buffer-A ediff-buffer-A)
130 (buffer-B ediff-buffer-B)
131 (ediff-buffer (current-buffer)))
133 (set-buffer buffer-A)
135 (set-buffer buffer-B)
137 (vlf-ediff-next buffer-A buffer-B ediff-buffer
139 (or (zerop ediff-number-of-differences)
140 (ediff-jump-to-difference -1)))
143 (defun vlf-next-chunk ()
144 "Move to next chunk."
145 (vlf-move-to-chunk vlf-end-pos (+ vlf-end-pos vlf-batch-size) t))
147 (defun vlf-prev-chunk ()
148 "Move to previous chunk."
149 (vlf-move-to-chunk (- vlf-start-pos vlf-batch-size) vlf-start-pos t))
151 (defun vlf-ediff-next (buffer-A buffer-B ediff-buffer
153 "Find next pair of chunks that differ in BUFFER-A and BUFFER-B\
154 governed by EDIFF-BUFFER. NEXT-FUNC is used to jump to the next
155 logical chunks in case there is no difference at the current ones."
156 (set-buffer buffer-A)
157 (run-hook-with-args 'vlf-before-batch-functions 'ediff)
158 (setq buffer-A (current-buffer)) ;names change, so reference by buffer object
159 (let ((end-A (= vlf-start-pos vlf-end-pos))
160 (chunk-A (cons vlf-start-pos vlf-end-pos))
161 (point-max-A (point-max))
162 (font-lock-A font-lock-mode)
163 (min-file-size vlf-file-size)
164 (forward-p (eq next-func 'vlf-next-chunk)))
166 (set-buffer buffer-B)
167 (run-hook-with-args 'vlf-before-batch-functions 'ediff)
168 (setq buffer-B (current-buffer)
169 min-file-size (min min-file-size vlf-file-size))
170 (let ((tramp-verbose (if (boundp 'tramp-verbose)
171 (min tramp-verbose 2)))
172 (end-B (= vlf-start-pos vlf-end-pos))
173 (chunk-B (cons vlf-start-pos vlf-end-pos))
174 (font-lock-B font-lock-mode)
176 (reporter (make-progress-reporter
177 "Searching for difference..."
178 (if forward-p vlf-start-pos
179 (- min-file-size vlf-end-pos))
184 (while (and (or (not end-A) (not end-B))
185 (or (zerop (compare-buffer-substrings
186 buffer-A (point-min) point-max-A
187 buffer-B (point-min) (point-max)))
188 (with-current-buffer ediff-buffer
190 (and (not end-A) (not end-B)
191 (vlf-ediff-refine buffer-A
193 (zerop ediff-number-of-differences))))
195 (setq end-B (= vlf-start-pos vlf-end-pos))
196 (with-current-buffer buffer-A
198 (setq end-A (= vlf-start-pos vlf-end-pos)
199 point-max-A (point-max)))
200 (progress-reporter-update reporter
201 (if forward-p vlf-end-pos
204 (progress-reporter-done reporter)
205 (if (or (not end-A) (not end-B))
206 (progn (vlf-update-buffer-name)
207 (set-buffer buffer-A)
208 (vlf-update-buffer-name))
210 (let ((max-file-size vlf-file-size))
211 (vlf-move-to-chunk (- max-file-size vlf-batch-size)
213 (set-buffer buffer-A)
214 (setq max-file-size (max max-file-size
216 (vlf-move-to-chunk (- max-file-size
219 (vlf-beginning-of-file)
220 (set-buffer buffer-A)
221 (vlf-beginning-of-file))
222 (set-buffer ediff-buffer)
224 (if (or (not forward-p)
225 (and (not end-A) (not end-B)))
226 (vlf-ediff-refine buffer-A buffer-B)))
229 (set-buffer buffer-A)
230 (set-buffer-modified-p nil)
231 (vlf-move-to-chunk (car chunk-A) (cdr chunk-A))
232 (set-buffer buffer-B)
233 (set-buffer-modified-p nil)
234 (vlf-move-to-chunk (car chunk-B) (cdr chunk-B))
235 (set-buffer ediff-buffer)
237 (vlf-ediff-refine buffer-A buffer-B))
238 (set-buffer buffer-A)
239 (if font-lock-A (font-lock-mode 1))
240 (run-hook-with-args 'vlf-after-batch-functions 'ediff)
241 (set-buffer buffer-B)
242 (if font-lock-B (font-lock-mode 1))
243 (run-hook-with-args 'vlf-after-batch-functions 'ediff)))))
245 (defun vlf-ediff-refine (buffer-A buffer-B)
246 "Try to minimize differences between BUFFER-A and BUFFER-B.
247 This can happen if first or last difference is at the start/end of
249 (or (zerop ediff-number-of-differences)
250 (let ((adjust-p (vlf-ediff-adjust buffer-A buffer-B)))
251 (setq adjust-p (or (vlf-ediff-adjust buffer-A buffer-B t)
253 (if adjust-p (ediff-update-diffs)))))
255 (defun vlf-ediff-adjust (buf-A buf-B &optional end)
256 "Additionally adjust buffer borders for BUF-A and BUF-B.
257 Adjust beginning if END is nil. Return t if refining is needed,
259 (let* ((diff-num (if end (1- ediff-number-of-differences) 0))
260 (diff-A (ediff-get-diff-overlay diff-num 'A))
261 (diff-B (ediff-get-diff-overlay diff-num 'B))
262 diff-A-str diff-B-str adjust-p)
263 (with-current-buffer buf-A
264 (setq adjust-p (if end (= (overlay-end diff-A) (point-max))
265 (= (overlay-start diff-A) (point-min)))
266 diff-A-str (and adjust-p (buffer-substring-no-properties
267 (overlay-start diff-A)
268 (overlay-end diff-A))))
270 (setq adjust-p (and adjust-p
271 (if end (= (overlay-end diff-B) (point-max))
272 (= (overlay-start diff-B) (point-min))))
273 diff-B-str (and adjust-p (buffer-substring-no-properties
274 (overlay-start diff-B)
275 (overlay-end diff-B))))
277 (let ((len-A (length diff-A-str))
278 (len-B (length diff-B-str))
279 (adjust-func (if end 'vlf-ediff-adjust-end
280 'vlf-ediff-adjust-start)))
283 (or (funcall adjust-func diff-A-str diff-B-str buf-B)
284 (setq adjust-p nil)))
286 (or (funcall adjust-func diff-B-str diff-A-str buf-A)
287 (setq adjust-p nil)))
288 (t (setq adjust-p nil))))))
291 (defun vlf-ediff-adjust-start (diff-short diff-long vlf-buffer)
292 "Remove difference between DIFF-SHORT and DIFF-LONG from beginning\
294 (when (string-suffix-p diff-short diff-long)
295 (set-buffer vlf-buffer)
296 (vlf-move-to-chunk (+ vlf-start-pos
297 (length (encode-coding-string
298 (substring diff-long 0
299 (- (length diff-long)
300 (length diff-short)))
301 buffer-file-coding-system t)))
304 (defun vlf-ediff-adjust-end (diff-short diff-long vlf-buffer)
305 "Remove difference between DIFF-SHORT and DIFF-LONG from the end of\
307 (when (string-prefix-p diff-short diff-long)
308 (set-buffer vlf-buffer)
309 (vlf-move-to-chunk vlf-start-pos
311 (length (encode-coding-string
314 buffer-file-coding-system t))))))
316 (unless (fboundp 'string-suffix-p)
317 (defun string-suffix-p (suffix string &optional ignore-case)
318 "Return non-nil if SUFFIX is a suffix of STRING.
319 If IGNORE-CASE is non-nil, the comparison is done without paying
320 attention to case differences."
321 (let ((start-pos (- (length string) (length suffix))))
322 (and (>= start-pos 0)
323 (eq t (compare-strings suffix nil nil string start-pos nil
328 ;;; vlf-ediff.el ends here