As discussed in earlier in the comments, it is quite possible to solve the problem of defining classes with this method. Granted, as you said, it takes two runs, but it is possible to make a document that will deal with the writing (and some minimal printing) on the first run, and that will process class-defined commands on the second run only (if correctly marked up, obviously).
In order to perform this check, I rely on the existence (or lack thereof) of an .aux
file, because it is both easy – most IDEs can delete it in one click, it allows for overwrites, etc. – and user friendly in case of problem – as I expect most users to try to delete this file first if something goes wrong.
The only requirement is that we store the code in a class file instead of a package. The following MWE implements this solution, and also a macro-based solution to the problem of multiple files (you should write one file after the other, not mix them up haphazardly). Due to the fact that I am using comma-separated lists for the macros, it might make things easier to index, e.g. adding an optional argument for the type of thing created (macro, environment, counter, etc.).
The .cls
file:
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{literate}
\newif\if@firstrun
\@firstruntrue
\newif\if@iscsname
\@iscsnamefalse
\newcounter{c@copyright}
\IfFileExists{\jobname.aux}
{\IfFileExists{\jobname.cls}
{\LoadClass{\jobname}}{}
\IfFileExists{\jobname.sty}
{\RequirePackage{\jobname}}{}
\@firstrunfalse}
{\LoadClass{book}}
\newcommand{\filetype}[1]{%
\if@firstrun
\begingroup\lst@EndWriteFile
\fi
\def\usefiletype{#1}
\listadd{\file@list}{\listingshead\lstinputlisting[style=plain]{\jobname.#1}\addvspace{\baselineskip}}
\setcounter{c@copyright}{0}
}
\newcommand{\listingshead}{%
\section{Listings for \jobname.\usefiletype}%
The file contains the following code:
}
\RequirePackage{etoolbox,xstring,xcolor,listings,verbatim,docmute,makeidx}
\lst@RequireAspects{writefile}
\makeindex
\newcommand{\numbercolor}{\color[HTML]{655643}}% Ash
\newcommand{\macrocolor}{\color[HTML]{78948D}}% Teal
\newcommand{\commentcolor}{\color[HTML]{BF4D28}}% Saffron
\newcommand{\mathcolor}{\textcolor[HTML]{CF872E}}% Ochre
\newcommand{\emphcolor}{\color{cyan}}
\newcommand{\identifiercolor}{\color{black}}
\newcommand{\bracketcolor}{\color{black}}
\lstset{
language=[AlLaTeX]TeX,
breaklines=true,
columns=spaceflexible,
emptylines=0,
breakindent=1em,
tabsize=4,
basicstyle=\ttfamily,
texcsstyle=*\macrocolor,
commentstyle=\commentcolor\itshape,
identifierstyle=\identifiercolor,
morestring=[b]$,
stringstyle=\mathcolor,
}
\lstdefinestyle{blockcode}{
frame=leftline,
framerule=.2ex,
rulecolor=\numbercolor,
numbers=left,
numberstyle=\footnotesize\numbercolor,
firstnumber=auto,
}
\lstdefinestyle{samplecode}{
numbers=left,
numberfirstline=true,
stepnumber=10000,
firstnumber=1,
numberstyle=\numbercolor\makebox[1em][c]{\Large$\star$}\@gobble,
}
\lstdefinestyle{macrocode}{
numbers=left,
numberfirstline=true,
stepnumber=10000,
firstnumber=1,
numberstyle=\numbercolor\makebox[1em][c]{\P}\@gobble,
identifierstyle=\identifiercolor\itshape,
literate= {\{}{{\bracketcolor\{}{\identifiercolor$\langle$}}{2}
{\}}{{\identifiercolor$\,\rangle$}{\bracketcolor\}}}{2}
{[}{{\bracketcolor[}{\identifiercolor$\langle$}}{2}
{]}{{\identifiercolor$\,\rangle$}{\bracketcolor]}}{2}
{<}{{\identifiercolor$\langle$}}{1}
{>}{{\identifiercolor$\,\rangle$}}{1},
}
\lstdefinestyle{inlinecode}{
literate= {\{}{{\bracketcolor\{}}{1}
{\}}{{\bracketcolor\}}}{1}
{[}{{\bracketcolor[}}{1}
{]}{{\bracketcolor]}}{1},
}
\lstdefinestyle{plain}{
style=blockcode,
texcsstyle=*\color{black},
emphstyle=\color{black},
commentstyle={\color{black}\itshape},
stringstyle=\color{black},
}
\newcommand{\addcs}[1]{\lstset{moretexcs={#1},}}
% Create commands for globally adding indexation patterns. By default, it is assumed to be a control sequence
\newcommand{\defaultindexdef}{}
\newcommand{\addindexdef}{}
\newcommand{\setdefaultindex}[3]{%
\def\defaultindexdef
{\@iscsnametrue
\expandafter\ifstrempty{#1}
{\def\macro@type{}}
{\def\macro@type{#1}}
\expandafter\ifstrempty{#2}{}{\def\macro@format{#2}}
\expandafter\ifstrempty{#3}{}{\def\macro@index{#3}}}
}
\newcommand{\addtoindex}{\@ifstar{\s@addtoindex}{\@addtoindex}}
\newcommand{\@addtoindex}[4]{%
\apptocmd{\addindexdef}
{\expandafter\ifstrequal\expandafter{\macrotype}{#1}
{\@iscsnamefalse
\expandafter\ifstrempty{#2}
{\def\macro@type{}}
{\def\macro@type{#2}}
\expandafter\ifstrempty{#3}{}{\def\macro@format{#3}}
\expandafter\ifstrempty{#4}{}{\def\macro@index{#4}}}
{}}
{}{}
}
\newcommand{\s@addtoindex}[4]{%
\apptocmd{\addindexdef}
{\expandafter\ifstrequal\expandafter{\macrotype}{#1}
{\@iscsnametrue
\expandafter\ifstrempty{#2}{}{\def\macro@type{#2}}
\expandafter\ifstrempty{#3}{}{\def\macro@format{#3}}
\expandafter\ifstrempty{#4}{}{\def\macro@index{#4}}}
{}}
{}{}
}
% Set default index
\setdefaultindex
{\macrotype}
{\macroname}
{\entryname @\string\texttt{{\string\textbackslash}\entryname}\space(\entrytype)}
% Create a command for setting the macro type based on the optional argument to code
\newcommand{\set@macro@type}[2]{
% Create helper macros for text
\def\macrotype{#1}
\def\macroname{\if@iscsname\textbackslash\fi#2}
% Create helper macros for indexes
\StrSubstitute{#2}{@}{"@}[\entryname]
\def\entrytype{\macro@type}
% Default definitions
\defaultindexdef
% Specific type definitions
\ifstrequal{#1}{m}
{\@iscsnametrue
\def\macro@type{}
\def\macro@index{%
\entryname @\string\texttt{{\string\textbackslash}\entryname}}}{}
\ifstrequal{#1}{l}
{\@iscsnametrue
\def\macro@type{length}}{}
\ifstrequal{#1}{d}
{\@iscsnametrue
\def\macro@type{dimension}}{}
\ifstrequal{#1}{e}
{\@iscsnametrue
\def\macro@type{environment}
\def\macro@format{\textbackslash#2\par\textbackslash end#2}}{}
\ifstrequal{#1}{f}
{\@iscsnametrue
\def\macro@type{float}
\def\macro@format{\textbackslash#2\par\textbackslash end#2}}{}
\ifstrequal{#1}{p}
{\@iscsnamefalse
\def\macro@type{package}
\def\macro@index{%
\entryname @\string\texttt{\entryname}\entrytype}}{}
\ifstrequal{#1}{o}
{\@iscsnamefalse
\def\macro@type{option}
\def\macro@index{%
\entryname @\string\texttt{\entryname}\entrytype}}{}
\ifstrequal{#1}{c}
{\@iscsnamefalse
\def\macro@type{counter}
\def\macro@index{%
\entryname @\string\texttt{\string\textit{\entryname}}\space(\entrytype)}}{}
% Append user created macros
\addindexdef
}
\newcommand{\docindex}[2][]{%
\ifstrempty{#1}
{\@ifundefined{index@#2}
{\ClassError{literate}{Wrong item to index}{Control sequences must be indexed directly (no optional argument)\MessageBreak For parameters, write their type as an optional argument}}
{\index{\csname index@#2\endcsname}}}
{\@ifundefined{index@#1@#2}
{\ClassError{literate}{Wrong item to index}{Control sequences must be indexed directly (no optional argument)\MessageBreak For parameters, write their type as an optional argument}}
{\index{\csname index@#1@#2\endcsname}}}}
\newcommand{\processtexcs}[2][]{%
\set@macro@type{#1}{#2}
\if@iscsname
\def\@index@name{index@#2}
\else
\def\@index@name{index@#1@#2}
\fi
\def\@index@content{\macro@index}
\expandafter\xdef\csname\@index@name\endcsname
{\macro@index}
\texttt{\macro@format}\index{\@nameuse{\@index@name}}\par%
}
\lstnewenvironment{code}[2][m]
{\lstset{name=code@\usefiletype,style=blockcode,moretexcs={#2}}%
\AfterEndEnvironment{code}{\lstset{moretexcs={#2},}}%
\@ifundefined{usefiletype}{\ClassError{literate}{Missing filetype definition}{You must specify which file to write to with the command \string\filetype}}{}%
\addvspace{\baselineskip}%
\marginpar{%
\hskip0pt\vskip.3\baselineskip%
\forcsvlist{\processtexcs[#1]}{#2}}%
\csname\@lst @SetFirstNumber\endcsname%
\if@firstrun%
\lst@BeginAlsoWriteFile{\jobname.\usefiletype}%
\ifnum\value{c@copyright}=0\lstinputlisting{\jobname.copyright}\fi%
\fi}
{\if@firstrun\endgroup\fi%
\csname\@lst @SaveFirstNumber\endcsname%
\stepcounter{c@copyright}%
\addvspace{\baselineskip}}
\lstMakeShortInline[style=inlinecode]{|}
\lstnewenvironment{macro}
{\lstset{name=macro@\usefiletype,style=macrocode,}%
\addvspace{\baselineskip}%
\csname\@lst @SetFirstNumber\endcsname}
{\csname\@lst @SaveFirstNumber\endcsname
\addvspace{\baselineskip}}
\if@firstrun
\lstnewenvironment{copywrite}
{\if@firstrun\lst@BeginWriteFile{\jobname.copyright}\fi}
{\if@firstrun\lst@EndWriteFile\fi}
\newenvironment{example}
{\expandafter\comment}
{\expandafter\endcomment}
\else
\newenvironment{copywrite}
{\expandafter\comment}
{\expandafter\endcomment}
\lstnewenvironment{example}
{\lstset{name=example@\usefiletype,style=samplecode,}%
\addvspace{\baselineskip}%
\lst@BeginAlsoWriteFile{\jobname.tmp}}
{\lst@EndWriteFile%
\noindent\llap{\numbercolor\makebox[1em][c]{$\rightarrow$}\hspace{\lst@numbersep}}%
\input{\jobname.tmp}%
\addvspace{\baselineskip}}
\fi
\newcommand{\printlistings}{%
\if@firstrun\else
\forlistloop{}{\file@list}
\fi
}
\AtEndDocument{\if@firstrun\begingroup\lst@EndWriteFile\fi}
And the .tex
example of a self-documented class + package:
\documentclass{literate}
\addcs{ProvidesClass, definecolor, textcolor}
\addtoindex{col}{colour}{\macroname}{\entrytype!\entryname @\string\texttt{\entryname}}
\begin{document}
\begin{copywrite}
%
% This is a sample copyright notice that will be printed verbatim at the beginning of all files.
%
\end{copywrite}
\section{The first section}
\filetype{cls}
The class (|.cls|) begins thus:
\begin{code}{}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{MWE}
[2011/03/20 v1 Some sample class]
\LoadClass{tufte-book}
\end{code}
Then we create a new macro:
\begin{code}{mymacro,my@macro}
% Some comment
\newcommand{\mymacro}[2][]{#1{#2}}
\newcommand{\my@macro}[1]{$#1$}
\end{code}
It can be used this way:
\begin{macro}
\mymacro[format]{text}
\end{macro}
And outputs this:
\begin{example}
\mymacro[\emph]{some text}
\end{example}
Then we create a counter:
\begin{code}[c]{something}
% Some comment
\newcounter{something}
\end{code}
\section{The second section}
\filetype{sty}
We also create an |.sty| file:
\begin{code}{}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{MWE}
[2011/03/20 v1 Some sample package]
\end{code}
\pagebreak A new environment:
\begin{code}[e]{myenv}
\newenvironment{myenv}{}{}
\end{code}
A new color:
\begin{code}[col]{ochre}
\usepackage{xcolor}
\definecolor{ochre}{HTML}{CF872E}
\end{code}
\begin{example}
\textcolor{ochre}{Some ochre text!}
\end{example}
But perhaps should we return to talking about |\mymacro|\docindex{mymacro}, and later we'll switch back to colours\docindex[col]{ochre}.
\printlistings
\printindex
\end{document}
Requires 2 runs of LaTeX to print the documentation + one run of MakeIndex and one final run of LaTeX.
Edit (April 3): Added an AfterEndEnvironment
hook so the new macro names are set globally after the first code
environment (fixes a bug in syntax highlighting).
Edit (April 10): Added several new things:
A macro for adding some control sequences to listings
' colouring, without indexing them: \addcs{}
, which takes a CSV list as its argument.
An environment that writes a copyright file and prints it at the beginning of every new file: \begin{copywrite}
.
A command that inputs the listings of all created files, with a "heading" that can be customised via \renewcommand{\listingshead}
– by default, I have set it to print a section title and a short sentence. (Could be made more customisable I suppose…)
Semi-automatic indexing (see below).
Some error messages, because wrong indexing commands are easy to produce and the LaTeX error message was really, really unhelpful.
Several macros that automatically index the marked content of every code
environment based on its type. It also works with @
characters in macro names. By default, it is assumed that the string is a macro but you can change it using the optional argument (e.g. \begin{code}[e]{myenv}
for an environment).
I have defined macros (m
, default), environments (e
), floats (f
), lengths (l
), dimensions (d
), counters (c
), packages (p
) and options (o
). New types can be defined implicitly:
\begin{code}[great-macro]{mygreatmacro}
in which case they will be treated as macros. They can also be defined explicitly in the preamble with:
\addtoindex{<short name>}{<name to index>}{<format>}{<indexing code>}
% The starred version is for control sequences, the normal version for everything else
% Helper macros are `\macroname`, `\entryname` and `\entrytype`
In the main file, you then need to write \docindex{mymacro}
for control sequences, or \docindex[c]{mycounter}
for everything else. This is made necessary by the fact that, while you can have only one control sequence with a given name, you can have several strings of other types with the same name (say the \chapter
command vs the chapter
counter and perhaps a chapter
colour).
I will try to set up a repository with the complete file (which includes options at class selection), and try to work on some documentation as things are getting a bit complex now.
And I would indeed be very happy to get any feedback regarding bugs and possible features or improvements – in the hope that it can become a package once tested and cleaned up.
Best Answer
0. tl;dr
knitr
is preferable toSweave
, andezknitr
is a wrapper aroundknitr
that is probably worth using—especially if you are only building documents from the command line (but this limits you to R Markdown; see below); I don't think there are IDEs that have integratedezknitr
use (at least not at the time of writing)—because it makes it easier to ensure the directories and paths are all correct.knitr
/ezknitr
(henceforth justknitr
) may or may not be preferable to Thruston's suggested approach approach, depending on your use-case.What follows is some justification for these points, coupled with examples.
1.
knitr
vs.Sweave
knitr
is preferrable toSweave
for a variety of reasons. Two main reasons to preferknitr
toSweave
are (i) you get better integration withtikzDevice
inknitr
, and (ii) chunk options are more versatile.1.1.
knitr
andtikzDevice
I should mention the caveat that I've never really used
Sweave
, but my understanding from reading blog posts on the internet is that it is much more straightforward to usetikzDevice
withknitr
than it is withSweave
.Two reasons you might want to use
tikzDevice
with your graphs are because (i) you get better typesetting in labels and titles (especially of math), and (ii) you get a consistent font between the text in your document and the text in your graphs inside of your document. Here's an MWE showing both of these things.1This produces the following output:
1.2. More versatile chunk options in
knitr
(compared toSweave
)This example is taken directly from Yihui. In
knitr
(but notSweave
), it is possible to delay the evaluation of certain chunk options, so that you could, for example, include the p-value of a t-test in a caption.The output of this is:
2.
knitr
vs. Thruston's suggested approachIf you prefer to keep your R code and your LaTeX code separate, Thruston's suggested approach is not necessarily preferable, because it is possible to use external R code in a LaTeX document with
knitr
. That being said, there are some advantages and disadvantages of the two different approaches that are worth mentioning.Some advantages of
knitr
over Thruston's suggested approach are:Some advantages of Thruston's suggested approach over
knitr
are:Using
knitr
to make a document more reproducible (but trading off in readability):Not using
knitr
but copying and pasting the values from the output of an R script (thus arguably being more human-readable but introducing more possibility for human error):3.
ezknitr
vs.knitr
UPDATE: It seems that
ezknitr
does not currently process.Rnw
files. Hopefully this is a feature that will be added in the future (see here; also see here).I have yet to try outezknitr
myself, so I'll have to update this answer once I have a chance to do so, but the blog post that introducesezknitr
suggests that it addresses problems with paths and working directories that can sometimes be frustrating. To quote from the blog post:This is presumably useful, especially if you are building documents from the command line for a project with files in many different directories.4. Compiling (from the command line)
For posterity: RStudio—for the most part—is a good IDE for use with
knitr
and LaTeX (things get dicey as soon as you have a bibliography involved).You said you were more interested in compiling documents from the command line. When you use
knitr
, you edit a.Rnw
file and then you process it withknitr
'sknit()
function, which outputs a.tex
file. You never want to edit that.tex
file directly. All changes should be made to the.Rnw
file, and then you should regenerate the.tex
file usingknit()
.Thus, you could build your document from the command line by doing something like this:
You could also easily write some sort of batch/make/bash script to do this.2
Notes
dev
chunk option totikz
inknitr
and loadingfontspec
, so it's not possible to use an arbitrary font with XeLaTeX or LuaLaTeX, unfortunately. Hopefully this is an issue that will be fixed soon.arara
to build.Rnw
documents from the command line, but in the upcoming version ofarara
, Paulo has promised an out-of-the-box and batteries-includedarara
rule that works withknitr
, so it should be possible to usearara
to build.Rnw
documents in the (near) future.