|
89 | 89 | (let ((code (intern-string interner line)))
|
90 | 90 | (push code interned-file)))
|
91 | 91 | (push (coerce (nreverse interned-file) 'simple-vector) interned-files)))))
|
| 92 | + |
| 93 | +(defun intern-seqs (&rest seqs) |
| 94 | + (let ((interner (make-interner)) |
| 95 | + (interned-seqs nil)) |
| 96 | + (dolist (seq seqs (values interner (nreverse interned-seqs))) |
| 97 | + (let ((interned-seq nil)) |
| 98 | + (loop :for line :in seq :do |
| 99 | + (let ((code (intern-string interner line))) |
| 100 | + (push code interned-seq))) |
| 101 | + (push (coerce (nreverse interned-seq) 'simple-vector) interned-seqs))))) |
| 102 | + |
92 | 103 |
|
93 | 104 | ;;; Computing longest common subsequences between two sequences whose
|
94 | 105 | ;;; elements compare equal via EQL. The algorithm used here is based
|
|
244 | 255 | (defun compute-raw-diff (origin modified)
|
245 | 256 | (convert-lcs-to-diff (compute-lcs origin modified)))
|
246 | 257 |
|
| 258 | +(defun compute-raw-seq-diff (original-seq modified-seq) |
| 259 | + (multiple-value-bind (interner interned-seqs) |
| 260 | + (intern-seqs original-seq modified-seq) |
| 261 | + (declare (ignorable interner)) |
| 262 | + (convert-lcs-to-diff (apply #'compute-lcs interned-seqs)))) |
| 263 | + |
247 | 264 |
|
248 | 265 | ;;; producing diffs in "unified diff" format
|
249 | 266 |
|
@@ -298,6 +315,50 @@ of context; the default of three should be good enough for most situations.")
|
298 | 315 | (window-chunks :initform nil
|
299 | 316 | :accessor window-chunks)))
|
300 | 317 |
|
| 318 | +(defun apply-seq-window (original-seq window &key (offset 0)) |
| 319 | + "Apply the edits encoded in WINDOW to the ORIGINAL-SEQ." |
| 320 | + (multiple-value-bind (interner interned-seqs) |
| 321 | + (apply #'intern-seqs original-seq |
| 322 | + (mapcar #'chunk-lines (window-chunks window))) |
| 323 | + (let ((index (original-start-line window)) |
| 324 | + (result (coerce (first interned-seqs) 'list))) |
| 325 | + (flet ((ind () (+ index offset)) |
| 326 | + (back (line) (interned-object interner line))) |
| 327 | + (loop |
| 328 | + for chunk in (window-chunks window) |
| 329 | + for lines in (mapcar (lambda (l) (coerce l 'list)) (cdr interned-seqs)) |
| 330 | + do (case (chunk-kind chunk) |
| 331 | + (:common |
| 332 | + (mapc (lambda (line) |
| 333 | + (assert (eql line (nth (ind) result)) |
| 334 | + (line result index) |
| 335 | + "window does not apply at ~d, ~s!=~s " |
| 336 | + (ind) (back line) (back (nth (ind) result))) |
| 337 | + (incf index)) |
| 338 | + lines)) |
| 339 | + ((:replace :delete) |
| 340 | + (setf result |
| 341 | + (append (subseq result 0 (ind)) |
| 342 | + (subseq result (+ (ind) (length lines))))) |
| 343 | + (incf index (length lines)) |
| 344 | + (decf offset (length lines))) |
| 345 | + ((:insert :create) |
| 346 | + (setf result (append (subseq result 0 (ind)) |
| 347 | + lines |
| 348 | + (subseq result (ind)))) |
| 349 | + (incf offset (length lines))))) |
| 350 | + (values (mapcar #'back result) offset))))) |
| 351 | + |
| 352 | +(defun apply-seq-diff (original-seq diff) |
| 353 | + "Apply DIFF to the sequence ORIGINAL-SEQ." |
| 354 | + (apply #'values |
| 355 | + (reduce |
| 356 | + (lambda (accumulator window) |
| 357 | + (destructuring-bind (seq offset) accumulator |
| 358 | + (multiple-value-call #'list |
| 359 | + (apply-seq-window seq window :offset offset)))) |
| 360 | + (diff-windows diff) :initial-value (list original-seq 0)))) |
| 361 | + |
301 | 362 | (deftype chunk-kind () '(member :common :delete :replace :insert :create))
|
302 | 363 |
|
303 | 364 | (defclass chunk ()
|
@@ -488,6 +549,19 @@ DIFF:UNIFIED-DIFF or DIFF:CONTEXT-DIFF."
|
488 | 549 | modified-pathname modified)))
|
489 | 550 | (walk-diff-regions context diff-regions)))))
|
490 | 551 |
|
| 552 | +(defun generate-seq-diff (diff-kind original-seq modified-seq) |
| 553 | + "Compute a diff between ORIGINAL-PATHNAME and MODIFIED-PATHNAME." |
| 554 | + (multiple-value-bind (interner interned-seqs) |
| 555 | + (intern-seqs original-seq modified-seq) |
| 556 | + (let* ((original (first interned-seqs)) |
| 557 | + (modified (second interned-seqs)) |
| 558 | + (lcs (compute-lcs original modified))) |
| 559 | + (let ((diff-regions (convert-lcs-to-diff lcs)) |
| 560 | + (context (create-diff-generator diff-kind interner |
| 561 | + "original" original |
| 562 | + "modified" modified))) |
| 563 | + (walk-diff-regions context diff-regions))))) |
| 564 | + |
491 | 565 | (defun format-diff (diff-kind original-pathname modified-pathname &optional (stream *standard-output*))
|
492 | 566 | (render-diff (generate-diff diff-kind
|
493 | 567 | original-pathname
|
|
0 commit comments