Calaf
Calaf

Reputation: 10847

How can I show the differences between two programs?

I am giving a programming talk. Rather than use slides, I am presenting a sequence of incrementally longer programs, stored in filenames 0001.py, 0002.py, etc.

The code in each program introduces just one or a few lines of code as a modification to the preceding one.

I intend to present solely using emacs. I am familiar with ediff, but it would be a bit of a pain to use it live during a talk (as there are about 50 small programs, with one minute to introduce each increment).

Does there exist an emacs package that would allow me to split the window and to highlight what is actually different between (n).py and (n+1).py. (I'm using .py for concreteness, but hopefully the solution would work for any text file.)

I am asking here rather than on https://emacs.stackexchange.com because I will be happy with a solution using emacs, git, or any combination that I can string together to give a live demo, especially with the ability to modify the code live while answering questions.

Update

As phils suggests, M-x compare-windows almost solves this issue, but:

  1. It would be nice if it works correctly regardless of the current position of the cursor in the two buffers.
  2. It would be nice if all changes appear in one view, rather than iterating over the diffs. The point is to say "see, in the program on the right I've added just this line and that line, and look at all that that program can do compared to the previous one."

Update 2

In other words, how do I generate what is done manually in the HTML below to show the differences side-by-side?

.myflex {
  display: flex;
  flex-direction: row;
}

.before,
.after {
  border: 1px solid black;
  padding: 20px;
  margin: 20px;
  border-radius: 2px;
}

.pink {
  background-color: pink;
}

.green {
  background-color: PaleGreen;
}
<div class="myflex">
  <div class="before">
    <pre>
<span class='pink'>And so without particularly analyzing all the contiguous sections of a</span>
<span class='pink'>cone and of the ranks of an army, or the ranks and positions in any</span>
while the less their direct participation in the action itself, the more
they command and the fewer of them there are; rising in this way from
the lowest ranks to the man at the top, who takes the least direct share
in the action and directs his activity chiefly to commanding.
        </pre>
  </div>
  <div class="after">
    <pre>
<span class='green'>We see a law by which men, to take associated action, combine</span>
<span class='green'>in such relations that the more directly they participate in performing</span>
<span class='green'>the action the less they can command and the more numerous they are,</span>
while the less their direct participation in the action itself, the more
they command and the fewer of them there are; rising in this way from
the lowest ranks to the man at the top, who takes the least direct share
in the action and directs his activity chiefly to commanding.
        </pre>
  </div>
</div>

Sequel

(I'm only adding this sequel here because phils generously volunteered to answer more than I was initially asking for.)

Consider the program (in some imaginary language, stored in a .txt file):

hello

and consider that we insert a keyword that requires indentation:

repeat:
    hello

Now when we show the two programs in split-window, it would be nice if the hi is not highlighted, despite that the two lines differ by indentation; only the repeat: is.

The idea is that we'd be adding some loop operator and do not wish the gaze of the listener to go to either the indentation or to the entire line, only to the operator.

In other words, when displaying the difference, we would ideally highlight the repeat line, but neither hello nor the indentation preceding it.

avoiding highlighting indentation

Upvotes: 2

Views: 221

Answers (1)

phils
phils

Reputation: 73345

I suspect that some existing diff functionality can do this and so there'll be a better answer, but I've hacked the following together using compare-windows.

(defun my-compare-windows-complete (&optional ignore-whitespace)
  "Highlight all differences between two windows.

With a prefix argument, do not highlight whitespace-only differences.
\(This does not prevent the highlighting of whitespace that is part of
a difference which includes non-whitespace characters.)

To remove the highlighting, use \\[compare-windows-dehighlight]."
  (interactive "P")
  (require 'cl-lib)
  (require 'compare-w)
  (compare-windows-dehighlight)
  (let ((w1 (get-buffer-window))
        (w2 (funcall compare-windows-get-window-function)))
    (cl-letf ((w1p (window-point w1))
              (w2p (window-point w2))
              (compare-windows-highlight 'persistent)
              ((symbol-function 'compare-windows-dehighlight) #'ignore)
              ((symbol-function 'ding) (lambda () (error "done"))))
      (with-selected-window w1
        (goto-char (point-min)))
      (with-selected-window w2
        (goto-char (point-min)))
      (ignore-errors
        (while (compare-windows ignore-whitespace)))
      ;; Highlight any non-matching remainder in both buffers.
      (let ((b1 (window-buffer w1))
            (b2 (window-buffer w2))
            (p1 (window-point w1))
            (p2 (window-point w2))
            (max1 (with-selected-window w1 (point-max)))
            (max2 (with-selected-window w2 (point-max))))
        (compare-windows-highlight p1 max1 b1 w1 p2 max2 b2 w2))
      (set-window-point w1 w1p)
      (set-window-point w2 w2p))))

You can use M-x compare-windows-dehighlight afterwards to remove the highlighting.

The faces used for highlighting are:

  • compare-windows-removed (inheriting from diff-removed)
  • compare-windows-added (inheriting from diff-added)

Upvotes: 2

Related Questions