From 12cfdea91f115eee4cd10ccfb405cde7e47c47bb Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Fri, 17 Aug 2007 06:27:35 +0000
Subject: [PATCH] Add glossary directive and :term: role for crossreferencing
into the glossary.
---
TODO | 1 -
sphinx/addnodes.py | 6 +++++-
sphinx/directives.py | 32 ++++++++++++++++++++++++++++++++
sphinx/environment.py | 27 +++++++++++++++++++++++++--
sphinx/roles.py | 6 ++++--
sphinx/style/default.css | 5 +++++
sphinx/style/traditional.css | 5 +++++
sphinx/templates/index.html | 2 ++
sphinx/writer.py | 5 +++++
9 files changed, 83 insertions(+), 6 deletions(-)
diff --git a/TODO b/TODO
index a6be44774..a8608f84a 100644
--- a/TODO
+++ b/TODO
@@ -9,4 +9,3 @@ Global TODO
- look at the old tools/ scripts, what functionality should be rewritten
- add search via Xapian?
- optionally have a contents tree view in the sidebar (AJAX based)?
-- .. caveat?
\ No newline at end of file
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 51626d030..eea1a9a90 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -53,9 +53,13 @@ class highlightlang(nodes.Element): pass
# like emphasis, but doesn't apply further text processors, e.g. smartypants
class literal_emphasis(nodes.emphasis): pass
+# glossary
+class glossary(nodes.Element): pass
+
# make them known to docutils. this is needed, because the HTMl writer
# will choke at some point if these are not added
nodes._add_node_class_names("""index desc desc_content desc_signature
desc_classname desc_name desc_parameterlist desc_parameter desc_optional
centered versionmodified seealso productionlist production toctree
- pending_xref compact_paragraph highlightlang""".split())
+ pending_xref compact_paragraph highlightlang literal_emphasis
+ glossary""".split())
diff --git a/sphinx/directives.py b/sphinx/directives.py
index 3add1ae00..d0fc09219 100644
--- a/sphinx/directives.py
+++ b/sphinx/directives.py
@@ -579,3 +579,35 @@ def literalinclude_directive(name, arguments, options, content, lineno,
literalinclude_directive.content = 0
literalinclude_directive.arguments = (1, 0, 0)
directives.register_directive('literalinclude', literalinclude_directive)
+
+
+# ------ glossary directive ---------------------------------------------------------
+
+def glossary_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ """Glossary with cross-reference targets for :dfn: roles."""
+ env = state.document.settings.env
+ node = addnodes.glossary()
+ state.nested_parse(content, content_offset, node)
+
+ # the content should be definition lists
+ dls = [child for child in node if isinstance(child, nodes.definition_list)]
+ # now, extract definition terms to enable cross-reference creation
+ for dl in dls:
+ dl['classes'].append('glossary')
+ for li in dl.children:
+ if not li.children or not isinstance(li[0], nodes.term):
+ continue
+ termtext = li.children[0].astext()
+ new_id = 'term-' + nodes.make_id(termtext)
+ if new_id in env.gloss_entries:
+ new_id = 'term-' + str(len(env.gloss_entries))
+ env.gloss_entries.add(new_id)
+ li[0]['names'].append(new_id)
+ li[0]['ids'].append(new_id)
+ state.document.settings.env.note_glossaryterm(termtext, new_id)
+ return [node]
+
+glossary_directive.content = 1
+glossary_directive.arguments = (0, 0, 0)
+directives.register_directive('glossary', glossary_directive)
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 704c5b92f..ae3c808fa 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -52,7 +52,7 @@ default_settings = {
# This is increased every time a new environment attribute is added
# to properly invalidate pickle files.
-ENV_VERSION = 10
+ENV_VERSION = 11
def walk_depth(node, depth, maxdepth):
@@ -206,6 +206,7 @@ class BuildEnvironment:
self.modules = {} # modname -> filename, synopsis, platform, deprecated
self.tokens = {} # tokenname -> filename
self.labels = {} # labelname -> filename, labelid
+ self.glossary = {} # term -> filename, labelid
# Other inventories
self.indexentries = {} # filename -> list of
@@ -219,6 +220,7 @@ class BuildEnvironment:
self.currclass = None # current class name
self.currdesc = None # current descref name
self.index_num = 0 # autonumber for index targets
+ self.gloss_entries = set() # existing definition labels
def set_warning_stream(self, stream):
self.warning_stream = stream
@@ -247,6 +249,9 @@ class BuildEnvironment:
for labelname, (fn, _, _) in self.labels.items():
if fn == filename:
del self.labels[labelname]
+ for term, (fn, _) in self.glossary.items():
+ if fn == filename:
+ del self.glossary[term]
self.indexentries.pop(filename, None)
for version, changes in self.versionchanges.items():
new = [change for change in changes if change[1] != filename]
@@ -351,6 +356,8 @@ class BuildEnvironment:
self.filename = None
self.currmodule = None
self.currclass = None
+ self.indexnum = 0
+ self.gloss_entries = set()
if save_parsed:
# save the parsed doctree
@@ -497,7 +504,6 @@ class BuildEnvironment:
def note_token(self, tokenname):
self.tokens[tokenname] = self.filename
-
def note_index_entry(self, type, string, targetid, aliasname):
self.indexentries.setdefault(self.filename, []).append(
(type, string, targetid, aliasname))
@@ -505,6 +511,9 @@ class BuildEnvironment:
def note_versionchange(self, type, version, node):
self.versionchanges.setdefault(version, []).append(
(type, self.filename, self.currmodule, self.currdesc, node.deepcopy()))
+
+ def note_glossaryterm(self, text, labelname):
+ self.glossary[text] = (self.filename, labelname)
# -------
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
@@ -592,6 +601,20 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri(
docfilename, filename) + '#' + labelid
newnode.append(nodes.emphasis(sectname, sectname))
+ elif typ == 'term':
+ filename, labelid = self.glossary.get(target, ('', ''))
+ if not filename:
+ print >>self.warning_stream, \
+ '%s: term not in glossary: %s' % (docfilename, target)
+ newnode = contnode
+ else:
+ newnode = nodes.reference('', '')
+ if filename == docfilename:
+ newnode['refid'] = labelid
+ else:
+ newnode['refuri'] = builder.get_relative_uri(
+ docfilename, filename) + '#' + labelid
+ newnode.append(contnode)
elif typ == 'token':
filename = self.tokens.get(target, '')
if not filename:
diff --git a/sphinx/roles.py b/sphinx/roles.py
index 2f5dc6bb8..6aefee04c 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -20,7 +20,7 @@ ws_re = re.compile(r'\s+')
generic_docroles = {
'command' : nodes.strong,
- 'dfn' : addnodes.literal_emphasis,
+ 'dfn' : addnodes.emphasis,
'guilabel' : nodes.strong,
'kbd' : nodes.literal,
'keyword' : nodes.literal,
@@ -89,6 +89,7 @@ roles.register_local_role('rfc', indexmarkup_role)
# default is `literal`
innernodetypes = {
'ref': nodes.emphasis,
+ 'term': nodes.emphasis,
'token': nodes.strong,
}
@@ -106,7 +107,7 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
typ in ('data', 'exc', 'func', 'class', 'const', 'attr', 'meth'):
text = text[1:]
pnode['refspecific'] = True
- pnode['reftarget'] = ws_re.sub('', text)
+ pnode['reftarget'] = ws_re.sub((' ' if typ == 'term' else ''), text)
pnode['modname'] = env.currmodule
pnode['classname'] = env.currclass
pnode += innernodetypes.get(typ, nodes.literal)(rawtext, text, classes=['xref'])
@@ -153,6 +154,7 @@ specific_docroles = {
'ref': xfileref_role,
'token' : xfileref_role,
+ 'term': xfileref_role,
'menuselection' : menusel_role,
'file' : emph_literal_role,
diff --git a/sphinx/style/default.css b/sphinx/style/default.css
index 39de529e7..50a22cdb4 100644
--- a/sphinx/style/default.css
+++ b/sphinx/style/default.css
@@ -691,6 +691,11 @@ dt:target,
background-color: #fbe54e;
}
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
th {
text-align: left;
padding-right: 5px;
diff --git a/sphinx/style/traditional.css b/sphinx/style/traditional.css
index ca9587867..f99887d5b 100644
--- a/sphinx/style/traditional.css
+++ b/sphinx/style/traditional.css
@@ -591,6 +591,11 @@ dd {
margin-left: 30px;
}
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
.refcount {
color: #060;
}
diff --git a/sphinx/templates/index.html b/sphinx/templates/index.html
index d2b4c1640..4ead86283 100644
--- a/sphinx/templates/index.html
+++ b/sphinx/templates/index.html
@@ -44,6 +44,8 @@
quick access to all modules
General Index
all functions, classes, terms
+ Glossary
+ the most important terms explained
Search page
search this documentation
diff --git a/sphinx/writer.py b/sphinx/writer.py
index a034d3981..61664e29d 100644
--- a/sphinx/writer.py
+++ b/sphinx/writer.py
@@ -211,6 +211,11 @@ class HTMLTranslator(BaseTranslator):
def visit_index(self, node):
raise nodes.SkipNode
+ def visit_glossary(self, node):
+ pass
+ def depart_glossary(self, node):
+ pass
+
# these are only handled specially in the SmartyPantsHTMLTranslator
def visit_literal_emphasis(self, node):
return self.visit_emphasis(node)
|