class CondaDomain(Domain): """Domain for Conda Packages""" name = "conda" label = "Conda" object_types = { # ObjType(name, *roles, **attrs) 'recipe': ObjType('recipe', 'recipe'), 'package': ObjType('package', 'package', 'depends'), } directives = { 'recipe': CondaRecipe, 'package': CondaPackage, } roles = { 'recipe': XRefRole(), 'package': XRefRole(), } initial_data = { 'objects': {}, #: (type, name) -> docname, labelid 'backrefs': {} #: package_name -> docname, package_name } indices = [RecipeIndex] def clear_doc(self, docname: str): """Remove traces of a document in the domain-specific inventories.""" if 'objects' not in self.data: return to_remove = [ key for (key, (stored_docname, _)) in self.data['objects'].items() if docname == stored_docname ] for key in to_remove: del self.data['objects'][key] def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder, target, node, contnode): """Resolve references from "any" role.""" res = self.resolve_xref(env, fromdocname, builder, 'package', target, node, contnode) if res: return [('conda:package', res)] else: return [] def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder, role, target, node, contnode): """Resolve the ``pending_xref`` **node** with the given **role** and **target**.""" for objtype in self.objtypes_for_role(role) or []: if (objtype, target) in self.data['objects']: node = make_refnode(builder, fromdocname, self.data['objects'][objtype, target][0], self.data['objects'][objtype, target][1], contnode, target + ' ' + objtype) node.set_class('conda-package') return node if objtype == "package": for channel, urlformat in env.app.config.bioconda_other_channels.items( ): if RepoData().get_package_data(channels=channel, name=target): uri = urlformat.format(target) node = nodes.reference('', '', internal=False, refuri=uri, classes=[channel]) node += contnode return node return None # triggers missing-reference def get_objects(self): """Yields "object description" 5-tuples ``name``: fully qualified name ``dispname``: name to display when searching/linking ``type``: object type, a key in ``self.object_types`` ``docname``: the document where it is to be found ``anchor``: the anchor name for the object ``priority``: search priority - 1: default priority (placed before full-text matches) - 0: object is important (placed before default-priority objects) - 2: object is unimportant (placed after full-text matches) - -1: object should not show up in search at all """ for (typ, name), (docname, ref) in self.data['objects'].items(): dispname = "Recipe '{}'".format(name) yield name, dispname, typ, docname, ref, 1 def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: """Merge in data regarding *docnames* from a different domaindata inventory (coming from a subprocess in parallel builds). """ for (typ, name), (docname, ref) in otherdata['objects'].items(): if docname in docnames: self.data['objects'][typ, name] = (docname, ref)
class PythonDomain(Domain): """Python language domain.""" name = 'py' label = 'Python' object_types = { 'function': ObjType(_('function'), 'func', 'obj'), 'data': ObjType(_('data'), 'data', 'obj'), 'class': ObjType(_('class'), 'class', 'exc', 'obj'), 'exception': ObjType(_('exception'), 'exc', 'class', 'obj'), 'method': ObjType(_('method'), 'meth', 'obj'), 'classmethod': ObjType(_('class method'), 'meth', 'obj'), 'staticmethod': ObjType(_('static method'), 'meth', 'obj'), 'attribute': ObjType(_('attribute'), 'attr', 'obj'), 'module': ObjType(_('module'), 'mod', 'obj'), } # type: Dict[str, ObjType] directives = { 'function': PyModulelevel, 'data': PyModulelevel, 'class': PyClasslike, 'exception': PyClasslike, 'method': PyClassmember, 'classmethod': PyClassmember, 'staticmethod': PyClassmember, 'attribute': PyClassmember, 'module': PyModule, 'currentmodule': PyCurrentModule, 'decorator': PyDecoratorFunction, 'decoratormethod': PyDecoratorMethod, } roles = { 'data': PyXRefRole(), 'exc': PyXRefRole(), 'func': PyXRefRole(fix_parens=True), 'class': PyXRefRole(), 'const': PyXRefRole(), 'attr': PyXRefRole(), 'meth': PyXRefRole(fix_parens=True), 'mod': PyXRefRole(), 'obj': PyXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # modname -> docname, synopsis, platform, deprecated } # type: Dict[str, Dict[str, Tuple[Any]]] indices = [ PythonModuleIndex, ] @property def objects(self): # type: () -> Dict[str, Tuple[str, str]] return self.data.setdefault('objects', {}) # fullname -> docname, objtype def note_object(self, name, objtype, location=None): # type: (str, str, Any) -> None """Note a python object for cross reference. .. versionadded:: 2.1 """ if name in self.objects: docname = self.objects[name][0] logger.warning(__('duplicate object description of %s, ' 'other instance in %s, use :noindex: for one of them'), name, docname, location=location) self.objects[name] = (self.env.docname, objtype) @property def modules(self): # type: () -> Dict[str, Tuple[str, str, str, bool]] return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA def note_module(self, name, synopsis, platform, deprecated): # type: (str, str, str, bool) -> None """Note a python module for cross reference. .. versionadded:: 2.1 """ self.modules[name] = (self.env.docname, synopsis, platform, deprecated) def clear_doc(self, docname): # type: (str) -> None for fullname, (fn, _l) in list(self.objects.items()): if fn == docname: del self.objects[fullname] for modname, (fn, _x, _x, _y) in list(self.modules.items()): if fn == docname: del self.modules[modname] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None # XXX check duplicates? for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.objects[fullname] = (fn, objtype) for modname, data in otherdata['modules'].items(): if data[0] in docnames: self.modules[modname] = data def find_obj(self, env, modname, classname, name, type, searchmode=0): # type: (BuildEnvironment, str, str, str, str, int) -> List[Tuple[str, Any]] """Find a Python object for "name", perhaps using the given module and/or classname. Returns a list of (name, object entry) tuples. """ # skip parens if name[-2:] == '()': name = name[:-2] if not name: return [] matches = [] # type: List[Tuple[str, Any]] newname = None if searchmode == 1: if type is None: objtypes = list(self.object_types) else: objtypes = self.objtypes_for_role(type) if objtypes is not None: if modname and classname: fullname = modname + '.' + classname + '.' + name if fullname in self.objects and self.objects[fullname][1] in objtypes: newname = fullname if not newname: if modname and modname + '.' + name in self.objects and \ self.objects[modname + '.' + name][1] in objtypes: newname = modname + '.' + name elif name in self.objects and self.objects[name][1] in objtypes: newname = name else: # "fuzzy" searching mode searchname = '.' + name matches = [(oname, self.objects[oname]) for oname in self.objects if oname.endswith(searchname) and self.objects[oname][1] in objtypes] else: # NOTE: searching for exact match, object type is not considered if name in self.objects: newname = name elif type == 'mod': # only exact matches allowed for modules return [] elif classname and classname + '.' + name in self.objects: newname = classname + '.' + name elif modname and modname + '.' + name in self.objects: newname = modname + '.' + name elif modname and classname and \ modname + '.' + classname + '.' + name in self.objects: newname = modname + '.' + classname + '.' + name # special case: builtin exceptions have module "exceptions" set elif type == 'exc' and '.' not in name and \ 'exceptions.' + name in self.objects: newname = 'exceptions.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ 'object.' + name in self.objects: newname = 'object.' + name if newname is not None: matches.append((newname, self.objects[newname])) return matches def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA modname = node.get('py:module') clsname = node.get('py:class') searchmode = node.hasattr('refspecific') and 1 or 0 matches = self.find_obj(env, modname, clsname, target, type, searchmode) if not matches: return None elif len(matches) > 1: logger.warning(__('more than one target found for cross-reference %r: %s'), target, ', '.join(match[0] for match in matches), type='ref', subtype='python', location=node) name, obj = matches[0] if obj[1] == 'module': return self._make_module_refnode(builder, fromdocname, name, contnode) else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA modname = node.get('py:module') clsname = node.get('py:class') results = [] # type: List[Tuple[str, nodes.Element]] # always search in "refspecific" mode with the :any: role matches = self.find_obj(env, modname, clsname, target, None, 1) for name, obj in matches: if obj[1] == 'module': results.append(('py:mod', self._make_module_refnode(builder, fromdocname, name, contnode))) else: results.append(('py:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name, contnode, name))) return results def _make_module_refnode(self, builder, fromdocname, name, contnode): # type: (Builder, str, str, nodes.Node) -> nodes.Element # get additional info for modules docname, synopsis, platform, deprecated = self.modules[name] title = name if synopsis: title += ': ' + synopsis if deprecated: title += _(' (deprecated)') if platform: title += ' (' + platform + ')' return make_refnode(builder, fromdocname, docname, 'module-' + name, contnode, title) def get_objects(self): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] for modname, info in self.modules.items(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in self.objects.items(): if type != 'module': # modules are already handled yield (refname, refname, type, docname, refname, 1) def get_full_qualified_name(self, node): # type: (nodes.Element) -> str modname = node.get('py:module') clsname = node.get('py:class') target = node.get('reftarget') if target is None: return None else: return '.'.join(filter(None, [modname, clsname, target]))
class CMakeDomain(Domain): """CMake domain.""" name = 'cmake' label = 'CMake' object_types = { 'command': ObjType('command', 'command'), 'generator': ObjType('generator', 'generator'), 'variable': ObjType('variable', 'variable'), 'module': ObjType('module', 'module'), 'policy': ObjType('policy', 'policy'), 'prop_cache': ObjType('prop_cache', 'prop_cache'), 'prop_dir': ObjType('prop_dir', 'prop_dir'), 'prop_gbl': ObjType('prop_gbl', 'prop_gbl'), 'prop_inst': ObjType('prop_inst', 'prop_inst'), 'prop_sf': ObjType('prop_sf', 'prop_sf'), 'prop_test': ObjType('prop_test', 'prop_test'), 'prop_tgt': ObjType('prop_tgt', 'prop_tgt'), 'manual': ObjType('manual', 'manual'), } directives = { 'command': CMakeObject, 'variable': CMakeObject, # Other object types cannot be created except by the CMakeTransform # 'generator': CMakeObject, # 'module': CMakeObject, # 'policy': CMakeObject, # 'prop_cache': CMakeObject, # 'prop_dir': CMakeObject, # 'prop_gbl': CMakeObject, # 'prop_inst': CMakeObject, # 'prop_sf': CMakeObject, # 'prop_test': CMakeObject, # 'prop_tgt': CMakeObject, # 'manual': CMakeObject, } roles = { 'command': CMakeXRefRole(fix_parens=True, lowercase=True), 'generator': CMakeXRefRole(), 'variable': CMakeXRefRole(), 'module': CMakeXRefRole(), 'policy': CMakeXRefRole(), 'prop_cache': CMakeXRefRole(), 'prop_dir': CMakeXRefRole(), 'prop_gbl': CMakeXRefRole(), 'prop_inst': CMakeXRefRole(), 'prop_sf': CMakeXRefRole(), 'prop_test': CMakeXRefRole(), 'prop_tgt': CMakeXRefRole(), 'manual': CMakeXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): to_clear = set() for fullname, (fn, _) in self.data['objects'].items(): if fn == docname: to_clear.add(fullname) for fullname in to_clear: del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): targetid = '%s:%s' % (typ, target) obj = self.data['objects'].get(targetid) if obj is None: # TODO: warn somehow? return None return make_refnode(builder, fromdocname, obj[0], targetid, contnode, target) def get_objects(self): for refname, (docname, type) in self.data['objects'].items(): yield (refname, refname, type, docname, refname, 1)
class ReSTDomain(Domain): """ReStructuredText domain.""" name = 'rst' label = 'reStructuredText' object_types = { 'directive': ObjType(_('directive'), 'dir'), 'role': ObjType(_('role'), 'role'), } directives = { 'directive': ReSTDirective, 'role': ReSTRole, } roles = { 'dir': XRefRole(), 'role': XRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } # type: Dict[str, Dict[Tuple[str, str], str]] @property def objects(self): # type: () -> Dict[Tuple[str, str], str] return self.data.setdefault('objects', {}) # (objtype, fullname) -> docname def note_object(self, objtype, name, location=None): # type: (str, str, Any) -> None if (objtype, name) in self.objects: docname = self.objects[objtype, name] logger.warning( __('duplicate description of %s %s, other instance in %s') % (objtype, name, docname), location=location) self.objects[objtype, name] = self.env.docname def clear_doc(self, docname): # type: (str) -> None for (typ, name), doc in list(self.objects.items()): if doc == docname: del self.objects[typ, name] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None # XXX check duplicates for (typ, name), doc in otherdata['objects'].items(): if doc in docnames: self.objects[typ, name] = doc def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA objtypes = self.objtypes_for_role(typ) for objtype in objtypes: todocname = self.objects.get((objtype, target)) if todocname: return make_refnode(builder, fromdocname, todocname, objtype + '-' + target, contnode, target + ' ' + objtype) return None def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA results = [] # type: List[Tuple[str, nodes.Element]] for objtype in self.object_types: todocname = self.objects.get((objtype, target)) if todocname: results.append(('rst:' + self.role_for_objtype(objtype), make_refnode(builder, fromdocname, todocname, objtype + '-' + target, contnode, target + ' ' + objtype))) return results def get_objects(self): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] for (typ, name), docname in self.data['objects'].items(): yield name, name, typ, docname, typ + '-' + name, 1
checker.properties['flycheck-config-file'] = symbol symbol.properties['buffer-local'] = True doc = "Configuration file for `{0}'".format(checker.name) symbol.properties['variable-documentation'] = doc register_interpreter_function('flycheck-define-checker', flycheck_define_checker) register_interpreter_function('flycheck-def-option-var', flycheck_def_option_var) register_interpreter_function('flycheck-def-args-var', flycheck_def_args_var) register_interpreter_function('flycheck-def-config-file-var', flycheck_def_config_file_var) # Register our object type EmacsLispDomain.object_types['flyc-checker'] = ObjType( 'Flycheck syntax checker', 'flyc-checker', scope='flycheck-checker') class FlycheckChecker(EmacsLispSymbol): docstring_property = 'flycheck-documentation' def make_checker_chaining(self): symbol = self.lookup_auto_symbol() if not symbol: return next_checkers = symbol.properties.get('flycheck-next-checkers') if not next_checkers: return title = nodes.title('', 'Chained syntax checkers') intro = nodes.paragraph() intro += nodes.Text('The following syntax checkers are ')
class CDomain(Domain): """C language domain.""" name = 'c' label = 'C' object_types = { 'function': ObjType(l_('function'), 'func'), 'member': ObjType(l_('member'), 'member'), 'macro': ObjType(l_('macro'), 'macro'), 'type': ObjType(l_('type'), 'type'), 'var': ObjType(l_('variable'), 'data'), } directives = { 'function': CObject, 'member': CObject, 'macro': CObject, 'type': CObject, 'var': CObject, } roles = { 'func': CXRefRole(fix_parens=True), 'member': CXRefRole(), 'macro': CXRefRole(), 'data': CXRefRole(), 'type': CXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } # type: Dict[unicode, Dict[unicode, Tuple[unicode, Any]]] def clear_doc(self, docname): # type: (unicode) -> None for fullname, (fn, _l) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def merge_domaindata(self, docnames, otherdata): # type: (List[unicode], Dict) -> None # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA # strip pointer asterisk target = target.rstrip(' *') # becase TypedField can generate xrefs if target in CObject.stopwords: return contnode if target not in self.data['objects']: return None obj = self.data['objects'][target] return make_refnode(builder, fromdocname, obj[0], 'c.' + target, contnode, target) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, nodes.Node, nodes.Node) -> List[Tuple[unicode, nodes.Node]] # NOQA # strip pointer asterisk target = target.rstrip(' *') if target not in self.data['objects']: return [] obj = self.data['objects'][target] return [('c:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], 'c.' + target, contnode, target))] def get_objects(self): # type: () -> Iterator[Tuple[unicode, unicode, unicode, unicode, unicode, int]] for refname, (docname, type) in list(self.data['objects'].items()): yield (refname, refname, type, docname, 'c.' + refname, 1)
class CitationDomain(Domain): name = "cite" label = "citation" object_types = { 'citation': ObjType(l_('citation'), *ROLES, searchprio=-1), } directives = { 'conf': CitationConfDirective, 'refs': CitationReferencesDirective } roles = dict([(r, CitationXRefRole()) for r in ROLES]) initial_data = { 'keys': OrderedSet(), # Holds cite-keys in order of reference 'conf': DEFAULT_CONF, 'refdocs': {} } def __init__(self, env): super(CitationDomain, self).__init__(env) # Update conf env.domaindata['cite']['conf'].update(env.config.natbib) # TODO: warn if citations can't parse bibtex file self.citations = Citations(env) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): refdoc = env.domaindata['cite'].get('refdoc') if not refdoc: env.warn( fromdocname, 'no `refs` directive found; citations will have dead links', node.line) refuri = '' else: refuri = builder.get_relative_uri(fromdocname, refdoc) for nd in node.children: if isinstance(nd, nodes.pending): nd.details['refs'] = [] if builder.name == 'latex': cite_keys = nd.details['keys'] cite_keys = ','.join(cite_keys) cite_node = nodes.citation_reference(cite_keys, cite_keys) return cite_node for key in nd.details.pop('keys'): ref = self.citations.get(key) if ref is None: continue nd.details['refs'].append(ref) transform = nd.transform(**nd.details) node = transform.cite( typ, refuri, global_keys=env.domaindata['cite']['keys']) return node
class CSharpDomain(Domain): """ C# domain """ name = 'csharp' label = 'C#' object_types = { 'class': ObjType(l_('class'), 'type'), 'method': ObjType(l_('method'), 'meth'), 'property': ObjType(l_('property'), 'prop'), 'enum': ObjType(l_('enum'), 'type'), 'value': ObjType(l_('value'), 'enum'), 'attribute': ObjType(l_('attribute'), 'attr'), 'indexer': ObjType(l_('indexer'), 'idxr'), } directives = { 'namespace': CSharpCurrentNamespace, 'class': CSharpClass, 'method': CSharpMethod, 'property': CSharpProperty, 'enum': CSharpEnum, 'value': CSharpEnumValue, 'attribute': CSharpAttribute, 'indexer': CSharpIndexer, } roles = { 'type': CSharpXRefRole(), 'meth': CSharpXRefRole(), 'prop': CSharpXRefRole(), 'enum': CSharpXRefRole(), 'attr': CSharpXRefRole(), 'idxr': CSharpXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for (typ, name), doc in self.data['objects'].items(): if doc == docname: del self.data['objects'][typ, name] def resolve_xref(self, _, fromdocname, builder, typ, target, node, contnode): targets = [target] if node['csharp:parent'] is not None: parts = node['csharp:parent'].split('.') while parts: targets.append('.'.join(parts) + '.' + target) parts = parts[:-1] objects = self.data['objects'] objtypes = self.objtypes_for_role(typ) for tgt in targets: for objtype in objtypes: if (objtype, tgt) in objects: return make_refnode(builder, fromdocname, objects[objtype, tgt], objtype + '-' + tgt, contnode, tgt + ' ' + objtype) for tgt in targets: ref = get_msdn_ref(tgt) if ref is not None: return ref def get_objects(self): for (typ, name), docname in self.data['objects'].items(): yield name, name, typ, docname, typ + '-' + name, 1 def merge_domaindata(self, docnames, otherdata): # TODO: implement to allow parallel builds raise NotImplementedError def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): raise NotImplementedError
class CLDomain(Domain): """CL language domain.""" name = 'cl' label = 'Common Lisp' object_types = { 'package': ObjType(_('package'), 'package'), 'function': ObjType(_('function'), 'function'), 'macro': ObjType(_('macro'), 'macro'), 'variable': ObjType(_('variable'), 'variable'), 'type': ObjType(_('type'), 'type'), 'generic': ObjType(_('generic'), 'generic'), 'method': ObjType(_('method'), 'method'), } directives = { 'package': CLCurrentPackage, 'function': CLsExp, 'generic': CLGeneric, 'macro': CLsExp, 'variable': CLsExp, 'type': CLsExp, 'method': CLMethod, } roles = { 'symbol': CLXRefRole(), 'function': CLXRefRole(), 'generic': CLXRefRole(), 'macro': CLXRefRole(), 'variable': CLXRefRole(), 'type': CLXRefRole(), 'method': CLXRefRole(), } initial_data = { 'symbols': {}, 'methods': {}, } def clear_doc(self, docname): to_remove = [] for fullname, docs in self.data['symbols'].items(): for (fn, _) in docs: if fn == docname: to_remove.append(fullname) for fullname in to_remove: del self.data['symbols'][fullname] def find_obj(self, env, name): """Find a Lisp symbol for "name", perhaps using the given package Return a list of (name, object entry) tuples. """ symbols = self.data['symbols'] name = name.lower() if ":" in name: if name in symbols: return [(name, symbols[name])] else: def filter_symbols(symbol): symbol = symbol[0] if name == symbol: return True if ":" in symbol: symbol = symbol.split(":")[1] if name == symbol: return True return False return filter(filter_symbols, symbols.items()) def find_method(self, env, name, node): """Find a Lisp symbol for "name", perhaps using the given package Return a list of (name, object entry) tuples. """ methods = self.data['methods'] name = name.lower() sexp = name.split(" ") generic = sexp[0] specializer = " ".join(sexp[1:]) if generic in methods: if specializer in methods[generic]: return [methods[generic][specializer]] else: env.warn_node('can\'t find method %s' % (name), node) else: env.warn_node('can\'t find generic %s' % (name), node) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if " " in target: matches = self.find_method(env, target.upper(), node) else: matches = self.find_obj(env, target.upper()) if not matches: return None elif len(matches) > 1: env.warn_node( 'more than one target found for cross-reference ' '%r: %s' % (target, ', '.join(match[0] for match in matches)), node) # TODO (RS) this just chooses the first symbol, instead every # symbol should be presented. if " " in target: sexp = target.split(" ") generic = sexp[0].lower() specializer = " ".join(sexp[1:]) name = generic filename = matches[0][0] # the first filename link = "method" + ":" + generic + "(" + specializer + ")" else: name = matches[0][0] # the symbol name filename = matches[0][1][0][0] # the first filename type = matches[0][1][0][1] # the first type link = type + ":" + name return make_refnode(builder, fromdocname, filename, link, contnode, name) def get_symbols(self): for refname, docs in self.data['symbols'].iteritems(): for (docname, type) in docs: yield (refname, refname, type, docname, refname, 1)
class HTTPDomain(Domain): """HTTP language domain.""" name = 'http' label = 'HTTP' object_types = { 'method': ObjType(l_('method'), 'method'), 'response': ObjType(l_('response'), 'response'), } directives = { 'method': HTTPMethod, 'response': HTTPResponse, } roles = { 'method': XRefRole(), 'response': XRefRole(), } initial_data = { 'method': {}, # name -> docname, sig, title, method 'response': {}, # name -> docname, sig, title } def clear_doc(self, docname): """Remove traces of a document from self.data.""" for typ in self.initial_data: for name, entry in self.data[typ].items(): if entry[0] == docname: del self.data[typ][name] def find_xref(self, env, typ, target): """Returns a self.data entry for *target*, according to *typ*.""" try: return self.data[typ][target] except KeyError: return None def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): """ Resolve the ``pending_xref`` *node* with the given *typ* and *target*. Returns a new reference node, to replace the xref node. If no resolution can be found, returns None. """ match = self.find_xref(env, typ, target) if match: docname = match[0] sig = match[1] title = match[2] # Coerce contnode into the right nodetype nodetype = type(contnode) if issubclass(nodetype, literal): nodetype = self.directives[typ].nodetype # Override contnode with title, unless it has been manually # overridden in the text. if contnode.astext() == target: contnode = nodetype(title, title) else: child = contnode.children[0] contnode = nodetype(child, child) # Return the new reference node return make_refnode(builder, fromdocname, docname, typ + '-' + target, contnode, sig) def get_objects(self): """ Return an iterable of "object descriptions", which are tuples with five items: * `name` -- fully qualified name * `dispname` -- name to display when searching/linking * `type` -- object type, a key in ``self.object_types`` * `docname` -- the document where it is to be found * `anchor` -- the anchor name for the object * `priority` -- how "important" the object is (determines placement in search results) - 1: default priority (placed before full-text matches) - 0: object is important (placed before default-priority objects) - 2: object is unimportant (placed after full-text matches) - -1: object should not show up in search at all """ # Method descriptions for typ in self.initial_data: for name, entry in self.data[typ].iteritems(): docname = entry[0] yield(name, name, typ, docname, typ + '-' + name, 0)
class JavaScriptDomain(Domain): """JavaScript language domain.""" name = 'js' label = 'JavaScript' # if you add a new object type make sure to edit JSObject.get_index_string object_types = { 'function': ObjType(l_('function'), 'func'), 'class': ObjType(l_('class'), 'class'), 'data': ObjType(l_('data'), 'data'), 'attribute': ObjType(l_('attribute'), 'attr'), } directives = { 'function': JSCallable, 'class': JSConstructor, 'data': JSObject, 'attribute': JSObject, } roles = { 'func': JSXRefRole(fix_parens=True), 'class': JSXRefRole(fix_parens=True), 'data': JSXRefRole(), 'attr': JSXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for fullname, (fn, _l) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def merge_domaindata(self, docnames, otherdata): # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) def find_obj(self, env, obj, name, typ, searchorder=0): if name[-2:] == '()': name = name[:-2] objects = self.data['objects'] newname = None if searchorder == 1: if obj and obj + '.' + name in objects: newname = obj + '.' + name else: newname = name else: if name in objects: newname = name elif obj and obj + '.' + name in objects: newname = obj + '.' + name return newname, objects.get(newname) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): objectname = node.get('js:object') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, objectname, target, typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): objectname = node.get('js:object') name, obj = self.find_obj(env, objectname, target, None, 1) if not obj: return [] return [('js:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name))] def get_objects(self): for refname, (docname, type) in list(self.data['objects'].items()): yield refname, refname, type, docname, \ refname.replace('$', '_S_'), 1
class CPPDomain(Domain): """C++ language domain.""" name = 'cpp' label = 'C++' object_types = { 'class': ObjType(l_('class'), 'class'), 'function': ObjType(l_('function'), 'func'), 'member': ObjType(l_('member'), 'member'), 'type': ObjType(l_('type'), 'type') } directives = { 'class': CPPClassObject, 'function': CPPFunctionObject, 'member': CPPMemberObject, 'type': CPPTypeObject, 'namespace': CPPCurrentNamespace } roles = { 'class': CPPXRefRole(), 'func': CPPXRefRole(fix_parens=True), 'member': CPPXRefRole(), 'type': CPPXRefRole() } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for fullname, (fn, _, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _create_refnode(expr): name = str(expr) if name not in self.data['objects']: return None obj = self.data['objects'][name] if obj[1] not in self.objtypes_for_role(typ): return None return make_refnode(builder, fromdocname, obj[0], obj[2], contnode, name) parser = DefinitionParser(target) try: expr = parser.parse_type().get_name() parser.skip_ws() if not parser.eof or expr is None: raise DefinitionError('') except DefinitionError: env.warn_node('unparseable C++ definition: %r' % target, node) return None parent = node.get('cpp:parent', None) rv = _create_refnode(expr) if rv is not None or parent is None: return rv parent = parent.get_name() rv = _create_refnode(expr.prefix(parent)) if rv is not None: return rv parent, name = parent.split_owner() return _create_refnode(expr.prefix(parent)) def get_objects(self): for refname, (docname, type, theid) in self.data['objects'].items(): yield (refname, refname, type, docname, refname, 1)
class RubyDomain(Domain): """Ruby language domain.""" name = 'rb' label = 'Ruby' object_types = { 'function': ObjType(l_('function'), 'func', 'obj'), 'global': ObjType(l_('global variable'), 'global', 'obj'), 'method': ObjType(l_('method'), 'meth', 'obj'), 'class': ObjType(l_('class'), 'class', 'obj'), 'exception': ObjType(l_('exception'), 'exc', 'obj'), 'classmethod': ObjType(l_('class method'), 'meth', 'obj'), 'attr_reader': ObjType(l_('attribute'), 'attr', 'obj'), 'attr_writer': ObjType(l_('attribute'), 'attr', 'obj'), 'attr_accessor': ObjType(l_('attribute'), 'attr', 'obj'), 'const': ObjType(l_('const'), 'const', 'obj'), 'module': ObjType(l_('module'), 'mod', 'obj'), } directives = { 'function': RubyModulelevel, 'global': RubyGloballevel, 'method': RubyEverywhere, 'const': RubyEverywhere, 'class': RubyClasslike, 'exception': RubyClasslike, 'classmethod': RubyClassmember, 'attr_reader': RubyClassmember, 'attr_writer': RubyClassmember, 'attr_accessor': RubyClassmember, 'module': RubyModule, 'currentmodule': RubyCurrentModule, } roles = { 'func': RubyXRefRole(fix_parens=False), 'global': RubyXRefRole(), 'class': RubyXRefRole(), 'exc': RubyXRefRole(), 'meth': RubyXRefRole(fix_parens=False), 'attr': RubyXRefRole(), 'const': RubyXRefRole(), 'mod': RubyXRefRole(), 'obj': RubyXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # modname -> docname, synopsis, platform, deprecated } indices = [ RubyModuleIndex, ] def clear_doc(self, docname): for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] for modname, (fn, _, _, _) in list(self.data['modules'].items()): if fn == docname: del self.data['modules'][modname] def find_obj(self, env, modname, classname, name, type, searchorder=0): """ Find a Ruby object for "name", perhaps using the given module and/or classname. """ # skip parens if name[-2:] == '()': name = name[:-2] if not name: return None, None objects = self.data['objects'] newname = None if searchorder == 1: if modname and classname and \ modname + '::' + classname + '#' + name in objects: newname = modname + '::' + classname + '#' + name elif modname and classname and \ modname + '::' + classname + '.' + name in objects: newname = modname + '::' + classname + '.' + name elif modname and modname + '::' + name in objects: newname = modname + '::' + name elif modname and modname + '#' + name in objects: newname = modname + '#' + name elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif classname and classname + '#' + name in objects: newname = classname + '#' + name elif name in objects: newname = name else: if name in objects: newname = name elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif classname and classname + '#' + name in objects: newname = classname + '#' + name elif modname and modname + '::' + name in objects: newname = modname + '::' + name elif modname and modname + '#' + name in objects: newname = modname + '#' + name elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif modname and classname and \ modname + '::' + classname + '#' + name in objects: newname = modname + '::' + classname + '#' + name elif modname and classname and \ modname + '::' + classname + '.' + name in objects: newname = modname + '::' + classname + '.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ 'object.' + name in objects: newname = 'object.' + name if newname is None: return None, None return newname, objects[newname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if (typ == 'mod' or typ == 'obj' and target in self.data['modules']): docname, synopsis, platform, deprecated = \ self.data['modules'].get(target, ('','','', '')) if not docname: return None else: title = '%s%s%s' % ( (platform and '(%s) ' % platform), synopsis, (deprecated and ' (deprecated)' or '')) return make_refnode(builder, fromdocname, docname, 'module-' + target, contnode, title) else: modname = node.get('rb:module') clsname = node.get('rb:class') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, modname, clsname, target, typ, searchorder) if not obj: return None else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def get_objects(self): for modname, info in _iteritems(self.data['modules']): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in _iteritems(self.data['objects']): yield (refname, refname, type, docname, refname, 1)
class HTTPDomain(Domain): """HTTP domain.""" name = "http" label = "HTTP" object_types = { "options": ObjType("options", "options", "obj"), "head": ObjType("head", "head", "obj"), "post": ObjType("post", "post", "obj"), "get": ObjType("get", "get", "obj"), "put": ObjType("put", "put", "obj"), "patch": ObjType("patch", "patch", "obj"), "delete": ObjType("delete", "delete", "obj"), "trace": ObjType("trace", "trace", "obj"), "copy": ObjType("copy", "copy", "obj"), "any": ObjType("any", "any", "obj"), } directives = { "options": HTTPOptions, "head": HTTPHead, "post": HTTPPost, "get": HTTPGet, "put": HTTPPut, "patch": HTTPPatch, "delete": HTTPDelete, "trace": HTTPTrace, "copy": HTTPCopy, "any": HTTPAny, } roles = { "options": HTTPXRefRole("options"), "head": HTTPXRefRole("head"), "post": HTTPXRefRole("post"), "get": HTTPXRefRole("get"), "put": HTTPXRefRole("put"), "patch": HTTPXRefRole("patch"), "delete": HTTPXRefRole("delete"), "trace": HTTPXRefRole("trace"), "copy": HTTPXRefRole("copy"), "all": HTTPXRefRole("all"), "statuscode": http_statuscode_role, "method": http_method_role, "header": http_header_role, } initial_data = { "options": {}, # path: (docname, synopsis) "head": {}, "post": {}, "get": {}, "put": {}, "patch": {}, "delete": {}, "trace": {}, "copy": {}, "any": {}, } indices = [HTTPIndex] @property def routes(self): return dict((key, self.data[key]) for key in self.object_types) def clear_doc(self, docname): for typ, routes in self.routes.items(): for path, info in list(routes.items()): if info[0] == docname: del routes[path] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): try: info = self.data[str(typ)][target] except KeyError: text = contnode.rawsource if typ == "statuscode": return http_statuscode_role(None, text, text, None, None)[0][0] elif typ == "mailheader": return http_header_role(None, text, text, None, None)[0][0] else: return nodes.emphasis(text, text) else: anchor = http_resource_anchor(typ, target) title = typ.upper() + " " + target return make_refnode(builder, fromdocname, info[0], anchor, contnode, title) def get_objects(self): for method, routes in self.routes.items(): for path, info in routes.items(): anchor = http_resource_anchor(method, path) yield (path, path, method, info[0], anchor, 1)
class MongoDBDomain(Domain): """MongoDB Documentation domain.""" name = 'mongodb' label = 'MongoDB' # if you add a new object type make sure to edit MongoDBObject.get_index_string object_types = { 'dbcommand': ObjType(l_('dbcommand'), 'dbcommand'), 'operator': ObjType(l_('operator'), 'operator'), 'projection': ObjType(l_('projection'), 'projection'), 'binary': ObjType(l_('binary'), 'program'), 'setting': ObjType(l_('setting'), 'setting'), 'status': ObjType(l_('status'), 'status'), 'stats': ObjType(l_('stats'), 'stats'), 'readmode': ObjType(l_('readmode'), 'readmode'), 'method': ObjType(l_('method'), 'method'), 'data': ObjType(l_('data'), 'data'), 'aggregator': ObjType(l_('aggregator'), 'aggregator'), 'group': ObjType(l_('group'), 'group'), 'expression': ObjType(l_('expression'), 'expression'), 'collflag': ObjType(l_('collflag'), 'collflag'), 'error': ObjType(l_('error'), 'error'), } directives = { 'dbcommand': MongoDBCallable, 'operator': MongoDBCallable, 'projection': MongoDBCallable, 'binary': MongoDBCallable, 'setting': MongoDBCallable, 'status': MongoDBCallable, 'stats': MongoDBCallable, 'readmode': MongoDBCallable, 'method': MongoDBCallableComplex, 'data': MongoDBCallable, 'aggregator': MongoDBCallable, 'group': MongoDBCallable, 'expression': MongoDBCallable, 'collflag': MongoDBCallable, 'error': MongoDBCallable, } roles = { 'dbcommand': MongoDBXRefRole(), 'operator': MongoDBXRefRole(), 'projection': MongoDBXRefRole(), 'program': MongoDBXRefRole(), 'setting': MongoDBXRefRole(), 'status': MongoDBXRefRole(), 'stats': MongoDBXRefRole(), 'readmode': MongoDBXRefRole(), 'method': MongoDBXRefRole(), 'data': MongoDBXRefRole(), 'aggregator': MongoDBXRefRole(), 'group': MongoDBXRefRole(), 'expression': MongoDBXRefRole(), 'collflag': MongoDBXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def find_obj(self, env, obj, name, typ, searchorder=0): if name[-2:] == '()': name = name[:-2] objects = self.data['objects'] newname = None if searchorder == 1: if obj and obj + '.' + name in objects: newname = obj + '.' + name else: newname = name else: if name in objects: newname = name elif obj and obj + '.' + name in objects: newname = obj + '.' + name return newname, objects.get(newname) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): objectname = node.get('mongodb:object') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, objectname, target, typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name) def get_objects(self): for refname, (docname, type) in self.data['objects'].items(): yield refname, refname, type, docname, refname.replace('$', '_S_'), 1
class ZeekDomain(Domain): """Zeek domain.""" name = 'zeek' label = 'Zeek' object_types = { 'type': ObjType(_('type'), 'type'), 'native-type': ObjType(_('type'), 'type'), 'namespace': ObjType(_('namespace'), 'namespace'), 'id': ObjType(_('id'), 'id'), 'keyword': ObjType(_('keyword'), 'keyword'), 'enum': ObjType(_('enum'), 'enum'), 'attr': ObjType(_('attr'), 'attr'), } directives = { 'type': ZeekGeneric, 'native-type': ZeekNativeType, 'namespace': ZeekNamespace, 'id': ZeekIdentifier, 'keyword': ZeekKeyword, 'enum': ZeekEnum, 'attr': ZeekAttribute, } roles = { 'type': XRefRole(), 'namespace': XRefRole(), 'id': XRefRole(), 'keyword': XRefRole(), 'enum': XRefRole(), 'attr': XRefRole(), 'see': XRefRole(), } indices = [ ZeekNotices, ] initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): to_delete = [] for (typ, name), doc in self.data['objects'].items(): if doc == docname: to_delete.append((typ, name)) for (typ, name) in to_delete: del self.data['objects'][typ, name] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): objects = self.data['objects'] if typ == "see": if target not in self.data['idtypes']: logger.warning('%s: unknown target for ":zeek:see:`%s`"', fromdocname, target) return [] objtype = self.data['idtypes'][target] return make_refnode(builder, fromdocname, objects[objtype, target], objtype + '-' + target, contnode, target + ' ' + objtype) else: objtypes = self.objtypes_for_role(typ) for objtype in objtypes: if (objtype, target) in objects: return make_refnode(builder, fromdocname, objects[objtype, target], objtype + '-' + target, contnode, target + ' ' + objtype) else: logger.warning('%s: unknown target for ":zeek:%s:`%s`"', fromdocname, typ, target) def get_objects(self): for (typ, name), docname in self.data['objects'].items(): yield name, name, typ, docname, typ + '-' + name, 1
class FortranDomain(Domain): """Fortran language domain.""" name = 'f' label = 'Fortran' object_types = { 'program': ObjType(l_('program'), 'prog'), 'type': ObjType(l_('type'), 'type'), 'variable': ObjType(l_('variable'), 'var'), 'function': ObjType(l_('function'), 'func'), 'subroutine': ObjType(l_('subroutine'), 'func', 'subr'), 'module': ObjType(l_('module'), 'mod'), } directives = { 'program': FortranProgram, 'type': FortranType, 'variable': FortranObject, 'function': FortranWithSig, 'subroutine': FortranWithSig, 'module': FortranModule, 'currentmodule': FortranCurrentModule, } roles = { 'prog': FortranXRefRole(), 'type': FortranXRefRole(), 'var': FortranXRefRole(), 'func': FortranXRefRole(fix_parens=True), 'subr': FortranXRefRole(fix_parens=True), 'mod': FortranXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # modname -> docname, synopsis, platform, deprecated } indices = [ FortranModuleIndex, ] def clear_doc(self, docname): for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] for modname, (fn, _, _, _) in list(self.data['modules'].items()): if fn == docname: del self.data['modules'][modname] def find_obj(self, env, modname, name, role, searchorder=0): """ Find a Fortran object for "name", perhaps using the given module and/or typename. :Params: - **searchorder**, optional: Start using relative search """ # skip parens if name.endswith('()'): name = name[:-2] if not name: return None, None if f_sep in name: modname, name = name.split(f_sep) #modname = modname or '_' if '%' in name: name, tmp = name.split('%') objects = self.data['objects'] newname = None matches = [] objtypes = self.objtypes_for_role(role) if searchorder == 1: # :role:`/toto` if role in ['mod', 'prog']: if 'f' + f_sep + name not in objects: # exact match return [] newname = 'f' + f_sep + name elif modname and 'f'+f_sep + modname + f_sep + name in objects and \ objects['f'+f_sep + modname + f_sep + name][1] in objtypes: newname = 'f' + f_sep + modname + f_sep + name elif 'f'+f_sep + '_' + f_sep + name in objects and \ objects['f'+f_sep + '_' + f_sep + name][1] in objtypes: newname = 'f' + f_sep + '_' + f_sep + name elif 'f'+f_sep + name in objects and \ objects['f'+f_sep + name][1] in objtypes: newname = 'f' + f_sep + name elif name in objects and \ objects[name][1] in objtypes: newname = name else: # :role:`toto` # NOTE: searching for exact match, object type is not considered if 'f' + f_sep + name in objects: newname = 'f' + f_sep + name elif role in ['mod', 'prog']: # only exact matches allowed for modules return [] elif 'f' + f_sep + '_' + f_sep + name in objects: newname = 'f' + f_sep + '_' + f_sep + name elif modname and 'f' + f_sep + modname + f_sep + name in objects: newname = 'f' + f_sep + modname + f_sep + name # Last chance: fuzzy search if newname is None: matches = [ (oname, objects[oname]) for oname in objects if oname.endswith(f_sep + name) and objects[oname][1] in objtypes ] else: matches.append((newname, objects[newname])) return matches def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): modname = node.get('f:module', node.get('modname')) typename = node.get('f:type', node.get('typename')) searchorder = node.hasattr('refspecific') and 1 or 0 matches = self.find_obj(env, modname, target, type, searchorder) if not matches: return None elif len(matches) > 1: env.warn( fromdocname, 'more than one target found for cross-reference ' '%r: %s' % (target, ', '.join(match[0] for match in matches)), node.line) name, obj = matches[0] if obj[1] == 'module': # get additional info for modules docname, synopsis, platform, deprecated = self.data['modules'][ name[1 + len(f_sep):]] assert docname == obj[0] title = name if synopsis: title += ': ' + synopsis if deprecated: title += _(' (deprecated)') #return make_refnode(builder, fromdocname, docname, #'module-' + name, contnode, title) return make_refnode(builder, fromdocname, docname, name, contnode, title) else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def get_objects(self): for modname, info in self.data['modules'].items(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in self.data['objects'].items(): yield (refname, refname, type, docname, refname, 1)
class CDomain(Domain): """C language domain.""" name = 'c' label = 'C' object_types = { 'function': ObjType(l_('function'), 'func'), 'member': ObjType(l_('member'), 'member'), 'macro': ObjType(l_('macro'), 'macro'), 'type': ObjType(l_('type'), 'type'), 'var': ObjType(l_('variable'), 'data'), } directives = { 'function': CObject, 'member': CObject, 'macro': CObject, 'type': CObject, 'var': CObject, } roles = { 'func' : CXRefRole(fix_parens=True), 'member': CXRefRole(), 'macro': CXRefRole(), 'data': CXRefRole(), 'type': CXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def merge_domaindata(self, docnames, otherdata): # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # strip pointer asterisk target = target.rstrip(' *') if target not in self.data['objects']: return None obj = self.data['objects'][target] return make_refnode(builder, fromdocname, obj[0], 'c.' + target, contnode, target) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # strip pointer asterisk target = target.rstrip(' *') if target not in self.data['objects']: return [] obj = self.data['objects'][target] return [('c:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], 'c.' + target, contnode, target))] def get_objects(self): for refname, (docname, type) in list(self.data['objects'].items()): yield (refname, refname, type, docname, 'c.' + refname, 1)
class CoqDomain(Domain): """A domain to document Coq code. Sphinx has a notion of “domains”, used to tailor it to a specific language. Domains mostly consist in descriptions of the objects that we wish to describe (for Coq, this includes tactics, tactic notations, options, exceptions, etc.), as well as domain-specific roles and directives. Each domain is responsible for tracking its objects, and resolving references to them. In the case of Coq, this leads us to define Coq “subdomains”, which classify objects into categories in which names must be unique. For example, a tactic and a theorem may share a name, but two tactics cannot be named the same. """ name = 'coq' label = 'Coq' object_types = { # ObjType (= directive type) → (Local name, *xref-roles) 'cmd': ObjType('cmd', 'cmd'), 'cmdv': ObjType('cmdv', 'cmd'), 'tac': ObjType('tac', 'tac'), 'tacn': ObjType('tacn', 'tacn'), 'tacv': ObjType('tacv', 'tacn'), 'opt': ObjType('opt', 'opt'), 'thm': ObjType('thm', 'thm'), 'exn': ObjType('exn', 'exn'), 'index': ObjType('index', 'index', searchprio=-1) } directives = { # Note that some directives live in the same semantic subdomain; ie # there's one directive per object type, but some object types map to # the same role. 'cmd': VernacObject, 'cmdv': VernacVariantObject, 'tac': TacticObject, 'tacn': TacticNotationObject, 'tacv': TacticNotationVariantObject, 'opt': OptionObject, 'thm': GallinaObject, 'exn': ExceptionObject, } roles = { # Each of these roles lives in a different semantic “subdomain” 'cmd': XRefRole(), 'tac': XRefRole(), 'tacn': XRefRole(), 'opt': XRefRole(), 'thm': XRefRole(), 'exn': XRefRole(), # This one is special 'index': IndexXRefRole(), # These are used for highlighting 'notation': NotationRole, 'gallina': GallinaRole, 'ltac': LtacRole, 'n': NotationRole, 'g': GallinaRole, 'l': LtacRole, #FIXME unused? } indices = [ CoqVernacIndex, CoqTacticIndex, CoqOptionIndex, CoqGallinaIndex, CoqExceptionIndex ] data_version = 1 initial_data = { # Collect everything under a key that we control, since Sphinx adds # others, such as “version” 'objects' : { # subdomain → name → docname, objtype, targetid 'cmd': {}, 'tac': {}, 'tacn': {}, 'opt': {}, 'thm': {}, 'exn': {}, } } @staticmethod def find_index_by_name(targetid): for index in CoqDomain.indices: if index.name == targetid: return index def get_objects(self): # Used for searching and object inventories (intersphinx) for _, objects in self.data['objects'].items(): for name, (docname, objtype, targetid) in objects.items(): yield (name, name, objtype, docname, targetid, self.object_types[objtype].attrs['searchprio']) for index in self.indices: yield (index.name, index.localname, 'index', "coq-" + index.name, '', -1) def merge_domaindata(self, docnames, otherdata): DUP = "Duplicate declaration: '{}' also defined in '{}'.\n" for subdomain, their_objects in otherdata['objects'].items(): our_objects = self.data['objects'][subdomain] for name, (docname, objtype, targetid) in their_objects.items(): if docname in docnames: if name in our_objects: self.env.warn(docname, DUP.format(name, our_objects[name][0])) our_objects[name] = (docname, objtype, targetid) def resolve_xref(self, env, fromdocname, builder, role, targetname, node, contnode): # ‘target’ is the name that was written in the document # ‘role’ is where this xref comes from; it's exactly one of our subdomains if role == 'index': index = CoqDomain.find_index_by_name(targetname) if index: return make_refnode(builder, fromdocname, "coq-" + index.name, '', contnode, index.localname) else: resolved = self.data['objects'][role].get(targetname) if resolved: (todocname, _, targetid) = resolved return make_refnode(builder, fromdocname, todocname, targetid, contnode, targetname) def clear_doc(self, docname_to_clear): for subdomain_objects in self.data['objects'].values(): for name, (docname, _, _) in list(subdomain_objects.items()): if docname == docname_to_clear: del subdomain_objects[name]
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 = { '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) } # type: Dict[str, ObjType] directives = { 'program': Program, 'cmdoption': Cmdoption, # old name for backwards compatibility 'option': Cmdoption, 'envvar': EnvVar, 'glossary': Glossary, 'productionlist': ProductionList, } # type: Dict[str, Type[Directive]] roles = { 'option': OptionXRefRole(warn_dangling=True), 'envvar': EnvVarXRefRole(), # links to tokens in grammar productions 'token': XRefRole(), # links to terms in glossary 'term': XRefRole(lowercase=True, 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), } # type: Dict[str, Union[RoleFunction, XRefRole]] initial_data = { 'progoptions': {}, # (program, name) -> docname, labelid 'objects': {}, # (type, name) -> docname, labelid 'citations': {}, # citation_name -> docname, labelid, lineno 'citation_refs': {}, # citation_name -> list of docnames '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', 'ref': 'undefined label: %(target)s (if the link has no caption ' 'the label must precede a section header)', 'numref': 'undefined label: %(target)s', 'keyword': 'unknown keyword: %(target)s', 'doc': 'unknown document: %(target)s', 'option': 'unknown option: %(target)s', 'citation': 'citation not found: %(target)s', } enumerable_nodes = { # node_class -> (figtype, title_getter) nodes.figure: ('figure', None), nodes.table: ('table', None), nodes.container: ('code-block', None), } # type: Dict[Type[nodes.Node], Tuple[str, Callable]] def __init__(self, env): # type: (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 clear_doc(self, docname): # type: (str) -> None for key, (fn, _l) in list(self.data['progoptions'].items()): if fn == docname: del self.data['progoptions'][key] for key, (fn, _l) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][key] for key, (fn, _l, lineno) in list(self.data['citations'].items()): if fn == docname: del self.data['citations'][key] for key, docnames in list(self.data['citation_refs'].items()): if docnames == [docname]: del self.data['citation_refs'][key] elif docname in docnames: docnames.remove(docname) for key, (fn, _l, _l) in list(self.data['labels'].items()): if fn == docname: del self.data['labels'][key] for key, (fn, _l) in list(self.data['anonlabels'].items()): if fn == docname: del self.data['anonlabels'][key] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None # XXX duplicates? for key, data in otherdata['progoptions'].items(): if data[0] in docnames: self.data['progoptions'][key] = data for key, data in otherdata['objects'].items(): if data[0] in docnames: self.data['objects'][key] = data for key, data in otherdata['citations'].items(): if data[0] in docnames: self.data['citations'][key] = data for key, data in otherdata['citation_refs'].items(): citation_refs = self.data['citation_refs'].setdefault(key, []) for docname in data: if docname in docnames: citation_refs.append(docname) for key, data in otherdata['labels'].items(): if data[0] in docnames: self.data['labels'][key] = data for key, data in otherdata['anonlabels'].items(): if data[0] in docnames: self.data['anonlabels'][key] = data def process_doc(self, env, docname, document): # type: (BuildEnvironment, str, nodes.document) -> None self.note_citations(env, docname, document) self.note_citation_refs(env, docname, document) self.note_labels(env, docname, document) def note_citations(self, env, docname, document): # type: (BuildEnvironment, str, nodes.document) -> None for node in document.traverse(nodes.citation): node['docname'] = docname label = cast(nodes.label, node[0]).astext() if label in self.data['citations']: path = env.doc2path(self.data['citations'][label][0]) logger.warning( __('duplicate citation %s, other instance in %s'), label, path, location=node, type='ref', subtype='citation') self.data['citations'][label] = (docname, node['ids'][0], node.line) def note_citation_refs(self, env, docname, document): # type: (BuildEnvironment, str, nodes.document) -> None for node in document.traverse(addnodes.pending_xref): if node['refdomain'] == 'std' and node['reftype'] == 'citation': label = node['reftarget'] citation_refs = self.data['citation_refs'].setdefault( label, []) citation_refs.append(docname) def note_labels(self, env, docname, document): # type: (BuildEnvironment, str, nodes.document) -> None labels, anonlabels = self.data['labels'], self.data['anonlabels'] 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 labels: logger.warning(__('duplicate label %s, other instance in %s'), name, env.doc2path(labels[name][0]), location=node) anonlabels[name] = docname, labelid if node.tagname in ('section', 'rubric'): title = cast(nodes.title, node[0]) sectname = clean_astext(title) elif self.is_enumerable_node(node): sectname = self.get_numfig_title(node) if not sectname: continue elif node.traverse(addnodes.toctree): n = node.traverse(addnodes.toctree)[0] if n.get('caption'): sectname = n['caption'] else: continue else: # anonymous-only labels continue labels[name] = docname, labelid, sectname def add_object(self, objtype, name, docname, labelid): # type: (str, str, str, str) -> None self.data['objects'][objtype, name] = (docname, labelid) def add_program_option(self, program, name, docname, labelid): # type: (str, str, str, str) -> None self.data['progoptions'][program, name] = (docname, labelid) def check_consistency(self): # type: () -> None for name, (docname, labelid, lineno) in self.data['citations'].items(): if name not in self.data['citation_refs']: logger.warning(__('Citation [%s] is not referenced.'), name, type='ref', subtype='citation', location=(docname, lineno)) def build_reference_node(self, fromdocname, builder, docname, labelid, sectname, rolename, **options): # type: (str, Builder, str, str, str, str, Any) -> nodes.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 = addnodes.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, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA 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 == 'citation': resolver = self._resolve_citation_xref else: resolver = self._resolve_obj_xref return resolver(env, fromdocname, builder, typ, target, node, contnode) def _resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA if node['refexplicit']: # reference to anonymous label; the reference uses # the supplied link caption docname, labelid = self.data['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.data['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, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA if target in self.data['labels']: docname, labelid, figname = self.data['labels'].get( target, ('', '', '')) else: docname, labelid = self.data['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(__("no number is assigned for %s: %s"), figtype, 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, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA # keywords are oddballs: they are referenced by named labels docname, labelid, _ = self.data['labels'].get(target, ('', '', '')) if not docname: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def _resolve_doc_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA # 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, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA progname = node.get('std:program') target = target.strip() docname, labelid = self.data['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.data['progoptions'].get( (progname, target), ('', '')) if docname: break else: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def _resolve_citation_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA from sphinx.environment import NoUri docname, labelid, lineno = self.data['citations'].get( target, ('', '', 0)) if not docname: if 'ids' in node: # remove ids attribute that annotated at # transforms.CitationReference.apply. del node['ids'][:] return None try: return make_refnode(builder, fromdocname, docname, labelid, contnode) except NoUri: # remove the ids we added in the CitationReferences # transform since they can't be transfered to # the contnode (if it's a Text node) if not isinstance(contnode, nodes.Element): del node['ids'][:] raise def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA objtypes = self.objtypes_for_role(typ) or [] for objtype in objtypes: if (objtype, target) in self.data['objects']: docname, labelid = self.data['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, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA results = [] # type: List[Tuple[str, nodes.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.data['objects']: docname, labelid = self.data['objects'][key] results.append(('std:' + self.role_for_objtype(objtype), make_refnode(builder, fromdocname, docname, labelid, contnode))) return results def get_objects(self): # type: () -> 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.data['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.data['objects'].items(): yield (name, name, type, info[0], info[1], self.object_types[type].attrs['searchprio']) for name, info in self.data['labels'].items(): yield (name, info[2], 'label', info[0], info[1], -1) # add anonymous-only labels as well non_anon_labels = set(self.data['labels']) for name, info in self.data['anonlabels'].items(): if name not in non_anon_labels: yield (name, name, 'label', info[0], info[1], -1) def get_type_name(self, type, primary=False): # type: (ObjType, bool) -> str # never prepend "Default" return type.lname def is_enumerable_node(self, node): # type: (nodes.Node) -> bool return node.__class__ in self.enumerable_nodes def get_numfig_title(self, node): # type: (nodes.Node) -> str """Get the title of enumerable nodes to refer them using its title""" if self.is_enumerable_node(node): _, title_getter = self.enumerable_nodes.get( node.__class__, (None, None)) if title_getter: return title_getter(node) else: for subnode in node: if subnode.tagname in ('caption', 'title'): return clean_astext(subnode) return None def get_enumerable_node_type(self, node): # type: (nodes.Node) -> str """Get type of enumerable nodes.""" def has_child(node, cls): # type: (nodes.Element, Type) -> bool return any(isinstance(child, cls) for child in node) if isinstance(node, nodes.section): return 'section' elif isinstance(node, nodes.container): if node.get('literal_block') and has_child(node, nodes.literal_block): return 'code-block' else: return None else: figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None)) return figtype def get_figtype(self, node): # type: (nodes.Node) -> str """Get figure type of nodes. .. deprecated:: 1.8 """ warnings.warn( 'StandardDomain.get_figtype() is deprecated. ' 'Please use get_enumerable_node_type() instead.', RemovedInSphinx30Warning, stacklevel=2) return self.get_enumerable_node_type(node) def get_fignumber(self, env, builder, figtype, docname, target_node): # type: (BuildEnvironment, Builder, str, str, nodes.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): # target_node is found, but fignumber is not assigned. # Maybe it is defined in orphaned document. raise ValueError def get_full_qualified_name(self, node): # type: (nodes.Element) -> 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
class LassoDomain(Domain): """Lasso language domain. """ name = 'ls' label = 'Lasso' object_types = { 'method': ObjType(l_('method'), 'meth'), 'member': ObjType(l_('member'), 'meth'), 'provide': ObjType(l_('provide'), 'meth'), 'require': ObjType(l_('require'), 'meth'), 'type': ObjType(l_('type'), 'type'), 'trait': ObjType(l_('trait'), 'trait'), 'thread': ObjType(l_('thread'), 'thread'), } directives = { 'method': LSTag, 'member': LSTag, 'provide': LSTraitTag, 'require': LSTraitTag, # name and signature only 'type': LSDefinition, 'trait': LSDefinition, 'thread': LSDefinition, } roles = { 'meth': LSXRefRole(fix_parens=True), 'type': LSXRefRole(), 'trait': LSXRefRole(), 'thread': LSXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def merge_domaindata(self, docnames, otherdata): # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) def find_obj(self, env, obj, name, typ, searchorder=0): if name[-2:] == '()': name = name[:-2] objects = self.data['objects'] newname = None if searchorder == 1: if obj and obj + '->' + name in objects: newname = obj + '->' + name else: newname = name else: if name in objects: newname = name elif obj and obj + '->' + name in objects: newname = obj + '->' + name return newname, objects.get(newname) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): objectname = node.get('ls:object') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, objectname, target.lower(), typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): objectname = node.get('ls:object') name, obj = self.find_obj(env, objectname, target.lower(), None, 1) if not obj: return [] return [('ls:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name, contnode, name))] def get_objects(self): for refname, (docname, type) in self.data['objects'].items(): yield (refname, refname, type, docname, refname, 1)
class ReSTDomain(Domain): """ReStructuredText domain.""" name = 'rst' label = 'reStructuredText' object_types = { 'directive': ObjType(l_('directive'), 'dir'), 'role': ObjType(l_('role'), 'role'), } directives = { 'directive': ReSTDirective, 'role': ReSTRole, } roles = { 'dir': XRefRole(), 'role': XRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } # type: Dict[unicode, Dict[unicode, Tuple[unicode, ObjType]]] def clear_doc(self, docname): # type: (unicode) -> None for (typ, name), doc in list(self.data['objects'].items()): if doc == docname: del self.data['objects'][typ, name] def merge_domaindata(self, docnames, otherdata): # type: (List[unicode], Dict) -> None # XXX check duplicates for (typ, name), doc in otherdata['objects'].items(): if doc in docnames: self.data['objects'][typ, name] = doc def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA objects = self.data['objects'] objtypes = self.objtypes_for_role(typ) for objtype in objtypes: if (objtype, target) in objects: return make_refnode(builder, fromdocname, objects[objtype, target], objtype + '-' + target, contnode, target + ' ' + objtype) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, nodes.Node, nodes.Node) -> List[nodes.Node] # NOQA objects = self.data['objects'] results = [] for objtype in self.object_types: if (objtype, target) in self.data['objects']: results.append( ('rst:' + self.role_for_objtype(objtype), make_refnode(builder, fromdocname, objects[objtype, target], objtype + '-' + target, contnode, target + ' ' + objtype))) return results def get_objects(self): # type: () -> Iterator[Tuple[unicode, unicode, unicode, unicode, unicode, int]] for (typ, name), docname in iteritems(self.data['objects']): yield name, name, typ, docname, typ + '-' + name, 1
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 = { '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) } # type: Dict[str, ObjType] directives = { 'program': Program, 'cmdoption': Cmdoption, # old name for backwards compatibility 'option': Cmdoption, 'envvar': EnvVar, 'glossary': Glossary, 'productionlist': ProductionList, } # type: Dict[str, Type[Directive]] roles = { '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), } # type: Dict[str, Union[RoleFunction, XRefRole]] 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', 'ref': 'undefined label: %(target)s (if the link has no caption ' 'the label must precede a section header)', 'numref': 'undefined label: %(target)s', 'keyword': 'unknown keyword: %(target)s', 'doc': 'unknown document: %(target)s', 'option': 'unknown option: %(target)s', } enumerable_nodes = { # node_class -> (figtype, title_getter) nodes.figure: ('figure', None), nodes.table: ('table', None), nodes.container: ('code-block', None), } # type: Dict[Type[Node], Tuple[str, Callable]] 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) def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None: warnings.warn('StandardDomain.add_object() is deprecated.', RemovedInSphinx50Warning, stacklevel=2) self.objects[objtype, name] = (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 = None # type: Any 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, _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['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 in ('section', 'rubric'): title = cast(nodes.title, node[0]) sectname = clean_astext(title) elif self.is_enumerable_node(node): sectname = self.get_numfig_title(node) if not sectname: continue else: toctree = next(iter(node.traverse(addnodes.toctree)), None) if toctree and toctree.get('caption'): sectname = toctree.get('caption') else: # anonymous-only labels continue 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) -> 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 == 'citation': warnings.warn( 'pending_xref(domain=std, type=citation) is deprecated: %r' % node, RemovedInSphinx40Warning, stacklevel=2) domain = env.get_domain('citation') return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode) 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) -> 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) -> 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(__("no number is assigned for %s: %s"), figtype, 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) -> 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) -> 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) -> 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: for objtype, term in self.objects: if objtype == 'term' and term.lower() == target.lower(): docname, labelid = self.objects[objtype, term] logger.warning(__( 'term %s not found in case sensitive match.' 'made a reference to %s instead.'), target, term, location=node, type='ref', subtype='term') break else: docname, labelid = '', '' if not docname: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> 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 = [] # type: 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) -> 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) -> 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): # target_node is found, but fignumber is not assigned. # Maybe it is defined in orphaned document. raise ValueError def get_full_qualified_name(self, node: Element) -> 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 note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_citations() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_citation_refs() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_labels() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
class LuaDomain(Domain): """Lua language domain.""" name = 'lua' label = 'Lua' object_types = { 'function': ObjType(l_('function'), 'func', 'obj'), 'data': ObjType(l_('data'), 'data', 'obj'), 'class': ObjType(l_('class'), 'class', 'obj'), 'method': ObjType(l_('method'), 'meth', 'obj'), 'staticmethod': ObjType(l_('static method'), 'smeth', 'obj'), 'attribute': ObjType(l_('attribute'), 'attr', 'obj'), 'module': ObjType(l_('module'), 'mod', 'obj'), } directives = { 'function': LuaModulelevel, 'data': LuaModulelevel, 'class': LuaClasslike, 'exception': LuaClasslike, 'method': LuaClassmember, 'classmethod': LuaClassmember, 'staticmethod': LuaClassmember, 'attribute': LuaClassmember, 'module': LuaModule, 'currentmodule': LuaCurrentModule, 'decorator': LuaDecoratorFunction, 'decoratormethod': LuaDecoratorMethod, } roles = { 'data': LuaXRefRole(), 'exc': LuaXRefRole(), 'func': LuaXRefRole(fix_parens=True), 'class': LuaXRefRole(), 'const': LuaXRefRole(), 'attr': LuaXRefRole(), 'meth': LuaXRefRole(fix_parens=True), 'mod': LuaXRefRole(), 'obj': LuaXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # modname -> docname, synopsis, platform, deprecated } indices = [ LuaModuleIndex, ] def clear_doc(self, docname): for fullname, (fn, _) in self.data['objects'].items(): if fn == docname: del self.data['objects'][fullname] for modname, (fn, _, _, _) in self.data['modules'].items(): if fn == docname: del self.data['modules'][modname] def find_obj(self, env, modname, classname, name, type, searchmode=0): """Find a Lua object for "name", perhaps using the given module and/or classname. Returns a list of (name, object entry) tuples. """ # skip parens if name[-2:] == '()': name = name[:-2] if not name: return [] objects = self.data['objects'] matches = [] newname = None if searchmode == 1: objtypes = self.objtypes_for_role(type) if objtypes is not None: if modname and classname: fullname = modname + '.' + classname + '.' + name if fullname in objects and objects[fullname][1] in objtypes: newname = fullname if not newname: if modname and modname + '.' + name in objects and \ objects[modname + '.' + name][1] in objtypes: newname = modname + '.' + name elif name in objects and objects[name][1] in objtypes: newname = name else: # "fuzzy" searching mode searchname = '.' + name matches = [(oname, objects[oname]) for oname in objects if oname.endswith(searchname) and objects[oname][1] in objtypes] else: # NOTE: searching for exact match, object type is not considered if name in objects: newname = name elif type == 'mod': # only exact matches allowed for modules return [] elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif modname and classname and \ modname + '.' + classname + '.' + name in objects: newname = modname + '.' + classname + '.' + name # special case: builtin exceptions have module "exceptions" set elif type == 'exc' and '.' not in name and \ 'exceptions.' + name in objects: newname = 'exceptions.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ 'object.' + name in objects: newname = 'object.' + name if newname is not None: matches.append((newname, objects[newname])) return matches def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): modname = node.get('lua:module') clsname = node.get('lua:class') searchmode = node.hasattr('refspecific') and 1 or 0 matches = self.find_obj(env, modname, clsname, target, type, searchmode) if not matches: return None elif len(matches) > 1: env.warn_node( 'more than one target found for cross-reference ' '%r: %s' % (target, ', '.join(match[0] for match in matches)), node) name, obj = matches[0] if obj[1] == 'module': # get additional info for modules docname, synopsis, platform, deprecated = self.data['modules'][name] assert docname == obj[0] title = name if synopsis: title += ': ' + synopsis if deprecated: title += _(' (deprecated)') if platform: title += ' (' + platform + ')' return make_refnode(builder, fromdocname, docname, 'module-' + name, contnode, title) else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def get_objects(self): for modname, info in self.data['modules'].iteritems(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in self.data['objects'].iteritems(): yield (refname, refname, type, docname, refname, 1)
class JavaScriptDomain(Domain): """JavaScript language domain.""" name = 'js' label = 'JavaScript' # if you add a new object type make sure to edit JSObject.get_index_string object_types = { 'function': ObjType(_('function'), 'func'), 'method': ObjType(_('method'), 'meth'), 'class': ObjType(_('class'), 'class'), 'data': ObjType(_('data'), 'data'), 'attribute': ObjType(_('attribute'), 'attr'), 'module': ObjType(_('module'), 'mod'), } directives = { 'function': JSCallable, 'method': JSCallable, 'class': JSConstructor, 'data': JSObject, 'attribute': JSObject, 'module': JSModule, } roles = { 'func': JSXRefRole(fix_parens=True), 'meth': JSXRefRole(fix_parens=True), 'class': JSXRefRole(fix_parens=True), 'data': JSXRefRole(), 'attr': JSXRefRole(), 'mod': JSXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # mod_name -> docname } # type: Dict[str, Dict[str, Tuple[str, str]]] def clear_doc(self, docname): # type: (str) -> None for fullname, (pkg_docname, _l) in list(self.data['objects'].items()): if pkg_docname == docname: del self.data['objects'][fullname] for mod_name, pkg_docname in list(self.data['modules'].items()): if pkg_docname == docname: del self.data['modules'][mod_name] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) for mod_name, pkg_docname in otherdata['modules'].items(): if pkg_docname in docnames: self.data['modules'][mod_name] = pkg_docname def find_obj(self, env, mod_name, prefix, name, typ, searchorder=0): # type: (BuildEnvironment, str, str, str, str, int) -> Tuple[str, Tuple[str, str]] if name[-2:] == '()': name = name[:-2] objects = self.data['objects'] searches = [] if mod_name and prefix: searches.append('.'.join([mod_name, prefix, name])) if mod_name: searches.append('.'.join([mod_name, name])) if prefix: searches.append('.'.join([prefix, name])) searches.append(name) if searchorder == 0: searches.reverse() newname = None for search_name in searches: if search_name in objects: newname = search_name return newname, objects.get(newname) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA mod_name = node.get('js:module') prefix = node.get('js:object') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA mod_name = node.get('js:module') prefix = node.get('js:object') name, obj = self.find_obj(env, mod_name, prefix, target, None, 1) if not obj: return [] return [('js:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name))] def get_objects(self): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] for refname, (docname, type) in list(self.data['objects'].items()): yield refname, refname, type, docname, \ refname.replace('$', '_S_'), 1 def get_full_qualified_name(self, node): # type: (nodes.Element) -> str modname = node.get('js:module') prefix = node.get('js:object') target = node.get('reftarget') if target is None: return None else: return '.'.join(filter(None, [modname, prefix, target]))
class TrafficServerDomain(Domain): """ Apache Traffic Server Documentation. """ name = 'ts' label = 'Traffic Server' data_version = 2 object_types = { 'cv': ObjType(l_('configuration variable'), 'cv'), 'stat': ObjType(l_('statistic'), 'stat') } directives = {'cv': TSConfVar, 'stat': TSStat} roles = {'cv': TSConfVarRef(), 'stat': TSStatRef()} initial_data = { 'cv': {}, # full name -> docname 'stat': {} } dangling_warnings = { 'cv': "No definition found for configuration variable '%(target)s'", 'stat': "No definition found for statistic '%(target)s'" } def clear_doc(self, docname): cv_list = self.data['cv'] for var, doc in list(cv_list.items()): if doc == docname: del cv_list[var] stat_list = self.data['stat'] for var, doc in list(stat_list.items()): if doc == docname: del stat_list[var] def find_doc(self, key, obj_type): zret = None if obj_type == 'cv': obj_list = self.data['cv'] elif obj_type == 'stat': obj_list = self.data['stat'] else: obj_list = None if obj_list and key in obj_list: zret = obj_list[key] return zret def resolve_xref(self, env, src_doc, builder, obj_type, target, node, cont_node): dst_doc = self.find_doc(target, obj_type) if (dst_doc): return sphinx.util.nodes.make_refnode(builder, src_doc, dst_doc, nodes.make_id(target), cont_node, 'records.config') # Python 2/3 compat - iteritems is 2, items is 3 # Although perhaps the lists are small enough items could be used in Python 2. try: {}.iteritems() def get_objects(self): for var, doc in self.data['cv'].iteritems(): yield var, var, 'cv', doc, var, 1 for var, doc in self.data['stat'].iteritems(): yield var, var, 'stat', doc, var, 1 except AttributeError: def get_objects(self): for var, doc in self.data['cv'].items(): yield var, var, 'cv', doc, var, 1 for var, doc in self.data['stat'].items(): yield var, var, 'stat', doc, var, 1
class JavaScriptDomain(Domain): """JavaScript language domain.""" name = 'js' label = 'JavaScript' # if you add a new object type make sure to edit JSObject.get_index_string object_types = { 'function': ObjType(_('function'), 'func'), 'method': ObjType(_('method'), 'meth'), 'class': ObjType(_('class'), 'class'), 'data': ObjType(_('data'), 'data'), 'attribute': ObjType(_('attribute'), 'attr'), 'module': ObjType(_('module'), 'mod'), } directives = { 'function': JSCallable, 'method': JSCallable, 'class': JSConstructor, 'data': JSObject, 'attribute': JSObject, 'module': JSModule, } roles = { 'func': JSXRefRole(fix_parens=True), 'meth': JSXRefRole(fix_parens=True), 'class': JSXRefRole(fix_parens=True), 'data': JSXRefRole(), 'attr': JSXRefRole(), 'mod': JSXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, node_id, objtype 'modules': {}, # modname -> docname, node_id } # type: Dict[str, Dict[str, Tuple[str, str]]] @property def objects(self) -> Dict[str, Tuple[str, str, str]]: return self.data.setdefault('objects', {}) # fullname -> docname, node_id, objtype def note_object(self, fullname: str, objtype: str, node_id: str, location: Any = None) -> None: if fullname in self.objects: docname = self.objects[fullname][0] logger.warning(__('duplicate %s description of %s, other %s in %s'), objtype, fullname, objtype, docname, location=location) self.objects[fullname] = (self.env.docname, node_id, objtype) @property def modules(self) -> Dict[str, Tuple[str, str]]: return self.data.setdefault('modules', {}) # modname -> docname, node_id def note_module(self, modname: str, node_id: str) -> None: self.modules[modname] = (self.env.docname, node_id) def clear_doc(self, docname: str) -> None: for fullname, (pkg_docname, node_id, _l) in list(self.objects.items()): if pkg_docname == docname: del self.objects[fullname] for modname, (pkg_docname, node_id) in list(self.modules.items()): if pkg_docname == docname: del self.modules[modname] def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: # XXX check duplicates for fullname, (fn, node_id, objtype) in otherdata['objects'].items(): if fn in docnames: self.objects[fullname] = (fn, node_id, objtype) for mod_name, (pkg_docname, node_id) in otherdata['modules'].items(): if pkg_docname in docnames: self.modules[mod_name] = (pkg_docname, node_id) def find_obj(self, env: BuildEnvironment, mod_name: str, prefix: str, name: str, typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str, str]]: if name[-2:] == '()': name = name[:-2] searches = [] if mod_name and prefix: searches.append('.'.join([mod_name, prefix, name])) if mod_name: searches.append('.'.join([mod_name, name])) if prefix: searches.append('.'.join([prefix, name])) searches.append(name) if searchorder == 0: searches.reverse() newname = None for search_name in searches: if search_name in self.objects: newname = search_name return newname, self.objects.get(newname) def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, typ: str, target: str, node: pending_xref, contnode: Element ) -> Element: mod_name = node.get('js:module') prefix = node.get('js:object') searchorder = 1 if node.hasattr('refspecific') else 0 name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name) def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, target: str, node: pending_xref, contnode: Element ) -> List[Tuple[str, Element]]: mod_name = node.get('js:module') prefix = node.get('js:object') name, obj = self.find_obj(env, mod_name, prefix, target, None, 1) if not obj: return [] return [('js:' + self.role_for_objtype(obj[2]), make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name))] def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]: for refname, (docname, node_id, typ) in list(self.objects.items()): yield refname, refname, typ, docname, node_id, 1 def get_full_qualified_name(self, node: Element) -> str: modname = node.get('js:module') prefix = node.get('js:object') target = node.get('reftarget') if target is None: return None else: return '.'.join(filter(None, [modname, prefix, target]))
class CabalDomain(Domain): ''' Sphinx domain for cabal needs Domain.merge_doc for parallel building, just union all dicts ''' name = 'cabal' label = 'Cabal' object_types = { 'pkg-section': ObjType(_('pkg-section'), 'pkg-section'), 'pkg-field' : ObjType(_('pkg-field') , 'pkg-field' ), 'cfg-section': ObjType(_('cfg-section'), 'cfg-section'), 'cfg-field' : ObjType(_('cfg-field') , 'cfg-field' ), } directives = { 'pkg-section': CabalPackageSection, 'pkg-field' : CabalPackageField, 'cfg-section': CabalConfigSection, 'cfg-field' : ConfigField, } roles = { 'pkg-section': XRefRole(warn_dangling=True), 'pkg-field' : CabalPackageFieldXRef(warn_dangling=True), 'cfg-section': XRefRole(warn_dangling=True), 'cfg-field' : CabalConfigFieldXRef(warn_dangling=True), 'cfg-flag' : CabalConfigFieldXRef(warn_dangling=True), } initial_data = { 'pkg-sections': {}, 'pkg-fields' : {}, 'cfg-sections': {}, 'index-num' : {}, #per document number of objects # used to order references page 'cfg-fields' : {}, 'cfg-flags' : {}, } indices = [ ConfigFieldIndex ] types = { 'pkg-section': 'pkg-sections', 'pkg-field' : 'pkg-fields', 'cfg-section': 'cfg-sections', 'cfg-field' : 'cfg-fields', 'cfg-flag' : 'cfg-flags', } def clear_doc(self, docname): for k in ['pkg-sections', 'pkg-fields', 'cfg-sections', 'cfg-fields', 'cfg-flags']: to_del = [] for name, (fn, _, _) in self.data[k].items(): if fn == docname: to_del.append(name) for name in to_del: del self.data[k][name] try: del self.data['index-num'][docname] except KeyError: pass def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): objtypes = self.objtypes_for_role(type) for typ, key in ((typ, key) for typ in objtypes for key in make_data_keys(typ, target, node)): try: data = env.domaindata['cabal'][self.types[typ]][key] except KeyError: continue doc, ref, meta = data title = make_title(typ, key, meta) return make_refnode(builder, fromdocname, doc, ref, contnode, title) def get_objects(self): ''' Used for search functionality ''' for typ in ['pkg-section', 'pkg-field', 'cfg-section', 'cfg-field', 'cfg-flag']: key = self.types[typ] for name, (fn, target, meta) in self.data[key].items(): title = make_title(typ, name, meta) yield title, title, typ, fn, target, 0
class ChmDomain(Domain): name = 'chm' label = 'CHARMM' object_types = { 'keyword': ObjType(l_('keyword'), 'kw', 'obj'), 'command': ObjType(l_('command'), 'cmd', 'obj'), 'developer': ObjType(l_('developer'), 'dev', 'obj'), 'spec': ObjType(l_('spec'), 'spec', 'obj'), } directives = { 'keyword': KwDirective, 'command': CmdDirective, 'developer': DevDirective, 'spec': SpecDirective, } roles = { 'kw': KwXRefRole(), 'cmd': CmdXRefRole(), 'dev': ChmXRefRole(), 'spec': SpecXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype # 'keywords': {}, # kwname -> docname, synopsis } indices = [ # ChmKwIndex, ] def clear_doc(self, docname): for (typ, name), doc in self.data['objects'].items(): if doc == docname: del self.data['objects'][typ, name] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): objects = self.data['objects'] objtypes = self.objtypes_for_role(typ) for objtype in objtypes: # This is for dealing with :kw: when their :keyword: might have a `:length:` != 4. # Corner cases might be nasty..? if objtype in (u'keyword', u'command'): orig_target = target while 1: # look for the target name in the global dictionary, if you dont find it # keep shortening the name you look for until you find it, or fail. # this allows you to have a stylized :kw:, where the first n chars are used # to create the lookup key, and all the characters are printed in the manual if (objtype, target) in objects: # yeah.... contnode.children[0] = nodes.Text( CHARmmify(orig_target, len(target))) break else: target = target[:-1] if not target: return if (objtype, target) in objects: return make_refnode(builder, fromdocname, objects[objtype, target], objtype + '-' + target, contnode, target + ' ' + objtype) def get_objects(self): for (typ, name), docname in self.data['objects'].iteritems(): yield name, name, typ, docname, typ + '-' + name, 1
class GuacDomain(Domain): """ Sphinx domain specific to the Guacamole protocol, containing a single directive ("guac:instruction") that represents a single Guacamole instruction. """ # Each of the attributes and functions below are defined and inherited from # the superclass (Domain). name = 'guac' label = 'Guacamole Protocol' initial_data = { # Mapping of instruction name to (docname, title) tuples, where docname # is the name of the document containing the object and title is opcode # of the instruction being documented 'instruction': {} } object_types = {'instruction': ObjType('instruction')} directives = {'instruction': GuacInstruction} roles = {'instruction': XRefRole()} dangling_warnings = { 'instruction': "No documentation found for Guacamole instruction '%(target)s'" } def clear_doc(self, docname): # Clear all cached data associated with the given document instruction_list = self.data['instruction'] for inst, doc in list(instruction_list.items()): if doc == docname: del instruction_list[inst] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # Do not attempt to satisfy crossreferences for object types not # handled by this domain if typ not in self.data: return None # Do not attempt to satisfy crossreferences for unknown objects data = self.data[typ] if target not in data: return None # Retrieve target document and title from stored data (todocname, title) = data[target] return make_refnode(builder, fromdocname, todocname, target, contnode, title) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): resolved = [] # Search ALL types to enumerate all possible resolutions for typ in self.object_types: data = self.data[typ] # If the target exists within the set of known objects of the # current type, generate the required ('domain:role', node) tuple if target in data: (todocname, title) = data[target] resolved.append((self.name + ':' + typ, make_refnode(builder, fromdocname, todocname, target, contnode, title))) return resolved