diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2019-05-03 23:15:35 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-03 23:15:35 +0900 |
commit | 035d5507f0cef2fd636bf90a08ff32a69f8a1cf6 (patch) | |
tree | f8bd271c9d3b80f3575feab23df16139afa0aa32 | |
parent | 10399371888c9e2a1cb0cd920ce838df2c669064 (diff) | |
parent | d960f2265d7a035459f0b0200c26d07dd999c4b5 (diff) | |
download | sphinx-git-035d5507f0cef2fd636bf90a08ff32a69f8a1cf6.tar.gz |
Merge pull request #6332 from tk0miya/rest_renderer
Add ReSTRenderer; a helper for rendering reST text
-rw-r--r-- | sphinx/util/rst.py | 35 | ||||
-rw-r--r-- | sphinx/util/template.py | 16 | ||||
-rw-r--r-- | tests/test_util_rst.py | 36 | ||||
-rw-r--r-- | tests/test_util_template.py | 37 |
4 files changed, 121 insertions, 3 deletions
diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index c3d32feb7..c897b075a 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -9,11 +9,14 @@ """ import re +from collections import defaultdict from contextlib import contextmanager +from unicodedata import east_asian_width from docutils.parsers.rst import roles from docutils.parsers.rst.languages import en as english from docutils.utils import Reporter +from jinja2 import environmentfilter from sphinx.locale import __ from sphinx.util import docutils @@ -21,13 +24,20 @@ from sphinx.util import logging if False: # For type annotation - from typing import Generator # NOQA + from typing import Callable, Dict, Generator # NOQA from docutils.statemachine import StringList # NOQA + from jinja2 import Environment # NOQA logger = logging.getLogger(__name__) docinfo_re = re.compile(':\\w+:.*?') symbols_re = re.compile(r'([!-\-/:-@\[-`{-~])') # symbols without dot(0x2e) +SECTIONING_CHARS = ['=', '-', '~'] + +# width of characters +WIDECHARS = defaultdict(lambda: "WF") # type: Dict[str, str] + # WF: Wide + Full-width +WIDECHARS["ja"] = "WFA" # In Japanese, Ambiguous characters also have double width def escape(text): @@ -37,6 +47,29 @@ def escape(text): return text +def textwidth(text, widechars='WF'): + # type: (str, str) -> int + """Get width of text.""" + def charwidth(char, widechars): + # type: (str, str) -> int + if east_asian_width(char) in widechars: + return 2 + else: + return 1 + + return sum(charwidth(c, widechars) for c in text) + + +@environmentfilter +def heading(env, text, level=1): + # type: (Environment, str, int) -> str + """Create a heading for *level*.""" + assert level <= 3 + width = textwidth(text, WIDECHARS[env.language]) # type: ignore + sectioning_char = SECTIONING_CHARS[level - 1] + return '%s\n%s' % (text, sectioning_char * width) + + @contextmanager def default_role(docname, name): # type: (str, str) -> Generator diff --git a/sphinx/util/template.py b/sphinx/util/template.py index c33e16819..b521c5c79 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -15,7 +15,7 @@ from jinja2.sandbox import SandboxedEnvironment from sphinx import package_dir from sphinx.jinja2glue import SphinxFileSystemLoader from sphinx.locale import get_translator -from sphinx.util import texescape +from sphinx.util import rst, texescape if False: # For type annotation @@ -84,3 +84,17 @@ class LaTeXRenderer(SphinxRenderer): self.env.variable_end_string = '%>' self.env.block_start_string = '<%' self.env.block_end_string = '%>' + + +class ReSTRenderer(SphinxRenderer): + def __init__(self, template_path=None, language=None): + # type: (str, str) -> None + super().__init__(template_path) + + # add language to environment + self.env.extend(language=language) + + # use texescape as escape filter + self.env.filters['e'] = rst.escape + self.env.filters['escape'] = rst.escape + self.env.filters['heading'] = rst.heading diff --git a/tests/test_util_rst.py b/tests/test_util_rst.py index ba836ff1e..1e72eda45 100644 --- a/tests/test_util_rst.py +++ b/tests/test_util_rst.py @@ -9,8 +9,11 @@ """ from docutils.statemachine import StringList +from jinja2 import Environment -from sphinx.util.rst import append_epilog, escape, prepend_prolog +from sphinx.util.rst import ( + append_epilog, escape, heading, prepend_prolog, textwidth +) def test_escape(): @@ -83,3 +86,34 @@ def test_prepend_prolog_without_CR(app): ('<generated>', 0, ''), ('dummy.rst', 0, 'hello Sphinx world'), ('dummy.rst', 1, 'Sphinx is a document generator')] + + +def test_textwidth(): + assert textwidth('Hello') == 5 + assert textwidth('русский язык') == 12 + assert textwidth('русский язык', 'WFA') == 23 # Cyrillic are ambiguous chars + + +def test_heading(): + env = Environment() + env.extend(language=None) + + assert heading(env, 'Hello') == ('Hello\n' + '=====') + assert heading(env, 'Hello', 1) == ('Hello\n' + '=====') + assert heading(env, 'Hello', 2) == ('Hello\n' + '-----') + assert heading(env, 'Hello', 3) == ('Hello\n' + '~~~~~') + assert heading(env, 'русский язык', 1) == ( + 'русский язык\n' + '============' + ) + + # language=ja: ambiguous + env.language = 'ja' + assert heading(env, 'русский язык', 1) == ( + 'русский язык\n' + '=======================' + ) diff --git a/tests/test_util_template.py b/tests/test_util_template.py new file mode 100644 index 000000000..b25e9dc87 --- /dev/null +++ b/tests/test_util_template.py @@ -0,0 +1,37 @@ +""" + test_util_template + ~~~~~~~~~~~~~~~~~~ + + Tests sphinx.util.template functions. + + :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from sphinx.util.template import ReSTRenderer + + +def test_ReSTRenderer_escape(): + r = ReSTRenderer() + template = '{{ "*hello*" | e }}' + assert r.render_string(template, {}) == r'\*hello\*' + + +def test_ReSTRenderer_heading(): + r = ReSTRenderer() + + template = '{{ "hello" | heading }}' + assert r.render_string(template, {}) == 'hello\n=====' + + template = '{{ "hello" | heading(1) }}' + assert r.render_string(template, {}) == 'hello\n=====' + + template = '{{ "русский язык" | heading(2) }}' + assert r.render_string(template, {}) == ('русский язык\n' + '------------') + + # language: ja + r.env.language = 'ja' + template = '{{ "русский язык" | heading }}' + assert r.render_string(template, {}) == ('русский язык\n' + '=======================') |