Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I want to print code, preferably syntax highlighted, and then combine this with a diff where the included code is the newer version of the code. I want lines that have been added or changed to be highlighted or displayed normally, and for lines that have been unmodifed to be faded.

Currently I am including the code with the lstlisting package, but that doesn't give me a way to combine the diff to highlight changes.

Something like this would be great.

Original File

foo
bar

Modified File (included in the LaTeX document)

foo
bar2
baz

And the result in the document would be

foo
bar2
baz

(except ideally the normal text will be faded and the bold text would be normal)

share|improve this question
    
So you want this highlighting to be automatic? –  Werner Jun 6 '12 at 19:42
1  
Simple answer: That can't be done by LaTeX. Of course if you use some external scripts you can achieved this. –  Marco Daniel Jun 6 '12 at 19:48
    
I'd be interested to know if/how this could be implemented using LuaLaTeX. –  Scribblemacher Jun 6 '12 at 20:01
    
I was hoping to find something that combined pygments with some smart processing to do this. I expect it can't be done in pure latex. –  Mike Cooper Jun 6 '12 at 20:14
    
This should be pretty simple via Python with Pygments and difflib. I'll throw together a minimal example, and then sometime in the next few days add a macro for this to the syntax highlighting portion of my PythonTeX package. –  G. Poore Jun 7 '12 at 23:06
add comment

1 Answer

Here's a Python script that will do the diff. It's for Python 2.x, but should work under 3 with a little tweaking (codecs.open() -> open(), etc.). You'll need to customize the treatment of added/modified and deleted lines to suit your preference. I will add a macro for this to my PythonTeX package, but it will be at least a few days till I incorporate that and release a new version.

# -*- coding: utf-8 -*-
'''
Read in two code files, compute diff, and output a highlighted version in TeX 
format.  Usage: diff2tex.py old_file new_file

'''

# Imports
from __future__ import unicode_literals
import sys
import codecs
import difflib
import re
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import LatexFormatter


file_encoding = 'utf=8'
lexer = 'python'
formatter = LatexFormatter(style='default', texcomments=False, mathescape=False, linenos=True)

def pygmentize(expr):
    s = highlight(expr, get_lexer_by_name(lexer), formatter)
    # Strip Verbatim environment
    s = re.sub(r'\\begin{Verbatim}\[.+\]\n', '', s)
    s = re.sub(r'\\end{Verbatim}\n', '', s)
    return s

def same_wrapper(expr):
    return r'\colorbox{white}{\parbox{\linewidth}{' + expr.rstrip('\r\n') + '\\vphantom{fg}}}\n'

def new_wrapper(expr):
    return r'\colorbox{PaleGreen}{\parbox{\linewidth}{' + expr.rstrip('\r\n') + '\\vphantom{fg}}}\n'

def del_wrapper(expr):
    return r'\colorbox{LightPink}{\parbox{\linewidth}{' + expr.rstrip('\r\n') + '\\vphantom{fg}}}\n'


old_file = codecs.open(sys.argv[1], 'r', encoding=file_encoding)
new_file = codecs.open(sys.argv[2], 'r', encoding=file_encoding)
differ = difflib.Differ()
diff = differ.compare(old_file.readlines(), new_file.readlines())

result = []
for line in diff:
    if line.startswith('  '):
        line = re.sub('^  ', '', line)
        result.append(same_wrapper(pygmentize(line)))        
    elif line.startswith('+ '):
        line = re.sub('\+ ', '', line)
        result.append(new_wrapper(pygmentize(line)))
    elif line.startswith('- '):
        line = re.sub('- ', '', line)
        result.append(del_wrapper(pygmentize(line)))
    elif line.startswith('\? '):
        pass


# Create a header and footer for a sample .tex document
header = '''\\documentclass[11pt]{article}
\\usepackage[T1]{fontenc}
\\usepackage{lmodern}
\\usepackage[utf8]{inputenc}
\\usepackage{fancyvrb}
\\usepackage[svgnames]{xcolor}
\\usepackage{upquote}

\\begin{document}
'''

footer = '''
\\end{document}
'''

# Get the correct command to start the Verbatim environment
begin_verbatim = highlight(' ', get_lexer_by_name(lexer), formatter).split('\n',1)[0] + '\n'


out_file = codecs.open('diff.tex', 'w', encoding=file_encoding)
out_file.write(header)
# Need to get and write the Pygments macros
out_file.write(formatter.get_style_defs())
out_file.write('\n\n')
out_file.write(begin_verbatim)
out_file.write(''.join(result))
out_file.write('\\end{Verbatim}\n')
out_file.write(footer)
out_file.close()

We can give this two files. The old version:

#!/usr/bin/env python
'''
A docstring
'''
def f(x): #Will edit this
    x = x + 1
    x = x**2 #Will delete this
    return x**2

f(x)
print('Done!')

And the new, edited file:

#!/usr/bin/env python
'''
A docstring
'''
def f_new(x):
    #Add a comment
    x = x + 1
    return x**2

f_new(x)
print('Done!')

Running python diff2tex.py old.py new.py yields a document that compiles to the following:

enter image description here

share|improve this answer
    
Very nice answer! :) –  Claudio Fiandrino Jun 9 '12 at 7:12
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.