Skip to content

Commit 095510b

Browse files
authored
Merge pull request #13 from python-lsp/improve-sphinx-rst
Improve sphinx reStructuredText parsing
2 parents a127f75 + 1771df2 commit 095510b

File tree

2 files changed

+200
-13
lines changed

2 files changed

+200
-13
lines changed

docstring_to_markdown/rst.py

+137-9
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,153 @@
66

77

88
class Directive:
9-
def __init__(self, pattern: str, replacement: str, name: Union[str, None] = None):
9+
def __init__(
10+
self, pattern: str, replacement: str,
11+
name: Union[str, None] = None,
12+
flags: int = 0
13+
):
1014
self.pattern = pattern
1115
self.replacement = replacement
1216
self.name = name
13-
17+
self.flags = flags
18+
19+
20+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-python-objects
21+
SPHINX_CROSS_REF_PYTHON = (
22+
'mod',
23+
'func',
24+
'data',
25+
'const',
26+
'class',
27+
'meth',
28+
'attr',
29+
'exc',
30+
'obj'
31+
)
32+
33+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-c-constructs
34+
SPHINX_CROSS_REF_C = (
35+
'member',
36+
'data',
37+
'func',
38+
'macro',
39+
'struct',
40+
'union',
41+
'enum',
42+
'enumerator',
43+
'type'
44+
)
45+
46+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing
47+
SPHINX_CROSS_REF_CPP = (
48+
'any',
49+
'class',
50+
'struct',
51+
'func',
52+
'member',
53+
'var',
54+
'type',
55+
'concept',
56+
'enum',
57+
'enumerator'
58+
)
59+
60+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#the-javascript-domain
61+
SPHINX_CROSS_REF_JS = (
62+
'mod',
63+
'func',
64+
'meth',
65+
'class',
66+
'data',
67+
'attr'
68+
)
69+
70+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#the-restructuredtext-domain
71+
SPHINX_CROSS_REF_RST = (
72+
'dir',
73+
'role'
74+
)
75+
76+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html
77+
SPHINX_CROSS_REF_OTHER = (
78+
'any',
79+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#cross-referencing-other-items-of-interest
80+
'envvar',
81+
'token',
82+
'keyword',
83+
'option',
84+
'term',
85+
)
86+
87+
SPHINX_PARAM = (
88+
'param',
89+
'parameter',
90+
'arg',
91+
'argument',
92+
'key',
93+
'keyword'
94+
)
1495

1596
SPHINX_RULES: List[Directive] = [
1697
Directive(
17-
pattern=r':(func|meth|class|obj|term):`\.?(?P<name>[^`]+?)`',
98+
pattern=r':c:({}):`\.?(?P<name>[^`]+?)`'.format('|'.join(SPHINX_CROSS_REF_C)),
1899
replacement=r'`\g<name>`'
19100
),
20101
Directive(
21-
pattern=r'^:param (?P<param>\S+):',
22-
replacement=r'- `\g<param>`:'
102+
pattern=r':cpp:({}):`\.?(?P<name>[^`]+?)`'.format('|'.join(SPHINX_CROSS_REF_CPP)),
103+
replacement=r'`\g<name>`'
23104
),
24105
Directive(
25-
pattern=r'^:return:',
26-
replacement=r'Returns:'
27-
)
106+
pattern=r':js:({}):`\.?(?P<name>[^`]+?)`'.format('|'.join(SPHINX_CROSS_REF_JS)),
107+
replacement=r'`\g<name>`'
108+
),
109+
Directive(
110+
pattern=r'(:py)?:({}):`\.?(?P<name>[^`]+?)`'.format('|'.join(SPHINX_CROSS_REF_PYTHON)),
111+
replacement=r'`\g<name>`'
112+
),
113+
Directive(
114+
pattern=r'(:rst)?:({}):`\.?(?P<name>[^`]+?)`'.format('|'.join(SPHINX_CROSS_REF_RST)),
115+
replacement=r'`\g<name>`'
116+
),
117+
Directive(
118+
pattern=r':({}):`\.?(?P<name>[^`]+?)`'.format('|'.join(SPHINX_CROSS_REF_OTHER)),
119+
replacement=r'`\g<name>`'
120+
),
121+
Directive(
122+
pattern=r'^\s*:({}) (?P<type>\S+) (?P<param>\S+):'.format('|'.join(SPHINX_PARAM)),
123+
replacement=r'- `\g<param>` (`\g<type>`):',
124+
flags=re.MULTILINE
125+
),
126+
Directive(
127+
pattern=r'^\s*:({}) (?P<param>\S+): (?P<desc>.*)(\n|\r\n?):type \2: (?P<type>.*)$'.format('|'.join(SPHINX_PARAM)),
128+
replacement=r'- `\g<param>` (\g<type>): \g<desc>',
129+
flags=re.MULTILINE
130+
),
131+
Directive(
132+
pattern=r'^\s*:({}) (?P<param>\S+):'.format('|'.join(SPHINX_PARAM)),
133+
replacement=r'- `\g<param>`:',
134+
flags=re.MULTILINE
135+
),
136+
Directive(
137+
pattern=r'^\s*:type (?P<param>\S+):',
138+
replacement=r' . Type: `\g<param>`:',
139+
flags=re.MULTILINE
140+
),
141+
Directive(
142+
pattern=r'^\s*:(return|returns):',
143+
replacement=r'- returns:',
144+
flags=re.MULTILINE
145+
),
146+
Directive(
147+
pattern=r'^\s*:rtype: (?P<type>\S+)',
148+
replacement=r'- return type: `\g<type>`',
149+
flags=re.MULTILINE
150+
),
151+
Directive(
152+
pattern=r'^\s*:(raises|raise|except|exception) (?P<exception>\S+):',
153+
replacement=r'- raises `\g<exception>`:',
154+
flags=re.MULTILINE
155+
),
28156
]
29157

30158

@@ -555,7 +683,7 @@ def flush_buffer():
555683
lines = '\n'.join(lines_buffer)
556684
# rst markup handling
557685
for directive in DIRECTIVES:
558-
lines = re.sub(directive.pattern, directive.replacement, lines)
686+
lines = re.sub(directive.pattern, directive.replacement, lines, flags=directive.flags)
559687

560688
for (section, header) in RST_SECTIONS.items():
561689
lines = lines.replace(header, '\n#### ' + section + '\n')

tests/test_rst.py

+63-4
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,41 @@ def func(): pass
624624
"""
625625

626626

627+
# https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#info-field-lists
628+
SPHINX_SIGNATURE = """
629+
:param str sender: The person sending the message
630+
:param str recipient: The recipient of the message
631+
:param str message_body: The body of the message
632+
:param priority: The priority of the message, can be a number 1-5
633+
:type priority: integer or None
634+
:return: the message id
635+
:rtype: int
636+
:raises ValueError: if the message_body exceeds 160 characters
637+
"""
638+
639+
SPHINX_SIGNATURE_MARKDOWN = """\
640+
- `sender` (`str`): The person sending the message
641+
- `recipient` (`str`): The recipient of the message
642+
- `message_body` (`str`): The body of the message
643+
- `priority` (integer or None): The priority of the message, can be a number 1-5
644+
- returns: the message id
645+
- return type: `int`
646+
- raises `ValueError`: if the message_body exceeds 160 characters
647+
"""
648+
649+
SPHINX_NESTED = """\
650+
.. code-block:: python
651+
def foo():
652+
''':param str message_body: blah blah'''
653+
"""
654+
655+
SPHINX_NESTED_MARKDOWN = """\
656+
```python
657+
def foo():
658+
''':param str message_body: blah blah'''
659+
```
660+
"""
661+
627662
RST_CASES = {
628663
'handles prompt continuation and multi-line output': {
629664
'rst': CODE_MULTI_LINE_CODE_OUTPUT,
@@ -715,17 +750,33 @@ def func(): pass
715750
'rst': REFERENCES,
716751
'md': REFERENCES_MARKDOWN
717752
},
718-
'converts sphinx func, meth, and class': {
753+
'converts sphinx cross-references to func, meth, class, etc.': {
719754
'rst': ':func:`function1`, :meth:`.Script.inline`, :class:`.Environment`',
720755
'md': '`function1`, `Script.inline`, `Environment`'
721756
},
757+
'converts sphinx cross-references in Python domain': {
758+
'rst': ':py:func:`function1`, :py:meth:`.Script.inline`, :py:class:`.Environment`',
759+
'md': '`function1`, `Script.inline`, `Environment`'
760+
},
761+
'converts sphinx cross-references in C domain': {
762+
'rst': ':c:func:`function1`, :c:struct:`Data`',
763+
'md': '`function1`, `Data`'
764+
},
765+
'converts sphinx cross-references in C++ domain': {
766+
'rst': ':cpp:func:`function1`, :cpp:var:`data`',
767+
'md': '`function1`, `data`'
768+
},
769+
'converts sphinx cross-references in JS domain': {
770+
'rst': ':js:func:`function1`, :js:class:`Math`',
771+
'md': '`function1`, `Math`'
772+
},
722773
'converts sphinx params': {
723774
'rst': ':param x: test arg',
724775
'md': '- `x`: test arg'
725776
},
726-
'converts sphinx return': {
727-
'rst': ':return: return description',
728-
'md': 'Returns: return description'
777+
'converts indented sphinx params': {
778+
'rst': '\t:param x: test arg',
779+
'md': '- `x`: test arg'
729780
},
730781
'converts non-standard simple table': {
731782
'rst': SIMPLE_TABLE,
@@ -746,6 +797,14 @@ def func(): pass
746797
'converts nested parameter lists': {
747798
'rst': NESTED_PARAMETERS,
748799
'md': NESTED_PARAMETERS_MARKDOWN
800+
},
801+
'converts sphinx signatures': {
802+
'rst': SPHINX_SIGNATURE,
803+
'md': SPHINX_SIGNATURE_MARKDOWN
804+
},
805+
'keeps params intact in code blocks': {
806+
'rst': SPHINX_NESTED,
807+
'md': SPHINX_NESTED_MARKDOWN
749808
}
750809
}
751810

0 commit comments

Comments
 (0)