Using xargs to define custom environment

argumentsenvironmentsoptional arguments

I want to define a custom environment for the following LaTeX box using xargs package, but currently I am running into errors and a bit confused on how to properly make the environment.

First, here's the box in question and then the usage and finally environment:

    \newtcblisting[auto counter, number within=section, list inside=examplelist]{tcbexample}[2][]{%
    colback=gray!5, colbacktitle=gray!40, coltitle=black,
    frame hidden, arc=2pt, titlerule=0pt, toptitle=2pt, bottomtitle=2pt,
    fonttitle=\bfseries, breakable, enhanced, parbox=false,
    comment and listing,
    title=Example~\thetcbcounter,
    comment={#2},#1
    }

Here's an example of the box in use and the source code:

\begin{tcbexample}[name = Finding All Occurences, listing options = {language = Python}]
{Use re.findall() to return all instances of a string (text) }
locations = "CA 91105, NY 13078, CA 94702"
re.findall(r'\d\d\d\d\d', locations)
# ['91105', '13078', '94702'] (code)
\end{tcbexample}

enter image description here

This basically follows a pattern of \begin{tcbexample}[optional arguments, which set name, list type, listing options]{text/comments}code\end{tcbexample}. I decided to save a bit on the typing by adding a new environment with xargs but it keeps coming up with errors. Here is what I have:

\newenvironmentx{example}[2][1={},2={}]{%
    \begin{tcbexample}{#1}{#2}
        }{%
    \end{tcbexample}
}

Does anyone know how to correctly make a new custom environment with xargs? If so, could you help me figure out what is wrong and how to correct it? Thanks!

Best Answer

Let's first see what the proposed setup would lead to:

\documentclass{article}
\usepackage{xargs}

\newenvironmentx{test}[2][1={abc},2={xyz}]
{Begin \#1=#1, \#2=#2}{End}

\begin{document}

\begin{test}
Text
\end{test}

\begin{test}[ABC]
Text
\end{test}

\begin{test}[ABC][XYZ]
Text
\end{test}

\begin{test}[][XYZ]
Text
\end{test}

\end{document}

enter image description here

I therefore guess that you want a similar syntax for your example environment:

\begin{example}[<tcolorbox options>][<comment>]

I'm not sure this would help, because it would force you to add [] every time you want a comment:

\begin{example}[][some comment]

because

\begin{example}[some comment]

would pass the material in brackets as <tcolorbox options>.

Now you can judge whether you want to go along with this strategy.

Next, I'll show you how to dispense with \newcommandx which is not supported by tcolorbox and use expl3 for the job.

\documentclass{article}

\NewDocumentEnvironment{test}{ O{abc} O{xyz} }
  {Begin \#1=#1, \#2=#2}
  {End}

\begin{document}

\begin{test}
Text
\end{test}

\begin{test}[ABC]
Text
\end{test}

\begin{test}[ABC][XYZ]
Text
\end{test}

\begin{test}[][XYZ]
Text
\end{test}

\end{document}

You get the same output as before. Note that this is simpler, but very similar to the xargs approach: O{abc} denotes an optional argument with default value abc.

If your LaTeX compiler balks at \NewDocumentEnvironment, add \usepackage{xparse}.

The good news is that such method is supported by tcolorbox. And it allows finer control.

\documentclass{article}
\usepackage[many]{tcolorbox}
\tcbuselibrary{xparse,listings}

\NewTCBListing[
  auto counter,
  number within=section,
  list inside=examplelist,
]{example}{ O{} !o }{
  colback=gray!5,
  colbacktitle=gray!40,
  coltitle=black,
  frame hidden,
  arc=2pt,
  titlerule=0pt,
  toptitle=2pt,
  bottomtitle=2pt,
  fonttitle=\bfseries,
  breakable,
  enhanced,
  parbox=false,
  title=Example~\thetcbcounter,
  IfValueTF={#2}{comment and listing,comment={#2}}{listing only},
  #1,
}

\begin{document}

\begin{example}[%
  listing options = {language = Python}
][Use re.findall() to return all instances of a string (text)]
locations = "CA 91105, NY 13078, CA 94702"
re.findall(r'\d\d\d\d\d', locations)
# ['91105', '13078', '94702'] (code)
\end{example}

\begin{example}[listing options = {language = Python}]
locations = "CA 91105, NY 13078, CA 94702"
re.findall(r'\d\d\d\d\d', locations)
# ['91105', '13078', '94702'] (code)
\end{example}

\end{document}

enter image description here

You can see that “no comment” also does listings only, so you get no dashed line.

Related Question