diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 32dd2f24f..a86002f1c 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -23,7 +23,7 @@ except ImportError: from docutils import nodes from docutils.io import DocTreeInput, StringOutput -from docutils.core import publish_parts +from docutils.core import Publisher, publish_parts from docutils.utils import new_document from docutils.frontend import OptionParser from docutils.readers.doctree import Reader as DoctreeReader @@ -74,6 +74,8 @@ class StandaloneHTMLBuilder(Builder): script_files = ['_static/jquery.js', '_static/doctools.js'] def init(self): + # cached publisher object for snippets + self._publisher = None # a hash of all config values that, if changed, cause a full rebuild self.config_hash = '' self.tags_hash = '' @@ -181,13 +183,24 @@ class StandaloneHTMLBuilder(Builder): """Utility: Render a lone doctree node.""" doc = new_document('') doc.append(node) - return publish_parts( - doc, - source_class=DocTreeInput, - reader=DoctreeReader(), - writer=HTMLWriter(self), - settings_overrides={'output_encoding': 'unicode'} - ) + + if self._publisher is None: + self._publisher = Publisher( + source_class = DocTreeInput, + destination_class=StringOutput) + self._publisher.set_components('standalone', + 'restructuredtext', 'pseudoxml') + + pub = self._publisher + + pub.reader = DoctreeReader() + pub.writer = HTMLWriter(self) + pub.process_programmatic_settings( + None, {'output_encoding': 'unicode'}, None) + pub.set_source(doc, None) + pub.set_destination(None, None) + pub.publish() + return pub.writer.parts def prepare_writing(self, docnames): from sphinx.search import IndexBuilder diff --git a/sphinx/search.py b/sphinx/search.py index fe20c24a5..8bde13266 100644 --- a/sphinx/search.py +++ b/sphinx/search.py @@ -14,8 +14,14 @@ from cStringIO import StringIO from docutils.nodes import Text, NodeVisitor -from sphinx.util.stemmer import PorterStemmer from sphinx.util import jsdump, rpartition +try: + # http://bitbucket.org/methane/porterstemmer/ + from porterstemmer import Stemmer as CStemmer + CSTEMMER = True +except ImportError: + from sphinx.util.stemmer import PorterStemmer + CSTEMMER = False word_re = re.compile(r'\w+(?u)') @@ -62,15 +68,23 @@ class _JavaScriptIndex(object): js_index = _JavaScriptIndex() -class Stemmer(PorterStemmer): - """ - All those porter stemmer implementations look hideous. - make at least the stem method nicer. - """ +if CSTEMMER: + class Stemmer(CStemmer): + + def stem(self, word): + return self(word.lower()) + +else: + class Stemmer(PorterStemmer): + """ + All those porter stemmer implementations look hideous. + make at least the stem method nicer. + """ + + def stem(self, word): + word = word.lower() + return PorterStemmer.stem(self, word, 0, len(word) - 1) - def stem(self, word): - word = word.lower() - return PorterStemmer.stem(self, word, 0, len(word) - 1) class WordCollector(NodeVisitor): @@ -196,11 +210,11 @@ class IndexBuilder(object): visitor = WordCollector(doctree) doctree.walk(visitor) - def add_term(word, prefix='', stem=self._stemmer.stem): + def add_term(word, stem=self._stemmer.stem): word = stem(word) if len(word) < 3 or word in stopwords or word.isdigit(): return - self._mapping.setdefault(prefix + word, set()).add(filename) + self._mapping.setdefault(word, set()).add(filename) for word in word_re.findall(title): add_term(word) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 7e4b1092b..1eb8383aa 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -416,30 +416,28 @@ def copy_static_entry(source, target, builder, context={}): # traverse() is called so many times during a build that it saves # on average 20-25% overall build time! -def _all_traverse(self): +def _all_traverse(self, result): """Version of Node.traverse() that doesn't need a condition.""" - result = [] result.append(self) for child in self.children: - result.extend(child._all_traverse()) + child._all_traverse(result) return result -def _fast_traverse(self, cls): +def _fast_traverse(self, cls, result): """Version of Node.traverse() that only supports instance checks.""" - result = [] if isinstance(self, cls): result.append(self) for child in self.children: - result.extend(child._fast_traverse(cls)) + child._fast_traverse(cls, result) return result def _new_traverse(self, condition=None, include_self=1, descend=1, siblings=0, ascend=0): if include_self and descend and not siblings and not ascend: if condition is None: - return self._all_traverse() + return self._all_traverse([]) elif isinstance(condition, (types.ClassType, type)): - return self._fast_traverse(condition) + return self._fast_traverse(condition, []) return self._old_traverse(condition, include_self, descend, siblings, ascend)