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)