text: """
      sphinx.domains.std
      ~~~~~~~~~~~~~~~~~~
  
      The standard domain.
  
      :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
      :license: BSD, see LICENSE for details.
  """
  
  import re
  from copy import copy
  from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
                      Tuple, Type, Union, cast)
  
  from docutils import nodes
  from docutils.nodes import Element, Node, system_message
  from docutils.parsers.rst import Directive, directives
  from docutils.statemachine import StringList
  
  from sphinx import addnodes
  from sphinx.addnodes import desc_signature, pending_xref
  from sphinx.directives import ObjectDescription
  from sphinx.domains import Domain, ObjType
  from sphinx.locale import _, __
  from sphinx.roles import XRefRole
  from sphinx.util import docname_join, logging, ws_re
  from sphinx.util.docutils import SphinxDirective
  from sphinx.util.nodes import clean_astext, make_id, make_refnode
  from sphinx.util.typing import OptionSpec, RoleFunction
  
  if TYPE_CHECKING:
      from sphinx.application import Sphinx
      from sphinx.builders import Builder
      from sphinx.environment import BuildEnvironment
  
  logger = logging.getLogger(__name__)
  
  
  # RE for option descriptions
  option_desc_re = re.compile(r'((?:/|--|-|\+)?[^\s=]+)(=?\s*.*)')
  # RE for grammar tokens
  token_re = re.compile(r'`((~?\w*:)?\w+)`', re.U)
  
  
  class GenericObject(ObjectDescription[str]):
      """
      A generic x-ref directive registered with Sphinx.add_object_type().
      """
      indextemplate: str = ''
      parse_node: Callable[["GenericObject", "BuildEnvironment", str, desc_signature], str] = None  # NOQA
  
      def handle_signature(self, sig: str, signode: desc_signature) -> str:
          if self.parse_node:
              name = self.parse_node(self.env, sig, signode)
          else:
              signode.clear()
              signode += addnodes.desc_name(sig, sig)
              # normalize whitespace like XRefRole does
              name = ws_re.sub(' ', sig)
          return name
  
      def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
          node_id = make_id(self.env, self.state.document, self.objtype, name)
          signode['ids'].append(node_id)
  
          # Assign old styled node_id not to break old hyperlinks (if possible)
          # Note: Will be removed in Sphinx-5.0 (RemovedInSphinx50Warning)
          old_node_id = self.make_old_id(name)
          if old_node_id not in self.state.document.ids and old_node_id not in signode['ids']:
              signode['ids'].append(old_node_id)
  
          self.state.document.note_explicit_target(signode)
  
          if self.indextemplate:
              colon = self.indextemplate.find(':')
              if colon != -1:
                  indextype = self.indextemplate[:colon].strip()
                  indexentry = self.indextemplate[colon + 1:].strip() % (name,)
              else:
                  indextype = 'single'
                  indexentry = self.indextemplate % (name,)
              self.indexnode['entries'].append((indextype, indexentry, node_id, '', None))
  
          std = cast(StandardDomain, self.env.get_domain('std'))
          std.note_object(self.objtype, name, node_id, location=signode)
  
      def make_old_id(self, name: str) -> str:
          """Generate old styled node_id for generic objects.
  
          .. note:: Old Styled node_id was used until Sphinx-3.0.
                    This will be removed in Sphinx-5.0.
          """
          return self.objtype + '-' + name
  
  
  class EnvVar(GenericObject):
      indextemplate = _('environment variable; %s')
  
  
  class EnvVarXRefRole(XRefRole):
      """
      Cross-referencing role for environment variables (adds an index entry).
      """
  
      def result_nodes(self, document: nodes.document, env: "BuildEnvironment", node: Element,
                       is_ref: bool) -> Tuple[List[Node], List[system_message]]:
          if not is_ref:
              return [node], []
          varname = node['reftarget']
          tgtid = 'index-%s' % env.new_serialno('index')
          indexnode = addnodes.index()
          indexnode['entries'] = [
              ('single', varname, tgtid, '', None),
              ('single', _('environment variable; %s') % varname, tgtid, '', None)
          ]
          targetnode = nodes.target('', '', ids=[tgtid])
          document.note_explicit_target(targetnode)
          return [indexnode, targetnode, node], []
  
  
  class Target(SphinxDirective):
      """
      Generic target for user-defined cross-reference types.
      """
      indextemplate = ''
  
      has_content = False
      required_arguments = 1
      optional_arguments = 0
      final_argument_whitespace = True
      option_spec: OptionSpec = {}
  
      def run(self) -> List[Node]:
          # normalize whitespace in fullname like XRefRole does
          fullname = ws_re.sub(' ', self.arguments[0].strip())
          node_id = make_id(self.env, self.state.document, self.name, fullname)
          node = nodes.target('', '', ids=[node_id])
          self.set_source_info(node)
  
          # Assign old styled node_id not to break old hyperlinks (if possible)
          # Note: Will be removed in Sphinx-5.0 (RemovedInSphinx50Warning)
          old_node_id = self.make_old_id(fullname)
          if old_node_id not in self.state.document.ids and old_node_id not in node['ids']:
              node['ids'].append(old_node_id)
  
          self.state.document.note_explicit_target(node)
          ret: List[Node] = [node]
          if self.indextemplate:
              indexentry = self.indextemplate % (fullname,)
              indextype = 'single'
              colon = indexentry.find(':')
              if colon != -1:
                  indextype = indexentry[:colon].strip()
                  indexentry = indexentry[colon + 1:].strip()
              inode = addnodes.index(entries=[(indextype, indexentry, node_id, '', None)])
              ret.insert(0, inode)
          name = self.name
          if ':' in self.name:
              _, name = self.name.split(':', 1)
  
          std = cast(StandardDomain, self.env.get_domain('std'))
          std.note_object(name, fullname, node_id, location=node)
  
          return ret
  
      def make_old_id(self, name: str) -> str:
          """Generate old styled node_id for targets.
  
          .. note:: Old Styled node_id was used until Sphinx-3.0.
                    This will be removed in Sphinx-5.0.
          """
          return self.name + '-' + name
  
  
  class Cmdoption(ObjectDescription[str]):
      """
      Description of a command-line option (.. option).
      """
  
      def handle_signature(self, sig: str, signode: desc_signature) -> str:
          """Transform an option description into RST nodes."""
          count = 0
          firstname = ''
          for potential_option in sig.split(', '):
              potential_option = potential_option.strip()
              m = option_desc_re.match(potential_option)
              if not m:
                  logger.warning(__('Malformed option description %r, should '
                                    'look like "opt", "-opt args", "--opt args", '
                                    '"/opt args" or "+opt args"'), potential_option,
                                 location=signode)
                  continue
              optname, args = m.groups()
              if optname.endswith('[') and args.endswith(']'):
                  # optional value surrounded by brackets (ex. foo[=bar])
                  optname = optname[:-1]
                  args = '[' + args
  
              if count:
                  signode += addnodes.desc_addname(', ', ', ')
              signode += addnodes.desc_name(optname, optname)
              signode += addnodes.desc_addname(args, args)
              if not count:
                  firstname = optname
                  signode['allnames'] = [optname]
              else:
                  signode['allnames'].append(optname)
              count += 1
          if not firstname:
              raise ValueError
          return firstname
  
      def add_target_and_index(self, firstname: str, sig: str, signode: desc_signature) -> None:
          currprogram = self.env.ref_context.get('std:program')
          for optname in signode.get('allnames', []):
              prefixes = ['cmdoption']
              if currprogram:
                  prefixes.append(currprogram)
              if not optname.startswith(('-', '/')):
                  prefixes.append('arg')
              prefix = '-'.join(prefixes)
              node_id = make_id(self.env, self.state.document, prefix, optname)
              signode['ids'].append(node_id)
  
              old_node_id = self.make_old_id(prefix, optname)
              if old_node_id not in self.state.document.ids and \
                 old_node_id not in signode['ids']:
                  signode['ids'].append(old_node_id)
  
          self.state.document.note_explicit_target(signode)
  
          domain = cast(StandardDomain, self.env.get_domain('std'))
          for optname in signode.get('allnames', []):
              domain.add_program_option(currprogram, optname,
                                        self.env.docname, signode['ids'][0])
  
          # create an index entry
          if currprogram:
              descr = _('%s command line option') % currprogram
          else:
              descr = _('command line option')
          for option in sig.split(', '):
              entry = '; '.join([descr, option])
              self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None))
  
      def make_old_id(self, prefix: str, optname: str) -> str:
          """Generate old styled node_id for cmdoption.
  
          .. note:: Old Styled node_id was used until Sphinx-3.0.
                    This will be removed in Sphinx-5.0.
          """
          return nodes.make_id(prefix + '-' + optname)
  
  
  class Program(SphinxDirective):
      """
      Directive to name the program for which options are documented.
      """
  
      has_content = False
      required_arguments = 1
      optional_arguments = 0
      final_argument_whitespace = True
      option_spec: OptionSpec = {}
  
      def run(self) -> List[Node]:
          program = ws_re.sub('-', self.arguments[0].strip())
          if program == 'None':
              self.env.ref_context.pop('std:program', None)
          else:
              self.env.ref_context['std:program'] = program
          return []
  
  
  class OptionXRefRole(XRefRole):
      def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
                       title: str, target: str) -> Tuple[str, str]:
          refnode['std:program'] = env.ref_context.get('std:program')
          return title, target
  
  
  def split_term_classifiers(line: str) -> List[Optional[str]]:
      # split line into a term and classifiers. if no classifier, None is used..
      parts: List[Optional[str]] = re.split(' +: +', line) + [None]
      return parts
  
  
  def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index_key: str,
                         source: str, lineno: int, node_id: str, document: nodes.document
                         ) -> nodes.term:
      # get a text-only representation of the term and register it
      # as a cross-reference target
      term = nodes.term('', '', *textnodes)
      term.source = source
      term.line = lineno
      termtext = term.astext()
  
      if node_id:
          # node_id is given from outside (mainly i18n module), use it forcedly
          term['ids'].append(node_id)
      else:
          node_id = make_id(env, document, 'term', termtext)
          term['ids'].append(node_id)
          document.note_explicit_target(term)
  
      std = cast(StandardDomain, env.get_domain('std'))
      std._note_term(termtext, node_id, location=term)
  
      # add an index entry too
      indexnode = addnodes.index()
      indexnode['entries'] = [('single', termtext, node_id, 'main', index_key)]
      indexnode.source, indexnode.line = term.source, term.line
      term.append(indexnode)
  
      return term
  
  
  class Glossary(SphinxDirective):
      """
      Directive to create a glossary with cross-reference targets for :term:
      roles.
      """
  
      has_content = True
      required_arguments = 0
      optional_arguments = 0
      final_argument_whitespace = False
      option_spec: OptionSpec = {
          'sorted': directives.flag,
      }
  
      def run(self) -> List[Node]:
          node = addnodes.glossary()
          node.document = self.state.document
          node['sorted'] = ('sorted' in self.options)
  
          # This directive implements a custom format of the reST definition list
          # that allows multiple lines of terms before the definition.  This is
          # easy to parse since we know that the contents of the glossary *must
          # be* a definition list.
  
          # first, collect single entries
          entries: List[Tuple[List[Tuple[str, str, int]], StringList]] = []
          in_definition = True
          in_comment = False
          was_empty = True
          messages: List[Node] = []
          for line, (source, lineno) in zip(self.content, self.content.items):
              # empty line -> add to last definition
              if not line:
                  if in_definition and entries:
                      entries[-1][1].append('', source, lineno)
                  was_empty = True
                  continue
              # unindented line -> a term
              if line and not line[0].isspace():
                  # enable comments
                  if line.startswith('.. '):
                      in_comment = True
                      continue
                  else:
                      in_comment = False
  
                  # first term of definition
                  if in_definition:
                      if not was_empty:
                          messages.append(self.state.reporter.warning(
                              _('glossary term must be preceded by empty line'),
                              source=source, line=lineno))
                      entries.append(([(line, source, lineno)], StringList()))
                      in_definition = False
                  # second term and following
                  else:
                      if was_empty:
                          messages.append(self.state.reporter.warning(
                              _('glossary terms must not be separated by empty lines'),
                              source=source, line=lineno))
                      if entries:
                          entries[-1][0].append((line, source, lineno))
                      else:
                          messages.append(self.state.reporter.warning(
                              _('glossary seems to be misformatted, check indentation'),
                              source=source, line=lineno))
              elif in_comment:
                  pass
              else:
                  if not in_definition:
                      # first line of definition, determines indentation
                      in_definition = True
                      indent_len = len(line) - len(line.lstrip())
                  if entries:
                      entries[-1][1].append(line[indent_len:], source, lineno)
                  else:
                      messages.append(self.state.reporter.warning(
                          _('glossary seems to be misformatted, check indentation'),
                          source=source, line=lineno))
              was_empty = False
  
          # now, parse all the entries into a big definition list
          items: List[nodes.definition_list_item] = []
          for terms, definition in entries:
              termnodes: List[Node] = []
              system_messages: List[Node] = []
              for line, source, lineno in terms:
                  parts = split_term_classifiers(line)
                  # parse the term with inline markup
                  # classifiers (parts[1:]) will not be shown on doctree
                  textnodes, sysmsg = self.state.inline_text(parts[0], lineno)
  
                  # use first classifier as a index key
                  term = make_glossary_term(self.env, textnodes, parts[1], source, lineno,
                                            node_id=None, document=self.state.document)
                  term.rawsource = line
                  system_messages.extend(sysmsg)
                  termnodes.append(term)
  
              termnodes.extend(system_messages)
  
              defnode = nodes.definition()
              if definition:
                  self.state.nested_parse(definition, definition.items[0][1],
                                          defnode)
              termnodes.append(defnode)
              items.append(nodes.definition_list_item('', *termnodes))
  
          dlist = nodes.definition_list('', *items)
          dlist['classes'].append('glossary')
          node += dlist
          return messages + [node]
  
  
  def token_xrefs(text: str, productionGroup: str = '') -> List[Node]:
      if len(productionGroup) != 0:
          productionGroup += ':'
      retnodes: List[Node] = []
      pos = 0
      for m in token_re.finditer(text):
          if m.start() > pos:
              txt = text[pos:m.start()]
              retnodes.append(nodes.Text(txt, txt))
          token = m.group(1)
          if ':' in token:
              if token[0] == '~':
                  _, title = token.split(':')
                  target = token[1:]
              elif token[0] == ':':
                  title = token[1:]
                  target = title
              else:
                  title = token
                  target = token
          else:
              title = token
              target = productionGroup + token
          refnode = pending_xref(title, reftype='token', refdomain='std',
                                 reftarget=target)
          refnode += nodes.literal(token, title, classes=['xref'])
          retnodes.append(refnode)
          pos = m.end()
      if pos < len(text):
          retnodes.append(nodes.Text(text[pos:], text[pos:]))
      return retnodes
  
  
  class ProductionList(SphinxDirective):
      """
      Directive to list grammar productions.
      """
  
      has_content = False
      required_arguments = 1
      optional_arguments = 0
      final_argument_whitespace = True
      option_spec: OptionSpec = {}
  
      def run(self) -> List[Node]:
          domain = cast(StandardDomain, self.env.get_domain('std'))
          node: Element = addnodes.productionlist()
          self.set_source_info(node)
          # The backslash handling is from ObjectDescription.get_signatures
          nl_escape_re = re.compile(r'\\\n')
          lines = nl_escape_re.sub('', self.arguments[0]).split('\n')
  
          productionGroup = ""
          i = 0
          for rule in lines:
              if i == 0 and ':' not in rule:
                  productionGroup = rule.strip()
                  continue
              i += 1
              try:
                  name, tokens = rule.split(':', 1)
              except ValueError:
                  break
              subnode = addnodes.production(rule)
              name = name.strip()
              subnode['tokenname'] = name
              if subnode['tokenname']:
                  prefix = 'grammar-token-%s' % productionGroup
                  node_id = make_id(self.env, self.state.document, prefix, name)
                  subnode['ids'].append(node_id)
  
                  # Assign old styled node_id not to break old hyperlinks (if possible)
                  # Note: Will be removed in Sphinx-5.0 (RemovedInSphinx50Warning)
                  old_node_id = self.make_old_id(name)
                  if (old_node_id not in self.state.document.ids and
                          old_node_id not in subnode['ids']):
                      subnode['ids'].append(old_node_id)
  
                  self.state.document.note_implicit_target(subnode, subnode)
  
                  if len(productionGroup) != 0:
                      objName = "%s:%s" % (productionGroup, name)
                  else:
                      objName = name
                  domain.note_object('token', objName, node_id, location=node)
              subnode.extend(token_xrefs(tokens, productionGroup))
              node.append(subnode)
          return [node]
  
      def make_old_id(self, token: str) -> str:
          """Generate old styled node_id for tokens.
  
          .. note:: Old Styled node_id was used until Sphinx-3.0.
                    This will be removed in Sphinx-5.0.
          """
          return nodes.make_id('grammar-token-' + token)
  
  
  class TokenXRefRole(XRefRole):
      def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
                       title: str, target: str) -> Tuple[str, str]:
          target = target.lstrip('~')  # a title-specific thing
          if not self.has_explicit_title and title[0] == '~':
              if ':' in title:
                  _, title = title.split(':')
              else:
                  title = title[1:]
          return title, target
  
  
  class StandardDomain(Domain):
      """
      Domain for all objects that don't fit into another domain or are added
      via the application interface.
      """
  
      name = 'std'
      label = 'Default'
  
      object_types: Dict[str, ObjType] = {
          'term': ObjType(_('glossary term'), 'term', searchprio=-1),
          'token': ObjType(_('grammar token'), 'token', searchprio=-1),
          'label': ObjType(_('reference label'), 'ref', 'keyword',
                           searchprio=-1),
          'envvar': ObjType(_('environment variable'), 'envvar'),
          'cmdoption': ObjType(_('program option'), 'option'),
          'doc': ObjType(_('document'), 'doc', searchprio=-1)
      }
  
      directives: Dict[str, Type[Directive]] = {
          'program': Program,
          'cmdoption': Cmdoption,  # old name for backwards compatibility
          'option': Cmdoption,
          'envvar': EnvVar,
          'glossary': Glossary,
          'productionlist': ProductionList,
      }
      roles: Dict[str, Union[RoleFunction, XRefRole]] = {
          'option':  OptionXRefRole(warn_dangling=True),
          'envvar':  EnvVarXRefRole(),
          # links to tokens in grammar productions
          'token':   TokenXRefRole(),
          # links to terms in glossary
          'term':    XRefRole(innernodeclass=nodes.inline,
                              warn_dangling=True),
          # links to headings or arbitrary labels
          'ref':     XRefRole(lowercase=True, innernodeclass=nodes.inline,
                              warn_dangling=True),
          # links to labels of numbered figures, tables and code-blocks
          'numref':  XRefRole(lowercase=True,
                              warn_dangling=True),
          # links to labels, without a different title
          'keyword': XRefRole(warn_dangling=True),
          # links to documents
          'doc':     XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
      }
  
      initial_data = {
          'progoptions': {},      # (program, name) -> docname, labelid
          'objects': {},          # (type, name) -> docname, labelid
          'labels': {             # labelname -> docname, labelid, sectionname
              'genindex': ('genindex', '', _('Index')),
              'modindex': ('py-modindex', '', _('Module Index')),
              'search':   ('search', '', _('Search Page')),
          },
          'anonlabels': {         # labelname -> docname, labelid
              'genindex': ('genindex', ''),
              'modindex': ('py-modindex', ''),
              'search':   ('search', ''),
          },
      }
  
      dangling_warnings = {
          'term': 'term not in glossary: %(target)s',
          'numref':  'undefined label: %(target)s',
          'keyword': 'unknown keyword: %(target)s',
          'doc': 'unknown document: %(target)s',
          'option': 'unknown option: %(target)s',
      }
  
      # node_class -> (figtype, title_getter)
      enumerable_nodes: Dict[Type[Node], Tuple[str, Optional[Callable]]] = {
          nodes.figure: ('figure', None),
          nodes.table: ('table', None),
          nodes.container: ('code-block', None),
      }
  
      def __init__(self, env: "BuildEnvironment") -> None:
          super().__init__(env)
  
          # set up enumerable nodes
          self.enumerable_nodes = copy(self.enumerable_nodes)  # create a copy for this instance
          for node, settings in env.app.registry.enumerable_nodes.items():
              self.enumerable_nodes[node] = settings
  
      def note_hyperlink_target(self, name: str, docname: str, node_id: str,
                                title: str = '') -> None:
          """Add a hyperlink target for cross reference.
  
          .. warning::
  
             This is only for internal use.  Please don't use this from your extension.
             ``document.note_explicit_target()`` or ``note_implicit_target()`` are recommended to
             add a hyperlink target to the document.
  
             This only adds a hyperlink target to the StandardDomain.  And this does not add a
             node_id to node.  Therefore, it is very fragile to calling this without
             understanding hyperlink target framework in both docutils and Sphinx.
  
          .. versionadded:: 3.0
          """
          if name in self.anonlabels and self.anonlabels[name] != (docname, node_id):
              logger.warning(__('duplicate label %s, other instance in %s'),
                             name, self.env.doc2path(self.anonlabels[name][0]))
  
          self.anonlabels[name] = (docname, node_id)
          if title:
              self.labels[name] = (docname, node_id, title)
  
      @property
      def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
          return self.data.setdefault('objects', {})  # (objtype, name) -> docname, labelid
  
      def note_object(self, objtype: str, name: str, labelid: str, location: Any = None
                      ) -> None:
          """Note a generic object for cross reference.
  
          .. versionadded:: 3.0
          """
          if (objtype, name) in self.objects:
              docname = self.objects[objtype, name][0]
              logger.warning(__('duplicate %s description of %s, other instance in %s'),
                             objtype, name, docname, location=location)
          self.objects[objtype, name] = (self.env.docname, labelid)
  
      @property
      def _terms(self) -> Dict[str, Tuple[str, str]]:
          """.. note:: Will be removed soon. internal use only."""
          return self.data.setdefault('terms', {})  # (name) -> docname, labelid
  
      def _note_term(self, term: str, labelid: str, location: Any = None) -> None:
          """Note a term for cross reference.
  
          .. note:: Will be removed soon. internal use only.
          """
          self.note_object('term', term, labelid, location)
  
          self._terms[term.lower()] = (self.env.docname, labelid)
  
      @property
      def progoptions(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
          return self.data.setdefault('progoptions', {})  # (program, name) -> docname, labelid
  
      @property
      def labels(self) -> Dict[str, Tuple[str, str, str]]:
          return self.data.setdefault('labels', {})  # labelname -> docname, labelid, sectionname
  
      @property
      def anonlabels(self) -> Dict[str, Tuple[str, str]]:
          return self.data.setdefault('anonlabels', {})  # labelname -> docname, labelid
  
      def clear_doc(self, docname: str) -> None:
          key: Any = None
          for key, (fn, _l) in list(self.progoptions.items()):
              if fn == docname:
                  del self.progoptions[key]
          for key, (fn, _l) in list(self.objects.items()):
              if fn == docname:
                  del self.objects[key]
          for key, (fn, _l) in list(self._terms.items()):
              if fn == docname:
                  del self._terms[key]
          for key, (fn, _l, _l) in list(self.labels.items()):
              if fn == docname:
                  del self.labels[key]
          for key, (fn, _l) in list(self.anonlabels.items()):
              if fn == docname:
                  del self.anonlabels[key]
  
      def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
          # XXX duplicates?
          for key, data in otherdata['progoptions'].items():
              if data[0] in docnames:
                  self.progoptions[key] = data
          for key, data in otherdata['objects'].items():
              if data[0] in docnames:
                  self.objects[key] = data
          for key, data in otherdata['terms'].items():
              if data[0] in docnames:
                  self._terms[key] = data
          for key, data in otherdata['labels'].items():
              if data[0] in docnames:
                  self.labels[key] = data
          for key, data in otherdata['anonlabels'].items():
              if data[0] in docnames:
                  self.anonlabels[key] = data
  
      def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None:  # NOQA
          for name, explicit in document.nametypes.items():
              if not explicit:
                  continue
              labelid = document.nameids[name]
              if labelid is None:
                  continue
              node = document.ids[labelid]
              if isinstance(node, nodes.target) and 'refid' in node:
                  # indirect hyperlink targets
                  node = document.ids.get(node['refid'])
                  labelid = node['names'][0]
              if (node.tagname == 'footnote' or
                      'refuri' in node or
                      node.tagname.startswith('desc_')):
                  # ignore footnote labels, labels automatically generated from a
                  # link and object descriptions
                  continue
              if name in self.labels:
                  logger.warning(__('duplicate label %s, other instance in %s'),
                                 name, env.doc2path(self.labels[name][0]),
                                 location=node)
              self.anonlabels[name] = docname, labelid
              if node.tagname == 'section':
                  title = cast(nodes.title, node[0])
                  sectname = clean_astext(title)
              elif node.tagname == 'rubric':
                  sectname = clean_astext(node)
              elif node.tagname == 'target' and len(node) > 0:
                  # inline target (ex: blah _`blah` blah)
                  sectname = clean_astext(node)
              elif self.is_enumerable_node(node):
                  sectname = self.get_numfig_title(node)
              else:
                  toctree = next(node.findall(addnodes.toctree), None)
                  if toctree and toctree.get('caption'):
                      sectname = toctree.get('caption')
                  else:
                      # anonymous-only labels
                      continue
              if sectname:
                  self.labels[name] = docname, labelid, sectname
  
      def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
          self.progoptions[program, name] = (docname, labelid)
  
      def build_reference_node(self, fromdocname: str, builder: "Builder", docname: str,
                               labelid: str, sectname: str, rolename: str, **options: Any
                               ) -> Element:
          nodeclass = options.pop('nodeclass', nodes.reference)
          newnode = nodeclass('', '', internal=True, **options)
          innernode = nodes.inline(sectname, sectname)
          if innernode.get('classes') is not None:
              innernode['classes'].append('std')
              innernode['classes'].append('std-' + rolename)
          if docname == fromdocname:
              newnode['refid'] = labelid
          else:
              # set more info in contnode; in case the
              # get_relative_uri call raises NoUri,
              # the builder will then have to resolve these
              contnode = pending_xref('')
              contnode['refdocname'] = docname
              contnode['refsectname'] = sectname
              newnode['refuri'] = builder.get_relative_uri(
                  fromdocname, docname)
              if labelid:
                  newnode['refuri'] += '#' + labelid
          newnode.append(innernode)
          return newnode
  
      def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
                       typ: str, target: str, node: pending_xref, contnode: Element
                       ) -> Optional[Element]:
          if typ == 'ref':
              resolver = self._resolve_ref_xref
          elif typ == 'numref':
              resolver = self._resolve_numref_xref
          elif typ == 'keyword':
              resolver = self._resolve_keyword_xref
          elif typ == 'doc':
              resolver = self._resolve_doc_xref
          elif typ == 'option':
              resolver = self._resolve_option_xref
          elif typ == 'term':
              resolver = self._resolve_term_xref
          else:
              resolver = self._resolve_obj_xref
  
          return resolver(env, fromdocname, builder, typ, target, node, contnode)
  
      def _resolve_ref_xref(self, env: "BuildEnvironment", fromdocname: str,
                            builder: "Builder", typ: str, target: str, node: pending_xref,
                            contnode: Element) -> Optional[Element]:
          if node['refexplicit']:
              # reference to anonymous label; the reference uses
              # the supplied link caption
              docname, labelid = self.anonlabels.get(target, ('', ''))
              sectname = node.astext()
          else:
              # reference to named label; the final node will
              # contain the section name after the label
              docname, labelid, sectname = self.labels.get(target, ('', '', ''))
          if not docname:
              return None
  
          return self.build_reference_node(fromdocname, builder,
                                           docname, labelid, sectname, 'ref')
  
      def _resolve_numref_xref(self, env: "BuildEnvironment", fromdocname: str,
                               builder: "Builder", typ: str, target: str,
                               node: pending_xref, contnode: Element) -> Optional[Element]:
          if target in self.labels:
              docname, labelid, figname = self.labels.get(target, ('', '', ''))
          else:
              docname, labelid = self.anonlabels.get(target, ('', ''))
              figname = None
  
          if not docname:
              return None
  
          target_node = env.get_doctree(docname).ids.get(labelid)
          figtype = self.get_enumerable_node_type(target_node)
          if figtype is None:
              return None
  
          if figtype != 'section' and env.config.numfig is False:
              logger.warning(__('numfig is disabled. :numref: is ignored.'), location=node)
              return contnode
  
          try:
              fignumber = self.get_fignumber(env, builder, figtype, docname, target_node)
              if fignumber is None:
                  return contnode
          except ValueError:
              logger.warning(__("Failed to create a cross reference. Any number is not "
                                "assigned: %s"),
                             labelid, location=node)
              return contnode
  
          try:
              if node['refexplicit']:
                  title = contnode.astext()
              else:
                  title = env.config.numfig_format.get(figtype, '')
  
              if figname is None and '{name}' in title:
                  logger.warning(__('the link has no caption: %s'), title, location=node)
                  return contnode
              else:
                  fignum = '.'.join(map(str, fignumber))
                  if '{name}' in title or 'number' in title:
                      # new style format (cf. "Fig.{number}")
                      if figname:
                          newtitle = title.format(name=figname, number=fignum)
                      else:
                          newtitle = title.format(number=fignum)
                  else:
                      # old style format (cf. "Fig.%s")
                      newtitle = title % fignum
          except KeyError as exc:
              logger.warning(__('invalid numfig_format: %s (%r)'), title, exc, location=node)
              return contnode
          except TypeError:
              logger.warning(__('invalid numfig_format: %s'), title, location=node)
              return contnode
  
          return self.build_reference_node(fromdocname, builder,
                                           docname, labelid, newtitle, 'numref',
                                           nodeclass=addnodes.number_reference,
                                           title=title)
  
      def _resolve_keyword_xref(self, env: "BuildEnvironment", fromdocname: str,
                                builder: "Builder", typ: str, target: str,
                                node: pending_xref, contnode: Element) -> Optional[Element]:
          # keywords are oddballs: they are referenced by named labels
          docname, labelid, _ = self.labels.get(target, ('', '', ''))
          if not docname:
              return None
          return make_refnode(builder, fromdocname, docname,
                              labelid, contnode)
  
      def _resolve_doc_xref(self, env: "BuildEnvironment", fromdocname: str,
                            builder: "Builder", typ: str, target: str,
                            node: pending_xref, contnode: Element) -> Optional[Element]:
          # directly reference to document by source name; can be absolute or relative
          refdoc = node.get('refdoc', fromdocname)
          docname = docname_join(refdoc, node['reftarget'])
          if docname not in env.all_docs:
              return None
          else:
              if node['refexplicit']:
                  # reference with explicit title
                  caption = node.astext()
              else:
                  caption = clean_astext(env.titles[docname])
              innernode = nodes.inline(caption, caption, classes=['doc'])
              return make_refnode(builder, fromdocname, docname, None, innernode)
  
      def _resolve_option_xref(self, env: "BuildEnvironment", fromdocname: str,
                               builder: "Builder", typ: str, target: str,
                               node: pending_xref, contnode: Element) -> Optional[Element]:
          progname = node.get('std:program')
          target = target.strip()
          docname, labelid = self.progoptions.get((progname, target), ('', ''))
          if not docname:
              commands = []
              while ws_re.search(target):
                  subcommand, target = ws_re.split(target, 1)
                  commands.append(subcommand)
                  progname = "-".join(commands)
  
                  docname, labelid = self.progoptions.get((progname, target), ('', ''))
                  if docname:
                      break
              else:
                  return None
  
          return make_refnode(builder, fromdocname, docname,
                              labelid, contnode)
  
      def _resolve_term_xref(self, env: "BuildEnvironment", fromdocname: str,
                             builder: "Builder", typ: str, target: str,
                             node: pending_xref, contnode: Element) -> Element:
          result = self._resolve_obj_xref(env, fromdocname, builder, typ,
                                          target, node, contnode)
          if result:
              return result
          else:
              # fallback to case insentive match
              if target.lower() in self._terms:
                  docname, labelid = self._terms[target.lower()]
                  return make_refnode(builder, fromdocname, docname, labelid, contnode)
              else:
                  return None
  
      def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str,
                            builder: "Builder", typ: str, target: str,
                            node: pending_xref, contnode: Element) -> Optional[Element]:
          objtypes = self.objtypes_for_role(typ) or []
          for objtype in objtypes:
              if (objtype, target) in self.objects:
                  docname, labelid = self.objects[objtype, target]
                  break
          else:
              docname, labelid = '', ''
          if not docname:
              return None
          return make_refnode(builder, fromdocname, docname,
                              labelid, contnode)
  
      def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str,
                           builder: "Builder", target: str, node: pending_xref,
                           contnode: Element) -> List[Tuple[str, Element]]:
          results: List[Tuple[str, Element]] = []
          ltarget = target.lower()  # :ref: lowercases its target automatically
          for role in ('ref', 'option'):  # do not try "keyword"
              res = self.resolve_xref(env, fromdocname, builder, role,
                                      ltarget if role == 'ref' else target,
                                      node, contnode)
              if res:
                  results.append(('std:' + role, res))
          # all others
          for objtype in self.object_types:
              key = (objtype, target)
              if objtype == 'term':
                  key = (objtype, ltarget)
              if key in self.objects:
                  docname, labelid = self.objects[key]
                  results.append(('std:' + self.role_for_objtype(objtype),
                                  make_refnode(builder, fromdocname, docname,
                                               labelid, contnode)))
          return results
  
      def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
          # handle the special 'doc' reference here
          for doc in self.env.all_docs:
              yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
          for (prog, option), info in self.progoptions.items():
              if prog:
                  fullname = ".".join([prog, option])
                  yield (fullname, fullname, 'cmdoption', info[0], info[1], 1)
              else:
                  yield (option, option, 'cmdoption', info[0], info[1], 1)
          for (type, name), info in self.objects.items():
              yield (name, name, type, info[0], info[1],
                     self.object_types[type].attrs['searchprio'])
          for name, (docname, labelid, sectionname) in self.labels.items():
              yield (name, sectionname, 'label', docname, labelid, -1)
          # add anonymous-only labels as well
          non_anon_labels = set(self.labels)
          for name, (docname, labelid) in self.anonlabels.items():
              if name not in non_anon_labels:
                  yield (name, name, 'label', docname, labelid, -1)
  
      def get_type_name(self, type: ObjType, primary: bool = False) -> str:
          # never prepend "Default"
          return type.lname
  
      def is_enumerable_node(self, node: Node) -> bool:
          return node.__class__ in self.enumerable_nodes
  
      def get_numfig_title(self, node: Node) -> Optional[str]:
          """Get the title of enumerable nodes to refer them using its title"""
          if self.is_enumerable_node(node):
              elem = cast(Element, node)
              _, title_getter = self.enumerable_nodes.get(elem.__class__, (None, None))
              if title_getter:
                  return title_getter(elem)
              else:
                  for subnode in elem:
                      if isinstance(subnode, (nodes.caption, nodes.title)):
                          return clean_astext(subnode)
  
          return None
  
      def get_enumerable_node_type(self, node: Node) -> Optional[str]:
          """Get type of enumerable nodes."""
          def has_child(node: Element, cls: Type) -> bool:
              return any(isinstance(child, cls) for child in node)
  
          if isinstance(node, nodes.section):
              return 'section'
          elif (isinstance(node, nodes.container) and
                'literal_block' in node and
                has_child(node, nodes.literal_block)):
              # given node is a code-block having caption
              return 'code-block'
          else:
              figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
              return figtype
  
      def get_fignumber(self, env: "BuildEnvironment", builder: "Builder",
                        figtype: str, docname: str, target_node: Element) -> Tuple[int, ...]:
          if figtype == 'section':
              if builder.name == 'latex':
                  return tuple()
              elif docname not in env.toc_secnumbers:
                  raise ValueError  # no number assigned
              else:
                  anchorname = '#' + target_node['ids'][0]
                  if anchorname not in env.toc_secnumbers[docname]:
                      # try first heading which has no anchor
                      return env.toc_secnumbers[docname].get('')
                  else:
                      return env.toc_secnumbers[docname].get(anchorname)
          else:
              try:
                  figure_id = target_node['ids'][0]
                  return env.toc_fignumbers[docname][figtype][figure_id]
              except (KeyError, IndexError) as exc:
                  # target_node is found, but fignumber is not assigned.
                  # Maybe it is defined in orphaned document.
                  raise ValueError from exc
  
      def get_full_qualified_name(self, node: Element) -> Optional[str]:
          if node.get('reftype') == 'option':
              progname = node.get('std:program')
              command = ws_re.split(node.get('reftarget'))
              if progname:
                  command.insert(0, progname)
              option = command.pop()
              if command:
                  return '.'.join(['-'.join(command), option])
              else:
                  return None
          else:
              return None
  
  
  def warn_missing_reference(app: "Sphinx", domain: Domain, node: pending_xref
                             ) -> Optional[bool]:
      if (domain and domain.name != 'std') or node['reftype'] != 'ref':
          return None
      else:
          target = node['reftarget']
          if target not in domain.anonlabels:  # type: ignore
              msg = __('undefined label: %s')
          else:
              msg = __('Failed to create a cross reference. A title or caption not found: %s')
  
          logger.warning(msg % target, location=node, type='ref', subtype=node['reftype'])
          return True
  
  
  def setup(app: "Sphinx") -> Dict[str, Any]:
      app.add_domain(StandardDomain)
      app.connect('warn-missing-reference', warn_missing_reference)
  
      return {
          'version': 'builtin',
          'env_version': 2,
          'parallel_read_safe': True,
          'parallel_write_safe': True,
      }
  