From b5ff272f77bec90ba97cef0cfa42330e56c4a543 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 23 Jan 2021 23:50:06 +0900 Subject: [PATCH] Close #7642: std domain: Optimize case-insensitive match of term Since 3.0.1, the term role has matched to the words in glossary case-sensitively. It's important change for preventing conflicts by word cases. But, it also brings a problem for references in natural text. This optimizes the case-insensitive match of the term role. It allows to search glossary words twice with no performance penalty; the first search is case sensitive and another is case insenstive. --- CHANGES | 1 + sphinx/domains/std.py | 39 ++++++++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/CHANGES b/CHANGES index 604689ed8..c7cfe7db8 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,7 @@ Features added references when :confval:`napoleon_preprocess_types` enabled * #6241: mathjax: Include mathjax.js only on the document using equations * #8651: std domain: cross-reference for a rubric having inline item is broken +* #7642: std domain: Optimize case-insensitive match of term * #8681: viewcode: Support incremental build * #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright` * #207: Now :confval:`highlight_language` supports multiple languages diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 5b1dab2b8..352e5c7f3 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -323,7 +323,7 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index term['ids'].append(node_id) std = cast(StandardDomain, env.get_domain('std')) - std.note_object('term', termtext, node_id, location=term) + std._note_term(termtext, node_id, location=term) # add an index entry too indexnode = addnodes.index() @@ -694,6 +694,20 @@ class StandardDomain(Domain): RemovedInSphinx50Warning, stacklevel=2) self.objects[objtype, name] = (docname, labelid) + @property + def _terms(self) -> Dict[str, Tuple[str, str]]: + """.. note:: Will be removed soon. internal use only.""" + return self.data.setdefault('terms', {}) # (name) -> docname, labelid + + def _note_term(self, term: str, labelid: str, location: Any = None) -> None: + """Note a term for cross reference. + + .. note:: Will be removed soon. internal use only. + """ + self.note_object('term', term, labelid, location) + + self._terms[term.lower()] = (self.env.docname, labelid) + @property def progoptions(self) -> Dict[Tuple[str, str], Tuple[str, str]]: return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid @@ -714,6 +728,9 @@ class StandardDomain(Domain): for key, (fn, _l) in list(self.objects.items()): if fn == docname: del self.objects[key] + for key, (fn, _l) in list(self._terms.items()): + if fn == docname: + del self._terms[key] for key, (fn, _l, _l) in list(self.labels.items()): if fn == docname: del self.labels[key] @@ -729,6 +746,9 @@ class StandardDomain(Domain): for key, data in otherdata['objects'].items(): if data[0] in docnames: self.objects[key] = data + for key, data in otherdata['terms'].items(): + if data[0] in docnames: + self._terms[key] = data for key, data in otherdata['labels'].items(): if data[0] in docnames: self.labels[key] = data @@ -967,19 +987,12 @@ class StandardDomain(Domain): 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 + # fallback to case insentive match + if target.lower() in self._terms: + docname, labelid = self._terms[target.lower()] + return make_refnode(builder, fromdocname, docname, labelid, contnode) else: - 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, @@ -1147,7 +1160,7 @@ def setup(app: "Sphinx") -> Dict[str, Any]: return { 'version': 'builtin', - 'env_version': 1, + 'env_version': 2, 'parallel_read_safe': True, 'parallel_write_safe': True, }