% \iffalse meta-comment % % comment2tex.dtx % Copyright (C) 2026 Erik Nijenhuis. % % This work may be distributed and/or modified under the conditions of % the LaTeX Project Public License, either version 1.3c of this license % or (at your option) any later version. % %<*driver> \documentclass{ltxdoc} \usepackage{listings} \usepackage{comment2tex} \GetFileInfo{comment2tex.sty} \lstset{basicstyle=\ttfamily\small,columns=fullflexible, breaklines=true,showstringspaces=false} \EnableCrossrefs \CodelineIndex \begin{document} \DocInput{comment2tex.dtx} \end{document} % % \fi % % \title{The \textsf{comment2tex} package} % \author{Erik Nijenhuis} % \date{\filedate \quad \fileversion} % \maketitle % % \begin{abstract} % \textsf{comment2tex} typesets a source file that carries its documentation in % special comments: the comments become ordinary \LaTeX, the rest becomes a % listing. It ships a Lua converter and two \TeX\ wrappers --- one for % \LaTeX/Lua\LaTeX\ and one for plain \TeX\ --- both providing \cs{includebash} % and \cs{includelua}. % \end{abstract} % % \tableofcontents % % \section{Introduction} % % A source file may document itself: lines beginning with a chosen % \emph{doc-comment} prefix are prose, everything else is code. For Bash the % prefix is a double hash (\texttt{\#\#}); for Lua it is a triple dash % (\texttt{-{}-{}-}). The converter \texttt{comment2tex.lua} turns such a file % into \LaTeX: doc lines are emitted verbatim (minus the prefix) and runs of % code are wrapped in a listing. % % This package is the \TeX\ front end. \cs{includebash} and \cs{includelua} % take a source file, run it through the converter, and input the result, so a % document can pull in annotated sources without a manual build step. % % \section{Usage} % % \DescribeMacro{\includebash} % \DescribeMacro{\includelua} % \cs{includebash}\marg{file} typesets a Bash source (doc prefix \texttt{\#\#}); % \cs{includelua}\marg{file} typesets a Lua source (doc prefix % \texttt{-{}-{}-}). Both derive an output name by replacing the file's % extension with \texttt{.c2t.tex}, generate that file with the appropriate % \emph{style} and \emph{wrapper}, and \cs{input} it. The \meta{file} must % carry a single extension. % % The \LaTeX\ wrapper uses the \texttt{lstlisting} wrapper, so load and % configure \textsf{listings} (this package does that for you with a plain % default \cs{lstset}). The plain \TeX\ wrapper uses a built-in verbatim % environment instead, since \textsf{listings} is \LaTeX-only. % % \section{Engines and use cases} % % How the conversion happens depends on the engine. % % \begin{description} % \item[Lua\LaTeX\ (recommended).] Lua\TeX\ embeds Lua, so the wrapper loads % \texttt{comment2tex.lua} as a library and converts \emph{in process} through % \cs{directlua}. No shell escape, no separate run: just % \texttt{lualatex document}. % \item[pdf\LaTeX\ with shell escape.] pdf\TeX\ cannot run Lua, so with % \texttt{-{}-shell-escape} the wrapper calls \texttt{texlua comment2tex.lua} % through \cs{write18} to generate the fragment, then inputs it. Run % \texttt{pdflatex -shell-escape document}. % \item[pdf\LaTeX, separate run (no shell escape).] Generate the fragments ahead % of time and let a normal run merely \cs{input} them. For each source run % \texttt{texlua comment2tex.lua -{}-style \meta{style} -o \meta{base}.c2t.tex % \meta{file}} (or \texttt{make}), then \texttt{pdflatex document}. If a % required fragment is missing the package raises an error telling you which % command to run. % \item[plain \TeX.] \texttt{comment2tex.tex} provides the same two macros for % plain \TeX. Under \texttt{luatex} it converts in process; under % \texttt{pdftex}/\texttt{tex} it uses the same shell-escape or separate-run % fallback. % \end{description} % % \begin{center} % \begin{tabular}{lll} % \hline % Engine & In process & Otherwise \\ % \hline % Lua\LaTeX & yes (\cs{directlua}) & --- \\ % pdf\LaTeX & no & \texttt{-shell-escape} or separate run \\ % plain \texttt{luatex} & yes & --- \\ % plain \texttt{pdftex}/\texttt{tex} & no & \texttt{-shell-escape} or separate run \\ % \hline % \end{tabular} % \end{center} % % In every case the generated file is \meta{base}\texttt{.c2t.tex}, so the % in-process, shell-escape and separate-run routes are interchangeable. % % \section{The converter, by example} % % Because the converter is itself a literate Lua file, the cleanest % demonstration is to let it document itself. The remainder of this section is % produced by \cs{includelua}\marg{comment2tex.lua} --- the generated documentation % demonstrates the converter's functionality under Lua\LaTeX. % % \includelua{comment2tex.lua} % % \section{Implementation} % % \subsection{The \LaTeX\ wrapper} % % \changes{v1.0}{2026/06/08}{Initial version} % % Announce the package and load \textsf{listings} with a neutral default style. % \begin{macrocode} %<*package> \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{comment2tex} [2026/06/08 v1.0 Include annotated source files as LaTeX listings] \RequirePackage{listings} \lstset{basicstyle=\ttfamily\small,columns=fullflexible, breaklines=true,showstringspaces=false} % \end{macrocode} % % The name of the converter, overridable before loading if it is not on the % current directory or \TeX\ tree. % \begin{macrocode} \providecommand\ctxscript{comment2tex.lua} % \end{macrocode} % % Under LuaTeX, load the converter once into the global \texttt{comment2tex}, % locating it through \texttt{kpse}. % \begin{macrocode} \ifdefined\directlua \directlua{ if not comment2tex then local f = kpse.find_file("comment2tex.lua","lua") or kpse.find_file("comment2tex.lua") or "comment2tex.lua" comment2tex = assert(loadfile(f))("comment2tex") end } \fi % \end{macrocode} % % \begin{macro}{\ctx@include} % Strip the extension, build the output name, generate it the best way the % engine allows, then input it. \cs{ctx@strip} keeps everything before the % first dot. % \begin{macrocode} \def\ctx@strip#1.#2\ctx@stop{#1} \newcommand\ctx@include[2]{% \edef\ctx@in{#2}% \edef\ctx@base{\ctx@strip#2\ctx@stop}% \edef\ctx@out{\ctx@base.c2t.tex}% \ifdefined\directlua \directlua{comment2tex.write("#1","lstlisting", "\luaescapestring{\ctx@in}","\luaescapestring{\ctx@out}")}% \else \ifdefined\pdfshellescape \ifnum\pdfshellescape>0\relax \immediate\write18{texlua \ctxscript\space --style #1 --wrapper lstlisting -o \ctx@out\space \ctx@in}% \fi \fi \fi \IfFileExists{\ctx@out}{\input{\ctx@out}}{% \PackageError{comment2tex}{Converted file \ctx@out\space not found}% {Use LuaLaTeX, run pdflatex with -shell-escape, or pre-build it:^^J% texlua \ctxscript\space --style #1 -o \ctx@out\space \ctx@in}}% } % \end{macrocode} % \end{macro} % % \begin{macro}{\includebash} % \begin{macro}{\includelua} % The two public entry points fix the style. % \begin{macrocode} \newcommand\includebash[1]{\ctx@include{bash}{#1}} \newcommand\includelua[1]{\ctx@include{lua}{#1}} % % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{The plain \TeX\ wrapper} % % Plain \TeX\ has neither \textsf{listings} nor the \LaTeX\ file helpers, so the % wrapper carries its own verbatim listing and uses primitive file tests. % Control-sequence names are letters only (no digits, no \texttt{@}). % % Locate and load the converter under Lua\TeX, exactly as the package does. % \begin{macrocode} %<*plain> \def\ctxscript{comment2tex.lua} \expandafter\ifx\csname directlua\endcsname\relax\else \directlua{ if not comment2tex then local f = kpse.find_file("comment2tex.lua","lua") or kpse.find_file("comment2tex.lua") or "comment2tex.lua" comment2tex = assert(loadfile(f))("comment2tex") end } \fi % \end{macrocode} % % \begin{macro}{\ctxendmark} % The end-of-listing sentinel, stored as a string of category-12 characters so % it can be compared against a verbatim line. A temporary escape character % (\verb+|+) lets us define it while the backslash is category~12 (``other''). % \begin{macrocode} \begingroup \catcode`\|=0 \catcode`\\=12 |gdef|ctxendmark{\endctxlisting}% |endgroup % \end{macrocode} % \end{macro} % % \begin{macro}{\ctxuncatcode} % Make the usual specials (and the space) category 12, the verbatim regime. % \begin{macrocode} \def\ctxuncatcode{\def\do##1{\catcode`##1=12 }\dospecials} % \end{macrocode} % \end{macro} % % \begin{macro}{\ctxlisting} % \begin{macro}{\ctxgrab} % \cs{ctxlisting} (emitted by the converter as \verb+\ctxlisting%+, the trailing % comment swallowing its own line end) opens a group, switches to the verbatim % regime with an active end-of-line, and starts grabbing lines. \cs{ctxgrab} % collects one line up to the active \verb+^^M+ and hands it on. Every physical % line in this group ends with \verb+%+ so no stray active end-of-line leaks % into the definitions. % \begin{macrocode} \begingroup \catcode`\^^M=\active% \gdef\ctxlisting{% \par\begingroup% \ctxuncatcode% \parindent=0pt \tt% \catcode`\^^M=\active% \ctxgrab}% \gdef\ctxgrab#1^^M{\ctxcheckend{#1}}% \endgroup % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\ctxcheckend} % Compare the grabbed line with the sentinel: if equal, finish; otherwise % typeset it and loop. \cs{ctxnext} defers the recursion until after \cs{fi}. % \begin{macrocode} \def\ctxcheckend#1{% \def\ctxtmp{#1}% \ifx\ctxtmp\ctxendmark \let\ctxnext\ctxfinish \else \leavevmode#1\par \let\ctxnext\ctxgrab \fi \ctxnext} \def\ctxfinish{\par\endgroup} % \end{macrocode} % \end{macro} % % \begin{macro}{\ctxinclude} % Mirror of the \LaTeX\ \cs{ctx@include}: derive the output name, generate it in % process (Lua\TeX) or via shell escape (pdf\TeX), then input it, erroring if it % is still missing. % \begin{macrocode} \def\ctxstrip#1.#2\ctxstop{#1} \def\ctxinclude#1#2{% \edef\ctxin{#2}% \edef\ctxbase{\ctxstrip#2\ctxstop}% \edef\ctxout{\ctxbase.c2t.tex}% \expandafter\ifx\csname directlua\endcsname\relax \expandafter\ifx\csname pdfshellescape\endcsname\relax\else \ifnum\pdfshellescape>0 \immediate\write18{texlua \ctxscript\space --style #1 --wrapper plain -o \ctxout\space \ctxin}% \fi \fi \else \directlua{comment2tex.write("#1","plain", "\luaescapestring{\ctxin}","\luaescapestring{\ctxout}")}% \fi \openin0=\ctxout\relax \ifeof0 \closein0 \errmessage{comment2tex: \ctxout\space not found (use luatex, -shell-escape, or pre-run texlua)}% \else \closein0 \input \ctxout\relax \fi} % \end{macrocode} % \end{macro} % % \begin{macro}{\includebash} % \begin{macro}{\includelua} % The public entry points. % \begin{macrocode} \def\includebash{\ctxinclude{bash}} \def\includelua{\ctxinclude{lua}} % % \end{macrocode} % \end{macro} % \end{macro} % % \Finale \endinput