Quiero redefinir frame
como frame + itemize
. Hasta ahora he probado esto.
\documentclass{beamer}
\newenvironment{myframe}{\begin{frame}\begin{itemize}}{\end{itemize}\end{frame}}
\begin{document}
\begin{myframe}
\item
\end{myframe}
\end{document}
¿Qué es exactamente lo que está mal en esto y luego cómo redefinirlo correctamente?
Respuesta1
El beamer
entorno frame
recolecta su cuerpo usando la siguiente técnica: lee y almacena tokens siguiendo \begin{frame}
la búsqueda \end{frame}
sin expandirlos excepto en un caso (ver más abajo), pero aceptará un \end{frame}
como final del cuerpo del entorno que se recolecta solo si ha visto el el mismo número de \begin{...}
que \end{...}
durante este proceso de recopilación (no intenta igualarlos). Esto es lo que \beamer@begin@stack
se utiliza en \beamer@collect@@body
from beamerbaseframe.sty
.
En caso de que la pila esté vacía (es decir, haya visto tantas \end{...}
como \begin{...}
después \begin{frame}
) y lo siguiente \end{...}
no sea un \end{frame}
, lo expande con \end
la esperanza de que aparezca \end{frame}
. 1 Pero estosolosucede bajo la condición que di (tantas \end{...}
como después de que se hayan visto \begin{...}
las iniciales ).\begin{frame}
En su ejemplo, \begin{itemize}
empuja uno b
(es decir, abre un nivel) a la \beamer@begin@stack
pila. Esto b
aparece cuando \end{myframe}
se lee. En este punto, \beamer@collect@@body
sabe que ha visto tantos \begin{...}
como \end{...}
después del \begin{frame}
. Esto es cierto pero engañoso, ya que si \end{myframe}
se ampliara, produciría dos estallidos. Entonces, \beamer@begin@stack
piensa que el siguiente \end{...}
debe proporcionar \end{frame}
, posiblemente después de expandir el \end...
token (ver nota al pie 1). Desgraciadamente, esto es incorrecto; el siguiente \end{...}
es \end{document}
. \enddocument
Por lo tanto, se expande, luego \beamer@collect@@body
continúa buscándolo \end{frame}
pero, por supuesto, nunca lo encuentra, llega al final del archivo, lo que genera el error:
Runaway argument?
\let \AtEndDocument \@firstofone \@enddocumenthook \@checkend {docume\ETC.
! File ended while scanning use of \beamer@collect@@body.
<inserted text>
\par
Entonces, básicamente, el problema es que \beamer@collect@@body
no puedes llevar un recuento adecuado de \end{...}
porque \end{myframe}
ocultas dos de estos ( \end{itemize}\end{frame}
) y \beamer@collect@@body
no los descubrirá mediante la expansión de \endmyframe
a pesar de la presencia de \end{myframe}
, porque ha visto más \begin{...}
que \end{...}
en el punto donde ve. this \end{myframe}
(pila no vacía debido a \begin{itemize}
).
Las soluciones requieren que no te escondas \end{...}
en macros que \beamer@collect@@body
no se van a expandir. Uno está usando elenviron
paquete como enLa respuesta de ferahfeza, otro está usando \itemize
y \enditemize
de la siguiente manera (ninguno de ellos afecta la \beamer@begin@stack
pila, por lo tanto, cuando \beamer@collect@@body
ve \end{myframe}
, la pila está vacía, por lo tanto \endmyframe
se expande una vez, lo que hace que el \end{frame}
proceso de escaneo del cuerpo sea visible para el entorno):
\documentclass{beamer}
\newenvironment*{myframe}[1]
{\begin{frame}{#1}%
\begingroup\itemize}
{\enditemize\endgroup
\end{frame}}
\begin{document}
\begin{myframe}{Frame title}
\item An item
\item Another item
\end{myframe}
\end{document}
Nota
Más precisamente, lo que sucede es lo siguiente (en
beamer 2018/12/02 v3.55
). Si:- la
\beamer@begin@stack
pila está vacía (tantas\end{...}
como\begin{...}
se han visto después de\begin{frame}
cuyo cuerpo se está recolectando) y - el siguiente
\end{...}
no es un\end{frame}
, digamos que es un\end{foobar}
,
luego
\beamer@collect@@body
sustituye la expansión de primer nivel de\endfoobar\endgroup
esto\end{foobar}
en el material recopilado. Esto es similar a lo que\end{foobar}
produciría la expansión, aunque esta última haría algunas cosas más (versource2e.pdf
p. 272):- verifique que el texto de reemplazo
\@currenvir
seafoobar
(esto fallaría en elbeamer
frame
proceso de recolección del cuerpo del que estamos hablando, tal\@currenvir
cualframe
); - respetar un comando anterior
\@endparenv
(usado para suprimir la sangría de párrafo al comienzo del texto después de algunos entornos de creación de párrafos, a menos que dicho texto esté precedido por una línea en blanco o por\par
); - respetar un comando anterior
\ignorespacesafterend
(usado para hacer que el siguiente\end{...}
ignore los espacios que pueden seguirlo).
- la