ItalianSwirls/italianswirls/server.py

179 lines
5.6 KiB
Python
Raw Normal View History

2022-12-01 16:35:07 +01:00
import logging
2022-12-03 16:48:13 +01:00
from typing import Optional
2022-12-01 16:35:07 +01:00
from jedi import Script
from jedi.api.refactoring import RefactoringError
2024-08-21 07:57:24 +02:00
from lsprotocol.types import (
CompletionItem,
CompletionList,
CompletionOptions,
CompletionParams,
DefinitionParams,
Hover,
HoverParams,
InsertTextFormat,
Location,
ReferenceParams,
RenameParams,
TextDocumentPositionParams,
TypeDefinitionParams,
WorkspaceEdit,
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_DEFINITION,
TEXT_DOCUMENT_HOVER,
TEXT_DOCUMENT_REFERENCES,
TEXT_DOCUMENT_RENAME,
TEXT_DOCUMENT_TYPE_DEFINITION,
)
2022-12-01 16:35:07 +01:00
from pygls.server import LanguageServer
from pygls.workspace import Document
2024-08-21 07:57:24 +02:00
from italianswirls.glue import (
gen_document_edits,
get_jedi_position,
get_lsp_completion_kind,
get_lsp_locations,
)
2022-12-01 16:35:07 +01:00
2024-08-22 11:31:32 +02:00
LS = LanguageServer("italianswirls", "v1.1.0")
2022-12-01 16:35:07 +01:00
def get_jedi_script(document: Document) -> Script:
2022-12-03 18:05:36 +01:00
"""Get Jedi Script object from this document."""
2022-12-01 16:35:07 +01:00
return Script(code=document.source, path=document.path)
2022-12-03 18:05:36 +01:00
def get_jedi_script_from_params(
2024-08-21 07:57:24 +02:00
params: TextDocumentPositionParams, server: LanguageServer
2022-12-03 18:05:36 +01:00
) -> Script:
"""Get Jedi Script using text document params provided by the client."""
document_uri = params.text_document.uri
document = server.workspace.get_document(document_uri)
script = get_jedi_script(document)
return script
2024-08-21 07:53:19 +02:00
@LS.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=["."]))
2022-12-03 16:39:56 +01:00
async def do_completion(
server: LanguageServer,
params: CompletionParams,
) -> CompletionList:
2022-12-01 16:35:07 +01:00
"""Return completion items."""
2022-12-03 18:05:36 +01:00
script = get_jedi_script_from_params(params, server)
2022-12-01 16:35:07 +01:00
jedi_position = get_jedi_position(params.position)
jedi_completions = script.complete(*jedi_position)
completion_items = []
for jedi_completion in jedi_completions:
name = jedi_completion.name
item = CompletionItem(
label=name,
filter_text=name,
2022-12-03 18:05:36 +01:00
kind=get_lsp_completion_kind(jedi_completion.type),
2022-12-01 16:35:07 +01:00
sort_text=name,
insert_text_name=name,
insert_text_format=InsertTextFormat.PlainText,
)
completion_items.append(item)
return CompletionList(
is_incomplete=False,
items=completion_items,
)
2024-08-21 07:53:19 +02:00
@LS.feature(TEXT_DOCUMENT_DEFINITION)
2022-12-03 18:05:36 +01:00
async def do_definition(
server: LanguageServer,
params: DefinitionParams,
) -> Optional[list[Location]]:
"""Return the definition location(s) of the target symbol."""
script = get_jedi_script_from_params(params, server)
jedi_position = get_jedi_position(params.position)
jedi_names = script.goto(
*jedi_position,
follow_imports=True,
follow_builtin_imports=True,
)
return get_lsp_locations(jedi_names) or None
2024-08-21 07:53:19 +02:00
@LS.feature(TEXT_DOCUMENT_TYPE_DEFINITION)
2022-12-03 18:06:05 +01:00
async def do_type_definition(
server: LanguageServer,
params: TypeDefinitionParams,
) -> Optional[list[Location]]:
"""Return the type definition location(s) of the target symbol."""
script = get_jedi_script_from_params(params, server)
jedi_position = get_jedi_position(params.position)
jedi_names = script.infer(*jedi_position)
return get_lsp_locations(jedi_names) or None
2024-08-21 07:53:19 +02:00
@LS.feature(TEXT_DOCUMENT_REFERENCES)
2022-12-03 19:41:32 +01:00
async def do_references(
server: LanguageServer,
params: ReferenceParams,
) -> Optional[list[Location]]:
"""Return the type definition location(s) of the target symbol."""
script = get_jedi_script_from_params(params, server)
jedi_position = get_jedi_position(params.position)
jedi_names = script.get_references(*jedi_position)
return get_lsp_locations(jedi_names) or None
2024-08-21 07:53:19 +02:00
@LS.feature(TEXT_DOCUMENT_HOVER)
2022-12-03 16:39:56 +01:00
async def do_hover(
server: LanguageServer,
2022-12-03 18:05:36 +01:00
params: HoverParams,
2022-12-03 16:48:13 +01:00
) -> Optional[Hover]:
2022-12-03 18:05:36 +01:00
"""Provide "hover", which is the documentation of the target symbol.
2022-12-03 16:39:56 +01:00
Jedi provides a list of names with information, usually only one. We handle
them all and concatenate them, separated by a horizontal line. For
2022-12-03 18:05:36 +01:00
simplicity, the text is mostly provided untouched, including docstrings, so
if your client tries to interpret it as Markdown even though there are
rogue `**kwargs` hanging around you might have a few display issues.
2022-12-03 16:39:56 +01:00
"""
2022-12-03 18:05:36 +01:00
script = get_jedi_script_from_params(params, server)
2022-12-03 16:39:56 +01:00
jedi_position = get_jedi_position(params.position)
jedi_help_names = script.help(*jedi_position)
2022-12-03 16:48:13 +01:00
if not jedi_help_names:
return None
2022-12-03 16:39:56 +01:00
help_texts = []
for jedi_name in jedi_help_names:
2022-12-03 16:48:13 +01:00
text = ""
if full_name := jedi_name.full_name:
text += f"`{full_name}`\n"
2022-12-03 16:39:56 +01:00
if sigs := jedi_name.get_signatures():
2022-12-03 16:48:13 +01:00
text += "\n".join(f"`{sig.to_string()}`" for sig in sigs) + "\n"
2022-12-03 16:39:56 +01:00
if docstring := jedi_name.docstring(raw=True):
2022-12-03 16:48:13 +01:00
text += "\n" + docstring
if text:
help_texts.append(text)
if not help_texts:
return None
2022-12-03 16:39:56 +01:00
hover_text = "\n\n---\n\n".join(help_texts)
2022-12-03 18:05:36 +01:00
return Hover(contents=hover_text)
2022-12-03 16:39:56 +01:00
2024-08-21 07:53:19 +02:00
@LS.feature(TEXT_DOCUMENT_RENAME)
async def do_rename(
server: LanguageServer,
params: RenameParams,
) -> Optional[WorkspaceEdit]:
"""Ask Jedi to rename a symbol and return the resulting state."""
script = get_jedi_script_from_params(params, server)
jedi_position = get_jedi_position(params.position)
try:
refactoring = script.rename(*jedi_position, new_name=params.new_name)
except RefactoringError as exc:
logging.error(f"Refactoring failed: {exc}")
return None
changes = list(gen_document_edits(refactoring, server.workspace))
logging.info(f"changes: {changes}")
return WorkspaceEdit(document_changes=changes) if changes else None