T-F-S / tcolorbox

A LaTeX package to create highly customizable colored boxes.
http://www.ctan.org/pkg/tcolorbox
LaTeX Project Public License v1.3c
224 stars 16 forks source link

selection of recording file #239

Open nignasi opened 1 year ago

nignasi commented 1 year ago

\tcbstartrecording has an optional argument to fix the name of recording file. This name is used in tcbinputrecords. I understand that it can be useful to define diferent record files which are opened, filled and closed one after the other but as all records are written over the "default" (in this moment) recording file, it's not possible to alternate records on diferent files.

I've tested alternating files and it seems that every \tcbstartrecording starts a new file, even if the file already exists, so only the records after the last start are really conserved.

I think that an optional parameter to select recording file could be useful. See https://tex.stackexchange.com/q/690242/1952

T-F-S commented 1 year ago

The current recording mechanism is designed for just one recording at a time.

For multiple recordings, we need multiple allocated file handles and some ID for the current file. Since tcolorbox's recording is just a small layer over the LaTeX \write capabilities hiding the current file handle, a multiple approach is can, of course, be done.

For this, I would not adapt the current macros, but add new ones with similar name and a mandatory ID parameter. I think, the code should be not very complicated, but today I have no time to look into the matter further.

muzimuzhi commented 1 year ago

An attempt. Following new commands are provided:

\TcbStartRecordings[file1, file2, ...] % \tcbrecord will write to the last file
\TcbPauseRecording
\TcbResumeRecording[file]              % by default the last paused recording
\TcbStopRecordings[file1, file2, ...]

[...], but add new ones with similar name and a mandatory ID parameter.

Oh, maybe what @T-F-S suggested was \TcbRecord{<file>}.

Full example

```tex \documentclass{article} \usepackage{tcolorbox} \ExplSyntaxOn \tcbset{ no~recording/.code={ \cs_set_eq:NN \tcbrecord \use_none:n }, no~recording } \tl_new:N \g__tcb_record_active_iow_tl % active iow stream \tl_new:N \g__tcb_record_active_file_tl % active recording file \int_new:N \g__tcb_records_int % seems not very useful(?) \cs_new_protected:Npn \tcb_record:n % though currently \iow_now:Nn expands its #1, expand tl to iow just in case { \exp_after:wN \iow_now:Ne \g__tcb_record_active_iow_tl } % \iow_now:Nn wraps its #2 in \exp_not:n / \unexpanded, but % \tcbrecord needs to expand its argument \cs_generate_variant:Nn \iow_now:Nn {Ne} \NewDocumentCommand \TcbStartRecordings { O{\jobname.records} } { \cs_set_eq:NN \tcbrecord \tcb_record:n \clist_map_inline:nn {#1} { \int_incr:N \g__tcb_records_int % seems not very useful \cs_new:cpx { __tcb_record_file_##1: } { \int_use:N \g__tcb_records_int } \tl_gset:Nx \g__tcb_record_active_file_tl {##1} \tl_gset:Nx \g__tcb_record_active_iow_tl { \use:c {g__tcb_record_ \int_use:N \g__tcb_records_int _iow} } % no need to save uses of \iow_new:N, it just lets #1 to \c_term_iow % (different from \newwrite) \exp_after:wN \iow_new:N \g__tcb_record_active_iow_tl \exp_after:wN \iow_open:Nn \g__tcb_record_active_iow_tl {##1} } } \NewDocumentCommand \TcbPauseRecording {} { \cs_set_eq:NN \tcbrecord \use_none:n } \NewDocumentCommand \TcbResumeRecording { O{\g__tcb_record_active_file_tl} } { \cs_set_eq:NN \tcbrecord \tcb_record:n \tl_gset:Nx \g__tcb_record_active_file_tl {#1} \tl_gset:Nx \g__tcb_record_active_iow_tl { \use:c { g__tcb_record_ \use:c {__tcb_record_file_#1:} _iow } } } \NewDocumentCommand \TcbStopRecordings { O{\jobname.records} } { \cs_set_eq:NN \tcbrecord \use_none:n \clist_map_inline:nn {#1} { \cs_if_exist:cT { __tcb_record_file_##1: } { \iow_close:c { g__tcb_record_ \use:c {__tcb_record_file_##1:} _iow } \cs_undefine:c { __tcb_record_file_##1: } } } } \ExplSyntaxOff \begin{document} \def\x{(expanded at output time)} % the last one wins \TcbStartRecordings[\jobname.solu1, \jobname.solu2] \tcbrecord{recorded to solu2 \x\par} \TcbResumeRecording[\jobname.solu1] \tcbrecord{recorded to solu1 \x\par} \TcbPauseRecording \tcbrecord{not recorded\par} % resume solu1 recording \TcbResumeRecording \tcbrecord{recorded to solu1, continued\par} \TcbResumeRecording[\jobname.solu2] \tcbrecord{recorded to solu2, continued\par} \TcbStopRecordings[\jobname.solu1, \jobname.solu2] \def\x{(expanded at input time)} \subsection*{solu1} \tcbinputrecords[\jobname.solu1] \subsection*{solu2} \tcbinputrecords[\jobname.solu2] \end{document} ``` ![image](https://github.com/T-F-S/tcolorbox/assets/6376638/c29a49e8-70d1-4174-bc2b-5184e4b0e5cd)

T-F-S commented 1 year ago

My attempt is to identify the different records by an ID like example or theorem to write the records into different files:

\documentclass{article}
\usepackage{hyperref}
\usepackage{tcolorbox}

\makeatletter
\ExplSyntaxOn

\NewDocumentCommand \TcbStartRecording { O{\jobname.#2-records} m }
  {
    \cs_set_nopar:cpx { __tcobox_record_file_#2 } { #1 }
    \cs_if_free:cT { g__tcobox_record_#2_iow }
      {
        \iow_new:c { g__tcobox_record_#2_iow }
      }
    \iow_open:cn { g__tcobox_record_#2_iow }{ #1 }
  }

\NewDocumentCommand \TcbRecord { m +m }
  {
    \cs_if_exist:cT { g__tcobox_record_#1_iow }
      {
        \iow_now:cx { g__tcobox_record_#1_iow }{ #2 }
      }
  }

\NewDocumentCommand \TcbStopRecording { m }
  {
    \cs_if_exist:cT { g__tcobox_record_#1_iow }
      {
        \iow_close:c { g__tcobox_record_#1_iow }
      }
  }

\cs_new_protected:Npn \__tcobox_input_records:n #1
  {
    \tcbinputrecords [ #1 ]
  }

\NewDocumentCommand \TcbInputRecords { o m }
  {
    \IfNoValueTF { #1 }
      {
        \cs_if_exist:cT { __tcobox_record_file_#2 }
          {
            \exp_args:Nv \__tcobox_input_records:n { __tcobox_record_file_#2 }
          }
      }
      {
        \__tcobox_input_records:n { #1 }
      }
  }

\ExplSyntaxOff

\tcbset{%
  Record/.style n args={2}{phantom={\TcbRecord{#1}{#2}}},
  no recording/.code={%
    \let\tcbrecord\tcb@null%
    \RenewDocumentCommand\TcbRecord{m+m}{}%
  },
  %no recording
}
\let\tcbrecord\tcb@null%

\makeatother

\NewTColorBox[auto counter]{mybox}{ O{} m m }{%
  title = {Mybox~\thetcbcounter: #3},
  label = {mybox-#2},
  #1,
  record = {\string\par{}See Mybox \string\ref{mybox-#2} (#3)},
}

\NewTColorBox[auto counter]{theorem}{ O{} m m }{%
  title = {Theorem~\thetcbcounter: #3},
  label = {theorem-#2},
  #1,
  Record = {theorem}{\string\par{}See Theorem \string\ref{theorem-#2} (#3)},
}

\NewTColorBox[auto counter]{example}{ O{} m m }{%
  title = {Example~\thetcbcounter: #3},
  label = {example-#2},
  #1,
  Record = {example}{\string\par{}See Example \string\ref{example-#2} (#3)},
}

\begin{document}
\subsection*{General Part}

\tcbstartrecording
\TcbStartRecording{theorem}
\TcbStartRecording{example}

\tcbrecord{Test on page \thepage}
\TcbRecord{theorem}{Theorem on page \thepage}

\begin{example}{E}{Example Box}
some example
\end{example}

\begin{mybox}{A}{General Box}
some content
\end{mybox}

\begin{theorem}{B}{Theorem Box}
some content
\end{theorem}

\begin{theorem}[no recording]{C}{No recording}
some content
\end{theorem}

\TcbRecord{example}{\string\par{}Some further example}

\begin{mybox}{D}{Another General Box}
some content
\end{mybox}

\TcbStopRecording{example}
\TcbStopRecording{theorem}
\tcbstoprecording

\subsection*{Records}
\tcbinputrecords

\subsection*{Theorem Records}
\TcbInputRecords{theorem}

\subsection*{Example Records}
\TcbInputRecords{example}

\end{document}

grafik

On https://tex.stackexchange.com/questions/690242/recording-two-files-with-tcolorbox, there already is a good answer which does not need new tcolorbox code, but introduces a switch to select the displayed content while reading the (single) record file.

T-F-S commented 1 year ago

I think that https://tex.stackexchange.com/a/690275 is good way to reach the intended goal without adding further complexity to tcolorbox. I will leave this open to keep the thing in my mind.