Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cb51b09
Changed towards jedi 0.16
Foxboron Mar 29, 2020
7ca854b
remove deprecated params call
bnavigator Apr 9, 2020
b1e08e1
remove deprecated names call
bnavigator Apr 9, 2020
9e8d7fd
convert position into jedi line, column
bnavigator Apr 15, 2020
16a526b
jedi.Script keywords source vs code
bnavigator Apr 16, 2020
551990f
don't expect isabs at first place
bnavigator Apr 16, 2020
afafa62
check expected in all refs in builtin test
bnavigator Apr 16, 2020
90e5d45
bump minimum jedi version to 0.17
bnavigator Apr 16, 2020
c38ceb3
cleanup unused imports
bnavigator Apr 16, 2020
47e25f7
skip test_references_builtin for PY2
bnavigator Apr 18, 2020
688d4ee
remove TypeError in _detail again
bnavigator Apr 29, 2020
e26bef5
cosmetics from review comments
bnavigator May 8, 2020
65322ad
remove try..catch of jedi completion.
bnavigator May 8, 2020
22211f2
minor comment change [skip ci]
bnavigator May 9, 2020
edf9a1e
Fix some style issues
ccordoba12 May 9, 2020
e174216
take project path from internal _workspace if available
bnavigator May 9, 2020
1ad6229
test for root_path attribute in jedi_script()
bnavigator May 9, 2020
56a4be5
Always use workspace.root_path for Jedi's project path
ccordoba12 May 9, 2020
7debe28
Use the workspace fixture to fix failing tests
ccordoba12 May 9, 2020
a079d7f
Fix style issue
ccordoba12 May 9, 2020
93b5bef
Merge branch 'develop' into jedi
ccordoba12 May 9, 2020
9026ea8
Make workspace a required argument for the Document class
ccordoba12 May 9, 2020
3da9869
Fix pylint complaint
ccordoba12 May 9, 2020
da659ae
Fix another pyling warning
ccordoba12 May 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions pyls/_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Copyright 2017 Palantir Technologies, Inc.
from distutils.version import LooseVersion
import functools
import inspect
import logging
Expand Down Expand Up @@ -140,18 +139,34 @@ def format_docstring(contents):
"""
contents = contents.replace('\t', u'\u00A0' * 4)
contents = contents.replace(' ', u'\u00A0' * 2)
if LooseVersion(JEDI_VERSION) < LooseVersion('0.15.0'):
contents = contents.replace('*', '\\*')
return contents


def clip_column(column, lines, line_number):
# Normalise the position as per the LSP that accepts character positions > line length
# https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#position
"""
Normalise the position as per the LSP that accepts character positions > line length

https://microsoft.github.io/language-server-protocol/specification#position
"""
max_column = len(lines[line_number].rstrip('\r\n')) if len(lines) > line_number else 0
return min(column, max_column)


def position_to_jedi_linecolumn(document, position):
"""
Convert the LSP format 'line', 'character' to Jedi's 'line', 'column'

https://microsoft.github.io/language-server-protocol/specification#position
"""
code_position = {}
if position:
code_position = {'line': position['line'] + 1,
'column': clip_column(position['character'],
document.lines,
position['line'])}
return code_position


if os.name == 'nt':
import ctypes

Expand Down
8 changes: 5 additions & 3 deletions pyls/plugins/definition.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Copyright 2017 Palantir Technologies, Inc.
import logging
from pyls import hookimpl, uris
from pyls import hookimpl, uris, _utils

log = logging.getLogger(__name__)


@hookimpl
def pyls_definitions(config, document, position):
settings = config.plugin_settings('jedi_definition')
definitions = document.jedi_script(position).goto_assignments(
code_position = _utils.position_to_jedi_linecolumn(document, position)
definitions = document.jedi_script().goto(
follow_imports=settings.get('follow_imports', True),
follow_builtin_imports=settings.get('follow_builtin_imports', True))
follow_builtin_imports=settings.get('follow_builtin_imports', True),
**code_position)

return [
{
Expand Down
5 changes: 3 additions & 2 deletions pyls/plugins/highlight.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Copyright 2017 Palantir Technologies, Inc.
import logging
from pyls import hookimpl, lsp
from pyls import hookimpl, lsp, _utils

log = logging.getLogger(__name__)


@hookimpl
def pyls_document_highlight(document, position):
usages = document.jedi_script(position).usages()
code_position = _utils.position_to_jedi_linecolumn(document, position)
usages = document.jedi_script().get_references(**code_position)

def is_valid(definition):
return definition.line is not None and definition.column is not None
Expand Down
71 changes: 34 additions & 37 deletions pyls/plugins/hover.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Copyright 2017 Palantir Technologies, Inc.
from distutils.version import LooseVersion

import logging

from pyls import hookimpl, _utils
Expand All @@ -9,43 +9,40 @@

@hookimpl
def pyls_hover(document, position):
definitions = document.jedi_script(position).goto_definitions()
code_position = _utils.position_to_jedi_linecolumn(document, position)
definitions = document.jedi_script().infer(**code_position)
word = document.word_at_position(position)

if LooseVersion(_utils.JEDI_VERSION) >= LooseVersion('0.15.0'):
# Find first exact matching definition
definition = next((x for x in definitions if x.name == word), None)

# Ensure a definition is used if only one is available
# even if the word doesn't match. An example of this case is 'np'
# where 'numpy' doesn't match with 'np'. Same for NumPy ufuncs
if len(definitions) == 1:
definition = definitions[0]

if not definition:
return {'contents': ''}

# raw docstring returns only doc, without signature
doc = _utils.format_docstring(definition.docstring(raw=True))

# Find first exact matching signature
signature = next((x.to_string() for x in definition.get_signatures() if x.name == word), '')

contents = []
if signature:
contents.append({
'language': 'python',
'value': signature,
})
if doc:
contents.append(doc)
if not contents:
return {'contents': ''}
return {'contents': contents}
else:
# Find an exact match for a completion
for d in definitions:
if d.name == word:
return {'contents': _utils.format_docstring(d.docstring()) or ''}
# Find first exact matching definition
definition = next((x for x in definitions if x.name == word), None)

# Ensure a definition is used if only one is available
# even if the word doesn't match. An example of this case is 'np'
# where 'numpy' doesn't match with 'np'. Same for NumPy ufuncs
if len(definitions) == 1:
definition = definitions[0]

if not definition:
return {'contents': ''}

# raw docstring returns only doc, without signature
doc = _utils.format_docstring(definition.docstring(raw=True))

# Find first exact matching signature
signature = next((x.to_string() for x in definition.get_signatures()
if x.name == word), '')

contents = []
if signature:
contents.append({
'language': 'python',
'value': signature,
})

if doc:
contents.append(doc)

if not contents:
return {'contents': ''}

return {'contents': contents}
27 changes: 11 additions & 16 deletions pyls/plugins/jedi_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,11 @@

@hookimpl
def pyls_completions(config, document, position):
try:
definitions = document.jedi_script(position).completions()
except AttributeError as e:
if 'CompiledObject' in str(e):
# Needed to handle missing CompiledObject attribute
# 'sub_modules_dict'
definitions = None
else:
raise e
"""Get formatted completions for current code position"""
code_position = _utils.position_to_jedi_linecolumn(document, position)
completions = document.jedi_script().complete(**code_position)

if not definitions:
if not completions:
return None

completion_capabilities = config.capabilities.get('textDocument', {}).get('completion', {})
Expand All @@ -69,7 +63,7 @@ def pyls_completions(config, document, position):
settings = config.plugin_settings('jedi_completion', document_path=document.path)
should_include_params = settings.get('include_params')
include_params = snippet_support and should_include_params and use_snippets(document, position)
return [_format_completion(d, include_params) for d in definitions] or None
return [_format_completion(c, include_params) for c in completions] or None


def is_exception_class(name):
Expand Down Expand Up @@ -138,9 +132,9 @@ def _format_completion(d, include_params=True):
path = path.replace('/', '\\/')
completion['insertText'] = path

if (include_params and hasattr(d, 'params') and d.params and
not is_exception_class(d.name)):
positional_args = [param for param in d.params
sig = d.get_signatures()
if (include_params and sig and not is_exception_class(d.name)):
positional_args = [param for param in sig[0].params
if '=' not in param.description and
param.name not in {'/', '*'}]

Expand All @@ -163,8 +157,9 @@ def _format_completion(d, include_params=True):


def _label(definition):
if definition.type in ('function', 'method') and hasattr(definition, 'params'):
params = ', '.join([param.name for param in definition.params])
sig = definition.get_signatures()
if definition.type in ('function', 'method') and sig:
params = ', '.join([param.name for param in sig[0].params])
return '{}({})'.format(definition.name, params)

return definition.name
Expand Down
6 changes: 3 additions & 3 deletions pyls/plugins/references.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Copyright 2017 Palantir Technologies, Inc.
import logging
from pyls import hookimpl, uris
from pyls import hookimpl, uris, _utils

log = logging.getLogger(__name__)


@hookimpl
def pyls_references(document, position, exclude_declaration=False):
# Note that usages is not that great in a lot of cases: https://github.com/davidhalter/jedi/issues/744
usages = document.jedi_script(position).usages()
code_position = _utils.position_to_jedi_linecolumn(document, position)
usages = document.jedi_script().get_references(**code_position)

if exclude_declaration:
# Filter out if the usage is the actual declaration of the thing
Expand Down
3 changes: 2 additions & 1 deletion pyls/plugins/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

@hookimpl
def pyls_signature_help(document, position):
signatures = document.jedi_script(position).call_signatures()
code_position = _utils.position_to_jedi_linecolumn(document, position)
signatures = document.jedi_script().get_signatures(**code_position)

if not signatures:
return {'signatures': []}
Expand Down
28 changes: 11 additions & 17 deletions pyls/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ def _create_document(self, doc_uri, source=None, version=None):

class Document(object):

def __init__(self, uri, source=None, version=None, local=True, extra_sys_path=None, rope_project_builder=None,
config=None, workspace=None):
def __init__(self, uri, workspace, source=None, version=None, local=True, extra_sys_path=None,
rope_project_builder=None, config=None):
self.uri = uri
self.version = version
self.path = uris.to_fs_path(uri)
Expand Down Expand Up @@ -213,16 +213,9 @@ def word_at_position(self, position):
return m_start[0] + m_end[-1]

def jedi_names(self, all_scopes=False, definitions=True, references=False):
environment_path = None
if self._config:
jedi_settings = self._config.plugin_settings('jedi', document_path=self.path)
environment_path = jedi_settings.get('environment')
environment = self.get_enviroment(environment_path) if environment_path else None

return jedi.api.names(
source=self.source, path=self.path, all_scopes=all_scopes,
definitions=definitions, references=references, environment=environment,
)
script = self.jedi_script()
return script.get_names(all_scopes=all_scopes, definitions=definitions,
references=references)

def jedi_script(self, position=None):
extra_paths = []
Expand All @@ -233,19 +226,20 @@ def jedi_script(self, position=None):
environment_path = jedi_settings.get('environment')
extra_paths = jedi_settings.get('extra_paths') or []

sys_path = self.sys_path(environment_path) + extra_paths
environment = self.get_enviroment(environment_path) if environment_path else None
sys_path = self.sys_path(environment_path) + extra_paths
project_path = self._workspace.root_path

kwargs = {
'source': self.source,
'code': self.source,
'path': self.path,
'sys_path': sys_path,
'environment': environment,
'project': jedi.Project(path=project_path, sys_path=sys_path),
}

if position:
kwargs['line'] = position['line'] + 1
kwargs['column'] = _utils.clip_column(position['character'], self.lines, position['line'])
# Deprecated by Jedi to use in Script() constructor
kwargs += _utils.position_to_jedi_linecolumn(self, position)

return jedi.Script(**kwargs)

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
'configparser; python_version<"3.0"',
'future>=0.14.0; python_version<"3"',
'backports.functools_lru_cache; python_version<"3.2"',
'jedi>=0.14.1,<0.16',
'jedi>=0.17.0,<0.18.0',
'python-jsonrpc-server>=0.3.2',
'pluggy',
'ujson<=1.35; platform_system!="Windows"'
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ def config(workspace): # pylint: disable=redefined-outer-name


@pytest.fixture
def doc():
return Document(DOC_URI, DOC)
def doc(workspace): # pylint: disable=redefined-outer-name
return Document(DOC_URI, workspace, DOC)
12 changes: 6 additions & 6 deletions test/plugins/test_autopep8_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ def func():
GOOD_DOC = """A = ['hello', 'world']\n"""


def test_format(config):
doc = Document(DOC_URI, DOC)
def test_format(config, workspace):
doc = Document(DOC_URI, workspace, DOC)
res = pyls_format_document(config, doc)

assert len(res) == 1
assert res[0]['newText'] == "a = 123\n\n\ndef func():\n pass\n"


def test_range_format(config):
doc = Document(DOC_URI, DOC)
def test_range_format(config, workspace):
doc = Document(DOC_URI, workspace, DOC)

def_range = {
'start': {'line': 0, 'character': 0},
Expand All @@ -39,6 +39,6 @@ def test_range_format(config):
assert res[0]['newText'] == "a = 123\n\n\n\n\ndef func():\n pass\n"


def test_no_change(config):
doc = Document(DOC_URI, GOOD_DOC)
def test_no_change(config, workspace):
doc = Document(DOC_URI, workspace, GOOD_DOC)
assert not pyls_format_document(config, doc)
Loading