hgbook
changeset 4:33a2e7b9978d
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.
Instead of having to cut and paste example text, the task is automated.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Sun Jun 25 22:04:50 2006 -0700 (2006-06-25) |
parents | 906d9021f9e5 |
children | faa29ca23fc8 |
files | .hgignore en/99defs.tex en/Makefile en/examples/mq.qinit-help en/examples/run-example en/mq.tex |
line diff
1.1 --- a/.hgignore Sat Jun 24 17:42:40 2006 -0700 1.2 +++ b/.hgignore Sun Jun 25 22:04:50 2006 -0700 1.3 @@ -3,6 +3,7 @@ 1.4 1.5 syntax: glob 1.6 1.7 +.run 1.8 .*.swp 1.9 *~ 1.10 *.aux
2.1 --- a/en/99defs.tex Sat Jun 24 17:42:40 2006 -0700 2.2 +++ b/en/99defs.tex Sun Jun 25 22:04:50 2006 -0700 2.3 @@ -1,10 +1,11 @@ 2.4 -\newcommand{\tildefile}[1]{\texttt{\~/#1}} 2.5 +\newcommand{\tildefile}[1]{\texttt{\~{}/#1}} 2.6 \newcommand{\filename}[1]{\texttt{#1}} 2.7 \newcommand{\hgext}[1]{\texttt{#1}} 2.8 \newcommand{\hgcmd}[1]{``\texttt{hg #1}''} 2.9 \newcommand{\hgcmdargs}[2]{``\texttt{hg #1 #2}''} 2.10 2.11 -\DefineVerbatimEnvironment{codesample}{Verbatim}{frame=single,gobble=2,numbers=left} 2.12 +\DefineVerbatimEnvironment{codesample4}{Verbatim}{frame=single,gobble=4,numbers=left,commandchars=\\\{\}} 2.13 +\newcommand{\interaction}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{examples/#1.out}} 2.14 2.15 %%% Local Variables: 2.16 %%% mode: latex
3.1 --- a/en/Makefile Sat Jun 24 17:42:40 2006 -0700 3.2 +++ b/en/Makefile Sun Jun 25 22:04:50 2006 -0700 3.3 @@ -4,6 +4,10 @@ 3.4 99defs.tex \ 3.5 mq.tex 3.6 3.7 +example-sources := \ 3.8 + examples/run-example \ 3.9 + examples/mq.qinit-help 3.10 + 3.11 latex-options = \ 3.12 -interaction batchmode \ 3.13 -output-directory $(dir $(1)) \ 3.14 @@ -13,7 +17,7 @@ 3.15 3.16 pdf: pdf/hgbook.pdf 3.17 3.18 -pdf/hgbook.pdf: $(sources) 3.19 +pdf/hgbook.pdf: $(sources) examples 3.20 mkdir -p $(dir $@) 3.21 pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) 3.22 cp 99book.bib $(dir $@) 3.23 @@ -32,11 +36,18 @@ 3.24 cd $(dir $(1)) && t4ht -f/$(basename $(notdir $(1))) 3.25 endef 3.26 3.27 -html/onepage/hgbook.html: $(sources) 3.28 +html/onepage/hgbook.html: $(sources) examples 3.29 $(call htlatex,$@,$<) 3.30 3.31 -html/split/hgbook.html: $(sources) 3.32 +html/split/hgbook.html: $(sources) examples 3.33 $(call htlatex,$@,$<,2) 3.34 3.35 +.PHONY: examples 3.36 + 3.37 +examples: examples/.run 3.38 + 3.39 +examples/.run: $(example-sources) 3.40 + cd examples && ./run-example 3.41 + 3.42 clean: 3.43 rm -rf html pdf *.aux *.dvi *.log *.out
4.1 --- a/en/examples/mq.qinit-help Sat Jun 24 17:42:40 2006 -0700 4.2 +++ b/en/examples/mq.qinit-help Sun Jun 25 22:04:50 2006 -0700 4.3 @@ -1,2 +1,2 @@ 4.4 -# name: help 4.5 +#$ name: help 4.6 hg help qinit
5.1 --- a/en/examples/run-example Sat Jun 24 17:42:40 2006 -0700 5.2 +++ b/en/examples/run-example Sun Jun 25 22:04:50 2006 -0700 5.3 @@ -1,16 +1,36 @@ 5.4 #!/usr/bin/python 5.5 +# 5.6 +# This program takes something that resembles a shell script and runs 5.7 +# it, spitting input (commands from the script) and output into text 5.8 +# files, for use in examples. 5.9 5.10 import cStringIO 5.11 import os 5.12 import pty 5.13 import re 5.14 +import shutil 5.15 import sys 5.16 +import tempfile 5.17 +import time 5.18 5.19 +def tex_escape(s): 5.20 + if '\\' in s: 5.21 + s = s.replace('\\', '\\\\') 5.22 + if '{' in s: 5.23 + s = s.replace('{', '\\{') 5.24 + if '}' in s: 5.25 + s = s.replace('}', '\\}') 5.26 + return s 5.27 + 5.28 class example: 5.29 + shell = '/bin/bash' 5.30 + pi_re = re.compile('#\$\s*(name):\s*(.*)$') 5.31 + 5.32 def __init__(self, name): 5.33 self.name = name 5.34 5.35 def parse(self): 5.36 + '''yield each hunk of input from the file.''' 5.37 fp = open(self.name) 5.38 cfp = cStringIO.StringIO() 5.39 for line in fp: 5.40 @@ -20,28 +40,54 @@ 5.41 cfp.seek(0) 5.42 cfp.truncate() 5.43 5.44 - name_re = re.compile('#\s*name:\s*(.*)$') 5.45 - 5.46 def status(self, s): 5.47 sys.stdout.write(s) 5.48 if not s.endswith('\n'): 5.49 sys.stdout.flush() 5.50 5.51 + def drain(self, ifp, ofp): 5.52 + while True: 5.53 + s = ifp.read(4096) 5.54 + if not s: break 5.55 + if ofp: ofp.write(tex_escape(s)) 5.56 + 5.57 def run(self): 5.58 ofp = None 5.59 - self.status('running %s ' % os.path.basename(self.name)) 5.60 - for hunk in self.parse(): 5.61 - m = self.name_re.match(hunk) 5.62 - if m: 5.63 - self.status('.') 5.64 - out = m.group(1) 5.65 - assert os.sep not in out 5.66 - if out: 5.67 - ofp = open('%s.%s.out' % (self.name, out), 'w') 5.68 + basename = os.path.basename(self.name) 5.69 + self.status('running %s ' % basename) 5.70 + tmpdir = tempfile.mkdtemp(prefix=basename) 5.71 + try: 5.72 + for hunk in self.parse(): 5.73 + # is this line a processing instruction? 5.74 + m = self.pi_re.match(hunk) 5.75 + if m: 5.76 + pi, rest = m.groups() 5.77 + if pi == 'name': 5.78 + self.status('.') 5.79 + out = rest 5.80 + assert os.sep not in out 5.81 + if out: 5.82 + ofp = open('%s.%s.out' % (self.name, out), 'w') 5.83 + else: 5.84 + ofp = None 5.85 else: 5.86 - ofp = None 5.87 - elif ofp: ofp.write(hunk) 5.88 - self.status('\n') 5.89 + # it's something we should execute 5.90 + cin, cout = os.popen4('cd %s; %s' % (tmpdir, hunk)) 5.91 + cin.close() 5.92 + if ofp: 5.93 + # first, print the command we ran 5.94 + if not hunk.startswith('#'): 5.95 + nl = hunk.endswith('\n') 5.96 + hunk = ('$ \\textbf{%s}' % 5.97 + tex_escape(hunk.rstrip('\n'))) 5.98 + if nl: hunk += '\n' 5.99 + ofp.write(hunk) 5.100 + # then its output 5.101 + self.drain(cout, ofp) 5.102 + self.status('\n') 5.103 + finally: 5.104 + os.wait() 5.105 + shutil.rmtree(tmpdir) 5.106 5.107 def main(path='.'): 5.108 args = sys.argv[1:] 5.109 @@ -53,6 +99,7 @@ 5.110 if name == 'run-example' or name.startswith('.'): continue 5.111 if name.endswith('.out') or name.endswith('~'): continue 5.112 example(os.path.join(path, name)).run() 5.113 + print >> open(os.path.join(path, '.run'), 'w'), time.asctime() 5.114 5.115 if __name__ == '__main__': 5.116 main()
6.1 --- a/en/mq.tex Sat Jun 24 17:42:40 2006 -0700 6.2 +++ b/en/mq.tex Sun Jun 25 22:04:50 2006 -0700 6.3 @@ -131,16 +131,26 @@ 6.4 Because MQ is implemented as an extension, you must explicitly enable 6.5 before you can use it. (You don't need to download anything; MQ ships 6.6 with the standard Mercurial distribution.) To enable MQ, edit your 6.7 -\tildefile{.hgrc} file, and add the following lines: 6.8 +\tildefile{.hgrc} file, and add the lines in figure~\ref{ex:mq:config}. 6.9 6.10 -\begin{codesample} 6.11 - [extensions] 6.12 - hgext.mq = 6.13 -\end{codesample} 6.14 +\begin{figure} 6.15 + \begin{codesample4} 6.16 + [extensions] 6.17 + hgext.mq = 6.18 + \end{codesample4} 6.19 + \label{ex:mq:config} 6.20 + \caption{Contents to add to \tildefile{.hgrc} to enable the MQ extension} 6.21 +\end{figure} 6.22 6.23 Once the extension is enabled, it will make a number of new commands 6.24 -available. 6.25 +available. To verify that the extension is working, follow the 6.26 +example in figure~\ref{ex:mq:enabled}. 6.27 6.28 +\begin{figure} 6.29 + \interaction{mq.qinit-help.help} 6.30 + \caption{How to verify that MQ is enabled} 6.31 + \label{ex:mq:enabled} 6.32 +\end{figure} 6.33 6.34 %%% Local Variables: 6.35 %%% mode: latex