Skip to content

Commit 32da58c

Browse files
committed
implement recognize changes in inter file dependencies
1 parent ac49bbd commit 32da58c

File tree

8 files changed

+200
-89
lines changed

8 files changed

+200
-89
lines changed

robotcode/jsonrpc2/protocol.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -592,27 +592,37 @@ async def handle_response(self, message: JsonRPCResponse) -> None:
592592
res = None
593593
if message.result is not None:
594594
res = from_dict(message.result, entry.result_type)
595-
if entry.future._loop == asyncio.get_running_loop():
595+
if entry.future.get_loop() == asyncio.get_running_loop():
596596
entry.future.set_result(res)
597597
else:
598-
if entry.future._loop.is_running():
598+
if entry.future.get_loop().is_running():
599599

600-
def s(f: asyncio.Future[Any], r: Any) -> None:
601-
if not f.done():
602-
f.set_result(r)
600+
def set_result(f: asyncio.Future[Any], r: Any, ev: threading.Event) -> None:
601+
try:
602+
if not f.done() and f.get_loop().is_running():
603+
f.set_result(r)
604+
finally:
605+
ev.set()
606+
607+
done = threading.Event()
608+
609+
entry.future.get_loop().call_soon_threadsafe(set_result, entry.future, res, done)
610+
611+
if not done.wait(120):
612+
raise TimeoutError("Can't set response result")
603613

604-
entry.future._loop.call_soon_threadsafe(s, entry.future, res)
605614
else:
606615
self._logger.warning("Response loop is not running.")
607616

608617
except (SystemExit, KeyboardInterrupt):
609618
raise
610619
except BaseException as e:
611620
if not entry.future.done():
612-
if entry.future._loop == asyncio.get_running_loop():
621+
if entry.future.get_loop() == asyncio.get_running_loop():
613622
entry.future.set_exception(e)
614623
else:
615-
entry.future._loop.call_soon_threadsafe(entry.future.set_exception, e)
624+
if entry.future.get_loop().is_running():
625+
entry.future.get_loop().call_soon_threadsafe(entry.future.set_exception, e)
616626

617627
@_logger.call
618628
async def handle_error(self, message: JsonRPCError) -> None:

robotcode/language_server/common/parts/diagnostics.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
ErrorCodes,
3232
PreviousResultId,
3333
ProgressToken,
34+
PublishDiagnosticsParams,
3435
RelatedFullDocumentDiagnosticReport,
3536
RelatedUnchangedDocumentDiagnosticReport,
3637
ServerCapabilities,
@@ -226,15 +227,16 @@ async def ensure_workspace_loaded(self) -> None:
226227
await self.on_workspace_loaded(self)
227228
await self.force_refresh_all()
228229

229-
async def force_refresh_all(self) -> None:
230+
async def force_refresh_all(self, refresh: bool = True) -> None:
230231
for doc in self.parent.documents.documents:
231232
self.get_diagnostics_data(doc).force = True
232233

233-
await self.refresh()
234+
if refresh:
235+
await self.refresh()
234236

235-
async def force_refresh_document(self, document: TextDocument) -> None:
237+
async def force_refresh_document(self, document: TextDocument, refresh: bool = True) -> None:
236238
self.get_diagnostics_data(document).force = True
237-
if document.opened_in_editor:
239+
if refresh and document.opened_in_editor:
238240
await self.refresh()
239241

240242
@_logger.call
@@ -262,8 +264,20 @@ async def _get_diagnostics_for_document(self, document: TextDocument, data: Diag
262264
if result.diagnostics is not None:
263265
collected_keys.append(result.key)
264266

265-
if document.opened_in_editor:
266-
await self.refresh()
267+
# if document.opened_in_editor:
268+
# await self.refresh()
269+
270+
if data.entries:
271+
self.parent.send_notification(
272+
"textDocument/publishDiagnostics",
273+
PublishDiagnosticsParams(
274+
uri=document.document_uri,
275+
version=document._version,
276+
diagnostics=[
277+
e for e in itertools.chain(*(i for i in data.entries.values() if i is not None))
278+
],
279+
),
280+
)
267281

268282
except asyncio.CancelledError:
269283
self._logger.debug(lambda: f"_get_diagnostics cancelled for {document}")
@@ -292,8 +306,7 @@ async def _text_document_diagnostic(
292306

293307
task = data.task
294308

295-
data = DiagnosticsData()
296-
document.set_data(self, data)
309+
data.force = False
297310

298311
if task is not None and not task.done():
299312
self._logger.debug(lambda: f"try to cancel diagnostics for {document}")
@@ -309,23 +322,22 @@ async def _text_document_diagnostic(
309322
def done(t: asyncio.Task[Any]) -> None:
310323

311324
self._logger.debug(lambda: f"diagnostics for {document} {'canceled' if t.cancelled() else 'ended'}")
312-
try:
313-
t.result()
314-
except asyncio.CancelledError:
315-
pass
316-
except (SystemExit, KeyboardInterrupt):
317-
raise
318-
except BaseException as e:
319-
self._logger.exception(e)
325+
if t.done() and not t.cancelled():
326+
ex = t.exception()
327+
328+
if ex is None or isinstance(ex, asyncio.CancelledError):
329+
return
320330

321331
data.task.add_done_callback(done)
322332

323333
if data.id == previous_result_id:
324334
return RelatedUnchangedDocumentDiagnosticReport(result_id=data.id)
325335

326-
return RelatedFullDocumentDiagnosticReport(
327-
list(itertools.chain(*(e for e in data.entries.values() if e is not None))), result_id=data.id
328-
)
336+
# return RelatedFullDocumentDiagnosticReport(
337+
# list(itertools.chain(*(e for e in data.entries.values() if e is not None))), result_id=data.id
338+
# )
339+
340+
return RelatedFullDocumentDiagnosticReport([], result_id=data.id)
329341
except asyncio.CancelledError:
330342
self._logger.debug("canceled _text_document_diagnostic")
331343
raise

robotcode/language_server/common/text_document.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import weakref
77
from typing import Any, Awaitable, Callable, Dict, List, Optional, TypeVar, Union, cast
88

9-
from ...utils.async_tools import Lock, create_sub_task
9+
from ...utils.async_tools import Lock, async_event, create_sub_task
1010
from ...utils.logging import LoggingDescriptor
1111
from ...utils.uri import Uri
1212
from .lsp_types import DocumentUri, Range
@@ -90,7 +90,7 @@ async def revert(self, version: Optional[int]) -> bool:
9090
async def apply_none_change(self) -> None:
9191
async with self._lock:
9292
self._lines = None
93-
self._invalidate_cache()
93+
await self._invalidate_cache()
9494

9595
@_logger.call
9696
async def apply_full_change(self, version: Optional[int], text: Optional[str], *, save: bool = False) -> None:
@@ -102,7 +102,7 @@ async def apply_full_change(self, version: Optional[int], text: Optional[str], *
102102
self._lines = None
103103
if save:
104104
self._orig_text = self._text
105-
self._invalidate_cache()
105+
await self._invalidate_cache()
106106

107107
@_logger.call
108108
async def apply_incremental_change(self, version: Optional[int], range: Range, text: str) -> None:
@@ -138,7 +138,7 @@ async def apply_incremental_change(self, version: Optional[int], range: Range, t
138138
self._text = new_text.getvalue()
139139
finally:
140140
self._lines = None
141-
self._invalidate_cache()
141+
await self._invalidate_cache()
142142

143143
def __get_lines(self) -> List[str]:
144144
if self._lines is None:
@@ -152,21 +152,31 @@ async def get_lines(self) -> List[str]:
152152
return self.__get_lines()
153153
return self._lines
154154

155-
def _invalidate_cache(self) -> None:
155+
@async_event
156+
async def cache_invalidate(sender) -> None: # NOSONAR
157+
...
158+
159+
@async_event
160+
async def cache_invalidated(sender) -> None: # NOSONAR
161+
...
162+
163+
async def _invalidate_cache(self) -> None:
164+
await self.cache_invalidate(self)
156165
self._cache.clear()
166+
await self.cache_invalidated(self)
157167

158168
@_logger.call
159169
async def invalidate_cache(self) -> None:
160170
async with self._lock:
161-
self._invalidate_cache()
171+
await self._invalidate_cache()
162172

163-
def _invalidate_data(self) -> None:
173+
async def _invalidate_data(self) -> None:
164174
self._data.clear()
165175

166176
@_logger.call
167177
async def invalidate_data(self) -> None:
168178
async with self._lock:
169-
self._invalidate_data()
179+
await self._invalidate_data()
170180

171181
async def __remove_cache_entry_safe(self, _ref: Any) -> None:
172182
async with self._lock:
@@ -185,6 +195,19 @@ def __get_cache_reference(self, entry: Callable[..., Any], /, *, add_remove: boo
185195

186196
return reference
187197

198+
def get_cache_value(
199+
self,
200+
entry: Union[Callable[[TextDocument], Awaitable[_T]], Callable[..., Awaitable[_T]]],
201+
) -> Optional[_T]:
202+
203+
reference = self.__get_cache_reference(entry)
204+
205+
e = self._cache.get(reference, None)
206+
if e is None:
207+
return None
208+
209+
return cast(Optional[_T], e.data)
210+
188211
async def get_cache(
189212
self,
190213
entry: Union[Callable[[TextDocument], Awaitable[_T]], Callable[..., Awaitable[_T]]],
@@ -216,12 +239,12 @@ def set_data(self, key: Any, data: Any) -> None:
216239
def get_data(self, key: Any, default: Optional[_T] = None) -> _T:
217240
return self._data.get(key, default)
218241

219-
def _clear(self) -> None:
242+
async def _clear(self) -> None:
220243
self._lines = None
221-
self._invalidate_cache()
222-
self._invalidate_data()
244+
await self._invalidate_cache()
245+
await self._invalidate_data()
223246

224247
@_logger.call
225248
async def clear(self) -> None:
226249
async with self._lock:
227-
self._clear()
250+
await self._clear()

robotcode/language_server/robotframework/diagnostics/namespace.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from typing import (
1515
Any,
1616
AsyncGenerator,
17-
Callable,
1817
Dict,
1918
Iterable,
2019
List,
@@ -28,7 +27,7 @@
2827
)
2928

3029
from ....utils.async_itertools import as_async_iterable
31-
from ....utils.async_tools import Lock
30+
from ....utils.async_tools import Lock, async_event
3231
from ....utils.logging import LoggingDescriptor
3332
from ....utils.uri import Uri
3433
from ...common.lsp_types import (
@@ -130,6 +129,11 @@ async def visit_Variable(self, node: ast.AST) -> None: # noqa: N802
130129
if name.endswith("="):
131130
name = name[:-1].rstrip()
132131

132+
has_value = bool(variable.value)
133+
value = tuple(
134+
s.replace("${CURDIR}", str(Path(self.source).parent).replace("\\", "\\\\")) for s in variable.value
135+
)
136+
133137
self._results.append(
134138
VariableDefinition(
135139
name=variable.name,
@@ -141,9 +145,9 @@ async def visit_Variable(self, node: ast.AST) -> None: # noqa: N802
141145
end_line_no=variable.lineno,
142146
end_col_offset=variable.end_col_offset,
143147
source=self.source,
144-
has_value=bool(variable.value),
148+
has_value=has_value,
145149
resolvable=True,
146-
value=variable.value,
150+
value=value,
147151
)
148152
)
149153

@@ -546,7 +550,6 @@ def __init__(
546550
imports_manager: ImportsManager,
547551
model: ast.AST,
548552
source: str,
549-
invalidated_callback: Callable[[Namespace], None],
550553
document: Optional[TextDocument] = None,
551554
document_type: Optional[DocumentType] = None,
552555
languages: Optional[Languages] = None,
@@ -557,7 +560,6 @@ def __init__(
557560

558561
self.model = model
559562
self.source = source
560-
self.invalidated_callback = invalidated_callback
561563
self._document = weakref.ref(document) if document is not None else None
562564
self.document_type: Optional[DocumentType] = document_type
563565
self.languages = languages
@@ -598,6 +600,22 @@ def __init__(
598600
self.imports_manager.resources_changed.add(self.resources_changed)
599601
self.imports_manager.variables_changed.add(self.variables_changed)
600602

603+
@async_event
604+
async def has_invalidated(sender) -> None: # NOSONAR
605+
...
606+
607+
@async_event
608+
async def has_initialized(sender) -> None: # NOSONAR
609+
...
610+
611+
@async_event
612+
async def has_imports_changed(sender) -> None: # NOSONAR
613+
...
614+
615+
@async_event
616+
async def has_analysed(sender) -> None: # NOSONAR
617+
...
618+
601619
@property
602620
def document(self) -> Optional[TextDocument]:
603621
return self._document() if self._document is not None else None
@@ -663,7 +681,7 @@ async def invalidate(self) -> None:
663681

664682
await self._reset_global_variables()
665683

666-
self.invalidated_callback(self)
684+
await self.has_invalidated(self)
667685

668686
@_logger.call
669687
async def get_diagnostisc(self) -> List[Diagnostic]:
@@ -744,7 +762,9 @@ class DataEntry(NamedTuple):
744762

745763
@_logger.call(condition=lambda self: not self._initialized)
746764
async def ensure_initialized(self) -> bool:
765+
747766
if not self._initialized:
767+
imports_changed = False
748768
async with self._initialize_lock:
749769
if not self._initialized:
750770
try:
@@ -760,6 +780,8 @@ async def ensure_initialized(self) -> bool:
760780
if old_imports is None:
761781
self.document.set_data(Namespace, imports)
762782
elif old_imports != imports:
783+
imports_changed = True
784+
763785
new_imports = []
764786
for e in old_imports:
765787
if e in imports:
@@ -801,12 +823,19 @@ async def ensure_initialized(self) -> bool:
801823
await self._reset_global_variables()
802824

803825
self._initialized = True
826+
804827
except BaseException as e:
805828
if not isinstance(e, asyncio.CancelledError):
806829
self._logger.exception(e, level=logging.DEBUG)
807830
await self.invalidate()
808831
raise
809832

833+
if self._initialized:
834+
await self.has_initialized(self)
835+
836+
if imports_changed:
837+
await self.has_imports_changed(self)
838+
810839
return self._initialized
811840

812841
@property
@@ -1532,6 +1561,7 @@ async def _analyze(self) -> None:
15321561
source=DIAGNOSTICS_SOURCE_NAME,
15331562
code=err.type_name,
15341563
)
1564+
15351565
except asyncio.CancelledError:
15361566
canceled = True
15371567
self._logger.debug("analyzing canceled")
@@ -1545,6 +1575,8 @@ async def _analyze(self) -> None:
15451575
else f"end analyzed {self.document} failed in {time.monotonic() - start_time}s"
15461576
)
15471577

1578+
await self.has_analysed(self)
1579+
15481580
async def get_finder(self) -> KeywordFinder:
15491581
if self._finder is None:
15501582
await self.ensure_initialized()

0 commit comments

Comments
 (0)