8000 Extract parameter docstrings (#197) · mirror-dump/python-lsp-server@bae67dc · GitHub
[go: up one dir, main page]

Skip to content

Commit bae67dc

Browse files
authored
Extract parameter docstrings (python-lsp#197)
* Format all docstrings * Parameter docstrings
1 parent db176ca commit bae67dc

File tree

5 files changed

+59
-26
lines changed

5 files changed

+59
-26
lines changed

pyls/_utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,15 @@ def _apply(impl):
114114
if result is not None:
115115
log.debug("Hook from plugin %s returned: %s", impl.plugin_name, result)
116116
return result
117+
118+
119+
def format_docstring(contents):
120+
"""Python doc strings come in a number of formats, but LSP wants markdown.
121+
122+
Until we can find a fast enough way of discovering and parsing each format,
123+
we can do a little better by at least preserving indentation.
124+
"""
125+
contents = contents.replace('\t', '\u00A0' * 4)
126+
contents = contents.replace(' ', '\u00A0' * 2)
127+
contents = contents.replace('*', '\\*')
128+
return contents

pyls/plugins/hover.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import logging
3-
from pyls import hookimpl
3+
from pyls import hookimpl, _utils
44

55
log = logging.getLogger(__name__)
66

@@ -17,16 +17,4 @@ def pyls_hover(document, position):
1717
# :(
1818
return {'contents': ''}
1919

20-
# Maybe the docstring could be huuuuuuuuuuge...
21-
return {'contents': _format_docstring(definitions[0].docstring()) or ""}
22-
23-
24-
def _format_docstring(contents):
25-
"""Python doc strings come in a number of formats, but LSP wants markdown.
26-
27-
Until we can find a fast enough way of discovering and parsing each format,
28-
we can do a little better by at least preserving indentation.
29-
"""
30-
contents = contents.replace('\t', '\u00A0' * 4)
31-
contents = contents.replace(' ', '\u00A0' * 2)
32-
return contents
20+
return {'contents': _utils.format_docstring(definitions[0].docstring()) or ""}

pyls/plugins/jedi_completion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import logging
33
from pyls.lsp import CompletionItemKind
4-
from pyls import hookimpl
4+
from pyls import hookimpl, _utils
55

66
log = logging.getLogger(__name__)
77

@@ -14,7 +14,7 @@ def pyls_completions(document, position):
1414
'label': _label(d),
1515
'kind': _kind(d),
1616
'detail': _detail(d),
17-
'documentation': d.docstring(),
17+
'documentation': _utils.format_docstring(d.docstring()),
1818
'sortText': _sort_text(d),
1919
'insertText': d.name
2020
} for d in definitions]

pyls/plugins/signature.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import logging
3-
from pyls import hookimpl
3+
import re
4+
from pyls import hookimpl, _utils
45

56
log = logging.getLogger(__name__)
67

8+
SPHINX = re.compile("\s*:param\s+(?P<param>\w+):\s*(?P<doc>[^\\n]+)")
9+
EPYDOC = re.compile("\s*@param\s+(?P<param>\w+):\s*(?P<doc>[^\\n]+)")
10+
GOOGLE = re.compile("\s*(?P<param>\w+).*:\s*(?P<doc>[^\\n]+)")
11+
12+
DOC_REGEX = [SPHINX, EPYDOC, GOOGLE]
13+
714

815
@hookimpl
916
def pyls_signature_help(document, position):
@@ -15,15 +22,14 @@ def pyls_signature_help(document, position):
1522
s = signatures[0]
1623
sig = {
1724
'label': s.docstring().splitlines()[0],
18-
'documentation': s.docstring(raw=True)
25+
'documentation': _utils.format_docstring(s.docstring(raw=True))
1926
}
2027

2128
# If there are params, add those
2229
if len(s.params) > 0:
2330
sig['parameters'] = [{
2431
'label': p.name,
25-
# TODO: we could do smarter things here like parsing the function docstring
26-
'documentation': ""
32+
'documentation': _param_docs(s.docstring(), p.name)
2733
} for p in s.params]
2834

2935
# We only return a single signature because Python doesn't allow overloading
@@ -34,3 +40,14 @@ def pyls_signature_help(document, position):
3440
sig_info['activeParameter'] = s.index
3541

3642
return sig_info
43+
44+
45+
def _param_docs(docstring, param_name):
46+
for line in docstring.splitlines():
47+
for regex in DOC_REGEX:
48+
m = regex.match(line)
49+
if not m:
50+
continue
51+
if m.group('param') != param_name:
52+
continue
53+
return m.group('doc') or ""

test/plugins/test_signature.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import pytest
33
from pyls import uris
4-
from pyls.plugins.signature import pyls_signature_help
4+
from pyls.plugins import signature
55
from pyls.workspace import Document
66

77
DOC_URI = uris.from_fs_path(__file__)
88
DOC = """import sys
99
1010
def main(param1, param2):
11-
\"\"\" Main docstring \"\"\"
11+
\"\"\" Main docstring
12+
13+
Args:
14+
param1 (str): Docs for param1
15+
\"\"\"
1216
raise Exception()
1317
1418
main(
@@ -17,23 +21,35 @@ def main(param1, param2):
1721

1822
def test_no_signature():
1923
# Over blank line
20-
sig_position = {'line': 5, 'character': 0}
24+
sig_position = {'line': 9, 'character': 0}
2125
doc = Document(DOC_URI, DOC)
2226

23-
sigs = pyls_signature_help(doc, sig_position)['signatures']
27+
sigs = signature.pyls_signature_help(doc, sig_position)['signatures']
2428
assert len(sigs) == 0
2529

2630

2731
def test_signature():
2832
# Over '( ' in main(
29-
sig_position = {'line': 6, 'character': 5}
33+
sig_position = {'line': 10, 'character': 5}
3034
doc = Document(DOC_URI, DOC)
3135

32-
sig_info = pyls_signature_help(doc, sig_position)
36+
sig_info = signature.pyls_signature_help(doc, sig_position)
3337

3438
sigs = sig_info['signatures']
3539
assert len(sigs) == 1
3640
assert sigs[0]['label'] == 'main(param1, param2)'
3741
assert sigs[0]['parameters'][0]['label'] == 'param1'
42+
assert sigs[0]['parameters'][0]['documentation'] == 'Docs for param1'
3843

3944
assert sig_info['activeParameter'] == 0
45+
46+
47+
@pytest.mark.parametrize('regex,doc', [
48+
(signature.SPHINX, " :param test: parameter docstring"),
49+
(signature.EPYDOC, " @param test: parameter docstring"),
50+
(signature.GOOGLE, " test (str): parameter docstring")
51+
])
52+
def test_docstring_params(regex, doc):
53+
m = regex.match(doc)
54+
assert m.group('param') == "test"
55+
assert m.group('doc') == "parameter docstring"

0 commit comments

Comments
 (0)
0