robotcode-language-server 2.6.2__py3-none-any.whl → 2.8.0.dev548__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.
Files changed (31) hide show
  1. robotcode/language_server/__version__.py +1 -1
  2. robotcode/language_server/common/parts/diagnostics.py +50 -17
  3. robotcode/language_server/common/parts/documents.py +34 -31
  4. robotcode/language_server/common/parts/workspace.py +57 -55
  5. robotcode/language_server/common/protocol.py +3 -1
  6. robotcode/language_server/robotframework/configuration.py +3 -0
  7. robotcode/language_server/robotframework/parts/code_action_documentation.py +440 -129
  8. robotcode/language_server/robotframework/parts/code_action_helper_mixin.py +17 -2
  9. robotcode/language_server/robotframework/parts/code_action_quick_fixes.py +6 -25
  10. robotcode/language_server/robotframework/parts/code_action_refactor.py +8 -6
  11. robotcode/language_server/robotframework/parts/code_lens.py +3 -4
  12. robotcode/language_server/robotframework/parts/completion.py +193 -21
  13. robotcode/language_server/robotframework/parts/debugging_utils.py +75 -6
  14. robotcode/language_server/robotframework/parts/diagnostics.py +15 -3
  15. robotcode/language_server/robotframework/parts/documents_cache.py +5 -1
  16. robotcode/language_server/robotframework/parts/hover.py +103 -21
  17. robotcode/language_server/robotframework/parts/http_server.py +17 -7
  18. robotcode/language_server/robotframework/parts/inline_value.py +69 -7
  19. robotcode/language_server/robotframework/parts/keywords_treeview.py +151 -53
  20. robotcode/language_server/robotframework/parts/model_variables.py +331 -0
  21. robotcode/language_server/robotframework/parts/references.py +47 -31
  22. robotcode/language_server/robotframework/parts/rename.py +1 -2
  23. robotcode/language_server/robotframework/parts/robot_workspace.py +229 -25
  24. robotcode/language_server/robotframework/parts/selection_range.py +26 -13
  25. robotcode/language_server/robotframework/parts/semantic_tokens.py +414 -113
  26. robotcode/language_server/robotframework/parts/signature_help.py +162 -70
  27. robotcode/language_server/robotframework/protocol.py +1 -0
  28. {robotcode_language_server-2.6.2.dist-info → robotcode_language_server-2.8.0.dev548.dist-info}/METADATA +7 -6
  29. {robotcode_language_server-2.6.2.dist-info → robotcode_language_server-2.8.0.dev548.dist-info}/RECORD +31 -30
  30. {robotcode_language_server-2.6.2.dist-info → robotcode_language_server-2.8.0.dev548.dist-info}/WHEEL +1 -1
  31. {robotcode_language_server-2.6.2.dist-info → robotcode_language_server-2.8.0.dev548.dist-info}/entry_points.txt +0 -0
@@ -2,9 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  import ast
4
4
  from dataclasses import dataclass
5
- from typing import List, Optional, Tuple
5
+ from typing import List, Optional, Tuple, Union
6
6
 
7
- from robotcode.core.lsp.types import DocumentUri, Position, Range
7
+ from robotcode.core.lsp.types import CodeActionContext, CodeActionKind, DocumentUri, Position, Range
8
8
  from robotcode.core.text_document import TextDocument
9
9
  from robotcode.robot.diagnostics.namespace import Namespace
10
10
  from robotcode.robot.utils.ast import range_from_node
@@ -13,6 +13,21 @@ from robotcode.robot.utils.visitor import Visitor
13
13
  SHOW_DOCUMENT_SELECT_AND_RENAME_COMMAND = "_robotcode.codeActionShowDocumentSelectAndRename"
14
14
 
15
15
 
16
+ def code_action_kind_requested(context: CodeActionContext, kind: Union[CodeActionKind, str]) -> bool:
17
+ """Whether a code action request with `context` asks for code actions of `kind`.
18
+
19
+ Without `only` it asks for every kind. With `only` it asks for the listed kinds and the kinds below them, since
20
+ the LSP defines kinds hierarchically: `refactor` also takes `refactor.extract.function`.
21
+ """
22
+ if not context.only:
23
+ return True
24
+ value = kind.value if isinstance(kind, CodeActionKind) else kind
25
+ return any(
26
+ value == requested or value.startswith(f"{requested}.")
27
+ for requested in (o.value if isinstance(o, CodeActionKind) else o for o in context.only)
28
+ )
29
+
30
+
16
31
  @dataclass
17
32
  class CodeActionDataBase:
18
33
  type: str
@@ -38,7 +38,6 @@ from robotcode.core.lsp.types import (
38
38
  CodeActionContext,
39
39
  CodeActionDisabledType,
40
40
  CodeActionKind,
41
- CodeActionTriggerKind,
42
41
  Command,
43
42
  OptionalVersionedTextDocumentIdentifier,
44
43
  Position,
@@ -73,6 +72,7 @@ from .code_action_helper_mixin import (
73
72
  CodeActionDataBase,
74
73
  CodeActionHelperMixin,
75
74
  FindSectionsVisitor,
75
+ code_action_kind_requested,
76
76
  )
77
77
  from .protocol_part import RobotLanguageServerProtocolPart
78
78
 
@@ -177,10 +177,7 @@ class RobotCodeActionQuickFixesProtocolPart(RobotLanguageServerProtocolPart, Mod
177
177
  ) -> Optional[List[Union[Command, CodeAction]]]:
178
178
  result: List[Union[Command, CodeAction]] = []
179
179
 
180
- if (context.only and CodeActionKind.QUICK_FIX in context.only) or context.trigger_kind in [
181
- CodeActionTriggerKind.INVOKED,
182
- CodeActionTriggerKind.AUTOMATIC,
183
- ]:
180
+ if code_action_kind_requested(context, CodeActionKind.QUICK_FIX):
184
181
  namespace = self.parent.documents_cache.get_namespace(document)
185
182
 
186
183
  for diagnostic in (
@@ -388,14 +385,7 @@ class RobotCodeActionQuickFixesProtocolPart(RobotLanguageServerProtocolPart, Mod
388
385
  if (
389
386
  range.start.line == range.end.line
390
387
  and range.start.character <= range.end.character
391
- and (
392
- (context.only and CodeActionKind.QUICK_FIX in context.only)
393
- or context.trigger_kind
394
- in [
395
- CodeActionTriggerKind.INVOKED,
396
- CodeActionTriggerKind.AUTOMATIC,
397
- ]
398
- )
388
+ and code_action_kind_requested(context, CodeActionKind.QUICK_FIX)
399
389
  ):
400
390
  all_diagnostics = [d for d in context.diagnostics if d.source and d.source.startswith("robotcode.")]
401
391
  if all_diagnostics:
@@ -471,10 +461,7 @@ class RobotCodeActionQuickFixesProtocolPart(RobotLanguageServerProtocolPart, Mod
471
461
  ) -> Optional[List[Union[Command, CodeAction]]]:
472
462
  result: List[Union[Command, CodeAction]] = []
473
463
 
474
- if (context.only and CodeActionKind.QUICK_FIX in context.only) or context.trigger_kind in [
475
- CodeActionTriggerKind.INVOKED,
476
- CodeActionTriggerKind.AUTOMATIC,
477
- ]:
464
+ if code_action_kind_requested(context, CodeActionKind.QUICK_FIX):
478
465
  for diagnostic in (
479
466
  d
480
467
  for d in context.diagnostics
@@ -587,10 +574,7 @@ class RobotCodeActionQuickFixesProtocolPart(RobotLanguageServerProtocolPart, Mod
587
574
  ) -> Optional[List[Union[Command, CodeAction]]]:
588
575
  result: List[Union[Command, CodeAction]] = []
589
576
 
590
- if (context.only and CodeActionKind.QUICK_FIX in context.only) or context.trigger_kind in [
591
- CodeActionTriggerKind.INVOKED,
592
- CodeActionTriggerKind.AUTOMATIC,
593
- ]:
577
+ if code_action_kind_requested(context, CodeActionKind.QUICK_FIX):
594
578
  for diagnostic in (
595
579
  d
596
580
  for d in context.diagnostics
@@ -737,10 +721,7 @@ class RobotCodeActionQuickFixesProtocolPart(RobotLanguageServerProtocolPart, Mod
737
721
  ) -> Optional[List[Union[Command, CodeAction]]]:
738
722
  result: List[Union[Command, CodeAction]] = []
739
723
 
740
- if (context.only and CodeActionKind.QUICK_FIX in context.only) or context.trigger_kind in [
741
- CodeActionTriggerKind.INVOKED,
742
- CodeActionTriggerKind.AUTOMATIC,
743
- ]:
724
+ if code_action_kind_requested(context, CodeActionKind.QUICK_FIX):
744
725
  for diagnostic in (
745
726
  d
746
727
  for d in context.diagnostics
@@ -13,7 +13,6 @@ from robotcode.core.lsp.types import (
13
13
  CodeActionContext,
14
14
  CodeActionDisabledType,
15
15
  CodeActionKind,
16
- CodeActionTriggerKind,
17
16
  Command,
18
17
  OptionalVersionedTextDocumentIdentifier,
19
18
  Position,
@@ -41,6 +40,7 @@ from .code_action_helper_mixin import (
41
40
  SHOW_DOCUMENT_SELECT_AND_RENAME_COMMAND,
42
41
  CodeActionDataBase,
43
42
  CodeActionHelperMixin,
43
+ code_action_kind_requested,
44
44
  )
45
45
  from .protocol_part import RobotLanguageServerProtocolPart
46
46
 
@@ -111,6 +111,7 @@ class RobotCodeActionRefactorProtocolPart(RobotLanguageServerProtocolPart, CodeA
111
111
  @code_action_kinds(
112
112
  [
113
113
  CODE_ACTION_KIND_REFACTOR_EXTRACT_FUNCTION,
114
+ CODE_ACTION_KIND_REFACTOR_EXTRACT_VARIABLE,
114
115
  CODE_ACTION_KIND_SURROUND_WITH,
115
116
  ]
116
117
  )
@@ -259,7 +260,7 @@ class RobotCodeActionRefactorProtocolPart(RobotLanguageServerProtocolPart, CodeA
259
260
  ) -> Optional[List[Union[Command, CodeAction]]]:
260
261
  from robot.parsing.model.blocks import Keyword, TestCase
261
262
 
262
- if range.start == range.end:
263
+ if range.start == range.end or not code_action_kind_requested(context, CODE_ACTION_KIND_SURROUND_WITH):
263
264
  return None
264
265
 
265
266
  model = self.parent.documents_cache.get_model(document)
@@ -491,9 +492,8 @@ class RobotCodeActionRefactorProtocolPart(RobotLanguageServerProtocolPart, CodeA
491
492
  def code_action_assign_result_to_variable(
492
493
  self, document: TextDocument, range: Range, context: CodeActionContext
493
494
  ) -> Optional[List[Union[Command, CodeAction]]]:
494
- if range.start.line != range.end.line or not (
495
- (context.only and CodeActionKind.REFACTOR_EXTRACT in context.only)
496
- or context.trigger_kind in [CodeActionTriggerKind.INVOKED, CodeActionTriggerKind.AUTOMATIC]
495
+ if range.start.line != range.end.line or not code_action_kind_requested(
496
+ context, CODE_ACTION_KIND_REFACTOR_EXTRACT_VARIABLE
497
497
  ):
498
498
  return None
499
499
 
@@ -575,7 +575,9 @@ class RobotCodeActionRefactorProtocolPart(RobotLanguageServerProtocolPart, CodeA
575
575
  ) -> Optional[List[Union[Command, CodeAction]]]:
576
576
  from robot.parsing.model.blocks import Keyword, TestCase
577
577
 
578
- if range.start == range.end:
578
+ if range.start == range.end or not code_action_kind_requested(
579
+ context, CODE_ACTION_KIND_REFACTOR_EXTRACT_FUNCTION
580
+ ):
579
581
  return None
580
582
 
581
583
  model = self.parent.documents_cache.get_model(document)
@@ -8,7 +8,6 @@ from robotcode.core.text_document import TextDocument
8
8
  from robotcode.core.utils.logging import LoggingDescriptor
9
9
  from robotcode.language_server.robotframework.configuration import AnalysisConfig
10
10
  from robotcode.robot.diagnostics.library_doc import KeywordDoc
11
- from robotcode.robot.diagnostics.model_helper import ModelHelper
12
11
  from robotcode.robot.utils.ast import range_from_token
13
12
  from robotcode.robot.utils.visitor import Visitor
14
13
 
@@ -18,7 +17,7 @@ if TYPE_CHECKING:
18
17
  from ..protocol import RobotLanguageServerProtocol
19
18
 
20
19
 
21
- class RobotCodeLensProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
20
+ class RobotCodeLensProtocolPart(RobotLanguageServerProtocolPart):
22
21
  _logger = LoggingDescriptor()
23
22
 
24
23
  def __init__(self, parent: "RobotLanguageServerProtocol") -> None:
@@ -73,7 +72,7 @@ class RobotCodeLensProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
73
72
  line = code_lens.data["line"]
74
73
 
75
74
  if self.parent.diagnostics.workspace_loaded_event.is_set():
76
- kw_doc = self.get_keyword_definition_at_line(namespace.library_doc, line)
75
+ kw_doc = next((k for k in namespace.library_doc.keywords.keywords if k.line_no == line), None)
77
76
 
78
77
  if kw_doc is not None and not kw_doc.is_error_handler:
79
78
  if not self.parent.robot_references.has_cached_keyword_references(
@@ -111,7 +110,7 @@ class RobotCodeLensProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
111
110
  document, kw_doc, include_declaration=False
112
111
  )
113
112
  code_lens.command = Command(
114
- f"{len(references)} references",
113
+ f"{len(references)} {'reference' if len(references) == 1 else 'references'}",
115
114
  "editor.action.showReferences",
116
115
  [str(document.uri), code_lens.range.start, references],
117
116
  )
@@ -52,6 +52,7 @@ from robotcode.core.lsp.types import (
52
52
  CompletionContext,
53
53
  CompletionItem,
54
54
  CompletionItemKind,
55
+ CompletionItemLabelDetails,
55
56
  CompletionList,
56
57
  InsertTextFormat,
57
58
  MarkupContent,
@@ -62,12 +63,15 @@ from robotcode.core.lsp.types import (
62
63
  )
63
64
  from robotcode.core.text_document import TextDocument
64
65
  from robotcode.core.utils.logging import LoggingDescriptor
66
+ from robotcode.robot.diagnostics.diagnostic_rules import private_keyword_call_message
65
67
  from robotcode.robot.diagnostics.entities import VariableDefinitionType
66
68
  from robotcode.robot.diagnostics.library_doc import (
69
+ ArgumentInfo,
67
70
  CompleteResultKind,
68
71
  KeywordArgumentKind,
69
72
  KeywordDoc,
70
73
  KeywordMatcher,
74
+ LibraryDoc,
71
75
  )
72
76
  from robotcode.robot.diagnostics.model_helper import ModelHelper
73
77
  from robotcode.robot.diagnostics.namespace import DocumentType, Namespace
@@ -80,9 +84,11 @@ from robotcode.robot.utils.ast import (
80
84
  whitespace_at_begin_of_token,
81
85
  whitespace_from_begin_of_token,
82
86
  )
87
+ from robotcode.robot.utils.markdown_docs import LinkResolver
83
88
 
84
89
  from ...common.decorators import trigger_characters
85
90
  from ..configuration import CompletionConfig
91
+ from .code_action_documentation import DocumentationTarget, keyword_reference_targets, link_documentation
86
92
  from .protocol_part import RobotLanguageServerProtocolPart
87
93
 
88
94
  if RF_VERSION >= (6, 1):
@@ -94,6 +100,7 @@ else:
94
100
 
95
101
  if TYPE_CHECKING:
96
102
  from ..protocol import RobotLanguageServerProtocol
103
+ from .code_action_documentation import RobotCodeActionDocumentationProtocolPart
97
104
 
98
105
  if RF_VERSION < (7, 0):
99
106
  from robot.variables.search import VariableIterator
@@ -319,6 +326,14 @@ class CompletionItemImportData(CompletionItemData):
319
326
  id: str
320
327
 
321
328
 
329
+ # a file of a Variables import is resolved like a library, but has no page in the Documentation Viewer
330
+ VARIABLES_IMPORT = "VARIABLES"
331
+
332
+
333
+ class CompletionItemVariablesImportData(CompletionItemData):
334
+ import_type: str
335
+
336
+
322
337
  class CompletionCollector(ModelHelper):
323
338
  _logger = LoggingDescriptor()
324
339
 
@@ -409,18 +424,25 @@ class CompletionCollector(ModelHelper):
409
424
  ]:
410
425
  if (lib_id := data.get("id", None)) is not None:
411
426
  try:
412
- lib_doc = next(
427
+ lib_entry = next(
413
428
  (
414
- ld.library_doc
429
+ ld
415
430
  for ld in self.namespace.libraries.values()
416
431
  if str(id(ld.library_doc)) == lib_id
417
432
  ),
418
433
  None,
419
434
  )
420
- if lib_doc is not None:
435
+ if lib_entry is not None:
436
+ entry = lib_entry
421
437
  completion_item.documentation = MarkupContent(
422
438
  kind=MarkupKind.MARKDOWN,
423
- value=lib_doc.to_markdown(False),
439
+ value=self._documentation(
440
+ lambda r: entry.library_doc.to_markdown(False, link_resolver=r),
441
+ lambda: self._code_action_documentation.entry_target(
442
+ entry, True, document, self.namespace
443
+ ),
444
+ entry.library_doc,
445
+ ),
424
446
  )
425
447
 
426
448
  except (
@@ -444,9 +466,22 @@ class CompletionCollector(ModelHelper):
444
466
  )
445
467
 
446
468
  if lib_doc is not None:
469
+ library_doc = lib_doc
470
+ library_name = name
471
+ is_variables = cast(Dict[str, Any], data).get("import_type") == VARIABLES_IMPORT
447
472
  completion_item.documentation = MarkupContent(
448
473
  kind=MarkupKind.MARKDOWN,
449
- value=lib_doc.to_markdown(False),
474
+ value=self._documentation(
475
+ lambda r: library_doc.to_markdown(False, link_resolver=r),
476
+ lambda: (
477
+ None
478
+ if is_variables
479
+ else self._code_action_documentation.import_target(
480
+ library_name, (), True, document, self.namespace
481
+ )
482
+ ),
483
+ library_doc,
484
+ ),
450
485
  )
451
486
 
452
487
  except (SystemExit, KeyboardInterrupt):
@@ -457,19 +492,26 @@ class CompletionCollector(ModelHelper):
457
492
  elif comp_type == CompleteResultKind.RESOURCE.name:
458
493
  if (res_id := data.get("id", None)) is not None:
459
494
  try:
460
- lib_doc = next(
495
+ res_entry = next(
461
496
  (
462
- ld.library_doc
497
+ ld
463
498
  for ld in self.namespace.resources.values()
464
499
  if str(id(ld.library_doc)) == res_id
465
500
  ),
466
501
  None,
467
502
  )
468
503
 
469
- if lib_doc is not None:
504
+ if res_entry is not None:
505
+ resource_entry = res_entry
470
506
  completion_item.documentation = MarkupContent(
471
507
  kind=MarkupKind.MARKDOWN,
472
- value=lib_doc.to_markdown(False),
508
+ value=self._documentation(
509
+ lambda r: resource_entry.library_doc.to_markdown(False, link_resolver=r),
510
+ lambda: self._code_action_documentation.entry_target(
511
+ resource_entry, False, document, self.namespace
512
+ ),
513
+ resource_entry.library_doc,
514
+ ),
473
515
  )
474
516
 
475
517
  except (
@@ -493,9 +535,17 @@ class CompletionCollector(ModelHelper):
493
535
  )
494
536
 
495
537
  if lib_doc is not None:
538
+ resource_doc = lib_doc
539
+ resource_name = name
496
540
  completion_item.documentation = MarkupContent(
497
541
  kind=MarkupKind.MARKDOWN,
498
- value=lib_doc.to_markdown(False),
542
+ value=self._documentation(
543
+ lambda r: resource_doc.to_markdown(False, link_resolver=r),
544
+ lambda: self._code_action_documentation.import_target(
545
+ resource_name, (), False, document, self.namespace
546
+ ),
547
+ resource_doc,
548
+ ),
499
549
  )
500
550
 
501
551
  except (SystemExit, KeyboardInterrupt):
@@ -512,9 +562,19 @@ class CompletionCollector(ModelHelper):
512
562
  )
513
563
 
514
564
  if kw_doc is not None:
565
+ keyword_doc = kw_doc
515
566
  completion_item.documentation = MarkupContent(
516
567
  kind=MarkupKind.MARKDOWN,
517
- value=kw_doc.to_markdown(),
568
+ value=self._documentation(
569
+ lambda r: keyword_doc.to_markdown(
570
+ link_resolver=r,
571
+ reference_targets=keyword_reference_targets(keyword_doc, r is not None),
572
+ ),
573
+ lambda: self._code_action_documentation.keyword_target(
574
+ keyword_doc, None, document, self.namespace
575
+ ),
576
+ keyword_doc.parent,
577
+ ),
518
578
  )
519
579
 
520
580
  except (
@@ -532,6 +592,37 @@ class CompletionCollector(ModelHelper):
532
592
 
533
593
  return completion_item
534
594
 
595
+ @property
596
+ def _links(self) -> bool:
597
+ return self.parent.parent.robot_initialization_options.documentation_viewer_links
598
+
599
+ @property
600
+ def _code_action_documentation(self) -> "RobotCodeActionDocumentationProtocolPart":
601
+ return self.parent.parent.robot_code_action_documentation
602
+
603
+ def _documentation(
604
+ self,
605
+ render: Callable[[Optional[LinkResolver]], str],
606
+ target: Callable[[], Optional[DocumentationTarget]],
607
+ library_doc: Optional[LibraryDoc],
608
+ *,
609
+ heading: bool = True,
610
+ ) -> str:
611
+ """The documentation of an item; with links, linked into the Documentation Viewer at the page of `target`."""
612
+ if not self._links:
613
+ return render(None)
614
+ documentation_target = target()
615
+ link_resolver = (
616
+ self._code_action_documentation.link_resolver(
617
+ self.document, self.namespace, documentation_target, library_doc
618
+ )
619
+ if documentation_target is not None and library_doc is not None
620
+ else None
621
+ )
622
+ return link_documentation(
623
+ render, documentation_target, library_doc, heading=heading, link_resolver=link_resolver
624
+ )
625
+
535
626
  def create_headers_completion_items(self, range: Optional[Range]) -> List[CompletionItem]:
536
627
  doc_type = self.parent.parent.documents_cache.get_document_type(self.document)
537
628
 
@@ -810,6 +901,24 @@ class CompletionCollector(ModelHelper):
810
901
 
811
902
  return result
812
903
 
904
+ def _is_foreign_private(self, kw: KeywordDoc) -> bool:
905
+ return private_keyword_call_message(kw, self.namespace.source) is not None
906
+
907
+ def _is_hidden(self, kw: KeywordDoc, private: bool) -> bool:
908
+ return (private and self.config.hide_private_keywords) or (
909
+ kw.is_deprecated and self.config.hide_deprecated_keywords
910
+ )
911
+
912
+ @staticmethod
913
+ def _private_label_details(private: bool) -> Optional[CompletionItemLabelDetails]:
914
+ return CompletionItemLabelDetails(description="private") if private else None
915
+
916
+ @staticmethod
917
+ def _keyword_sort_text(prefix: str, kw: KeywordDoc, private: bool) -> str:
918
+ """Other keywords first, then deprecated ones, then the shown private keywords of other files."""
919
+ rank = 2 if private else 1 if kw.is_deprecated else 0
920
+ return f"{prefix}{rank}_{kw.name}"
921
+
813
922
  def create_keyword_completion_items(
814
923
  self,
815
924
  token: Optional[Token],
@@ -889,13 +998,18 @@ class CompletionCollector(ModelHelper):
889
998
  for kw in libraries[namespace_name].library_doc.keywords.values():
890
999
  if kw.is_error_handler:
891
1000
  continue
1001
+ private = self._is_foreign_private(kw)
1002
+ if self._is_hidden(kw, private):
1003
+ continue
892
1004
  result.append(
893
1005
  CompletionItem(
894
1006
  label=kw.name,
1007
+ label_details=self._private_label_details(private),
895
1008
  kind=CompletionItemKind.FUNCTION,
896
1009
  detail=f"{CompleteResultKind.KEYWORD.value} "
897
1010
  f"{f'({kw.libname})' if kw.libname is not None else ''}",
898
- sort_text=f"019_{kw.name}",
1011
+ deprecated=kw.is_deprecated,
1012
+ sort_text=self._keyword_sort_text("019_", kw, private),
899
1013
  insert_text_format=(
900
1014
  InsertTextFormat.PLAIN_TEXT if not kw.is_embedded else InsertTextFormat.SNIPPET
901
1015
  ),
@@ -935,14 +1049,19 @@ class CompletionCollector(ModelHelper):
935
1049
  for kw in res.library_doc.keywords.values():
936
1050
  if kw.is_error_handler:
937
1051
  continue
1052
+ private = self._is_foreign_private(kw)
1053
+ if self._is_hidden(kw, private):
1054
+ continue
938
1055
 
939
1056
  result.append(
940
1057
  CompletionItem(
941
1058
  label=kw.name,
1059
+ label_details=self._private_label_details(private),
942
1060
  kind=CompletionItemKind.FUNCTION,
943
1061
  detail=f"{CompleteResultKind.KEYWORD.value} "
944
1062
  f"{f'({kw.libname})' if kw.libname is not None else ''}",
945
- sort_text=f"019_{kw.name}",
1063
+ deprecated=kw.is_deprecated,
1064
+ sort_text=self._keyword_sort_text("019_", kw, private),
946
1065
  insert_text_format=(
947
1066
  InsertTextFormat.PLAIN_TEXT
948
1067
  if not kw.is_embedded
@@ -985,14 +1104,18 @@ class CompletionCollector(ModelHelper):
985
1104
  and not kw.matcher.normalized_name.startswith(namespace_matcher.normalized_name)
986
1105
  ):
987
1106
  continue
1107
+ private = self._is_foreign_private(kw)
1108
+ if self._is_hidden(kw, private):
1109
+ continue
988
1110
 
989
1111
  result.append(
990
1112
  CompletionItem(
991
1113
  label=kw.name,
1114
+ label_details=self._private_label_details(private),
992
1115
  kind=CompletionItemKind.FUNCTION,
993
1116
  detail=f"{CompleteResultKind.KEYWORD.value} {f'({kw.libname})' if kw.libname is not None else ''}",
994
1117
  deprecated=kw.is_deprecated,
995
- sort_text=f"020_{kw.name}",
1118
+ sort_text=self._keyword_sort_text("020_", kw, private),
996
1119
  insert_text_format=InsertTextFormat.PLAIN_TEXT if not kw.is_embedded else InsertTextFormat.SNIPPET,
997
1120
  text_edit=TextEdit(
998
1121
  range=r,
@@ -1783,6 +1906,13 @@ class CompletionCollector(ModelHelper):
1783
1906
  kw_node.tokens[name_token_index:],
1784
1907
  token_at_position,
1785
1908
  position,
1909
+ (
1910
+ self._code_action_documentation.import_target(
1911
+ import_node.name, import_node.args, True, self.document, self.namespace, libdoc
1912
+ )
1913
+ if self._links
1914
+ else None
1915
+ ),
1786
1916
  )
1787
1917
 
1788
1918
  except (SystemExit, KeyboardInterrupt, CancelledError):
@@ -2056,10 +2186,11 @@ class CompletionCollector(ModelHelper):
2056
2186
  sort_text=f"030_{e.label}",
2057
2187
  insert_text_format=InsertTextFormat.PLAIN_TEXT,
2058
2188
  text_edit=TextEdit(range=r, new_text=e.label) if r is not None else None,
2059
- data=CompletionItemData(
2189
+ data=CompletionItemVariablesImportData(
2060
2190
  document_uri=str(self.document.uri),
2061
2191
  type=e.kind.name,
2062
2192
  name=((first_part) if first_part is not None else "") + e.label,
2193
+ import_type=VARIABLES_IMPORT,
2063
2194
  ),
2064
2195
  )
2065
2196
  for e in complete_list
@@ -2175,11 +2306,19 @@ class CompletionCollector(ModelHelper):
2175
2306
 
2176
2307
  keyword_token_index = kw_node.tokens.index(keyword_token)
2177
2308
 
2309
+ target = None
2310
+ if self._links:
2311
+ prefix_entry, _ = self.get_namespace_info_from_keyword_token(self.namespace, keyword_doc_and_token[1])
2312
+ target = self._code_action_documentation.keyword_target(
2313
+ keyword_doc, prefix_entry, self.document, self.namespace
2314
+ )
2315
+
2178
2316
  return self._complete_keyword_arguments_at_position(
2179
2317
  keyword_doc,
2180
2318
  kw_node.tokens[keyword_token_index:],
2181
2319
  token_at_position,
2182
2320
  position,
2321
+ target,
2183
2322
  )
2184
2323
 
2185
2324
  TRUE_STRINGS = {"TRUE", "YES", "ON", "1"}
@@ -2191,10 +2330,15 @@ class CompletionCollector(ModelHelper):
2191
2330
  tokens: Tuple[Token, ...],
2192
2331
  token_at_position: Token,
2193
2332
  position: Position,
2333
+ target: Optional[DocumentationTarget] = None,
2194
2334
  ) -> Optional[List[CompletionItem]]:
2195
2335
  if keyword_doc.is_any_run_keyword():
2196
2336
  return None
2197
2337
 
2338
+ def type_documentation(markdown: str) -> str:
2339
+ # the documentation of a type links no names, only its links to a place in the documentation
2340
+ return self._documentation(lambda _: markdown, lambda: target, None, heading=False)
2341
+
2198
2342
  (
2199
2343
  argument_index,
2200
2344
  kw_arguments,
@@ -2260,7 +2404,7 @@ class CompletionCollector(ModelHelper):
2260
2404
  or has_name
2261
2405
  )
2262
2406
  ):
2263
- type_infos = keyword_doc.parent.get_types(kw_arguments[argument_index].types)
2407
+ type_infos = keyword_doc.parent.get_types_for_argument(kw_arguments[argument_index])
2264
2408
  for i, type_info in enumerate(type_infos):
2265
2409
  if type_info.name == "boolean":
2266
2410
  if RF_VERSION >= (6, 0) and self.namespace.languages:
@@ -2287,7 +2431,7 @@ class CompletionCollector(ModelHelper):
2287
2431
  for i, b_snippet in enumerate(bool_snippets):
2288
2432
  if b_snippet[0]:
2289
2433
  cache_name = f"BOOL{id(type_info)}_{i}"
2290
- self.parent.doc_cache[cache_name] = type_info.to_markdown()
2434
+ self.parent.doc_cache[cache_name] = type_documentation(type_info.to_markdown())
2291
2435
  result.append(
2292
2436
  CompletionItem(
2293
2437
  label=b_snippet[0],
@@ -2312,7 +2456,9 @@ class CompletionCollector(ModelHelper):
2312
2456
  label="None",
2313
2457
  kind=CompletionItemKind.CONSTANT,
2314
2458
  detail=f"{type_info.name}",
2315
- documentation=MarkupContent(MarkupKind.MARKDOWN, type_info.to_markdown()),
2459
+ documentation=MarkupContent(
2460
+ MarkupKind.MARKDOWN, type_documentation(type_info.to_markdown())
2461
+ ),
2316
2462
  sort_text="50_000_None",
2317
2463
  insert_text_format=InsertTextFormat.PLAIN_TEXT,
2318
2464
  text_edit=TextEdit(range=completion_range, new_text="None"),
@@ -2323,7 +2469,9 @@ class CompletionCollector(ModelHelper):
2323
2469
  label="${None}",
2324
2470
  kind=CompletionItemKind.VARIABLE,
2325
2471
  detail=f"{type_info.name}",
2326
- documentation=MarkupContent(MarkupKind.MARKDOWN, type_info.to_markdown()),
2472
+ documentation=MarkupContent(
2473
+ MarkupKind.MARKDOWN, type_documentation(type_info.to_markdown())
2474
+ ),
2327
2475
  sort_text="50_001_None",
2328
2476
  insert_text_format=InsertTextFormat.PLAIN_TEXT,
2329
2477
  text_edit=TextEdit(range=completion_range, new_text="${None}"),
@@ -2332,7 +2480,7 @@ class CompletionCollector(ModelHelper):
2332
2480
  if type_info.members:
2333
2481
  for member_index, member in enumerate(type_info.members):
2334
2482
  cache_name = f"TYPE_MEMBER{id(type_info)}_{member_index}"
2335
- self.parent.doc_cache[cache_name] = (
2483
+ self.parent.doc_cache[cache_name] = type_documentation(
2336
2484
  f"```python\n{member.name} = {member.value}\n```"
2337
2485
  "\n\n---"
2338
2486
  f"\n{type_info.to_markdown(only_doc=True)}"
@@ -2375,7 +2523,7 @@ class CompletionCollector(ModelHelper):
2375
2523
  ]
2376
2524
  for i, snippet in enumerate(snippets):
2377
2525
  cache_name = f"TYPE_ITEMS{id(type_info)}_{i}"
2378
- self.parent.doc_cache[cache_name] = type_info.to_markdown()
2526
+ self.parent.doc_cache[cache_name] = type_documentation(type_info.to_markdown())
2379
2527
  if snippet:
2380
2528
  result.append(
2381
2529
  CompletionItem(
@@ -2449,6 +2597,29 @@ class CompletionCollector(ModelHelper):
2449
2597
  - 1
2450
2598
  )
2451
2599
 
2600
+ # once for all arguments of the keyword
2601
+ targets = keyword_reference_targets(keyword_doc, self._links)
2602
+
2603
+ def documentation_data(argument: ArgumentInfo) -> Optional[CompletionItemData]:
2604
+ # the description of the argument and the documentation of its types, sent when the item is resolved
2605
+ doc = self._documentation(
2606
+ lambda r: (
2607
+ keyword_doc.argument_to_markdown(argument, link_resolver=r, reference_targets=targets) or ""
2608
+ ),
2609
+ lambda: target,
2610
+ keyword_doc.parent,
2611
+ heading=False,
2612
+ )
2613
+ if not doc:
2614
+ return None
2615
+ cache_name = f"ARG{id(keyword_doc)}_{argument.name}"
2616
+ self.parent.doc_cache[cache_name] = doc
2617
+ return CompletionItemData(
2618
+ document_uri=str(self.document.uri),
2619
+ type=CompleteResultKind.DOC_CACHE.name,
2620
+ name=cache_name,
2621
+ )
2622
+
2452
2623
  result += [
2453
2624
  CompletionItem(
2454
2625
  label=f"{e.signature(False)}=",
@@ -2460,6 +2631,7 @@ class CompletionCollector(ModelHelper):
2460
2631
  text_edit=TextEdit(range=completion_range, new_text=f"{e.name}="),
2461
2632
  command=Command("", "editor.action.triggerSuggest", []),
2462
2633
  preselect=True if i == preselected else None,
2634
+ data=documentation_data(e),
2463
2635
  )
2464
2636
  for i, e in enumerate(kw_arguments)
2465
2637
  if e.name not in known_names