Commits

Doug Hellmann committed 7289d79

rewrite sphinxcontrib.spelling to be easier to understand and produce nicer output

Comments (0)

Files changed (12)

spelling/Makefile

+# Default target is to show help
+help:
+	@echo "sdist          - Source distribution"
+	@echo "html           - HTML documentation"
+	@echo "upload         - upload a new release to PyPI"
+	@echo "installwebsite - deploy web version of docs"
+	@echo "develop        - install development version"
+	@echo "test           - run the test suite"
+
+
+.PHONY: sdist
+sdist: html
+	python setup.py sdist
+
+.PHONY: upload
+upload: html
+	python setup.py sdist upload
+
+# Documentation
+.PHONY: html
+html:
+	(cd docs && $(MAKE) html)
+
+installwebsite: html
+	(cd docs/build/html && rsync --rsh=ssh --archive --delete --verbose . www.doughellmann.com:/var/www/doughellmann/DocumentRoot/docs/sphinxcontrib.spelling/)
+
+# Register the new version on pypi
+.PHONY: register
+register:
+	python setup.py register
+
+# Testing
+test:
+	tox
+
+develop:
+	python setup.py develop
+.. -*- mode: rst -*-
+
 =========================
  sphinxcontrib-spelling
 =========================
 Sphinx-based documentation.  It uses PyEnchant_ to produce a report
 showing misspelled words.
 
-Installation
-============
+Refer to the `main documentation page
+<http://www.doughellmann.com/docs/sphinxcontrib.spelling/>`__ for
+installation and setup details.
 
-1. Follow the instructions on the PyEnchant_ site to install enchant and then PyEnchant.
-2. ``pip install sphinxcontrib-spelling``
-
-Configuration
-=============
-
-1. Add ``'sphinxcontrib.spelling'`` to the ``extensions`` list in ``conf.py``.
-
-  ::
-
-    extensions = [ 'sphinxcontrib.spelling' ]
-
-Configuration Options
----------------------
-
-``spelling_show_suggestions``
-  Boolean controlling whether suggestions for misspelled words are
-  printed.  Defaults to False.
-``spelling_lang``
-  String specifying the language, as understood by PyEnchant and
-  enchant.  Defaults to ``en_US`` for US English.
-``spelling_word_list_filename``
-  String specifying a file containing a list of words known to be
-  spelled correctly but that do not appear in the language dictionary
-  selected by ``spelling_lang``.  The file should contain one word per
-  line.  Refer to the `PyEnchant tutoral
-  <http://www.rfk.id.au/software/pyenchant/tutorial.html>`_ for
-  details.
-
-Running
+License
 =======
 
-To process a document with the spell checker, use ``sphinx-build`` and
-specify ``spelling`` as the builder name using the ``-b`` option.  The
-output includes the headings from the document and any misspelled
-words.  If suggestions are enabled, they are shown on the same line as
-the misspelling.
+Copyright Doug Hellmann, All Rights Reserved
 
-::
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Doug Hellmann not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
 
-  $ sphinx-build -b spelling source output
-  
-  virtualenvwrapper
-
-   Features
-   - "Plugin"
-
-   Introduction
-   - "plugins"
-
-   Details
-
-    Installation
-
-     Supported Shells
-     - "bitbucket"
-
-History
-=======
-
-0.2
-  Warn but otherwise ignore unknown node types.
-
-0.1
-  First public release.
+DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
 
 .. _PyEnchant: http://www.rfk.id.au/software/pyenchant/

spelling/docs/Makefile

+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/sphinxcontribspelling.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sphinxcontribspelling.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/sphinxcontribspelling"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sphinxcontribspelling"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	make -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+spelling:
+	$(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) $(BUILDDIR)/spelling
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."

spelling/docs/source/conf.py

+# -*- coding: utf-8 -*-
+#
+# sphinxcontrib.spelling documentation build configuration file, created by
+# sphinx-quickstart on Sun Apr 17 15:33:23 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [ 'sphinxcontrib.spelling' ]
+
+spelling_show_suggestions = True
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'sphinxcontrib.spelling'
+copyright = u'2011, Doug Hellmann'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.2'
+# The full version, including alpha/beta/rc tags.
+release = '0.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'nature'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'sphinxcontribspellingdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'sphinxcontribspelling.tex', u'sphinxcontrib.spelling Documentation',
+   u'Doug Hellmann', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'sphinxcontribspelling', u'sphinxcontrib.spelling Documentation',
+     [u'Doug Hellmann'], 1)
+]

spelling/docs/source/developers.rst

+============
+ Developers
+============
+
+If you would like to contribute to sphinxcontrib.spelling directly,
+these instructions should help you get started.  Patches, bug reports,
+and feature requests are all welcome through the `BitBucket site
+<https://bitbucket.org/birkenfeld/sphinx-contrib/>`__.
+Contributions in the form of patches or pull requests are easier to
+integrate and will receive priority attention.
+
+Building Documentation
+======================
+
+The documentation for sphinxcontrib.spelling is written in
+reStructuredText and converted to HTML using Sphinx. The build itself
+is driven by make.  You will need the following packages in order to
+build the docs:
+
+- Sphinx
+- docutils
+- sphinxcontrib.spelling
+
+Once all of the tools are installed into a virtualenv using
+pip, run ``make html`` to generate the HTML version of the
+documentation.

spelling/docs/source/history.rst

+=================
+ Release History
+=================
+
+1.0
+
+  - Re-implement using just a Builder, without a separate visitor
+    class.
+  - Show the file and line number of any words not appearing in the
+    dictionary, instead of the section title.
+  - Log the file, line, and unknown words as the documents are
+    processed.
+
+0.2
+
+  - Warn but otherwise ignore unknown node types.
+
+0.1
+
+  - First public release.

spelling/docs/source/index.rst

+.. sphinxcontrib.spelling documentation master file, created by
+   sphinx-quickstart on Sun Apr 17 15:33:23 2011.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+========================
+ sphinxcontrib.spelling
+========================
+
+``sphinxcontrib.spelling`` is a spelling checker for Sphinx_.  It uses
+PyEnchant_ to produce a report showing misspelled words.
+
+Features
+========
+
+1. Supports multiple source languages using the standard enchant
+   dictionaries.
+2. Supports project-specific dictionaries for localized jargon and
+   other terminology that may not appear in the global dictionaries.
+3. Suggests alternatives to words not found in the dictionary, when
+   possible.
+
+Details
+=======
+
+.. toctree::
+   :maxdepth: 2
+
+   install
+   run
+   developers
+   history
+
+
+.. _PyEnchant: http://www.rfk.id.au/software/pyenchant/
+
+.. _Sphinx: http://sphinx.pocoo.org/

spelling/docs/source/install.rst

+==============
+ Installation
+==============
+
+Installing sphinxcontrib.spelling
+=================================
+
+1. Follow the instructions on the PyEnchant_ site to install
+   **enchant** and then **PyEnchant**.
+2. ``pip install sphinxcontrib-spelling``
+
+Configuration
+=============
+
+1. Add ``'sphinxcontrib.spelling'`` to the ``extensions`` list in ``conf.py``.
+
+  ::
+
+    extensions = [ 'sphinxcontrib.spelling' ]
+
+Configuration Options
+=====================
+
+``spelling_show_suggestions``
+  Boolean controlling whether suggestions for misspelled words are
+  printed.  Defaults to False.
+``spelling_lang``
+  String specifying the language, as understood by PyEnchant and
+  enchant.  Defaults to ``en_US`` for US English.
+``spelling_word_list_filename``
+  String specifying a file containing a list of words known to be
+  spelled correctly but that do not appear in the language dictionary
+  selected by ``spelling_lang``.  The file should contain one word per
+  line.  Refer to the `PyEnchant tutoral
+  <http://www.rfk.id.au/software/pyenchant/tutorial.html>`_ for
+  details.
+
+
+.. _PyEnchant: http://www.rfk.id.au/software/pyenchant/

spelling/docs/source/run.rst

+=========
+ Running
+=========
+
+To process a document with the spell checker, use ``sphinx-build`` and
+specify ``spelling`` as the builder name using the ``-b`` option.  The
+output includes the headings from the document and any misspelled
+words.  If suggestions are enabled, they are shown on the same line as
+the misspelling. A log of all the words not found in the dictionary is
+saved to the file ``spelling/output.txt`` under the build directory.
+
+::
+
+    $ make spelling
+    sphinx-build -b spelling -d build/doctrees   source build/spelling
+    Running Sphinx v1.0.7
+    Initializing Spelling Checker
+    loading pickled environment... done
+    building [spelling]: all documents
+    updating environment: 1 added, 2 changed, 0 removed
+    reading sources... [ 33%] history
+    reading sources... [ 66%] index
+    reading sources... [100%] run
+
+    looking for now-outdated files... none found
+    pickling environment... done
+    checking consistency... done
+    preparing documents... done
+    writing output... [ 20%] developers
+    writing output... [ 40%] history
+    writing output... [ 60%] index
+    writing output... [ 80%] install
+    (line  28) PyEnchant ["Penchant"]
+    writing output... [100%] run
+
+    Spelling checker messages written to ./docs/build/spelling/output.txt
+    build finished with problems.
+    make: *** [spelling] Error 1
+

spelling/docs/source/spelling_wordlist.txt

+inlined
+PyEnchant
+sphinxcontrib
+reStructuredText
+docutils
+virtualenv

spelling/docs/spelling_wordlist.txt

Empty file added.

spelling/sphinxcontrib/spelling.py

 """Spelling checker extension for Sphinx.
 """
 
+import codecs
+import imp
+import itertools
+import os
+import re
+import textwrap
+
+#from docutils import core
+from docutils.frontend import OptionParser
+from docutils.io import StringOutput
+import docutils.nodes
 from docutils.nodes import GenericNodeVisitor
-#from docutils import core
 from docutils.writers import Writer
 from sphinx.builders import Builder
 from sphinx.util.console import bold, darkgreen
-from docutils.frontend import OptionParser
+from sphinx.util.console import purple, red, darkgreen, darkgray
 from sphinx.util.nodes import inline_all_toctrees
-from docutils.io import StringOutput
-
-import itertools
-import re
-import textwrap
-import imp
 
 import enchant
 from enchant.tokenize import get_tokenizer
 
         return
 
-class Section(object):
-    
-    def __init__(self, spelling_checker):
-        self.title = ''
-        self.spelling_checker = spelling_checker
-        self.errors = []
-        self.subsections = []
         
-    def get_text_lines(self):
-        lines = self.errors[:]
-
-        for subsection in self.subsections:
-            lines.extend(' ' + line
-                         for line in subsection.get_text_lines()
-                         )
-
-        if lines:
-            lines.insert(0, self.title)
-            lines.insert(0, '')
-        return lines
-    
-    def add_subsection(self, subsection):
-        self.subsections.append(subsection)
-        return
-    
-    def set_title(self, title):
-        self.title = title
-        self.check_spelling(title)
-        
-    def astext(self):
-        lines = itertools.chain(*tuple(section.get_text_lines()
-                                       for section in self.subsections
-                                       )
-                                 )
-        # May want this formatting when suggestions are added
-        real_lines = []
-        for line in lines:
-            initial_indent = line[:len(line)-len(line.lstrip())]
-            if line.lstrip().startswith('-'): # error line
-                subsequent_indent = initial_indent + '  '
-            else:
-                subsequent_indent = initial_indent
-            line = textwrap.fill(line.lstrip(), width=70,
-                                 initial_indent=initial_indent,
-                                 subsequent_indent=subsequent_indent,
-                                 )
-            real_lines.append(line)
-        return '\n'.join(real_lines)
-
-    def add_text(self, text):
-        self.check_spelling(text)
-    
-    def check_spelling(self, text):
-        for word, suggestions in self.spelling_checker.check(text):
-            msg = '- "%s"' % word
-            if suggestions:
-                msg = '%s: %s' % (msg, ', '.join(suggestions))
-            self.errors.append(msg)
-        return
-        
-TEXT_NODES = set([ 'block_quote', 'paragraph',
-                   'list_item', 'term', 'definition_list_item', ])
-    
-
-class SpellingVisitor(GenericNodeVisitor):
-    """A Visitor class; see the docutils for more details.
-    """
-    def __init__(self, checker, *args, **kw):
-        GenericNodeVisitor.__init__(self, *args, **kw)
-        self.spelling_checker = checker
-        self.sections = [ Section(self.spelling_checker) ]
-
-    def visit_title(self, node):
-        # Set the title for the current section.
-        if not self.sections[-1].title:
-            self.sections[-1].set_title(node.astext())
-
-    def visit_section(self, node):
-        # Start a new section
-        new_section = Section(self.spelling_checker)
-        self.sections[-1].add_subsection(new_section)
-        self.sections.append(new_section)
-        
-    def depart_section(self, node):
-        # Pop the section stack
-        self.sections.pop()
-
-    def visit_Text(self, node):
-        if node.parent.tagname in TEXT_NODES:
-            self.sections[-1].add_text(node.astext())
-
-    def default_visit(self, node): pass
-    def default_departure(self, node): pass
-
-    def unknown_visit(self, node):
-        self.document.reporter.warning('Ignoring node: %s' % node.tagname)
-    def unknown_departure(self, node): pass
-
-    # Ignore conditional "only" nodes
-    def visit_only(self, node): pass
-    def depart_only(self, node): pass
-
-    # Ignore inline literal text
-    def visit_literal(self, node): pass
-    def depart_literal(self, node): pass
-    def visit_emphasis(self, node): pass
-    def depart_emphasis(self, node): pass
-
-    # Ignore comment blocks
-    def visit_comment(self, node): pass
-    def depart_comment(self, node): pass
-
-    # Ignore literal blocks
-    def visit_literal_block(self, node): pass
-
-    def astext(self):
-        body = '\n'.join(section.astext()
-                         for section in self.sections
-                         )
-        return body
-
-
-class SpellingWriter(Writer):
-    """Boilerplate attaching our Visitor to a docutils document."""
-    def __init__(self, checker, *args, **kwds):
-        self.spelling_checker = checker
-        Writer.__init__(self, *args, **kwds)
-    def translate(self):
-        visitor = SpellingVisitor(self.spelling_checker, self.document)
-        self.document.walkabout(visitor)
-        self.output = '\n' + visitor.astext() + '\n'
+TEXT_NODES = set([ 'block_quote',
+                   'paragraph',
+                   'list_item',
+                   'term',
+                   'definition_list_item',
+                   ])
 
 
 class SpellingBuilder(Builder):
     def get_outdated_docs(self):
         return 'all documents'
 
-    def write(self, *ignored):
-        self.output = []
+    def prepare_writing(self, docnames):
+        return
 
+    def get_target_uri(self, docname, typ=None):
+        return ''
+
+    def format_suggestions(self, suggestions):
+        if not self.config.spelling_show_suggestions or not suggestions:
+            return u''
+        return u'[' + u', '.join(u'"%s"' % s for s in suggestions) + u']'
+
+    def write_doc(self, docname, doctree):
+        word_list_filename = os.path.join(self.srcdir, self.config.spelling_word_list_filename)
         checker = SpellingChecker(lang=self.config.spelling_lang,
                                   suggest=self.config.spelling_show_suggestions,
-                                  word_list_filename=self.config.spelling_word_list_filename,
+                                  word_list_filename=word_list_filename,
                                   )
         
-        master = self.config.master_doc
-        docwriter = SpellingWriter(checker=checker)
-        docsettings = OptionParser(
-            defaults=self.env.settings,
-            components=(docwriter,)).get_default_values()
+        for node in doctree.traverse(docutils.nodes.Text):
+            if node.tagname == '#text' and  node.parent.tagname in TEXT_NODES:
+
+                # Figure out the line number for this node by climbing the
+                # tree until we find a node that has a line number.
+                lineno = None
+                parent = node
+                seen = set()
+                while lineno is None:
+                    #self.info('looking for line number on %r' % node)
+                    seen.add(parent)
+                    parent = node.parent
+                    if parent is None or parent in seen:
+                        break
+                    lineno = parent.line
+                filename = self.env.doc2path(docname, base=None)
+
+                # Check the text of the node.
+                for word, suggestions in checker.check(node.astext()):
+                    msg_parts = []
+                    if lineno:
+                        msg_parts.append(darkgreen('(line %3d)' % lineno))
+                    msg_parts.append(red(word))
+                    msg_parts.append(self.format_suggestions(suggestions))
+                    msg = ' '.join(msg_parts)
+                    self.info(msg)
+                    self.write_entry(docname, lineno, word, suggestions)
+
+                    # We found at least one bad spelling, so set the status
+                    # code for the app to a value that indicates an error.
+                    self.app.statuscode = 1
+        return
+
+    def write_entry(self, docname, lineno, word, suggestions):
+        output = codecs.open(os.path.join(self.outdir, 'output.txt'), 'a', encoding='UTF-8')
+        try:
+            output.write(u"%s:%s: [%s] %s\n" % (self.env.doc2path(docname, None),
+                                                lineno, word,
+                                                self.format_suggestions(suggestions),
+                                                )
+                         )
+        finally:
+            output.close()
             
-        tree = self.env.get_doctree(master)
-        tree = inline_all_toctrees(self, set(), master, tree, darkgreen)
-        tree['docname'] = master
-
-        destination = StringOutput(encoding='utf-8')
-        text = docwriter.write(tree, destination).strip()
-        if text:
-            self.output.append(text)
-
-        return
-    
     def finish(self):
-        self.info()
-        for text in self.output:
-            self.info(text)
+        output_filename = os.path.join(self.outdir, 'output.txt')
+        self.info('Spelling checker messages written to %s' % output_filename)
         return
 
 def setup(app):