deepnote-python-lsp-server 0.0.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepnote_python_lsp_server-0.0.0.dist-info/METADATA +263 -0
- deepnote_python_lsp_server-0.0.0.dist-info/RECORD +43 -0
- deepnote_python_lsp_server-0.0.0.dist-info/WHEEL +6 -0
- deepnote_python_lsp_server-0.0.0.dist-info/entry_points.txt +24 -0
- deepnote_python_lsp_server-0.0.0.dist-info/licenses/LICENSE +22 -0
- deepnote_python_lsp_server-0.0.0.dist-info/top_level.txt +1 -0
- pylsp/__init__.py +33 -0
- pylsp/__main__.py +132 -0
- pylsp/_utils.py +325 -0
- pylsp/config/__init__.py +2 -0
- pylsp/config/config.py +202 -0
- pylsp/config/flake8_conf.py +64 -0
- pylsp/config/pycodestyle_conf.py +34 -0
- pylsp/config/source.py +88 -0
- pylsp/hookspecs.py +140 -0
- pylsp/lsp.py +115 -0
- pylsp/plugins/__init__.py +2 -0
- pylsp/plugins/_resolvers.py +132 -0
- pylsp/plugins/_rope_task_handle.py +106 -0
- pylsp/plugins/autopep8_format.py +95 -0
- pylsp/plugins/definition.py +84 -0
- pylsp/plugins/flake8_lint.py +243 -0
- pylsp/plugins/folding.py +210 -0
- pylsp/plugins/highlight.py +36 -0
- pylsp/plugins/hover.py +57 -0
- pylsp/plugins/jedi_completion.py +318 -0
- pylsp/plugins/jedi_rename.py +56 -0
- pylsp/plugins/mccabe_lint.py +56 -0
- pylsp/plugins/preload_imports.py +38 -0
- pylsp/plugins/pycodestyle_lint.py +113 -0
- pylsp/plugins/pydocstyle_lint.py +127 -0
- pylsp/plugins/pyflakes_lint.py +97 -0
- pylsp/plugins/pylint_lint.py +367 -0
- pylsp/plugins/references.py +33 -0
- pylsp/plugins/rope_autoimport.py +407 -0
- pylsp/plugins/rope_completion.py +161 -0
- pylsp/plugins/signature.py +81 -0
- pylsp/plugins/symbols.py +214 -0
- pylsp/plugins/yapf_format.py +199 -0
- pylsp/python_lsp.py +921 -0
- pylsp/text_edit.py +97 -0
- pylsp/uris.py +139 -0
- pylsp/workspace.py +730 -0
pylsp/plugins/symbols.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from pylsp import hookimpl
|
|
8
|
+
from pylsp.lsp import SymbolKind
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@hookimpl
|
|
14
|
+
def pylsp_document_symbols(config, document):
|
|
15
|
+
symbols_settings = config.plugin_settings("jedi_symbols")
|
|
16
|
+
all_scopes = symbols_settings.get("all_scopes", True)
|
|
17
|
+
add_import_symbols = symbols_settings.get("include_import_symbols", True)
|
|
18
|
+
definitions = document.jedi_names(all_scopes=all_scopes)
|
|
19
|
+
symbols = []
|
|
20
|
+
exclude = set({})
|
|
21
|
+
redefinitions = {}
|
|
22
|
+
|
|
23
|
+
while definitions != []:
|
|
24
|
+
d = definitions.pop(0)
|
|
25
|
+
|
|
26
|
+
# Skip symbols imported from other modules.
|
|
27
|
+
if not add_import_symbols:
|
|
28
|
+
# Skip if there's an import in the code the symbol is defined.
|
|
29
|
+
code = d.get_line_code()
|
|
30
|
+
if " import " in code or "import " in code:
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
# Skip imported symbols comparing module names.
|
|
34
|
+
sym_full_name = d.full_name
|
|
35
|
+
if sym_full_name is not None:
|
|
36
|
+
document_dot_path = document.dot_path
|
|
37
|
+
|
|
38
|
+
# We assume a symbol is imported from another module to start
|
|
39
|
+
# with.
|
|
40
|
+
imported_symbol = True
|
|
41
|
+
|
|
42
|
+
# The last element of sym_full_name is the symbol itself, so
|
|
43
|
+
# we need to discard it to do module comparisons below.
|
|
44
|
+
if "." in sym_full_name:
|
|
45
|
+
sym_module_name = sym_full_name.rpartition(".")[0]
|
|
46
|
+
else:
|
|
47
|
+
sym_module_name = sym_full_name
|
|
48
|
+
|
|
49
|
+
# This is necessary to display symbols in init files (the checks
|
|
50
|
+
# below fail without it).
|
|
51
|
+
if document_dot_path.endswith("__init__"):
|
|
52
|
+
document_dot_path = document_dot_path.rpartition(".")[0]
|
|
53
|
+
|
|
54
|
+
# document_dot_path is the module where the symbol is imported,
|
|
55
|
+
# whereas sym_module_name is the one where it was declared.
|
|
56
|
+
if document_dot_path in sym_module_name:
|
|
57
|
+
# If document_dot_path is in sym_module_name, we can safely assume
|
|
58
|
+
# that the symbol was declared in the document.
|
|
59
|
+
imported_symbol = False
|
|
60
|
+
elif sym_module_name.split(".")[0] in document_dot_path.split("."):
|
|
61
|
+
# If the first module in sym_module_name is one of the modules in
|
|
62
|
+
# document_dot_path, we need to check if sym_module_name starts
|
|
63
|
+
# with the modules in document_dot_path.
|
|
64
|
+
document_mods = document_dot_path.split(".")
|
|
65
|
+
for i in range(1, len(document_mods) + 1):
|
|
66
|
+
submod = ".".join(document_mods[-i:])
|
|
67
|
+
if sym_module_name.startswith(submod):
|
|
68
|
+
imported_symbol = False
|
|
69
|
+
break
|
|
70
|
+
|
|
71
|
+
# When there's no __init__.py next to a file or in one of its
|
|
72
|
+
# parents, the checks above fail. However, Jedi has a nice way
|
|
73
|
+
# to tell if the symbol was declared in the same file: if
|
|
74
|
+
# sym_module_name starts by __main__.
|
|
75
|
+
if imported_symbol:
|
|
76
|
+
if not sym_module_name.startswith("__main__"):
|
|
77
|
+
continue
|
|
78
|
+
else:
|
|
79
|
+
# We need to skip symbols if their definition doesn't have `full_name` info, they
|
|
80
|
+
# are detected as a definition, but their description (e.g. `class Foo`) doesn't
|
|
81
|
+
# match the code where they're detected by Jedi. This happens for relative imports.
|
|
82
|
+
if _include_def(d):
|
|
83
|
+
if d.description not in d.get_line_code():
|
|
84
|
+
continue
|
|
85
|
+
else:
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
if _include_def(d) and Path(document.path) == Path(d.module_path):
|
|
89
|
+
tuple_range = _tuple_range(d)
|
|
90
|
+
if tuple_range in exclude:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
kind = redefinitions.get(tuple_range, None)
|
|
94
|
+
if kind is not None:
|
|
95
|
+
exclude |= {tuple_range}
|
|
96
|
+
|
|
97
|
+
if d.type == "statement":
|
|
98
|
+
if d.description.startswith("self"):
|
|
99
|
+
kind = "field"
|
|
100
|
+
|
|
101
|
+
symbol = {
|
|
102
|
+
"name": d.name,
|
|
103
|
+
"containerName": _container(d),
|
|
104
|
+
"location": {
|
|
105
|
+
"uri": document.uri,
|
|
106
|
+
"range": _range(d),
|
|
107
|
+
},
|
|
108
|
+
"kind": _kind(d) if kind is None else _SYMBOL_KIND_MAP[kind],
|
|
109
|
+
}
|
|
110
|
+
symbols.append(symbol)
|
|
111
|
+
|
|
112
|
+
if d.type == "class":
|
|
113
|
+
try:
|
|
114
|
+
defined_names = list(d.defined_names())
|
|
115
|
+
for method in defined_names:
|
|
116
|
+
if method.type == "function":
|
|
117
|
+
redefinitions[_tuple_range(method)] = "method"
|
|
118
|
+
elif method.type == "statement":
|
|
119
|
+
redefinitions[_tuple_range(method)] = "field"
|
|
120
|
+
else:
|
|
121
|
+
redefinitions[_tuple_range(method)] = method.type
|
|
122
|
+
definitions = list(defined_names) + definitions
|
|
123
|
+
except Exception:
|
|
124
|
+
pass
|
|
125
|
+
return symbols
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _include_def(definition):
|
|
129
|
+
return (
|
|
130
|
+
# Don't tend to include parameters as symbols
|
|
131
|
+
definition.type != "param"
|
|
132
|
+
and
|
|
133
|
+
# Unused vars should also be skipped
|
|
134
|
+
definition.name != "_"
|
|
135
|
+
and _kind(definition) is not None
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _container(definition):
|
|
140
|
+
try:
|
|
141
|
+
# Jedi sometimes fails here.
|
|
142
|
+
parent = definition.parent()
|
|
143
|
+
# Here we check that a grand-parent exists to avoid declaring symbols
|
|
144
|
+
# as children of the module.
|
|
145
|
+
if parent.parent():
|
|
146
|
+
return parent.name
|
|
147
|
+
except:
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _range(definition):
|
|
154
|
+
# This gets us more accurate end position
|
|
155
|
+
definition = definition._name.tree_name.get_definition()
|
|
156
|
+
(start_line, start_column) = definition.start_pos
|
|
157
|
+
(end_line, end_column) = definition.end_pos
|
|
158
|
+
return {
|
|
159
|
+
"start": {"line": start_line - 1, "character": start_column},
|
|
160
|
+
"end": {"line": end_line - 1, "character": end_column},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _tuple_range(definition):
|
|
165
|
+
definition = definition._name.tree_name.get_definition()
|
|
166
|
+
return (definition.start_pos, definition.end_pos)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
_SYMBOL_KIND_MAP = {
|
|
170
|
+
"none": SymbolKind.Variable,
|
|
171
|
+
"type": SymbolKind.Class,
|
|
172
|
+
"tuple": SymbolKind.Class,
|
|
173
|
+
"dict": SymbolKind.Class,
|
|
174
|
+
"dictionary": SymbolKind.Class,
|
|
175
|
+
"function": SymbolKind.Function,
|
|
176
|
+
"lambda": SymbolKind.Function,
|
|
177
|
+
"generator": SymbolKind.Function,
|
|
178
|
+
"class": SymbolKind.Class,
|
|
179
|
+
"instance": SymbolKind.Class,
|
|
180
|
+
"method": SymbolKind.Method,
|
|
181
|
+
"builtin": SymbolKind.Class,
|
|
182
|
+
"builtinfunction": SymbolKind.Function,
|
|
183
|
+
"module": SymbolKind.Module,
|
|
184
|
+
"file": SymbolKind.File,
|
|
185
|
+
"xrange": SymbolKind.Array,
|
|
186
|
+
"slice": SymbolKind.Class,
|
|
187
|
+
"traceback": SymbolKind.Class,
|
|
188
|
+
"frame": SymbolKind.Class,
|
|
189
|
+
"buffer": SymbolKind.Array,
|
|
190
|
+
"dictproxy": SymbolKind.Class,
|
|
191
|
+
"funcdef": SymbolKind.Function,
|
|
192
|
+
"property": SymbolKind.Property,
|
|
193
|
+
"import": SymbolKind.Module,
|
|
194
|
+
"keyword": SymbolKind.Variable,
|
|
195
|
+
"constant": SymbolKind.Constant,
|
|
196
|
+
"variable": SymbolKind.Variable,
|
|
197
|
+
"value": SymbolKind.Variable,
|
|
198
|
+
"param": SymbolKind.Variable,
|
|
199
|
+
"statement": SymbolKind.Variable,
|
|
200
|
+
"boolean": SymbolKind.Boolean,
|
|
201
|
+
"int": SymbolKind.Number,
|
|
202
|
+
"longlean": SymbolKind.Number,
|
|
203
|
+
"float": SymbolKind.Number,
|
|
204
|
+
"complex": SymbolKind.Number,
|
|
205
|
+
"string": SymbolKind.String,
|
|
206
|
+
"unicode": SymbolKind.String,
|
|
207
|
+
"list": SymbolKind.Array,
|
|
208
|
+
"field": SymbolKind.Field,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _kind(d):
|
|
213
|
+
"""Return the VSCode Symbol Type"""
|
|
214
|
+
return _SYMBOL_KIND_MAP.get(d.type)
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import whatthepatch
|
|
8
|
+
from yapf.yapflib import file_resources, style
|
|
9
|
+
from yapf.yapflib.yapf_api import FormatCode
|
|
10
|
+
|
|
11
|
+
from pylsp import hookimpl
|
|
12
|
+
from pylsp._utils import get_eol_chars
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@hookimpl
|
|
18
|
+
def pylsp_format_document(workspace, document, options):
|
|
19
|
+
log.info("Formatting document %s with yapf", document)
|
|
20
|
+
with workspace.report_progress("format: yapf"):
|
|
21
|
+
return _format(document, options=options)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@hookimpl
|
|
25
|
+
def pylsp_format_range(document, range, options):
|
|
26
|
+
log.info("Formatting document %s in range %s with yapf", document, range)
|
|
27
|
+
# First we 'round' the range up/down to full lines only
|
|
28
|
+
range["start"]["character"] = 0
|
|
29
|
+
range["end"]["line"] += 1
|
|
30
|
+
range["end"]["character"] = 0
|
|
31
|
+
|
|
32
|
+
# From Yapf docs:
|
|
33
|
+
# lines: (list of tuples of integers) A list of tuples of lines, [start, end],
|
|
34
|
+
# that we want to format. The lines are 1-based indexed. It can be used by
|
|
35
|
+
# third-party code (e.g., IDEs) when reformatting a snippet of code rather
|
|
36
|
+
# than a whole file.
|
|
37
|
+
|
|
38
|
+
# Add 1 for 1-indexing vs LSP's 0-indexing
|
|
39
|
+
lines = [(range["start"]["line"] + 1, range["end"]["line"] + 1)]
|
|
40
|
+
return _format(document, lines=lines, options=options)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_style_config(document_path, options=None):
|
|
44
|
+
# Exclude file if it follows the patterns for that
|
|
45
|
+
exclude_patterns_from_ignore_file = file_resources.GetExcludePatternsForDir(
|
|
46
|
+
os.getcwd()
|
|
47
|
+
)
|
|
48
|
+
if file_resources.IsIgnored(document_path, exclude_patterns_from_ignore_file):
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
# Get the default styles as a string
|
|
52
|
+
# for a preset configuration, i.e. "pep8"
|
|
53
|
+
style_config = file_resources.GetDefaultStyleForDir(os.path.dirname(document_path))
|
|
54
|
+
if options is None:
|
|
55
|
+
return style_config
|
|
56
|
+
|
|
57
|
+
# We have options passed from LSP format request
|
|
58
|
+
# let's pass them to the formatter.
|
|
59
|
+
# First we want to get a dictionary of the preset style
|
|
60
|
+
# to pass instead of a string so that we can modify it
|
|
61
|
+
style_config = style.CreateStyleFromConfig(style_config)
|
|
62
|
+
|
|
63
|
+
use_tabs = style_config["USE_TABS"]
|
|
64
|
+
indent_width = style_config["INDENT_WIDTH"]
|
|
65
|
+
|
|
66
|
+
if options.get("tabSize") is not None:
|
|
67
|
+
indent_width = max(int(options.get("tabSize")), 1)
|
|
68
|
+
|
|
69
|
+
if options.get("insertSpaces") is not None:
|
|
70
|
+
# TODO is it guaranteed to be a boolean, or can it be a string?
|
|
71
|
+
use_tabs = not options.get("insertSpaces")
|
|
72
|
+
|
|
73
|
+
if use_tabs:
|
|
74
|
+
# Indent width doesn't make sense when using tabs
|
|
75
|
+
# the specifications state: "Size of a tab in spaces"
|
|
76
|
+
indent_width = 1
|
|
77
|
+
|
|
78
|
+
style_config["USE_TABS"] = use_tabs
|
|
79
|
+
style_config["INDENT_WIDTH"] = indent_width
|
|
80
|
+
style_config["CONTINUATION_INDENT_WIDTH"] = indent_width
|
|
81
|
+
|
|
82
|
+
for style_option, value in options.items():
|
|
83
|
+
# Apply arbitrary options passed as formatter options
|
|
84
|
+
if style_option not in style_config:
|
|
85
|
+
# ignore if it's not a known yapf config
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
style_config[style_option] = value
|
|
89
|
+
|
|
90
|
+
return style_config
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def diff_to_text_edits(diff, eol_chars):
|
|
94
|
+
# To keep things simple our text edits will be line based.
|
|
95
|
+
# We will also return the edits uncompacted, meaning a
|
|
96
|
+
# line replacement will come in as a line remove followed
|
|
97
|
+
# by a line add instead of a line replace.
|
|
98
|
+
text_edits = []
|
|
99
|
+
# keep track of line number since additions
|
|
100
|
+
# don't include the line number it's being added
|
|
101
|
+
# to in diffs. lsp is 0-indexed so we'll start with -1
|
|
102
|
+
prev_line_no = -1
|
|
103
|
+
|
|
104
|
+
for change in diff.changes:
|
|
105
|
+
if change.old and change.new:
|
|
106
|
+
# old and new are the same line, no change
|
|
107
|
+
# diffs are 1-indexed
|
|
108
|
+
prev_line_no = change.old - 1
|
|
109
|
+
elif change.new:
|
|
110
|
+
# addition
|
|
111
|
+
text_edits.append(
|
|
112
|
+
{
|
|
113
|
+
"range": {
|
|
114
|
+
"start": {"line": prev_line_no + 1, "character": 0},
|
|
115
|
+
"end": {"line": prev_line_no + 1, "character": 0},
|
|
116
|
+
},
|
|
117
|
+
"newText": change.line + eol_chars,
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
elif change.old:
|
|
121
|
+
# remove
|
|
122
|
+
lsp_line_no = change.old - 1
|
|
123
|
+
text_edits.append(
|
|
124
|
+
{
|
|
125
|
+
"range": {
|
|
126
|
+
"start": {"line": lsp_line_no, "character": 0},
|
|
127
|
+
"end": {
|
|
128
|
+
# From LSP spec:
|
|
129
|
+
# If you want to specify a range that contains a line
|
|
130
|
+
# including the line ending character(s) then use an
|
|
131
|
+
# end position denoting the start of the next line.
|
|
132
|
+
"line": lsp_line_no + 1,
|
|
133
|
+
"character": 0,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
"newText": "",
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
prev_line_no = lsp_line_no
|
|
140
|
+
|
|
141
|
+
return text_edits
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def ensure_eof_new_line(document, eol_chars, text_edits):
|
|
145
|
+
# diffs don't include EOF newline https://github.com/google/yapf/issues/1008
|
|
146
|
+
# we'll add it ourselves if our document doesn't already have it and the diff
|
|
147
|
+
# does not change the last line.
|
|
148
|
+
if document.source.endswith(eol_chars):
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
lines = document.lines
|
|
152
|
+
last_line_number = len(lines) - 1
|
|
153
|
+
|
|
154
|
+
if text_edits and text_edits[-1]["range"]["start"]["line"] >= last_line_number:
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
text_edits.append(
|
|
158
|
+
{
|
|
159
|
+
"range": {
|
|
160
|
+
"start": {"line": last_line_number, "character": 0},
|
|
161
|
+
"end": {"line": last_line_number + 1, "character": 0},
|
|
162
|
+
},
|
|
163
|
+
"newText": lines[-1] + eol_chars,
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _format(document, lines=None, options=None):
|
|
169
|
+
source = document.source
|
|
170
|
+
# Yapf doesn't work with CRLF/CR line endings, so we replace them by '\n'
|
|
171
|
+
# and restore them below when adding new lines
|
|
172
|
+
eol_chars = get_eol_chars(source)
|
|
173
|
+
if eol_chars in ["\r", "\r\n"]:
|
|
174
|
+
source = source.replace(eol_chars, "\n")
|
|
175
|
+
else:
|
|
176
|
+
eol_chars = "\n"
|
|
177
|
+
|
|
178
|
+
style_config = get_style_config(document_path=document.path, options=options)
|
|
179
|
+
|
|
180
|
+
diff_txt, changed = FormatCode(
|
|
181
|
+
source,
|
|
182
|
+
lines=lines,
|
|
183
|
+
filename=document.filename,
|
|
184
|
+
print_diff=True,
|
|
185
|
+
style_config=style_config,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if not changed:
|
|
189
|
+
return []
|
|
190
|
+
|
|
191
|
+
patch_generator = whatthepatch.parse_patch(diff_txt)
|
|
192
|
+
diff = next(patch_generator)
|
|
193
|
+
patch_generator.close()
|
|
194
|
+
|
|
195
|
+
text_edits = diff_to_text_edits(diff=diff, eol_chars=eol_chars)
|
|
196
|
+
|
|
197
|
+
ensure_eof_new_line(document=document, eol_chars=eol_chars, text_edits=text_edits)
|
|
198
|
+
|
|
199
|
+
return text_edits
|