mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #6332 from tk0miya/rest_renderer
Add ReSTRenderer; a helper for rendering reST text
This commit is contained in:
commit
035d5507f0
@ -9,11 +9,14 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from unicodedata import east_asian_width
|
||||||
|
|
||||||
from docutils.parsers.rst import roles
|
from docutils.parsers.rst import roles
|
||||||
from docutils.parsers.rst.languages import en as english
|
from docutils.parsers.rst.languages import en as english
|
||||||
from docutils.utils import Reporter
|
from docutils.utils import Reporter
|
||||||
|
from jinja2 import environmentfilter
|
||||||
|
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.util import docutils
|
from sphinx.util import docutils
|
||||||
@ -21,13 +24,20 @@ from sphinx.util import logging
|
|||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Generator # NOQA
|
from typing import Callable, Dict, Generator # NOQA
|
||||||
from docutils.statemachine import StringList # NOQA
|
from docutils.statemachine import StringList # NOQA
|
||||||
|
from jinja2 import Environment # NOQA
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
docinfo_re = re.compile(':\\w+:.*?')
|
docinfo_re = re.compile(':\\w+:.*?')
|
||||||
symbols_re = re.compile(r'([!-\-/:-@\[-`{-~])') # symbols without dot(0x2e)
|
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):
|
def escape(text):
|
||||||
@ -37,6 +47,29 @@ def escape(text):
|
|||||||
return 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
|
@contextmanager
|
||||||
def default_role(docname, name):
|
def default_role(docname, name):
|
||||||
# type: (str, str) -> Generator
|
# type: (str, str) -> Generator
|
||||||
|
@ -15,7 +15,7 @@ from jinja2.sandbox import SandboxedEnvironment
|
|||||||
from sphinx import package_dir
|
from sphinx import package_dir
|
||||||
from sphinx.jinja2glue import SphinxFileSystemLoader
|
from sphinx.jinja2glue import SphinxFileSystemLoader
|
||||||
from sphinx.locale import get_translator
|
from sphinx.locale import get_translator
|
||||||
from sphinx.util import texescape
|
from sphinx.util import rst, texescape
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -84,3 +84,17 @@ class LaTeXRenderer(SphinxRenderer):
|
|||||||
self.env.variable_end_string = '%>'
|
self.env.variable_end_string = '%>'
|
||||||
self.env.block_start_string = '<%'
|
self.env.block_start_string = '<%'
|
||||||
self.env.block_end_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
|
||||||
|
@ -9,8 +9,11 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from docutils.statemachine import StringList
|
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():
|
def test_escape():
|
||||||
@ -83,3 +86,34 @@ def test_prepend_prolog_without_CR(app):
|
|||||||
('<generated>', 0, ''),
|
('<generated>', 0, ''),
|
||||||
('dummy.rst', 0, 'hello Sphinx world'),
|
('dummy.rst', 0, 'hello Sphinx world'),
|
||||||
('dummy.rst', 1, 'Sphinx is a document generator')]
|
('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'
|
||||||
|
'======================='
|
||||||
|
)
|
||||||
|
37
tests/test_util_template.py
Normal file
37
tests/test_util_template.py
Normal file
@ -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'
|
||||||
|
'=======================')
|
Loading…
Reference in New Issue
Block a user