robotcode-language-server 0.92.0__tar.gz → 0.93.0__tar.gz

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 (75) hide show
  1. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/.gitignore +2 -0
  2. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/PKG-INFO +5 -5
  3. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/pyproject.toml +4 -4
  4. robotcode_language_server-0.93.0/src/robotcode/language_server/__version__.py +1 -0
  5. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/cli.py +7 -1
  6. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/diagnostics.py +133 -129
  7. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/hover.py +15 -1
  8. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/inlay_hint.py +2 -4
  9. robotcode_language_server-0.93.0/src/robotcode/language_server/robotframework/parts/robot_workspace.py +128 -0
  10. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/semantic_tokens.py +73 -24
  11. robotcode_language_server-0.92.0/src/robotcode/language_server/__version__.py +0 -1
  12. robotcode_language_server-0.92.0/src/robotcode/language_server/robotframework/parts/robot_workspace.py +0 -125
  13. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/LICENSE.txt +0 -0
  14. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/README.md +0 -0
  15. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/__init__.py +0 -0
  16. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/__init__.py +0 -0
  17. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/decorators.py +0 -0
  18. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/__init__.py +0 -0
  19. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/code_action.py +0 -0
  20. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/code_lens.py +0 -0
  21. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/commands.py +0 -0
  22. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/completion.py +0 -0
  23. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/declaration.py +0 -0
  24. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/definition.py +0 -0
  25. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/document_highlight.py +0 -0
  26. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/document_symbols.py +0 -0
  27. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/documents.py +0 -0
  28. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/folding_range.py +0 -0
  29. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/formatting.py +0 -0
  30. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/hover.py +0 -0
  31. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/implementation.py +0 -0
  32. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/inlay_hint.py +0 -0
  33. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/inline_value.py +0 -0
  34. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/linked_editing_ranges.py +0 -0
  35. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/protocol_part.py +0 -0
  36. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/references.py +0 -0
  37. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/rename.py +0 -0
  38. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/selection_range.py +0 -0
  39. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/semantic_tokens.py +0 -0
  40. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/signature_help.py +0 -0
  41. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/window.py +0 -0
  42. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/parts/workspace.py +0 -0
  43. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/protocol.py +0 -0
  44. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/common/server.py +0 -0
  45. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/hooks.py +0 -0
  46. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/py.typed +0 -0
  47. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/__init__.py +0 -0
  48. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/configuration.py +0 -0
  49. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/__init__.py +0 -0
  50. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/code_action_documentation.py +0 -0
  51. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/code_action_helper_mixin.py +0 -0
  52. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/code_action_quick_fixes.py +0 -0
  53. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/code_action_refactor.py +0 -0
  54. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/code_lens.py +0 -0
  55. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/completion.py +0 -0
  56. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/debugging_utils.py +0 -0
  57. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/diagnostics.py +0 -0
  58. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/document_highlight.py +0 -0
  59. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/document_symbols.py +0 -0
  60. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/documents_cache.py +0 -0
  61. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/folding_range.py +0 -0
  62. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/formatting.py +0 -0
  63. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/goto.py +0 -0
  64. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/http_server.py +0 -0
  65. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/inline_value.py +0 -0
  66. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/keywords_treeview.py +0 -0
  67. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/project_info.py +0 -0
  68. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/protocol_part.py +0 -0
  69. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/references.py +0 -0
  70. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/rename.py +0 -0
  71. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/robocop_diagnostics.py +0 -0
  72. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/selection_range.py +0 -0
  73. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/parts/signature_help.py +0 -0
  74. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/protocol.py +0 -0
  75. {robotcode_language_server-0.92.0 → robotcode_language_server-0.93.0}/src/robotcode/language_server/robotframework/server.py +0 -0
@@ -282,6 +282,8 @@ playground/
282
282
  test-results
283
283
  results.html
284
284
  report.html
285
+ log.html
286
+ output.xml
285
287
 
286
288
  # pyenv
287
289
  .python-version
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: robotcode-language-server
3
- Version: 0.92.0
3
+ Version: 0.93.0
4
4
  Summary: RobotCode Language Server for Robot Framework
5
5
  Project-URL: Homepage, https://robotcode.io
6
6
  Project-URL: Donate, https://opencollective.com/robotcode
@@ -25,10 +25,10 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
25
25
  Classifier: Topic :: Utilities
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.8
28
- Requires-Dist: robotcode-analyze==0.92.0
29
- Requires-Dist: robotcode-jsonrpc2==0.92.0
30
- Requires-Dist: robotcode-robot==0.92.0
31
- Requires-Dist: robotcode==0.92.0
28
+ Requires-Dist: robotcode-analyze==0.93.0
29
+ Requires-Dist: robotcode-jsonrpc2==0.93.0
30
+ Requires-Dist: robotcode-robot==0.93.0
31
+ Requires-Dist: robotcode==0.93.0
32
32
  Requires-Dist: robotframework>=4.1.0
33
33
  Description-Content-Type: text/markdown
34
34
 
@@ -27,10 +27,10 @@ classifiers = [
27
27
  ]
28
28
  dependencies = [
29
29
  "robotframework>=4.1.0",
30
- "robotcode-jsonrpc2==0.92.0",
31
- "robotcode-robot==0.92.0",
32
- "robotcode-analyze==0.92.0",
33
- "robotcode==0.92.0",
30
+ "robotcode-jsonrpc2==0.93.0",
31
+ "robotcode-robot==0.93.0",
32
+ "robotcode-analyze==0.93.0",
33
+ "robotcode==0.93.0",
34
34
  ]
35
35
  dynamic = ["version"]
36
36
 
@@ -82,7 +82,13 @@ def language_server(
82
82
  profile: Optional[RobotBaseProfile] = None
83
83
  analysis_config: Optional[WorkspaceAnalysisConfig] = None
84
84
 
85
- config_files, root_folder, _ = get_config_files(paths, app.config.config_files, verbose_callback=app.verbose)
85
+ config_files, root_folder, _ = get_config_files(
86
+ paths,
87
+ app.config.config_files,
88
+ root_folder=app.config.root,
89
+ no_vcs=app.config.no_vcs,
90
+ verbose_callback=app.verbose,
91
+ )
86
92
  if root_folder:
87
93
  os.chdir(root_folder)
88
94
 
@@ -65,12 +65,6 @@ class DiagnosticsResult:
65
65
  skipped: bool = False
66
66
 
67
67
 
68
- @dataclass
69
- class WorkspaceDocumentsResult:
70
- name: Optional[str]
71
- document: TextDocument
72
-
73
-
74
68
  @dataclass
75
69
  class DiagnosticsData:
76
70
  lock: RLock
@@ -158,7 +152,7 @@ class DiagnosticsProtocolPart(LanguageServerProtocolPart):
158
152
  @event
159
153
  def load_workspace_documents(
160
154
  sender,
161
- ) -> Optional[List[WorkspaceDocumentsResult]]: ...
155
+ ) -> None: ...
162
156
 
163
157
  @event
164
158
  def on_workspace_loaded(sender: Any) -> None: ...
@@ -362,72 +356,74 @@ class DiagnosticsProtocolPart(LanguageServerProtocolPart):
362
356
  check_current_task_canceled(1)
363
357
  continue
364
358
 
365
- self._logger.debug(lambda: f"start collecting workspace diagnostics for {len(documents)} documents")
359
+ with self._logger.measure_time(
360
+ lambda: f"analyzing workspace for {len(documents)} documents",
361
+ context_name="workspace_diagnostics",
362
+ ):
366
363
 
367
- self.on_workspace_diagnostics_analyze(self)
364
+ self.on_workspace_diagnostics_analyze(self)
368
365
 
369
- if self._break_diagnostics_loop_event.is_set():
370
- self._logger.debug("break workspace diagnostics loop 1")
371
- self.on_workspace_diagnostics_break(self)
372
- continue
366
+ if self._break_diagnostics_loop_event.is_set():
367
+ self._logger.debug("break workspace diagnostics loop 1", context_name="workspace_diagnostics")
368
+ self.on_workspace_diagnostics_break(self)
369
+ continue
373
370
 
374
- start = time.monotonic()
375
- with self.parent.window.progress(
376
- "Analyze Workspace",
377
- cancellable=False,
378
- current=0,
379
- max=len(documents),
380
- start=False,
381
- ) as progress:
382
- for i, document in enumerate(documents):
383
- self._logger.debug(lambda: f"Analyze {document}")
384
- check_current_task_canceled()
371
+ with self.parent.window.progress(
372
+ "Analyze Workspace",
373
+ cancellable=False,
374
+ current=0,
375
+ max=len(documents),
376
+ start=False,
377
+ ) as progress:
378
+ for i, document in enumerate(documents):
379
+ check_current_task_canceled()
380
+
381
+ if self._break_diagnostics_loop_event.is_set():
382
+ self._logger.debug(
383
+ "break workspace diagnostics loop 2", context_name="workspace_diagnostics"
384
+ )
385
+ self.on_workspace_diagnostics_break(self)
386
+ break
385
387
 
386
- if self._break_diagnostics_loop_event.is_set():
387
- self._logger.debug("break workspace diagnostics loop 2")
388
- self.on_workspace_diagnostics_break(self)
389
- break
388
+ done_something = True
390
389
 
391
- done_something = True
392
-
393
- analysis_mode = self.get_analysis_progress_mode(document.uri)
394
-
395
- if analysis_mode == AnalysisProgressMode.DETAILED:
396
- progress.begin()
397
- path = document.uri.to_path()
398
- folder = self.parent.workspace.get_workspace_folder(document.uri)
399
- name = path if folder is None else path.relative_to(folder.uri.to_path())
400
-
401
- progress.report(f"Analyze {i+1}/{len(documents)}: {name}", current=i + 1)
402
- elif analysis_mode == AnalysisProgressMode.SIMPLE:
403
- progress.begin()
404
- progress.report(f"Analyze {i+1}/{len(documents)}", current=i + 1)
405
-
406
- try:
407
- with self._current_diagnostics_task_lock:
408
- self._current_diagnostics_task = run_as_task(self._analyse_document, document)
409
- self._current_diagnostics_task.result(self._diagnostics_task_timeout)
410
-
411
- except CancelledError:
412
- self._logger.debug(lambda: f"Analyzing {document} cancelled")
413
- except BaseException as e:
414
- ex = e
415
- self._logger.exception(
416
- lambda: f"Error in analyzing ${document}: {ex}",
417
- exc_info=ex,
418
- )
419
- finally:
420
- with self._current_diagnostics_task_lock:
421
- self._current_diagnostics_task = None
390
+ analysis_mode = self.get_analysis_progress_mode(document.uri)
422
391
 
423
- self._logger.debug(
424
- lambda: f"Analyzing workspace for {len(documents)} " f"documents takes {time.monotonic() - start}s"
425
- )
392
+ if analysis_mode == AnalysisProgressMode.DETAILED:
393
+ progress.begin()
394
+ path = document.uri.to_path()
395
+ folder = self.parent.workspace.get_workspace_folder(document.uri)
396
+ name = path if folder is None else path.relative_to(folder.uri.to_path())
426
397
 
427
- if self._break_diagnostics_loop_event.is_set():
428
- self._logger.debug("break workspace diagnostics loop 3")
429
- self.on_workspace_diagnostics_break(self)
430
- continue
398
+ progress.report(f"Analyze {i+1}/{len(documents)}: {name}", current=i + 1)
399
+ elif analysis_mode == AnalysisProgressMode.SIMPLE:
400
+ progress.begin()
401
+ progress.report(f"Analyze {i+1}/{len(documents)}", current=i + 1)
402
+
403
+ try:
404
+ with self._current_diagnostics_task_lock:
405
+ self._current_diagnostics_task = run_as_task(self._analyse_document, document)
406
+ self._current_diagnostics_task.result(self._diagnostics_task_timeout)
407
+
408
+ except CancelledError:
409
+ self._logger.debug(
410
+ lambda: f"Analyzing {document.uri} cancelled", context_name="workspace_diagnostics"
411
+ )
412
+ except BaseException as e:
413
+ ex = e
414
+ self._logger.exception(
415
+ lambda: f"Error in analyzing ${document.uri}: {ex}",
416
+ exc_info=ex,
417
+ context_name="workspace_diagnostics",
418
+ )
419
+ finally:
420
+ with self._current_diagnostics_task_lock:
421
+ self._current_diagnostics_task = None
422
+
423
+ if self._break_diagnostics_loop_event.is_set():
424
+ self._logger.debug("break workspace diagnostics loop 3", context_name="workspace_diagnostics")
425
+ self.on_workspace_diagnostics_break(self)
426
+ continue
431
427
 
432
428
  self.on_workspace_diagnostics_collect(self)
433
429
 
@@ -437,78 +433,86 @@ class DiagnosticsProtocolPart(LanguageServerProtocolPart):
437
433
  if doc.opened_in_editor or self.get_diagnostics_mode(document.uri) == DiagnosticsMode.WORKSPACE
438
434
  ]
439
435
 
440
- for document in set(documents) - set(documents_to_collect):
441
- check_current_task_canceled()
436
+ with self._logger.measure_time(
437
+ lambda: f"collect workspace diagnostic for {len(documents_to_collect)} documents",
438
+ context_name="collect_workspace_diagnostics",
439
+ ):
442
440
 
443
- if self._break_diagnostics_loop_event.is_set():
444
- self._logger.debug("break workspace diagnostics loop 4")
445
- self.on_workspace_diagnostics_break(self)
446
- break
447
-
448
- self.reset_document_diagnostics_data(document)
449
-
450
- start = time.monotonic()
451
- with self.parent.window.progress(
452
- "Collect Diagnostics",
453
- cancellable=False,
454
- current=0,
455
- max=len(documents_to_collect),
456
- start=False,
457
- ) as progress:
458
- for i, document in enumerate(documents_to_collect):
459
- self._logger.debug(lambda: f"Collect diagnostics for {document}")
441
+ for document in set(documents) - set(documents_to_collect):
460
442
  check_current_task_canceled()
461
443
 
462
444
  if self._break_diagnostics_loop_event.is_set():
463
- self._logger.debug("break workspace diagnostics loop 5")
445
+ self._logger.debug("break workspace diagnostics loop 4")
464
446
  self.on_workspace_diagnostics_break(self)
465
447
  break
466
448
 
467
- mode = self.get_diagnostics_mode(document.uri)
468
- if mode == DiagnosticsMode.OFF:
469
- self.reset_document_diagnostics_data(document)
470
- continue
471
-
472
- done_something = True
473
-
474
- analysis_mode = self.get_analysis_progress_mode(document.uri)
475
-
476
- if analysis_mode == AnalysisProgressMode.DETAILED:
477
- progress.begin()
478
- path = document.uri.to_path()
479
- folder = self.parent.workspace.get_workspace_folder(document.uri)
480
- name = path if folder is None else path.relative_to(folder.uri.to_path())
481
-
482
- progress.report(f"Collect {i+1}/{len(documents_to_collect)}: {name}", current=i + 1)
483
- elif analysis_mode == AnalysisProgressMode.SIMPLE:
484
- progress.begin()
485
- progress.report(f"Collect {i+1}/{len(documents_to_collect)}", current=i + 1)
486
-
487
- try:
488
- with self._current_diagnostics_task_lock:
489
- self._current_diagnostics_task = self.create_document_diagnostics_task(
490
- document,
491
- False,
492
- mode == DiagnosticsMode.WORKSPACE or document.opened_in_editor,
493
- )
494
- if self._current_diagnostics_task is not None:
495
- self._current_diagnostics_task.result(self._diagnostics_task_timeout)
496
- except CancelledError:
497
- self._logger.debug(lambda: f"Collecting diagnostics for {document} cancelled")
498
- except BaseException as e:
499
- ex = e
500
- self._logger.exception(
501
- lambda: f"Error getting diagnostics for ${document}: {ex}",
502
- exc_info=ex,
449
+ self.reset_document_diagnostics_data(document)
450
+
451
+ with self.parent.window.progress(
452
+ "Collect Diagnostics",
453
+ cancellable=False,
454
+ current=0,
455
+ max=len(documents_to_collect),
456
+ start=False,
457
+ ) as progress:
458
+ 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",
503
462
  )
504
- finally:
505
- with self._current_diagnostics_task_lock:
506
- self._current_diagnostics_task = None
463
+ check_current_task_canceled()
507
464
 
508
- self._logger.debug(
509
- lambda: f"collecting workspace diagnostics for {len(documents_to_collect)} "
510
- f"documents takes {time.monotonic() - start}s"
511
- )
465
+ if self._break_diagnostics_loop_event.is_set():
466
+ self._logger.debug(
467
+ "break workspace diagnostics loop 5", context_name="collect_workspace_diagnostics"
468
+ )
469
+ self.on_workspace_diagnostics_break(self)
470
+ break
471
+
472
+ mode = self.get_diagnostics_mode(document.uri)
473
+ if mode == DiagnosticsMode.OFF:
474
+ self.reset_document_diagnostics_data(document)
475
+ continue
476
+
477
+ done_something = True
478
+
479
+ analysis_mode = self.get_analysis_progress_mode(document.uri)
480
+
481
+ if analysis_mode == AnalysisProgressMode.DETAILED:
482
+ progress.begin()
483
+ path = document.uri.to_path()
484
+ folder = self.parent.workspace.get_workspace_folder(document.uri)
485
+ name = path if folder is None else path.relative_to(folder.uri.to_path())
486
+
487
+ progress.report(f"Collect {i+1}/{len(documents_to_collect)}: {name}", current=i + 1)
488
+ elif analysis_mode == AnalysisProgressMode.SIMPLE:
489
+ progress.begin()
490
+ progress.report(f"Collect {i+1}/{len(documents_to_collect)}", current=i + 1)
491
+
492
+ try:
493
+ with self._current_diagnostics_task_lock:
494
+ self._current_diagnostics_task = self.create_document_diagnostics_task(
495
+ document,
496
+ False,
497
+ mode == DiagnosticsMode.WORKSPACE or document.opened_in_editor,
498
+ )
499
+ if self._current_diagnostics_task is not None:
500
+ self._current_diagnostics_task.result(self._diagnostics_task_timeout)
501
+ except CancelledError:
502
+ self._logger.debug(
503
+ lambda: f"Collecting diagnostics for {document.uri} cancelled",
504
+ context_name="collect_workspace_diagnostics",
505
+ )
506
+ except BaseException as e:
507
+ ex = e
508
+ self._logger.exception(
509
+ lambda: f"Error getting diagnostics for ${document.uri}: {ex}",
510
+ exc_info=ex,
511
+ context_name="collect_workspace_diagnostics",
512
+ )
513
+ finally:
514
+ with self._current_diagnostics_task_lock:
515
+ self._current_diagnostics_task = None
512
516
 
513
517
  except (SystemExit, KeyboardInterrupt, CancelledError):
514
518
  raise
@@ -184,11 +184,19 @@ class RobotHoverProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
184
184
  )
185
185
 
186
186
  if found_range is not None:
187
- result.append((found_range, kw.to_markdown()))
187
+ txt = kw.to_markdown()
188
+ if kw.libtype == "RESOURCE":
189
+ txt = namespace.imports_manager.replace_variables_scalar(
190
+ txt,
191
+ str(document.uri.to_path().parent),
192
+ namespace.get_resolvable_variables(nodes, position),
193
+ )
194
+ result.append((found_range, txt))
188
195
  if result:
189
196
  r = result[0][0]
190
197
  if all(r == i[0] for i in result):
191
198
  doc = "\n\n---\n\n".join(i[1] for i in result)
199
+
192
200
  return Hover(
193
201
  contents=MarkupContent(kind=MarkupKind.MARKDOWN, value=doc),
194
202
  range=r,
@@ -233,6 +241,7 @@ class RobotHoverProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
233
241
  document: TextDocument,
234
242
  position: Position,
235
243
  ) -> Optional[Hover]:
244
+ namespace = self.parent.documents_cache.get_namespace(document)
236
245
  test_case = cast(TestCase, node)
237
246
 
238
247
  if not position.is_in_range(range_from_node(test_case.header)):
@@ -259,6 +268,11 @@ class RobotHoverProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
259
268
  txt += "\n*Tags*: "
260
269
  txt += f"{', '.join(tags.values)}\n"
261
270
 
271
+ txt = namespace.imports_manager.replace_variables_scalar(
272
+ txt,
273
+ str(document.uri.to_path().parent),
274
+ namespace.get_resolvable_variables(nodes, position),
275
+ )
262
276
  return Hover(
263
277
  contents=MarkupContent(kind=MarkupKind.MARKDOWN, value=MarkDownFormatter().format(txt)),
264
278
  range=range_from_token(name_token),
@@ -329,8 +329,7 @@ class RobotInlayHintProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
329
329
 
330
330
  except (SystemExit, KeyboardInterrupt):
331
331
  raise
332
- except BaseException as e:
333
- self._logger.exception(e)
332
+ except BaseException:
334
333
  return None
335
334
 
336
335
  arguments = library_node.get_tokens(RobotToken.ARGUMENT)
@@ -373,8 +372,7 @@ class RobotInlayHintProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
373
372
 
374
373
  except (SystemExit, KeyboardInterrupt):
375
374
  raise
376
- except BaseException as e:
377
- self._logger.exception(e)
375
+ except BaseException:
378
376
  return None
379
377
 
380
378
  arguments = library_node.get_tokens(RobotToken.ARGUMENT)
@@ -0,0 +1,128 @@
1
+ from concurrent.futures import CancelledError
2
+ from logging import CRITICAL
3
+ from pathlib import Path
4
+ from threading import Event
5
+ from typing import TYPE_CHECKING, Any, List, Optional
6
+
7
+ from robotcode.core.ignore_spec import DEFAULT_SPEC_RULES, GIT_IGNORE_FILE, ROBOT_IGNORE_FILE, IgnoreSpec, iter_files
8
+ from robotcode.core.language import language_id
9
+ from robotcode.core.uri import Uri
10
+ from robotcode.core.utils.logging import LoggingDescriptor
11
+ from robotcode.jsonrpc2.protocol import rpc_method
12
+ from robotcode.language_server.common.parts.diagnostics import (
13
+ AnalysisProgressMode,
14
+ DiagnosticsMode,
15
+ )
16
+ from robotcode.language_server.robotframework.configuration import AnalysisConfig
17
+ from robotcode.robot.diagnostics.library_doc import (
18
+ RESOURCE_FILE_EXTENSION,
19
+ ROBOT_FILE_EXTENSION,
20
+ )
21
+
22
+ from ..configuration import RobotCodeConfig
23
+
24
+ if TYPE_CHECKING:
25
+ from ..protocol import RobotLanguageServerProtocol
26
+
27
+ from .protocol_part import RobotLanguageServerProtocolPart
28
+
29
+
30
+ class CantReadDocumentError(Exception):
31
+ pass
32
+
33
+
34
+ class RobotWorkspaceProtocolPart(RobotLanguageServerProtocolPart):
35
+ _logger = LoggingDescriptor()
36
+
37
+ def __init__(self, parent: "RobotLanguageServerProtocol") -> None:
38
+ super().__init__(parent)
39
+ self.parent.documents.on_read_document_text.add(self.on_read_document_text)
40
+ self.parent.diagnostics.load_workspace_documents.add(self.load_workspace_documents)
41
+ self.parent.diagnostics.on_get_diagnostics_mode.add(self.on_get_diagnostics_mode)
42
+ self.parent.diagnostics.on_get_analysis_progress_mode.add(self.on_get_analysis_progress_mode)
43
+ self.documents_loaded = Event()
44
+
45
+ @language_id("robotframework")
46
+ def on_read_document_text(self, sender: Any, uri: Uri) -> Optional[str]:
47
+ from robot.utils import FileReader
48
+
49
+ with FileReader(uri.to_path()) as reader:
50
+ return str(reader.read())
51
+
52
+ def on_get_diagnostics_mode(self, sender: Any, uri: Uri) -> Optional[DiagnosticsMode]:
53
+ config = self.parent.workspace.get_configuration(AnalysisConfig, uri)
54
+ return config.diagnostic_mode
55
+
56
+ def on_get_analysis_progress_mode(self, sender: Any, uri: Uri) -> Optional[AnalysisProgressMode]:
57
+ config = self.parent.workspace.get_configuration(AnalysisConfig, uri)
58
+ return config.progress_mode
59
+
60
+ def load_workspace_documents(self, sender: Any) -> None:
61
+ with self._logger.measure_time(lambda: "loading workspace documents", context_name="load_workspace_documents"):
62
+ try:
63
+ result: List[Path] = []
64
+
65
+ for folder in self.parent.workspace.workspace_folders:
66
+ config = self.parent.workspace.get_configuration(RobotCodeConfig, folder.uri)
67
+
68
+ extensions = [ROBOT_FILE_EXTENSION, RESOURCE_FILE_EXTENSION]
69
+ with self.parent.window.progress("Collect sources", cancellable=False):
70
+ files = list(
71
+ filter(
72
+ lambda f: f.suffix in extensions,
73
+ iter_files(
74
+ folder.uri.to_path(),
75
+ ignore_files=[ROBOT_IGNORE_FILE, GIT_IGNORE_FILE],
76
+ include_hidden=False,
77
+ parent_spec=IgnoreSpec.from_list(
78
+ [*DEFAULT_SPEC_RULES, *(config.workspace.exclude_patterns or [])],
79
+ folder.uri.to_path(),
80
+ ),
81
+ verbose_callback=self._logger.debug,
82
+ verbose_trace=False,
83
+ ),
84
+ )
85
+ )
86
+
87
+ result.extend(files)
88
+
89
+ canceled = False
90
+ with self.parent.window.progress(
91
+ "Load workspace", current=0, max=len(files), start=False, cancellable=False
92
+ ) as progress:
93
+ for i, f in enumerate(files):
94
+ try:
95
+ self.parent.documents.get_or_open_document(f)
96
+
97
+ if config.analysis.progress_mode != AnalysisProgressMode.OFF:
98
+ name = f.relative_to(folder.uri.to_path())
99
+
100
+ progress.begin()
101
+ progress.report(
102
+ (
103
+ f"Load {name!s}"
104
+ if config.analysis.progress_mode == AnalysisProgressMode.DETAILED
105
+ else None
106
+ ),
107
+ current=i,
108
+ )
109
+ except (SystemExit, KeyboardInterrupt):
110
+ raise
111
+ except CancelledError:
112
+ canceled = True
113
+ break
114
+ except BaseException as e:
115
+ ex = e
116
+ self._logger.exception(
117
+ lambda: f"Can't load document {f}: {ex}",
118
+ level=CRITICAL,
119
+ context_name="load_workspace_documents",
120
+ )
121
+ finally:
122
+ if canceled:
123
+ self._logger.info(lambda: "Workspace loading canceled")
124
+
125
+ @rpc_method(name="robot/cache/clear", threaded=True)
126
+ def robot_cache_clear(self) -> None:
127
+ for folder in self.parent.workspace.workspace_folders:
128
+ self.parent.documents_cache.get_imports_manager_for_workspace_folder(folder).clear_cache()
@@ -21,8 +21,10 @@ from typing import (
21
21
  )
22
22
 
23
23
  from robot.parsing.lexer.tokens import Token
24
+ from robot.parsing.model.blocks import Section
24
25
  from robot.parsing.model.statements import (
25
26
  Arguments,
27
+ Documentation,
26
28
  Fixture,
27
29
  KeywordCall,
28
30
  LibraryImport,
@@ -62,6 +64,7 @@ from robotcode.robot.diagnostics.model_helper import ModelHelper
62
64
  from robotcode.robot.diagnostics.namespace import DEFAULT_BDD_PREFIXES, Namespace
63
65
  from robotcode.robot.utils import get_robot_version
64
66
  from robotcode.robot.utils.ast import (
67
+ cached_isinstance,
65
68
  iter_nodes,
66
69
  iter_over_keyword_names_and_owners,
67
70
  token_in_range,
@@ -72,6 +75,9 @@ from .protocol_part import RobotLanguageServerProtocolPart
72
75
  if get_robot_version() >= (5, 0):
73
76
  from robot.parsing.model.statements import ExceptHeader, WhileHeader
74
77
 
78
+ if get_robot_version() >= (7, 0):
79
+ from robot.parsing.model.blocks import InvalidSection
80
+
75
81
  if TYPE_CHECKING:
76
82
  from ..protocol import RobotLanguageServerProtocol
77
83
 
@@ -120,6 +126,7 @@ class RobotSemTokenTypes(Enum):
120
126
 
121
127
  class RobotSemTokenModifiers(Enum):
122
128
  BUILTIN = "builtin"
129
+ EMBEDDED = "embedded"
123
130
 
124
131
 
125
132
  @dataclass
@@ -340,6 +347,7 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
340
347
  node: ast.AST,
341
348
  col_offset: Optional[int] = None,
342
349
  length: Optional[int] = None,
350
+ yield_arguments: bool = False,
343
351
  ) -> Iterator[SemTokenInfo]:
344
352
  sem_info = cls.mapping().get(token.type, None) if token.type is not None else None
345
353
  if sem_info is not None:
@@ -391,7 +399,7 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
391
399
  yield SemTokenInfo.from_token(token, sem_type, sem_mod)
392
400
 
393
401
  elif token.type in [Token.KEYWORD, ROBOT_KEYWORD_INNER] or (
394
- token.type == Token.NAME and isinstance(node, (Fixture, Template, TestTemplate))
402
+ token.type == Token.NAME and cached_isinstance(node, Fixture, Template, TestTemplate)
395
403
  ):
396
404
  if (
397
405
  namespace.find_keyword(
@@ -461,6 +469,9 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
461
469
 
462
470
  kw_index = len(kw_namespace) + 1 if kw_namespace else 0
463
471
 
472
+ if token.type == Token.NAME and kw_doc is not None:
473
+ sem_type = RobotSemTokenTypes.KEYWORD
474
+
464
475
  if kw_namespace:
465
476
  kw = token.value[kw_index:]
466
477
 
@@ -501,13 +512,25 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
501
512
  col_offset + kw_index + start,
502
513
  arg_start - start,
503
514
  )
504
- yield SemTokenInfo.from_token(
505
- token,
506
- RobotSemTokenTypes.EMBEDDED_ARGUMENT,
507
- sem_mod,
508
- col_offset + kw_index + arg_start,
509
- arg_end - arg_start,
515
+
516
+ embedded_token = Token(
517
+ Token.ARGUMENT,
518
+ token.value[arg_start:arg_end],
519
+ token.lineno,
520
+ token.col_offset + arg_start,
510
521
  )
522
+
523
+ for sub_token in ModelHelper.tokenize_variables(
524
+ embedded_token,
525
+ ignore_errors=True,
526
+ identifiers="$@&%",
527
+ ):
528
+ for e in cls.generate_sem_sub_tokens(
529
+ namespace, builtin_library_doc, sub_token, node, yield_arguments=True
530
+ ):
531
+ e.sem_modifiers = {RobotSemTokenModifiers.EMBEDDED}
532
+ yield e
533
+
511
534
  start = arg_end + 1
512
535
 
513
536
  if start < end:
@@ -521,7 +544,7 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
521
544
 
522
545
  else:
523
546
  yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset + kw_index, len(kw))
524
- elif token.type == Token.NAME and isinstance(node, (LibraryImport, ResourceImport, VariablesImport)):
547
+ elif token.type == Token.NAME and cached_isinstance(node, LibraryImport, ResourceImport, VariablesImport):
525
548
  if "\\" in token.value:
526
549
  if col_offset is None:
527
550
  col_offset = token.col_offset
@@ -543,7 +566,9 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
543
566
  length,
544
567
  )
545
568
  elif get_robot_version() >= (5, 0) and token.type == Token.OPTION:
546
- if (isinstance(node, ExceptHeader) or isinstance(node, WhileHeader)) and "=" in token.value:
569
+ if (
570
+ cached_isinstance(node, ExceptHeader) or cached_isinstance(node, WhileHeader)
571
+ ) and "=" in token.value:
547
572
  if col_offset is None:
548
573
  col_offset = token.col_offset
549
574
 
@@ -589,7 +614,12 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
589
614
  1,
590
615
  )
591
616
  else:
592
- if token.type != Token.ARGUMENT or token.type != Token.NAME and isinstance(node, Metadata):
617
+ if (
618
+ yield_arguments
619
+ or token.type != Token.ARGUMENT
620
+ or token.type != Token.NAME
621
+ and cached_isinstance(node, Metadata)
622
+ ):
593
623
  yield SemTokenInfo.from_token(token, sem_type, sem_mod, col_offset, length)
594
624
 
595
625
  def generate_sem_tokens(
@@ -602,25 +632,25 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
602
632
  if (
603
633
  token.type in {Token.ARGUMENT, Token.TESTCASE_NAME, Token.KEYWORD_NAME}
604
634
  or token.type == Token.NAME
605
- and isinstance(node, (VariablesImport, LibraryImport, ResourceImport))
635
+ and cached_isinstance(node, VariablesImport, LibraryImport, ResourceImport)
606
636
  ):
607
- if (isinstance(node, Variable) and token.type == Token.ARGUMENT and node.name and node.name[0] == "&") or (
608
- isinstance(node, Arguments)
609
- ):
637
+ if (
638
+ cached_isinstance(node, Variable) and token.type == Token.ARGUMENT and node.name and node.name[0] == "&"
639
+ ) or (cached_isinstance(node, Arguments)):
610
640
  name, value = split_from_equals(token.value)
611
641
  if value is not None:
612
642
  length = len(name)
613
643
 
614
644
  yield SemTokenInfo.from_token(
615
645
  Token(
616
- ROBOT_NAMED_ARGUMENT if isinstance(node, Variable) else SemanticTokenTypes.PARAMETER,
646
+ ROBOT_NAMED_ARGUMENT if cached_isinstance(node, Variable) else SemanticTokenTypes.PARAMETER,
617
647
  name,
618
648
  token.lineno,
619
649
  token.col_offset,
620
650
  ),
621
651
  (
622
652
  RobotSemTokenTypes.NAMED_ARGUMENT
623
- if isinstance(node, Variable)
653
+ if cached_isinstance(node, Variable)
624
654
  else SemanticTokenTypes.PARAMETER
625
655
  ),
626
656
  )
@@ -640,7 +670,7 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
640
670
  token.col_offset + length + 1,
641
671
  token.error,
642
672
  )
643
- elif isinstance(node, Arguments) and name:
673
+ elif cached_isinstance(node, Arguments) and name:
644
674
  yield SemTokenInfo.from_token(
645
675
  Token(
646
676
  ROBOT_NAMED_ARGUMENT,
@@ -663,11 +693,13 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
663
693
  ignore_errors=True,
664
694
  identifiers="$" if token.type == Token.KEYWORD_NAME else "$@&%",
665
695
  ):
666
- for e in self.generate_sem_sub_tokens(namespace, builtin_library_doc, sub_token, node):
696
+ for e in self.generate_sem_sub_tokens(
697
+ namespace, builtin_library_doc, sub_token, node, yield_arguments=True
698
+ ):
667
699
  yield e
668
700
 
669
701
  else:
670
- for e in self.generate_sem_sub_tokens(namespace, builtin_library_doc, token, node):
702
+ for e in self.generate_sem_sub_tokens(namespace, builtin_library_doc, token, node, yield_arguments=True):
671
703
  yield e
672
704
 
673
705
  def generate_run_kw_tokens(
@@ -953,11 +985,19 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
953
985
  last_col = 0
954
986
 
955
987
  def get_tokens() -> Iterator[Tuple[Token, ast.AST]]:
988
+ current_section: Optional[Section] = None
989
+ in_invalid_section = False
990
+
956
991
  for node in iter_nodes(model):
992
+ if cached_isinstance(node, Section):
993
+ current_section = node
994
+ if get_robot_version() >= (7, 0):
995
+ in_invalid_section = cached_isinstance(current_section, InvalidSection)
996
+
957
997
  check_current_task_canceled()
958
998
 
959
- if isinstance(node, Statement):
960
- if isinstance(node, LibraryImport) and node.name:
999
+ if cached_isinstance(node, Statement):
1000
+ if cached_isinstance(node, LibraryImport) and node.name:
961
1001
  lib_doc = namespace.get_imported_library_libdoc(node.name, node.args, node.alias)
962
1002
  kw_doc = lib_doc.inits.keywords[0] if lib_doc and lib_doc.inits else None
963
1003
  if lib_doc is not None:
@@ -1009,7 +1049,7 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
1009
1049
 
1010
1050
  yield token, node
1011
1051
  continue
1012
- if isinstance(node, VariablesImport) and node.name:
1052
+ if cached_isinstance(node, VariablesImport) and node.name:
1013
1053
  lib_doc = namespace.get_imported_variables_libdoc(node.name, node.args)
1014
1054
  kw_doc = lib_doc.inits.keywords[0] if lib_doc and lib_doc.inits else None
1015
1055
  if lib_doc is not None:
@@ -1061,12 +1101,12 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
1061
1101
 
1062
1102
  yield token, node
1063
1103
  continue
1064
- if isinstance(node, (KeywordCall, Fixture)):
1104
+ if cached_isinstance(node, KeywordCall, Fixture):
1065
1105
  kw_token = cast(
1066
1106
  Token,
1067
1107
  (
1068
1108
  node.get_token(Token.KEYWORD)
1069
- if isinstance(node, KeywordCall)
1109
+ if cached_isinstance(node, KeywordCall)
1070
1110
  else node.get_token(Token.NAME)
1071
1111
  ),
1072
1112
  )
@@ -1109,8 +1149,16 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
1109
1149
  yield kw_res
1110
1150
 
1111
1151
  continue
1152
+ if cached_isinstance(node, Documentation):
1153
+ for token in node.tokens:
1154
+ if token.type == Token.ARGUMENT:
1155
+ continue
1156
+ yield token, node
1157
+ continue
1112
1158
 
1113
1159
  for token in node.tokens:
1160
+ if not in_invalid_section and token.type == Token.COMMENT:
1161
+ continue
1114
1162
  yield token, node
1115
1163
 
1116
1164
  lines = document.get_lines()
@@ -1136,6 +1184,7 @@ class RobotSemanticTokenProtocolPart(RobotLanguageServerProtocolPart):
1136
1184
  ),
1137
1185
  ),
1138
1186
  )
1187
+
1139
1188
  token_col_offset = token_range.start.character
1140
1189
  token_length = token_range.end.character - token_range.start.character
1141
1190
 
@@ -1 +0,0 @@
1
- __version__ = "0.92.0"
@@ -1,125 +0,0 @@
1
- import time
2
- from concurrent.futures import CancelledError
3
- from logging import CRITICAL
4
- from threading import Event
5
- from typing import TYPE_CHECKING, Any, List, Optional
6
-
7
- from robotcode.core.ignore_spec import DEFAULT_SPEC_RULES, GIT_IGNORE_FILE, ROBOT_IGNORE_FILE, IgnoreSpec, iter_files
8
- from robotcode.core.language import language_id
9
- from robotcode.core.uri import Uri
10
- from robotcode.core.utils.logging import LoggingDescriptor
11
- from robotcode.jsonrpc2.protocol import rpc_method
12
- from robotcode.language_server.common.parts.diagnostics import (
13
- AnalysisProgressMode,
14
- DiagnosticsMode,
15
- WorkspaceDocumentsResult,
16
- )
17
- from robotcode.language_server.robotframework.configuration import AnalysisConfig
18
- from robotcode.robot.diagnostics.library_doc import (
19
- RESOURCE_FILE_EXTENSION,
20
- ROBOT_FILE_EXTENSION,
21
- )
22
-
23
- from ..configuration import RobotCodeConfig
24
-
25
- if TYPE_CHECKING:
26
- from ..protocol import RobotLanguageServerProtocol
27
-
28
- from .protocol_part import RobotLanguageServerProtocolPart
29
-
30
-
31
- class CantReadDocumentError(Exception):
32
- pass
33
-
34
-
35
- class RobotWorkspaceProtocolPart(RobotLanguageServerProtocolPart):
36
- _logger = LoggingDescriptor()
37
-
38
- def __init__(self, parent: "RobotLanguageServerProtocol") -> None:
39
- super().__init__(parent)
40
- self.parent.documents.on_read_document_text.add(self.on_read_document_text)
41
- self.parent.diagnostics.load_workspace_documents.add(self.load_workspace_documents)
42
- self.parent.diagnostics.on_get_diagnostics_mode.add(self.on_get_diagnostics_mode)
43
- self.parent.diagnostics.on_get_analysis_progress_mode.add(self.on_get_analysis_progress_mode)
44
- self.documents_loaded = Event()
45
-
46
- @language_id("robotframework")
47
- def on_read_document_text(self, sender: Any, uri: Uri) -> Optional[str]:
48
- from robot.utils import FileReader
49
-
50
- with FileReader(uri.to_path()) as reader:
51
- return str(reader.read())
52
-
53
- def on_get_diagnostics_mode(self, sender: Any, uri: Uri) -> Optional[DiagnosticsMode]:
54
- config = self.parent.workspace.get_configuration(AnalysisConfig, uri)
55
- return config.diagnostic_mode
56
-
57
- def on_get_analysis_progress_mode(self, sender: Any, uri: Uri) -> Optional[AnalysisProgressMode]:
58
- config = self.parent.workspace.get_configuration(AnalysisConfig, uri)
59
- return config.progress_mode
60
-
61
- def load_workspace_documents(self, sender: Any) -> List[WorkspaceDocumentsResult]:
62
- start = time.monotonic()
63
- try:
64
- result: List[WorkspaceDocumentsResult] = []
65
-
66
- for folder in self.parent.workspace.workspace_folders:
67
- config = self.parent.workspace.get_configuration(RobotCodeConfig, folder.uri)
68
-
69
- extensions = [ROBOT_FILE_EXTENSION, RESOURCE_FILE_EXTENSION]
70
- with self.parent.window.progress("Collect sources", cancellable=False):
71
- files = list(
72
- filter(
73
- lambda f: f.suffix in extensions,
74
- iter_files(
75
- folder.uri.to_path(),
76
- ignore_files=[ROBOT_IGNORE_FILE, GIT_IGNORE_FILE],
77
- include_hidden=False,
78
- parent_spec=IgnoreSpec.from_list(
79
- [*DEFAULT_SPEC_RULES, *(config.workspace.exclude_patterns or [])],
80
- folder.uri.to_path(),
81
- ),
82
- ),
83
- )
84
- )
85
-
86
- canceled = False
87
- with self.parent.window.progress(
88
- "Load workspace", current=0, max=len(files), start=False, cancellable=False
89
- ) as progress:
90
- for i, f in enumerate(files):
91
- try:
92
- self.parent.documents.get_or_open_document(f)
93
-
94
- if config.analysis.progress_mode != AnalysisProgressMode.OFF:
95
- name = f.relative_to(folder.uri.to_path())
96
-
97
- progress.begin()
98
- progress.report(
99
- (
100
- f"Load {name!s}"
101
- if config.analysis.progress_mode == AnalysisProgressMode.DETAILED
102
- else None
103
- ),
104
- current=i,
105
- )
106
- except (SystemExit, KeyboardInterrupt):
107
- raise
108
- except CancelledError:
109
- canceled = True
110
- break
111
- except BaseException as e:
112
- ex = e
113
- self._logger.exception(lambda: f"Can't load document {f}: {ex}", level=CRITICAL)
114
-
115
- if canceled:
116
- return []
117
-
118
- return result
119
- finally:
120
- self._logger.info(lambda: f"Workspace loaded {len(result)} documents in {time.monotonic() - start}s")
121
-
122
- @rpc_method(name="robot/cache/clear", threaded=True)
123
- def robot_cache_clear(self) -> None:
124
- for folder in self.parent.workspace.workspace_folders:
125
- self.parent.documents_cache.get_imports_manager_for_workspace_folder(folder).clear_cache()