2

I would like to use a configuration similar to the following, so that the width of the minipage automatically adapts to that of the tcblisting, but I don't know how to get the width of varwidth (noted here \varwidthwidth).

\documentclass{article}
\usepackage{varwidth}
\usepackage{lipsum}
\usepackage[listings]{tcolorbox}

\begin{document}

\begin{minipage}{\linewidth-\varwidthwidth-1em} \lipsum[1][1-5] \end{minipage}\hfill% \begin{varwidth}{.5\linewidth} \begin{tcblisting}{hbox,listing only} x \end{tcblisting} \end{varwidth}

\end{document}

MB4E
  • 1,084
  • Are the contents of the two boxes the same (once as listing once as result -- if so, isn't that supported by tcblisting?) or like in your MWE not related at all? You'd need to evaluate the varwidth before your minipage in order to access its width (and if you still need the outcome in the order you show, you'll need to change your process either to a two-pass solution or to verbatimly grab the contents, process them, store the results in a box, insert your minipage and then the contents of your box storing the varwidth). – Skillmon Sep 11 '23 at 09:04
  • The content of minipage may be random. The question is linked to this one, which unfortunately did not work with tcblisting. – MB4E Sep 11 '23 at 09:11
  • Thanks for the response, you should've made that more clear in your question (especially the link to your old question, stating what didn't work for your specific use case). Unfortunately I'm currently not on my main machine, if you got time until this evening (and no other answer until then), I might be able to help you (I got more or less the right code laying around to make this work). – Skillmon Sep 11 '23 at 09:18
  • 2
    Does this help -- https://tex.stackexchange.com/questions/512420/auto-adjust-the-width-of-tcolorbox-right-hand-side -- or this one -- https://tex.stackexchange.com/questions/19295/side-by-side-source-and-output-when-documenting-a-style-file/114582#114582 – js bibra Sep 11 '23 at 10:50
  • 1
    Also, you need to add \dimexpr to your calculated minipage ;width. – John Kormylo Sep 11 '23 at 15:08

2 Answers2

3

Your definition wrapping varwidth around tcblisting doesn't really work to keep the maximum width at .5 linewidth (it works for the text, but not for the drawn box -- I fixed that by using the width-key and dropping hbox, but only if the natural width exceeds the specified horizontal maximum space, so the tcblisting might be used twice, once with hbox once with width).

Other than that the following does what you want, using enverb (some unpublished code I wrote, mostly for the documentation of my expkv-bundle). It assumes that the left hand side never needs verbatim content, and hence grabs it as a normal mandatory argument.

The code features automatic indentation detection based on the indentation of the line with the \end-statement and allows to remove additional symbols (spaces are counted here, but the code is "stupid" and would remove any character, and currently throws a low level error if there is a line shorter than what should be removed). The default settings remove two additional spaces (because that's the amount I use to indent the contents of an environment). It can also add contents at the start and end of each line with the bol and eol keys.

Also \enverb features a mechanism to read an optional argument setting its settings locally. I ripped out a few other features that aren't necessary for your application (or were related to my specific output needs). Instead I added two keys to control your output, varwidth sets the maximum width of the nested varwidth environment, and tcbopts sets the options forwarded to tcblisting (and if you use += it'll add to the existing options).

\documentclass{article}
\usepackage{lipsum}
\usepackage[listings]{tcolorbox}

\makeatletter \usepackage{expkv-def}

\ExplSyntaxOn \cs_new_eq:NN \enverb@count \tl_count:n \ExplSyntaxOff %% key setup \ekvdefinekeys{enverb} { % if true ignore the number of spaces preceding the \end-statement boolTF auto-ignore = \enverb@ifautoignore ,initial auto-ignore % number of additional characters to ignore for nested indentation ,eint more-ignore = \enverb@moreignore ,initial more-ignore = 2 % if used ignore exactly this many characters (overwrites auto-ignore) ,protected code ignore = \let\enverb@ifautoignore@secondoftwo ,also eint ignore = \enverb@ignore % contents to place at the start of every line ,long store bol = \enverb@bol@content % contents to place at the end of every line ,long store eol = \enverb@eol@content ,e: initial eol = \csname char_generate:nn\endcsname{13}{12} %% Additional keys for your environment % maximum varwidth width ,long store varwidth = \mbAeVarwidthwidth ,initial varwidth = .5\linewidth % tcb-options ,long store tcbopts = \mbAeTcbopts ,initial tcbopts = {listing only, box align=center} % define tcbopts+={<keys>} to add to the tcbopts instead of resetting them ,meta tcbopts += {o: tcbopts = {\mbAeTcbopts, #1}} ,meta tcbopts+ = {o: tcbopts = {\mbAeTcbopts, #1}} } \protected\long\ekvsetdef\enverbsetup{enverb}

%% auxiliary error function \newcommand\enverb@error[1] {% \GenericError {(enverb)@spaces@spaces@spaces@spaces}% {Environment enverb error: #1}% {Just use it correctly!}% {Read the sources.}% }

%% setup for weird category code regime \begingroup \lccode\~=^^M \catcode\:=13 \lccode:=\ % &lt;- space \catcode;=13 \lccode\;=^^I % <- tab \lowercase{\endgroup %% code for spaces and CR \def\enverb@body@space{}% \def\enverb@body@tab{}% \def\enverb@body@newline#1~% {\enverb@ifnotend{#1}{\enverb@bol\unexpanded{#1}\enverb@eol~}}% %% activate the category code regime of the body \protected\def\enverb@body@setup {% \let\enverbCollectedBody@empty \let\do@makeother\dospecials \catcode\^^M=13 \let~\enverb@body@newline \catcode\ =13 \let:\enverb@body@space \catcode\^^I=13 \let;\enverb@body@tab \let\enverb@bol\relax \let\enverb@eol\relax } %% check for optional argument \newcommand\enverb@search@oarg@a {% \ifx:\next \ifenverb@firsteol \else \enverb@body@add{:}% \fi \let\next\enverb@search@oarg@b \else \ifx~\next \ifenverb@firsteol \enverb@firsteolfalse \else \enverb@body@add{~}% \fi \let\next\enverb@search@oarg@b \else \ifx[\next\@gobble]% \let\next\enverb@oarg \else \ifenverb@firsteol \let\next\enverb@body@after@begin \else \let\next\enverb@body \fi \fi \fi \fi \next } %% start body collection \newcommand\enverb@body {\edef\enverbCollectedBody{\iffalse}\fi\expandafter~\enverbCollectedBody} %% check the line after an oarg \def\enverb@body@after@oarg#1~% {\enverb@ensure@blank{#1}{closing bracket}\enverb@body} %% check the line after the \begin statement \def\enverb@body@after@begin#1~% {\enverb@ensure@blank{#1}{\string\begin}\enverb@body} } %% quick check for empty line \newcommand\enverb@ensure@blank[2] {% \expandafter\enverb@ifempty\expanded{{#1}}{}% {% \expanded{% \noexpand\enverb@error {% Line after #2 not empty.\noexpand\MessageBreak Contains: \detokenize\expandafter{\romannumeral^^@#1}% }% }% }% } %% quick check for empty argument \newcommand\enverb@ifempty[1] {% \enverb@ifempty@\enverb@ifempty@A#1\enverb@ifempty@B.\enverb@ifempty@true \enverb@ifempty@A\enverb@ifempty@B } \def\enverb@ifempty@#1\enverb@ifempty@A\enverb@ifempty@B#2#3{#3} \def\enverb@ifempty@true\enverb@ifempty@A\enverb@ifempty@B#1#2{#1} \newcommand\enverb@gadd[2]{\xdef#1{\unexpanded\expandafter{#1#2}}} \newcommand\enverb@body@add[1] {% \edef\enverbCollectedBody {\unexpanded\expandafter{\enverbCollectedBody#1}}% } % start of environment enverb' \newcommand\enverb {% \begingroup \def\tmp{enverb}% \expandafter \endgroup \expandafter\enverb@ifnotend@setup@perhaps\expanded {{\string{\@currenvir\string}}}% \begingroup \enverb@body@setup \enverb@firsteoltrue \let\enverb@collected@oarg\@empty \enverb@search@oarg } \newif\ifenverb@firsteol \newcommand\enverb@search@oarg{\futurelet\next\enverb@search@oarg@a} \newcommand\enverb@search@oarg@b{\expandafter\enverb@search@oarg\@gobble} \newcommand\enverb@oarg{\endgroup\enverb@oarg@} \NewDocumentCommand\enverb@oarg@{O{}} {% \edef\enverb@collected@oarg{\unexpanded{#1}}% \begingroup \enverb@body@setup \enverb@body@after@oarg } \def\enverb@ifnotend#1% {% \def\enverb@ifnotend##1% {% \enverb@ifnotend@ ##1\enverb@mark\enverb@ifnotend@maybe #1\enverb@mark\@thirdofthree \enverb@stop }% \def\enverb@ifnotend@##1#1##2\enverb@mark##3##4\enverb@stop{##3{##1}{##2}}% } \expandafter\enverb@ifnotend\expanded{{\expandafter\@gobble\string\\end}} \newcommand\enverb@ifnotend@maybe[2] {\expandafter\enverb@ifnotend@perhaps\expandafter{\romannumeral^^@#2}{#1}} \newcommand\enverb@ifnotend@setup@perhaps[1] {% \def\enverb@ifnotend@perhaps##1% {% \enverb@ifnotend@perhaps@\enverb@mark##1\enverb@mark\enverb@ifnotend@end \enverb@mark#1\enverb@mark@thirdofthree \enverb@stop }% \def\enverb@ifnotend@perhaps@ ##1\enverb@mark#1##2\enverb@mark##3##4\enverb@stop {##3{##2}}% } \providecommand@thirdofthree[3]{#3} \newcommand\enverb@ifnotend@end[3] {% \iffalse{\fi}% \enverb@ensure@blank{#1}{\string\end}% \expanded {% \endgroup \enverbsetup {\unexpanded\expandafter{\enverb@collected@oarg}}% \noexpand\enverb@final {\unexpanded\expandafter{\enverbCollectedBody}}% }% {#2}% \expandafter\end\expandafter{@currenvir}% } \long\def\enverb@final#1#2% {% \enverb@ifautoignore {\enverb@setup@ignore{\enverb@count{#2}+\enverb@moreignore}}% {\enverb@setup@ignore\enverb@ignore}% \edef\enverb@line##1\enverb@eol {% \noexpand\detokenize{##1}% \noexpand\unexpanded{\unexpanded\expandafter{\enverb@eol@content}}% }% \edef\enverbCollectedBody{#1}% } \providecommand@firstofnine[9]{#1} \newcommand\enverb@setup@ignore[1] {\expandafter\enverb@setup@ignore@\the\numexpr#1\relax;\enverb@line} \def\enverb@setup@ignore@#1;#2% {% \ifnum#1>9 \expandafter@firstoftwo \else \expandafter@secondoftwo \fi {\expandafter\enverb@setup@ignore@\the\numexpr#1-8;{@firstofnine{#2}}}% {% \let\enverb@bol@gobble@empty % just so that renewcommand doesn't go nuts \expanded {% \unexpanded{\renewcommand\enverb@bol@gobble}\ifnum#1>\z@[#1]\fi {% \noexpand\unexpanded {\unexpanded\expandafter{\enverb@bol@content}}% \unexpanded{#2}% }% \unexpanded{\def\enverb@bol##1\enverb@eol}% {% \unexpanded{\expandafter\enverb@ifempty\expanded}{{##1}}% {\noexpand\enverb@line}% {\noexpand\enverb@bol@gobble}% ##1\unexpanded{\enverb@eol}% }% }% }% } \let\endenverb@empty \makeatother

\newcommand\mbAeLeftHandSide{} \newsavebox\mbAeRightHandSide \newcommand\mbAeRightHandSideListing[1] {% \sbox\mbAeRightHandSide {% % \csname tl_analysis_show:N\endcsname\mbAeRightHandSideContents \scantokens\expanded {{% \noexpand\begin{tcblisting}% {#1,\unexpanded\expandafter{\mbAeTcbopts}}% \mbAeRightHandSideContents \noexpand\end{tcblisting}\csname char_generate:nn\endcsname{13}{12}% \noexpand\noexpand\noexpand\noexpand }}% }% } \expanded{\unexpanded{\def\mbAeSplitSides#1}\string\BREAK}#2\stop {% \def\mbAeLeftHandSideContents {#1}% \def\mbAeRightHandSideContents{#2}% } \newenvironment{mbAe} {\enverb} {% \expandafter\mbAeSplitSides\enverbCollectedBody\stop \mbAeRightHandSideListing{hbox}% first pass, try hbox \ifdim\wd\mbAeRightHandSide>\mbAeVarwidthwidth\relax % if too wide use width \mbAeRightHandSideListing{width=\mbAeVarwidthwidth}% \fi \par\noindent \begin{minipage}[c]{\dimexpr\linewidth-\wd\mbAeRightHandSide-1em\relax} \scantokens\expanded{{\mbAeLeftHandSideContents\noexpand\noexpand\noexpand\noexpand}}% \end{minipage}% \hfill \usebox\mbAeRightHandSide \par }

\usepackage{varwidth} % only needed to show your approach

\begin{document} \begin{mbAe}[tcbopts+={colback=red!15}] \lipsum[1][1-5] \BREAK x \end{mbAe}

\begin{mbAe} \lipsum[1][1-5] \BREAK This is a very long line that takes most likely more than half a line width \end{mbAe}

\begin{minipage}{\dimexpr\linewidth-5cm-1em\relax} \lipsum[1][1-5] \end{minipage}\hfill% \begin{varwidth}{.5\linewidth} \begin{tcblisting}{hbox,listing only} This is a very long line that takes most likely more than half a line width \end{tcblisting} \end{varwidth} \end{document}

enter image description here

Skillmon
  • 60,462
  • Thank you for this answer, which is relatively complex, even though I would have needed two environments, one for the right-hand side and one for the left-hand side. – MB4E Sep 12 '23 at 07:40
  • @MB4E Well, just imagine everything between \makeatletter and \makeatother was a \usepackage{enverb} (except for the 2 additional keys), and everything you'd do was the stuff starting at \newcommand*\mbAeLeftHandSide{}. In the accepted answer by egreg of your other question you had no two environments and were fine with it, which is why I thought this would be the case here as well. Would the \BREAK variant be fine? Or an environment in which you need to nest the two other environments? – Skillmon Sep 12 '23 at 08:13
  • Yes, the \BREAK variant should be fine. – MB4E Sep 12 '23 at 08:26
  • The John Kormylo answer is also good for me. – MB4E Sep 12 '23 at 08:40
  • @MB4E Why did you use varwidth in the first place if you didn't want to dynamically limit the space? Also, see my edit for the \BREAK variant. – Skillmon Sep 12 '23 at 10:52
  • It could be interesting in a more general context, but useless with hbox and tcblisting. I'm looking at the proposed variant. Thanks again. – MB4E Sep 12 '23 at 12:41
3

You don't need varwidth to measure the width of an \hbox. Note that the baseline of the tcblisting is at the bottom. Interestingly, I had to use lrbox instead of \savebox.

\documentclass{article}
\usepackage{lipsum}
\usepackage[listings]{tcolorbox}

\newsavebox{\tempbox}

\begin{document}

\begin{lrbox}{\tempbox} \begin{tcblisting}{hbox,listing only} %Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus x \end{tcblisting} \end{lrbox}% \ifdim\wd\tempbox>0.5\textwidth \begin{lrbox}{\tempbox} \begin{tcblisting}{listing only, width=0.5\textwidth} %Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus x \end{tcblisting} \end{lrbox}% \fi \noindent\begin{minipage}[b]{\dimexpr \linewidth-\wd\tempbox-1em} \lipsum[1][1-5] \end{minipage}\hfill\box\tempbox

\end{document}

John Kormylo
  • 79,712
  • 3
  • 50
  • 120
  • You can't use \savebox because tcblisting relies on category code changes, and \savebox reads its argument, hence tokenises it, and freezes the category codes. Also your answer lacks any means to set a maximum width for the tcblisting. – Skillmon Sep 11 '23 at 18:16
  • With the hbox option, you can't control the width at all. Without the hbox option the width will be whatever you set it to be. – John Kormylo Sep 11 '23 at 19:41
  • You sort of can by using the hacky varwidth upper-key with hbox (which the previous edit of my answer used), or by using multiple passes through tcblisting, once with hbox once with width (which my answer uses now). But your answer doesn't do that at all (but this is the goal of OP, see the linked question in the comment below this question). – Skillmon Sep 11 '23 at 19:52
  • It is not really necessary to control the maximum width, so the proposed solution is rather good for me, even if I would like tcblisting to be centered vertically relative to minipage. – MB4E Sep 12 '23 at 07:36