Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Migrate the Outline Explorer to use LSP information #13109

Merged
merged 33 commits into from
Sep 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
274d63e
Compute diff between symbols across codeeditor changes
andfoy Jun 26, 2020
3e5b917
Merge remote-tracking branch 'upstream/4.x' into outline_explorer_lsp
andfoy Jun 26, 2020
c2b4edd
Remove f-string
andfoy Jun 26, 2020
8e214bf
Manage to display symbols in the outline explorer
andfoy Jul 2, 2020
57d8ec5
Fix tree update logic
andfoy Jul 8, 2020
9d028b0
Re-enable click and cursor following functions
andfoy Jul 8, 2020
6a17888
Remove semicolons
andfoy Jul 8, 2020
a9416e4
Fix style issues
andfoy Jul 9, 2020
3643eb4
Remove f-strings
andfoy Jul 9, 2020
a3092c3
Fix switching regression
andfoy Jul 9, 2020
cd24ec2
Update widget tests
andfoy Jul 10, 2020
ff46a9b
Add LSP + OutlineExplorer test
andfoy Jul 14, 2020
dbd6705
Remove tests
andfoy Jul 14, 2020
c18dd73
Mark test as second
andfoy Jul 14, 2020
c70aff6
Remove extra text that cannot be retrieved without LSP
andfoy Jul 14, 2020
8e6950a
Merge remote-tracking branch 'upstream/4.x' into outline_explorer_lsp
andfoy Jul 14, 2020
f26e684
Remove more unused strings
andfoy Jul 14, 2020
b109e06
Merge remote-tracking branch 'upstream/4.x' into outline_explorer_lsp
andfoy Aug 24, 2020
8864403
Skip module definitions on Python
andfoy Aug 25, 2020
83b6c5b
git subrepo clone --branch=fix_method_attribute_symbols --force https…
andfoy Aug 26, 2020
59d0d31
Remove os and sys
andfoy Sep 2, 2020
bbfedb8
Merge remote-tracking branch 'upstream/4.x' into outline_explorer_lsp
andfoy Sep 2, 2020
b87ebf3
Add loading spinner and fix issues with multi-line class imports
andfoy Sep 4, 2020
139a401
git subrepo clone --branch=fix_method_attribute_symbols --force https…
andfoy Sep 4, 2020
0a8fb3e
git subrepo clone --branch=fix_method_attribute_symbols --force https…
andfoy Sep 4, 2020
f064378
Remove commented sections
andfoy Sep 4, 2020
141473f
Merge remote-tracking branch 'upstream/4.x' into outline_explorer_lsp
andfoy Sep 4, 2020
08ef147
Do not include imports as part of tests
andfoy Sep 4, 2020
0faf0a6
Set include import symbols option to Fals
andfoy Sep 12, 2020
da7ab18
git subrepo clone --branch=fix_method_attribute_symbols --force https…
andfoy Sep 12, 2020
772425a
git subrepo clone --branch=fix_method_attribute_symbols --force https…
andfoy Sep 12, 2020
b1ed973
git subrepo clone --branch=develop --force https://github.com/palanti…
ccordoba12 Sep 16, 2020
a3258a3
Merge branch '4.x' into outline_explorer_lsp
ccordoba12 Sep 16, 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
4 changes: 2 additions & 2 deletions external-deps/python-language-server/.gitrepo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/palantir/python-language-server.git
branch = develop
commit = eb479ff2b214a13fd1c2fb1dcf2993a45c3bb830
parent = 1aeb5896872898f765a124a69fe6bcfa96a266e3
commit = d81c7ba14d54b8e52192b0e00cbb4dacbb6f414d
parent = 772425a82808da813eca028f9fbb02a3d239e933
method = merge
cmdver = 0.4.1
12 changes: 12 additions & 0 deletions external-deps/python-language-server/pyls/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ def find_parents(root, path, names):
return []


def path_to_dot_name(path):
"""Given a path to a module, derive its dot-separated full name."""
directory = os.path.dirname(path)
module_name, _ = os.path.splitext(os.path.basename(path))
full_name = [module_name]
while os.path.exists(os.path.join(directory, '__init__.py')):
this_directory = os.path.basename(directory)
directory = os.path.dirname(directory)
full_name = [this_directory] + full_name
return '.'.join(full_name)


def match_uri_to_workspace(uri, workspaces):
if uri is None:
return None
Expand Down
74 changes: 64 additions & 10 deletions external-deps/python-language-server/pyls/plugins/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,65 @@

@hookimpl
def pyls_document_symbols(config, document):
all_scopes = config.plugin_settings('jedi_symbols').get('all_scopes', True)
# pylint: disable=broad-except
# pylint: disable=too-many-nested-blocks
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
symbols_settings = config.plugin_settings('jedi_symbols')
all_scopes = symbols_settings.get('all_scopes', True)
add_import_symbols = symbols_settings.get('include_import_symbols', True)
definitions = document.jedi_names(all_scopes=all_scopes)
return [{
'name': d.name,
'containerName': _container(d),
'location': {
'uri': document.uri,
'range': _range(d),
},
'kind': _kind(d),
} for d in definitions if _include_def(d)]
module_name = document.dot_path
symbols = []
exclude = set({})
redefinitions = {}
while definitions != []:
d = definitions.pop(0)
if not add_import_symbols:
sym_full_name = d.full_name
if sym_full_name is not None:
if (not sym_full_name.startswith(module_name) and
not sym_full_name.startswith('__main__')):
continue

if _include_def(d) and document.path == d.module_path:
tuple_range = _tuple_range(d)
if tuple_range in exclude:
continue

kind = redefinitions.get(tuple_range, None)
if kind is not None:
exclude |= {tuple_range}

if d.type == 'statement':
if d.description.startswith('self'):
kind = 'field'

symbol = {
'name': d.name,
'containerName': _container(d),
'location': {
'uri': document.uri,
'range': _range(d),
},
'kind': _kind(d) if kind is None else _SYMBOL_KIND_MAP[kind],
}
symbols.append(symbol)

if d.type == 'class':
try:
defined_names = list(d.defined_names())
for method in defined_names:
if method.type == 'function':
redefinitions[_tuple_range(method)] = 'method'
elif method.type == 'statement':
redefinitions[_tuple_range(method)] = 'field'
else:
redefinitions[_tuple_range(method)] = method.type
definitions = list(defined_names) + definitions
except Exception:
pass
return symbols


def _include_def(definition):
Expand Down Expand Up @@ -56,6 +104,11 @@ def _range(definition):
}


def _tuple_range(definition):
definition = definition._name.tree_name.get_definition()
return (definition.start_pos, definition.end_pos)


_SYMBOL_KIND_MAP = {
'none': SymbolKind.Variable,
'type': SymbolKind.Class,
Expand Down Expand Up @@ -95,6 +148,7 @@ def _range(definition):
'string': SymbolKind.String,
'unicode': SymbolKind.String,
'list': SymbolKind.Array,
'field': SymbolKind.Field
}


Expand Down
1 change: 1 addition & 0 deletions external-deps/python-language-server/pyls/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def __init__(self, uri, workspace, source=None, version=None, local=True, extra_
self.uri = uri
self.version = version
self.path = uris.to_fs_path(uri)
self.dot_path = _utils.path_to_dot_name(self.path)
self.filename = os.path.basename(self.path)

self._config = workspace._config
Expand Down
10 changes: 3 additions & 7 deletions external-deps/python-language-server/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@
'backports.functools_lru_cache; python_version<"3.2"',
'jedi>=0.17.0,<0.18.0',
'python-jsonrpc-server>=0.4.0',
'pluggy']

if sys.version_info[0] == 2:
install_requires.append('ujson<=2.0.3; platform_system!="Windows"')
else:
install_requires.append('ujson>=3.0.0')

'pluggy',
'ujson<=2.0.3 ; platform_system!="Windows" and python_version<"3.0"',
'ujson>=3.0.0 ; python_version>"3"']

setup(
name='python-language-server',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def sym(name):
# Check we have some sane mappings to VSCode constants
assert sym('a')['kind'] == SymbolKind.Variable
assert sym('B')['kind'] == SymbolKind.Class
assert sym('__init__')['kind'] == SymbolKind.Function
assert sym('__init__')['kind'] == SymbolKind.Method
assert sym('main')['kind'] == SymbolKind.Function

# Not going to get too in-depth here else we're just testing Jedi
Expand All @@ -54,7 +54,7 @@ def test_symbols(config, workspace):

# All four symbols (import sys, a, B, main)
# y is not in the root scope, it shouldn't be returned
assert len(symbols) == 4
assert len(symbols) == 5

def sym(name):
return [s for s in symbols if s['name'] == name][0]
Expand Down
3 changes: 2 additions & 1 deletion spyder/config/lsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@
},
'jedi_symbols': {
'enabled': True,
'all_scopes': True
'all_scopes': True,
'include_import_symbols': False
},
'mccabe': {
'enabled': False,
Expand Down
33 changes: 33 additions & 0 deletions spyder/plugins/completion/languageserver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,39 @@ class InsertTextFormat:
PLAIN_TEXT = 1
SNIPPET = 2


# ----------------- DOCUMENT SYMBOL RELATED VALUES ------------------


class SymbolKind:
FILE = 1
MODULE = 2
NAMESPACE = 3
PACKAGE = 4
CLASS = 5
METHOD = 6
PROPERTY = 7
FIELD = 8
CONSTRUCTOR = 9
ENUM = 10
INTERFACE = 11
FUNCTION = 12
VARIABLE = 13
CONSTANT = 14
STRING = 15
NUMBER = 16
BOOLEAN = 17
ARRAY = 18
OBJECT = 19
KEY = 20
NULL = 21
ENUM_MEMBER = 22
STRUCT = 23
EVENT = 24
OPERATOR = 25
TYPE_PARAMETER = 26


# ----------------- SAVING REQUEST RELATED VALUES -------------------


Expand Down
4 changes: 4 additions & 0 deletions spyder/plugins/editor/widgets/codeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,8 @@ def document_did_open(self):
@request(method=LSPRequestTypes.DOCUMENT_SYMBOL)
def request_symbols(self):
"""Request document symbols."""
if self.oe_proxy is not None:
self.oe_proxy.emit_request_in_progress()
params = {'file': self.filename}
return params

Expand All @@ -1137,6 +1139,8 @@ def process_symbols(self, params):
symbols = params['params']
if symbols:
self.classfuncdropdown.update_data(symbols)
if self.oe_proxy is not None:
self.oe_proxy.update_outline_info(symbols)
except RuntimeError:
# This is triggered when a codeeditor instance was removed
# before the response can be processed.
Expand Down
55 changes: 55 additions & 0 deletions spyder/plugins/editor/widgets/tests/assets/text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""


import os
import sys


# %% functions
def d():
def inner():
return 2
return inner


# ---- func 1 and 2

def func1():
for i in range(3):
print(i)


def func2():
if True:
pass

# ---- other functions

def a():
pass

def b():
pass

def c():
pass

# %% classes
class Class1:
def __init__(self):
super(Class1, self).__init__()
self.x = 2

def method3(self):
pass

def method2(self):
pass

def method1(self):
pass
Loading