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.
Files changed (43) hide show
  1. deepnote_python_lsp_server-0.0.0.dist-info/METADATA +263 -0
  2. deepnote_python_lsp_server-0.0.0.dist-info/RECORD +43 -0
  3. deepnote_python_lsp_server-0.0.0.dist-info/WHEEL +6 -0
  4. deepnote_python_lsp_server-0.0.0.dist-info/entry_points.txt +24 -0
  5. deepnote_python_lsp_server-0.0.0.dist-info/licenses/LICENSE +22 -0
  6. deepnote_python_lsp_server-0.0.0.dist-info/top_level.txt +1 -0
  7. pylsp/__init__.py +33 -0
  8. pylsp/__main__.py +132 -0
  9. pylsp/_utils.py +325 -0
  10. pylsp/config/__init__.py +2 -0
  11. pylsp/config/config.py +202 -0
  12. pylsp/config/flake8_conf.py +64 -0
  13. pylsp/config/pycodestyle_conf.py +34 -0
  14. pylsp/config/source.py +88 -0
  15. pylsp/hookspecs.py +140 -0
  16. pylsp/lsp.py +115 -0
  17. pylsp/plugins/__init__.py +2 -0
  18. pylsp/plugins/_resolvers.py +132 -0
  19. pylsp/plugins/_rope_task_handle.py +106 -0
  20. pylsp/plugins/autopep8_format.py +95 -0
  21. pylsp/plugins/definition.py +84 -0
  22. pylsp/plugins/flake8_lint.py +243 -0
  23. pylsp/plugins/folding.py +210 -0
  24. pylsp/plugins/highlight.py +36 -0
  25. pylsp/plugins/hover.py +57 -0
  26. pylsp/plugins/jedi_completion.py +318 -0
  27. pylsp/plugins/jedi_rename.py +56 -0
  28. pylsp/plugins/mccabe_lint.py +56 -0
  29. pylsp/plugins/preload_imports.py +38 -0
  30. pylsp/plugins/pycodestyle_lint.py +113 -0
  31. pylsp/plugins/pydocstyle_lint.py +127 -0
  32. pylsp/plugins/pyflakes_lint.py +97 -0
  33. pylsp/plugins/pylint_lint.py +367 -0
  34. pylsp/plugins/references.py +33 -0
  35. pylsp/plugins/rope_autoimport.py +407 -0
  36. pylsp/plugins/rope_completion.py +161 -0
  37. pylsp/plugins/signature.py +81 -0
  38. pylsp/plugins/symbols.py +214 -0
  39. pylsp/plugins/yapf_format.py +199 -0
  40. pylsp/python_lsp.py +921 -0
  41. pylsp/text_edit.py +97 -0
  42. pylsp/uris.py +139 -0
  43. pylsp/workspace.py +730 -0
@@ -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