\usepackage{etoolbox} \usepackage{fancyvrb} \usepackage{listings} \lstset{basicstyle=\ttfamily,fontadjust=true,basewidth=0.5em,xleftmargin=26pt} \usepackage[strings,nohyphen]{underscore} \usepackage{mdframed} \newsavebox{\measurebox} \newlength{\widestline} % \measurewidest{line one\\line two\\...} -> \widestline = width of widest \newcommand{\measurewidest}[1]{% \begin{lrbox}{\measurebox}% \begin{tabular}{@{}l@{}}#1\end{tabular}% \end{lrbox}% \setlength{\widestline}{\wd\measurebox}% } % \asymbox[]{line one\\line two\\...} other three sides: 8pt \newcommand{\asymbox}[2][0pt]{% \begin{lrbox}{\measurebox}% \begin{tabular}{@{}l@{}}\tt #2\end{tabular}% \end{lrbox}% {\setlength{\fboxsep}{0pt}% \fcolorbox{black}{yellow!20}{% \kern#1% \vbox{\kern0pt\hbox{\usebox{\measurebox}}\kern0pt}% \kern0pt}}% } % =========================================================================== % Annotated code listings. Emitted by sks/code/code_block.py; the layout % policy lives here so it can be tuned without touching the Python. % % The model: a listing is a vertical stack of BLOCKS, each block a run of % code lines with an optional comment beside it. There is deliberately no % table -- comments are a fixed distance from their own block and do not % align with each other, and each block's shaded box is as wide as that % block's longest line. % % Two mechanisms carry the whole appearance: % % 1. Every line is a \strut'ed \makebox of the block's width. A LaTeX % \strut is exactly 0.7\baselineskip high and 0.3 deep -- one full % \baselineskip -- so consecutive lines inside a block abut exactly, % with no gap and no overlap, at any point size. Without the strut a % line with no descender would sit closer to its neighbour and the % rhythm would follow the text rather than the grid. % % 2. \offinterlineskip in the listing removes the interline glue BETWEEN % BLOCKS, so consecutive blocks abut too and a boxed block continues % the rhythm of the plain lines around it. (It does not reach inside % a \parbox, which restores normal interline spacing -- that is why % the struts in point 1 are doing the work there, and why the box's % vertical padding comes from \fboxsep below rather than from a % padding line, which would be spaced off the grid.) % =========================================================================== \definecolor{klcodeshade}{rgb}{1,1,.6} \newlength{\kllistingwidth} % width of a whole unannotated listing % How the listing is placed WITHOUT being boxed, so that it can still break % across pages: \klshift moves it right (an :offset with :hpos left) and % \klindent is the width taken out of \linewidth (either side's offset). % A minipage would do both at once but cannot break -- see Code.tex in % sks/code/code_block.py, which sets these before \begin{klcode}. \newlength{\klshift} \setlength{\klshift}{0pt} \newlength{\klindent} \setlength{\klindent}{0pt} \newlength{\klcodewidth} % width of the current block's box \newlength{\klcommentwidth} % what is left for its comment % Horizontal inset of the code from the box's left edge. 0pt puts the % box's left edge ON the code column, where the line's own indentation % begins, rather than out to the left of it; a positive value moves the % CODE right, never the box left. \newlength{\klcodepad} \setlength{\klcodepad}{0pt} \newlength{\klcodegap} \setlength{\klcodegap}{14pt} % box to comment \newlength{\klcodemin} \setlength{\klcodemin}{6em} % narrower: see below % Clear space above and below a block that HAS a box, so that two boxed % blocks with no unboxed lines between them read as two boxes rather than % one. Applied per block, never per line: the lines within a block must % still abut, or the shading stops being a solid rectangle. % 1.5pt is 2px at the CSS reference 96dpi, matching the html margin in % css/code.css -- these two are meant to look the same, so change them % together. \newlength{\klboxgap} \setlength{\klboxgap}{1.5pt} % The box's own vertical margin: space INSIDE the shading, above the first % line and below the last. Same 2px equivalent, matching the html padding. \newlength{\klboxpad} \setlength{\klboxpad}{1.5pt} % \klline{} — one code line, padded and strut'ed to the block width. \newcommand{\klline}[1]{% \makebox[\klcodewidth][l]{\hspace{\klcodepad}\strut\ttfamily #1}} % \klcodebox{} — the block's lines, shaded when the block has a % comment (\klcomment is set by \klblock). % % The shading is ONE \colorbox around the whole block, not one per line, % because only a box around the whole block can have a vertical margin. % % That margin is \vspace* INSIDE the \parbox rather than \fboxsep: \fboxsep % pads all four sides, and any horizontal padding would put the box's left % edge out to the LEFT of the code column. With \fboxsep 0 the box spans % exactly the code's own extent, so it begins where the line's indentation % begins and the code stays aligned with the unshaded lines around it. \newcommand{\klcodebox}[1]{% \ifx\klcomment\empty \parbox[c]{\klcodewidth}{#1}% \else \setlength{\fboxsep}{0pt}% \colorbox{klcodeshade}{% \parbox[c]{\klcodewidth}{\vspace*{\klboxpad}#1\vspace*{\klboxpad}}}% \fi} % \klblock{}{}{} % is \klline/\klshaded calls separated by \\; may be % empty. The comment gets what the box leaves of \linewidth (NOT % \textwidth: inside a list or minipage they differ, and \textwidth would % push the comment into the margin). If that remainder is too narrow to % set prose in, the comment goes BELOW the block rather than being % squeezed into an overfull box. \newcommand{\klblock}[3]{% \begingroup % A block is boxed exactly when it has a comment, so this one test also % decides whether the block gets the \klboxgap separation. \def\klcomment{#3}% \ifx\klcomment\empty\else\vskip\klboxgap\fi \settowidth{\klcodewidth}{\ttfamily #1}% \addtolength{\klcodewidth}{2\klcodepad}% % The box advances the line by exactly \klcodewidth (\fboxsep is 0 in % \klcodebox). If a horizontal padding is ever reintroduced there, its % width must be subtracted here too: leaving it out overfills the line, % and \raggedright then breaks it -- dropping the comment onto the next % line at the margin, which looks like the narrow-comment fallback. \setlength{\klcommentwidth}% {\dimexpr\linewidth-\klindent-\klcodewidth-\klcodegap\relax}% \noindent \ifdim\klcommentwidth<\klcodemin \klcodebox{#2}% \ifx\klcomment\empty\else \\\parbox[t]{\linewidth}{\klcommentfont #3}% \fi \else \klcodebox{#2}% \ifx\klcomment\empty\else \hspace{\klcodegap}% \parbox[c]{\klcommentwidth}{\klcommentfont #3}% \fi \fi % End the block's line. Without this every block joins ONE horizontal % list and the blocks are broken into lines and justified like words. \par \ifx\klcomment\empty\else\vskip\klboxgap\fi \endgroup} % The annotation font. Ragged right: a justified annotation beside a % narrow box hyphenates badly. \newcommand{\klcommentfont}{\sffamily\itshape\small\raggedright} % \begin{klcode} ... \end{klcode} — the listing itself. \offinterlineskip % is what makes the struts the only thing setting vertical rhythm. \newenvironment{klcode} {\par\addvspace{0.5\baselineskip}% \begingroup \setlength{\parindent}{0pt}% \setlength{\parskip}{0pt}% \raggedright % An unboxed listing is shifted with \leftskip rather than wrapped in a % minipage, so that it can still break across pages. \leftskip=\klshift \offinterlineskip} {\endgroup\par\addvspace{0.5\baselineskip}}