% \iffalse meta-comment
%
% Copyright (C) 2014, 2017 by Leo Liu <leoliu.pku@gmail.com>
% ---------------------------------------------------------------------------
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, 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 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Leo Liu.
%
% This work consists of the files seealso.dtx and seealso.ins
% and the derived filebase seealso.sty.
%
% \fi
%
% \iffalse
%<*driver>
\ProvidesFile{seealso.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%<package>\ProvidesPackage{seealso}
%<*package>
    [2017/03/23 v1.2 makeidx's see and seealso with page number support.]
%</package>
%
%<*driver>
\documentclass{ltxdoc}
\usepackage[a4paper,left=2in,right=1in,top=1in,bottom=1in]{geometry}
\newcommand\pkg[1]{\textsf{#1}}
\usepackage[UTF8,hyperref]{ctex}
\makeatletter
\renewcommand\glossary@prologue{%
  \section*{版本历史}
  \markboth{版本历史}{版本历史}}
\renewcommand\index@prologue{%
  \section*{Index / 代码索引}
  \markboth{Index / 代码索引}{Index / 代码索引}
  斜体的数字表示对应项说明所在的页码。下划线的数字表示定义所在的代码行号；而直
  立体的数字表示对应项使用时所在的行号。}
\def\option{\begingroup
   \catcode`\\12
   \MakePrivateLetters \option@}
\let\endoption\endtrivlist
\long\def\option@#1{\endgroup \topsep\MacroTopsep \trivlist
  \edef\saved@macroname{\string#1}%
  \def\makelabel##1{\llap{##1}}%
  \if@inlabel
    \let\@tempa\@empty \count@\macro@cnt
    \loop \ifnum\count@>\z@
      \edef\@tempa{\@tempa\hbox{\strut}}\advance\count@\m@ne \repeat
    \edef\makelabel##1{\llap{\vtop to\baselineskip
                               {\@tempa\hbox{##1}\vss}}}%
    \advance \macro@cnt \@ne
  \else  \macro@cnt\@ne  \fi
  \edef\@tempa{\noexpand\item[%
     \noexpand\PrintEnvName
     {\string#1}]}%
  \@tempa
  \global\advance\c@CodelineNo\@ne
      \SpecialMainOptionIndex{#1}\nobreak
  \global\advance\c@CodelineNo\m@ne
  \ignorespaces}
\def\SpecialMainOptionIndex#1{\@bsphack
  \special@index{%
    #1\actualchar
    {\string\ttfamily\space#1}
    (option)%
    \encapchar main}%
  \@esphack}
\def\SpecialOptionIndex#1{\@bsphack
  \begingroup
    \HD@target
    \let\HDorg@encapchar\encapchar
    \edef\encapchar usage{%
      \HDorg@encapchar hdclindex{\the\c@HD@hypercount}{usage}}%
    \index{#1\actualchar{\protect\ttfamily#1}
           (option)\encapchar usage}%
    \index{options:\levelchar#1\actualchar{\protect\ttfamily#1}\encapchar
           usage}%
  \endgroup
  \@esphack}
\def\DescribeOption{\leavevmode\@bsphack\begingroup\MakePrivateLetters
  \Describe@Option}
\def\Describe@Option#1{\endgroup
              \marginpar{\raggedleft\PrintDescribeEnv{#1}}%
              \SpecialOptionIndex{#1}\@esphack\ignorespaces}
\def\stylemeaning#1{\texttt{%
  \expandafter\expandafter\expandafter\strip@prefix
    \expandafter\meaning\csname seealso@#1\endcsname}}
\renewcommand\author[2][]{%
  \ifstrempty{#1}{}{%
    \hypersetup{pdfauthor=#1}}%
  \def\@author{#2}}
\renewcommand\title[2][]{%
  \ifstrempty{#1}{}{%
    \hypersetup{pdftitle=#1}}%
  \def\@title{#2}}
\makeatother
\usepackage{seealso}
\usepackage{fancyvrb}
\fvset{gobble=2}
\usepackage{hypdoc}
\hypersetup{pdfstartview=FitH}
\AtBeginDocument{\MakeShortVerb\|}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{seealso.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{279}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
%
% \changes{v0.1}{2014/04/04}{初始版本。}
% \changes{v1.0}{2014/04/05}{功能较完备的版本。}
%
% \DoNotIndex{\newcommand,\newenvironment,\^,\~,\@for,\@ifnextchar,
% \@let@token,\@onlypreamble, \active, \AtBeginDocument,\begingroup,\catcode,
% \csdef,\cslet,\cslet,\csuse, \DeclareBoolOption,\def,\define@key,\do,
% \docsvlist, \dolistloop,\edef,\else, \emph,\empty,\endgroup,\expandafter,
% \fi, \forcsvlist, \forlistcsloop, \forlistloop, \futurelet,\global,
% \ifcsdef, \ifcsempty,\ifinlist,\ifinlistcs, \ifstrempty,\ifundef,\ifx,
% \ignorespaces, \kvsetkeys,\lccode,\let, \listadd,\listbreak, \listcsgadd,
% \lowercase,\newif, \next, \noexpand,\ProcessKeyvalOptions, \RequirePackage,
% \reserved@a, \SetupKeyvalOptions,\space,\unexpanded,\unless}
%
% \GetFileInfo{seealso.dtx}
% \title[The seealso Package (\fileversion)]
%   {\hypertarget{English}{The \pkg{seealso} Package (\fileversion)}
%    \makebox[0pt][l]{\hspace{3cm}\large
%      \hyperlink{Chinese}{$\Rightarrow$ \textsf{中文版}}}}
% \author[Liú Hǎiyáng (Leo Liu) <leoliu.pku@gmail.com>]
%   {Liú Hǎiyáng (Leo Liu) \\ \nolinkurl{leoliu.pku@gmail.com}}
% \date{\filedate}
%
% \maketitle
%
% \section{Introduction}
%
% When preparing index, the macros |\see| and |\seealso| defined by \LaTeXe's
% standard package \pkg{makeidx} are handy to use. They are used as special
% page format command, to present cross reference of index entry. For example,
% if we use
% \begin{Verbatim}
% \index{math|see{mathematics}}
% \end{Verbatim}
% to reference the index entry ``mathematics'', we will get
% \begin{quote}
% math, \emph{see} mathematics
% \end{quote}
% in the output index.
%
% However, |\see| and |\seealso| commands in \LaTeXe{} cannot produce page
% numbers. Moreover, if we use multiple |\see| or |\seealso|'s for one entry,
% there will be multiple reference targets produced. Therefore we may get
% this unexpected output:
% \begin{quote}
% math, \emph{see} mathematics, \emph{see} mathematics, \emph{see} mathematics
% \end{quote}
% if we have used \verb=\index{math|see{mathematics}}= at 3 different places
% in the document.
%
% The \pkg{seealso} package solve the problem. If we use
% \begin{Verbatim}
% \usepackage{seealso}
% \end{Verbatim}
% all cross references of index entry will produce page numbers, and the
% reference targets, which we call \emph{see list}, will be merged. The
% previous example will produce an output like this:
% \begin{quote}
% math, \seepage{mathematics}{2}, \seepage{mathematics}{4}, \seepage{mathematics}{5}
% \end{quote}
%
% \section{Reference}
%
% \subsection{Package Loading}
%
% The basic usage of the package is to simply load the package
% \begin{Verbatim}
% \usepackage{seealso}
% \end{Verbatim}
% then |\see| and |\seealso| commands will be redefined to support page number
% output and reference target merging.
%
% The \pkg{seealso} package should be loaded after \pkg{makeidx},
% \pkg{imakeidx}, etc. Or the redefinition of |\see| and |\seealso| may be
% broken.
%
% \subsection{Package Options}
%
% \DescribeOption{override}
% |override| is a boolean option, with a default value |true|. When it is set
% the original |\see| and |\seealso| will be overridden. However, when it is
% set to be |false|, say
% \begin{Verbatim}
% \usepackage[override=false]{seealso}
% \end{Verbatim}
% then |\see| and |\seealso| will not be overridden, and |\seepage| and
% |\seealsopage| should be used for page number output.
%
% \DescribeOption{activecr}
% |activecr| is a boolean option, with a default value |true|. When it is set,
% the see list will be output at the end of an index entry line (in a |.ind|
% file). If we set |activecr=false|, however, the see list will not be output
% automatically, one should use |\SeealsoPrintList| manually at the end of the
% index entry instead. Usually |\SeealsoPrintList| is added via |delim_t|
% style of Makeindex.
%
% \subsection{The \cs{see} and \cs{seealso} Commands}
%
% \DescribeMacro{\seepage}
% \DescribeMacro{\seealsopage}
% |\seepage| and |\seealsopage| provide the main functions of this package.
% They provide the same faculty of cross reference, but output page numbers
% and prevent redundancy. For example, suppose that there are
% \begin{Verbatim}
% \index{foo|seepage{bar}}
% \end{Verbatim}
% in page 1, 2, and 3, we will have a |.ind| file as below:
% \begin{Verbatim}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{bar}{3}
% \end{Verbatim}
% And the output of the index will be:
% \begin{quote}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{bar}{3}
% \end{quote}
%
% It is possible to use different references for one index entry. For example
% we can use
% \begin{Verbatim}
% \index{foo|seepage{bar}}
% \end{Verbatim}
% and
% \begin{Verbatim}
% \index{foo|seepage{foobaz}}
% \end{Verbatim}
% several times, the generated |.ind| file may be:
% \begin{Verbatim}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{foobaz}{3}
% \end{Verbatim}
% And the output of the index will be:
% \begin{quote}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{foobaz}{3}
% \end{quote}
%
% \DescribeMacro{\seenopage}
% \DescribeMacro{\seealsonopage}
% The macros |\seenopage| and |\seealsonopage| save the original definitions
% of |\see| and |\seealso| before loading \pkg{seealso}, which do not produce
% page numbers.
%
% \DescribeMacro{\see}
% \DescribeMacro{\seealso}
% When option |override| is |true|, which is the default, |\see| and
% |\seealso| works just as |\seepage| and |\seealsopage|.
%
% \DescribeMacro{\SeealsoPrintList}
% |\seepage| and |\seealsopage| will collect the see lists, which will be
% output at the end of the line under default |activecr| option. However, when
% |activecr| is set to be |false|, the see lists won't be output at the end of
% source line where |\seepage| and |\seealsopage| are. In the latter case, we
% can add |\SeealsoPrintList| manually at the end of line to output the see
% lists. Usually we can set |delim_t| variable in the style file of Makeindex,
% for example:
% \begin{Verbatim}
% % example.ist
% delim_t "\\SeealsoPrintList"
% \end{Verbatim}
% Then a |\SeealsoPrintList| will be appended at the end of every non-empty
% index entry, and the see lists will be output properly.
%
% \DescribeMacro{\DeclareSeealsoMacro}
% |\DeclareSeealsoMacro| can be used to define a new macro like |\see| and
% |\seealso|. The syntax is
% \begin{quote}
% |\DeclareSeealsoMacro\|\meta{macro}|{|\meta{seelist}|}{|\meta{name}|}|
% \end{quote}
% where |\|\meta{macro} is the macro name, \meta{seelist} is the new see list,
% and \meta{name} is the name of the see list to be output (like |\seename|).
% Using |\DeclareSeealsoMacro|, two macros will be defined, the one is
% |\|\meta{macro}, the other is |\|\meta{macro}|name|. If there has no
% |\|\meta{macro}|name| predefined, it is defined to be \meta{name}, or the
% definition remains unchanged.
%
% For example, |\seepage| and |\seealsopage| are defined like this:
% \begin{Verbatim}
% \DeclareSeealsoMacro\seepage{see}{see}
% \DeclareSeealsoMacro\seealsopage{also}{see also}
% \end{Verbatim}
% The definitions above also provide the default value of |\seename| and
% |\alsoname| to be |see| and |see also|, respectively.
%
% \subsection{Output Styles}
%
% \DescribeMacro{\seealsosetup}
% The macro |\seealsosetup| is used to configure the output style of see
% lists. The syntax is:
% \begin{quote}
% |\seealsosetup[|\meta{seelists}|]{|\meta{kv-options}|}|
% \end{quote}
% where \meta{seelists} is a comma separated list, each item of which is a see
% list to be setup. \meta{seelists} can also be omitted or set to be empty,
% which means we are setting up the default output style of all kind of see
% lists. \meta{kv-options} is the options in key-value syntax.
%
% All possible output styles are summarized in table~\ref{tab:outstyleen}.
% \begin{table}[htbp]
% \centering
% \begin{tabular}{lp{4cm}ll}
% \hline
% Key & Meaning & Argument & Default Value \\
% \hline
% |name| & The name of see list to be output, e.g. |\seename| & none
%   & (invalid) \\
% |listsep| & The separator before a see list & none
%   & \stylemeaning{listsep} \\
% |itemsep| & The separator between see list items & none
%   & \stylemeaning{itemsep} \\
% |nameformat| & Output format of the see list name & |#1|
%   & \stylemeaning{nameformat} \\
% |itemformat| & Output format of the see list items & |#1|
%   & \stylemeaning{itemformat} \\
% |pageformat| & Output format of the page numbers & |#1|
%   & \stylemeaning{pageformat} \\
% \hline
% \end{tabular}
% \caption{Output styles used by \cs{seealsosetup}}\label{tab:outstyleen}
% \end{table}
%
% For example, if we want to set all page numbers produced by |\seealsopage|
% to be italic, we can use:
% \begin{Verbatim}
% \seealsosetup[also]{pageformat=\textit{#1}}
% \end{Verbatim}
%
% Besides the output styles, |\seealsosetup| accepts |enditem| and |enditem+|
% options, which is a list of token that determines whether to output the see
% list at the end of line, if |activecr| option is true. The default value of
% the list includes some common macros in |theindex| environment:
% |\indexspace|, |\item|, |\subitem|, |\subsubitem|, and |\end|. For example,
% if we have a special index which supports 4th level item |\subsubsubitem|
% and a special space |\myindexspace|, we can use
% \begin{Verbatim}
% \seealsosetup{enditem+={\subsubsubitem,\myindexspace}}
% \end{Verbatim}
% to add the new macros to the list.
%
% \DescribeMacro{\SeealsoGobble}
% |\SeealsoGobble| takes a character \meta{c} as argument. It checks the
% following character, gobble it and ignore spaces if it is \meta{c}, or do
% nothing if it is not \meta{c}. This macro is useful in |pageformat| style
% to prevent outputting the page numbers, like this:
% \begin{Verbatim}
% \seealsosetup[see,also]{pageformat=\SeealsoGobble{,}}
% \end{Verbatim}
%
%
% \section{Known Issues}
%
% Here are some known problems and their solutions:
% \begin{itemize}
% \item
% Since the output format of page numbers of |\seepage|, |\seealsopage| is the
% same as normal page numbers, it may be ambiguous if we use normal |\index|
% and \verb=\index{...|seepage}= simultaneously. Usually we should avoid using
% different style of |\index| with or without |\seepage| and |\seealsopage|.
% If it is impossible, we can set special page number format to identify them,
% or we can use |\SeealsoGobble| to disable page numbers.
% \end{itemize}
%
%
% \clearpage
% \title{\hypertarget{Chinese}{\pkg{seealso} 宏包 (\fileversion)}
%   \makebox[0pt][l]{\hspace{3cm}\large
%     \hyperlink{English}{$\Rightarrow$ \textsf{English Version}}}}
% \author{刘海洋 \\ \nolinkurl{leoliu.pku@gmail.com}}
% \date{\filedate}
%
% \maketitle
%
% \section{简介}
%
% 在索引生成时，\LaTeXe{} 的标准宏包 \pkg{makeidx} 定义了 |\see| 与 |\seealso|
% 两个宏，它们通常是在 |\index| 中作为一种特殊的页码格式使用，表示索引项的引
% 用。例如使用
% \begin{Verbatim}
% \index{math|see{mathematics}}
% \end{Verbatim}
% 会使索引项 math 引用到 mathematics。生成类似
% \begin{quote}
% math, \emph{see} mathematics
% \end{quote}
% 的索引条目。
%
% 不过，\LaTeXe{} 的 |\see| 与 |\seealso| 命令并不会为交叉引用的索引项输出页
% 码。更严重的问题是，如果对一个索引项使用了多次交叉引用，则会多次输出相同的目
% 标引用。因此可能出现这样的尴尬效果：
% \begin{quote}
% math, \emph{see} mathematics, \emph{see} mathematics, \emph{see} mathematics
% \end{quote}
% 其中可能在三个不同的地方使用了 \verb=\index{math|see{mathematics}}=。
%
% \pkg{seealso} 宏包即为解决这种问题而编写。只要使用了
% \begin{Verbatim}
% \usepackage{seealso}
% \end{Verbatim}
% 则交叉引用的索引项会同时显示页码，同时多个交叉引用的目标（我们称为\emph{参见
% 列表}）会合并显示。前面的例子就可能会输出：
% \begin{quote}
% math, \seepage{mathematics}{2}, \seepage{mathematics}{4}, \seepage{mathematics}{5}
% \end{quote}
% 这样的格式。
%
% \section{参考手册}
%
% \subsection{宏包载入}
%
% 基本的用法就是直接使用
% \begin{Verbatim}
% \usepackage{seealso}
% \end{Verbatim}
% 这样 |\see| 和 |\seealso| 命令就会被重定义为支持输出页码和合并目标的格式。
%
% 注意 \pkg{seealso} 宏包的载入应该晚于 \pkg{makeidx}、\pkg{imakeidx} 等定义
% |\see|、|\seealso| 的宏包，否则其功能将会失效。
%
% \subsection{宏包选项}
%
% \DescribeOption{override}
% |override| 是布尔型选项，默认为 |true|。表示覆盖已有的 |\see| 与 |\seealso|
% 定义。如果设置
% \begin{Verbatim}
% \usepackage[override=false]{seealso}
% \end{Verbatim}
% 则不对 |\see| 与 |\seealso| 覆盖，要使用 |\seepage| 和
% |\seealsopage| 命令调用相应的功能。
%
% \DescribeOption{activecr}
% |activecr| 是布尔型选项，默认为 |true|。表示打开在（|.ind| 文件中）索引项行
% 末自动输出所有参见列表的功能。如果设置 |activecr=false|，则必须手工（通常通
% 过设置 Makeindex 的 |delim_t| 格式）在索引项末加上 |\SeealsoPrintList| 输出
% 参见列表。
%
% \subsection{\cs{see} 与 \cs{seealso} 命令}
%
% \DescribeMacro{\seepage}
% \DescribeMacro{\seealsopage}
% |\seepage| 与 |\seealsopage| 提供了宏包的主要功能，它们与原始的
% |\see|、|\seealso| 命令类似提供索引项的交叉引用功能，但同时会输出页码，并避
% 免重复输出交叉引用。例如，在文档中第 1, 2, 3 页分别多次使用
% \begin{Verbatim}
% \index{foo|seepage{bar}}
% \end{Verbatim}
% 则在 |.ind| 文件中会生成：
% \begin{Verbatim}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{bar}{3}
% \end{Verbatim}
% 而输出的结果将会是：
% \begin{quote}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{bar}{3}
% \end{quote}
%
% 同一个词可以有不同的参见引用项目，例如在不同的位置几次使用
% \begin{Verbatim}
% \index{foo|seepage{bar}}
% \end{Verbatim}
% 和
% \begin{Verbatim}
% \index{foo|seepage{foobaz}}
% \end{Verbatim}
% 则 |.ind| 文件中可能生成：
% \begin{Verbatim}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{foobaz}{3}
% \end{Verbatim}
% 于是将得到输出结果
% \begin{quote}
% foo, \seepage{bar}{1}, \seepage{bar}{2}, \seepage{foobaz}{3}
% \end{quote}
%
% \DescribeMacro{\seenopage}
% \DescribeMacro{\seealsonopage}
% |\seenopage| 与 |\seealsonopage| 这两个宏则保存了在调用 \pkg{seealso} 宏包之
% 前，|\see| 与 |\seealso| 的原始定义，它们不会输出页码。
%
% \DescribeMacro{\see}
% \DescribeMacro{\seealso}
% 在默认的 |override| 选项下，|\see| 与 |\seealso| 的功能与
% |\seepage|、|\seealsopage| 相同。
%
% \DescribeMacro{\SeealsoPrintList}
% |\seepage| 和 |\seealsopage| 会收集参见列表，在默认的 |activecr| 选项为
% |ture| 时会在行末自动输出。但如果 |activecr| 选项设置为 |false|，则
% |\seepage| 与 |\seealsopage| 所在行结束后不会自动输出参见列表。此时，可以手
% 工在此行最后加上 |\SeealsoPrintList| 宏，输出参见列表。通常可以在 Makeindex
% 的格式文件中，输出格式选项 |delim_t| 里面加上 |\SeealsoPrintList| 宏，如：
% \begin{Verbatim}
% % example.ist
% delim_t "\\SeealsoPrintList"
% \end{Verbatim}
% 这样，每个非空索引项输出的最后都会加上 |\SeealsoPrintList|，按需要输出参见列
% 表。
%
% \DescribeMacro{\DeclareSeealsoMacro}
% |\DeclareSeealsoMacro| 命令用于生成新的类似 |\see| 与 |\seealso| 的宏。其语
% 法为：
% \begin{quote}
% |\DeclareSeealsoMacro\|\meta{macro}|{|\meta{seelist}|}{|\meta{name}|}|
% \end{quote}
% 其中 |\|\meta{macro} 是新的宏名称，\meta{seelist} 是新的参见列表
% 名，\meta{name} 是输出参见列表时的名称。使用该命令会生成两个宏，一个是
% |\|\meta{macro}，一个是对应的名称 |\|\meta{macro}|name|。
% |\|\meta{macro}|name| 如果事先没有定义，则定义其值为 \meta{name}，否则保持不
% 变。
%
% 例如，|\seepage| 和 |\seealsopage| 就是这样定义的：
% \begin{Verbatim}
% \DeclareSeealsoMacro\seepage{see}{see}
% \DeclareSeealsoMacro\seealsopage{also}{see also}
% \end{Verbatim}
% 上述命令同时定义了 |\seename| 的备选值为 |see|，以及 |\alsoname| 的备选值为
% |see also|。
%
% \subsection{格式设置}
%
% \DescribeMacro{\seealsosetup}
% |\seealsosetup| 命令用于设置参见列表的输出格式。其语法为：
% \begin{quote}
% |\seealsosetup[|\meta{seelists}|]{|\meta{kv-options}|}|
% \end{quote}
% 其中，可选参数 \meta{seelists} 是一个逗号分隔的列表，每一项表示要设置其格式
% 的参见列表，如果不设置 \meta{seelists} 或设为空，则设置默认的输出格式。而选
% 项 \meta{kv-options} 则是输出格式项。
%
% 可用的输出格式见表~\ref{tab:outstylezh}。
% \begin{table}[htbp]
% \centering
% \begin{tabular}{llll}
% \hline
% 选项 & 意义 & 参数 & 默认值 \\
% \hline
% |name| & 输出参见列表的名称 & 无 & （不可用） \\
% |listsep| & 参见列表前的分隔符 & 无 & \stylemeaning{listsep} \\
% |itemsep| & 参见列表项间的分隔符 & 无 & \stylemeaning{itemsep} \\
% |nameformat| & 输出参见名“see also”的格式 & |#1| & \stylemeaning{nameformat} \\
% |itemformat| & 参见列表项的格式 & |#1| & \stylemeaning{itemformat} \\
% |pageformat| & 页码格式 & |#1| & \stylemeaning{pageformat} \\
% \hline
% \end{tabular}
% \def\tablename{表}
% \caption{\cs{seealsosetup} 使用的输出格式}\label{tab:outstylezh}
% \end{table}
%
% 例如，如果要让所有 |\seealsopage| 命令生成的页码以斜体显示，就可以使用
% \begin{Verbatim}
% \seealsosetup[also]{pageformat=\textit{#1}}
% \end{Verbatim}
%
% 除了输出格式，|\seealsosetup| 还接受 |enditem| 和 |enditem+| 选项，用于设置
% \pkg{seealso} 宏包在 |activecr| 选项设置时，进入行尾之后遇到什么记号时决定输
% 出参见列表。通常情况下并不需要设置该选项，|enditem| 选项的默认值已经包括了在
% 标准的 |theindex| 环境中会出现的一些宏：|\indexspace|、|\item|、|\subitem|、
% |\subsubitem|、|\end|。使用 |enditem+| 选项则可以向列表中增加新的记号。例
% 如，如果某种特殊的索引支持 4 级项 |\subsubsubitem| 和特殊的间隔
% |\myindexspace|，就可以使用
% \begin{Verbatim}
% \seealsosetup{enditem+={\subsubsubitem,\myindexspace}}
% \end{Verbatim}
% 向列表中添加新的记号。
%
% \DescribeMacro{\SeealsoGobble}
% |\SeealsoGobble| 宏接受一个字符作为参数，它检查后面是否有此字符，如果是则吞
% 掉此字符及后面的空格，否则什么都不做。在 |pageformat| 选项中使用这个宏可以用
% 来禁止显示参见项的页码，像下面这样：
% \begin{Verbatim}
% \seealsosetup[see,also]{pageformat=\SeealsoGobble{,}}
% \end{Verbatim}
%
% \section{已知问题}
%
% 以下是一些已知的问题和解决方案：
% \begin{itemize}
% \item
% 由于 |\seepage|, |\seealsopage| 的页码显示形式与普通页码相同，所以可能会造成
% 页码重叠的情况。最好尽量避免交叉引用的项目有多种形式的索引。如果不能避免，为
% 解决此问题，可以设置特别的页码输出格式以示区分，或者借用 |\SeealsoGobble| 设
% 置不输出页码。
% \end{itemize}
%
% \StopEventually{}
%
% \clearpage
% \section{Implementation / 代码实现}
%
% \iffalse
%<*package>
% \fi
%
% \subsection{准备工作}
%
% 引入相关编程工具。
%
% \pkg{etoolbox} 是基本宏工具。原有的 \cs{ifinlist} 和 \cs{ifinlistcs} 不能用
% 于带花括号的文字的查找，会限制我们的使用。为此按 \pkg{etoolbox} 手册的建议，
% 补充了 \cs{seealso@ifstrinlist} 和 \cs{seealso@ifstrinlistcs} 两个宏来在列表
% 中搜索字符串。它们会比 \cs{ifinlist} 与 \cs{ifinlistcs} 性能略差一点。
% \changes{v1.2}{2017/03/23}{允许 \cs{see} 等命令参数中带花括号。}
%    \begin{macrocode}
\RequirePackage{etoolbox}
% {<listmacro>}{<string>}{<true>}{<false>}
\long\def\seealso@ifinlist@#1#2#3#4{%
  \def\next{#4}%
  \def\do##1{%
    \ifstrequal{##1}{#2}
      {\def\next{#3}\listbreak}
      {}}%
  \dolistloop{#1}%
  \next}
% {<string>}{<listmacro>}{<true>}{<false>}
\protected\long\def\seealso@ifinlist#1#2{%
  \seealso@ifinlist@{#2}{#1}}
% {<string>}{<listcsname>}{<true>}{<false>}
\protected\long\def\seealso@ifinlistcs#1#2{%
  \expandafter\seealso@ifinlist@\csname #2\endcsname{#1}}
%    \end{macrocode}
% 
% \pkg{kvoptions} 用于处理宏包选项。
%    \begin{macrocode}
\RequirePackage{kvoptions}
\SetupKeyvalOptions{
  family=seealso@opt,
  prefix=seealso@,
  setkeys=\kvsetkeys}
%    \end{macrocode}
%
% 声明宏包选项。
%
% \begin{option}{override}
% \changes{v1.0}{2014/04/05}{新增选项。}
% 重定义 |\see| 与 |\seealso| 为有页码的形式。默认打开。
%    \begin{macrocode}
\DeclareBoolOption[true]{override}
%    \end{macrocode}
% \end{option}
%
% \begin{option}{activecr}
% \changes{v1.0}{2014/04/05}{新增选项。}
% 使用换行符作为输出 |\see| 等命令的指令。默认打开。
%    \begin{macrocode}
\DeclareBoolOption[true]{activecr}
%    \end{macrocode}
% \end{option}
%
% 执行选项。
%    \begin{macrocode}
\ProcessKeyvalOptions*
%    \end{macrocode}
%
% \begin{macro}{\seealso@charlet}
% 参数 |#1| 是一个字符或 |\| 加字符的形式，|\seealso@charlet| 将此字符看做活动
% 字符的宏，使用 |\let| 与后面的内容赋值，但本身不改变字符的 catcode。
%    \begin{macrocode}
\def\seealso@charlet#1{%
  \begingroup\lccode`\~=`#1\lowercase{\endgroup\let~}}
%    \end{macrocode}
% \end{macro}
%
% \subsection{参见列表及其实现}
%
% \begin{macro}{\seealso@macrolist}
% 列表记录所有独立的类似 |\seepage| 的宏。默认只有 |see| 和 |also| 两组，对应
% 命令 |\seepage| 和 |\seealsopage|。
%    \begin{macrocode}
\let\seealso@macrolist\empty
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@clearlist}
% 清空参见列表，如 |\seealso@see@list|、|\seealso@also@list|。
%    \begin{macrocode}
\def\seealso@clearlist#1{%
  \global\cslet{seealso@#1@list}\empty}
\AtBeginDocument{\forlistloop\seealso@clearlist\seealso@macrolist}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\SeealsoPrintList}
% 输出参见列表，如 |\seealso@see@list|、|\seealso@also@list|。
%    \begin{macrocode}
\newcommand\SeealsoPrintList{%
  \forlistloop\seealso@printlist\seealso@macrolist
  \forlistloop\seealso@clearlist\seealso@macrolist}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ifseealso@gobblefirstlistsep}
% 测试是否忽略第一个列表分隔符（因为前面使用了 |\SeealsoGobble|）。
%    \begin{macrocode}
\newif\ifseealso@gobblefirstlistsep
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ifseealso@firstitem}
% 测试是否是在输出参见列表的第一项。
%    \begin{macrocode}
\newif\ifseealso@firstitem
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@printlist}
% 输出参见列表 |#1|。如果列表为空则无操作。
%    \begin{macrocode}
\def\seealso@printlist#1{%
  \ifcsempty{seealso@#1@list}
    {}
    {\ifseealso@gobblefirstlistsep
       \seealso@gobblefirstlistsepfalse
     \else
       \csuse{seealso@#1@listsep}%
     \fi
     \csuse{seealso@#1@nameformat}{\csuse{#1name}}%
     \seealso@firstitemtrue
     \forlistcsloop{\seealso@listitem{#1}}{seealso@#1@list}}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@listitem}
% 输出参见列表的一项。如果不是第一项，同时输出分隔符。
%    \begin{macrocode}
\def\seealso@listitem#1#2{%
  \ifseealso@firstitem
    \seealso@firstitemfalse
  \else
    \csuse{seealso@#1@itemsep}%
  \fi
  \csuse{seealso@#1@itemformat}{#2}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\DeclareSeealsoMacro}
% \changes{v1.0}{2014/04/05}{新增以定义新的参见命令。}
% 定义一个新的带页码的参见命令。|#1| 是命令名，|#2| 是该命令使用的参见列
% 表，|#3| 是列表输出时使用的名字。
%    \begin{macrocode}
\newcommand\DeclareSeealsoMacro[3]{%
%    \end{macrocode}
% 首先定义参见命令 |#1| 本身。
%    \begin{macrocode}
  \newcommand#1[2]{%
    \seealso@setactivecr
    \seealso@ifinlistcs{##1}{seealso@#2@list}
      {}
      {\listcsgadd{seealso@#2@list}{##1}}%
    \csuse{seealso@#2@pageformat}{##2}}%
%    \end{macrocode}
% 将参见命令加入列表。
%    \begin{macrocode}
  \listadd\seealso@macrolist{#2}%
%    \end{macrocode}
% 定义 |name| 选项，用来设置 |\|\meta{macro}|name|。如 |\seename| 和
% |\alsoname|。
%    \begin{macrocode}
  \define@key{seealso@#2}{name}{%
    \csdef{#2name}{##1}}%
%    \end{macrocode}
% 如果事先没有定义，则定义 |\|\meta{macro}|name| 为 |#3|。
%    \begin{macrocode}
  \ifcsdef{#2name}
    {}
    {\kvsetkeys{seealso@#2}{name=#3}}%
%    \end{macrocode}
% 定义无参选项。
%    \begin{macrocode}
  \def\do##1{%
    \define@key{seealso@#2}{##1}{%
      \csdef{seealso@#2@##1}{####1}}%
    \kvsetkeys{seealso@#2}{##1=\csuse{seealso@##1}}}%
  \docsvlist{listsep,itemsep}%
%    \end{macrocode}
% 定义有一个参数的选项。
%    \begin{macrocode}
  \def\do##1{%
    \define@key{seealso@#2}{##1}{%
      \csdef{seealso@#2@##1}########1{####1}}%
      \kvsetkeys{seealso@#2}{##1=\csuse{seealso@##1}{####1}}}%
  \docsvlist{nameformat,itemformat,pageformat}}
\@onlypreamble{\DeclareSeealsoMacro}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ifseealso@iscractive}
% 判断当前换行符是否已经被激活。
%    \begin{macrocode}
\newif\ifseealso@iscractive
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@setactivecr}
% |activecr| 选项为真时，设置换行符为 |\seealso@cr| 并激活。
%    \begin{macrocode}
\def\seealso@setactivecr{%
  \ifseealso@activecr
    \unless\ifseealso@iscractive
      \catcode`\^^M=\active
      \seealso@charlet\^^M\seealso@cr
      \seealso@iscractivetrue
    \fi
  \fi}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@cr}
% \changes{v1.1}{2014/04/07}{处理折行问题。}
% 是在 |activecr| 选项下，换行符的定义。它检测后面的记号是否在
% |\seealso@enditemlist| 中，以判断是否处于折行状态，决定是否输出列表。
%    \begin{macrocode}
\def\seealso@cr{%
  \futurelet\next\seealso@cr@aux}
%    \end{macrocode}
% |\seealso@cr@aux| 是实际完成判断折行与输出的辅助过程。如果折行，则补全折行造
% 成的空白；否则取消激活换行符为宏，并输出参见列表。
%    \begin{macrocode}
\def\seealso@cr@aux{%
  \seealso@testwrap
  \ifseealso@wrap
    \space
  \else
    \catcode`\^^M=5
    \seealso@iscractivefalse
    \SeealsoPrintList
  \fi}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ifseealso@wrap}
% 判断当前是否在折行的行尾。
%    \begin{macrocode}
\newif\ifseealso@wrap
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@testwrap}
% 检查由 |\seealso@cr| 获取的下一记号 |\next| 是否在列表
% |\seealso@enditemlist| 中，确定当前是否在折行行尾。
%    \begin{macrocode}
\def\seealso@testwrap{%
  \seealso@wraptrue
  \forlistloop\seealso@testwrap@aux\seealso@enditemlist}
\def\seealso@testwrap@aux#1{%
  \ifx#1\next
    \seealso@wrapfalse
    \expandafter\listbreak
  \fi}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@enditemlist}
% 表示索引项结束的记号列表。用于判断是否处于折行状态。
%    \begin{macrocode}
\let\seealso@enditemlist\empty
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@enditemlistadd}
% 向结束记号列表中增加一项。
%    \begin{macrocode}
\def\seealso@enditemlistadd#1{%
  \seealso@ifinlist{#1}\seealso@enditemlist
    {}
    {\listadd\seealso@enditemlist{#1}}}
%    \end{macrocode}
% \end{macro}
% 下面定义结束记号列表的在 |seealso| 族下的键 |enditem| 与 |enditem+|，允许用
% 户自定义此列表。这里要求列表总包含 |\seealso@cr|。
%    \begin{macrocode}
\define@key{seealso}{enditem}{%
  \let\seealso@enditemlist\empty
  \listadd\seealso@enditemlist{\seealso@cr}%
  \forcsvlist\seealso@enditemlistadd{#1}}
\define@key{seealso}{enditem+}{%
  \forcsvlist\seealso@enditemlistadd{#1}}
%    \end{macrocode}
% 结束记号列表的默认值，除了 |\seealso@cr| 外，还包括通常在 |theindex| 等环境
% 中出现的其他一些宏。
%    \begin{macrocode}
\kvsetkeys{seealso}{%
  enditem={\indexspace,\item,\subitem,\subsubitem,\end}}
%    \end{macrocode}
%
% 下面定义参见列表的默认输出格式。以下宏给出的都是全局的默认值，可以单独修改每
% 一项的输出格式。
% \begin{macro}{\seealso@listsep}
% 参见列表之前的分隔符。
%    \begin{macrocode}
\def\seealso@listsep{,\space}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\seealso@itemsep}
% 参见列表项之间的分隔符。
%    \begin{macrocode}
\def\seealso@itemsep{,\space}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\seealso@nameformat}
% 参见名“see also”的输出格式。
%    \begin{macrocode}
\def\seealso@nameformat#1{\emph{#1}\space}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\seealso@itemformat}
% 参见列表项的输出格式。
%    \begin{macrocode}
\def\seealso@itemformat#1{#1}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\seealso@pageformat}
% 参见页码的输出格式。
%    \begin{macrocode}
\def\seealso@pageformat#1{#1}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seealso@define@key}
% \begin{macro}{\seealso@define@keyarg}
% 定义 |seealso| 族的键，用于设置输出格式。
%    \begin{macrocode}
\def\seealso@define@key#1{%
  \define@key{seealso}{#1}{%
    \csdef{seealso@#1}{##1}}}
\def\seealso@define@keyarg#1{%
  \define@key{seealso}{#1}{%
    \csdef{seealso@#1}####1{##1}}}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% 为每个输出格式定义键。
%    \begin{macrocode}
\forcsvlist\seealso@define@key{listsep,itemsep}
\forcsvlist\seealso@define@keyarg{nameformat,itemformat,pageformat}
%    \end{macrocode}
%
% \begin{macro}{\SeealsoGobble}
% \changes{v1.0}{2014/04/06}{支持吞掉分隔符的页码格式，即不显示页码。}
% \changes{v1.1}{2014/04/07}{处理 \pkg{hyperref} 兼容性。}
% \changes{v1.1}{2014/04/09}{不再要求设置 \texttt{listsep} 为空。}
% 检查后面的字符是否 |#1|，如果是则吞掉分隔符 |#1| 及后面的空格。这个宏可用于
% 设置 |pageformat| 格式，吞掉一个类似 |,\space| 类型的分隔符。
%
% 这里检查是否定义了 \pkg{hyperref} 的 |\hyperindexformat|（表示打开了索引的超
% 链接功能），如果有 |\hyperindexformat|，则需要调整参数次序以解决
% |\hyperindexformat| 与 |\see| 等命令嵌套的问题。
%    \begin{macrocode}
\AtBeginDocument{%
  \ifundef\hyperindexformat{%
    \let\SeealsoGobble\seealso@gobble
  }{%
    \def\SeealsoGobble#1{\seealso@swap{\seealso@gobble{#1}}}%
    \def\seealso@swap#1#2#3#4{#2#3#4#1}%
  }}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\seealso@gobble}
% 实际的 |\SeealsoGobble| 命令，检查后面的字符是否 |#1|，如果是则吞掉分隔符
% |#1| 及后面的空格。
%    \begin{macrocode}
\def\seealso@gobble#1{%
  \@ifnextchar#1%
    {\seealso@gobbleignorespaces}
%    \end{macrocode}
% 如果 |\SeealsoGobble| 后面即将输出，即为换行符或 |\SeealsoPrintList|，则决定
% 后面不再输出第一个参见列表的 |listsep|。
%    \begin{macrocode}
    {\ifseealso@activecr
       \ifx\@let@token\seealso@cr
         \seealso@gobblefirstlistseptrue
       \fi
     \else
       \ifx\@let@token\SeealsoPrintList
         \seealso@gobblefirstlistseptrue
       \fi
     \fi}}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\seealso@gobbleignorespaces}
% \changes{v1.1}{2014/04/08}{正确处理 \cs{SeealsoGobble} 在自动折行下的行为。}
% 吞掉分隔符参数 |#1|（通常是一个逗号）并忽略后面的空格。
%
% 为了处理折行时会遇到的换行符宏 |\seealso@cr|，在发现后面的参数是
% |\seealso@cr| 时就递归调用自身把 |\seealso@cr| 也清除掉。此时就好像直接把两
% 行连接起来，不再调用 |\seealso@cr| 的复杂判断流程。
%    \begin{macrocode}
\def\seealso@gobbleignorespaces#1{%
  \@ifnextchar\seealso@cr{\seealso@gobbleignorespaces}{\ignorespaces}}
%    \end{macrocode}
% \end{macro}
%
% \subsection{定义用户接口}
%
% \begin{macro}{\seealsosetup}
% \changes{v1.0}{2014/04/05}{新增格式设置。}
% \changes{v1.1}{2014/04/09}{正确处理有参数的格式。}
% 设置输出格式。可选参数是一个逗号列表：如果可选参数为空，用 |#2| 设置默认的输
% 出格式；否则对可选参数的每一项，设置参数 |#2| 的格式。为了避免因嵌套定义造
% 成使用时参数不得不使用双重 |##|，这里作了特别的展开处理。
%    \begin{macrocode}
\newcommand\seealsosetup[2][]{%
  \ifstrempty{#1}{%
    \kvsetkeys{seealso}{#2}%
  }{%
    \edef\do##1{%
      \noexpand\kvsetkeys{seealso@##1}{\unexpanded{#2}}}%
    \docsvlist{#1}}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\seenopage}
% \begin{macro}{\seealsonopage}
% 保存旧的 |\see| 与 |\seealso| 命令定义，使用 |override| 选项时可临时使用旧的
% 定义。
%    \begin{macrocode}
\let\seenopage\see
\let\seealsonopage\seealso
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\seepage}
% \begin{macro}{\seealsopage}
% 带页码输出的 |\seepage| 与 |\seealsopage|。使用 |override| 选项时可直接使用
% |\see| 与 |\seealso| 代替 |\seepage| 与 |\seealsopage|。
%
% 如果之前没有定义，这里会同时定义 |\seename| 和 |\alsoname|，与 \pkg{makeidx}
% 初始值一致。
%    \begin{macrocode}
\DeclareSeealsoMacro\seepage{see}{see}
\DeclareSeealsoMacro\seealsopage{also}{see also}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\see}
% \begin{macro}{\seealso}
% 使用 |override| 时，重定义 |\see| 与 |\seealso|。
%    \begin{macrocode}
\ifseealso@override
  \def\see{\seepage}
  \def\seealso{\seealsopage}
\fi
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \iffalse
%</package>
% \fi
%
% \Finale
\endinput
