-
Notifications
You must be signed in to change notification settings - Fork 225
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, | |
| ) | |
| } |

