Compare commits
No commits in common. "b22c9838b9232d8ecdf0efb685b746146e3ff382" and "d3e015a51a60ec0656f4285481d02a28012b2fdb" have entirely different histories.
b22c9838b9
...
d3e015a51a
|
@ -1,15 +1,11 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from jedi import Script
|
from jedi import Script
|
||||||
from jedi.api.classes import Name
|
from pygls.lsp.methods import COMPLETION, HOVER, INITIALIZE
|
||||||
from pygls.lsp.methods import COMPLETION, DEFINITION, HOVER, TYPE_DEFINITION
|
|
||||||
from pygls.lsp.types import (CompletionItem, CompletionItemKind,
|
from pygls.lsp.types import (CompletionItem, CompletionItemKind,
|
||||||
CompletionList, CompletionOptions,
|
CompletionList, CompletionOptions,
|
||||||
CompletionParams, DefinitionParams, Hover,
|
CompletionParams, Hover, InsertTextFormat,
|
||||||
HoverParams, InsertTextFormat,
|
Position, TextDocumentPositionParams)
|
||||||
Location, Position, Range,
|
|
||||||
TextDocumentPositionParams, TypeDefinitionParams)
|
|
||||||
from pygls.server import LanguageServer
|
from pygls.server import LanguageServer
|
||||||
from pygls.workspace import Document
|
from pygls.workspace import Document
|
||||||
|
|
||||||
|
@ -33,76 +29,36 @@ JEDI_COMPLETION_TYPE_MAP = {
|
||||||
|
|
||||||
|
|
||||||
def get_jedi_script(document: Document) -> Script:
|
def get_jedi_script(document: Document) -> Script:
|
||||||
"""Get Jedi Script object from this document."""
|
"""Get Jedi Script object from this document and project."""
|
||||||
return Script(code=document.source, path=document.path)
|
return Script(code=document.source, path=document.path)
|
||||||
|
|
||||||
|
|
||||||
def get_jedi_script_from_params(
|
|
||||||
params: TextDocumentPositionParams,
|
|
||||||
server: LanguageServer
|
|
||||||
) -> 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
|
|
||||||
|
|
||||||
|
|
||||||
def get_jedi_position(position: Position) -> tuple[int, int]:
|
def get_jedi_position(position: Position) -> tuple[int, int]:
|
||||||
"""Translate LSP Position to Jedi position (where line is 1-based)."""
|
"""Translate pygls's Position to Jedi position (line is 1-based)."""
|
||||||
return position.line + 1, position.character
|
return position.line + 1, position.character
|
||||||
|
|
||||||
|
|
||||||
def get_lsp_position(line: int, column: int) -> Position:
|
def get_pygls_compl_kind(jedi_compl_type: str) -> CompletionItemKind:
|
||||||
"""Translate Jedi position to LSP Position (where line is 0-based)."""
|
|
||||||
return Position(line=line - 1, character=column)
|
|
||||||
|
|
||||||
|
|
||||||
def get_lsp_range(name: Name) -> Optional[Range]:
|
|
||||||
"""Get an LSP range for this name, if it has a location."""
|
|
||||||
if name.line is None or name.column is None:
|
|
||||||
return None
|
|
||||||
start_position = get_lsp_position(name.line, name.column)
|
|
||||||
end_position = get_lsp_position(name.line, name.column + len(name.name))
|
|
||||||
return Range(start=start_position, end=end_position)
|
|
||||||
|
|
||||||
|
|
||||||
def get_lsp_location(name: Name) -> Optional[Location]:
|
|
||||||
"""Return an LSP location from this Jedi Name."""
|
|
||||||
if name.module_path is None:
|
|
||||||
return None
|
|
||||||
if (lsp_range := get_lsp_range(name)) is None:
|
|
||||||
return None
|
|
||||||
return Location(uri=name.module_path.as_uri(), range=lsp_range)
|
|
||||||
|
|
||||||
|
|
||||||
def get_lsp_locations(names: list[Name]) -> list[Location]:
|
|
||||||
"""Return a list of LSP locations from this list of Jedi Names.
|
|
||||||
|
|
||||||
Names that cannot be converted to a LSP location are discarded.
|
|
||||||
"""
|
|
||||||
lsp_locations = []
|
|
||||||
for name in names:
|
|
||||||
if lsp_location := get_lsp_location(name):
|
|
||||||
lsp_locations.append(lsp_location)
|
|
||||||
return lsp_locations
|
|
||||||
|
|
||||||
|
|
||||||
def get_lsp_completion_kind(jedi_compl_type: str) -> CompletionItemKind:
|
|
||||||
"""Return an LSP completion item kind from this Jedi completion type."""
|
|
||||||
return JEDI_COMPLETION_TYPE_MAP.get(
|
return JEDI_COMPLETION_TYPE_MAP.get(
|
||||||
jedi_compl_type,
|
jedi_compl_type,
|
||||||
CompletionItemKind.Text
|
CompletionItemKind.Text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@LS.feature(INITIALIZE)
|
||||||
|
async def do_initialize(*args):
|
||||||
|
LOG.debug("do_initialize 👋")
|
||||||
|
|
||||||
|
|
||||||
@LS.feature(COMPLETION, CompletionOptions(trigger_characters=["."]))
|
@LS.feature(COMPLETION, CompletionOptions(trigger_characters=["."]))
|
||||||
async def do_completion(
|
async def do_completion(
|
||||||
server: LanguageServer,
|
server: LanguageServer,
|
||||||
params: CompletionParams,
|
params: CompletionParams,
|
||||||
) -> CompletionList:
|
) -> CompletionList:
|
||||||
"""Return completion items."""
|
"""Return completion items."""
|
||||||
script = get_jedi_script_from_params(params, server)
|
document_uri = params.text_document.uri
|
||||||
|
document = server.workspace.get_document(document_uri)
|
||||||
|
script = get_jedi_script(document)
|
||||||
jedi_position = get_jedi_position(params.position)
|
jedi_position = get_jedi_position(params.position)
|
||||||
jedi_completions = script.complete(*jedi_position)
|
jedi_completions = script.complete(*jedi_position)
|
||||||
|
|
||||||
|
@ -112,7 +68,7 @@ async def do_completion(
|
||||||
item = CompletionItem(
|
item = CompletionItem(
|
||||||
label=name,
|
label=name,
|
||||||
filter_text=name,
|
filter_text=name,
|
||||||
kind=get_lsp_completion_kind(jedi_completion.type),
|
kind=get_pygls_compl_kind(jedi_completion.type),
|
||||||
sort_text=name,
|
sort_text=name,
|
||||||
insert_text_name=name,
|
insert_text_name=name,
|
||||||
insert_text_format=InsertTextFormat.PlainText,
|
insert_text_format=InsertTextFormat.PlainText,
|
||||||
|
@ -125,69 +81,34 @@ async def do_completion(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@LS.feature(DEFINITION)
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
@LS.feature(TYPE_DEFINITION)
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
@LS.feature(HOVER)
|
@LS.feature(HOVER)
|
||||||
async def do_hover(
|
async def do_hover(
|
||||||
server: LanguageServer,
|
server: LanguageServer,
|
||||||
params: HoverParams,
|
params: TextDocumentPositionParams,
|
||||||
) -> Optional[Hover]:
|
) -> Hover:
|
||||||
"""Provide "hover", which is the documentation of the target symbol.
|
"""Provide "hover", which is documentation of a symbol.
|
||||||
|
|
||||||
Jedi provides a list of names with information, usually only one. We handle
|
Jedi provides a list of names with information, usually only one. We handle
|
||||||
them all and concatenate them, separated by a horizontal line. For
|
them all and concatenate them, separated by a horizontal line. For
|
||||||
simplicity, the text is mostly provided untouched, including docstrings, so
|
simplicity, the text is mostly provided untouched.
|
||||||
if your client tries to interpret it as Markdown even though there are
|
|
||||||
rogue `**kwargs` hanging around you might have a few display issues.
|
|
||||||
"""
|
"""
|
||||||
script = get_jedi_script_from_params(params, server)
|
document_uri = params.text_document.uri
|
||||||
|
document = server.workspace.get_document(document_uri)
|
||||||
|
script = get_jedi_script(document)
|
||||||
jedi_position = get_jedi_position(params.position)
|
jedi_position = get_jedi_position(params.position)
|
||||||
jedi_help_names = script.help(*jedi_position)
|
jedi_help_names = script.help(*jedi_position)
|
||||||
if not jedi_help_names:
|
|
||||||
return None
|
|
||||||
|
|
||||||
help_texts = []
|
help_texts = []
|
||||||
for jedi_name in jedi_help_names:
|
for jedi_name in jedi_help_names:
|
||||||
text = ""
|
text = f"`{jedi_name.full_name}`"
|
||||||
if full_name := jedi_name.full_name:
|
|
||||||
text += f"`{full_name}`\n"
|
|
||||||
if sigs := jedi_name.get_signatures():
|
if sigs := jedi_name.get_signatures():
|
||||||
text += "\n".join(f"`{sig.to_string()}`" for sig in sigs) + "\n"
|
text += "\n" + "\n".join(f"`{sig.to_string()}`" for sig in sigs)
|
||||||
if docstring := jedi_name.docstring(raw=True):
|
if docstring := jedi_name.docstring(raw=True):
|
||||||
text += "\n" + docstring
|
text += "\n\n" + docstring
|
||||||
if text:
|
|
||||||
help_texts.append(text)
|
help_texts.append(text)
|
||||||
if not help_texts:
|
|
||||||
return None
|
|
||||||
|
|
||||||
hover_text = "\n\n---\n\n".join(help_texts)
|
hover_text = "\n\n---\n\n".join(help_texts)
|
||||||
return Hover(contents=hover_text)
|
return Hover(contents=hover_text) # TODO range
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
pygls ~= 0.13.1
|
pygls
|
||||||
jedi ~= 0.18.2
|
jedi ~= 0.18.2
|
||||||
|
|
Loading…
Reference in a new issue