6

I am attempting to document my MATLAB programming code in LaTeX. For this, I use the mcode package, which defines the MATLAB lstlisting environment. With the commands

\newcommand{\mfileMain}{main.m}
\newcommand{\mfileMainFirstNumber}{\mfileMainFirstLine}
\newcommand{\mfileMainFirstLine}{26}
\newcommand{\mfileMainLastLine}{33}
\lstinputlisting[firstnumber=\mfileMainFirstNumber,
                 firstline=\mfileMainFirstLine,
                 lastline=\mfileMainLastLine]{\mfileMain}

I set the firstnumber, firstline and lastline parameters and use them to import a piece of MATLAB code into the LaTeX document with \lstinputlisting. However, when I add some code before the \mfileMainFirstLine, in this case 26, the code is shifted and the documentation is ruined. To fix this, I would have to update the line numbers.

I am trying to fix this issue by adding 'tracers' to my MATLAB program. I use the commands

filename=['lstinputlisting_line_numbers_' mfilename '.txt'];
fileID=fopen(filename,'w');

to create a text file in which I store the line numbers for which the code is documented using the command

dbstack_eval=dbstack;fprintf(fileID,[num2str(dbstack_eval.line+1) '\n']);

. This results in a text file lstinputlisting_line_numbers_.txt with content for instance like

53
62
75
91

in case I want to document four parts of the MATLAB program main.m.

What I want to do from here is simply use this line number data in the \lstinputlisting commands. Perhaps it is possible to store the line numbers in the \newcommands{}{}, or perhaps there is a better, more elegant way of doing it. In this way, the line numbers would update automatically in case I add new lines of code. Consequently, my LaTeX document would still be okay, saving me a lot of time not having to update the line numbers myself.

A minimum working example could look like the following. The LaTeX file main.tex would have the content

\documentclass[a4paper]{article}
\usepackage[framed,numbered]{mcode}

% Data for the 'lstinputlisting' environments.
\newcommand{\mfileMain}{main.m}
\newcommand{\mfileMainFirstNumberA}{\mfileMainFirstLineA}
\newcommand{\mfileMainFirstLineA}{WHAT SHOULD I PUT HERE?}
\newcommand{\mfileMainLastLineA}{WHAT SHOULD I PUT HERE?}
\newcommand{\mfileMainFirstNumberB}{\mfileMainFirstLineB}
\newcommand{\mfileMainFirstLineB}{WHAT SHOULD I PUT HERE?}
\newcommand{\mfileMainLastLineB}{WHAT SHOULD I PUT HERE?}

\begin{document}
    In the code
    \lstinputlisting[firstnumber=\mfileMainFirstNumberA,
                     firstline=\mfileMainFirstLineA,
                     lastline=\mfileMainLastLineA]{\mfileMain}
    we calculate the .... 

    And in the code
    \lstinputlisting[firstnumber=\mfileMainFirstNumberB,
                     firstline=\mfileMainFirstLineB,
                     lastline=\mfileMainLastLineB]{\mfileMain}
    we calculate the.....
\end{document}

. The MATLAB file main.m would have the content

%% Line number output.

filename=['lstinputlisting_line_numbers_' mfilename '.txt'];
fileID=fopen(filename,'w');

dbstack_eval=dbstack;fprintf(fileID,[num2str(dbstack_eval.line+1) '\n']);    
%% Input to be specified by user.

alpha=10; % PDE parameter.
x_s=0; % Starting coordinate.
x_e=1; % End coordinate.
N_n=1e2+1; % (N)umber of (n)odes.
N_ip=2; % (N)umber of (i)ntegration (p)oints.

dbstack_eval=dbstack;fprintf(fileID,[num2str(dbstack_eval.line+1) '\n']);    
fclose(fileID); % Close the file 'fileID'.
  • This should be possible -- I try a solution – user31729 Sep 8 '14 at 8:59
  • 2
    You should have a look at the matlab-prettifier package; it improves upon mcode in many ways. See this answer, for instance. – jub0bs Sep 8 '14 at 9:20
  • 1
    Wouldn't it be simpler to use linerange markers in MATLAB comments? See subsection 5.7 in the listings manual. – jub0bs Sep 8 '14 at 9:25
  • 1
    @Jubobs Indeed the matlab-prettifier package looks great. But it does not solve the current problem, or does it? And concerning Section 5.7 in the listings manual....I don't really understand it :(... Perhaps it solves the issue. Could you perhaps take my minimum working example and adapt it accordingly in an answer here? Then everybody can test it. Thanks a lot! – Adriaan Sep 8 '14 at 9:44
  • 2
    @Adriaan See my answer. – jub0bs Sep 8 '14 at 9:53
8

In my opinion, it would be simpler and more robust to just use linerange markers for listings in comments of your m-file. See the example below. I used the matlab-prettifier package instead of mcode, but the approach should work with either.

Basically, you define a prefix for those linerange markers with the rangeprefix key; here, the prefix I use is simply a percent character (%) followed by a space character:

rangeprefix=\%\ ,

Note that both of those characters must be escaped here (but not in your MATLAB code). Then, in your code, you use a pair of descriptive strings to mark the start and end of each of the ranges of interest. In the example below, I used param and endparam for the first range of interest, and param2 and endparam2 for the second range of interest. Make sure to use

includerangemarker=false

if you don't want the markers themselves to appear in the output.

You can find more details about those linerange markers in subsection 5.7 of the listings manual.

enter image description here

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{matlab-prettifier}

\usepackage{filecontents}
\begin{filecontents*}{sample.m}
foo=3
bar=5
baz=foo
foo=bar
bar=baz
% param
% first set of parameters
alpha=10; % PDE parameter.
x_s=0; % Starting coordinate.
x_e=1; % End coordinate.
N_n=1e2+1; % (N)umber of (n)odes.
N_ip=2; % (N)umber of (i)ntegration (p)oints.
% endparam
clear baz
% param2
% second set of parameters
beta=20;
N = 42;
% endparam2
\end{filecontents*}

\lstset{
  style              = Matlab-editor,
  basicstyle         = \mlttfamily,
  rangeprefix        = \%\ ,
  includerangemarker = false,
}

\begin{document}
\lstinputlisting[linerange=param-endparam]{sample.m}
\lstinputlisting[linerange=param2-endparam2]{sample.m}
\end{document}
6

I am not sure, what the 4th number is expected to be, but this reads the numbers and assigns them to macros.

Apart from this all, one should consider Jubob's comments ;-)

\documentclass[paper=a4,12pt]{scrbook}

\usepackage{etoolbox}%
\usepackage{blindtext}%
\usepackage[framed,numbered]{mcode}%

\newread\mylinenumbers

\newcommand{\mfileMain}{main.m}%

\def\mFileMainFirstLine{}
\def\mFileMainFirstNumber{}
\def\mFileMainLastLine{}


\AtBeginDocument{%
  \openin\mylinenumbers=lstinputlisting_line_numbers_.tex
  \read\mylinenumbers to \mFileMainFirstNumber
  \read\mylinenumbers to \mFileMainFirstLine
  \read\mylinenumbers to \mFileMainLastLine
  \closein\mylinenumbers
}%




\begin{document}

\lstinputlisting[firstnumber=\mFileMainFirstNumber,
                     firstline=\mFileMainFirstLine,
                     lastline=\mFileMainLastLine]{\mfileMain}

\end{document}

Improved version

Use a separate command \ReadinLineNumbers just before the \lstinputlisting takes places, therefore updating the linenumber information.

In principle, this could be added to the \lstinputlisting` command directly, it would break for other calls of the command however.

\documentclass[paper=a4,12pt]{scrbook}
\usepackage[framed,numbered]{mcode}%

\newread\mylinenumbers

\newcommand{\mfileMain}{main.m}%

\def\mFileMainFirstLine{}
\def\mFileMainFirstNumber{}
\def\mFileMainLastLine{}

% This command assumes that there always triples for the line number information
\newcommand{\ReadinLineNumbers}{%
  \read\mylinenumbers to \mFileMainFirstNumber
  \read\mylinenumbers to \mFileMainFirstLine
  \read\mylinenumbers to \mFileMainLastLine
}%

\AtBeginDocument{%
  \openin\mylinenumbers=lstinputlisting_line_numbers_.tex
}%

\AtEndDocument{%
  \closein\mylinenumbers
}%

\begin{document}

\ReadinLineNumbers

\lstinputlisting[firstnumber=\mFileMainFirstNumber,
                     firstline=\mFileMainFirstLine,
                     lastline=\mFileMainLastLine]{\mfileMain}

\ReadinLineNumbers

\lstinputlisting[firstnumber=\mFileMainFirstNumber,
                     firstline=\mFileMainFirstLine,
                     lastline=\mFileMainLastLine]{\mfileMain}

\end{document}
  • 1
    \immediate has no effect on \openin and on \read. – egreg Sep 8 '14 at 9:22
  • @egreg: Right: I copied some code for writing and forgot the remove \immediate – user31729 Sep 8 '14 at 9:24
  • @ChristianHupfer Looks good, but this does not work for when one wants to use \lstinputlisting multiple times for different pieces of code from one main.m, right? – Adriaan Sep 8 '14 at 9:37
  • 1
    @Adriaan: That feature was not in your question, right? ;-) – user31729 Sep 8 '14 at 9:38
  • 1
    I will add a solution for that ;-) – user31729 Sep 8 '14 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.