* Allow custom static files to be created for the HTML builder.

* Add more block tags to the template, making inheriting them easier.
* Make the HTML stylesheet configurable for html and htmlhelp builder.
* Make the Pygments style configurable.
* Create template and style dirs in quickstart.
This commit is contained in:
Georg Brandl 2008-02-23 15:24:30 +00:00
parent 5740db88e8
commit 7d9721dd85
29 changed files with 165 additions and 102 deletions

View File

@ -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,7 +103,7 @@ def main(argv=sys.argv):
elif opt == '-P':
use_pdb = True
app = Application(srcdir, outdir, doctreedir, buildername,
app = Sphinx(srcdir, outdir, doctreedir, buildername,
confoverrides, status, sys.stderr, freshenv)
if not app.builder:
return 1

View File

@ -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):

View File

@ -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 = {},
@ -444,18 +445,19 @@ class StandaloneHTMLBuilder(Builder):
indextemplate = path.join(self.srcdir, indextemplate)
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):
# 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(styledirname, filename),
path.join(self.outdir, 'style', filename))
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']

View File

@ -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),

View File

@ -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,13 +54,22 @@ else:
for _lexer in lexers.values():
_lexer.add_filter('raiseonerror')
hfmter = HtmlFormatter(style=PythonDocStyle)
lfmter = LatexFormatter(style=PythonDocStyle)
class PygmentsBridge(object):
def __init__(self, dest='html', stylename='sphinx'):
if not pygments:
return
self.dest = dest
if stylename == 'sphinx':
style = SphinxStyle
else:
style = get_style_by_name(stylename)
self.hfmter = HtmlFormatter(style=style)
self.lfmter = LatexFormatter(style=style)
def highlight_block(source, lang, dest='html'):
def highlight_block(self, source, lang):
def unhighlighted():
if dest == 'html':
if self.dest == 'html':
return '<pre>' + cgi.escape(source) + '</pre>\n'
else:
return highlight(source, lexers['none'], lfmter)
@ -72,13 +83,13 @@ def highlight_block(source, lang, dest='html'):
# maybe Python -- try parsing it
src = source + '\n'
# Replace "..." by a special mark,
# which is also a valid python expression
# 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
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
@ -94,10 +105,12 @@ def highlight_block(source, lang, dest='html'):
else:
lexer = lexers[lang]
try:
return highlight(source, lexer, dest == 'html' and hfmter or lfmter)
return highlight(source, lexer, self.dest == 'html' and self.hfmter or self.lfmter)
except ErrorToken:
# this is most probably not Python, so let it pass unhighlighted
# this is most probably not the selected language, so let it pass unhighlighted
return unhighlighted()
def get_stylesheet(dest='html'):
return (dest == 'html' and hfmter or lfmter).get_style_defs()
def get_stylesheet(self):
if not pygments:
return ''
return (self.dest == 'html' and self.hfmter or self.lfmter).get_style_defs()

View File

@ -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

View File

@ -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

View File

@ -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 '''

View File

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 401 B

View File

@ -779,14 +779,9 @@ form.comment textarea {
/* :::: PRINT :::: */
@media print {
div.documentwrapper {
width: 100%;
}
div.document,
div.documentwrapper,
div.bodywrapper,
div.body {
div.bodywrapper {
margin: 0;
width : 100%;
}

View File

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 392 B

View File

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 522 B

View File

Before

Width:  |  Height:  |  Size: 199 B

After

Width:  |  Height:  |  Size: 199 B

View File

Before

Width:  |  Height:  |  Size: 415 B

After

Width:  |  Height:  |  Size: 415 B

View File

Before

Width:  |  Height:  |  Size: 199 B

After

Width:  |  Height:  |  Size: 199 B

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 976 B

After

Width:  |  Height:  |  Size: 976 B

View File

@ -1,9 +1,13 @@
{% if builder != 'htmlhelp' %}{% set titlesuffix = " &mdash; " + project + " Documentation" %}{% endif -%}
{% block doctype -%}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
{% endblock -%}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
{%- if builder != 'htmlhelp' %}
{%- set titlesuffix = " &mdash; " + project + " Documentation" %}
{%- endif %}
<title>{{ title|striptags }}{{ titlesuffix }}</title>
{%- if builder == 'web' %}
<link rel="stylesheet" href="{{ pathto('index') }}?do=stylesheet{%
@ -12,8 +16,8 @@
<link rel="alternate" type="{{ type|e(true) }}" title="{{ title|e(true) }}" href="{{ link|e(true) }}">
{%- endfor %}
{%- else %}
<link rel="stylesheet" href="{{ pathto('style/default.css', 1) }}" type="text/css">
<link rel="stylesheet" href="{{ pathto('style/pygments.css', 1) }}" type="text/css">
<link rel="stylesheet" href="{{ pathto('static/' + style, 1) }}" type="text/css">
<link rel="stylesheet" href="{{ pathto('static/pygments.css', 1) }}" type="text/css">
{%- endif %}
{%- if builder != 'htmlhelp' %}
<script type="text/javascript">
@ -22,10 +26,11 @@
VERSION: '{{ release }}'
};
</script>
<script type="text/javascript" src="{{ pathto('style/jquery.js', 1) }}"></script>
<script type="text/javascript" src="{{ pathto('style/interface.js', 1) }}"></script>
<script type="text/javascript" src="{{ pathto('style/doctools.js', 1) }}"></script>
<script type="text/javascript" src="{{ pathto('static/jquery.js', 1) }}"></script>
<script type="text/javascript" src="{{ pathto('static/interface.js', 1) }}"></script>
<script type="text/javascript" src="{{ pathto('static/doctools.js', 1) }}"></script>
{%- endif %}
{%- block rellinks %}
{%- if hasdoc('about') %}
<link rel="author" title="About these documents" href="{{ pathto('about') }}">
{%- endif %}
@ -45,10 +50,13 @@
{%- if prev %}
<link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}">
{%- endif %}
{% block head %}{% endblock %}
{%- endblock %}
{%- block extrahead %}{% endblock %}
</head>
<body>
{% filter capture('relbar') %}
{%- filter capture('relbar') %}
{%- block relbar %}
<div class="related">
<h3>Navigation</h3>
<ul>
@ -70,7 +78,9 @@
{%- endfor %}
</ul>
</div>
{% endfilter %}
{%- endblock %}
{%- endfilter %}
<div class="document">
<div class="documentwrapper">
{%- if builder != 'htmlhelp' %}
@ -83,6 +93,8 @@
</div>
{%- endif %}
</div>
{%- block sidebar %}
{%- if builder != 'htmlhelp' %}
<div class="sidebar">
<div class="sidebarwrapper">
@ -128,9 +140,13 @@
</div>
</div>
{%- endif %}
{%- endblock %}
<div class="clearer"></div>
</div>
{%- block bottomrelbar %}
{{ relbar }}
{%- endblock %}
{%- block footer %}
<div class="footer">
{%- if hasdoc('copyright') %}
&copy; <a href="{{ pathto('copyright') }}">Copyright</a> {{ copyright }}.
@ -141,5 +157,6 @@
Last updated on {{ last_updated }}.
{%- endif %}
</div>
{%- endblock %}
</body>
</html>

View File

@ -32,7 +32,7 @@
{%- else -%}
<tr{% if indent %} class="cg-{{ cgroup }}"{% endif %}>
<td>{% if collapse -%}
<img src="{{ pathto('style/minus.png', 1) }}" id="toggle-{{ cgroup }}"
<img src="{{ pathto('static/minus.png', 1) }}" id="toggle-{{ cgroup }}"
class="toggler" style="display: none">
{%- endif %}</td>
<td>{% if indent %}&nbsp;&nbsp;&nbsp;{% endif %}

View File

@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% set title = 'Search Documentation' %}
{% block head %}
<script type="text/javascript" src="{{ pathto('style/searchtools.js', 1) }}"></script>
<script type="text/javascript" src="{{ pathto('static/searchtools.js', 1) }}"></script>
{% endblock %}
{% block body %}
<h1 id="search-documentation">Search Documentation</h1>

View File

@ -821,6 +821,6 @@ def setup_app(config, check_superuser=False):
if check_superuser:
_check_superuser(app)
app = SharedDataMiddleware(app, {
'/style': path.join(config['data_root_path'], 'style')
'/static': path.join(config['data_root_path'], 'static')
})
return app