22

I want to store contents of an environment and add them to a macro or command I could easily retrieve them, but I do not want to use the collect package. Are there any alternatives?

\documentclass{article}
\usepackage{environ}
\usepackage{blindtext}



\NewEnviron{env}{
\global\let\content\BODY
%i want to add content to running list and later get whole contents
}

\begin{document}
  \begin{env}
 text text text\\
 \end{env}

\blindtext


\begin{env}
  moretext moretext more text\\
\end{env}

\end{document}

4 Answers4

9

You can print the saved environments by “name” or all of them.

This requires two runs (when the saved environments change), because of the usage of \label.

Update

Since etex methods are now discouraged, here's a reimplementation with expl3.

\documentclass{article}
\usepackage{refcount}

\ExplSyntaxOn

\newcounter{savedenv} \seq_new:N \g_ephraim_savedenv_seq

\NewDocumentEnvironment{savedenv}{ o +b } { \refstepcounter{savedenv} \IfValueT{#1}{\label{savedenv@#1}} \seq_gput_right:Nn \g_ephraim_savedenv_seq { #2 } }{}

\NewDocumentCommand{\printsaved}{m} {% #1 is a list of labels \clist_map_inline:nn { #1 } { \seq_item:Nn \g_ephraim_savedenv_seq { \getrefnumber{savedenv@##1} } \par } } \NewDocumentCommand{\printallsaved}{} { \seq_use:Nn \g_ephraim_savedenv_seq { \par } }

\ExplSyntaxOff

\begin{document}

\begin{savedenv}[a] Something for a \end{savedenv}

\begin{savedenv}[b] Something for b \end{savedenv}

\begin{savedenv} Something with no label \end{savedenv}

\textbf{Let's print them}

This is a: \printsaved{a}

This is b: \printsaved{b}

\textbf{Let's print them in different order}

\printsaved{b,a}

\textbf{Print all of them}

\printallsaved

\end{document}

enter image description here

Original code

\documentclass{article}
\usepackage{etex,environ,refcount}

\globtoksblk\savedenvtoks{1000} \newcounter{savedenvcount}

\NewEnviron{savedenv}[1][]{% \refstepcounter{savedenvcount}% \if\relax\detokenize{#1}\relax \else \label{#1}% \fi \global\toks\numexpr\savedenvtoks+\value{savedenvcount}\relax=\expandafter{\BODY}% } \toks\savedenvtoks={??}

\newcommand{\printsaved}[1]{% \the\toks\numexpr\savedenvtoks+\getrefnumber{#1}\relax }

\makeatletter \newcommand{\printallsaved}{% @tempcnta=\z@ \loop \ifnum@tempcnta<\value{savedenvcount} \advance@tempcnta@ne \the\toks\numexpr\savedenvtoks+@tempcnta\relax\par \repeat } \makeatother

\begin{document}

\begin{savedenv}[a] Something for a \end{savedenv}

\begin{savedenv}[b] Something for b \end{savedenv}

\begin{savedenv} Something with no label \end{savedenv}

\textbf{Let's print them}

This is a: \printsaved{a}

This is b: \printsaved{b}

\textbf{Print all of them}

\printallsaved

\end{document}

You can save up to 1000 environments, but you can change the number in the obvious way. Don't try setting the number to much more than 30000.

enter image description here

egreg
  • 1,121,712
  • how about if i want to create multiple \globtoksblk eg \savedenvtoks1{10}, need help making savedenv to take a \globtoksblk parameter and also for printallsaved to take a parameter indicating which \globtoksblk...? – Ephraim Ncory Jun 13 '14 at 07:58
  • @EphraimNcory It's not very clear. Do you want to have different classes of "saved environments"? – egreg Jun 13 '14 at 08:00
  • yeah something like that, i want to create multiple tok blocks and be able to get their toks. – Ephraim Ncory Jun 13 '14 at 08:03
  • @EphraimNcory I think you should ask a new question, with more details explaining your needs. – egreg Jun 13 '14 at 08:05
  • i asked a new question with more details, hope you find time to help. – Ephraim Ncory Jun 13 '14 at 10:12
8

I suggest to use newenviron package instead of environ, because it defines a \envnamebody command.

Note: Do not call your environment env, because it will collide with the implicit \envbody command always being defined for any environment by newenviron.

After changing to that, one can use the etoolbox - list commands \listgadd etc. and forlistloop do add the current content to the list and processing later on, e.g. printing it.

You need a 'printing' command, I named it \showlist.

The content is glued together without any spacing between at the end of the environment code.

Edited version -- glueing 'arbitrary(?)' texts together

\documentclass{article}
\usepackage{tcolorbox}
\usepackage{newenviron}
\usepackage{blindtext}
\usepackage{etoolbox}

\def\mylist{}%
\listadd{\mylist}{}% Initialize list

\newrobustcmd{\myexpandingcommand}[1]{%
\listgadd{\mylist}{#1}%
}%

\newenviron{content}{%
}{%
\noindent\textbf{\LARGE \textcolor{blue}{Environment content}}  % Remove later on!

\noindent\envbody%
\expandafter\myexpandingcommand\expandafter{\envbody}%
\endgraf\bigskip\bigskip% Can be removed
}%




% Macro showing the current list element%
\newrobustcmd{\showlist}[1]{%
#1%
}%

\begin{document}

\begin{content}  % Store some content%
\blindtext%
\end{content}

\noindent\textbf{\LARGE \textcolor{green}{Text content outside of environment}}  % Can be removed, just for diagnosis/test

\noindent\blindtext
\endgraf\bigskip

\begin{content}

\textbf{\textcolor{red}{Even more text and now even some math: \huge \(\displaystyle\int\limits^{b}_{a} f(x) dx\)}}%

\end{content}


\begin{center}
\begin{tcolorbox}[width=0.8\textwidth,title={Now the combined content of the list}]
\forlistloop{\showlist}{\mylist}%
\end{tcolorbox}%
\end{center}

\end{document}

enter image description here

  • 1
    The last version of environ allows for changing the macro the body is stored in. – egreg Jun 12 '14 at 07:44
  • @egreg: I will look into it and post another solution, eventually... –  Jun 12 '14 at 09:03
  • i need to create more lists which could be different but \myexpandingcommand will only work for \mylist, help. – Ephraim Ncory Jun 13 '14 at 08:02
  • @EphraimNcory: I know, there is some problem with that, I will look after later on, but I had no idea, that you need more than one list, it was not stated in your question –  Jun 13 '14 at 08:06
  • Unfortunately newenviron appears to be incompatible with a new version. It raises an error Extra \endgroup only by including the package (\usepackage). ____________________________________________________________________________________________ To clarify: \envnamebody doesn't mean that literally, but \<insert your environment name here>body i.e. if the environment name is test then the macro is \testbody – user202729 Nov 13 '21 at 02:14
5

I leave this answer here because they were inspirational for the creation of the scontents package. With this package it is quite easy to do what you are looking for. Everything stored in memory including verbatim content. If you wish you can also save the content in external files using the key write-env=file.tex. I am always inclined to store the content in memory:)

\documentclass{article}
\usepackage{scontents}
\pagestyle{empty}
\begin{document}
\begin{scontents}[store-env=main]
Something for main A.
\end{scontents}

\begin{scontents}[store-env=main]
Something for \verb|main B|.
\end{scontents}

\begin{scontents}[store-env=other]
Something for \verb|other|.
\end{scontents}

\textbf{Let's print them}

This is first stored in main: \getstored[1]{main}\par
This is second stored in main: \getstored[2]{main}\par
This is stored in other: \getstored[1]{other}

\textbf{Print all of stored in main}\par
\foreachsc[sep={\\[10pt]}]{main}
\end{document}

Saludos

  • For other uses, I think getfrom_seq internal (warning: internal, API may change any time!) function can be used to get the value of the variable. Otherwise there's still an option of extending VerbatimOut – user202729 Nov 01 '21 at 14:24
  • Remark: internally it's possible to use \tl_set:Nx \l__tmpa_tl { \__scontents_getfrom_seq:nn {1} {main} } \tl_remove_once:NV \l__tmpa_tl \c__scontents_hidden_space_str to store the raw content of scontents into some token list. – user202729 Nov 02 '21 at 02:43
  • @user202729 Do you have a specific application in mind for that? – Pablo González L Nov 03 '21 at 16:31
  • I was looking for some environment that stores the inside text verbatim and accessible from other programming code (expl3) to pass it to Python/shell/write to file or something -- although for LuaTeX there's also the option of using record_env or something https://tex.stackexchange.com/questions/38150/in-lualatex-how-do-i-pass-the-content-of-an-environment-to-lua-verbatim , something similar is not possible for XeLaTeX/PDFLaTeX – user202729 Nov 03 '21 at 17:47
  • @user202729 When I developed the idea for the package, I didn't think of something like that...mmm – Pablo González L Nov 04 '21 at 19:51
  • 1
    @user202729 I think you could take a look at filecontentsdef and create something from there, that package was the base to create scontents, of course (at first sight) Lua is the best option. – Pablo González L Nov 04 '21 at 20:00
  • (Remark: despite the package name, it provides an environment that does not use any temporary file.) – user202729 Nov 11 '21 at 03:40
3

This answer concerns collecting and storing the content of the environment verbatim. (some advantages includes executing the content with catcode equal to catcode at retrieval time, which can be done with e.g. \scantokens), or otherwise you may want to pass the selected content to Python or something similar)

Other options:


Method Tab Space Newline Final newline Letters Others
+v argument type in xparse (*) Does not work (link contains patch to workaround) space catcode \ExplSyntax-Off: char 13 cat other, \ExplSyntax-On: first character is same character as \endlinechar (inherent TeX limitation, cannot fix), remaining are same as above user specified other cat other cat
\collectverb and \Collectverb (*) \ExplSyntax-On: ignored; \ExplSyntax-Off: Start of line: stripped, otherwise: space with space catcode active space \ExplSyntax-On: active space, \ExplSyntax-Off: active char 13 user specified letter cat other cat
\collectverb* and \Collectverb* (*) \ExplSyntax-On: ignored; \ExplSyntax-Off: Start of line: stripped, otherwise: same as space → space with other cat \ExplSyntax-On: space, \ExplSyntax-Off: active char 13 user specified letter cat other cat
\collect-verbenv and \Collect-verbenv (strips first line in \ExplSyntaxOn environment) active tab (char 9) active space active newline (char 13) user specified letter cat other cat
file-contents-defmacro environment Controlled by \FCDtab-tomacro, default: same as space Active, default expand to space (space cat) Active, char 13, default expand to \par Always present letter cat other cat
scontents internal macro other cat other cat char 10 cat other Stripped letter cat other cat

(*): not really an environment

Remark:

  1. For the ones delimited with {}: {} can be used as delimiter, as long as inner {} is balanced.
  2. The line \begin{...} and \end{...} usually have to be on separate line.
  3. Most of the time, the behavior with tab/newline is "the default one defined by the implementation" if the author does not do anything special with it. (can be changed by redefining \endlinechar or setting their catcode manually etc.)
  4. What the active characters expanded to can be changed by redefining their meaning.
  5. scontents macro is an internal method, might break at any time.
  6. As mentioned in the documentation, it can be nested in most cases.
  7. \getstored is not expandable.
  8. However there's an internal method (see example code below)
\begin{scontents}[store-env=test]
\a test & test %comment

second line third line \end{scontents}

\ExplSyntaxOn

\tl_new:N \my__a are stored as characters. \tl_set:Nx \my__a { __scontents_getfrom_seq:nn {1} {test} }

\tl_remove_once:NV \my__a \c__scontents_hidden_space_str \tl_analysis_show:N \my__a

\ExplSyntaxOff

  1. The environments defined by the environ package does not store the content verbatim.
  2. The environments defined by the newenviron package does not store the content verbatim. (plus another issue I mentioned in a comment Collecting contents of environment and store them for later retrieval )
user202729
  • 7,143
  • Fancyvrb also support implementing environment that grabs content verbatim, I think. See https://tex.stackexchange.com/questions/647563/i-want-to-implement-a-code-highlighting-environment-using-l3regex/647565#647565 for example. – user202729 Jun 17 '22 at 11:52
  • I also made my own package for this → https://ctan.org/pkg/saveenv which supports tab character & less "surprising" catcode & use in \ExplSyntaxOn environment .
  • – user202729 Jul 08 '22 at 14:03