I recently needed to put together a set of slides to review some code I wrote for work. A slide deck is not how I would normally or preferrably present my code, but the request was for slides so that's what I set out to make. For me, a presentation is always an opportunity for deeper exploration of LaTeX and Beamer, so these code review slides presented an interesting opportunity.
Going into this project, I had a few goals:
These code review slides are supposed to cover hundreds of lines of code, so I don't want to be manually
copying all that code in my slides. Instead, it'd be great if I could use LaTeX to automatically pull in, say,
lines 121-160 from src/file.c
into a particular slide.
If I'm including code directly in from source files, I want to make sure I'm including lines from the correct version of the files I'm presenting. It would be great if, as part of the build process for the slides, I could automatically pull in the current git branch and latest commit hash and display that clearly in the slides for later reference and present confirmation that I'm reading the right files.
I found a great and broadly applicable solution goal #2, but I'm going to put my code for tracking git version in a different note. Hopefully I'll remember to come back and link to it here!
Fortunately there exists an easy way to include source code - or any text in an external file - in a LaTeX
document - the listings
package! I found this package via an old post on texblog.org. It is
very easy to get startd with - just place a
\usepackage{listings}
\usepackage{listings}
at the top of your LaTeX file, and include lines from other files with:
\lstinputlisting{path/to/file.txt}
\lstinputlisting{path/to/file.txt}
This is pretty handy but I just want to include excerpts, not entire files.
A StackExchange answer from user egreg showed me a way to include just a few lines from a file:
\lstinputlisting[firstline=300,lastline=500]{file.cc}
\lstinputlisting[firstline=300,lastline=500]{file.cc}
The listings
package also supports a linerange
method of including multiple chunks of a file in a single excerpt, as shown in
this
StackExchange answer:
\lstinputlisting[linerange={1-4,7-9}]{file.cc}
\lstinputlisting[linerange={1-4,7-9}]{file.cc}
For now, I'm going to stick with the firstline,lastline
method because it
works better with line numbering. I can definitely see myself using the linerange
version in a situation where line numbers aren't as important though.
Giving just the firstline
and lastline
arguments to \lstinputlisting
gave a code listing where the line numbers did not
match the source file. For example, the command
\lstinputlisting[firstline=61,lastline=69]{../Terrain2STL/src/STLWriter.c}
\lstinputlisting[firstline=61,lastline=69]{../Terrain2STL/src/STLWriter.c}
gave me an output of
even though that void startSTLfile...
line is actually line 61 in the source
file. Passing the additional firstnumber
argument to \lstinputlisting
as described in this post let me choose
the correct starting line number. An updated command
\lstinputlisting[firstline=61,lastline=69,firstnumber=61]{../Terrain2STL/src/STLWriter.c}
\lstinputlisting[firstline=61,lastline=69,firstnumber=61]{../Terrain2STL/src/STLWriter.c}
gives
The final issue I encounted was that really long code excerpts would run off the end of the page and not be
visible to the reader. This turned out be an easy fix, at least for slides in beamer. The solution proposed by
this StackExchange post is to simply change the \begin{frame}
line at the start of a long slide to \begin{frame}[allowframebreaks]
. With this modification, a slide with a long code excerpt
(or any long text for that matter) will auto-expend in subsequent slides with the same title. Very
convenient!
I eventually settled on the following macro for all my code excerpting needs:[1]
\newcommand{\excerpt}[3] { \small{\texttt{\detokenize{#1}}:} \lstinputlisting[firstline=#2, lastline=#3, firstnumber=#2]{#1}}
\newcommand{\excerpt}[3] {
\small{\texttt{\detokenize{#1}}:}
\lstinputlisting[firstline=#2, lastline=#3, firstnumber=#2]{#1}}
It can then used as:
\excerpt{../Terrain2STL/src/STLWriter.c}{61}{69}
\excerpt{../Terrain2STL/src/STLWriter.c}{61}{69}
Where the arguments are 1) path to excerpt source file, 2) first source line to include in excerpt, 3) last source line to include in excerpt.
That command producesa code listing in a LaTeX document that looks like this:
The following snippet should also be included before the \begin{document}
line to setup the listings
package correctly:
\usepackage{listings} % lstset adapted from https://tex.stackexchange.com/questions/147512/latex-a-single-code-listing-over-multiple-pages \lstset{ % basicstyle=\tiny\ttfamily, % the size of the fonts that are used for the code numbers=left, % where to put the line-numbers numberstyle=\tiny, % the size of the fonts that are used for the line-numbers stepnumber=1, % the step between two line-numbers. If it is 1 each line will be numbered numbersep=5pt, % how far the line-numbers are from the code backgroundcolor=\color{white}, % choose the background color. You must add \usepackage{color} showspaces=false, % show spaces adding particular underscores showstringspaces=false, % underline spaces within strings showtabs=false, % show tabs within strings adding particular underscores frame=single, % adds a frame around the code tabsize=2, % sets default tabsize to 2 spaces captionpos=b, % sets the caption-position to bottom breaklines=true, % sets automatic line breaking breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace escapeinside={\%*}{*)}, % if you want to add a comment within your code float=H }
\usepackage{listings}
% lstset adapted from https://tex.stackexchange.com/questions/147512/latex-a-single-code-listing-over-multiple-pages
\lstset{ %
basicstyle=\tiny\ttfamily, % the size of the fonts that are used for the code
numbers=left, % where to put the line-numbers
numberstyle=\tiny, % the size of the fonts that are used for the line-numbers
stepnumber=1, % the step between two line-numbers. If it is 1 each line will be numbered
numbersep=5pt, % how far the line-numbers are from the code
backgroundcolor=\color{white}, % choose the background color. You must add \usepackage{color}
showspaces=false, % show spaces adding particular underscores
showstringspaces=false, % underline spaces within strings
showtabs=false, % show tabs within strings adding particular underscores
frame=single, % adds a frame around the code
tabsize=2, % sets default tabsize to 2 spaces
captionpos=b, % sets the caption-position to bottom
breaklines=true, % sets automatic line breaking
breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace
escapeinside={\%*}{*)}, % if you want to add a comment within your code
float=H
}
To see all these variations of code excerpting, check out a set of demo slides and the LaTeX source file
below. I'd recommend downloading both to see the different commands, including my final \excerpt
macro, render code snippets!
The \detokenize{}
macro is there to make sure TeX doesn't try to
process the source file name - I ran into issues where special characters like underscores would mess up
the display of the source file name. detokenize
fixed that ↩︎