8000 Report autoimport progress (#305) · rookiecoder1st/python-lsp-server@2e98dfa · GitHub
[go: up one dir, main page]

Skip to content

Commit 2e98dfa

Browse files
authored
Report autoimport progress (python-lsp#305)
1 parent c3b362e commit 2e98dfa

File tree

2 files changed

+149
-41
lines changed

2 files changed

+149
-41
lines changed

pylsp/plugins/_rope_task_handle.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from typing import Callable, ContextManager, List, Optional, Sequence
5+
6+
from rope.base.taskhandle import BaseJobSet, BaseTaskHandle
7+
8+
from pylsp.workspace import Workspace
9+
10+
log = logging.getLogger(__name__)
11+
Report = Callable[[str, int], None]
12+
13+
14+
class PylspJobSet(BaseJobSet):
15+
count: int = 0
16+
done: int = 0
17+
_reporter: Report
18+
_report_iter: ContextManager
19+
job_name: str = ""
20+
21+
def __init__(self, count: Optional[int], report_iter: ContextManager):
22+
if count is not None:
23+
self.count = count
24+
self._reporter = report_iter.__enter__()
25+
self._report_iter = report_iter
26+
27+
def started_job(self, name: Optional[str]) -> None:
28+
if name:
29+
self.job_name = name
30+
31+
def finished_job(self) -> None:
32+
self.done += 1
33+
if self.get_percent_done() is not None and int(self.get_percent_done()) >= 100:
34+
if self._report_iter is None:
35+
return
36+
self._report_iter.__exit__(None, None, None)
37+
self._report_iter = None
38+
else:
39+
self._report()
40+
41+
def check_status(self) -> None:
42+
pass
43+
44+
def get_percent_done(self) -> Optional[float]:
45+
if self.count == 0:
46+
return 0
47+
return (self.done / self.count) * 100
48+
49+
def increment(self) -> None:
50+
"""
51+
Increment the number of tasks to complete.
52+
53+
This is used if the number is not known ahead of time.
54+
"""
55+
self.count += 1
56+
self._report()
57+
67F4
58+
def _report(self):
59+
percent = int(self.get_percent_done())
60+
message = f"{self.job_name} {self.done}/{self.count}"
61+
log.debug(f"Reporting {message} {percent}%")
62+
self._reporter(message, percent)
63+
64+
65+
class PylspTaskHandle(BaseTaskHandle):
66+
name: str
67+
observers: List
68+
job_sets: List[PylspJobSet]
69+
stopped: bool
70+
workspace: Workspace
71+
_report: Callable[[str, str], None]
72+
73+
def __init__(self, workspace: Workspace):
74+
self.workspace = workspace
75+
self.job_sets = []
76+
self.observers = []
77+
78+
def create_jobset(self, name="JobSet", count: Optional[int] = None):
79+
report_iter = self.workspace.report_progress(name, None, None)
80+
result = PylspJobSet(count, report_iter)
81+
self.job_sets.append(result)
82+
self._inform_observers()
83+
return result
84+
85+
def stop(self) -> None:
86+
pass
87+
88+
def current_jobset(self) -> Optional[BaseJobSet]:
89+
pass
90+
91+
def add_observer(self) -> None:
92+
pass
93+
94+
def is_stopped(self) -> bool:
95+
pass
96+
97+
def get_jobsets(self) -> Sequence[BaseJobSet]:
98+
pass
99+
100+
def _inform_observers(self) -> None:
101+
for observer in self.observers:
102+
observer()

pylsp/plugins/rope_autoimport.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2022- Python Language Server Contributors.
22

33
import logging
4-
from typing import Any, Dict, Generator, List, Set
4+
from typing import Any, Dict, Generator, List, Optional, Set
55

66
import parso
77
from jedi import Script
@@ -15,6 +15,8 @@
1515
from pylsp.config.config import Config
1616
from pylsp.workspace import Document, Workspace
1717

18+
from ._rope_task_handle import PylspTaskHandle
19+
1820
log = logging.getLogger(__name__)
1921

2022
_score_pow = 5
@@ -46,8 +48,10 @@ def _should_insert(expr: tree.BaseNode, word_node: tree.Leaf) -> bool: # pylint
4648
if first_child == word_node:
4749
return True # If the word is the first word then its fine
4850
if len(expr.children) > 1:
49-
if any(node.type == "operator" and "." in node.value or
50-
node.type == "trailer" for node in expr.children):
51+
if any(
52+
node.type == "operator" and "." in node.value or node.type == "trailer"
53+
for node in expr.children
54+
):
5155
return False # Check if we're on a method of a function
5256
if isinstance(first_child, (tree.PythonErrorNode, tree.PythonNode)):
5357
# The tree will often include error nodes like this to indicate errors
@@ -56,8 +60,7 @@ def _should_insert(expr: tree.BaseNode, word_node: tree.Leaf) -> bool: # pylint
5660
return _handle_first_child(first_child, expr, word_node)
5761

5862

59-
def _handle_first_child(first_child: NodeOrLeaf, expr: tree.BaseNode,
60-
word_node: tree.Leaf) -> bool:
63+
def _handle_first_child(first_child: NodeOrLeaf, expr: tree.BaseNode, word_node: tree.Leaf) -> bool:
6164
"""Check if we suggest imports given the following first child."""
6265
if isinstance(first_child, tree.Import):
6366
return False
@@ -121,22 +124,16 @@ def _process_statements(
121124
insert_line = autoimport.find_insertion_line(document.source) - 1
122125
start = {"line": insert_line, "character": 0}
123126
edit_range = {"start": start, "end": start}
124-
edit = {
125-
"range": edit_range,
126-
"newText": suggestion.import_statement + "\n"
127-
}
128-
score = _get_score(suggestion.source, suggestion.import_statement,
129-
suggestion.name, word)
127+
edit = {"range": edit_range, "newText": suggestion.import_statement + "\n"}
128+
score = _get_score(suggestion.source, suggestion.import_statement, suggestion.name, word)
130129
if score > _score_max:
131130
continue
132131
# TODO make this markdown
133132
yield {
134133
"label": suggestion.name,
135134
"kind": suggestion.itemkind,
136135
"sortText": _sort_import(score),
137-
"data": {
138-
"doc_uri": doc_uri
139-
},
136+
"data": {"doc_uri": doc_uri},
140137
"detail": _document(suggestion.import_statement),
141138
"additionalTextEdits": [edit],
142139
}
@@ -150,8 +147,7 @@ def get_names(script: Script) -> Set[str]:
150147

151148

152149
@hookimpl
153-
def pylsp_completions(config: Config, workspace: Workspace, document: Document,
154-
position):
150+
def pylsp_completions(config: Config, workspace: Workspace, document: Document, position):
155151
"""Get autoimport suggestions."""
156152
line = document.lines[position["line"]]
157153
expr = parso.parse(line)
@@ -161,17 +157,15 @@ def pylsp_completions(config: Config, workspace: Workspace, document: Document,
161157
word = word_node.value
162158
log.debug(f"autoimport: searching for word: {word}")
163159
rope_config = config.settings(document_path=document.path).get("rope", {})
164-
ignored_names: Set[str] = get_names(
165-
document.jedi_script(use_document_path=True))
160+
ignored_names: Set[str] = get_names(document.jedi_script(use_document_path=True))
166161
autoimport = workspace._rope_autoimport(rope_config)
167-
suggestions = list(
168-
autoimport.search_full(word, ignored_names=ignored_names))
162+
suggestions = list(autoimport.search_full(word, ignored_names=ignored_names))
169163
results = list(
170164
sorted(
171-
_process_statements(suggestions, document.uri, word, autoimport,
172-
document),
165+
_process_statements(suggestions, document.uri, word, autoimport, document),
173166
key=lambda statement: statement["sortText"],
174-
))
167+
)
168+
)
175169
if len(results) > MAX_RESULTS:
176170
results = results[:MAX_RESULTS]
177171
return results
@@ -181,11 +175,10 @@ def _document(import_statement: str) -> str:
181175
return """# Auto-Import\n""" + import_statement
182176

183177

184-
def _get_score(source: int, full_statement: str, suggested_name: str,
185-
desired_name) -> int:
178+
def _get_score(source: int, full_statement: str, suggested_name: str, desired_name) -> int:
186179
import_length = len("import")
187180
full_statement_score = len(full_statement) - import_length
188-
suggested_name_score = ((len(suggested_name) - len(desired_name)))**2
181+
suggested_name_score = ((len(suggested_name) - len(desired_name))) ** 2
189182
source_score = 20 * source
190183
return suggested_name_score + full_statement_score + source_score
191184

@@ -198,24 +191,37 @@ def _sort_import(score: int) -> str:
198191
return "[z" + str(score).rjust(_score_pow, "0")
199192

200193

201-
@hookimpl
202-
def pylsp_initialize(config: Config, workspace: Workspace):
203-
"""Initialize AutoImport. Generates the cache for local and global items."""
204-
memory: bool = config.plugin_settings("rope_autoimport").get(
205-
"memory", False)
194+
def _reload_cache(config: Config, workspace: Workspace, files: Optional[List[Document]] = None):
195+
memory: bool = config.plugin_settings("rope_autoimport").get("memory", False)
206196
rope_config = config.settings().get("rope", {})
207197
autoimport = workspace._rope_autoimport(rope_config, memory)
208-
autoimport.generate_modules_cache()
209-
autoimport.generate_cache()
198+
task_handle = PylspTaskHandle(workspace)
199+
resources: Optional[List[Resource]] = (
200+
None if files is None else [document._rope_resource(rope_config) for document in files]
201+
)
202+
autoimport.generate_cache(task_handle=task_handle, resources=resources)
203+
autoimport.generate_modules_cache(task_handle=task_handle)
210204

211205

212206
@hookimpl
213-
def pylsp_document_did_save(config: Config, workspace: Workspace,
214-
document: Document):
207+
def pylsp_initialize(config: Config, workspace: Workspace):
208+
"""Initialize AutoImport.
209+
210+
Generates the cache for local and global items.
211+
"""
212+
_reload_cache(config, workspace)
213+
214+
215+
@hookimpl
216+
def pylsp_document_did_open(config: Config, workspace: Workspace):
217+
"""Initialize AutoImport.
218+
219+
Generates the cache for local and global items.
220+
"""
221+
_reload_cache(config, workspace)
222+
223+
224+
@hookimpl
225+
def pylsp_document_did_save(config: Config, workspace: Workspace, document: Document):
215226
"""Update the names associated with this document."""
216-
rope_config = config.settings().get("rope", {})
217-
rope_doucment: Resource = document._rope_resource(rope_config)
218-
autoimport = workspace._rope_autoimport(rope_config)
219-
autoimport.generate_cache(resources=[rope_doucment])
220-
# Might as well using saving the document as an indicator to regenerate the module cache
221-
autoimport.generate_modules_cache()
227+
_reload_cache(config, workspace, [document])

0 commit comments

Comments
 (0)
0