如何实现列表环境不另起一行?

发布于 2022-01-20 19:12:28

`documentclass{ctexart}
begin{document}
进入列表:begin{enumerate}
item 第一条目
item 第二条目
end{enumerate}
end{document}`
编译结果是
微信图片_20220120190831.jpg
我想要的结果如下图
微信图片_20220120190815.jpg

查看更多

关注者
0
被浏览
370
4 个回答
Saino
Saino 2022-02-03

如果允许descripition嵌套可以用楼上@u3793 的回答。
严格按问题的要求我这里手搓了一个实现,非常非常复杂,大概需要编译两到三次。
使用方法就是

\begin{nobreaklist}{enumerate}
  \item item 1
  \item item 2\\the line 2
\end{nobreaklist}

默认情况下,算上列表标签,一行的内容超过行宽的60%就会另起一行,可以用\SetNobreaklistLimit修改阈值和\ResetNobreaklistLimit恢复默认。

  1. 这个环境除紧随第一个 \item 的段落,其他段可嵌套别的列表环境。
  2. nobreaklist 自身不能放在别的列表内部,技术上虽然可以实现(更复杂),但这种排版的效果并不必要,并且非常不规范。
  3. 费了一些工夫 make enumitem happy,enumitem 宏包大致上可以正常使用,但它魔改太多,不敢保证 100% 兼容。

如果修改了文章,最好是删除.aux文件重新编译,否则有几率位置错乱。

\documentclass{article}
\usepackage{linegoal}
\usepackage{lipsum}
\usepackage{enumitem}

\makeatletter

% \nobreaklist@@command to save orginal \command
\let\nobreaklist@@trivlist=\@trivlist
\let\nobreaklist@@noitemerr=\@noitemerr
\let\nobreaklist@@item=\@item
\let\nobreaklist@relax=\relax
\newcounter{nobreaklist}
\newif\if@nobreaklist@prevgrafknown
\def\nobreaklist@first@@item[#1]{%
  % save item info (label and parshape)
  \nobreaklist@@item[#1]%
  \let\@noitemerr\nobreaklist@@noitemerr
  % manually label with \everypar set in \@item
  \the\everypar
  % use \@setpar info set in \@trivlist to restore \par
  \@restorepar
  % detect prevgraf right after \par restored
  % succeeding \par's will change \prevgraf
  \unless\if@nobreaklist@prevgrafknown
    \par
    \edef\nobreaklist@prevgraf{\the\prevgraf}%
    \immediate\write\@auxout{\gdef\string\nobreaklist@prevgraf@\romannumeral\c@nobreaklist{\nobreaklist@prevgraf}}%
    \@latex@warning@no@line{Position for nobreaklist changed.
      Rerun to get it right}%
  \fi
  \let\@item=\nobreaklist@second@@item
}
\def\nobreaklist@second@@item[#1]{%
  \nobreaklist@@item[#1]%
  % parshape for paragraphs other than the first
  \expandafter\everypar\expandafter{\the\everypar \parshape\@ne\@totalleftmargin\linewidth}%
  \let\@item=\nobreaklist@@item
}

% max proportion of \linewidth when the list par start
\def\nobreaklist@leftmargini@max@proportion{0.6}
\let\nobreaklist@leftmargini@max@proportion@default=\nobreaklist@leftmargini@max@proportion
\def\SetNobreaklistLimit#1{\def\nobreaklist@leftmargini@max@proportion{#1}}
\def\ResetNobreaklistLimit{%
  \let\nobreaklist@leftmargini@max@proportion=\nobreaklist@leftmargini@max@proportion@default}

% #1: enumerate or itemize
% #2: code to set \make@enumitem@happy or empty
\def\donobreaklist#1#2{%
  \let\make@enumitem@happy=\@firstofone
  #2%
  % unique even this feature not used
  \stepcounter{nobreaklist}%
  % restart to orginal list env
  % define here to use #1
  \long\def\nobreaklist@restart##1\nobreaklist@relax{%
    \fi
    % restore global modifications in \list
    \global\advance\@listdepth\m@ne
    \addtocounter{nobreaklist}\m@ne
    % match \begingroup from \begin{nobreaklist}
    \endgroup
    % enter vmode to disable this feature
    \par
    % \begin takes cares of group matching, env name and hooks
    \begin{nobreaklist}{#1}%
  }%
% vmode don't need this feature
\ifvmode
  \make@enumitem@happy{\csname #1\endcsname}
\else
  % \prevgraf saved in aux file
  \@ifundefined{nobreaklist@prevgraf@\romannumeral\c@nobreaklist}
    {\@nobreaklist@prevgrafknownfalse}
    {\@nobreaklist@prevgrafknowntrue}%
  \if@nobreaklist@prevgrafknown
    \edef\nobreaklist@prevgraf{\csname nobreaklist@prevgraf@\romannumeral\c@nobreaklist\endcsname}%
    % rewrite to aux
    \immediate\write\@auxout{\gdef\string\nobreaklist@prevgraf@\romannumeral\c@nobreaklist{\nobreaklist@prevgraf}}%
  \fi
  % even first run does this to detect linegoal
  % because \@itemlabel is known only after \list called
  % this will save a compile run
  \def\@trivlist{%
    % tune vertical spacing
    % \partopsep not needed: always horizontal mode
    \if@nobreaklist@prevgrafknown
        \ifnum \nobreaklist@prevgraf>1
        \itemsep=\z@
        \parsep=\parskip
        \topsep=\z@
      \fi
    \fi
    \if@nmbrlist
      % not \stepcounter, avoid resettings
      \addtocounter{\@listctr}\@ne
    \fi
    % In enumerate or itemize,
    %   \makelabel{LABEL} is \hss\llap{LABEL},
    %   which ensures \hbox{\makelabel{LABEL}} being zero width.
    % Then the label is placed as \hbox{LABEL}
    %   at \labelsep left from \@totalleftmargin,
    %   following an \hskip \itemindent.
    % No need to take care of \itemindent,
    %   since it's additional indention to \leftmargin
    %   only on the first line of item paragraph
    \setbox\@tempboxa=\hbox{%
      \hbox{\@itemlabel}%
      \if@nmbrlist
        % restore initial state of the counter (global)
        \addtocounter{\@listctr}\m@ne
      \fi
      \hskip\labelsep}%
    % wrap \hskip in hbox to avoid line break
    % Update: replace \kern with \hbox to work with line start
    % \kern\wd\@tempboxa
    \hbox to\wd\@tempboxa{}%
    % using \linegoal in \dimexpr isn't essentially allowed
    % but it's we can use it at the end of the expression
    % for the subtle fact that \linegoal's expansion has a \relax inside,
    % which is approximately \linewidth\relax\LNGL@set@linegoal,
    % then recompile to be the actual linegoal and \relax
    %
    % note that \linegoal ignores \leftmargin's set by parshape
    %  \linegoal = \linewidth-\pdfxpos-\trueleftmargin
    % and we need
    %   \leftmargin = \pdfxpos-\trueleftmargin-\@totalleftmargin
    \leftmargin=\dimexpr\linewidth-\@totalleftmargin-\linegoal\relax
    % if too long
    \ifdim \dimexpr\leftmargin+\@totalleftmargin\relax>
        \nobreaklist@leftmargini@max@proportion\dimexpr\linewidth+\@totalleftmargin\relax
      \nobreaklist@restart
    \else
    % make \lastbox non-void to avoid the itemindent removal in \@item,
    % place hbox here because \linegoal is the last command
    % that generates non-hbox horizontal item
    \hbox{}%
    \nobreaklist@@trivlist
    % no need to be global, \begin{nobreaklist} has \begingroup
    % if want to use \nobreaklist ... \endnoreaklist, change to \global\let here
    \let\@trivlist=\nobreaklist@@trivlist\fi}%
  % disable the first \par which is in \@trivlist
  % then there will be a \@setpar in \@trivlist too
  \let\par\relax
  \make@enumitem@happy{\csname #1\endcsname}%
  % parshape for the paragraph starting this environment
  % \parshape\tw@ \z@ \dimexpr\linewidth+\leftmargin\relax \@totalleftmargin \linewidth
  \if@nobreaklist@prevgrafknown
    \nobreaklist@parshape\nobreaklist@prevgraf
  \else
    \parshape=1
      \dimexpr\@totalleftmargin-\leftmargin\relax
      \dimexpr\linewidth+\leftmargin+\rightmargin\relax
  \fi
  % disable the \par in \@item
  \let\par\relax
  % disable verticle adjustments executed after the \par
  % (\addpenalty and \addvspcce)
  % which assume to be vertical mode,
  % and simply give a \@noitemerr when in horzontal mode,
  % but the mode will be horizontal since \par is disabled.
  \let\@noitemerr\relax
  \let\@item=\nobreaklist@first@@item
\fi
% parameter delimiter for \nobreaklist@restart
\nobreaklist@relax}


% set parshape
% #1: \prevgraf after current line
\def\nobreaklist@parshape#1{%
  \edef\@parshape@str{%
    \dimexpr\@totalleftmargin-\leftmargin\relax
    \dimexpr\linewidth+\leftmargin+\rightmargin\relax}%
  \@tempcnta=#1%
  \@temptokena={}%
  \loop
    \ifnum\@tempcnta>0
    \@temptokena=\expandafter{\the\@temptokena\@parshape@str}%
    \advance\@tempcnta-1
  \repeat
  \parshape \numexpr#1+1\relax \the\@temptokena \@totalleftmargin \linewidth
}

% make enumitem happy
\@ifpackageloaded{enumitem}{%
  % #2: dummy, absorb the empty argument
  %   provided in case it's let to \donobreaklist
  % #3: enumitem's key=val list
  \def\make@enumitem@happy@and@donobreaklist#1#2[#3]{%
    \donobreaklist{#1}{%
      \def\make@enumitem@happy##1{##1[#3]}%
    }%
  }%
}{%
  \let\make@enumitem@happy@and@donobreaklist=\donobreaklist
}
% #2: list environment type, only works for enumerate and itemize
\newenvironment{nobreaklist}[2][]{%
  % if #1 is not empty
  \unless\if\relax\detokenize{#1}\relax
    \@namedef{nobreaklist@prevgraf@\romannumeral\numexpr\c@nobreaklist+1\relax}{#1}%
  \fi
  \@ifnextchar[%
  {\make@enumitem@happy@and@donobreaklist{#2}{}}%
  {\donobreaklist{#2}{}}%
}{\endlist}
\makeatother

\begin{document}
some \begin{nobreaklist}{enumerate}
\item item 1
\item item 2\\the line 2
\end{nobreaklist}

\lipsum[1][1-8] \begin{nobreaklist}{enumerate}[label=(\emph{\alph *})]
\item item 1
\item item 2\\the line 2
\end{nobreaklist}

\lipsum[1][1-6] \begin{nobreaklist}{enumerate}
\item item 1
\item item 2\\the line 2
\end{nobreaklist}

\SetNobreaklistLimit{0.8}
\lipsum[1][1-6] \begin{nobreaklist}{enumerate}
\item item 1
\item item 2\\the line 2
\end{nobreaklist}
\end{document}

image.png

对于嵌套的情况,偶尔排版正确是巧合,一般情况可能会无法正确定位

\begin{itemize}
  \item \lipsum[1][1-4]\par para 2 \begin{nobreaklist}{enumerate}[label=(\emph{\alph *})]
    \item item 2.1
    \item test \begin{nobreaklist}{itemize}
      \item item 2.2.1
      \item item 2.2.2
    \end{nobreaklist}
    \par
    \lipsum[2][1-4]\begin{nobreaklist}{enumerate}
      \item item 1
      \item item 2
    \end{nobreaklist}
  \end{nobreaklist}
\end{itemize}

image.png
有一个临时的解决方案,手动地设置所在行的位置\begin{nobreaklist}[<行数>]{enumerate}

\begin{itemize}
  \item \lipsum[1][1-4]\par para 2 \begin{nobreaklist}{enumerate}[label=(\emph{\alph *})]
        \item    item 2.1
        \item test \begin{nobreaklist}{itemize}
            \item item 2.2.1
            \item item 2.2.2
        \end{nobreaklist}
        \par
        \lipsum[2][1-4]\begin{nobreaklist}[5]{enumerate}
            \item item 1
            \item item 2
        \end{nobreaklist}
    \end{nobreaklist}
\end{itemize}

image.png

华行蓉左
华行蓉左 2022-01-20

前一个问题?悬挂缩进?minpage小页?

夏大-鱼羊
夏大-鱼羊 2022-01-20

给个不能嵌套的理由?

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览