-
Notifications
You must be signed in to change notification settings - Fork 222
Description
Problem
The formatting of signatures in hover and code completion documentation is fine for signatures of small functions:
but is not necessarily readable for functions with many complex arguments:
Proposed solution
Add a hook allowing to format the signature with a formatter of choice, e.g. with black
. Users could then customize the number of characters in a lines, wrapping, and potentially custom highlighting (instead of using code fences).
Context
Currently docstrings in hover and completion documentation are composed of two parts:
- signature
- the docstring itself
The docstring gets formatted by docstring-to-markdown
which now supports entrypoints allowing to add support for custom formats used in internal codebases or to override the formatting.
python-lsp-server/pylsp/_utils.py
Lines 212 to 241 in cc6d398
def format_docstring( | |
contents: str, markup_kind: str, signatures: Optional[List[str]] = None | |
): | |
"""Transform the provided docstring into a MarkupContent object. | |
If `markup_kind` is 'markdown' the docstring will get converted to | |
markdown representation using `docstring-to-markdown`; if it is | |
`plaintext`, it will be returned as plain text. | |
Call signatures of functions (or equivalent code summaries) | |
provided in optional `signatures` argument will be prepended | |
to the provided contents of the docstring if given. | |
""" | |
if not isinstance(contents, str): | |
contents = "" | |
if markup_kind == "markdown": | |
try: | |
value = docstring_to_markdown.convert(contents) | |
except docstring_to_markdown.UnknownFormatError: | |
# try to escape the Markdown syntax instead: | |
value = escape_markdown(contents) | |
if signatures: | |
value = wrap_signature("\n".join(signatures)) + "\n\n" + value | |
return {"kind": "markdown", "value": value} | |
value = contents | |
if signatures: | |
value = "\n".join(signatures) + "\n\n" + value | |
return {"kind": "plaintext", "value": escape_plain_text(value)} |
The signature part formatting is hard-coded to use wrap_signature
:
python-lsp-server/pylsp/_utils.py
Lines 194 to 195 in cc6d398
def wrap_signature(signature): | |
return "```python\n" + signature + "\n```\n" |
The signature strings come from jedi's BaseSignature.to_string()
method:
python-lsp-server/pylsp/plugins/jedi_completion.py
Lines 213 to 216 in cc6d398
docs = _utils.format_docstring( | |
d.docstring(raw=True), | |
signatures=[signature.to_string() for signature in d.get_signatures()], | |
markup_kind=markup_kind, |
python-lsp-server/pylsp/plugins/hover.py
Lines 33 to 50 in cc6d398
# Find first exact matching signature | |
signature = next( | |
( | |
x.to_string() | |
for x in definition.get_signatures() | |
if (x.name == word and x.type not in ["module"]) | |
), | |
"", | |
) | |
return { | |
"contents": _utils.format_docstring( | |
# raw docstring returns only doc, without signature | |
definition.docstring(raw=True), | |
preferred_markup_kind, | |
signatures=[signature] if signature else None, | |
) | |
} |