Something went wrong. Try again.
Runabout is a long-term agent harness.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869(in-package #:runabout.repl)
(defparameter *diff-context* 2 "Unchanged lines shown around each change.")
(defparameter *diff-limit* 1000000 "Line pairs past which a diff stops looking for the shortest script and showsthe changed span as one replacement: still correct, only longer.")
(defun diff-script (a b) "The shortest edit script from vector A to vector B, as :SAME, :DEL and :ADDin order; NIL when comparing them would take more than *DIFF-LIMIT* line pairs." (let ((n (length a)) (m (length b))) (unless (> (* n m) *diff-limit*)
(let ((lcs (make-array (list (1+ n) (1+ m)) :element-type 'fixnum :initial-element 0))) (loop for i from (1- n) downto 0 do (loop for j from (1- m) downto 0 do (setf (aref lcs i j) (if (equal (aref a i) (aref b j)) (1+ (aref lcs (1+ i) (1+ j))) (max (aref lcs (1+ i) j) (aref lcs i (1+ j))))))) (loop with i = 0 and j = 0 while (or (< i n) (< j m)) collect (cond ((and (< i n) (< j m) (equal (aref a i) (aref b j))) (incf i) (incf j) :same) ((and (< i n) (or (= j m) (>= (aref lcs (1+ i) j) (aref lcs i (1+ j))))) (incf i) :del) (t (incf j) :add)))))))
(defun text-diff (old new from to) "A unified diff from OLD, headed FROM, to NEW, headed TO; \"\" if they are equal." (let* ((a (coerce (file-lines old) 'vector)) (b (coerce (file-lines new) 'vector)) (n (length a)) (m (length b))
(head (or (mismatch a b :test #'equal) n)) (tail (- n (or (mismatch a b :test #'equal :from-end t :start1 head :start2 head) n))) (middle (or (diff-script (subseq a head (- n tail)) (subseq b head (- m tail))) (append (make-list (- n tail head) :initial-element :del) (make-list (- m tail head) :initial-element :add)))) (entries (coerce (loop with i = 0 and j = 0 for op in (append (make-list head :initial-element :same) middle (make-list tail :initial-element :same)) collect (list op i j (if (eq op :add) (aref b j) (aref a i))) unless (eq op :add) do (incf i) unless (eq op :del) do (incf j)) 'vector)) (changes (loop for (op) across entries for index from 0 unless (eq op :same) collect index))) (if (null changes) "" (with-output-to-string (out) (format out "--- ~a~%+++ ~a~%" from to) (loop while changes do (let* ((lo (pop changes)) (hi lo)) (loop while (and changes (<= (- (first changes) hi) (1+ (* 2 *diff-context*)))) do (setf hi (pop changes))) (let* ((hunk (subseq entries (max 0 (- lo *diff-context*)) (min (length entries) (+ hi *diff-context* 1)))) (olds (- (length hunk) (count :add hunk :key #'first))) (news (- (length hunk) (count :del hunk :key #'first))) (start (aref hunk 0)))
(format out "@@ -~d,~d +~d,~d @@~%" (+ (second start) (min olds 1)) olds (+ (third start) (min news 1)) news) (loop for (op nil nil (text . ending)) across hunk do (format out "~a~a~%" (ecase op (:same " ") (:del "-") (:add "+")) text) (when (equal ending "") (format out "\\ No newline at end of file~%"))))))))))