diff --git a/sphinx/__init__.py b/sphinx/__init__.py index fbeeca098..1a78304b1 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -15,7 +15,7 @@ import getopt from os import path from cStringIO import StringIO -from sphinx.application import Application +from sphinx.application import Sphinx from sphinx.util.console import nocolor __version__ = '$Revision: 5369 $'[11:-2] @@ -103,8 +103,8 @@ def main(argv=sys.argv): elif opt == '-P': use_pdb = True - app = Application(srcdir, outdir, doctreedir, buildername, - confoverrides, status, sys.stderr, freshenv) + app = Sphinx(srcdir, outdir, doctreedir, buildername, + confoverrides, status, sys.stderr, freshenv) if not app.builder: return 1 diff --git a/sphinx/application.py b/sphinx/application.py index 76fbe53e1..00b5da23a 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -49,7 +49,7 @@ events = { 'doctree-resolved' : 'the doctree, the docname', } -class Application(object): +class Sphinx(object): def __init__(self, srcdir, outdir, doctreedir, buildername, confoverrides, status, warning=sys.stderr, freshenv=False): @@ -177,9 +177,10 @@ class Application(object): def add_node(self, node): nodes._add_node_class_names([node.__name__]) - def add_directive(self, name, cls, content, arguments): + def add_directive(self, name, cls, content, arguments, **options): cls.content = content cls.arguments = arguments + cls.options = options directives.register_directive(name, cls) def add_role(self, name, role): diff --git a/sphinx/builder.py b/sphinx/builder.py index f609e4285..2d2c4c892 100644 --- a/sphinx/builder.py +++ b/sphinx/builder.py @@ -32,7 +32,7 @@ from sphinx.htmlhelp import build_hhx from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator from sphinx.latexwriter import LaTeXWriter from sphinx.environment import BuildEnvironment, NoUri -from sphinx.highlighting import pygments, get_stylesheet +from sphinx.highlighting import PygmentsBridge from sphinx.util.console import bold, purple, red, darkgreen # side effect: registers roles and directives @@ -212,7 +212,7 @@ class Builder(object): # individually self.write(docnames, updated_docnames) - # finish (write style files etc.) + # finish (write static files etc.) self.info(bold('finishing... ')) self.finish() if self.app._warncount: @@ -242,9 +242,9 @@ class Builder(object): 'writing output... ', darkgreen): try: doctree = self.env.get_and_resolve_doctree(docname, self) - self.write_doc(docname, doctree) except Exception, err: warnings.append('%s:: doctree not found!' % docname) + self.write_doc(docname, doctree) for warning in warnings: if warning.strip(): self.warn(warning) @@ -317,6 +317,7 @@ class StandaloneHTMLBuilder(Builder): release = self.config.release, version = self.config.version, last_updated = self.last_updated, + style = self.config.html_style, builder = self.name, parents = [], titles = {}, @@ -442,20 +443,21 @@ class StandaloneHTMLBuilder(Builder): indextemplate = self.config.html_index if indextemplate: indextemplate = path.join(self.srcdir, indextemplate) - self.handle_page('index', {'indextemplate': indextemplate}, 'index.html') + self.handle_page('index', {'indextemplate': indextemplate}, 'index.html') - # copy style files - self.info(bold('copying style files...')) - styledirname = path.join(path.dirname(__file__), 'style') - ensuredir(path.join(self.outdir, 'style')) - for filename in os.listdir(styledirname): - if not filename.startswith('.'): - shutil.copyfile(path.join(styledirname, filename), - path.join(self.outdir, 'style', filename)) + # copy static files + self.info(bold('copying static files...')) + ensuredir(path.join(self.outdir, 'static')) + staticdirnames = path.join(path.dirname(__file__), 'static') + \ + self.config.static_path + for staticdirname in staticdirnames: + for filename in os.listdir(staticdirname): + if not filename.startswith('.'): + shutil.copyfile(path.join(staticdirname, filename), + path.join(self.outdir, 'static', filename)) # add pygments style file - f = open(path.join(self.outdir, 'style', 'pygments.css'), 'w') - if pygments: - f.write(get_stylesheet()) + f = open(path.join(self.outdir, 'static', 'pygments.css'), 'w') + f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet()) f.close() # dump the search index @@ -766,10 +768,10 @@ class LaTeXBuilder(Builder): def finish(self): self.info(bold('copying TeX support files...')) - styledirname = path.join(path.dirname(__file__), 'texinputs') - for filename in os.listdir(styledirname): + staticdirname = path.join(path.dirname(__file__), 'texinputs') + for filename in os.listdir(staticdirname): if not filename.startswith('.'): - shutil.copyfile(path.join(styledirname, filename), + shutil.copyfile(path.join(staticdirname, filename), path.join(self.outdir, filename)) @@ -873,7 +875,7 @@ class ChangesBuilder(Builder): f.write(self.stemplate.render(ctx)) finally: f.close() - shutil.copyfile(path.join(path.dirname(__file__), 'style', 'default.css'), + shutil.copyfile(path.join(path.dirname(__file__), 'static', 'default.css'), path.join(self.outdir, 'default.css')) def hl(self, text, version): @@ -918,7 +920,6 @@ class CheckExternalLinksBuilder(Builder): self.check(node, docname) except KeyError: continue - return def check(self, node, docname): uri = node['refuri'] diff --git a/sphinx/config.py b/sphinx/config.py index d3c9a4b59..442aab0a7 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -19,6 +19,9 @@ class Config(object): # the values are: (default, needs fresh doctrees if changed) + # If you add a value here, don't forget to include it in the + # quickstart.py file template as well! + config_values = dict( # general substitutions project = ('Python', True), @@ -38,8 +41,11 @@ class Config(object): unused_docs = ([], True), add_function_parentheses = (True, True), add_module_names = (True, True), + pygments_style = ('sphinx', False), # HTML options + html_style = ('default.css', False), + html_static_path = ([], False), html_last_updated_fmt = ('%b %d, %Y', False), html_use_smartypants = (True, False), html_translator_class = (None, False), diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index 8729758ce..7715681da 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -11,6 +11,7 @@ import sys import cgi +import re import parser try: @@ -21,15 +22,16 @@ try: from pygments.formatters import HtmlFormatter, LatexFormatter from pygments.filters import ErrorToken from pygments.style import Style + from pygments.styles import get_style_by_name from pygments.styles.friendly import FriendlyStyle from pygments.token import Generic, Comment, Number except ImportError: pygments = None else: - class PythonDocStyle(Style): + class SphinxStyle(Style): """ - Like friendly, but a bit darker to enhance contrast on - the green background. + Like friendly, but a bit darker to enhance contrast on the green + background. """ background_color = '#eeffcc' @@ -52,52 +54,63 @@ else: for _lexer in lexers.values(): _lexer.add_filter('raiseonerror') - hfmter = HtmlFormatter(style=PythonDocStyle) - lfmter = LatexFormatter(style=PythonDocStyle) - -def highlight_block(source, lang, dest='html'): - def unhighlighted(): - if dest == 'html': - return '
' + cgi.escape(source) + '\n' +class PygmentsBridge(object): + def __init__(self, dest='html', stylename='sphinx'): + if not pygments: + return + self.dest = dest + if stylename == 'sphinx': + style = SphinxStyle else: - return highlight(source, lexers['none'], lfmter) - if not pygments: - return unhighlighted() - if lang == 'python': - if source.startswith('>>>'): - # interactive session - lexer = lexers['pycon'] - else: - # maybe Python -- try parsing it - src = source + '\n' + style = get_style_by_name(stylename) + self.hfmter = HtmlFormatter(style=style) + self.lfmter = LatexFormatter(style=style) - # Replace "..." by a special mark, - # which is also a valid python expression - mark = "__highlighting__ellipsis__" - src = src.replace("...", mark) - - # lines beginning with "..." are probably placeholders for suite - import re - src = re.sub(r"(?m)^(\s*)" + mark + "(.)", r"\1"+ mark + r"# \2", src) - - # if we're using 2.5, use the with statement - if sys.version_info >= (2, 5): - src = 'from __future__ import with_statement\n' + src - - try: - parser.suite(src) - except (SyntaxError, UnicodeEncodeError): - return unhighlighted() + def highlight_block(self, source, lang): + def unhighlighted(): + if self.dest == 'html': + return '
' + cgi.escape(source) + '\n' else: - lexer = lexers['python'] - else: - lexer = lexers[lang] - try: - return highlight(source, lexer, dest == 'html' and hfmter or lfmter) - except ErrorToken: - # this is most probably not Python, so let it pass unhighlighted - return unhighlighted() + return highlight(source, lexers['none'], lfmter) + if not pygments: + return unhighlighted() + if lang == 'python': + if source.startswith('>>>'): + # interactive session + lexer = lexers['pycon'] + else: + # maybe Python -- try parsing it + src = source + '\n' -def get_stylesheet(dest='html'): - return (dest == 'html' and hfmter or lfmter).get_style_defs() + # Replace "..." by a special mark, which is also a valid python expression + # (Note, the highlighter gets the original source, this is only done + # to allow "..." in code and still highlight it as Python code.) + mark = "__highlighting__ellipsis__" + src = src.replace("...", mark) + + # lines beginning with "..." are probably placeholders for suite + src = re.sub(r"(?m)^(\s*)" + mark + "(.)", r"\1"+ mark + r"# \2", src) + + # if we're using 2.5, use the with statement + if sys.version_info >= (2, 5): + src = 'from __future__ import with_statement\n' + src + + try: + parser.suite(src) + except (SyntaxError, UnicodeEncodeError): + return unhighlighted() + else: + lexer = lexers['python'] + else: + lexer = lexers[lang] + try: + return highlight(source, lexer, self.dest == 'html' and self.hfmter or self.lfmter) + except ErrorToken: + # this is most probably not the selected language, so let it pass unhighlighted + return unhighlighted() + + def get_stylesheet(self): + if not pygments: + return '' + return (self.dest == 'html' and self.hfmter or self.lfmter).get_style_defs() diff --git a/sphinx/htmlwriter.py b/sphinx/htmlwriter.py index 08dc198f9..dc6838a9b 100644 --- a/sphinx/htmlwriter.py +++ b/sphinx/htmlwriter.py @@ -12,6 +12,7 @@ from docutils import nodes from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator +from sphinx.highlighting import PygmentsBridge from sphinx.util.smartypants import sphinx_smarty_pants @@ -45,6 +46,7 @@ class HTMLTranslator(BaseTranslator): def __init__(self, builder, *args, **kwds): BaseTranslator.__init__(self, *args, **kwds) + self.highlighter = PygmentsBridge('html', builder.config.pygments_style) self.no_smarty = 0 self.builder = builder self.highlightlang = 'python' @@ -173,8 +175,8 @@ class HTMLTranslator(BaseTranslator): # overwritten def visit_literal_block(self, node): - from sphinx.highlighting import highlight_block - self.body.append(highlight_block(node.rawsource, self.highlightlang)) + self.body.append(self.highlighter.highlight_block(node.rawsource, + self.highlightlang)) raise nodes.SkipNode # overwritten diff --git a/sphinx/latexwriter.py b/sphinx/latexwriter.py index 32bc49060..c11d3f68d 100644 --- a/sphinx/latexwriter.py +++ b/sphinx/latexwriter.py @@ -20,7 +20,7 @@ from docutils import nodes, writers from sphinx import addnodes from sphinx import highlighting -# Move to a template? +# XXX: Move to a template? HEADER = r'''%% Generated by Sphinx. \documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s} \usepackage[utf8]{inputenc} @@ -105,6 +105,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 'release': builder.config.release, 'date': date, } + self.highlighter = highlighting.PygmentsBridge('latex', builder.config.pygments_style) self.context = [] self.descstack = [] self.highlightlang = 'python' @@ -123,7 +124,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def astext(self): return (HEADER % self.options) + \ - highlighting.get_stylesheet('latex') + '\n\n' + \ + self.highlighter.get_stylesheet() + '\n\n' + \ u''.join(self.body) + \ (FOOTER % self.options) @@ -638,8 +639,8 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_literal_block(self, node): self.verbatim = '' def depart_literal_block(self, node): - hlcode = highlighting.highlight_block(self.verbatim.rstrip('\n'), - self.highlightlang, 'latex') + hlcode = self.highlighter.highlight_block(self.verbatim.rstrip('\n'), + self.highlightlang) # workaround for Unicode issue hlcode = hlcode.replace(u'€', u'@texteuro[]') # workaround for Pygments bug diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index b3643a0a2..05b729fc7 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -9,7 +9,7 @@ :license: BSD. """ -import sys, os, time +import sys, os, time, shutil from os import path from sphinx.util.console import darkgreen, purple, bold, red, nocolor @@ -27,7 +27,7 @@ QUICKSTART_CONF = '''\ # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out -# show the default value as assigned to them. +# serve to show the default value. import sys @@ -42,7 +42,7 @@ import sys #extensions = [] # Add any paths that contain templates here, relative to this directory. -#templates_path = [] +templates_path = ['%(dot)stemplates'] # The suffix of source filenames. source_suffix = '%(suffix)s' @@ -78,10 +78,23 @@ today_fmt = '%%B %%d, %%Y' # unit titles (such as .. function::). #add_module_names = True +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + # Options for HTML output # ----------------------- +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# 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 = ['%(dot)sstatic'] + # 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' @@ -139,6 +152,9 @@ def suffix(x): """Please enter a file suffix, e.g. '.rst' or '.txt'.""" return x[0:1] == '.' and len(x) > 1 +def ok(x): + return True + def do_prompt(d, key, text, default=None, validator=nonempty): while True: @@ -193,6 +209,12 @@ One document is special in that it is considered the top node of the of the documents. Normally, this is "index", but if your "index" document is a custom template, you can also set this to another filename.''' do_prompt(d, 'master', 'Name of your master document (without suffix)', 'index') + print ''' +Inside the "src" directory, two directories will be created; ".templates" +for custom HTML templates and ".static" for custom stylesheets and other +static files. Since the leading dot may be inconvenient for Windows users, +you can enter another prefix (such as "_") to replace the dot.''' + do_prompt(d, 'dot', 'Name prefix for templates and static dir', '.', ok) d['year'] = time.strftime('%Y') d['now'] = time.asctime() @@ -206,6 +228,11 @@ document is a custom template, you can also set this to another filename.''' masterfile = path.join(d['path'], 'src', d['master'] + d['suffix']) + templatedir = path.join(d['path'], 'src', d['dot'] + 'templates') + os.mkdir(templatedir) + staticdir = path.join(d['path'], 'src', d['dot'] + 'static') + os.mkdir(staticdir) + print print bold('Finished: An initial directory structure has been created.') print ''' diff --git a/sphinx/style/admin.css b/sphinx/static/admin.css similarity index 100% rename from sphinx/style/admin.css rename to sphinx/static/admin.css diff --git a/sphinx/style/comment.png b/sphinx/static/comment.png similarity index 100% rename from sphinx/style/comment.png rename to sphinx/static/comment.png diff --git a/sphinx/style/default.css b/sphinx/static/default.css similarity index 98% rename from sphinx/style/default.css rename to sphinx/static/default.css index c91a5c337..4e8d4d320 100644 --- a/sphinx/style/default.css +++ b/sphinx/static/default.css @@ -779,16 +779,11 @@ form.comment textarea { /* :::: PRINT :::: */ @media print { - div.documentwrapper { - width: 100%; - } - - div.document, - div.documentwrapper, - div.bodywrapper, - div.body { + div.document, + div.documentwrapper, + div.bodywrapper { margin: 0; - width : 100%; + width : 100%; } div.sidebar, diff --git a/sphinx/style/doctools.js b/sphinx/static/doctools.js similarity index 100% rename from sphinx/style/doctools.js rename to sphinx/static/doctools.js diff --git a/sphinx/style/file.png b/sphinx/static/file.png similarity index 100% rename from sphinx/style/file.png rename to sphinx/static/file.png diff --git a/sphinx/style/hovercomment.png b/sphinx/static/hovercomment.png similarity index 100% rename from sphinx/style/hovercomment.png rename to sphinx/static/hovercomment.png diff --git a/sphinx/style/interface.js b/sphinx/static/interface.js similarity index 100% rename from sphinx/style/interface.js rename to sphinx/static/interface.js diff --git a/sphinx/style/jquery.js b/sphinx/static/jquery.js similarity index 100% rename from sphinx/style/jquery.js rename to sphinx/static/jquery.js diff --git a/sphinx/style/minus.png b/sphinx/static/minus.png similarity index 100% rename from sphinx/style/minus.png rename to sphinx/static/minus.png diff --git a/sphinx/style/nocomment.png b/sphinx/static/nocomment.png similarity index 100% rename from sphinx/style/nocomment.png rename to sphinx/static/nocomment.png diff --git a/sphinx/style/plus.png b/sphinx/static/plus.png similarity index 100% rename from sphinx/style/plus.png rename to sphinx/static/plus.png diff --git a/sphinx/style/preview.png b/sphinx/static/preview.png similarity index 100% rename from sphinx/style/preview.png rename to sphinx/static/preview.png diff --git a/sphinx/style/rightsidebar.css b/sphinx/static/rightsidebar.css similarity index 100% rename from sphinx/style/rightsidebar.css rename to sphinx/static/rightsidebar.css diff --git a/sphinx/style/searchtools.js b/sphinx/static/searchtools.js similarity index 100% rename from sphinx/style/searchtools.js rename to sphinx/static/searchtools.js diff --git a/sphinx/style/stickysidebar.css b/sphinx/static/stickysidebar.css similarity index 100% rename from sphinx/style/stickysidebar.css rename to sphinx/static/stickysidebar.css diff --git a/sphinx/style/top.png b/sphinx/static/top.png similarity index 100% rename from sphinx/style/top.png rename to sphinx/static/top.png diff --git a/sphinx/style/traditional.css b/sphinx/static/traditional.css similarity index 100% rename from sphinx/style/traditional.css rename to sphinx/static/traditional.css diff --git a/sphinx/templates/layout.html b/sphinx/templates/layout.html index f54126912..281ea4798 100644 --- a/sphinx/templates/layout.html +++ b/sphinx/templates/layout.html @@ -1,9 +1,13 @@ -{% if builder != 'htmlhelp' %}{% set titlesuffix = " — " + project + " Documentation" %}{% endif -%} +{% block doctype -%} +{% endblock -%} + {%- if builder != 'htmlhelp' %} + {%- set titlesuffix = " — " + project + " Documentation" %} + {%- endif %}