控制由 \NewDocumentCommand 定義的命令所發出的錯誤訊息

控制由 \NewDocumentCommand 定義的命令所發出的錯誤訊息

\NewDocumentCommand命令使用了一個巧妙的技巧來產生更好的錯誤訊息 - 如果 的參數\mycommand混亂,那麼所產生的錯誤將是類似於“文件在掃描使用時結束\__xparse_grab_D:w”之類的內容,因此每當xparse 想要獲取另一個參數,它定義\mycommand<space>進行參數抓取,然後使用它來完成工作,這樣如果出現問題,錯誤就會類似於「段落在\mycommand<space>完成之前結束」。

我想要這樣的命令\NewDocumentCommandAs需要兩個參數——第一個是要定義的巨集的名稱,第二個是要在錯誤訊息中使用的命令的名稱。

我有兩個用例。一是我有一個套件定義了僅在特定環境中使用的命令。因此,它在套件的私有命名空間中定義了命令,並且\let是環境啟動時私有版本的命令的公共名稱。顯然,我想將所有錯誤訊息都基於命令的公共名稱,所以我想說類似的話

\NewDocumentCommandAs \__my_pkg_cmd:args \cmd {<args>}{<body>}

第二個用例是,我有一個帶有可選參數的命令,該參數是第一個左括號之前的所有內容,然後是一個由從第一個左括號開始的平衡括號分隔的參數。因為「until」參數類型吸收了它正在掃描的標記,所以我需要重新插入左括號並使用輔助:

\NewDocumentCommand \mycommand { o u( } { \mycommand_aux{#1}{#2}( }
\NewDocumentCommandAs \mycommand_aux \mycommand { m m r() } { 
    % do stuff here
}

當然,這可以透過製作一個版本的 u(也許是 U?)來重新插入它刪除的令牌來解決,但這可能是一個不尋常的用例。

出於格式化目的,我想我會提交我的兩個實作作為答案。有其他人處理過這個問題嗎?我缺少一些更好的方法嗎?我不應該這樣做並以不同的方式解決這些問題嗎?此功能是否可能添加到 xparse 的未來版本?

答案1

我的第一次嘗試是:

\cs_new_protected:Npn \NewDocumentCommandAs#1#2#3#4{
    \group_begin:
    % Store the original value of #2
    \cs_set_eq:cN { tempsave_ \cs_to_str:N #2 } #2
    \cs_set_eq:cc { tempsave_ \cs_to_str:N #2 \c_space_tl code } 
                  { \cs_to_str:N #2 \c_space_tl code }
    % Use \DeclareDocumentCommand with #2 so that error messages work
    \DeclareDocumentCommand#2{#3}{\group_end: #4}
    % Define #1 to be a wrapper command that sets #2<space>code equal to #1<space>code
    \cs_new:Npx #1{
        \group_begin:
        \exp_not:N \cs_set_eq:NN
            \exp_not:c { \cs_to_str:N #2 \c_space_tl code }
            \exp_not:c { \cs_to_str:N #1 \c_space_tl code }
        \exp_not:c { \cs_to_str:N #1 \c_space_tl inner }
    }
    % Save the value of #2 set by DeclareDocumentCommand
    \cs_gset_eq:cN { \cs_to_str:N #1 \c_space_tl inner } #2
    \cs_gset_eq:cc{ \cs_to_str:N #1 \c_space_tl code } { \cs_to_str:N #2 \c_space_tl code }
    % Restore the original value of #2
    \cs_gset_eq:Nc #2 { tempsave_ \cs_to_str:N #2 }
    \cs_gset_eq:cc { \cs_to_str:N #2 \c_space_tl code } { tempsave_ \cs_to_str:N #2 \c_space_tl code }
    \group_end:
}

這有點混亂,但工作正常。這樣做的最大好處是,它不依賴\NewDocumentCommand儲存巨集的內部細節之外的實現#1<space>code,並且不使用其他輔助命令。一個限制是,如果所有參數都是“m”類型,它將不起作用,但這並不困擾我。此方法也不適用於可擴充巨集。

另一種可能的方法是製作修補版本,\__xparse_declare_cmd_mixed_aux:Nn該版本會對錯誤命令(儲存在 中)使用不同的名稱\l__xparse_fn_tl,並暫時安裝定義以便使用\NewDocumentCommand。這樣做的優點是您可以執行類似的操作\__xparse_declare_cmd_mixed_expandable:Nn並獲得可擴展版本的工作。即使所有參數都是強制性的,它也可以工作。缺點是它取決於 的具體實現\__xparse_declare_cmd_mixed_aux:Nn

% Make two copies of \__xparse_declare_cmd_aux:Nn, one to patch, one as a save
% If \l_..._environment_bool is true, it forces xparse to use argument grabbers even when
% all arguments are mandatory. So we'll set it to be true for \NewDocumentCommandAs
\cs_new_eq:NN \__my_xparse_declare_cmd_aux_as:Nnn \__xparse_declare_cmd_aux:Nnn
\cs_new_eq:NN \__my_xparse_declare_cmd_aux_save:Nnn \__xparse_declare_cmd_aux:Nnn
\patchcmd \__my_xparse_declare_cmd_aux_as:Nnn { \bool_set_false:N \l__xparse_environment_bool } { \bool_set_true:N \l__xparse_environment_bool }{}{\oops}

\cs_new_eq:NN \__my_xparse_declare_cmd_mixed_aux_as:Nn   \__xparse_declare_cmd_mixed_aux:Nn
\cs_new_eq:NN \__my_xparse_declare_cmd_mixed_aux_save:Nn \__xparse_declare_cmd_mixed_aux:Nn

% Replace \l__xparse_function_tl with my own token list that I can set independently
\tl_new:N \l__my_xparse_function_as_tl
\patchcmd \__my_xparse_declare_as_cmd_mixed_aux:Nn 
    {{ \l__xparse_function_tl \c_space_tl }} 
    {{ \l__my_xparse_function_as_tl \c_space_tl }}{}{\oops}

\cs_new:Npn \NewDocumentCommandAs #1#2#3#4 {
    % Patch in my modified version of \__xparse_declare_cmd_mixed_aux
    \cs_set_eq:NN \__xparse_declare_cmd_mixed_aux:Nn \__my_xparse_declare_cmd_mixed_aux_as:Nn
    \tl_set:Nn \l__my_xparse_function_as_tl { \cs_to_str:N #2 } % Use #2 as the error name
    \NewDocumentCommand#1{#3}{#4}
    \cs_set_eq:NN \__xparse_declare_cmd_mixed_aux:Nn \__my_xparse_declare_as_cmd_mixed_aux_save:Nn
}

相關內容