diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3file.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3file.dtx | 758 |
1 files changed, 482 insertions, 276 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3file.dtx b/Master/texmf-dist/source/latex/l3kernel/l3file.dtx index fee42ad5736..6ea0556fab4 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3file.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3file.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3file.dtx Copyright (C) 1990-2016 The LaTeX3 Project +%% File: l3file.dtx Copyright (C) 1990-2017 The LaTeX3 Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this @@ -24,7 +24,7 @@ \documentclass[full]{l3doc} %</driver> %<*driver|package> -\GetIdInfo$Id: l3file.dtx 6805 2016-12-28 22:15:52Z joseph $ +\GetIdInfo$Id: l3file.dtx 6964 2017-02-20 02:59:41Z bruno $ {L3 File and I/O operations} %</driver|package> %<*driver> @@ -691,9 +691,9 @@ 0 \tl_map_function:NN \l_@@_internal_name_tl \@@_name_sanitize_aux:n } - \c_two + { 2 } } - = \c_zero + = 0 { \tl_remove_all:Nn \l_@@_internal_name_tl { " } \tl_if_in:NnT \l_@@_internal_name_tl { ~ } @@ -714,10 +714,7 @@ } } \cs_new:Npn \@@_name_sanitize_aux:n #1 - { - \token_if_eq_charcode:NNT #1 " - { + \c_one } - } + { \token_if_eq_charcode:NNT #1 " { + 1 } } % \end{macrocode} % \end{macro} % \end{macro} @@ -925,7 +922,7 @@ % but non-existent stream number. Unlike writing, there is no concept % of reading from the log. % \begin{macrocode} -\cs_new_eq:NN \c_term_ior \c_sixteen +\int_const:Nn \c_term_ior { 16 } % \end{macrocode} % \end{variable} % @@ -964,14 +961,14 @@ \prop_new:N \g_@@_streams_prop %<*package> \int_step_inline:nnnn - { \c_zero } - { \c_one } + { 0 } + { 1 } { \cs_if_exist:NTF \normalend { \tex_count:D 38 \scan_stop: } { \tex_count:D 16 \scan_stop: - \cs_if_exist:NT \loccount { - \c_one } + \cs_if_exist:NT \loccount { - 1 } } } { @@ -1100,7 +1097,7 @@ % \begin{macrocode} \cs_new_protected:Npn \ior_close:N #1 { - \int_compare:nT { - \c_one < #1 < \c_sixteen } + \int_compare:nT { -1 < #1 < \c_term_ior } { \tex_closein:D #1 \prop_gremove:NV \g_@@_streams_prop #1 @@ -1147,21 +1144,23 @@ % % \begin{macro}[pTF]{\ior_if_eof:N} % To test if some particular input stream is exhausted the following -% conditional is provided. +% conditional is provided. The primitive test can only deal with +% numbers in the range $[0,15]$ so we catch outliers (they are +% exhausted). % \begin{macrocode} -\prg_new_conditional:Nnn \ior_if_eof:N { p , T , F , TF } +\prg_new_conditional:Npnn \ior_if_eof:N #1 { p , T , F , TF } { \cs_if_exist:NTF #1 { - \if_int_compare:w #1 = \c_sixteen - \prg_return_true: - \else: - \if_eof:w #1 - \prg_return_true: - \else: - \prg_return_false: - \fi: - \fi: + \int_compare:nTF { -1 < #1 < \c_term_ior } + { + \if_eof:w #1 + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + { \prg_return_true: } } { \prg_return_true: } } @@ -1184,7 +1183,7 @@ { \use:x { - \int_set:Nn \tex_endlinechar:D { - \c_one } + \int_set:Nn \tex_endlinechar:D { -1 } \exp_not:n { \etex_readline:D #1 to #2 } \int_set:Nn \tex_endlinechar:D { \int_use:N \tex_endlinechar:D } } @@ -1213,11 +1212,22 @@ % % \begin{variable}{\c_log_iow, \c_term_iow} % Here we allocate two output streams for writing to the transcript -% file only (\cs{c_log_iow}) and to both the terminal and -% transcript file (\cs{c_term_iow}). +% file only (\cs{c_log_iow}) and to both the terminal and transcript +% file (\cs{c_term_iow}). Recent \LuaTeX{} provide $128$ write +% streams; we also use \cs{c_term_iow} as the first non-allowed write +% stream so its value depends on the engine. % \begin{macrocode} -\cs_new_eq:NN \c_log_iow \c_minus_one -\int_const:Nn \c_term_iow { 128 } +\int_const:Nn \c_log_iow { -1 } +\int_const:Nn \c_term_iow + { + \cs_if_exist:NTF \luatex_directlua:D + { + \int_compare:nNnTF \luatex_luatexversion:D > { 80 } + { 128 } + { 16 } + } + { 16 } + } % \end{macrocode} % \end{variable} % @@ -1226,15 +1236,12 @@ % \begin{macrocode} \seq_new:N \g_@@_streams_seq %<*initex> -\seq_set_eq:NN \g_@@_streams_seq \g__ior_streams_seq -\cs_if_exist:NT \luatex_directlua:D +\use:x { - \int_compare:nNnT \luatex_luatexversion:D > { 80 } + \exp_not:n { \seq_gset_split:Nnn \g_@@_streams_seq { } } { - \int_step_inline:nnnn { 16 } { 1 } { 127 } - { - \seq_gput_right:Nn \g_@@_streams_seq {#1} - } + \int_step_function:nnnN { 0 } { 1 } { \c_term_iow } + \prg_do_nothing: } } %</initex> @@ -1255,14 +1262,14 @@ \prop_new:N \g_@@_streams_prop %<*package> \int_step_inline:nnnn - { \c_zero } - { \c_one } + { 0 } + { 1 } { \cs_if_exist:NTF \normalend { \tex_count:D 39 \scan_stop: } { \tex_count:D 17 \scan_stop: - \cs_if_exist:NT \loccount { - \c_one } + \cs_if_exist:NT \loccount { - 1 } } } { @@ -1339,7 +1346,7 @@ % \begin{macrocode} \cs_new_protected:Npn \iow_close:N #1 { - \int_compare:nT { - \c_one < #1 < \c_sixteen } + \int_compare:nT { - \c_log_iow < #1 < \c_term_iow } { \tex_immediate:D \tex_closeout:D #1 \prop_gremove:NV \g_@@_streams_prop #1 @@ -1386,7 +1393,8 @@ % % \subsubsection{Immediate writing} % -% \begin{macro}[aux]{\@@_with:Nnn, \@@_with_aux:nNnn} +% \begin{macro}[int]{\@@_with:Nnn} +% \begin{macro}[aux]{\@@_with_aux:nNnn} % If the integer~|#1| is equal to~|#2|, just leave~|#3| in the input % stream. Otherwise, pass the old value to an auxiliary, which sets % the integer to the new value, runs the code, and restores the @@ -1406,6 +1414,7 @@ } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}{\iow_now:Nn, \iow_now:Nx, \iow_now:cn, \iow_now:cx} % This routine writes the second argument onto the output stream without @@ -1464,7 +1473,14 @@ % used by the messaging system, but is designed such that it is % available for other uses. % -% \begin{macro}[var, added = 2012-06-24]{\l_iow_line_count_int} +% \begin{macro}{\c_catcode_other_space_tl} +% Create a space with category code $12$: an \enquote{other} space. +% \begin{macrocode} +\tl_const:Nx \c_catcode_other_space_tl { \char_generate:nn { `\ } { 12 } } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_iow_line_count_int} % This is the \enquote{raw} number of characters in a line which % can be written to the terminal. % The standard value is the line length typically used by @@ -1473,96 +1489,102 @@ \int_new:N \l_iow_line_count_int \int_set:Nn \l_iow_line_count_int { 78 } % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}[aux]{\l_@@_target_count_int} -% This stores the target line count: the full number of characters -% in a line, minus any part for a leader at the start of each line. +% \begin{variable}{\l_@@_newline_tl} +% The token list inserted to produce a new line, with the +% \meta{run-on text}. % \begin{macrocode} -\int_new:N \l_@@_target_count_int +\tl_new:N \l_@@_newline_tl % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}[aux] -% { -% \l_@@_current_line_int, -% \l_@@_current_word_int, -% \l_@@_current_indentation_int -% } -% These store the number of characters in the line and word currently -% being constructed, and the current indentation, respectively. +% \begin{variable}{\l_@@_line_target_int} +% This stores the target line count: the full number of characters +% in a line, minus any part for a leader at the start of each line. % \begin{macrocode} -\int_new:N \l_@@_current_line_int -\int_new:N \l_@@_current_word_int -\int_new:N \l_@@_current_indentation_int +\int_new:N \l_@@_line_target_int % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}[aux] -% { -% \l_@@_current_line_tl, -% \l_@@_current_word_tl, -% \l_@@_current_indentation_tl -% } -% These hold the current line of text and current word, -% and a number of spaces for indentation, respectively. -% \begin{macrocode} -\tl_new:N \l_@@_current_line_tl -\tl_new:N \l_@@_current_word_tl -\tl_new:N \l_@@_current_indentation_tl +% \begin{macro}[aux]{\@@_set_indent:n} +% \begin{macro}[aux]{\@@_unindent:w} +% \begin{variable}{\l_@@_one_indent_tl, \l_@@_one_indent_int} +% The \texttt{one_indent} variables hold one indentation marker and +% its length. The \cs{@@_unindent:w} auxiliary removes one +% indentation. The function \cs{@@_set_indent:n} (that could possibly +% be public) sets the indentation in a consistent way. We set it to +% four spaces by default. +% \begin{macrocode} +\tl_new:N \l_@@_one_indent_tl +\int_new:N \l_@@_one_indent_int +\cs_new:Npn \@@_unindent:w { } +\cs_new_protected:Npn \@@_set_indent:n #1 + { + \tl_set:Nx \l_@@_one_indent_tl + { \exp_args:No \__str_to_other_fast:n { \tl_to_str:n {#1} } } + \int_set:Nn \l_@@_one_indent_int { \str_count:N \l_@@_one_indent_tl } + \exp_last_unbraced:NNo + \cs_set:Npn \@@_unindent:w \l_@@_one_indent_tl { } + } +\exp_args:Nx \@@_set_indent:n { \prg_replicate:nn { 4 } { ~ } } % \end{macrocode} -%\end{macro} +% \end{variable} +% \end{macro} +% \end{macro} % -% \begin{macro}[aux]{\l_@@_wrap_tl} -% Used for the expansion step before detokenizing, and for the output -% from wrapping text: fully expanded and with lines which are not -% overly long. +% \begin{variable}{\l_@@_indent_tl, \l_@@_indent_int} +% The current indentation (some copies of \cs{l_@@_one_indent_tl}) and +% its number of characters. % \begin{macrocode} -\tl_new:N \l_@@_wrap_tl +\tl_new:N \l_@@_indent_tl +\int_new:N \l_@@_indent_int % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}[aux]{\l_@@_newline_tl} -% The token list inserted to produce the new line, with the -% \meta{run-on text}. +% \begin{variable}{\l_@@_line_tl, \l_@@_line_part_tl} +% These hold the current line of text and a partial line to be added +% to it, respectively. % \begin{macrocode} -\tl_new:N \l_@@_newline_tl +\tl_new:N \l_@@_line_tl +\tl_new:N \l_@@_line_part_tl % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}[aux]{\l_@@_line_start_bool} -% Boolean to avoid adding a space at the beginning of forced newlines, -% and to know when to add the indentation. +% \begin{variable}{\l_@@_line_break_bool} +% Indicates whether the line was broken precisely at a chunk boundary. % \begin{macrocode} -\bool_new:N \l_@@_line_start_bool +\bool_new:N \l_@@_line_break_bool % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}{\c_catcode_other_space_tl} -% Create a space with category code $12$: an \enquote{other} space. +% \begin{variable}{\l_@@_wrap_tl} +% Used for the expansion step before detokenizing, and for the output +% from wrapping text: fully expanded and with lines which are not +% overly long. % \begin{macrocode} -\tl_const:Nx \c_catcode_other_space_tl { \char_generate:nn { `\ } { 12 } } +\tl_new:N \l_@@_wrap_tl % \end{macrocode} -% \end{macro} +% \end{variable} % -% \begin{macro}[aux]{\c_@@_wrap_marker_tl} -% \begin{macro}[aux] +% \begin{variable}{\c_@@_wrap_marker_tl} +% \begin{variable} % { % \c_@@_wrap_end_marker_tl, % \c_@@_wrap_newline_marker_tl, % \c_@@_wrap_indent_marker_tl, % \c_@@_wrap_unindent_marker_tl % } -% Every special action of the wrapping code is preceded by +% Every special action of the wrapping code is starts with % the same recognizable string, \cs{c_@@_wrap_marker_tl}. % Upon seeing that \enquote{word}, the wrapping code reads % one space-delimited argument to know what operation to % perform. The setting of \tn{escapechar} here is not % very important, but makes \cs{c_@@_wrap_marker_tl} look -% nicer. +% marginally nicer. % \begin{macrocode} \group_begin: - \int_set:Nn \tex_escapechar:D { - \c_one } + \int_set:Nn \tex_escapechar:D { -1 } \tl_const:Nx \c_@@_wrap_marker_tl { \tl_to_str:n { \^^I \^^O \^^W \^^_ \^^W \^^R \^^A \^^P } } \group_end: @@ -1571,26 +1593,32 @@ { \tl_const:cx { c_@@_wrap_ #1 _marker_tl } { - \c_catcode_other_space_tl \c_@@_wrap_marker_tl - \c_catcode_other_space_tl #1 \c_catcode_other_space_tl } } % \end{macrocode} -% \end{macro} -% \end{macro} +% \end{variable} +% \end{variable} % % \begin{macro}{\iow_indent:n} -% \begin{macro}[aux]{\@@_indent:n, \@@_indent_error:n} -% We give a (protected) error definition to \cs{iow_indent:n} -% when outside messages. Within wrapped message, it places -% the instruction for increasing the indentation before its -% argument, and the instruction for unindenting afterwards. -% Note that there will be no forced line-break, so the indentation -% only changes when the next line is started. -% \begin{macrocode} +% \begin{macro}[EXP,aux]{\@@_indent:n} +% \begin{macro}[EXP,aux]{\@@_indent_error:n} +% We set \cs{iow_indent:n} to produce an error when outside +% messages. Within wrapped message, it is set to \cs{@@_indent:n} when +% valid and otherwise to \cs{@@_indent_error:n}. The first places the +% instruction for increasing the indentation before its argument, and +% the instruction for unindenting afterwards. The second produces an +% error expandably. Note that there will be no forced line-break, so +% the indentation only changes when the next line is started. +% \begin{macrocode} +\cs_new_protected:Npn \iow_indent:n #1 + { + \__msg_kernel_error:nnnnn { kernel } { iow-indent } + { \iow_wrap:nnnN } { \iow_indent:n } {#1} + #1 + } \cs_new:Npx \@@_indent:n #1 { \c_@@_wrap_indent_marker_tl @@ -1599,36 +1627,28 @@ } \cs_new:Npn \@@_indent_error:n #1 { - \__msg_kernel_expandable_error:nn { kernel } { indent-outside-wrapping-code } + \__msg_kernel_expandable_error:nnnnn { kernel } { iow-indent } + { \iow_wrap:nnnN } { \iow_indent:n } {#1} #1 } -\cs_new_protected:Npn \iow_indent:n { \@@_indent_error:n } % \end{macrocode} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}{\iow_wrap:nnnN} % \begin{macro}[aux]{\@@_wrap_set:Nx} % The main wrapping function works as follows. First give |\\|, % \verb*|\ | and other formatting commands the correct definition for -% messages, before fully-expanding the input. In package mode, the -% expansion uses \LaTeXe{}'s \cs{protect} mechanism. Afterwards, set -% the newline marker (two assignments to fully expand, then convert -% to a string) and its length, and initialize some registers. -% There is then a loop over each word in the input, which will do the -% actual wrapping. After the loop, the resulting text is passed on to -% the function which has been given as a post-processor. -% The argument |#4| is available for additional set up steps for -% the output. The definition of |\\| and \verb*|\ | use an -% \enquote{other} space rather than a normal space, because the latter -% might be absorbed by \TeX{} to end a number or other \texttt{f}-type -% expansions. The \cs{tl_to_str:N} step converts the \enquote{other} -% space back to a normal space. +% messages and perform the given setup~|#3|. The definition of +% \verb*|\ | uses an \enquote{other} space rather than a normal space, +% because the latter might be absorbed by \TeX{} to end a number or +% other \texttt{f}-type expansions. % \begin{macrocode} \cs_new_protected:Npn \iow_wrap:nnnN #1#2#3#4 { \group_begin: - \int_set:Nn \tex_escapechar:D { - \c_one } + \int_set:Nn \tex_escapechar:D { -1 } \cs_set:Npx \{ { \token_to_str:N \{ } \cs_set:Npx \# { \token_to_str:N \# } \cs_set:Npx \} { \token_to_str:N \} } @@ -1639,48 +1659,39 @@ \cs_set_eq:NN \ \c_catcode_other_space_tl \cs_set_eq:NN \iow_indent:n \@@_indent:n #3 -%<*initex> +% \end{macrocode} +% Then fully-expand the input: in package mode, the expansion uses +% \LaTeXe{}'s \cs{protected} mechanism. As soon as the expansion is +% done, reset \cs{iow_indent:n} to its error definition: it only works +% in the first argument of \cs{iow_wrap:nnnN}. +% \begin{macrocode} +%<*initex> \tl_set:Nx \l_@@_wrap_tl {#1} %</initex> %<*package> \@@_wrap_set:Nx \l_@@_wrap_tl {#1} %</package> + \cs_set_eq:NN \iow_indent:n \@@_indent_error:n % \end{macrocode} -% To warn users that \cs{iow_indent:n} only works in the first argument -% of \cs{iow_wrap:nnnN} reset \cs{iow_indent:n} to its error -% definition. Then store a newline character and the run-on text as a -% string in \cs{l_@@_newline_tl}, and set some variables. The first -% line's target count is equal to the length of the whole line. The -% value \cs{l_@@_target_count_int} is altered later on by -% \cs{@@_wrap_set_target:}. +% Afterwards, set the newline marker (two assignments to fully expand, +% then convert to a string) and initialize the target count for lines +% (the first line has target count \cs{l_iow_line_count_int} instead). % \begin{macrocode} - \cs_set_eq:NN \iow_indent:n \@@_indent_error:n \tl_set:Nx \l_@@_newline_tl { \iow_newline: #2 } \tl_set:Nx \l_@@_newline_tl { \tl_to_str:N \l_@@_newline_tl } - \int_set_eq:NN \l_@@_target_count_int \l_iow_line_count_int - \tl_clear:N \l_@@_current_indentation_tl - \int_zero:N \l_@@_current_line_int - \tl_set:Nn \l_@@_current_line_tl { \use_none:n } - \bool_set_true:N \l_@@_line_start_bool -% \end{macrocode} -% After some setup above (in particular the odd setting of the current line -% to \cs{use_none:n}), a loop goes through space-delimited words in the -% message, recognizing special markers. To make sure that the first line -% behaves identically to others, start with a newline marker: the -% \cs{use_none:n} above avoids actually getting a new line in the output. -% \begin{macrocode} - \use:x - { - \exp_not:n { \tl_clear:N \l_@@_wrap_tl } - \@@_wrap_loop:w - \tl_to_str:N \c_@@_wrap_newline_marker_tl - \tl_to_str:N \l_@@_wrap_tl - \tl_to_str:N \c_@@_wrap_end_marker_tl - \c_space_tl \c_space_tl - \exp_not:N \q_stop - } - \exp_args:NNo \group_end: - #4 \l_@@_wrap_tl + \int_set:Nn \l_@@_line_target_int + { \l_iow_line_count_int - \str_count:N \l_@@_newline_tl + 1 } +% \end{macrocode} +% There is then a loop over the input, which will store the wrapped +% result in \cs{l_@@_wrap_tl}. After the loop, the resulting text is +% passed on to the function which has been given as a post-processor. +% The \cs{tl_to_str:N} step converts the \enquote{other} spaces back +% to normal spaces. The \texttt{f}-expansion removes a leading space +% from \cs{l_@@_wrap_tl}. +% \begin{macrocode} + \@@_wrap_do: + \exp_args:NNf \group_end: + #4 { \tl_to_str:N \l_@@_wrap_tl } } % \end{macrocode} % As using the generic loader will mean that \cs{protected@edef} is @@ -1694,160 +1705,342 @@ % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\@@_wrap_set_target:} -% This is called at the beginning of every line (both those forced by -% |\\| and those due to line-breaking). The initial call does -% nothing except redefine \cs{@@_wrap_set_target:} itself (within the -% group in which \cs{iow_wrap:nnnN} works). The next call (at the -% beginning of the second line) disables any later call and sets the -% \cs{l_@@_target_count_int} to the correct value, namely the -% \cs{l_iow_line_count_int} shortened by the length of the run-on -% text (the shift by~$1$ is due to the presence of \cs{iow_newline:} -% in \cs{l_@@_newline_tl}). -% This is a bit of a hack to measure the string length of the run on text -% without the \pkg{l3str} module (which is still experimental). This should -% be replaced once the string module is finalised with something a little -% cleaner. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_set_target: +% \begin{macro}[aux]{\@@_wrap_do:, \@@_wrap_start:w} +% Escape spaces. Set up a few variables, in particular the initial +% value of \cs{l_@@_wrap_tl}: the space will stop the +% \texttt{f}-expansion of the main wrapping function and +% \cs{use_none:n} will remove a newline marker inserted by later code. +% The main loop consists of repeatedly calling the \texttt{chunk} +% auxiliary to wrap chunks delimited by (newline or indentation) +% markers. +% \begin{macrocode} +\cs_new_protected:Npn \@@_wrap_do: { - \cs_set_protected:Npn \@@_wrap_set_target: + \tl_set:Nx \l_@@_wrap_tl { - \cs_set_protected:Npn \@@_wrap_set_target: { } - \tl_replace_all:Nnn \l_@@_newline_tl { ~ } { \c_space_tl } - \int_set:Nn \l_@@_target_count_int - { \l_iow_line_count_int - \tl_count:N \l_@@_newline_tl + \c_one } + \exp_args:No \__str_to_other_fast:n \l_@@_wrap_tl + \c_@@_wrap_end_marker_tl } + \exp_after:wN \@@_wrap_start:w \l_@@_wrap_tl + } +\cs_new_protected:Npn \@@_wrap_start:w + { + \bool_set_false:N \l_@@_line_break_bool + \tl_clear:N \l_@@_line_tl + \tl_clear:N \l_@@_line_part_tl + \tl_set:Nn \l_@@_wrap_tl { ~ \use_none:n } + \int_zero:N \l_@@_indent_int + \tl_clear:N \l_@@_indent_tl + \@@_wrap_chunk:nw { \l_iow_line_count_int } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\@@_wrap_loop:w} -% The loop grabs one word in the input, and checks whether it is -% the special marker, or a normal word. +% \begin{macro}[aux]{\@@_wrap_chunk:nw, \@@_wrap_next:nw} +% The \texttt{chunk} and \texttt{next} auxiliaries are defined +% indirectly to obtain the expansions of \cs{c_catcode_other_space_tl} +% and \cs{c_@@_wrap_marker_tl} in their definition. The \texttt{next} +% auxiliary calls a function corresponding to the type of marker (its +% |##2|), which can be \texttt{newline} or \texttt{indent} or +% \texttt{unindent} or \texttt{end}. The first argument of the +% \texttt{chunk} auxiliary is a target number of characters and the +% second is some string to wrap. If the chunk is empty simply call +% \texttt{next}. Otherwise, set up a call to \cs{@@_wrap_line:nw}, +% including the indentation if the current line is empty, and +% including a trailing space (|#1|) before the +% \cs{@@_wrap_end_chunk:w} auxiliary. % \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_loop:w #1 ~ % +\cs_set_protected:Npn \@@_tmp:w #1#2 { - \tl_set:Nn \l_@@_current_word_tl {#1} - \tl_if_eq:NNTF \l_@@_current_word_tl \c_@@_wrap_marker_tl - { \@@_wrap_special:w } - { \@@_wrap_word: } + \cs_new_protected:Npn \@@_wrap_chunk:nw ##1##2 #2 + { + \tl_if_empty:nTF {##2} + { + \tl_clear:N \l_@@_line_part_tl + \@@_wrap_next:nw {##1} + } + { + \tl_if_empty:NTF \l_@@_line_tl + { + \@@_wrap_line:nw + { \l_@@_indent_tl } + ##1 - \l_@@_indent_int ; + } + { \@@_wrap_line:nw { } ##1 ; } + ##2 #1 + \@@_wrap_end_chunk:w 7 6 5 4 3 2 1 0 \q_stop + } + } + \cs_new_protected:Npn \@@_wrap_next:nw ##1##2 #1 + { \use:c { @@_wrap_##2:n } {##1} } } +\exp_args:NVV \@@_tmp:w \c_catcode_other_space_tl \c_@@_wrap_marker_tl % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\@@_wrap_word:} -% \begin{macro}[aux]{\@@_wrap_word_fits:} -% \begin{macro}[aux]{\@@_wrap_word_newline:} -% For a normal word, update the line count, then test if the current -% word would fit in the current line, and call the appropriate function. -% If the word fits in the current line, add it to the line, preceded by -% a space unless it is the first word of the line. -% Otherwise, the current line is added to the result, with the run-on text. -% The current word (and its character count) are then put in the new line. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_word: +% \begin{macro}[aux]{\@@_wrap_line:nw} +% \begin{macro}[EXP,aux] +% { +% \@@_wrap_line_loop:w, +% \@@_wrap_line_aux:Nw, +% \@@_wrap_line_end:NnnnnnnnN, +% \@@_wrap_line_end:nw, +% \@@_wrap_end_chunk:w +% } +% This is followed by \Arg{string} \meta{intexpr} |;|. It stores the +% \meta{string} and up to \meta{intexpr} characters from the current +% chunk into \cs{l_@@_line_part_tl}. Characters are grabbed 8~at a +% time and left in \cs{l_@@_line_part_tl} by the \texttt{line_loop} +% auxiliary. When $k<8$ remain to be found, the \texttt{line_aux} +% auxiliary calls the \texttt{line_end} auxiliary followed by (the +% single digit) $k$, then $7-k$ empty brace groups, then the chunk's +% remaining characters. The \texttt{line_end} auxiliary leaves +% $k$~characters from the chunk in the line part, then ends the +% assignment. Ignore the \cs{use_none:nnnnn} line for now. If the +% next character is a space the line can be broken there: +% store what we found into the result and get the next line. +% Otherwise some work is needed to find a break-point. So far we have +% ignored what happens if the chunk is shorter than the requested +% number of characters: this is dealt with by the \texttt{end_chunk} +% auxiliary, which gets treated like a character by the rest of the +% code. It ends up being called either as one of the arguments +% |#2|--|#9| of the \texttt{line_loop} auxiliary or as one of the +% arguments |#2|--|#8| of the \texttt{line_end} auxiliary. In both +% cases stop the assignment and work out how many characters are still +% needed. The weird \cs{use_none:nnnnn} ensures that the required +% data is in the right place. +% \begin{macrocode} +\cs_new_protected:Npn \@@_wrap_line:nw #1 + { + \tex_edef:D \l_@@_line_part_tl { \if_false: } \fi: + #1 + \exp_after:wN \@@_wrap_line_loop:w + \__int_value:w \__int_eval:w + } +\cs_new:Npn \@@_wrap_line_loop:w #1 ; #2#3#4#5#6#7#8#9 + { + \if_int_compare:w #1 < 8 \exp_stop_f: + \@@_wrap_line_aux:Nw #1 + \fi: + #2 #3 #4 #5 #6 #7 #8 #9 + \exp_after:wN \@@_wrap_line_loop:w + \__int_value:w \__int_eval:w #1 - 8 ; + } +\cs_new:Npn \@@_wrap_line_aux:Nw #1#2#3 \exp_after:wN #4 ; + { + #2 + \exp_after:wN \@@_wrap_line_end:NnnnnnnnN + \exp_after:wN #1 + \exp:w \exp_end_continue_f:w + \exp_after:wN \exp_after:wN + \if_case:w #1 \exp_stop_f: + \prg_do_nothing: + \or: \use_none:n + \or: \use_none:nn + \or: \use_none:nnn + \or: \use_none:nnnn + \or: \use_none:nnnnn + \or: \use_none:nnnnnn + \or: \use_none:nnnnnnn + \fi: + { } { } { } { } { } { } { } #3 + } +\cs_new:Npn \@@_wrap_line_end:NnnnnnnnN #1#2#3#4#5#6#7#8#9 + { + #2 #3 #4 #5 #6 #7 #8 + \use_none:nnnnn \__int_eval:w 8 - ; #9 + \token_if_eq_charcode:NNTF \c_space_token #9 + { \@@_wrap_line_end:nw { } } + { \if_false: { \fi: } \@@_wrap_break:w #9 } + } +\cs_new:Npn \@@_wrap_line_end:nw #1 + { + \if_false: { \fi: } + \@@_wrap_store_do:n {#1} + \@@_wrap_next_line:w + } +\cs_new:Npn \@@_wrap_end_chunk:w + #1 \__int_eval:w #2 - #3 ; #4#5 \q_stop { - \int_set:Nn \l_@@_current_word_int - { \exp_args:No \str_count_ignore_spaces:n \l_@@_current_word_tl } - \int_add:Nn \l_@@_current_line_int { \l_@@_current_word_int } - \int_compare:nNnTF \l_@@_current_line_int < \l_@@_target_count_int - { \@@_wrap_word_fits: } - { \@@_wrap_word_newline: } - \@@_wrap_loop:w + \if_false: { \fi: } + \exp_args:Nf \@@_wrap_next:nw { \int_eval:n { #2 - #4 } } } -\cs_new_protected:Npn \@@_wrap_word_fits: +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP,aux]{\@@_wrap_break:w} +% \begin{macro}[EXP,aux] +% { +% \@@_wrap_break_first:w, +% \@@_wrap_break_none:w, +% \@@_wrap_break_loop:w, +% \@@_wrap_break_end:w, +% } +% Functions here are defined indirectly: \cs{@@_tmp:w} is eventually +% called with an \enquote{other} space as its argument. The goal is +% to remove from \cs{l_@@_line_part_tl} the part after the last space. +% In most cases this is done by repeatedly calling the +% \texttt{break_loop} auxiliary, which leaves \enquote{words} +% (delimited by spaces) until it hits the trailing space: then its +% argument |##3| is |?| \cs{@@_wrap_break_end:w} instead of a single +% token, and that \texttt{break_end} auxiliary leaves in the +% assignment the line until the last space, then calls +% \cs{@@_wrap_line_end:nw} to finish up the line and move on to the +% next. If there is no space in \cs{l_@@_line_part_tl} then the +% \texttt{break_first} auxiliary calls the \texttt{break_none} +% auxiliary. In that case, if the current line is empty, the complete +% word (including |##4|, characters beyond what we had grabbed) is +% added to the line, making it over-long. Otherwise, the word will be +% used for the following line (and the last space of the line so far +% is removed because it was inserted due to the presence of a marker). +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 { - \bool_if:NTF \l_@@_line_start_bool + \cs_new:Npn \@@_wrap_break:w { - \bool_set_false:N \l_@@_line_start_bool - \tl_put_right:Nx \l_@@_current_line_tl - { \l_@@_current_indentation_tl \l_@@_current_word_tl } - \int_add:Nn \l_@@_current_line_int - { \l_@@_current_indentation_int } + \tex_edef:D \l_@@_line_part_tl + { \if_false: } \fi: + \exp_after:wN \@@_wrap_break_first:w + \l_@@_line_part_tl + #1 + { ? \@@_wrap_break_end:w } + \q_mark } + \cs_new:Npn \@@_wrap_break_first:w ##1 #1 ##2 { - \tl_put_right:Nx \l_@@_current_line_tl - { ~ \l_@@_current_word_tl } - \int_incr:N \l_@@_current_line_int + \use_none:nn ##2 \@@_wrap_break_none:w + \@@_wrap_break_loop:w ##1 #1 ##2 } - } -\cs_new_protected:Npn \@@_wrap_word_newline: - { - \@@_wrap_set_target: - \tl_put_right:Nx \l_@@_wrap_tl - { \l_@@_current_line_tl \l_@@_newline_tl } - \int_set:Nn \l_@@_current_line_int + \cs_new:Npn \@@_wrap_break_none:w ##1##2 #1 ##3 \q_mark ##4 #1 { - \l_@@_current_word_int - + \l_@@_current_indentation_int + \tl_if_empty:NTF \l_@@_line_tl + { ##2 ##4 \@@_wrap_line_end:nw { } } + { \@@_wrap_line_end:nw { \@@_wrap_trim:N } ##2 ##4 #1 } } - \tl_set:Nx \l_@@_current_line_tl - { \l_@@_current_indentation_tl \l_@@_current_word_tl } + \cs_new:Npn \@@_wrap_break_loop:w ##1 #1 ##2 #1 ##3 + { + \use_none:n ##3 + ##1 #1 + \@@_wrap_break_loop:w ##2 #1 ##3 + } + \cs_new:Npn \@@_wrap_break_end:w ##1 #1 ##2 ##3 #1 ##4 \q_mark + { ##1 \@@_wrap_line_end:nw { } ##3 } } +\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl % \end{macrocode} % \end{macro} % \end{macro} -% \end{macro} % -% \begin{macro}[aux]{\@@_wrap_special:w} -% \begin{macro}[aux]{\@@_wrap_newline:w} -% \begin{macro}[aux]{\@@_wrap_indent:w} -% \begin{macro}[aux]{\@@_wrap_unindent:w} -% \begin{macro}[aux]{\@@_wrap_end:w} -% When the \enquote{special} marker is encountered, -% read what operation to perform, as a space-delimited -% argument, perform it, and remember to loop. -% In fact, to avoid spurious spaces when two special -% actions follow each other, we look ahead for another -% copy of the marker. -% Forced newlines are almost identical to those caused by overflow, -% except that here the word is empty. -% To indent more, add four spaces to the start of the indentation -% token list. To reduce indentation, rebuild the indentation token -% list using \cs{prg_replicate:nn}. -% At the end, we simply save the last line (without the run-on text), -% and prevent the loop. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_special:w #1 ~ #2 ~ #3 ~ % +% \begin{macro}[aux]{\@@_wrap_next_line:w} +% The special case where the end of a line coincides with the end of a +% chunk is detected here, to avoid a spurious empty line. Otherwise, +% call \cs{@@_wrap_line:nw} to find characters for the next line +% (remembering to account for the indentation). +% \begin{macrocode} +\cs_new_protected:Npn \@@_wrap_next_line:w #1#2 \q_stop { - \use:c { @@_wrap_#1: } - \str_if_eq_x:nnTF { #2~#3 } { ~ \c_@@_wrap_marker_tl } - { \@@_wrap_special:w } - { \@@_wrap_loop:w #2 ~ #3 ~ } + \tl_clear:N \l_@@_line_tl + \token_if_eq_meaning:NNTF #1 \@@_wrap_end_chunk:w + { + \tl_clear:N \l_@@_line_part_tl + \bool_set_true:N \l_@@_line_break_bool + \@@_wrap_next:nw { \l_@@_line_target_int } + } + { + \@@_wrap_line:nw + { \l_@@_indent_tl } + \l_@@_line_target_int - \l_@@_indent_int ; + #1 #2 \q_stop + } } -\cs_new_protected:Npn \@@_wrap_newline: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_wrap_indent:, \@@_wrap_unindent:} +% These functions are called after a chunk has been wrapped, when +% encountering \texttt{indent}/\texttt{unindent} markers. Add the +% line part (last line part of the previous chunk) to the line so far +% and reset a boolean denoting the presence of a line-break. Most +% importantly, add or remove one indent from the current indent (both +% the integer and the token list). Finally, continue wrapping. +% \begin{macrocode} +\cs_new_protected:Npn \@@_wrap_indent:n #1 { - \@@_wrap_set_target: - \tl_put_right:Nx \l_@@_wrap_tl - { \l_@@_current_line_tl \l_@@_newline_tl } - \int_zero:N \l_@@_current_line_int - \tl_clear:N \l_@@_current_line_tl - \bool_set_true:N \l_@@_line_start_bool + \tl_put_right:Nx \l_@@_line_tl { \l_@@_line_part_tl } + \bool_set_false:N \l_@@_line_break_bool + \int_add:Nn \l_@@_indent_int { \l_@@_one_indent_int } + \tl_put_right:No \l_@@_indent_tl { \l_@@_one_indent_tl } + \@@_wrap_chunk:nw {#1} } -\cs_new_protected:Npx \@@_wrap_indent: +\cs_new_protected:Npn \@@_wrap_unindent:n #1 { - \int_add:Nn \l_@@_current_indentation_int \c_four - \tl_put_right:Nx \exp_not:N \l_@@_current_indentation_tl - { \c_space_tl \c_space_tl \c_space_tl \c_space_tl } + \tl_put_right:Nx \l_@@_line_tl { \l_@@_line_part_tl } + \bool_set_false:N \l_@@_line_break_bool + \int_sub:Nn \l_@@_indent_int { \l_@@_one_indent_int } + \tl_set:Nx \l_@@_indent_tl + { \exp_after:wN \@@_unindent:w \l_@@_indent_tl } + \@@_wrap_chunk:nw {#1} } -\cs_new_protected:Npn \@@_wrap_unindent: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_wrap_newline:, \@@_wrap_end:} +% These functions are called after a chunk has been line-wrapped, when +% encountering a \texttt{newline}/\texttt{end} marker. Unless we just +% took a line-break, store the line part and the line so far into the +% whole \cs{l_@@_wrap_tl}, trimming a trailing space. In the +% \texttt{newline} case look for a new line (of length +% \cs{l_@@_line_target_int}) in a new chunk. +% \begin{macrocode} +\cs_new_protected:Npn \@@_wrap_newline:n #1 { - \int_sub:Nn \l_@@_current_indentation_int \c_four - \tl_set:Nx \l_@@_current_indentation_tl - { \prg_replicate:nn \l_@@_current_indentation_int { ~ } } + \bool_if:NF \l_@@_line_break_bool + { \@@_wrap_store_do:n { \@@_wrap_trim:N } } + \bool_set_false:N \l_@@_line_break_bool + \@@_wrap_chunk:nw { \l_@@_line_target_int } } -\cs_new_protected:Npn \@@_wrap_end: +\cs_new_protected:Npn \@@_wrap_end:n #1 { - \tl_put_right:Nx \l_@@_wrap_tl - { \l_@@_current_line_tl } - \use_none_delimit_by_q_stop:w + \bool_if:NF \l_@@_line_break_bool + { \@@_wrap_store_do:n { \@@_wrap_trim:N } } + \bool_set_false:N \l_@@_line_break_bool } % \end{macrocode} % \end{macro} +% +% \begin{macro}[aux]{\@@_wrap_store_do:n} +% First add the last line part to the line, then append it to +% \cs{l_@@_wrap_tl} with the appropriate new line (with +% \enquote{run-on} text), possibly with its last space removed (|#1| +% is empty or \cs{@@_wrap_trim:N}). +% \begin{macrocode} +\cs_new_protected:Npn \@@_wrap_store_do:n #1 + { + \tl_set:Nx \l_@@_line_tl + { \l_@@_line_tl \l_@@_line_part_tl } + \tl_set:Nx \l_@@_wrap_tl + { + \l_@@_wrap_tl + \l_@@_newline_tl + #1 \l_@@_line_tl + } + \tl_clear:N \l_@@_line_tl + } +% \end{macrocode} % \end{macro} -% \end{macro} -% \end{macro} +% +% \begin{macro}[EXP, aux]{\@@_wrap_trim:N, \@@_wrap_trim:w} +% Remove one trailing \enquote{other} space from the argument. +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_wrap_trim:N ##1 + { \tl_if_empty:NF ##1 { \exp_after:wN \@@_wrap_trim:w ##1 \q_stop } } + \cs_new:Npn \@@_wrap_trim:w ##1 #1 \q_stop {##1} + } +\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl +% \end{macrocode} % \end{macro} % % \subsection{Messages} @@ -1878,8 +2071,13 @@ { File~names~must~contain~balanced~numbers~of~quotes~("). } -\__msg_kernel_new:nnn { kernel } { indent-outside-wrapping-code } - { Only~\iow_wrap:nnnN~(arg~1)~allows~\iow_indent:n } +\__msg_kernel_new:nnnn { kernel } { iow-indent } + { Only~#1 (arg~1)~allows~#2 } + { + The~command~#2 can~only~be~used~in~messages~ + which~will~be~wrapped~using~#1.~ + It~was~called~with~argument~'#3'. + } % \end{macrocode} % % \subsection{Deprecated functions} @@ -1887,7 +2085,15 @@ % \begin{macro}[added = 2012-06-24, updated = 2012-07-31, deprecated=2017-12-31]{\ior_get_str:NN} % For removal after 2017-12-31. % \begin{macrocode} -\cs_new_eq:NN \ior_get_str:NN \ior_str_get:NN +\cs_new_protected:Npn \ior_get_str:NN + { + \__msg_kernel_warning:nnxxx { kernel } { deprecated-command } + { 2017-12-31 } + { \token_to_str:N \ior_get_str:NN } + { \token_to_str:N \ior_str_get:NN } + \cs_gset_eq:NN \ior_get_str:NN \ior_str_get:NN + \ior_str_get:NN + } % \end{macrocode} % \end{macro} % |