我想使用类似下面这样的配置,让 minipage 的宽度自动适应 tcblisting 的宽度,但我不知道如何获取 varwidth 的宽度(这里记为 \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}
答案1
您不需要 varwidth 来测量 的宽度\hbox
。请注意,tcblisting 的基线位于底部。有趣的是,我不得不使用lrbox
而不是\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}
答案2
您的定义环绕varwidth
实际上tcblisting
并不能将最大宽度保持在 .5 线宽(它适用于文本,但不适用于绘制的框 - 我通过使用 -keywidth
和删除解决了这个问题hbox
,但只有当自然宽度超出指定的最大水平空间时,所以tcblisting
可能会使用两次,一次使用 ,hbox
一次使用width
)。
除此之外,下面的代码还可以做你想做的事情,使用enverb
(我写的一些未发布的代码,主要用于我的文档expkv-bundle
)。它假定左侧永远不需要逐字内容,因此将其作为正常的强制参数。
该代码具有基于使用 -statement 的行缩进的自动缩进检测功能\end
,并允许删除额外的符号(此处计算空格,但代码很“愚蠢”,会删除任何字符,并且如果行比应删除的行短,则当前会抛出低级错误)。默认设置会删除两个额外的空格(因为这是我用来缩进环境内容的数量)。它还可以使用和键在每行的开头和结尾添加bol
内容eol
。
还\enverb
具有一种机制,用于读取可选参数并在本地设置其设置。我删除了一些对您的应用程序不必要的其他功能(或与我的特定输出需求相关的功能)。相反,我添加了两个键来控制您的输出,varwidth
设置嵌套环境的最大宽度varwidth
,并tcbopts
设置转发到的选项tcblisting
(如果您使用+=
它,它将添加到现有选项中)。
\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`\:=`\ % <- 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}