Volker Braun avatar Volker Braun committed f236a1b

add commandline environment, typeset with listings package

Comments (0)

Files changed (3)

 Jason Grout
 Rob Beezer
 Nicolas Thiéry
+Volker Braun
 % Let's begin by loading some packages. The key bits of |sageblock| and
 % friends are stol---um, adapted from the |verbatim| package manual. So
 % grab the |verbatim| package. We also need the |fancyvrb| package for
-% the |sageexample| environment.
+% the |sageexample| environment
 %    \begin{macrocode}
 \RequirePackage{verbatim}
 \RequirePackage{fancyvrb}
 %    \end{macrocode}
+% and |listings| for the |sagecommandline| environment.
+%    \begin{macrocode}
+\RequirePackage{listings}
+\RequirePackage{color}
+\lstdefinelanguage{Sage}[]{Python}
+  {morekeywords={False,sage,True},sensitive=true}
+\lstdefinelanguage{SageOutput}[]{}
+  {morekeywords={False,True},sensitive=true}
+\lstdefinestyle{DefaultSageInputOutput}{
+  nolol,
+  identifierstyle=,
+  name=sagecommandline,
+  xleftmargin=5pt,
+  numbersep=5pt,
+  aboveskip=0pt,
+  belowskip=0pt,
+  breaklines=true,
+  numberstyle=\footnotesize,
+  numbers=right
+}
+\lstdefinestyle{DefaultSageInput}{
+  language=Sage,
+  style=DefaultSageInputOutput,
+  basicstyle={\ttfamily\bfseries},
+  commentstyle={\ttfamily\color{dgreencolor}},
+  keywordstyle={\ttfamily\color{dbluecolor}\bfseries},
+  stringstyle={\ttfamily\color{dgraycolor}\bfseries},
+}
+\lstdefinestyle{DefaultSageOutput}{
+  language=SageOutput,
+  style=DefaultSageInputOutput,
+  basicstyle={\ttfamily},
+  commentstyle={\ttfamily\color{dgreencolor}},
+  keywordstyle={\ttfamily\color{dbluecolor}},
+  stringstyle={\ttfamily\color{dgraycolor}},
+}
+\lstdefinestyle{SageInput}{
+  style=DefaultSageInput,
+}
+\lstdefinestyle{SageOutput}{
+  style=DefaultSageOutput,
+}
+\definecolor{dbluecolor}{rgb}{0.01,0.02,0.7}
+\definecolor{dgreencolor}{rgb}{0.2,0.4,0.0}
+\definecolor{dgraycolor}{rgb}{0.30,0.3,0.30}
+%    \end{macrocode}
 % Unsurprisingly, the |\sageplot| command works poorly without graphics
 % support.
 %    \begin{macrocode}
 %   \texttt{sageexample} environment}
 % \end{environment}
 %
+%
+% \begin{environment}{sagecommandline}
+%   This environment is similar to the |sageexample| environment, but
+%   typesets the sage output as text with python syntax highlighting.
+%    \begin{macrocode}
+\newcommand{\sagecommandlinetextoutput}{True}
+\newlength{\sagecommandlineskip}
+\setlength{\sagecommandlineskip}{8pt}
+\newenvironment{sagecommandline}{%
+   \ST@wsf{%
+try:^^J
+ _st_.commandline(\theST@inline, r"""}%
+   \ST@dodfsetup%
+   \ST@wdf{Sage commandline, line \the\inputlineno::^^J}%
+   \begingroup%
+   \@bsphack%
+   \let\do\@makeother\dospecials%
+   \catcode`\^^M\active%
+   \def\verbatim@processline{%
+     \ST@wsf{\the\verbatim@line}%
+     \ST@wdf{\the\verbatim@line}%
+   }%
+   \verbatim@start%
+}
+{
+  \@esphack%
+  \endgroup%
+  \ST@wsf{%
+    """, globals(), locals(), \sagecommandlinetextoutput)^^Jexcept:^^J
+    _st_.goboom(\the\inputlineno)}%
+  \ifST@paused%
+    \mbox{(Sage\TeX{} is paused)}%
+  \else%
+    \begin{NoHyper}\ref{@sageinline\theST@inline}\end{NoHyper}%
+    \@ifundefined{r@@sageinline\theST@inline}{\gdef\ST@rerun{x}}{}%
+  \fi%
+  \ST@wdf{}%
+  \stepcounter{ST@inline}}
+%    \end{macrocode}
+% \end{environment}
+%
 % \subsubsection{Pausing \ST}
 % \label{sec:pausing-sagetex}
 %
     self.useepstopdf = False
     self.plotdir = 'sage-plots-for-' + jobname + '.tex'
     self.filename = jobname
+    self.name = os.path.splitext(jobname)[0]
+    autogenstr = """% This file was *autogenerated* from {0}.sagetex.sage with
+ % sagetex.py version {1}\n""".format(self.name, version)"""
 %    \end{macrocode}
+% Don't remove the space before the percent sign above!
+%
 % Open a |.sout.tmp| file and write all our output to that. Then, when
 % we're done, we move that to |.sout|. The ``autogenerated'' line is
 % basically the same as the lines that get put at the top of preparsed
 % version mismatch problems.
 %    \begin{macrocode}
     self.souttmp = open(self.filename + '.sagetex.sout.tmp', 'w')
-    self.souttmp.write("""% This file was *autogenerated* from {0}.sage with
- % sagetex.py version {1}\n""".format(os.path.splitext(jobname)[0], version))
+    self.souttmp.write(autogenstr)
 %    \end{macrocode}
-% Don't remove the space before the percent sign above!
+% In addition to the |.sout| file, the |sagecommandline| also needs a
+% |.scmd| file. As before, we use a |.scmd.tmp| file and rename it
+% later on. We store the file and position in the data members
+%    \begin{macrocode}
+    self.scmdtmp = open(self.filename + '.sagetex.scmd.tmp', 'w')
+    self.scmdtmp.write(autogenstr)
+    self.scmdpos = 3
+%    \end{macrocode}
 %
 % \begin{macro}{progress}
 % This function just prints stuff. It allows us to not print a
 % works whether or not |hyperref| is loaded. Using two fields, as in
 % plain \LTX, doesn't work if |hyperref| is loaded.
 % \end{macro}
+
+% \begin{macro}{savecmd}
+%   Analogous to |inline|, this method saves the input string |s| to
+%   the temporary |.scmd| file. As an added bonus, it returns a pair
+%   of line numbers in the |.scmd| file, the first and last line of
+%   the newly-added output.
+%    \begin{macrocode}
+  def savecmd(self, counter, s):
+    self.progress('Sage commandline {0}'.format(counter))
+    self.scmdtmp.write(s.rstrip() + "\n")
+    begin = self.scmdpos
+    end = begin + len(s.splitlines()) - 1
+    self.scmdpos = end+1
+    return begin, end
+%    \end{macrocode}
+% \end{macro}
 %
 % \begin{macro}{blockbegin}
 % \begin{macro}{blockend}
 % \changes{v2.2.5}{2010/03/25}{Fix up spacing in sageexample displaymath envs}
 % \end{macro}
 %
+% \begin{macro}{commandline}
+%   This function handles the |commandline| environment, which
+%   typesets Sage code and its output.
+%    \begin{macrocode}
+  def commandline(self, counter, str, globals, locals, text_output):
+      print 'in commandline'
+      current_statement = None
+      current_lines = None
+      line_iterator = (line.lstrip() for line in str.splitlines())
+      latex_string = r"\vspace{\sagecommandlineskip}" + "\n"
+      bottom_skip = ''
+
+      # Gobbles everything until the first "sage: ..." block
+      for line in line_iterator:
+          if line.startswith("sage: "):
+              break
+      else:
+          return
+      sage_block = 0
+      while True:
+          # At each
+          assert line.startswith("sage: ")
+          current_statement  = line[6:]
+          current_lines = line
+          for line in line_iterator:
+              if line.startswith("sage: "):
+                  break
+              elif line.startswith("...   "):
+                  current_statement += "\n"+line[6:]
+                  current_lines += "\n"+line
+          else:
+              line = None # we reached the last line
+          # Now have everything from "sage:" to the next "sage:"
+
+          if current_lines.find('#@')>=0:
+              escapeoption = ',escapeinside={\\#@}{\\^^M}'
+          else:
+              escapeoption = ''
+
+          begin, end = self.savecmd(counter,current_lines)
+          latex_string += r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style=SageInput{3}]{{{4}.scmd}}".format(begin, end, begin-2, escapeoption, self.name)+"\n"
+
+          current_statement = preparse(current_statement)
+          try:  # is it an expression?
+              result = eval(current_statement, globals, locals)
+              resultstr = "{0}".format(result)
+              begin, end = self.savecmd(counter,resultstr)
+              if text_output:
+                latex_string += r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style=SageOutput]{{{3}.scmd}}".format(begin, end, begin-2, self.name)+"\n"
+                bottom_skip = r"\vspace{\sagecommandlineskip}" + "\n"
+              else:
+                latex_string += (
+                  r"\begin{displaymath}" + "\n" +
+                  latex(result)          + "\n" +
+                  r"\end{displaymath}"   + "\n" )
+                bottom_skip = ''
+          except SyntaxError:  # must be a statement!
+              exec current_statement in globals, locals
+          current_lines = current_statement = None
+          if line is None: break
+          sage_block += 1
+      latex_string += bottom_skip + r"\noindent" + "\n"
+      self.inline(counter, latex_string)
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{plot}
 % \phantomsection\label{plotfn}
 % I hope it's obvious that this function does plotting. It's the Python
 % tracebacks the user sees refer to that file, whose line numbers are
 % basically useless. We want to tell them where in the \LTX file
 % things went bad, so we do that, give them the traceback, and exit
-% after removing the |.sout.tmp| file.
+% after removing the |.sout.tmp| and |.scmd.tmp| file.
 %    \begin{macrocode}
   def goboom(self, line):
     print('\n**** Error in Sage code on line {0} of {1}.tex! Traceback\
  again.'.format(self.filename))
     self.souttmp.close()
     os.remove(self.filename + '.sagetex.sout.tmp')
+    self.scmdtmp.close()
+    os.remove(self.filename + '.sagetex.scmd.tmp')
     sys.exit(int(1))
 %    \end{macrocode}
 % We use |int(1)| above to make sure |sys.exit| sees a Python integer;
     s = '%' + m.hexdigest() + '% md5sum of corresponding .sage file\
  (minus "goboom" and pause/unpause lines)\n'
     self.souttmp.write(s)
+    self.scmdtmp.write(s)
 %    \end{macrocode}
 % Now, we do issue warnings to run Sage on the |.sage| file and an
 % external program might look for those to detect the need to rerun

sagetexpackage.dtx

 %</driver>
 % \fi
 %
-% \CheckSum{454}
+% \CheckSum{0}
 %
 % \CharacterTable
 %  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
 % filename, including it into your document, and so on. In
 % \autoref{sec:usage}, we will see what what we can do with \ST.
 %
+% \subsection{Sage Command Line}
+%
+% Yet another way to use \ST{} is as a pretty-printing command line,
+% this is provided by the |sagecommandline| environment. For example,
+% you can write
+% \begin{quote}
+% |\begin{sagecommandline}|\\
+% |  sage: 1+1                       # difficult|\\
+% |  sage: ( x^2+2*x+1 ).factor()    #@\label{square}|\\
+% |  (x + 1)^2|\\
+% |\end{sagecommandline}|
+% \end{quote}
+% As you can see, you have a choice of either explicitly providing the
+% Sage output (in which case it will be turned into a doctest), or
+% leaving it up to the computer to fill in the blanks. Moreover, any
+% sage comment that starts with a ``at'' sign is escaped to \LTX. In
+% particular, you can use |\label| to mark line numbers in order to
+% |\ref|erence and |\pageref|erence them as usual. The output looks
+% like this:
+\begin{sagecommandline}
+  sage: 1+1                       # difficult
+  sage: ( x^2+2*x+1 ).factor()    #@\label{square}
+  (x + 1)^2
+\end{sagecommandline}
+% and the label is on Line~\ref{square}, Page~\pageref{square}. If you
+% prefer to typeset the output in \LTX, you can set
+% \begin{quote}
+% |\renewcommand{\sagecommandlinetextoutput}{False}|
+% \end{quote}
+% which produces
+\renewcommand{\sagecommandlinetextoutput}{False}
+\begin{sagecommandline}
+  sage: var('a, b, c');
+  sage: ( a*x^2+b*x+c ).solve(x)
+\end{sagecommandline}
+\renewcommand{\sagecommandlinetextoutput}{True}
+% The Sage input and output is typeset using the |listings| package
+% with the styles |SageInput| and |SageOutput|, respectively. If you
+% don't like the defaults you can change them. It is recommended to
+% derive from |DefaultSageInput| and |DefaultSageOutput|, for example
+% \begin{quote}
+%   |\lstdefinestyle{SageInput}{style=DefaultSageInput,|\\
+%   |                           basicstyle={\color{red}}}|\\
+%   |\lstdefinestyle{SageOutput}{style=DefaultSageOutput,|\\
+%   |                            basicstyle={\color{green}}}|
+% \end{quote}
+% makes things overly colorful:
+\lstdefinestyle{SageInput}{style=DefaultSageInput,basicstyle={\color{red}}}
+\lstdefinestyle{SageOutput}{style=DefaultSageOutput,basicstyle={\color{green}}}
+\begin{sagecommandline}
+  sage: pi.n(100)
+\end{sagecommandline}
+\lstdefinestyle{SageInput}{style=DefaultSageInput}
+\lstdefinestyle{SageOutput}{style=DefaultSageOutput}
+%
+%
 % \section{Installation}
 % \label{sec:installation}
 % \changes{v2.0}{2009/01/14}{Fixed up installation section, final
 % Let's begin with a rough description of how \ST works. Naturally the
 % very first step is to put |\usepackage{sagetex}| in the preamble of
 % your document. When you use macros from this package and run \LTX on
-% your file, along with the usual zoo of auxiliary files, a |.sage| file
-% is written with the same basename as your document. This is a Sage
-% source file that uses the Python module from this package and when you
-% run Sage on that file, it will produce a |.sout| file. That file
-% contains \LTX code that, when you run \LTX on your source file again,
-% will pull in all the results of Sage's computation.
+% your file, along with the usual zoo of auxiliary files, a |.sage|
+% file is written with the same basename as your document. This is a
+% Sage source file that uses the Python module from this package and
+% when you run Sage on that file, it will produce a |.sout| and a
+% |.scmd| file. That file contains \LTX code that, when you run \LTX
+% on your source file again, will pull in all the results of Sage's
+% computation.
+%
+% The |sagecommandline| environment additionally logs the plain sage
+% commands and output furthermore in a |.scmd| file.
 %
 % All you really need to know is that to typeset your document, you need
 % to run \LTX, then run Sage, then run \LTX again. You can even ``run
 % The initial implementation of this environment is due to Nicolas M.
 % Thi\'ery.\\
 %
+% \DescribeEnv{sagecommandline} This environment is similar to the
+% sageexample environment in that it allow you to include doctest-like
+% snippets in your document. The difference is that the output is
+% typeset as text, much like running Sage on the command line, using
+% the lstlisting environment. In particular, this environment provides
+% Python syntax highlighting and line numbers.
+% For example,
+% \begin{quote}
+% |\begin{sagecommandline}|\\
+% |  sage: 1+1|\\
+% |  2|\\
+% |  sage: factor(x^2 + 2*x + 1)|\\
+% |  (x + 1)^2|\\
+% |\end{sagecommandline}|
+% \end{quote}
+%
 % \DescribeMacro{\sagetexindent} There is one final bit to our
 % verbatim-like environments: the indentation. The \ST package defines a
 % length |\sagetexindent|, which controls how much the Sage code is
 % following files:
 % \begin{enumerate}
 %   \item |sagetex.sty|
-%   \item the generated |.sout| file
+%   \item the generated |.sout| and |.scmd| files
 %   \item the \texttt{sage-plots-for-\meta{filename}.tex} directory and
 %     its contents
 % \end{enumerate}
 % so, the results will be written to that file. If the file exists, it
 % won't be overwritten unless you also specify the |-o| switch.
 %
-% You will need to run this after you've compiled your document and run
-% Sage on the |.sage| file. The script reads in the |.sout| file and
-% replaces all the calls to |\sage| and |\sageplot| with their plain
-% \LTX equivalent, and turns the |sageblock| and |sageverbatim|
+% You will need to run this after you've compiled your document and
+% run Sage on the |.sage| file. The script reads in the |.sout| file
+% and replaces all the calls to |\sage| and |\sageplot| with their
+% plain \LTX equivalent, and turns the |sageblock| and |sageverbatim|
 % environments into |verbatim| environments. Any |sagesilent|
-% environment is turned into a |comment| environment. The resulting
-% document should compile to something identical, or very nearly so, to
-% the original file.
+% environment is turned into a |comment| environment. Any
+% |sagecommandline| environment is turned into a |lstlisting|
+% environment, typesetting the relevant part of the |.scmd| file. The
+% resulting document should compile to something identical, or very
+% nearly so, to the original file.
 %
 % One large limitation of this script is that it can't change anything
 % while \ST is paused, since Sage doesn't compute anything for such
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.