Skip to content

Commit 2951759

Browse files
committed
perf(analyze): improved performance of code analysis (more then 2x faster)
1 parent 3874a9c commit 2951759

30 files changed

+1874
-1109
lines changed

packages/core/src/robotcode/core/utils/logging.py

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
Dict,
1717
Iterator,
1818
List,
19+
Mapping,
1920
Optional,
2021
Type,
2122
TypeVar,
@@ -192,20 +193,25 @@ def log(
192193
*args: Any,
193194
stacklevel: int = 2,
194195
context_name: Optional[str] = None,
196+
extra: Optional[Mapping[str, object]] = None,
195197
**kwargs: Any,
196198
) -> None:
197199
if self.is_enabled_for(level) and condition is not None and condition() or condition is None:
198200
depth = 0
199201
if context_name is not None:
200202
depth = self._measure_contexts.get(context_name, 0)
201203

202-
msg = (" " * depth) + (msg() if callable(msg) else msg)
204+
if depth > 0:
205+
extra = {**extra} if extra is not None else {}
206+
if "indent" not in extra:
207+
extra["indent"] = " " * depth
203208

204209
self.logger.log(
205210
level,
206-
msg,
211+
msg() if callable(msg) else msg,
207212
*args,
208213
stacklevel=stacklevel,
214+
extra=extra,
209215
**kwargs,
210216
)
211217

@@ -215,6 +221,8 @@ def debug(
215221
condition: Optional[Callable[[], bool]] = None,
216222
*args: Any,
217223
stacklevel: int = 3,
224+
context_name: Optional[str] = None,
225+
extra: Optional[Mapping[str, object]] = None,
218226
**kwargs: Any,
219227
) -> None:
220228
return self.log(
@@ -223,6 +231,8 @@ def debug(
223231
condition,
224232
*args,
225233
stacklevel=stacklevel,
234+
context_name=context_name,
235+
extra=extra,
226236
**kwargs,
227237
)
228238

@@ -236,6 +246,7 @@ def measure_time(
236246
*args: Any,
237247
level: int = logging.DEBUG,
238248
context_name: Optional[str] = None,
249+
extra: Optional[Mapping[str, object]] = None,
239250
**kwargs: Any,
240251
) -> Iterator[None]:
241252
if self.is_enabled_for(level):
@@ -244,9 +255,19 @@ def measure_time(
244255
if context_name is not None:
245256
depth = self._measure_contexts.get(context_name, 0)
246257

247-
self._measure_contexts[context_name] = depth + 1
258+
self._measure_contexts[context_name] = depth
259+
260+
self._log_measure_time(
261+
level,
262+
lambda: f"Start {msg() if callable(msg) else msg}",
263+
*args,
264+
context_name=context_name,
265+
extra=extra,
266+
**kwargs,
267+
)
248268

249-
self._log_measure_time(level, f"{' '*depth}Start {msg() if callable(msg) else msg}", *args, **kwargs)
269+
if context_name is not None:
270+
self._measure_contexts[context_name] = depth + 1
250271

251272
start_time = time.monotonic()
252273
try:
@@ -259,8 +280,10 @@ def measure_time(
259280

260281
self._log_measure_time(
261282
level,
262-
f"{' '*depth}End {msg() if callable(msg) else msg} took {duration} seconds",
283+
lambda: f"End {msg() if callable(msg) else msg} took {duration:.4f} seconds",
263284
*args,
285+
context_name=context_name,
286+
extra=extra,
264287
**kwargs,
265288
)
266289
else:
@@ -272,16 +295,22 @@ def info(
272295
condition: Optional[Callable[[], bool]] = None,
273296
*args: Any,
274297
stacklevel: int = 3,
298+
context_name: Optional[str] = None,
299+
extra: Optional[Mapping[str, object]] = None,
275300
**kwargs: Any,
276301
) -> None:
277-
return self.log(logging.INFO, msg, condition, *args, stacklevel=stacklevel, **kwargs)
302+
return self.log(
303+
logging.INFO, msg, condition, *args, stacklevel=stacklevel, context_name=context_name, extra=extra, **kwargs
304+
)
278305

279306
def warning(
280307
self,
281308
msg: Union[str, Callable[[], str]],
282309
condition: Optional[Callable[[], bool]] = None,
283310
*args: Any,
284311
stacklevel: int = 3,
312+
context_name: Optional[str] = None,
313+
extra: Optional[Mapping[str, object]] = None,
285314
**kwargs: Any,
286315
) -> None:
287316
return self.log(
@@ -290,6 +319,8 @@ def warning(
290319
condition,
291320
*args,
292321
stacklevel=stacklevel,
322+
context_name=context_name,
323+
extra=extra,
293324
**kwargs,
294325
)
295326

@@ -299,6 +330,8 @@ def error(
299330
condition: Optional[Callable[[], bool]] = None,
300331
*args: Any,
301332
stacklevel: int = 3,
333+
context_name: Optional[str] = None,
334+
extra: Optional[Mapping[str, object]] = None,
302335
**kwargs: Any,
303336
) -> None:
304337
return self.log(
@@ -307,6 +340,8 @@ def error(
307340
condition,
308341
*args,
309342
stacklevel=stacklevel,
343+
context_name=context_name,
344+
extra=extra,
310345
**kwargs,
311346
)
312347

@@ -316,9 +351,13 @@ def trace(
316351
condition: Optional[Callable[[], bool]] = None,
317352
*args: Any,
318353
stacklevel: int = 3,
354+
context_name: Optional[str] = None,
355+
extra: Optional[Mapping[str, object]] = None,
319356
**kwargs: Any,
320357
) -> None:
321-
return self.log(TRACE, msg, condition, *args, stacklevel=stacklevel, **kwargs)
358+
return self.log(
359+
TRACE, msg, condition, *args, stacklevel=stacklevel, context_name=context_name, extra=extra, **kwargs
360+
)
322361

323362
def exception(
324363
self,
@@ -327,6 +366,8 @@ def exception(
327366
exc_info: Any = True,
328367
*args: Any,
329368
stacklevel: int = 3,
369+
context_name: Optional[str] = None,
370+
extra: Optional[Mapping[str, object]] = None,
330371
level: Optional[int] = None,
331372
**kwargs: Any,
332373
) -> None:
@@ -342,6 +383,8 @@ def exception(
342383
*args,
343384
exc_info=exc_info,
344385
stacklevel=stacklevel,
386+
context_name=context_name,
387+
extra=extra,
345388
**kwargs,
346389
)
347390

@@ -352,6 +395,7 @@ def exception(
352395
*args,
353396
exc_info=exc_info,
354397
stacklevel=stacklevel,
398+
extra=extra,
355399
**kwargs,
356400
)
357401

@@ -361,6 +405,8 @@ def critical(
361405
condition: Optional[Callable[[], bool]] = None,
362406
*args: Any,
363407
stacklevel: int = 3,
408+
context_name: Optional[str] = None,
409+
extra: Optional[Mapping[str, object]] = None,
364410
**kwargs: Any,
365411
) -> None:
366412
return self.log(
@@ -369,6 +415,8 @@ def critical(
369415
condition,
370416
*args,
371417
stacklevel=stacklevel,
418+
context_name=context_name,
419+
extra=extra,
372420
**kwargs,
373421
)
374422

packages/core/src/robotcode/core/utils/path.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def path_is_relative_to(
1919
_RE_DRIVE_LETTER_PATH = re.compile(r"^[a-zA-Z]:")
2020

2121

22-
def normalized_path(path: Union[Path, str, "os.PathLike[Any]"]) -> Path:
22+
def normalized_path(path: "Union[str, os.PathLike[str]]") -> Path:
2323
p = os.path.normpath(os.path.abspath(path))
2424

2525
if sys.platform == "win32" and _RE_DRIVE_LETTER_PATH.match(str(p)):
@@ -28,7 +28,7 @@ def normalized_path(path: Union[Path, str, "os.PathLike[Any]"]) -> Path:
2828
return Path(p)
2929

3030

31-
def normalized_path_full(path: Union[Path, str, "os.PathLike[Any]"]) -> Path:
31+
def normalized_path_full(path: Union[str, "os.PathLike[Any]"]) -> Path:
3232
p = normalized_path(path)
3333

3434
orig_parents = list(reversed(p.parents))

packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import concurrent.futures
22
import functools
33
import itertools
4+
import logging
45
import time
56
import uuid
67
from concurrent.futures import CancelledError
@@ -337,6 +338,7 @@ def run_workspace_diagnostics(self) -> None:
337338
self.ensure_workspace_loaded()
338339

339340
while True:
341+
340342
check_current_task_canceled()
341343

342344
self.on_workspace_diagnostics_start(self)
@@ -359,6 +361,7 @@ def run_workspace_diagnostics(self) -> None:
359361
with self._logger.measure_time(
360362
lambda: f"analyzing workspace for {len(documents)} documents",
361363
context_name="workspace_diagnostics",
364+
level=logging.CRITICAL,
362365
):
363366

364367
self.on_workspace_diagnostics_analyze(self)
@@ -375,13 +378,15 @@ def run_workspace_diagnostics(self) -> None:
375378
max=len(documents),
376379
start=False,
377380
) as progress:
381+
breaked = False
378382
for i, document in enumerate(documents):
379383
check_current_task_canceled()
380384

381-
if self._break_diagnostics_loop_event.is_set():
385+
if breaked or self._break_diagnostics_loop_event.is_set():
382386
self._logger.debug(
383387
"break workspace diagnostics loop 2", context_name="workspace_diagnostics"
384388
)
389+
breaked = True
385390
self.on_workspace_diagnostics_break(self)
386391
break
387392

@@ -409,6 +414,7 @@ def run_workspace_diagnostics(self) -> None:
409414
self._logger.debug(
410415
lambda: f"Analyzing {document.uri} cancelled", context_name="workspace_diagnostics"
411416
)
417+
breaked = True
412418
except BaseException as e:
413419
ex = e
414420
self._logger.exception(
@@ -420,7 +426,7 @@ def run_workspace_diagnostics(self) -> None:
420426
with self._current_diagnostics_task_lock:
421427
self._current_diagnostics_task = None
422428

423-
if self._break_diagnostics_loop_event.is_set():
429+
if breaked or self._break_diagnostics_loop_event.is_set():
424430
self._logger.debug("break workspace diagnostics loop 3", context_name="workspace_diagnostics")
425431
self.on_workspace_diagnostics_break(self)
426432
continue
@@ -436,13 +442,15 @@ def run_workspace_diagnostics(self) -> None:
436442
with self._logger.measure_time(
437443
lambda: f"collect workspace diagnostic for {len(documents_to_collect)} documents",
438444
context_name="collect_workspace_diagnostics",
445+
level=logging.CRITICAL,
439446
):
440-
447+
breaked = False
441448
for document in set(documents) - set(documents_to_collect):
442449
check_current_task_canceled()
443450

444-
if self._break_diagnostics_loop_event.is_set():
451+
if breaked or self._break_diagnostics_loop_event.is_set():
445452
self._logger.debug("break workspace diagnostics loop 4")
453+
breaked = True
446454
self.on_workspace_diagnostics_break(self)
447455
break
448456

@@ -456,10 +464,6 @@ def run_workspace_diagnostics(self) -> None:
456464
start=False,
457465
) as progress:
458466
for i, document in enumerate(documents_to_collect):
459-
self._logger.debug(
460-
lambda: f"collect diagnostics for {document.uri}",
461-
context_name="collect_workspace_diagnostics",
462-
)
463467
check_current_task_canceled()
464468

465469
if self._break_diagnostics_loop_event.is_set():
@@ -503,6 +507,7 @@ def run_workspace_diagnostics(self) -> None:
503507
lambda: f"Collecting diagnostics for {document.uri} cancelled",
504508
context_name="collect_workspace_diagnostics",
505509
)
510+
breaked = True
506511
except BaseException as e:
507512
ex = e
508513
self._logger.exception(
@@ -584,8 +589,6 @@ def _get_diagnostics_for_document(
584589
send_diagnostics: bool,
585590
collect_slow: bool,
586591
) -> None:
587-
self._logger.debug(lambda: f"Get diagnostics for {document}")
588-
589592
if debounce:
590593
check_current_task_canceled(0.75)
591594

@@ -661,8 +664,6 @@ def _text_document_diagnostic(
661664
*args: Any,
662665
**kwargs: Any,
663666
) -> DocumentDiagnosticReport:
664-
self._logger.debug("textDocument/diagnostic")
665-
666667
self.parent.ensure_initialized()
667668

668669
try:

packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2068,7 +2068,7 @@ def complete_arguments() -> Optional[List[CompletionItem]]:
20682068
return None
20692069

20702070
try:
2071-
libdoc = self.namespace.get_imported_variables_libdoc(import_node.name, import_node.args)
2071+
libdoc = self.namespace.get_variables_import_libdoc(import_node.name, import_node.args)
20722072
if libdoc is not None:
20732073
init = next((v for v in libdoc.inits.values()), None)
20742074
if init:

packages/language_server/src/robotcode/language_server/robotframework/parts/diagnostics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _on_variables_changed(self, sender: Any, variables: List[LibraryDoc]) -> Non
7979
for doc in self.parent.documents.documents:
8080
namespace = self.parent.documents_cache.get_only_initialized_namespace(doc)
8181
if namespace is not None:
82-
lib_docs = (e.library_doc for e in namespace.get_imported_variables().values())
82+
lib_docs = (e.library_doc for e in namespace.get_variables_imports().values())
8383
if any(lib_doc in lib_docs for lib_doc in variables):
8484
self.parent.diagnostics.force_refresh_document(doc)
8585

packages/language_server/src/robotcode/language_server/robotframework/parts/goto.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,17 @@ def collect(
6565
)
6666

6767
if found_range is not None and variable.source:
68-
result.append(
69-
LocationLink(
70-
origin_selection_range=found_range,
71-
target_uri=str(Uri.from_path(variable.source)),
72-
target_range=variable.range,
73-
target_selection_range=(
74-
range_from_token(variable.name_token) if variable.name_token else variable.range
75-
),
68+
if variable.source:
69+
result.append(
70+
LocationLink(
71+
origin_selection_range=found_range,
72+
target_uri=str(Uri.from_path(variable.source)),
73+
target_range=variable.range,
74+
target_selection_range=(
75+
range_from_token(variable.name_token) if variable.name_token else variable.range
76+
),
77+
)
7678
)
77-
)
7879

7980
if result:
8081
return result

packages/language_server/src/robotcode/language_server/robotframework/parts/inlay_hint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ def handle_VariablesImport( # noqa: N802
360360
try:
361361
namespace = self.parent.documents_cache.get_namespace(document)
362362

363-
lib_doc = namespace.get_imported_variables_libdoc(library_node.name, library_node.args)
363+
lib_doc = namespace.get_variables_import_libdoc(library_node.name, library_node.args)
364364

365365
if lib_doc is None or lib_doc.errors:
366366
lib_doc = namespace.imports_manager.get_libdoc_for_variables_import(

0 commit comments

Comments
 (0)