deepnote-python-lsp-server 1.13.1rc2__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-1.13.1rc2.dist-info/METADATA +264 -0
- deepnote_python_lsp_server-1.13.1rc2.dist-info/RECORD +46 -0
- deepnote_python_lsp_server-1.13.1rc2.dist-info/WHEEL +5 -0
- deepnote_python_lsp_server-1.13.1rc2.dist-info/entry_points.txt +25 -0
- deepnote_python_lsp_server-1.13.1rc2.dist-info/licenses/LICENSE +22 -0
- deepnote_python_lsp_server-1.13.1rc2.dist-info/top_level.txt +1 -0
- pylsp/__init__.py +33 -0
- pylsp/__main__.py +132 -0
- pylsp/_utils.py +418 -0
- pylsp/_version.py +1 -0
- pylsp/config/__init__.py +2 -0
- pylsp/config/config.py +203 -0
- pylsp/config/flake8_conf.py +64 -0
- pylsp/config/pycodestyle_conf.py +34 -0
- pylsp/config/source.py +88 -0
- pylsp/hookspecs.py +150 -0
- pylsp/lsp.py +115 -0
- pylsp/plugins/__init__.py +2 -0
- pylsp/plugins/_resolvers.py +132 -0
- pylsp/plugins/_rope_task_handle.py +107 -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 +59 -0
- pylsp/plugins/jedi_completion.py +337 -0
- pylsp/plugins/jedi_rename.py +56 -0
- pylsp/plugins/mccabe_lint.py +56 -0
- pylsp/plugins/preload_imports.py +72 -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 +408 -0
- pylsp/plugins/rope_completion.py +161 -0
- pylsp/plugins/signature.py +81 -0
- pylsp/plugins/symbols.py +219 -0
- pylsp/plugins/type_definition.py +38 -0
- pylsp/plugins/yapf_format.py +199 -0
- pylsp/py.typed +0 -0
- pylsp/python_lsp.py +945 -0
- pylsp/text_edit.py +97 -0
- pylsp/uris.py +139 -0
- pylsp/workspace.py +791 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
import pycodestyle
|
|
5
|
+
|
|
6
|
+
from pylsp._utils import find_parents
|
|
7
|
+
|
|
8
|
+
from .source import ConfigSource
|
|
9
|
+
|
|
10
|
+
CONFIG_KEY = "pycodestyle"
|
|
11
|
+
USER_CONFIGS = [pycodestyle.USER_CONFIG] if pycodestyle.USER_CONFIG else []
|
|
12
|
+
PROJECT_CONFIGS = ["pycodestyle.cfg", "setup.cfg", "tox.ini"]
|
|
13
|
+
|
|
14
|
+
OPTIONS = [
|
|
15
|
+
("exclude", "plugins.pycodestyle.exclude", list),
|
|
16
|
+
("filename", "plugins.pycodestyle.filename", list),
|
|
17
|
+
("hang-closing", "plugins.pycodestyle.hangClosing", bool),
|
|
18
|
+
("ignore", "plugins.pycodestyle.ignore", list),
|
|
19
|
+
("max-line-length", "plugins.pycodestyle.maxLineLength", int),
|
|
20
|
+
("indent-size", "plugins.pycodestyle.indentSize", int),
|
|
21
|
+
("select", "plugins.pycodestyle.select", list),
|
|
22
|
+
("aggressive", "plugins.pycodestyle.aggressive", int),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PyCodeStyleConfig(ConfigSource):
|
|
27
|
+
def user_config(self):
|
|
28
|
+
config = self.read_config_from_files(USER_CONFIGS)
|
|
29
|
+
return self.parse_config(config, CONFIG_KEY, OPTIONS)
|
|
30
|
+
|
|
31
|
+
def project_config(self, document_path):
|
|
32
|
+
files = find_parents(self.root_path, document_path, PROJECT_CONFIGS)
|
|
33
|
+
config = self.read_config_from_files(files)
|
|
34
|
+
return self.parse_config(config, CONFIG_KEY, OPTIONS)
|
pylsp/config/source.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
import configparser
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfigSource:
|
|
13
|
+
"""Base class for implementing a config source."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, root_path) -> None:
|
|
16
|
+
self.root_path = root_path
|
|
17
|
+
self.is_windows = sys.platform == "win32"
|
|
18
|
+
self.xdg_home = os.environ.get(
|
|
19
|
+
"XDG_CONFIG_HOME", os.path.expanduser("~/.config")
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def user_config(self) -> None:
|
|
23
|
+
"""Return user-level (i.e. home directory) configuration."""
|
|
24
|
+
raise NotImplementedError()
|
|
25
|
+
|
|
26
|
+
def project_config(self, document_path) -> None:
|
|
27
|
+
"""Return project-level (i.e. workspace directory) configuration."""
|
|
28
|
+
raise NotImplementedError()
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def read_config_from_files(cls, files):
|
|
32
|
+
config = configparser.RawConfigParser()
|
|
33
|
+
for filename in files:
|
|
34
|
+
if os.path.exists(filename) and not os.path.isdir(filename):
|
|
35
|
+
config.read(filename)
|
|
36
|
+
|
|
37
|
+
return config
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def parse_config(cls, config, key, options):
|
|
41
|
+
"""Parse the config with the given options."""
|
|
42
|
+
conf = {}
|
|
43
|
+
for source, destination, opt_type in options:
|
|
44
|
+
opt_value = cls._get_opt(config, key, source, opt_type)
|
|
45
|
+
if opt_value is not None:
|
|
46
|
+
cls._set_opt(conf, destination, opt_value)
|
|
47
|
+
return conf
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def _get_opt(cls, config, key, option, opt_type):
|
|
51
|
+
"""Get an option from a configparser with the given type."""
|
|
52
|
+
for opt_key in [option, option.replace("-", "_")]:
|
|
53
|
+
if not config.has_option(key, opt_key):
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
if opt_type is bool:
|
|
57
|
+
return config.getboolean(key, opt_key)
|
|
58
|
+
|
|
59
|
+
if opt_type is int:
|
|
60
|
+
return config.getint(key, opt_key)
|
|
61
|
+
|
|
62
|
+
if opt_type is str:
|
|
63
|
+
return config.get(key, opt_key)
|
|
64
|
+
|
|
65
|
+
if opt_type is list:
|
|
66
|
+
return cls._parse_list_opt(config.get(key, opt_key))
|
|
67
|
+
|
|
68
|
+
raise ValueError("Unknown option type: %s" % opt_type)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def _parse_list_opt(cls, string):
|
|
72
|
+
return [s.strip() for s in string.split(",") if s.strip()]
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def _set_opt(cls, config_dict, path, value):
|
|
76
|
+
"""Set the value in the dictionary at the given path if the value is not None."""
|
|
77
|
+
if value is None:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
if "." not in path:
|
|
81
|
+
config_dict[path] = value
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
key, rest = path.split(".", 1)
|
|
85
|
+
if key not in config_dict:
|
|
86
|
+
config_dict[key] = {}
|
|
87
|
+
|
|
88
|
+
cls._set_opt(config_dict[key], rest, value)
|
pylsp/hookspecs.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
from pylsp import hookspec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@hookspec
|
|
8
|
+
def pylsp_code_actions(config, workspace, document, range, context):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@hookspec
|
|
13
|
+
def pylsp_code_lens(config, workspace, document) -> None:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@hookspec
|
|
18
|
+
def pylsp_commands(config, workspace) -> None:
|
|
19
|
+
"""The list of command strings supported by the server.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
List[str]: The supported commands.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@hookspec
|
|
27
|
+
def pylsp_completions(config, workspace, document, position, ignored_names) -> None:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@hookspec
|
|
32
|
+
def pylsp_completion_detail(config, item) -> None:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@hookspec(firstresult=True)
|
|
37
|
+
def pylsp_completion_item_resolve(config, workspace, document, completion_item) -> None:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@hookspec
|
|
42
|
+
def pylsp_definitions(config, workspace, document, position) -> None:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@hookspec(firstresult=True)
|
|
47
|
+
def pylsp_type_definition(config, document, position):
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@hookspec
|
|
52
|
+
def pylsp_dispatchers(config, workspace) -> None:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@hookspec
|
|
57
|
+
def pylsp_document_did_open(config, workspace, document) -> None:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@hookspec
|
|
62
|
+
def pylsp_document_did_save(config, workspace, document) -> None:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@hookspec
|
|
67
|
+
def pylsp_document_highlight(config, workspace, document, position) -> None:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@hookspec
|
|
72
|
+
def pylsp_document_symbols(config, workspace, document) -> None:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@hookspec(firstresult=True)
|
|
77
|
+
def pylsp_execute_command(config, workspace, command, arguments) -> None:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@hookspec
|
|
82
|
+
def pylsp_experimental_capabilities(config, workspace) -> None:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@hookspec
|
|
87
|
+
def pylsp_folding_range(config, workspace, document) -> None:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@hookspec(firstresult=True)
|
|
92
|
+
def pylsp_format_document(config, workspace, document, options) -> None:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@hookspec(firstresult=True)
|
|
97
|
+
def pylsp_format_range(config, workspace, document, range, options) -> None:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@hookspec(firstresult=True)
|
|
102
|
+
def pylsp_hover(config, workspace, document, position) -> None:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@hookspec
|
|
107
|
+
def pylsp_initialize(config, workspace) -> None:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@hookspec
|
|
112
|
+
def pylsp_initialized() -> None:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@hookspec
|
|
117
|
+
def pylsp_lint(config, workspace, document, is_saved) -> None:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@hookspec
|
|
122
|
+
def pylsp_references(
|
|
123
|
+
config, workspace, document, position, exclude_declaration
|
|
124
|
+
) -> None:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@hookspec(firstresult=True)
|
|
129
|
+
def pylsp_rename(config, workspace, document, position, new_name) -> None:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@hookspec
|
|
134
|
+
def pylsp_settings(config) -> None:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@hookspec(firstresult=True)
|
|
139
|
+
def pylsp_signature_help(config, workspace, document, position) -> None:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@hookspec
|
|
144
|
+
def pylsp_workspace_configuration_changed(config, workspace) -> None:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@hookspec
|
|
149
|
+
def pylsp_shutdown(config, workspace) -> None:
|
|
150
|
+
pass
|
pylsp/lsp.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
"""Some Language Server Protocol constants
|
|
5
|
+
|
|
6
|
+
https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CompletionItemKind:
|
|
11
|
+
Text = 1
|
|
12
|
+
Method = 2
|
|
13
|
+
Function = 3
|
|
14
|
+
Constructor = 4
|
|
15
|
+
Field = 5
|
|
16
|
+
Variable = 6
|
|
17
|
+
Class = 7
|
|
18
|
+
Interface = 8
|
|
19
|
+
Module = 9
|
|
20
|
+
Property = 10
|
|
21
|
+
Unit = 11
|
|
22
|
+
Value = 12
|
|
23
|
+
Enum = 13
|
|
24
|
+
Keyword = 14
|
|
25
|
+
Snippet = 15
|
|
26
|
+
Color = 16
|
|
27
|
+
File = 17
|
|
28
|
+
Reference = 18
|
|
29
|
+
Folder = 19
|
|
30
|
+
EnumMember = 20
|
|
31
|
+
Constant = 21
|
|
32
|
+
Struct = 22
|
|
33
|
+
Event = 23
|
|
34
|
+
Operator = 24
|
|
35
|
+
TypeParameter = 25
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DocumentHighlightKind:
|
|
39
|
+
Text = 1
|
|
40
|
+
Read = 2
|
|
41
|
+
Write = 3
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DiagnosticSeverity:
|
|
45
|
+
Error = 1
|
|
46
|
+
Warning = 2
|
|
47
|
+
Information = 3
|
|
48
|
+
Hint = 4
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DiagnosticTag:
|
|
52
|
+
Unnecessary = 1
|
|
53
|
+
Deprecated = 2
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class InsertTextFormat:
|
|
57
|
+
PlainText = 1
|
|
58
|
+
Snippet = 2
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class MessageType:
|
|
62
|
+
Error = 1
|
|
63
|
+
Warning = 2
|
|
64
|
+
Info = 3
|
|
65
|
+
Log = 4
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class SymbolKind:
|
|
69
|
+
File = 1
|
|
70
|
+
Module = 2
|
|
71
|
+
Namespace = 3
|
|
72
|
+
Package = 4
|
|
73
|
+
Class = 5
|
|
74
|
+
Method = 6
|
|
75
|
+
Property = 7
|
|
76
|
+
Field = 8
|
|
77
|
+
Constructor = 9
|
|
78
|
+
Enum = 10
|
|
79
|
+
Interface = 11
|
|
80
|
+
Function = 12
|
|
81
|
+
Variable = 13
|
|
82
|
+
Constant = 14
|
|
83
|
+
String = 15
|
|
84
|
+
Number = 16
|
|
85
|
+
Boolean = 17
|
|
86
|
+
Array = 18
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TextDocumentSyncKind:
|
|
90
|
+
NONE = 0
|
|
91
|
+
FULL = 1
|
|
92
|
+
INCREMENTAL = 2
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class NotebookCellKind:
|
|
96
|
+
Markup = 1
|
|
97
|
+
Code = 2
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#errorCodes
|
|
101
|
+
class ErrorCodes:
|
|
102
|
+
ParseError = -32700
|
|
103
|
+
InvalidRequest = -32600
|
|
104
|
+
MethodNotFound = -32601
|
|
105
|
+
InvalidParams = -32602
|
|
106
|
+
InternalError = -32603
|
|
107
|
+
jsonrpcReservedErrorRangeStart = -32099
|
|
108
|
+
ServerNotInitialized = -32002
|
|
109
|
+
UnknownErrorCode = -32001
|
|
110
|
+
jsonrpcReservedErrorRangeEnd = -32000
|
|
111
|
+
lspReservedErrorRangeStart = -32899
|
|
112
|
+
ServerCancelled = -32802
|
|
113
|
+
ContentModified = -32801
|
|
114
|
+
RequestCancelled = -32800
|
|
115
|
+
lspReservedErrorRangeEnd = -32800
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Copyright 2017-2020 Palantir Technologies, Inc.
|
|
2
|
+
# Copyright 2021- Python Language Server Contributors.
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from time import time
|
|
7
|
+
|
|
8
|
+
from jedi.api.classes import Completion
|
|
9
|
+
|
|
10
|
+
from pylsp import lsp
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ---- Base class
|
|
16
|
+
# -----------------------------------------------------------------------------
|
|
17
|
+
class Resolver:
|
|
18
|
+
def __init__(self, callback, resolve_on_error, time_to_live=60 * 30) -> None:
|
|
19
|
+
self.callback = callback
|
|
20
|
+
self.resolve_on_error = resolve_on_error
|
|
21
|
+
self._cache = {}
|
|
22
|
+
self._time_to_live = time_to_live
|
|
23
|
+
self._cache_ttl = defaultdict(set)
|
|
24
|
+
self._clear_every = 2
|
|
25
|
+
# see https://github.com/davidhalter/jedi/blob/master/jedi/inference/helpers.py#L194-L202
|
|
26
|
+
self._cached_modules = {"pandas", "numpy", "tensorflow", "matplotlib"}
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def cached_modules(self):
|
|
30
|
+
return self._cached_modules
|
|
31
|
+
|
|
32
|
+
@cached_modules.setter
|
|
33
|
+
def cached_modules(self, new_value):
|
|
34
|
+
self._cached_modules = set(new_value)
|
|
35
|
+
|
|
36
|
+
def clear_outdated(self) -> None:
|
|
37
|
+
now = self.time_key()
|
|
38
|
+
to_clear = [timestamp for timestamp in self._cache_ttl if timestamp < now]
|
|
39
|
+
for time_key in to_clear:
|
|
40
|
+
for key in self._cache_ttl[time_key]:
|
|
41
|
+
del self._cache[key]
|
|
42
|
+
del self._cache_ttl[time_key]
|
|
43
|
+
|
|
44
|
+
def time_key(self):
|
|
45
|
+
return int(time() / self._time_to_live)
|
|
46
|
+
|
|
47
|
+
def get_or_create(self, completion: Completion):
|
|
48
|
+
if not completion.full_name:
|
|
49
|
+
use_cache = False
|
|
50
|
+
else:
|
|
51
|
+
module_parts = completion.full_name.split(".")
|
|
52
|
+
use_cache = module_parts and module_parts[0] in self._cached_modules
|
|
53
|
+
|
|
54
|
+
if use_cache:
|
|
55
|
+
key = self._create_completion_id(completion)
|
|
56
|
+
if key not in self._cache:
|
|
57
|
+
if self.time_key() % self._clear_every == 0:
|
|
58
|
+
self.clear_outdated()
|
|
59
|
+
|
|
60
|
+
self._cache[key] = self.resolve(completion)
|
|
61
|
+
self._cache_ttl[self.time_key()].add(key)
|
|
62
|
+
return self._cache[key]
|
|
63
|
+
|
|
64
|
+
return self.resolve(completion)
|
|
65
|
+
|
|
66
|
+
def _create_completion_id(self, completion: Completion):
|
|
67
|
+
return (
|
|
68
|
+
completion.full_name,
|
|
69
|
+
completion.module_path,
|
|
70
|
+
completion.line,
|
|
71
|
+
completion.column,
|
|
72
|
+
self.time_key(),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def resolve(self, completion):
|
|
76
|
+
try:
|
|
77
|
+
sig = completion.get_signatures()
|
|
78
|
+
return self.callback(completion, sig)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
log.warning(
|
|
81
|
+
f"Something went wrong when resolving label for {completion}: {e}"
|
|
82
|
+
)
|
|
83
|
+
return self.resolve_on_error
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ---- Label resolver
|
|
87
|
+
# -----------------------------------------------------------------------------
|
|
88
|
+
def format_label(completion, sig):
|
|
89
|
+
if sig and completion.type in ("function", "method"):
|
|
90
|
+
params = ", ".join(param.name for param in sig[0].params)
|
|
91
|
+
label = f"{completion.name}({params})"
|
|
92
|
+
return label
|
|
93
|
+
return completion.name
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
LABEL_RESOLVER = Resolver(callback=format_label, resolve_on_error="")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ---- Snippets resolver
|
|
100
|
+
# -----------------------------------------------------------------------------
|
|
101
|
+
def format_snippet(completion, sig):
|
|
102
|
+
if not sig:
|
|
103
|
+
return {}
|
|
104
|
+
|
|
105
|
+
snippet_completion = {}
|
|
106
|
+
|
|
107
|
+
positional_args = [
|
|
108
|
+
param
|
|
109
|
+
for param in sig[0].params
|
|
110
|
+
if "=" not in param.description and param.name not in {"/", "*"}
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
if len(positional_args) > 1:
|
|
114
|
+
# For completions with params, we can generate a snippet instead
|
|
115
|
+
snippet_completion["insertTextFormat"] = lsp.InsertTextFormat.Snippet
|
|
116
|
+
snippet = completion.name + "("
|
|
117
|
+
for i, param in enumerate(positional_args):
|
|
118
|
+
snippet += "${{{}:{}}}".format(i + 1, param.name)
|
|
119
|
+
if i < len(positional_args) - 1:
|
|
120
|
+
snippet += ", "
|
|
121
|
+
snippet += ")$0"
|
|
122
|
+
snippet_completion["insertText"] = snippet
|
|
123
|
+
elif len(positional_args) == 1:
|
|
124
|
+
snippet_completion["insertTextFormat"] = lsp.InsertTextFormat.Snippet
|
|
125
|
+
snippet_completion["insertText"] = completion.name + "($0)"
|
|
126
|
+
else:
|
|
127
|
+
snippet_completion["insertText"] = completion.name + "()"
|
|
128
|
+
|
|
129
|
+
return snippet_completion
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
SNIPPET_RESOLVER = Resolver(callback=format_snippet, resolve_on_error={})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from typing import Callable, ContextManager
|
|
6
|
+
|
|
7
|
+
from rope.base.taskhandle import BaseJobSet, BaseTaskHandle
|
|
8
|
+
|
|
9
|
+
from pylsp._utils import throttle
|
|
10
|
+
from pylsp.workspace import Workspace
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
Report = Callable[[str, int], None]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PylspJobSet(BaseJobSet):
|
|
17
|
+
count: int = 0
|
|
18
|
+
done: int = 0
|
|
19
|
+
_reporter: Report
|
|
20
|
+
_report_iter: ContextManager
|
|
21
|
+
job_name: str = ""
|
|
22
|
+
|
|
23
|
+
def __init__(self, count: int | None, report_iter: ContextManager) -> None:
|
|
24
|
+
if count is not None:
|
|
25
|
+
self.count = count
|
|
26
|
+
self._reporter = report_iter.__enter__()
|
|
27
|
+
self._report_iter = report_iter
|
|
28
|
+
|
|
29
|
+
def started_job(self, name: str | None) -> None:
|
|
30
|
+
if name:
|
|
31
|
+
self.job_name = name
|
|
32
|
+
|
|
33
|
+
def finished_job(self) -> None:
|
|
34
|
+
self.done += 1
|
|
35
|
+
if self.get_percent_done() is not None and int(self.get_percent_done()) >= 100:
|
|
36
|
+
if self._report_iter is None:
|
|
37
|
+
return
|
|
38
|
+
self._report_iter.__exit__(None, None, None)
|
|
39
|
+
self._report_iter = None
|
|
40
|
+
else:
|
|
41
|
+
self._report()
|
|
42
|
+
|
|
43
|
+
def check_status(self) -> None:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def get_percent_done(self) -> float | None:
|
|
47
|
+
if self.count == 0:
|
|
48
|
+
return 0
|
|
49
|
+
return (self.done / self.count) * 100
|
|
50
|
+
|
|
51
|
+
def increment(self) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Increment the number of tasks to complete.
|
|
54
|
+
|
|
55
|
+
This is used if the number is not known ahead of time.
|
|
56
|
+
"""
|
|
57
|
+
self.count += 1
|
|
58
|
+
self._report()
|
|
59
|
+
|
|
60
|
+
@throttle(0.5)
|
|
61
|
+
def _report(self) -> None:
|
|
62
|
+
percent = int(self.get_percent_done())
|
|
63
|
+
message = f"{self.job_name} {self.done}/{self.count}"
|
|
64
|
+
log.debug(f"Reporting {message} {percent}%")
|
|
65
|
+
self._reporter(message, percent)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PylspTaskHandle(BaseTaskHandle):
|
|
69
|
+
name: str
|
|
70
|
+
observers: list
|
|
71
|
+
job_sets: list[PylspJobSet]
|
|
72
|
+
stopped: bool
|
|
73
|
+
workspace: Workspace
|
|
74
|
+
_report: Callable[[str, str], None]
|
|
75
|
+
|
|
76
|
+
def __init__(self, workspace: Workspace) -> None:
|
|
77
|
+
self.workspace = workspace
|
|
78
|
+
self.job_sets = []
|
|
79
|
+
self.observers = []
|
|
80
|
+
|
|
81
|
+
def create_jobset(self, name="JobSet", count: int | None = None):
|
|
82
|
+
report_iter = self.workspace.report_progress(
|
|
83
|
+
name, None, None, skip_token_initialization=True
|
|
84
|
+
)
|
|
85
|
+
result = PylspJobSet(count, report_iter)
|
|
86
|
+
self.job_sets.append(result)
|
|
87
|
+
self._inform_observers()
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
def stop(self) -> None:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
def current_jobset(self) -> BaseJobSet | None:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
def add_observer(self) -> None:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
def is_stopped(self) -> bool:
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
def get_jobsets(self) -> Sequence[BaseJobSet]:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
def _inform_observers(self) -> None:
|
|
106
|
+
for observer in self.observers:
|
|
107
|
+
observer()
|