4

Consider the following code:

\documentclass[10pt,letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{calc}
\usepackage{tikz}
\usepackage{listings}
\usepackage[listings,skins,theorems]{tcolorbox}

\newlength{\numlen}
\newcommand{\getnumlen}[1]{
    \setlength{\numlen}{\widthof{\fontfamily{cmtt}\selectfont#1 0123456789}}
    \setlength{\numlen}{0.1\numlen}
}
\newtcblisting{mytcblisting}[1][]{
    before = {\getnumlen{#1}},
    enhanced,
    left = 4.\numlen,%\numlen is correct here
    listing only,
    listing options = {
        language = C++,
        basicstyle=\fontfamily{cmtt}\selectfont#1,
        showstringspaces = false,
        numbers = left,
        numbersep = 4.\numlen,%\numlen seems to be 0 here
    },
}

\begin{document}
\begin{mytcblisting}[\tiny]
int main(int argc, char* argv[]) {
    return 0;
}
\end{mytcblisting}
\end{document}

I would like the left measure and the separation between the numbers and the code numbersep be based on the typical length of a number written in the cmtt family with the size passed as an argument of mytcblisting.

But with this code, the left option works well, but not the numbersep.

How to make it work?

  • Apparently tcblisting evaluates listing options before \before, having a zero value then. You can see this, if you set \setlength{\newlength}{20pt} in the preamble -- it will use this value then for the spacing. – user31729 Jul 6 '15 at 12:08
  • @ChristianHupfer Yes, I was assuming that was the origin of the problem... but the question remains: how to make it work (have the numbersep based on the size passed as argument #1)? – Vincent Jul 6 '15 at 12:19
  • Well, I looked into listings to get some clue what could be done from that side that \getnumlen{#1} could be called there as well – user31729 Jul 6 '15 at 12:20
  • @ChristianHupfer Does that mean that you have a working solution? If so, how to call \getnumlen{#1} in the listing options? – Vincent Jul 6 '15 at 12:29
  • 1
    Just replace before={...} by code={...}. This code is executed immediately, but before is used later when the box is drawn. – Thomas F. Sturm Jul 6 '15 at 13:16
4

Apparently, listing options is evaluated before before=\getnumlen{#1} is used and numlen set. This means that numlen is 0pt --> the \getnumlen{#1} must be called in this special option block (too).

A dirty trial --> Use \getnumlen{#1} in basicstyle option --> it's an effective code macro and no typesetting/font macro, so it should not harm there.

\documentclass[10pt,letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{calc}
\usepackage{tikz}
\usepackage{listings}
\usepackage[listings,skins,theorems]{tcolorbox}

\newlength{\numlen}
\newcommand{\getnumlen}[1]{%
  \setlength{\numlen}{\widthof{\fontfamily{cmtt}\selectfont#1 0123456789}}%
  \setlength{\numlen}{0.1\numlen}%
}
\newtcblisting{mytcblisting}[1][]{%
    listing options = {%
        language = C++,
        basicstyle=\getnumlen{#1}\fontfamily{cmtt}\selectfont#1,
        showstringspaces = false,
        numbers = left,
        numbersep = 4.\numlen,%\numlen seems to be 0 here
    },%
    before = {\getnumlen{#1}},
    enhanced,
    left = 4.\numlen,%\numlen is correct here
    listing only
}

\begin{document}
\begin{mytcblisting}[\tiny]
int main(int argc, char* argv[]) {
    return 0;
}
\end{mytcblisting}
\end{document}

Edit A cleaner way: Add a key to the lst key family:

\documentclass[10pt,letterpaper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{calc}
\usepackage{tikz}
\usepackage{listings}
\usepackage[listings,skins,theorems]{tcolorbox}

\makeatletter
\define@key{lst}{numlen}[\protect\tiny]{%%
  \getnumlen{#1}%
}
\makeatother

\newlength{\numlen}
\newcommand{\getnumlen}[1]{%
  \setlength{\numlen}{\widthof{\fontfamily{cmtt}\selectfont#1 0123456789}}%
  \setlength{\numlen}{0.1\numlen}%
}
\newtcblisting{mytcblisting}[1][]{%
    listing options = {%
        language = C++,
        basicstyle=\fontfamily{cmtt}\selectfont#1,
        showstringspaces = false,
        numbers = left,
        numlen={\protect #1},
        numbersep = 6.\numlen,%\numlen seems to be 0 here
    },%
    before = {\getnumlen{\protect #1}},
    enhanced,
    left = 4.\numlen,%\numlen is correct here
    listing only
}

\begin{document}
\begin{mytcblisting}[\tiny]
int main(int argc, char* argv[]) {
    return 0;
}
\end{mytcblisting}


\end{document}
  • In the edited example, I guess it's \protect #1 instead of \protect\tiny – Vincent Jul 6 '15 at 13:20
  • @Vincent: Yes, you're right. A typo. But it works too -- well, works in the sense that numlen isn't zero any longer. – user31729 Jul 6 '15 at 13:21

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.