
Редактировать 2:Внизу я обновил вопрос, включив ответ отсюда:newcommand для индекса вставляет нежелательные пробелы при использовании {}
Я создал функцию для отображения команды жирным шрифтом с возможностью указания раздела страницы руководства в скобках.
\usepackage{xparse}
\newcommand*{\man}[2][]{%
\textbf{#2}\IfNoValueF{#1}{(#1)}%
}
Теперь я также хочу иметь возможность создать индексную запись для этих команд. Поэтому я создал команду-обертку:
\newcommand*{\mani}[2][]{%
\man[#1]{#2}%
\index{#2@\man[#1]{#2}}%
}
Однако иногда мне хочется создать запись индекса, используя то же форматирование, но не печатать команду в текущем тексте. Вот MWE:
\documentclass{memoir}
\usepackage{xparse}
\newcommand*{\man}[2][]{%
\textbf{#2}\IfNoValueF{#1}{(#1)}%
}
\newcommand*{\mani}[2][]{%
\man[#1]{#2}%
\index{#2@\man[#1]{#2}}%
}
\makeindex
\begin{document}
Only an index entry: \index{ssh-keygen@\man[1]{ssh-keygen}}
Hello world
Command and index entry: \mani[1]{ssh-keygen}
\printindex
\end{document}
Файл output.idx
содержит две строки, вторая содержит дополнительный пробел. Таким образом, вопрос в том, почему там есть дополнительный пробел и как это исправить, чтобы не было дополнительного пробела?
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
\indexentry{ssh-keygen@\man [1]{ssh-keygen}}{1}
Редактировать 1:Я добавил снимок экрана с результатом этого дополнительного пространства: печатаются две идентичные записи индекса.
Обновлять
Я обновил свой код в соответствии с предоставленным решением, но теперь получаю сообщение об ошибке, которое я не понимаю.
\documentclass{memoir}
\usepackage{xparse}
\makeindex
\NewDocumentCommand\man{om}{%
\textbf{#2}\IfNoValueF{#1}{(#1)}%
}
\makeatletter
\newcommand*{\mani}{%
\@bsphack
\begingroup
\@sanitize
\@mani
}
\newcommand*{\@mani}[2][]{%
\man[#1]{#2}%
\@wrindex{#2@\string\man[#1]{#2}}%
}
\makeatletter
\begin{document}
Two separate commands: \man[1]{ssh-keygen}\index{ssh-keygen@\man[1]{ssh-keygen}}
Hello world
One command: \mani[1]{ssh-keygen}
\printindex
\end{document}
OverLeaf утверждает:
Компилятор не может понять команду, которую вы использовали. Проверьте правильность написания команды. Если команда является частью пакета, убедитесь, что вы включили пакет в преамбулу с помощью \usepackage{...}.
В файле журнала сообщается:
! Missing number, treated as zero.
<to be read again>
{
l.25 One command: \mani[1]{ssh-keygen}
A number should have been here; I inserted `0'.
(If you can't figure out why I needed to see a number,
look up `weird error' in the index to The TeXbook.)
решение1
Команда \index
должна получить свой аргумент путем чтения и токенизации элементов из файла .tex-input в разделе \@sanitize
-category-code-régime. ( \@sanitize
-category-code-régime означает: символ пробела, \
, $
, &
, #
, ^
, и имеет код категории 12 (другое). _
) Это происходит по нескольким причинам, например,%
~
- предотвращение нежелательного расширения расширяемых токенов.
- избегание добавления символа пробела при неразвернутой записи токена управляющего слова во внешний файл.
Но с вашей командой \mani
команда \index
получает свой аргумент, переданный из \mani
. Когда аргумент \mani
gathers/composes \index
, токены, формирующие этот аргумент, не токенизируются в соответствии с \@sanitize
-category-code-régime, а токенизируются в соответствии с обычным category-code-régime.
Под другими вещами токенизация в обычном режиме category-code подразумевает, что фразы типа \man
токенизируются как токены-управляющих-слов, а не как последовательности-символов \
, m
, a
, n
. Когда токены-управляющих-слов записываются неразвернутыми в текстовый-файл, например, некоторый .idx
-файл, принадлежащий процессу создания индекса, будет добавлен символ-пробел. Т.е. будет записана последовательность-символов \
, m
, a
, n
, .⟨space character⟩
В определении \mani
вы можете применить \string
команду \man
, чтобы превратить ее в последовательность символов-токенов. (Здесь полагается только один входной символ, имеющий код категории 0 (escape), и значение целочисленного параметра, \escapechar
равное номеру кодовой точки этого символа во внутренней схеме кодировки символов движка TeX. Обычно символ обратной косой черты \
является единственным символом с кодом категории 0 (escape) и обычно \escapechar
имеет значение 92, которое является номером кодовой точки символа обратной косой черты во внутренней схеме кодировки символов движка TeX.)
\documentclass{memoir}
\usepackage{xparse}
\NewDocumentCommand{\man}{om}{%
\textbf{#2}\IfNoValueF{#1}{(#1)}%
}
\NewDocumentCommand{\mani}{om}{%
\IfNoValueTF{#1}{%
\man{#2}%
\index{#2@\string\man{#2}}%
}{%
\man[#1]{#2}%
\index{#2@\string\man[#1]{#2}}%
}%
}%
\makeindex
\begin{document}
Only an index entry: \index{ssh-keygen@\man[1]{ssh-keygen}}
Hello world
Command and index entry: \mani[1]{ssh-keygen}
Only an index entry: \index{ssh-keygen-no-optional-argument@\man{ssh-keygen-no-optional-argument}}
Hello world
Command and index entry: \mani{ssh-keygen-no-optional-argument}
\printindex
\end{document}
В приведенном выше примере результирующий .idx-файл выглядит следующим образом:
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
\indexentry{ssh-keygen-no-optional-argument@\man{ssh-keygen-no-optional-argument}}{1}
\indexentry{ssh-keygen-no-optional-argument@\man{ssh-keygen-no-optional-argument}}{1}
При таком подходе единственное, что «строковывается» в аргументе \index
, который предоставляется через макрос \mani
, — это фраза \man
.
Вещи, которые приходят из \mani
первого или второго аргумента, не преобразуются в строку. В случае, если наборы токенов, предоставленные через эти аргументы, также содержат токены-управления-словами, вы также можете получить здесь нежелательные пробелы.
Я могу предложить процедуру \StringifyNAct
, которая применяется \string
к каждому токену в его аргументе:
\StringifyNAct{⟨action⟩}{⟨token 1⟩⟨token 2⟩...⟨token n⟩}
урожайность:
⟨action⟩{⟨stringification of token 1⟩}%
⟨action⟩{⟨stringification of token 2⟩}%
...
⟨action⟩{⟨stringification of token n⟩}%
где «стрингификация токена» означает результат применения \string
к рассматриваемому токену.
Благодаря \romannumeral
-расширению результат доставляется путем запуска двух шагов расширения (например, через две \expandafter
-цепочки).
Я предлагаю команде \mani
считывать и токенизировать свои аргументы в обычном режиме кодов категорий, но с символом пробела (и, вероятно, также с символом горизонтальной табуляции, адресуемым как ^^I
с помощью -нотации TeX ^^
) с кодом категории 12 (другие), затем применять ее \StringifyNAct
к аргументам, затем передавать результат в -команду \index
и — вложенным в \scantokens
— \man
в -команду:
\documentclass{memoir}
\makeatletter
%%========================Code for \StringifyNAct==============================
%%
%% Copyright (C) 2019, 2020 by Ulrich Diez ([email protected])
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo,
%% \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%% \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%% \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
\romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
\romannumeral0%
\UD@ExtractFirstArgLoop{#1UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{ #1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% In case an argument's first token is an opening brace, stringify that and
%% add another opening brace before that and remove everything behind the
%% matching closing brace:
%% \UD@StringifyOpeningBrace{{Foo}bar} yields {{Foo} whereby the second
%% opening brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyOpeningBrace[1]{%
\romannumeral0%
\expandafter\UD@ExtractFirstArgLoop\expandafter{%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\expandafter
\expandafter {%
\expandafter\UD@firstoftwo
\expandafter{%
\expandafter}%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\string
\expandafter}%
\string#1%
UD@SelDOm}%
}%
%%-----------------------------------------------------------------------------
%% In case an argument's first token is an opening brace, remove everything till
%% finding the corresponding closing brace. Then stringify that closing brace:
%% \UD@StringifyClosingBrace{{Foo}bar} yields: {}bar} whereby the first closing
%% brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyClosingBrace[1]{%
\romannumeral0\expandafter\expandafter\expandafter
\UD@StringifyClosingBraceloop
\UD@ExtractFirstArg{#1}{#1}%
}%
\newcommand\UD@CheckWhetherStringifiedOpenBraceIsSpace[1]{%
%% This can happen when character 32 (space) has catcode 1...
\expandafter\UD@CheckWhetherLeadingSpace\expandafter{%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@secondoftwo
\expandafter{%
\expandafter}%
\expandafter{%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\UD@firstoftwo
\expandafter{%
\expandafter}%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\string
\expandafter}%
\string#1%
}%
}%
\newcommand\UD@TerminateStringifyClosingBraceloop[2]{%
\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter\expandafter
\expandafter{%
\expandafter\string
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter#1%
\string#2%
}%
}%
\newcommand\UD@StringifyClosingBraceloopRemoveElement[4]{%
\expandafter\UD@PassFirstToSecond\expandafter{\expandafter
{\romannumeral0\expandafter\UD@secondoftwo\string}{}%
\UD@CheckWhetherStringifiedOpenBraceIsSpace{#4}{%
\UD@Exchange{\UD@removespace}%
}{%
\UD@Exchange{\UD@firstoftwo\expandafter{\expandafter}}%
}{%
\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter#1%
\romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
\expandafter
}%
\string#4%
}{\expandafter\UD@StringifyClosingBraceloop\expandafter{#2#3}}%
}%
\newcommand\UD@StringifyClosingBraceloop[2]{%
\UD@CheckWhetherNull{#1}{%
\UD@CheckWhetherStringifiedOpenBraceIsSpace{#2}{%
\UD@TerminateStringifyClosingBraceloop{\UD@removespace}%
}{%
\UD@TerminateStringifyClosingBraceloop{\UD@firstoftwo\expandafter{\expandafter}}%
}%
{#2}%
}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\UD@StringifyClosingBraceloopRemoveElement
{\UD@removespace}{\UD@removespace}%
}{%
\UD@StringifyClosingBraceloopRemoveElement
{\UD@firstoftwo\expandafter{\expandafter}}{\UD@firstoftwo{}}%
}%
{#1}{#2}%
}%
}%
%%-----------------------------------------------------------------------------
%% Apply <action> to the stringification of each token of the argument:
%%
%% \StringifyNAct{<action>}{<token 1><token 2>...<token n>}
%%
%% yields: <action>{<stringification of token 1>}%
%% <action>{<stringification of token 2>}%
%% ...
%% <action>{<stringification of token n>}%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%% Due to \romannumeral-expansion the result is delivered after two
%% \expandafter-chains.
%% If you leave <action> empty, you can apply a loop on the list formed by
%% {<stringification of token 1>}%
%% {<stringification of token 2>}%
%% ...
%% {<stringification of token n>}%
%%
%% Below a macro \ConcatenateStringifiedtokens is implemented which loops
%% on that list for concatenating.
%%.............................................................................
\newcommand\StringifyNAct{%
\romannumeral0\StringifyNActLoop{}%
}%
%%.............................................................................
%% \StringifyNActLoop{{<stringification of token 1>}...{<stringification of token k-1>}}%
%% {<action>}%
%% {<token k>...<token n>}
%%.............................................................................
\newcommand\StringifyNActLoop[3]{%
\UD@CheckWhetherNull{#3}{%
\UD@firstoftwo{ }{}#1%
}{%
\UD@CheckWhetherBrace{#3}{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\UD@StringifyClosingBrace{#3}%
}{%
\expandafter\StringifyNActLoop\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{\UD@StringifyOpeningBrace{#3}}{\StringifyNActLoop{#1}{#2}}%
}{#2}%
}%
}{%
\UD@CheckWhetherLeadingSpace{#3}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#3}{%
\StringifyNActLoop{#1#2{ }}{#2}%
}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
\expandafter\StringifyNActLoop\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter\string
\expandafter\UD@Exchange
\romannumeral0\UD@ExtractFirstArgLoop{#3UD@SelDOm}{}%
}{ #1#2}%
}%
{#2}%
}%
}%
}%
}%
}%
%% The promised loop for concatenating stringified tokens - apply as:
%%
%% \romannumeral0%
%% \expandafter\expandafter\expandafter
%% \ConcatenateStringifiedtokens
%% \StringifyNAct{}{<tokens to stringify>}\relax
%%
\newcommand*\ConcatenateStringifiedtokens{%
\ConcatenateStringifiedtokensloop{ }%
}%
\newcommand\ConcatenateStringifiedtokensloop[2]{%
\ifx\relax#2\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{#1}{%
\ConcatenateStringifiedtokensloop{#1#2}%
}%
}%
%%=================== End of code for \StringifyNAct ==========================
\makeatother
\usepackage{xparse}
\makeatletter
\NewDocumentCommand{\man}{om}{%
\UD@CheckWhetherNull{#2}{}{%
\toks@{#2}%
\textbf{\the\toks@}%
}%
\IfNoValueF{#1}{(#1)}%
}%
\NewDocumentCommand{\mani}{}{%
\begingroup
\catcode`\ =12\relax
\catcode`\^^I=12\relax
\maniinner
}%
\NewDocumentCommand{\maniinner}{om}{%
\endgroup
\IfNoValueTF{#1}{%
\expandafter\maniinnerinner\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter
\ConcatenateStringifiedtokens
\StringifyNAct{}{#2}\relax
}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter
\ConcatenateStringifiedtokens
\StringifyNAct{}{#2}\relax
}{%
\expandafter\maniinnerinner\expandafter[\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter
\ConcatenateStringifiedtokens
\StringifyNAct{}{#1}\relax
}]%
}%
}%
}%
\makeatother
\begingroup
\newcommand\maniinnerinner[1]{%
\endgroup
\NewDocumentCommand{\maniinnerinner}{om}{%
\IfNoValueTF{##1}{%
\scantokens{\man{##2}#1}%
\index{##2@\string\man{##2}}%
}{%
\scantokens{\man[##1]{##2}#1}%
\index{##2@\string\man[##1]{##2}}%
}%
}%
}%
\catcode`\%=12\relax
\maniinnerinner{%}%
\makeindex
\begin{document}
Only an index entry: \index{ssh-keygen@\man[1]{ssh-keygen}}
Hello world
Command and index entry: \mani[1]{ssh-keygen}
Only an index entry: \index{ssh-keygen-no-optional-argument@\man{ssh-keygen-no-optional-argument}}
Hello world
Command and index entry: \mani{ssh-keygen-no-optional-argument}
\newcommand\ke{ke}%
\newcommand\one{1}%
Only an index entry: \index{ssh-\ke y\string#gen@\man[\one]{ssh-\ke y\string#gen}}
Hello world
Command and index entry: \mani[\one]{ssh-\ke y\string#gen}
\printindex
\end{document}
В приведенном выше примере результирующий .idx-файл выглядит следующим образом:
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
\indexentry{ssh-keygen-no-optional-argument@\man{ssh-keygen-no-optional-argument}}{1}
\indexentry{ssh-keygen-no-optional-argument@\man{ssh-keygen-no-optional-argument}}{1}
\indexentry{ssh-\ke y\string#gen@\man[\one]{ssh-\ke y\string#gen}}{1}
\indexentry{ssh-\ke y\string#gen@\man[\one]{ssh-\ke y\string#gen}}{1}
решение2
Имя дескриптора, \write
используемого для .idx-файла с мемуарами, отличается от имени дескриптора, \write
используемого с макросом LaTeX 2ε \@wrindex
.
Таким образом, вам необходимо сопоставить имя «kernel- \write
-handle» для индекса с «memoir- \write
-handle» для индекса:
\documentclass{memoir}
\usepackage{xparse}
\makeindex
\NewDocumentCommand\man{om}{%
\textbf{#2}\IfNoValueF{#1}{(#1)}%
}
\makeatletter
\newcommand*{\mani}{%
\@bsphack
\begingroup
\@sanitize
\@mani
}
\NewDocumentCommand{\@mani}{om}{%
\@ifundefined{@indexfile}{%
\expandafter\let\expandafter\@indexfile\csname\jobname @idxfile\endcsname
}{}%
\IfNoValueTF{#1}{%
\man{#2}%
\@wrindex{#2@\string\man{#2}}%
}{%
\man[#1]{#2}%
\@wrindex{#2@\string\man[#1]{#2}}%
}%
}
\makeatletter
\begin{document}
Two separate commands: \man[1]{ssh-keygen}\index{ssh-keygen@\man[1]{ssh-keygen}}
Hello world
One command: \mani[1]{ssh-keygen}
\printindex
\end{document}
Файл .idx выглядит так:
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
\indexentry{ssh-keygen@\man[1]{ssh-keygen}}{1}
Имейте в виду, что в отличие от подхода стрингификации после токенизации, представленного в моем другом ответе, \@sanitize
не выполняется балансировка фигурных скобок в случае, если аргументы \mani
должны содержать control-symbol-token \{
и/или control-symbol-token \}
.