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