mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
293 lines
11 KiB
Python
293 lines
11 KiB
Python
"""
|
|
The Bro domain for Sphinx.
|
|
"""
|
|
|
|
def setup(Sphinx):
|
|
Sphinx.add_domain(BroDomain)
|
|
Sphinx.add_node(see)
|
|
Sphinx.add_directive_to_domain('bro', 'see', SeeDirective)
|
|
Sphinx.connect('doctree-resolved', process_see_nodes)
|
|
|
|
from sphinx import addnodes
|
|
from sphinx.domains import Domain, ObjType, Index
|
|
from sphinx.locale import l_, _
|
|
from sphinx.directives import ObjectDescription
|
|
from sphinx.roles import XRefRole
|
|
from sphinx.util.nodes import make_refnode
|
|
from sphinx import version_info
|
|
|
|
from docutils import nodes
|
|
from docutils.parsers.rst import Directive
|
|
from docutils.parsers.rst import directives
|
|
from docutils.parsers.rst.roles import set_classes
|
|
|
|
class see(nodes.General, nodes.Element):
|
|
refs = []
|
|
|
|
class SeeDirective(Directive):
|
|
has_content = True
|
|
|
|
def run(self):
|
|
n = see('')
|
|
n.refs = " ".join(self.content).split()
|
|
return [n]
|
|
|
|
# Wrapper for creating a tuple for index nodes, staying backwards
|
|
# compatible to Sphinx < 1.4:
|
|
def make_index_tuple(indextype, indexentry, targetname, targetname2):
|
|
if version_info >= (1, 4, 0, '', 0):
|
|
return (indextype, indexentry, targetname, targetname2, None)
|
|
else:
|
|
return (indextype, indexentry, targetname, targetname2)
|
|
|
|
def process_see_nodes(app, doctree, fromdocname):
|
|
for node in doctree.traverse(see):
|
|
content = []
|
|
para = nodes.paragraph()
|
|
para += nodes.Text("See also:", "See also:")
|
|
for name in node.refs:
|
|
join_str = " "
|
|
if name != node.refs[0]:
|
|
join_str = ", "
|
|
link_txt = join_str + name;
|
|
|
|
if name not in app.env.domaindata['bro']['idtypes']:
|
|
# Just create the text and issue warning
|
|
app.env.warn(fromdocname,
|
|
'unknown target for ".. bro:see:: %s"' % (name))
|
|
para += nodes.Text(link_txt, link_txt)
|
|
else:
|
|
# Create a reference
|
|
typ = app.env.domaindata['bro']['idtypes'][name]
|
|
todocname = app.env.domaindata['bro']['objects'][(typ, name)]
|
|
|
|
newnode = nodes.reference('', '')
|
|
innernode = nodes.literal(_(name), _(name))
|
|
newnode['refdocname'] = todocname
|
|
newnode['refuri'] = app.builder.get_relative_uri(
|
|
fromdocname, todocname)
|
|
newnode['refuri'] += '#' + typ + '-' + name
|
|
newnode.append(innernode)
|
|
para += nodes.Text(join_str, join_str)
|
|
para += newnode
|
|
|
|
content.append(para)
|
|
node.replace_self(content)
|
|
|
|
class BroGeneric(ObjectDescription):
|
|
def update_type_map(self, idname):
|
|
if 'idtypes' not in self.env.domaindata['bro']:
|
|
self.env.domaindata['bro']['idtypes'] = {}
|
|
self.env.domaindata['bro']['idtypes'][idname] = self.objtype
|
|
|
|
def add_target_and_index(self, name, sig, signode):
|
|
targetname = self.objtype + '-' + name
|
|
if targetname not in self.state.document.ids:
|
|
signode['names'].append(targetname)
|
|
signode['ids'].append(targetname)
|
|
signode['first'] = (not self.names)
|
|
self.state.document.note_explicit_target(signode)
|
|
|
|
objects = self.env.domaindata['bro']['objects']
|
|
key = (self.objtype, name)
|
|
if ( key in objects and self.objtype != "id" and
|
|
self.objtype != "type" ):
|
|
self.env.warn(self.env.docname,
|
|
'duplicate description of %s %s, ' %
|
|
(self.objtype, name) +
|
|
'other instance in ' +
|
|
self.env.doc2path(objects[key]),
|
|
self.lineno)
|
|
objects[key] = self.env.docname
|
|
self.update_type_map(name)
|
|
|
|
indextext = self.get_index_text(self.objtype, name)
|
|
if indextext:
|
|
self.indexnode['entries'].append(make_index_tuple('single',
|
|
indextext, targetname,
|
|
targetname))
|
|
|
|
def get_index_text(self, objectname, name):
|
|
return _('%s (%s)') % (name, self.objtype)
|
|
|
|
def handle_signature(self, sig, signode):
|
|
signode += addnodes.desc_name("", sig)
|
|
return sig
|
|
|
|
class BroNamespace(BroGeneric):
|
|
def add_target_and_index(self, name, sig, signode):
|
|
targetname = self.objtype + '-' + name
|
|
if targetname not in self.state.document.ids:
|
|
signode['names'].append(targetname)
|
|
signode['ids'].append(targetname)
|
|
signode['first'] = (not self.names)
|
|
self.state.document.note_explicit_target(signode)
|
|
|
|
objects = self.env.domaindata['bro']['objects']
|
|
key = (self.objtype, name)
|
|
objects[key] = self.env.docname
|
|
self.update_type_map(name)
|
|
|
|
indextext = self.get_index_text(self.objtype, name)
|
|
self.indexnode['entries'].append(make_index_tuple('single', indextext,
|
|
targetname, targetname))
|
|
self.indexnode['entries'].append(make_index_tuple('single',
|
|
"namespaces; %s" % (sig),
|
|
targetname, targetname))
|
|
|
|
def get_index_text(self, objectname, name):
|
|
return _('%s (namespace); %s') % (name, self.env.docname)
|
|
|
|
def handle_signature(self, sig, signode):
|
|
signode += addnodes.desc_name("", sig)
|
|
return sig
|
|
|
|
class BroEnum(BroGeneric):
|
|
def add_target_and_index(self, name, sig, signode):
|
|
targetname = self.objtype + '-' + name
|
|
if targetname not in self.state.document.ids:
|
|
signode['names'].append(targetname)
|
|
signode['ids'].append(targetname)
|
|
signode['first'] = (not self.names)
|
|
self.state.document.note_explicit_target(signode)
|
|
|
|
objects = self.env.domaindata['bro']['objects']
|
|
key = (self.objtype, name)
|
|
objects[key] = self.env.docname
|
|
self.update_type_map(name)
|
|
|
|
indextext = self.get_index_text(self.objtype, name)
|
|
#self.indexnode['entries'].append(make_index_tuple('single', indextext,
|
|
# targetname, targetname))
|
|
m = sig.split()
|
|
|
|
if len(m) < 2:
|
|
self.env.warn(self.env.docname,
|
|
"bro:enum directive missing argument(s)")
|
|
return
|
|
|
|
if m[1] == "Notice::Type":
|
|
if 'notices' not in self.env.domaindata['bro']:
|
|
self.env.domaindata['bro']['notices'] = []
|
|
self.env.domaindata['bro']['notices'].append(
|
|
(m[0], self.env.docname, targetname))
|
|
self.indexnode['entries'].append(make_index_tuple('single',
|
|
"%s (enum values); %s" % (m[1], m[0]),
|
|
targetname, targetname))
|
|
|
|
def handle_signature(self, sig, signode):
|
|
m = sig.split()
|
|
name = m[0]
|
|
signode += addnodes.desc_name("", name)
|
|
return name
|
|
|
|
class BroIdentifier(BroGeneric):
|
|
def get_index_text(self, objectname, name):
|
|
return name
|
|
|
|
class BroKeyword(BroGeneric):
|
|
def get_index_text(self, objectname, name):
|
|
return name
|
|
|
|
class BroAttribute(BroGeneric):
|
|
def get_index_text(self, objectname, name):
|
|
return _('%s (attribute)') % (name)
|
|
|
|
class BroNotices(Index):
|
|
"""
|
|
Index subclass to provide the Bro notices index.
|
|
"""
|
|
|
|
name = 'noticeindex'
|
|
localname = l_('Bro Notice Index')
|
|
shortname = l_('notices')
|
|
|
|
def generate(self, docnames=None):
|
|
content = {}
|
|
|
|
if 'notices' not in self.domain.env.domaindata['bro']:
|
|
return content, False
|
|
|
|
for n in self.domain.env.domaindata['bro']['notices']:
|
|
modname = n[0].split("::")[0]
|
|
entries = content.setdefault(modname, [])
|
|
entries.append([n[0], 0, n[1], n[2], '', '', ''])
|
|
|
|
content = sorted(content.items())
|
|
|
|
return content, False
|
|
|
|
class BroDomain(Domain):
|
|
"""Bro domain."""
|
|
name = 'bro'
|
|
label = 'Bro'
|
|
|
|
object_types = {
|
|
'type': ObjType(l_('type'), 'type'),
|
|
'namespace': ObjType(l_('namespace'), 'namespace'),
|
|
'id': ObjType(l_('id'), 'id'),
|
|
'keyword': ObjType(l_('keyword'), 'keyword'),
|
|
'enum': ObjType(l_('enum'), 'enum'),
|
|
'attr': ObjType(l_('attr'), 'attr'),
|
|
}
|
|
|
|
directives = {
|
|
'type': BroGeneric,
|
|
'namespace': BroNamespace,
|
|
'id': BroIdentifier,
|
|
'keyword': BroKeyword,
|
|
'enum': BroEnum,
|
|
'attr': BroAttribute,
|
|
}
|
|
|
|
roles = {
|
|
'type': XRefRole(),
|
|
'namespace': XRefRole(),
|
|
'id': XRefRole(),
|
|
'keyword': XRefRole(),
|
|
'enum': XRefRole(),
|
|
'attr': XRefRole(),
|
|
'see': XRefRole(),
|
|
}
|
|
|
|
indices = [
|
|
BroNotices,
|
|
]
|
|
|
|
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, env, fromdocname, builder, typ, target, node,
|
|
contnode):
|
|
objects = self.data['objects']
|
|
if typ == "see":
|
|
if target not in self.data['idtypes']:
|
|
self.env.warn(fromdocname,
|
|
'unknown target for ":bro:see:`%s`"' % (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:
|
|
self.env.warn(fromdocname,
|
|
'unknown target for ":bro:%s:`%s`"' % (typ, target))
|
|
|
|
def get_objects(self):
|
|
for (typ, name), docname in self.data['objects'].items():
|
|
yield name, name, typ, docname, typ + '-' + name, 1
|