% \iffalse %% File: l3msg.dtx Copyright (C) 2009 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 %% license or (at your option) any later version. The latest version %% of this license is in the file %% %% http://www.latex-project.org/lppl.txt %% %% This file is part of the ``expl3 bundle'' (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX Project Team. %% %% ----------------------------------------------------------------------- %% % %<*driver|package> \RequirePackage{l3names} % %\fi \GetIdInfo$Id: l3msg.dtx 1722 2009-11-06 13:22:12Z joseph $ {L3 Experimental LaTeX Messages module} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass[full]{l3doc} \begin{document} \DocInput{l3msg.dtx} \end{document} % % \fi % % \title{The \textsf{l3msg} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Communicating with the user} % \author{\Team} % \date{\filedate} % \maketitle % % \begin{documentation} % % Messages need to be passed to the user by modules, either when errors % occur or to indicate how the code is proceeding. The \pkg{l3msg} % module provides a consistent method for doing this (as opposed to % writing directly to the terminal or log). % % The system used by \pkg{l3msg} to create messages divides the process % into two distinct parts. Named messages are created in the first part % of the process; at this stage, no decision is made about the type % output that the message will produce. The second part of the process % is actually producing a message. At this stage a choice of message % \emph{class} has to be made, for example \texttt{error}, % \texttt{warning} or \texttt{info}. % % By separating out the creation and use of messages, several benefits % are available. First, the messages can be altered later without % needing details of where they are used in the code. This makes it % possible to alter the language used, the detail level and so on. % Secondly, the output which results from a given message can be % altered. This can be done on a message class, module or message name % basis. In this way, message behaviour can be altered and messages can % be entirely suppressed. % % \section{Creating new messages} % % All messages have to be created before they can be used. Inside the % message text, spaces are \emph{not} ignored. A space where \TeX\ % would normally gobble one can be created using |\ |, and a new line % with |\\|. New lines may have ``continuation'' text added by the % output system. % %\begin{function}{ % \msg_new:nnnn| % \msg_new:nnn| % \msg_set:nnnn| % \msg_set:nnn %} % \begin{syntax} % "\msg_new:nnnn" \marg{module} \marg{name} \marg{text} \marg{more text} % \end{syntax} % Creates new message for to produce initially % and if requested by the user. and can % use up to four macro parameters (|#1| to |#4|), which are supplied by % the message system. Inside and spaces are not % ignored. %\end{function} % % \section{Message classes} % % Creating message output requires the message to be given a class. % %\begin{function}{ % \msg_class_new:nn| % \msg_class_set:nn %} % \begin{syntax} % "\msg_class_new:nn" \marg{class} \marg{code} % \end{syntax} % Creates new to output a message, using to process % the message text. %\end{function} % % The module defines several common message classes. The following % describes the standard behaviour of each class if no redirection of % the class or message is active. % %\begin{function}{ % \msg_fatal:nnxxxx| % \msg_fatal:nnxxx| % \msg_fatal:nnxx| % \msg_fatal:nnx| % \msg_fatal:nn %} % \begin{syntax} % "\msg_fatal:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Issues error message , passing to % to the text-creating functions. The \TeX\ run then halts. %\end{function} % %\begin{function}{ % \msg_error:nnxxxx| % \msg_error:nnxxx| % \msg_error:nnxx| % \msg_error:nnx| % \msg_error:nn %} % \begin{syntax} % "\msg_error:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Issues error message , passing to % to the text-creating functions. % \begin{texnote} % The standard output here is similar to \cs{PackageError}. % \end{texnote} %\end{function} % %\begin{function}{ % \msg_warning:nnxxxx| % \msg_warning:nnxxx| % \msg_warning:nnxx| % \msg_warning:nnx| % \msg_warning:nn %} % \begin{syntax} % "\msg_warning:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Prints message to the terminal, passing to % to the text-creating functions. % \begin{texnote} % The standard output here is similar to \cs{PackageWarningNoLine}. % \end{texnote} %\end{function} % %\begin{function}{ % \msg_info:nnxxxx| % \msg_info:nnxxx| % \msg_info:nnxx| % \msg_info:nnx| % \msg_info:nn %} % \begin{syntax} % "\msg_info:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Prints message to the log, passing to % to the text-creating functions. % \begin{texnote} % The standard output here is similar to \cs{PackageInfoNoLine}. % \end{texnote} %\end{function} % %\begin{function}{ % \msg_log:nnxxxx| % \msg_log:nnxxx| % \msg_log:nnxx| % \msg_log:nnx| % \msg_log:nn %} % \begin{syntax} % "\msg_log:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Prints message to the log, passing to % to the text-creating functions. No continuation text is % added. %\end{function} % %\begin{function}{ % \msg_trace:nnxxxx| % \msg_trace:nnxxx| % \msg_trace:nnxx| % \msg_trace:nnx| % \msg_trace:nn %} % \begin{syntax} % "\msg_trace:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Prints message to the log, passing to % to the text-creating functions. No continuation text is % added. %\end{function} % %\begin{function}{ % \msg_none:nnxxxx| % \msg_none:nnxxx| % \msg_none:nnxx| % \msg_none:nnx| % \msg_none:nn %} % \begin{syntax} % "\msg_none:nnxxxx" \marg{module} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Does nothing: used for redirecting other message classes. Gobbles % arguments given. %\end{function} % % \section{Redirecting messages} % %\begin{function}{\msg_redirect_class:nn} % \begin{syntax} % "\msg_redirect_class:nn" \marg{class one} \marg{class two} % \end{syntax} % Redirect all messages of to appear as those of % . %\end{function} % %\begin{function}{\msg_redirect_module:nnn} % \begin{syntax} % "\msg_redirect_module:nnn" \marg{module} \marg{class one} % \marg{class two} % \end{syntax} % Redirect messages of to appear in . % \begin{texnote} % This function can be used to make some messages ``silent'' by % default. For example, all of the \texttt{trace} messages of % could be turned off with: % \begin{verbatim} % \msg_redirect_module:nnn { module } { trace } { none } % \end{verbatim} % \end{texnote} %\end{function} % %\begin{function}{\msg_redirect_name:nnn} % \begin{syntax} % "\msg_redirect_name:nnn" \marg{module} \marg{message} \marg{class} % \end{syntax} % Redirect to appear using . %\end{function} % % \section{Support functions for output} % %\begin{function}{\msg_line_context:} % \begin{syntax} % "\msg_line_context:" % \end{syntax} % Prints the current line number preceded by \cs{c_msg_on_line_tl}. %\end{function} % %\begin{function}{\msg_line_number:} % \begin{syntax} % "\msg_line_number:" % \end{syntax} % Prints the current line number. %\end{function} % %\begin{function}{ % \msg_newline:| % \msg_two_newlines: %} % \begin{syntax} % "\msg_newline:" % \end{syntax} % Print one or two newlines with no continuation information. %\end{function} % %\begin{function}{ % \msg_space:| % \msg_two_spaces:| % \msg_four_spaces: %} % \begin{syntax} % "\msg_space:" % \end{syntax} % Print one, two or four spaces: needed where a literal space would % otherwise be gobbled by \TeX. %\end{function} % % \section{Low-level functions} % % The low-level functions do not make assumptions about module names. % The output functions here produce messages directly, and do not % respond to redirection. % %\begin{function}{ % \msg_generic_new:nnn| % \msg_generic_new:nn| % \msg_generic_set:nnn| % \msg_generic_set:nn %} % \begin{syntax} % "\msg_generic_new:nnn" \marg{name} \marg{text} \marg{more text} % \end{syntax} % Creates new message to produce initially and % if requested by the user. and can use % up to four macro parameters (|#1| to |#4|), which are supplied by the % message system. Inside and spaces are not ignored. %\end{function} % %\begin{function}{\msg_direct_interrupt:xxxx} % \begin{syntax} % "\msg_direct_interrupt:xxxxn" \marg{first line} \marg{text} % ~~~~\marg{continuation} \marg{more text} % \end{syntax} % Executes a \TeX\ error, interrupting compilation. The % is displayed followed by and the input prompt. is % displays if requested by the user. If is blank a default % is supplied. Each line of (broken with |\\|) begins with % . %\end{function} % %\begin{function}{ % \msg_direct_log:xx| % \msg_direct_term:xx %} % \begin{syntax} % "\msg_direct_log:xx" \marg{text} \marg{continuation} % \end{syntax} % Prints to either the log or terminal. New lines (broken % with |\\|) start with . %\end{function} % % \section{Kernel-specific functions} % %\begin{function}{ % \msg_kernel_new:nnnn| % \msg_kernel_new:nnn| % \msg_kernel_set:nnnn| % \msg_kernel_set:nnn %} % \begin{syntax} % "\msg_kernel_new:nnnn" \marg{divsion} \marg{name} \marg{text} % ~~~~\marg{more text} % \end{syntax} % Creates new kernel message to produce initially and % if requested by the user. and can use % up to four macro parameters (|#1| to |#4|), which are supplied by the % message system. Kernel messages are divided into , roughly % equivalent to the \LaTeXe\ package names used. %\end{function} % %\begin{function}{ % \msg_kernel_fatal:nnxxxx| % \msg_kernel_fatal:nnxxx| % \msg_kernel_fatal:nnxx| % \msg_kernel_fatal:nnx| % \msg_kernel_fatal:nn %} % \begin{syntax} % "\msg_kernel_fatal:nnxx" \marg{division} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Issues kernel error message for , passing % to to the text-creating functions. The \TeX\ run then % halts. Cannot be redirected. %\end{function} % %\begin{function}{ % \msg_kernel_error:nnxxxx| % \msg_kernel_error:nnxxx| % \msg_kernel_error:nnxx| % \msg_kernel_error:nnx| % \msg_kernel_error:nn %} % \begin{syntax} % "\msg_kernel_error:nnxx" \marg{division} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Issues kernel error message for , passing % to to the text-creating functions. Cannot be redirected. %\end{function} % %\begin{function}{ % \msg_kernel_warning:nnxxxx| % \msg_kernel_warning:nnxxx| % \msg_kernel_warning:nnxx| % \msg_kernel_warning:nnx| % \msg_kernel_warning:nn %} % \begin{syntax} % "\msg_kernel_warning:nnxx" \marg{division} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Prints kernel message for to the terminal, passing % to to the text-creating functions. %\end{function} % %\begin{function}{ % \msg_kernel_info:nnxxxx| % \msg_kernel_info:nnxxx| % \msg_kernel_info:nnxx| % \msg_kernel_info:nnx| % \msg_kernel_info:nn %} % \begin{syntax} % "\msg_kernel_info:nxx" \marg{division} \marg{name} \marg{arg one} % ~~~~\marg{arg two} \marg{arg three} \marg{arg four} % \end{syntax} % Prints kernel message for to the log, passing % to to the text-creating functions. %\end{function} % %\begin{function}{\msg_kernel_bug:x} % \begin{syntax} % "\msg_kernel_bug:x" \marg{text} % \end{syntax} % Short-cut for ``This is a LaTeX bug: check coding'' errors. %\end{function} % % \section{Variables and constants} % %\begin{variable}{ % \c_msg_fatal_tl| % \c_msg_error_tl| % \c_msg_warning_tl| % \c_msg_info_tl %} % Simple headers for errors. %\end{variable} % %\begin{variable}{ % \c_msg_fatal_text_tl| % \c_msg_help_text_tl| % \c_msg_kernel_bug_text_tl| % \c_msg_kernel_bug_more_text_tl| % \c_msg_no_info_text_tl| % \c_msg_return_text_tl %} % Various pieces of text for use in messages, which are not changed by % the code here although they could be to alter the language. %\end{variable} % %\begin{variable}{\c_msg_on_line_tl} % The ``on line'' phrase for line numbers. %\end{variable} % %\begin{variable}{ % \c_msg_text_prefix_tl| % \c_msg_more_text_prefix_tl| %} % Header information for storing the ``paths'' to parts of a message. %\end{variable} % %\begin{variable}{ % \l_msg_class_tl| % \l_msg_current_class_tl %} % Information about message method, used for filtering. %\end{variable} % %\begin{variable}{\l_msg_names_clist} % List of all of the message names defined. %\end{variable} % %\begin{variable}{ % \l_msg_redirect_classes_prop| % \l_msg_redirect_names_prop %} % Re-direction lists containing the class of message to convert an % different one. %\end{variable} % %\begin{variable}{\l_msg_redirect_classes_clist} % List so that filtering does not loop. %\end{variable} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3msg} implementation} % % The usual lead-off. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \package_check_loaded_expl: % %<*initex|package> % \end{macrocode} % % \LaTeX\ is handling context, so the \TeX\ ``noise'' is turned down. % \begin{macrocode} \int_set:Nn \tex_errorcontextlines:D { \c_minus_one } % \end{macrocode} % % \subsection{Variables and constants} % %\begin{macro}{\c_msg_fatal_tl} %\begin{macro}{\c_msg_error_tl} %\begin{macro}{\c_msg_warning_tl} %\begin{macro}{\c_msg_info_tl} % Header information. % \begin{macrocode} \tl_new:Nn \c_msg_fatal_tl { Fatal~Error } \tl_new:Nn \c_msg_error_tl { Error } \tl_new:Nn \c_msg_warning_tl { Warning } \tl_new:Nn \c_msg_info_tl { Info } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\c_msg_fatal_text_tl} %\begin{macro}{\c_msg_help_text_tl} %\begin{macro}{\c_msg_kernel_bug_text_tl} %\begin{macro}{\c_msg_kernel_bug_more_text_tl} %\begin{macro}{\c_msg_no_info_text_tl} %\begin{macro}{\c_msg_return_text_tl} % Simple pieces of text for messages. % \begin{macrocode} \tl_new:Nn \c_msg_fatal_text_tl { This~is~a~fatal~error:~LaTeX~will~abort } \tl_new:Nn \c_msg_help_text_tl { For~immediate~help~type~H~ } \tl_new:Nn \c_msg_kernel_bug_text_tl { This~is~a~LaTeX~bug:~check~coding! } \tl_new:Nn \c_msg_kernel_bug_more_text_tl { There~is~a~coding~bug~somewhere~around~here. \msg_newline: This~probably~needs~examining~by~an~expert. \c_msg_return_text_tl } \tl_new:Nn \c_msg_no_info_text_tl { LaTeX~does~not~know~anything~more~about~this~error,~sorry. \c_msg_return_text_tl } \tl_new:Nn \c_msg_return_text_tl { \msg_two_newlines: Try~typing~~to~proceed. \msg_newline: If~that~doesn't~work,~type~X~~to~quit } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}[aux]{\c_msg_hide_tl} % An empty variable with a number of (category code 11) spaces at the % end of its name. This is used to push the \TeX\ part of an error % message ``off the screen''. % % No indentation here as \verb*| | is a letter! % \begin{macrocode} \group_begin: \char_make_letter:N\ % \tl_to_lowercase:n{% \group_end:% \tl_new:Nn% \c_msg_hide_tl % {}% }% % \end{macrocode} %\end{macro} % %\begin{macro}{\c_msg_on_line_tl} % Text for ``on line''. % \begin{macrocode} \tl_new:Nn \c_msg_on_line_tl { on~line } % \end{macrocode} %\end{macro} % %\begin{macro}{\c_msg_text_prefix_tl} %\begin{macro}{\c_msg_more_text_prefix_tl} % Prefixes for storage areas. % \begin{macrocode} \tl_new:Nn \c_msg_text_prefix_tl { msg_text ~>~ } \tl_new:Nn \c_msg_more_text_prefix_tl { msg_text_more ~>~ } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\l_msg_class_tl} %\begin{macro}{\l_msg_current_class_tl} % For holding the current message method and that for redirection. % \begin{macrocode} \tl_new:N \l_msg_class_tl \tl_new:N \l_msg_current_class_tl % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\l_msg_names_clist} % Lists used for filtering. % \begin{macrocode} \clist_new:N \l_msg_names_clist % \end{macrocode} %\end{macro} % %\begin{macro}{\l_msg_redirect_classes_prop} %\begin{macro}{\l_msg_redirect_names_prop} % For filtering messages, a list of all messages and of those which have % to be modified is required. % \begin{macrocode} \prop_new:N \l_msg_redirect_classes_prop \prop_new:N \l_msg_redirect_names_prop % \end{macrocode} %\end{macro} %\end{macro} %\begin{macro}{\l_msg_redirect_classes_clist} % To prevent an infinite loop. % \begin{macrocode} \clist_new:N \l_msg_redirect_classes_clist % \end{macrocode} %\end{macro} % % \subsection{Output helper functions} % %\begin{macro}{\msg_line_number:} %\begin{macro}{\msg_line_context:} % For writing the line number nicely. % \begin{macrocode} \cs_new_nopar:Npn \msg_line_number: { \toks_use:N \tex_inputlineno:D } \cs_new_nopar:Npn \msg_line_context: { \c_msg_on_line_tl \msg_space: \msg_line_number: } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\msg_newline:} %\begin{macro}{\msg_two_newlines:} % Always forces a new line. % \begin{macrocode} \cs_new_nopar:Npn \msg_newline: { ^^J } \cs_new_nopar:Npn \msg_two_newlines: { ^^J ^^J } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\msg_space:} %\begin{macro}{\msg_two_spaces:} %\begin{macro}{\msg_four_spaces:} % For printing spaces, some very simple functions. % \begin{macrocode} \cs_new_nopar:Npn \msg_space: { ~ } \cs_new_nopar:Npn \msg_two_spaces: { \msg_space: \msg_space: } \cs_new_nopar:Npn \msg_four_spaces: { \msg_two_spaces: \msg_two_spaces: } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} % %\subsection{Generic functions} % % The lowest level functions make no assumptions about modules, % \emph{etc.} % %\begin{macro}{\msg_generic_new:nnn} %\begin{macro}{\msg_generic_new:nn} % Creating a new message is basically the same as the non-checking % version, and so after a check everything hands over. % \begin{macrocode} \cs_new_nopar:Npn \msg_generic_new:nnn #1 { \chk_if_free_cs:c { \c_msg_text_prefix_tl #1 :xxxx } \msg_generic_set:nnn {#1} } \cs_new_nopar:Npn \msg_generic_new:nn #1 { \chk_if_free_cs:c { \c_msg_text_prefix_tl #1 :xxxx } \msg_generic_set:nn {#1} } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\msg_generic_set:nnn} %\begin{macro}{\msg_generic_set:nn} %\begin{macro}[aux]{\msg_generic_set_clist:n} %\begin{macro}[aux]{\msg_generic_set_text:n} %\begin{macro}[aux]{\msg_generic_set_more_text:n} % Creating a message is quite simple. There must be a short text part, % while the longer text may or may not be available. % \begin{macrocode} \cs_new_nopar:Npn \msg_generic_set:nnn #1 { \msg_generic_set_clist:n {#1} \char_make_space:N \ \msg_generic_set_more_text:nnn {#1} } \cs_new_nopar:Npn \msg_generic_set:nn #1 { \msg_generic_set_clist:n {#1} \char_make_space:N \ \msg_generic_set_text:nn {#1} } \cs_new_nopar:Npn \msg_generic_set_clist:n #1 { \clist_if_in:NnF \l_msg_names_clist { // #1 / } { \clist_put_right:Nn \l_msg_names_clist { // #1 / } } } \cs_new:Npn \msg_generic_set_text:nn #1#2 { \cs_set:cpn { \c_msg_text_prefix_tl #1 :xxxx } ##1##2##3##4 {#2} \cs_set_eq:cN { \c_msg_more_text_prefix_tl #1 :xxxx } \c_undefined \char_make_ignore:N \ } \cs_new:Npn \msg_generic_set_more_text:nnn #1#2#3 { \cs_set:cpn { \c_msg_text_prefix_tl #1 :xxxx } ##1##2##3##4 {#2} \cs_set:cpn { \c_msg_more_text_prefix_tl #1 :xxxx } ##1##2##3##4 {#3} \char_make_ignore:N \ } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_direct_interrupt:xxxx} %\begin{macro}[aux]{\msg_direct_interrupt:n} % The low-level interruption macro is rather opaque, unfortunately. The % idea here is to create a a message which hides all of \TeX's own % information by filling the output up with spaces. To achieve this, % spaces have to be letters: hence no indentation. The odd % \cs{c_msg_hide_tl} actually does the hiding: it is the % large run of spaces in the name that is important here. The meaning % of |\\| is altered so that the explanation text is a simple run % whilst the initial error has line-continuation shown. % \begin{macrocode} \group_begin: \char_set_lccode:nn {`\&} {`\ } % { \char_set_lccode:w `\} = `\ \scan_stop: \char_make_active:N \& \char_make_letter:N\ % \tl_to_lowercase:n{% \group_end:% \cs_new_protected:Npn\msg_direct_interrupt:xxxx#1#2#3#4{% \group_begin:% \cs_set_eq:NN\\\msg_newline:% \cs_set_eq:NN\ \msg_space:% \msg_direct_interrupt_aux:n{#4}% \cs_set_nopar:Npn\\{\msg_newline:#3}% \tex_errhelp:D\l_msg_tmp_tl% \cs_set:Npn&{% \tex_errmessage:D{% #1\msg_newline:% #2\msg_two_newlines:% \c_msg_help_text_tl% \c_msg_hide_tl % }% }% &% \group_end:% }% }% \cs_new:Npn \msg_direct_interrupt_aux:n #1 { \tl_if_empty:nTF {#1} { \tl_set:Nx \l_msg_tmp_tl { { \c_msg_no_info_text_tl } } }{ \tl_set:Nx \l_msg_tmp_tl { {#1} } } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}{\msg_direct_log:xx} %\begin{macro}{\msg_direct_term:xx} % Printing to the log or terminal without a stop is rather easier. % \begin{macrocode} \cs_new_protected:Npn \msg_direct_log:xx #1#2 { \group_begin: \cs_set:Npn \\ { \msg_newline: #2 } \cs_set_eq:NN \ \msg_space: \iow_log:x { #1 \msg_newline: } \group_end: } \cs_new_protected:Npn \msg_direct_term:xx #1#2 { \group_begin: \cs_set:Npn \\ { \msg_newline: #2 } \cs_set_eq:NN \ \msg_space: \iow_term:x { #1 \msg_newline: } \group_end: } % \end{macrocode} %\end{macro} %\end{macro} % %\subsection{General functions} % % The main functions for messaging are built around the separation of % module from the message name. These have short names as they will be % widely used. % %\begin{macro}{\msg_new:nnnn} %\begin{macro}{\msg_new:nnn} %\begin{macro}{\msg_set:nnnn} %\begin{macro}{\msg_set:nnn} % For making messages: all aliases. % \begin{macrocode} \cs_new_nopar:Npn \msg_new:nnnn #1#2 { \msg_generic_new:nnn { #1 / #2 } } \cs_new_nopar:Npn \msg_new:nnn #1#2 { \msg_generic_new:nn { #1 / #2 } } \cs_new_nopar:Npn \msg_set:nnnn #1#2 { \msg_generic_set:nnn { #1 / #2 } } \cs_new_nopar:Npn \msg_set:nnn #1#2 { \msg_generic_set:nn { #1 / #2 } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_class_new:nn} %\begin{macro}{\msg_class_set:nn} % Creating a new class produces three new functions, with varying % numbers of arguments. The \cs{msg_class_loop:n} function is set up % so that redirection will work as desired. % \begin{macrocode} \cs_new_nopar:Npn \msg_class_new:nn #1 { \chk_if_free_cs:c { msg_ #1 :nnxxxx } \prop_new:c { l_msg_redirect_ #1 _prop } \msg_class_set:nn {#1} } \cs_new_nopar:Npn \msg_class_set:nn #1#2 { \prop_clear:c { l_msg_redirect_ #1 _prop } \cs_set_protected:cpn { msg_ #1 :nnxxxx } ##1##2##3##4##5##6 { \msg_use:nnnnxxxx {#1} {#2} {##1} {##2} {##3} {##4} {##5} {##6} } \cs_set_protected:cpx { msg_ #1 :nnxxx } ##1##2##3##4##5 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} {##4} {##5} { } } \cs_set_protected:cpx { msg_ #1 :nnxx } ##1##2##3##4 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} {##4} { } { } } \cs_set_protected:cpx { msg_ #1 :nnx } ##1##2##3 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} { } { } { } } \cs_set_protected:cpx { msg_ #1 :nn } ##1##2 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} { } { } { } { } } } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}[aux]{\msg_use:nnnnxxxx} % The main message-using macro creates two auxiliary functions: one % containing the code for the message, and the second a loop function. % There is then a hand-off to the system for checking if redirection is % needed. % \begin{macrocode} \cs_new:Npn \msg_use:nnnnxxxx #1#2#3#4#5#6#7#8 { \cs_set_nopar:Npn \msg_use_code: { \clist_clear:N \l_msg_redirect_classes_clist #2 } \cs_set:Npn \msg_use_loop:n ##1 { \clist_if_in:NnTF \l_msg_redirect_classes_clist {#1} { \msg_kernel_error:nn { msg } { redirect-loop } {#1} }{ \clist_put_right:Nn \l_msg_redirect_classes_clist {#1} \cs_if_exist:cTF { msg_ ##1 :nnxxxx } { \use:c { msg_ ##1 :nnxxxx } {#3} {#4} {#5} {#6} {#7} {#8} }{ \msg_kernel_error:nnx { msg } { message-class-unknown } {##1} } } } \cs_if_exist:cTF { \c_msg_text_prefix_tl #3 / #4 :xxxx } { \msg_use_aux:nnn {#1} {#3} {#4} }{ \msg_kernel_error:nnxx { msg } { message-unknown } {#3} {#4} } } % \end{macrocode} %\end{macro} % %\begin{macro}[aux]{\msg_use_code:} %\begin{macro}[aux]{\msg_use_loop:} % Blank definitions are initially created for these functions. % \begin{macrocode} \cs_new_nopar:Npn \msg_use_code: { } \cs_new:Npn \msg_use_loop:n #1 { } % \end{macrocode} %\end{macro} %\end{macro} % %\begin{macro}[aux]{\msg_use_aux:nnn} % The first auxiliary macro looks for a match by name: the most % restrictive check. % \begin{macrocode} \cs_new_nopar:Npn \msg_use_aux:nnn #1#2#3 { \tl_set:Nn \l_msg_current_class_tl {#1} \tl_set:Nn \l_msg_current_module_tl {#2} \prop_if_in:NnTF \l_msg_redirect_names_prop { // #2 / #3 / } { \msg_use_loop_check:nn { names } { // #2 / #3 / } }{ \msg_use_aux:nn {#1} {#2} } } % \end{macrocode} %\end{macro} %\begin{macro}[aux]{\msg_use_aux:nn} % The second function checks for general matches by module or for % all modules. % \begin{macrocode} \cs_new_nopar:Npn \msg_use_aux:nn #1#2 { \prop_if_in:cnTF { l_msg_redirect_ #1 _prop } {#2} { \msg_use_loop_check:nn {#1} {#2} }{ \prop_if_in:cnTF { l_msg_redirect_ #1 _prop } { * } { \msg_use_loop_check:nn {#1} { * } }{ \msg_use_code: } } } % \end{macrocode} %\end{macro} %\begin{macro}[aux]{\msg_use_loop_check:nn} % When checking whether to loop, the same code is needed in a few % places. % \begin{macrocode} \cs_new:Npn \msg_use_loop_check:nn #1#2 { \prop_get:cnN { l_msg_redirect_ #1 _prop } {#2} \l_msg_class_tl \tl_if_eq:NNTF \l_msg_current_class_tl \l_msg_class_tl { \msg_use_code: }{ \msg_use_loop:n { \l_msg_class_tl } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\msg_fatal:nnxxxx} %\begin{macro}{\msg_fatal:nnxxx} %\begin{macro}{\msg_fatal:nnxx} %\begin{macro}{\msg_fatal:nnx} %\begin{macro}{\msg_fatal:nn} % For fatal errors, after the error message \TeX\ bails out. % \begin{macrocode} \msg_class_new:nn { fatal } { \msg_direct_interrupt:xxxx { \c_msg_fatal_tl \msg_two_newlines: } { ( \c_msg_fatal_tl ) \msg_space: \use:c { \c_msg_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { ( \c_msg_fatal_tl ) \msg_space: } { \c_msg_fatal_text_tl } \tex_end:D } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_error:nnxxxx} %\begin{macro}{\msg_error:nnxxx} %\begin{macro}{\msg_error:nnxx} %\begin{macro}{\msg_error:nnx} %\begin{macro}{\msg_error:nn} % For an error, the interrupt routine is called, then any recovery code % is tried. % \begin{macrocode} \msg_class_new:nn { error } { \msg_direct_interrupt:xxxx { #1~\c_msg_error_tl \msg_newline: } { ( #1 ) \msg_space: \use:c { \c_msg_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { ( #1 ) \msg_space: } { \cs_if_exist:cTF { \c_msg_more_text_prefix_tl #1 / #2 :xxxx } { \use:c { \c_msg_more_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { \c_msg_no_info_text_tl } } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_warning:nnxxxx} %\begin{macro}{\msg_warning:nnxxx} %\begin{macro}{\msg_warning:nnxx} %\begin{macro}{\msg_warning:nnx} %\begin{macro}{\msg_warning:nn} % Warnings are printed to the terminal. % \begin{macrocode} \msg_class_new:nn { warning } { \msg_direct_term:xx { \msg_space: #1 ~ \c_msg_warning_tl :~ \use:c { \c_msg_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { ( #1 ) \msg_two_spaces: } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_info:nnxxxx} %\begin{macro}{\msg_info:nnxxx} %\begin{macro}{\msg_info:nnxx} %\begin{macro}{\msg_info:nnx} %\begin{macro}{\msg_info:nn} % Information only goes into the log. % \begin{macrocode} \msg_class_new:nn { info } { \msg_direct_log:xx { \msg_space: #1~\c_msg_info_tl :~ \use:c { \c_msg_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { ( #1 ) \msg_two_spaces: } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_log:nnxxxx} %\begin{macro}{\msg_log:nnxxx} %\begin{macro}{\msg_log:nnxx} %\begin{macro}{\msg_log:nnx} %\begin{macro}{\msg_log:nn} % ``Log'' data is very similar to information, but with no extras % added. % \begin{macrocode} \msg_class_new:nn { log } { \msg_direct_log:xx { \use:c { \c_msg_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_trace:nnxxxx} %\begin{macro}{\msg_trace:nnxxx} %\begin{macro}{\msg_trace:nnxx} %\begin{macro}{\msg_trace:nnx} %\begin{macro}{\msg_trace:nn} % Trace data is the same as log data, more or less % \begin{macrocode} \msg_class_new:nn { trace } { \msg_direct_log:xx { \use:c { \c_msg_text_prefix_tl #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_none:nnxxxx} %\begin{macro}{\msg_none:nnxxx} %\begin{macro}{\msg_none:nnxx} %\begin{macro}{\msg_none:nnx} %\begin{macro}{\msg_none:nn} % The \texttt{none} message type is needed so that input can be gobbled. % \begin{macrocode} \msg_class_new:nn { none } { } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\subsection{Redirection functions} % %\begin{macro}{\msg_redirect_class:nn} % Converts class one into class two. % \begin{macrocode} \cs_new_nopar:Npn \msg_redirect_class:nn #1#2 { \prop_put:cnn { l_msg_redirect_ #1 _prop } { * } {#2} } % \end{macrocode} %\end{macro} % %\begin{macro}{\msg_redirect_module:nnn} % For when all messages of a class should be altered for a given module. % \begin{macrocode} \cs_new_nopar:Npn \msg_redirect_module:nnn #1#2#3 { \prop_put:cnn { l_msg_redirect_ #2 _prop } {#1} {#3} } % \end{macrocode} %\end{macro} % %\begin{macro}{\msg_redirect_name:nnn} % Named message will always use the given class. % \begin{macrocode} \cs_new_nopar:Npn \msg_redirect_name:nnn #1#2#3 { \prop_put:Nnn \l_msg_redirect_names_prop { // #1 / #2 / } {#3} } % \end{macrocode} %\end{macro} % %\subsection{Kernel-specific functions} % %\begin{macro}{\msg_kernel_new:nnnn} %\begin{macro}{\msg_kernel_new:nnn} %\begin{macro}{\msg_kernel_set:nnnn} %\begin{macro}{\msg_kernel_set:nnn} % The kernel needs some messages of its own. These are created using % pre-built functions. Two functions are provided: one more general % and one which only has the short text part. % \begin{macrocode} \cs_new_nopar:Npn \msg_kernel_new:nnnn #1#2 { \msg_new:nnnn { LaTeX } { #1 / #2 } } \cs_new_nopar:Npn \msg_kernel_new:nnn #1#2 { \msg_new:nnn { LaTeX } { #1 / #2 } } \cs_new_nopar:Npn \msg_kernel_set:nnnn #1#2 { \msg_set:nnnn { LaTeX } { #1 / #2 } } \cs_new_nopar:Npn \msg_kernel_set:nnn #1#2 { \msg_set:nnn { LaTeX } { #1 / #2 } } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}[aux]{\msg_kernel_classes_new:n} % Quickly make the fewer-arguments versions. % \begin{macrocode} \cs_new_nopar:Npn \msg_kernel_classes_new:n #1 { \cs_new_protected:cpx { msg_kernel_ #1 :nnxxx } ##1##2##3##4##5 { \exp_not:c { msg_kernel_ #1 :nnxxxx } {##1} {##2} {##3} {##4} {##5} { } } \cs_new_protected:cpx { msg_kernel_ #1 :nnxx } ##1##2##3##4 { \exp_not:c { msg_kernel_ #1 :nnxxxx } {##1} {##2} {##3} {##4} { } { } } \cs_new_protected:cpx { msg_kernel_ #1 :nnx } ##1##2##3 { \exp_not:c { msg_kernel_ #1 :nnxxxx } {##1} {##2} {##3} { } { } { } } \cs_new_protected:cpx { msg_kernel_ #1 :nn } ##1##2 { \exp_not:c { msg_kernel_ #1 :nnxxxx } {##1} {##2} { } { } { } { } } } % \end{macrocode} %\end{macro} % %\begin{macro}{\msg_kernel_fatal:nnxxxx} %\begin{macro}{\msg_kernel_fatal:nnxxx} %\begin{macro}{\msg_kernel_fatal:nnxx} %\begin{macro}{\msg_kernel_fatal:nnx} %\begin{macro}{\msg_kernel_fatal:nn} % Fatal kernel errors cannot be re-defined. % \begin{macrocode} \cs_new_protected:Npn \msg_kernel_fatal:nnxxxx #1#2#3#4#5#6 { \msg_direct_interrupt:xxxx { \c_msg_fatal_tl \msg_two_newlines: } { ( LaTeX ) \msg_space: \use:c { \c_msg_text_prefix_tl LaTeX / #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { ( LaTeX ) \msg_space: } { \c_msg_fatal_text_tl } \tex_end:D } \msg_kernel_classes_new:n { fatal } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_kernel_error:nnxxxx} %\begin{macro}{\msg_kernel_error:nnxxx} %\begin{macro}{\msg_kernel_error:nnxx} %\begin{macro}{\msg_kernel_error:nnx} %\begin{macro}{\msg_kernel_error:nn} % Neither can kernel errors. % \begin{macrocode} \cs_new_protected:Npn \msg_kernel_error:nnxxxx #1#2#3#4#5#6 { \msg_direct_interrupt:xxxx { LaTeX~\c_msg_error_tl \msg_newline: } { ( LaTeX ) \msg_space: \use:c { \c_msg_text_prefix_tl LaTeX / #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { ( LaTeX ) \msg_space: } { \cs_if_exist:cTF { \c_msg_more_text_prefix_tl LaTeX / #1 / #2 :xxxx } { \use:c { \c_msg_more_text_prefix_tl LaTeX / #1 / #2 :xxxx } {#3} {#4} {#5} {#6} } { \c_msg_no_info_text_tl } } } \msg_kernel_classes_new:n { error } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % %\begin{macro}{\msg_kernel_warning:nnxxxx} %\begin{macro}{\msg_kernel_warning:nnxxx} %\begin{macro}{\msg_kernel_warning:nnxx} %\begin{macro}{\msg_kernel_warning:nnx} %\begin{macro}{\msg_kernel_warning:nn} %\begin{macro}{\msg_kernel_info:nnxxxx} %\begin{macro}{\msg_kernel_info:nnxxx} %\begin{macro}{\msg_kernel_info:nnxx} %\begin{macro}{\msg_kernel_info:nnx} %\begin{macro}{\msg_kernel_info:nn} % Life is much more simple for warnings and information messages, as % these are just short-cuts to the standard classes. % \begin{macrocode} \cs_new_protected_nopar:Npn \msg_kernel_warning:nnxxxx #1#2 { \msg_warning:nnxxxx { LaTeX } { #1 / #2 } } \msg_kernel_classes_new:n { warning } \cs_new_protected_nopar:Npn \msg_kernel_info:nnxxxx #1#2 { \msg_info:nnxxxx { LaTeX } { #1 / #2 } } \msg_kernel_classes_new:n { info } % \end{macrocode} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} %\end{macro} % % Error messages needed to actually implement the message system % itself. % \begin{macrocode} \msg_kernel_new:nnnn { msg } { message-unknown } {Unknown message `#2' for module `#1'.} {% LaTeX was asked to display a message by the `#1' module.\\% The message was supposed to be called `#2', but I can't\\% find a message with that name.% \c_msg_return_text_tl } \msg_kernel_new:nnnn { msg } { message-class-unknown } {Unknown message class `#1'.} {% You have asked for a message to be redirected to class `#1'.\\% The message class `#1' has not been set up:\\% perhaps this is a typing error.% \c_msg_return_text_tl } \msg_kernel_new:nnnn { msg } { redirect-loop } {Message redirection loop for message class `#1'.} {% You have asked for a message to be redirected,\\% but the redirection instructions form a loop:\\% you've lost the message.% \c_msg_return_text_tl } % \end{macrocode} % %\begin{macro}{\msg_kernel_bug:x} % The \LaTeX\ coding bug error gets re-visited here. % \begin{macrocode} \cs_set_protected:Npn \msg_kernel_bug:x #1 { \msg_direct_interrupt:xxxx { \c_msg_kernel_bug_text_tl } { !~#1 } { ! } { \c_msg_kernel_bug_more_text_tl } } % \end{macrocode} %\end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex