Commits

Bryan O'Sullivan committed 33a2e7b

Make it possible to include example input and output from real programs.

Instead of having to cut and paste example text, the task is automated.

Comments (0)

Files changed (6)

 
 syntax: glob
 
+.run
 .*.swp
 *~
 *.aux
-\newcommand{\tildefile}[1]{\texttt{\~/#1}}
+\newcommand{\tildefile}[1]{\texttt{\~{}/#1}}
 \newcommand{\filename}[1]{\texttt{#1}}
 \newcommand{\hgext}[1]{\texttt{#1}}
 \newcommand{\hgcmd}[1]{``\texttt{hg #1}''}
 \newcommand{\hgcmdargs}[2]{``\texttt{hg #1 #2}''}
 
-\DefineVerbatimEnvironment{codesample}{Verbatim}{frame=single,gobble=2,numbers=left}
+\DefineVerbatimEnvironment{codesample4}{Verbatim}{frame=single,gobble=4,numbers=left,commandchars=\\\{\}}
+\newcommand{\interaction}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{examples/#1.out}}
 
 %%% Local Variables: 
 %%% mode: latex
 	99defs.tex \
 	mq.tex
 
+example-sources := \
+	examples/run-example \
+	examples/mq.qinit-help
+
 latex-options = \
 	-interaction batchmode \
 	-output-directory $(dir $(1)) \
 
 pdf: pdf/hgbook.pdf
 
-pdf/hgbook.pdf: $(sources)
+pdf/hgbook.pdf: $(sources) examples
 	mkdir -p $(dir $@)
 	pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1)
 	cp 99book.bib $(dir $@)
 	cd $(dir $(1)) && t4ht -f/$(basename $(notdir $(1)))
 endef
 
-html/onepage/hgbook.html: $(sources)
+html/onepage/hgbook.html: $(sources) examples
 	$(call htlatex,$@,$<)
 
-html/split/hgbook.html: $(sources)
+html/split/hgbook.html: $(sources) examples
 	$(call htlatex,$@,$<,2)
 
+.PHONY: examples
+
+examples: examples/.run
+
+examples/.run: $(example-sources)
+	cd examples && ./run-example
+
 clean:
 	rm -rf html pdf *.aux *.dvi *.log *.out

en/examples/mq.qinit-help

-# name: help
+#$ name: help
 hg help qinit

en/examples/run-example

 #!/usr/bin/python
+#
+# This program takes something that resembles a shell script and runs
+# it, spitting input (commands from the script) and output into text
+# files, for use in examples.
 
 import cStringIO
 import os
 import pty
 import re
+import shutil
 import sys
+import tempfile
+import time
 
+def tex_escape(s):
+    if '\\' in s:
+        s = s.replace('\\', '\\\\')
+    if '{' in s:
+        s = s.replace('{', '\\{')
+    if '}' in s:
+        s = s.replace('}', '\\}')
+    return s
+        
 class example:
+    shell = '/bin/bash'
+    pi_re = re.compile('#\$\s*(name):\s*(.*)$')
+    
     def __init__(self, name):
         self.name = name
 
     def parse(self):
+        '''yield each hunk of input from the file.'''
         fp = open(self.name)
         cfp = cStringIO.StringIO()
         for line in fp:
                 cfp.seek(0)
                 cfp.truncate()
         
-    name_re = re.compile('#\s*name:\s*(.*)$')
-    
     def status(self, s):
         sys.stdout.write(s)
         if not s.endswith('\n'):
             sys.stdout.flush()
 
+    def drain(self, ifp, ofp):
+        while True:
+            s = ifp.read(4096)
+            if not s: break
+            if ofp: ofp.write(tex_escape(s))
+        
     def run(self):
         ofp = None
-        self.status('running %s ' % os.path.basename(self.name))
-        for hunk in self.parse():
-            m = self.name_re.match(hunk)
-            if m:
-                self.status('.')
-                out = m.group(1)
-                assert os.sep not in out
-                if out:
-                    ofp = open('%s.%s.out' % (self.name, out), 'w')
+        basename = os.path.basename(self.name)
+        self.status('running %s ' % basename)
+        tmpdir = tempfile.mkdtemp(prefix=basename)
+        try:
+            for hunk in self.parse():
+                # is this line a processing instruction?
+                m = self.pi_re.match(hunk)
+                if m:
+                    pi, rest = m.groups()
+                    if pi == 'name':
+                        self.status('.')
+                        out = rest
+                        assert os.sep not in out
+                        if out:
+                            ofp = open('%s.%s.out' % (self.name, out), 'w')
+                        else:
+                            ofp = None
                 else:
-                    ofp = None
-            elif ofp: ofp.write(hunk)
-        self.status('\n')
+                    # it's something we should execute
+                    cin, cout = os.popen4('cd %s; %s' % (tmpdir, hunk))
+                    cin.close()
+                    if ofp:
+                        # first, print the command we ran
+                        if not hunk.startswith('#'):
+                            nl = hunk.endswith('\n')
+                            hunk = ('$ \\textbf{%s}' %
+                                    tex_escape(hunk.rstrip('\n')))
+                            if nl: hunk += '\n'
+                        ofp.write(hunk)
+                    # then its output
+                    self.drain(cout, ofp)
+            self.status('\n')
+        finally:
+            os.wait()
+            shutil.rmtree(tmpdir)
 
 def main(path='.'):
     args = sys.argv[1:]
         if name == 'run-example' or name.startswith('.'): continue
         if name.endswith('.out') or name.endswith('~'): continue
         example(os.path.join(path, name)).run()
+    print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
 
 if __name__ == '__main__':
     main()
 Because MQ is implemented as an extension, you must explicitly enable
 before you can use it.  (You don't need to download anything; MQ ships
 with the standard Mercurial distribution.)  To enable MQ, edit your
-\tildefile{.hgrc} file, and add the following lines:
+\tildefile{.hgrc} file, and add the lines in figure~\ref{ex:mq:config}.
 
-\begin{codesample}
-  [extensions]
-  hgext.mq =
-\end{codesample}
+\begin{figure}
+  \begin{codesample4}
+    [extensions]
+    hgext.mq =
+  \end{codesample4}
+  \label{ex:mq:config}
+  \caption{Contents to add to \tildefile{.hgrc} to enable the MQ extension}
+\end{figure}
 
 Once the extension is enabled, it will make a number of new commands
-available.  
+available.  To verify that the extension is working, follow the
+example in figure~\ref{ex:mq:enabled}.
 
+\begin{figure}
+  \interaction{mq.qinit-help.help}
+  \caption{How to verify that MQ is enabled}
+  \label{ex:mq:enabled}
+\end{figure}
 
 %%% Local Variables: 
 %%% mode: latex