diff --git a/demo/source/conf.py b/demo/source/conf.py index 574b75d7..2749a20f 100644 --- a/demo/source/conf.py +++ b/demo/source/conf.py @@ -17,7 +17,12 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # Disabled: , 'sphinx.ext.intersphinx' -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode' +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -62,15 +67,15 @@ #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +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 +add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +show_authors = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -81,8 +86,21 @@ # Enable todo output todo_include_todos = True +autoclass_content = 'class' +autodoc_member_order = 'bysource' +autodoc_default_flags = [ + 'members', + 'undoc-members', + 'private-members', + # 'special-members', + # 'inherited-members', + 'show-inheritance' +] + # -- Options for HTML output --------------------------------------------------- +html_translator_class = 'sphinx_bootstrap_theme.BootstrapTranslator' + # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'bootstrap' @@ -132,7 +150,7 @@ # Location of link to source. # Options are "nav" (default), "footer" or anything else to exclude. - 'source_link_position': "nav", + 'source_link_position': "footer", # Bootswatch (http://bootswatch.com/) theme. # @@ -199,7 +217,10 @@ # Custom sidebar templates, maps document names to template names. #html_sidebars = {} -html_sidebars = {'sidebar': ['localtoc.html', 'sourcelink.html', 'searchbox.html']} +html_sidebars = { + 'sidebar': ['localtoc.html', 'sourcelink.html', 'searchbox.html'], + 'examples': ['localtoc.html'] +} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -218,10 +239,10 @@ #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the diff --git a/demo/source/example_module.rst b/demo/source/example_module.rst new file mode 100644 index 00000000..dfc86d86 --- /dev/null +++ b/demo/source/example_module.rst @@ -0,0 +1,8 @@ +Example Module (:mod:`ExampleModule`) +===================================== + +.. automodule:: sphinx_bootstrap_theme.example_module + :members: + :undoc-members: + :private-members: + :special-members: diff --git a/demo/source/examples.rst b/demo/source/examples.rst index 06d695da..412e6be2 100644 --- a/demo/source/examples.rst +++ b/demo/source/examples.rst @@ -77,6 +77,18 @@ Danger ------ .. danger:: This is **danger**-ous. +Attention +--------- +.. attention:: This is a note. + +See Also +-------- +.. seealso:: Probably a reference. + +Error +----- +.. error:: This is a error. + Footnotes ========= I have footnoted a first item [#f1]_ and second item [#f2]_. diff --git a/demo/source/html_bootstrap_translator.rst b/demo/source/html_bootstrap_translator.rst new file mode 100644 index 00000000..121692a6 --- /dev/null +++ b/demo/source/html_bootstrap_translator.rst @@ -0,0 +1,8 @@ +HTML Bootstrap Translator (:mod:`html_bootstrap_translator`) +============================================================ + +.. automodule:: sphinx_bootstrap_theme.html_bootstrap_translator + :members: + :undoc-members: + :private-members: + :special-members: diff --git a/demo/source/index.rst b/demo/source/index.rst index f7a7b20a..53e76925 100644 --- a/demo/source/index.rst +++ b/demo/source/index.rst @@ -22,6 +22,9 @@ Setting up and using the theme. examples sidebar subdir/index + example_module + html_bootstrap_translator + Development history and feature wish lists. diff --git a/sphinx_bootstrap_theme/__init__.py b/sphinx_bootstrap_theme/__init__.py index 08736913..37e08524 100644 --- a/sphinx_bootstrap_theme/__init__.py +++ b/sphinx_bootstrap_theme/__init__.py @@ -6,7 +6,10 @@ __version__ = ".".join(str(v) for v in VERSION) __version_full__ = __version__ + def get_html_theme_path(): """Return list of HTML theme paths.""" cur_dir = os.path.abspath(os.path.dirname(__file__)) return [cur_dir] + +from sphinx_bootstrap_theme.html_bootstrap_translator import BootstrapTranslator diff --git a/sphinx_bootstrap_theme/bootstrap/layout.html b/sphinx_bootstrap_theme/bootstrap/layout.html index 18e16904..993d5381 100644 --- a/sphinx_bootstrap_theme/bootstrap/layout.html +++ b/sphinx_bootstrap_theme/bootstrap/layout.html @@ -14,6 +14,7 @@ {% if theme_bootstrap_version == "3" %} {% set theme_css_files = theme_css_files + [ '_static/bootswatch-' + bootstrap_version + '/' + theme_bootswatch_theme + '/bootstrap.min.css', + '_static/font-awesome-4.0.3/css/font-awesome.min.css', '_static/bootstrap-sphinx.css' ] %} @@ -21,6 +22,7 @@ {% set theme_css_files = theme_css_files + [ '_static/bootswatch-' + bootstrap_version + '/' + theme_bootswatch_theme + '/bootstrap.min.css', '_static/bootstrap-' + bootstrap_version + '/css/bootstrap-' + bootstrap_additional_css + '.min.css', + '_static/font-awesome-4.0.3/css/font-awesome.min.css', '_static/bootstrap-sphinx.css' ] %} @@ -29,6 +31,7 @@ {% set theme_css_files = theme_css_files + [ '_static/bootstrap-' + bootstrap_version + '/css/bootstrap.min.css', '_static/bootstrap-' + bootstrap_version + '/css/bootstrap-' + bootstrap_additional_css + '.min.css', + '_static/font-awesome-4.0.3/css/font-awesome.min.css', '_static/bootstrap-sphinx.css' ] %} @@ -102,7 +105,7 @@ {%- block content %} {{ navBar() }}
-
+
{%- block sidebar1 %}{{ bsidebar() }}{% endblock %}
{% block body %}{% endblock %} @@ -114,28 +117,91 @@ {%- block footer %}
-
-

- Back to top - {% if theme_source_link_position == "footer" %} -
- {% include "sourcelink.html" %} - {% endif %} -

-

- {%- if show_copyright %} - {%- if hasdoc('copyright') %} - {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
- {%- else %} - {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
+ {% if theme_bootstrap_version == "3" %} +

+ {% else %} + + {% endif %} +
diff --git a/sphinx_bootstrap_theme/bootstrap/navbar-2.html b/sphinx_bootstrap_theme/bootstrap/navbar-2.html index c4f87c88..992f05de 100644 --- a/sphinx_bootstrap_theme/bootstrap/navbar-2.html +++ b/sphinx_bootstrap_theme/bootstrap/navbar-2.html @@ -30,11 +30,6 @@ {% include "navbartoc.html" %} {% endif %} {% endblock %} - {% if theme_navbar_sidebarrel %} - {% block sidebarrel %} - {% include "relations.html" %} - {% endblock %} - {% endif %} {% block navbarextra %} {% endblock %} {% if theme_source_link_position == "nav" %} diff --git a/sphinx_bootstrap_theme/bootstrap/navbar.html b/sphinx_bootstrap_theme/bootstrap/navbar.html index d0bc85aa..b9ae64f4 100644 --- a/sphinx_bootstrap_theme/bootstrap/navbar.html +++ b/sphinx_bootstrap_theme/bootstrap/navbar.html @@ -1,4 +1,4 @@ - \n') + + def visit_note(self, node): + self.visit_admonition(node, 'note') + + def depart_note(self, node): + self.depart_admonition(node, 'note') + + def visit_attention(self, node): + self.visit_admonition(node, 'attention') + + def depart_attention(self, node): + self.depart_admonition(node, 'attention') + + def visit_warning(self, node): + self.visit_admonition(node, 'warning') + + def depart_warning(self, node): + self.depart_admonition(node, 'warning') + + def visit_danger(self, node): + self.visit_admonition(node, 'danger') + + def depart_danger(self, node): + self.depart_admonition(node, 'danger') + + def visit_error(self, node): + self.visit_admonition(node, 'error') + + def depart_error(self, node): + self.depart_admonition(node, 'error') + + def visit_seealso(self, node): + self.visit_admonition(node, 'seealso') + + def depart_seealso(self, node): + self.depart_admonition(node, 'seealso') + + def visit_example(self, node): + self.visit_admonition(node, 'example') + + def depart_example(self, node): + self.depart_admonition(node, 'example') + + def visit_todo_node(self, node): + node.remove(node[0]) # remove additional 'Todo' title node + self.visit_admonition(node, 'todo') + + def depart_todo_node(self, node): + self.depart_admonition(node, 'todo') + + def visit_index(self, node): + pass + + def depart_index(self, node): + pass + + # overridden + def visit_bullet_list(self, node): + atts = {} + old_compact_simple = self.compact_simple + self.context.append((self.compact_simple, self.compact_p)) + self.compact_p = None + self.compact_simple = self.is_compactable(node) + if self.compact_simple and not old_compact_simple: + atts['class'] = ' simple' + self.body.append(self.starttag(node, 'ul', **atts)) + + # overridden + def visit_list_item(self, node): + self.body.append(self.starttag(node, 'li', '',)) + if len(node): + node[0]['classes'].append('first') + + # overridden + def visit_definition_list(self, node): + """Bootstrap-ified Definition List + + .. admonition:: Example + + The ReST definition list:: + + Key One + Is the first + + Second Key + Is the second + + is rendered to: + + Key One + Is the first + + Second Key + Is the second + """ + self.body.append(self.starttag(node, 'dl', CLASS='dl-horizontal')) + + # overridden + def visit_field_list(self, node): + self.body.append( + self.starttag(node, 'div', + CLASS='accordion panel-group field-list')) + + # overridden + def depart_field_list(self, node): + self.body.append('
\n') + + def _fixup_return_type(self, node): + if node[0].astext() == 'Returns': + _this_index = node.parent.index(node) + + if len(node.parent.children) > _this_index + 1: + if node.parent[_this_index + 1][0].astext() == 'Return type': + _rtype = node.parent[_this_index + 1] + _rtype_new = [nodes.Text(' (')] + if isinstance(_rtype[1][0], nodes.paragraph): + for elem in _rtype[1][0]: + _rtype_new.append(elem.deepcopy()) + _rtype_new.append(nodes.Text(')')) + _strongs = node.traverse(condition=nodes.strong) + + if len(_strongs) > 0: + _return_name = _strongs[0] + _return_para = _return_name.parent + _first_strong_id_in_its_parent = \ + _return_para.index(_strongs[0]) + _return_para.insert(_first_strong_id_in_its_parent + 1, + _rtype_new) + + if isinstance(_return_para, nodes.paragraph) \ + and isinstance(_return_para.parent, + nodes.paragraph): + _return_para_parent = _return_para.parent + if len(_return_para_parent) > 1 \ + and isinstance(_return_para_parent[1], + (nodes.bullet_list, + nodes.definition_list, + nodes.field_list)): + _para = [] + for _p in _return_para_parent[0]: + _para.append(_p.deepcopy()) + _list = _return_para_parent[1].deepcopy() + _return_para_parent.clear() + _return_para_parent.extend(_para) + _return_para_parent.append(_list) + + # overridden + def visit_field(self, node): + if node[0][0].astext() == 'Return type': + # return type should be handled by _fixup_return_type + # on 'Returns' nodes + raise nodes.SkipNode + + _contextual_class = 'default' + self._fixup_return_type(node) + _field_name_title = node[0][0].astext() + if _field_name_title == 'Raises': + _contextual_class = 'warning' + if node[0][0].astext() in ['Raises']: + if node[0].__len__() == 3: + node[1][0].insert(0, nodes.Text(' -- ')) + node[1][0].insert(0, nodes.strong(text=node[0][2].astext())) + del node[0][2] + del node[0][1] + self.field_context.append(_field_name_title) + self.body.append( + self.starttag(node, 'div', + CLASS=('accordion-goup panel ' + 'panel-%s field' % _contextual_class))) + self.section_level += 1 + + # overridden + def depart_field(self, node): + self.field_context.pop() + self.section_level -= 1 + self.body.append('
') + + # overridden + def visit_field_name(self, node): + self.body.append( + self.starttag(node, 'div', '', + CLASS='accordion-heading panel-heading field-name')) + self.body.append('' % self.section_level) + + # overridden + def depart_field_name(self, node): + self.body.append('
' % self.section_level) + + # overridden + def visit_field_body(self, node): + self.body.append( + self.starttag(node, 'div', '', + CLASS='accordion-inner panel-body field-body')) + if self.field_context[-1] in ['Parameters', 'Raises', 'Returns']: + self._print_parameters(node) + + # overridden + def depart_field_body(self, node): + self.body.append('
') + + # overridden + def visit_table(self, node): + self.context.append(self.compact_p) + self.compact_p = True + classes = ' '.join(['table', self.settings.table_style]).strip() + self.body.append( + self.starttag(node, 'table', CLASS=classes)) + + # overridden + def depart_table(self, node): + self.compact_p = self.context.pop() + self.body.append('\n') + + def visit_desc(self, node): + if node['objtype'] in member_types.keys() \ + and node['objtype'] != 'staticmethod': + node[0].insert(0, + addnodes + .desc_annotation(text=member_types[node['objtype']])) + self.body.append( + self.starttag(node, 'div', + CLASS=('accordion-group panel panel-default ' + '%s' % node['objtype']))) + if node['objtype'] in ['class']: + self.collapse_context.append([node['objtype'], + '%s-id%d' % (node['objtype'], + self.collapse_id_count)]) + self.collapse_id_count += 1 + if len(self.collapse_context) > 0 \ + and node['objtype'] in member_types.keys(): + self.collapse_context[-1]\ + .append('%s-id%d' % (node['objtype'], self.collapse_item_count)) + self.collapse_item_count += 1 + self.section_level += 1 + + def depart_desc(self, node): + self.section_level -= 1 + if node['objtype'] in ['class']: + self.collapse_context.pop() + if len(self.collapse_context) > 0 \ + and node['objtype'] in member_types.keys(): + self.collapse_context[-1].pop() + self.body.append('') + + def visit_desc_signature(self, node): + self.body.append( + self.starttag(node, 'div', + CLASS=('accordion-heading panel-heading ' + 'desc-signature'))) + self.body.append('' % self.section_level) + if len(self.collapse_context) > 0 \ + and self.collapse_context[-1][0] in ['class'] \ + and len(self.collapse_context[-1]) == 3: + self.body.append( + '' % self.collapse_context[-1][2]) + + def depart_desc_signature(self, node): + if len(self.collapse_context) > 0 \ + and self.collapse_context[-1][0] in ['class'] \ + and len(self.collapse_context[-1]) == 3: + self.body.append('') + self.body.append('' % self.section_level) + + def visit_desc_name(self, node): + self.body.append(self.starttag(node, 'tt', '', CLASS='desc-name')) + + def depart_desc_name(self, node): + self.body.append('') + + def visit_desc_addname(self, node): + self.body.append( + self.starttag(node, 'tt', '', CLASS='desc-addname')) + self.body.append('') + + def depart_desc_addname(self, node): + self.body.append('') + + def visit_desc_parameterlist(self, node): + """The list of parameters in a method signature. + Including the enclosing brackets. + """ + self.body.append( + self.starttag(node, 'tt', '', CLASS='desc-parameterlist')) + self.body.append('(') + self.first_param = 1 + self.optional_param_level = 0 + # How many required parameters are left. + self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) + self.param_separator = node.child_text_separator + + def depart_desc_parameterlist(self, node): + self.body.append(')') + self.body.append('') + + def visit_desc_parameter(self, node): + """A single parameter in a method signature + """ + self.body.append( + self.starttag(node, 'span', '', CLASS='desc-parameter')) + if self.first_param: + self.first_param = 0 + elif not self.required_params_left: + self.body.append(self.param_separator) + if self.optional_param_level == 0: + self.required_params_left -= 1 + if not node.hasattr('noemph'): + self.body.append('') + + def depart_desc_parameter(self, node): + if not node.hasattr('noemph'): + self.body.append('') + if self.required_params_left: + self.body.append(self.param_separator) + self.body.append('') + + def visit_desc_optional(self, node): + """Optional parameters in a method signature. + """ + self.body.append(self.starttag(node, 'span', '', CLASS='desc-optional')) + self.optional_param_level += 1 + self.body.append('[') + + def depart_desc_optional(self, node): + self.optional_param_level -= 1 + self.body.append(']') + + def visit_desc_returns(self, node): + self.body.append(self.starttag(node, 'tt', '', CLASS='desc-returns')) + + def depart_desc_returns(self, node): + self.body.append('') + + def visit_desc_content(self, node): + """The content of a class, method or attribute. + """ + if len(self.collapse_context) > 0 \ + and self.collapse_context[-1][0] in ['class'] \ + and len(self.collapse_context[-1]) == 2: + self.body.append( + '
' % self.collapse_context[-1][1]) + self.collapse_id_count += 1 + + if len(self.collapse_context) > 0 \ + and self.collapse_context[-1][0] in ['class'] \ + and len(self.collapse_context[-1]) == 3: + self.body.append( + '
') + self.body.append( + self.starttag(node, 'div', + CLASS='accordion-inner panel-body desc-content')) + + def depart_desc_content(self, node): + if len(self.collapse_context) > 0 \ + and self.collapse_context[-1][0] in ['class'] \ + and len(self.collapse_context[-1]) == 2: + self.body.append('
') + + if len(self.collapse_context) > 0 \ + and self.collapse_context[-1][0] in ['class'] \ + and len(self.collapse_context[-1]) == 3: + self.body.append('
') + self.body.append('') + + def visit_desc_annotation(self, node): + """Annotations in object signatures. + + This is either the ``class``, ``method`` or ``attribute`` identifier or + the value of module or class attributes. + """ + self.body.append( + self.starttag(node, 'tt', '', + CLASS='desc-annotation text-muted muted')) + + def depart_desc_annotation(self, node): + self.body.append('') + + def visit_toctree(self, node): + self.body.append(self.starttag(node, 'span', CLASS='toctree')) + + def depart_toctree(self, node): + self.body.append('') + + # the following four methods are taken from + # http://stackoverflow.com/a/15562804 + def visit_displaymath(self, node): + import sphinx.ext.mathjax + sphinx.ext.mathjax.html_visit_displaymath(self, node) + + def depart_displaymath(self, node): + return + + # overridden + def visit_math(self, node): + import sphinx.ext.mathjax + sphinx.ext.mathjax.html_visit_math(self, node) + + # overridden + def depart_math(self, node): + return + + # overridden + def unknown_visit(self, node): + raise NotImplementedError('Unknown node: %s' % node.__class__.__name__) + + def _print_parameters(self, node): + self.body.append('' + '' + '' + '' + '' + '' + '') + + if isinstance(node.children[0], nodes.paragraph): + if self.field_context[-1] in ['Raises']: + if len(node.children) > 1: + if isinstance(node[1], nodes.bullet_list): + node[0].append(node[1].deepcopy()) + node.remove(node[1]) + + self._print_single_parameter(node[0]) + elif isinstance(node.children[0], nodes.bullet_list): + first = True + for _c in node.children[0]: + self._print_single_parameter(_c[0], first=first) + if first: + first = False + self.body.append('
') + node.clear() + + def _print_single_parameter(self, node, first=False): + _do_name = True + _name = None + _do_type = False + _types = [] + _do_desc = False + _desc = [] + for _c in node.children: + if _do_desc: + _desc.append(_c) + + elif _do_name and node.children.index(_c) == 0 \ + and isinstance(_c, (nodes.strong, nodes.literal)): + _name = _c[0].astext() + _do_name = False + + elif isinstance(_c, nodes.Text): + if _do_type is False and _c.astext() == ' (': + _do_type = True + if _c.astext() == ')': + _c.replace(')', '', 1) + _do_type = False + if _c.astext() in [' -- ', ' --'] \ + or _c.astext().find(' --') != -1: + if len(_c.astext().replace(' --', '', 1).lstrip()) > 0: + _desc.append( + nodes.Text( + _c.astext().replace(' --', '', 1).lstrip())) + _do_desc = True + if ' of ' in _c.astext() and _do_type: + _types.append(_c) + + elif isinstance(_c, (nodes.emphasis, nodes.literal, + nodes.reference)) \ + and _do_type: + if isinstance(_c, (nodes.emphasis, nodes.literal)): + _types.append(_c[0]) + else: + _types.append(_c) + + _cls = 'parameter-name' + if first: + _cls += ' first-parameter' + self.body.append( + '' + '' + '
%s
' % (_cls, _name)) + _cls = 'parameter-type' + if first: + _cls += ' first-parameter' + self.body.append('' + '' + '
' % _cls) + _of_type = False + for _ti in range(0, len(_types)): + if _types[_ti].astext() == ' of ': + self.body.append(_types[_ti].astext()) + _of_type = True + else: + if len(_types) > 1 and _ti > 0 and not _of_type: + self.body.append(', ') + if _of_type: + _of_type = False + self.body.append('') + if isinstance(_types[_ti], nodes.reference): + if isinstance(_types[_ti][0], nodes.literal): + _type = nodes.Text(_types[_ti][0].astext()) + _types[_ti].clear() + _types[_ti].append(_type) + _types[_ti].walkabout(self) + else: + self.body.append(_types[_ti].astext()) + self.body.append('') + + _cls = 'parameter-desc' + if first: + _cls += ' first-parameter' + self.body.append('
' + '' + '
' % _cls) + + for _desc_elem in _desc: + _desc_elem.walkabout(self) + self.body.append('
' + '' + '')