robotcode-language-server 2.5.1__tar.gz → 2.6.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.
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/.gitignore +5 -1
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/PKG-INFO +5 -5
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/pyproject.toml +4 -4
- robotcode_language_server-2.6.0/src/robotcode/language_server/__version__.py +1 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/diagnostics.py +1 -1
- robotcode_language_server-2.6.0/src/robotcode/language_server/robotframework/parts/code_action_documentation.py +375 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/code_action_quick_fixes.py +171 -93
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/code_action_refactor.py +107 -67
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/diagnostics.py +2 -1
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/hover.py +1 -1
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/inlay_hint.py +230 -3
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/semantic_tokens.py +132 -0
- robotcode_language_server-2.6.0/src/robotcode/language_server/robotframework/parts/signature_help.py +744 -0
- robotcode_language_server-2.5.1/src/robotcode/language_server/__version__.py +0 -1
- robotcode_language_server-2.5.1/src/robotcode/language_server/robotframework/parts/code_action_documentation.py +0 -229
- robotcode_language_server-2.5.1/src/robotcode/language_server/robotframework/parts/signature_help.py +0 -348
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/README.md +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/__init__.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/cli.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/__init__.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/decorators.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/__init__.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/code_action.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/code_lens.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/commands.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/completion.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/declaration.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/definition.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/document_highlight.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/document_symbols.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/documents.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/folding_range.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/formatting.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/hover.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/implementation.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/inlay_hint.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/inline_value.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/linked_editing_ranges.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/protocol_part.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/references.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/rename.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/selection_range.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/semantic_tokens.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/signature_help.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/window.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/workspace.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/parts/workspace_symbols.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/protocol.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/common/server.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/hooks.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/py.typed +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/__init__.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/configuration.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/__init__.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/code_action_helper_mixin.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/code_lens.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/completion.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/debugging_utils.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/document_highlight.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/document_symbols.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/documents_cache.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/folding_range.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/formatting.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/goto.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/http_server.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/inline_value.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/keywords_treeview.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/project_info.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/protocol_part.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/references.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/rename.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/robocop_diagnostics.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/robocop_helper.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/robot_workspace.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/selection_range.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/parts/workspace_symbols.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/protocol.py +0 -0
- {robotcode_language_server-2.5.1 → robotcode_language_server-2.6.0}/src/robotcode/language_server/robotframework/server.py +0 -0
|
@@ -331,7 +331,7 @@ output.xml
|
|
|
331
331
|
bundled/libs
|
|
332
332
|
|
|
333
333
|
# robotframework
|
|
334
|
-
results/
|
|
334
|
+
/results/
|
|
335
335
|
|
|
336
336
|
# kilocode
|
|
337
337
|
.kilocode/
|
|
@@ -339,3 +339,7 @@ results/
|
|
|
339
339
|
# .agents
|
|
340
340
|
.agents/
|
|
341
341
|
skills-lock.json
|
|
342
|
+
.claude
|
|
343
|
+
|
|
344
|
+
# sarif files
|
|
345
|
+
/**/*.sarif.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotcode-language-server
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.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.10
|
|
28
|
-
Requires-Dist: robotcode
|
|
29
|
-
Requires-Dist: robotcode-
|
|
30
|
-
Requires-Dist: robotcode-
|
|
31
|
-
Requires-Dist: robotcode
|
|
28
|
+
Requires-Dist: robotcode-analyze==2.6.0
|
|
29
|
+
Requires-Dist: robotcode-jsonrpc2==2.6.0
|
|
30
|
+
Requires-Dist: robotcode-robot==2.6.0
|
|
31
|
+
Requires-Dist: robotcode==2.6.0
|
|
32
32
|
Requires-Dist: robotframework>=5.0.0
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
|
|
@@ -28,10 +28,10 @@ classifiers = [
|
|
|
28
28
|
]
|
|
29
29
|
dependencies = [
|
|
30
30
|
"robotframework>=5.0.0",
|
|
31
|
-
"robotcode-jsonrpc2",
|
|
32
|
-
"robotcode-robot",
|
|
33
|
-
"robotcode-analyze",
|
|
34
|
-
"robotcode",
|
|
31
|
+
"robotcode-jsonrpc2==2.6.0",
|
|
32
|
+
"robotcode-robot==2.6.0",
|
|
33
|
+
"robotcode-analyze==2.6.0",
|
|
34
|
+
"robotcode==2.6.0",
|
|
35
35
|
]
|
|
36
36
|
dynamic = ["version"]
|
|
37
37
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.6.0"
|
|
@@ -436,7 +436,7 @@ class DiagnosticsProtocolPart(LanguageServerProtocolPart):
|
|
|
436
436
|
documents_to_collect = [
|
|
437
437
|
doc
|
|
438
438
|
for doc in documents
|
|
439
|
-
if doc.opened_in_editor or self.get_diagnostics_mode(
|
|
439
|
+
if doc.opened_in_editor or self.get_diagnostics_mode(doc.uri) == DiagnosticsMode.WORKSPACE
|
|
440
440
|
]
|
|
441
441
|
|
|
442
442
|
with self._logger.measure_time(
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import urllib.parse
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union, cast
|
|
4
|
+
|
|
5
|
+
from robot.parsing.lexer.tokens import Token
|
|
6
|
+
|
|
7
|
+
from robotcode.core.language import language_id
|
|
8
|
+
from robotcode.core.lsp.types import (
|
|
9
|
+
CodeAction,
|
|
10
|
+
CodeActionContext,
|
|
11
|
+
CodeActionKind,
|
|
12
|
+
Command,
|
|
13
|
+
Position,
|
|
14
|
+
Range,
|
|
15
|
+
)
|
|
16
|
+
from robotcode.core.text_document import TextDocument
|
|
17
|
+
from robotcode.core.uri import Uri
|
|
18
|
+
from robotcode.core.utils.dataclasses import CamelSnakeMixin
|
|
19
|
+
from robotcode.core.utils.logging import LoggingDescriptor
|
|
20
|
+
from robotcode.jsonrpc2.protocol import rpc_method
|
|
21
|
+
from robotcode.robot.diagnostics.entities import LibraryEntry
|
|
22
|
+
from robotcode.robot.diagnostics.library_doc import KeywordDoc, resolve_robot_variables
|
|
23
|
+
from robotcode.robot.diagnostics.model_helper import ModelHelper
|
|
24
|
+
from robotcode.robot.diagnostics.namespace import Namespace
|
|
25
|
+
from robotcode.robot.diagnostics.semantic_analyzer.enums import ImportType, NodeKind, TokenKind
|
|
26
|
+
from robotcode.robot.diagnostics.semantic_analyzer.model import SemanticModel
|
|
27
|
+
from robotcode.robot.diagnostics.semantic_analyzer.nodes import (
|
|
28
|
+
DefinitionStatement,
|
|
29
|
+
ImportStatement,
|
|
30
|
+
KeywordCallStatement,
|
|
31
|
+
)
|
|
32
|
+
from robotcode.robot.utils.ast import get_node_at_position, range_from_token
|
|
33
|
+
|
|
34
|
+
from ...common.decorators import code_action_kinds
|
|
35
|
+
from .protocol_part import RobotLanguageServerProtocolPart
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from ..protocol import RobotLanguageServerProtocol
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(repr=False)
|
|
42
|
+
class ConvertUriParams(CamelSnakeMixin):
|
|
43
|
+
uri: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RobotCodeActionDocumentationProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
|
|
47
|
+
_logger = LoggingDescriptor()
|
|
48
|
+
|
|
49
|
+
def __init__(self, parent: "RobotLanguageServerProtocol") -> None:
|
|
50
|
+
super().__init__(parent)
|
|
51
|
+
self.parent.commands.register_all(self)
|
|
52
|
+
|
|
53
|
+
parent.code_action.collect.add(self.collect)
|
|
54
|
+
|
|
55
|
+
@language_id("robotframework")
|
|
56
|
+
@code_action_kinds([CodeActionKind.SOURCE])
|
|
57
|
+
@_logger.call
|
|
58
|
+
def collect(
|
|
59
|
+
self,
|
|
60
|
+
sender: Any,
|
|
61
|
+
document: TextDocument,
|
|
62
|
+
range: Range,
|
|
63
|
+
context: CodeActionContext,
|
|
64
|
+
) -> Optional[List[Union[Command, CodeAction]]]:
|
|
65
|
+
namespace = self.parent.documents_cache.get_namespace(document)
|
|
66
|
+
|
|
67
|
+
# Tier 2 model-based path — used when the experimental SemanticAnalyzer
|
|
68
|
+
# is enabled. Reads everything off the SemanticModel: statement kind
|
|
69
|
+
# via `model.statement_at()`, the pre-resolved `keyword_doc`, the
|
|
70
|
+
# `import_name`, and SemanticTokens for cursor-position checks. No
|
|
71
|
+
# `find_keyword`, no AST walk.
|
|
72
|
+
semantic_model = namespace.semantic_model
|
|
73
|
+
if semantic_model is not None:
|
|
74
|
+
return self._collect_from_model(document, range, context, namespace, semantic_model)
|
|
75
|
+
|
|
76
|
+
return self._collect_legacy(document, range, context, namespace)
|
|
77
|
+
|
|
78
|
+
def _collect_legacy(
|
|
79
|
+
self,
|
|
80
|
+
document: TextDocument,
|
|
81
|
+
range: Range,
|
|
82
|
+
context: CodeActionContext,
|
|
83
|
+
namespace: Namespace,
|
|
84
|
+
) -> Optional[List[Union[Command, CodeAction]]]:
|
|
85
|
+
from robot.parsing.lexer import Token as RobotToken
|
|
86
|
+
from robot.parsing.model.statements import (
|
|
87
|
+
Fixture,
|
|
88
|
+
KeywordCall,
|
|
89
|
+
KeywordName,
|
|
90
|
+
LibraryImport,
|
|
91
|
+
ResourceImport,
|
|
92
|
+
Template,
|
|
93
|
+
TestTemplate,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
model = self.parent.documents_cache.get_model(document)
|
|
97
|
+
node = get_node_at_position(model, range.start)
|
|
98
|
+
|
|
99
|
+
if context.only and isinstance(node, (LibraryImport, ResourceImport)):
|
|
100
|
+
if CodeActionKind.SOURCE.value in context.only and range in range_from_token(
|
|
101
|
+
node.get_token(RobotToken.NAME)
|
|
102
|
+
):
|
|
103
|
+
url = self.build_url(
|
|
104
|
+
node.name,
|
|
105
|
+
node.args if isinstance(node, LibraryImport) else (),
|
|
106
|
+
document,
|
|
107
|
+
namespace,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return [self.open_documentation_code_action(url)]
|
|
111
|
+
|
|
112
|
+
if isinstance(node, (KeywordCall, Fixture, TestTemplate, Template)):
|
|
113
|
+
# only source actions
|
|
114
|
+
|
|
115
|
+
result = self.get_keyworddoc_and_token_from_position(
|
|
116
|
+
(
|
|
117
|
+
node.value
|
|
118
|
+
if isinstance(node, (TestTemplate, Template))
|
|
119
|
+
else node.keyword
|
|
120
|
+
if isinstance(node, KeywordCall)
|
|
121
|
+
else node.name
|
|
122
|
+
),
|
|
123
|
+
cast(
|
|
124
|
+
Token,
|
|
125
|
+
node.get_token(RobotToken.KEYWORD if isinstance(node, KeywordCall) else RobotToken.NAME),
|
|
126
|
+
),
|
|
127
|
+
[cast(Token, t) for t in node.get_tokens(RobotToken.ARGUMENT)],
|
|
128
|
+
namespace,
|
|
129
|
+
range.start,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if range.start != range.end:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
if result is not None:
|
|
136
|
+
kw_doc, _ = result
|
|
137
|
+
|
|
138
|
+
if kw_doc is not None:
|
|
139
|
+
if context.only and CodeActionKind.SOURCE.value in context.only:
|
|
140
|
+
return self._build_keyword_action(kw_doc, document, namespace)
|
|
141
|
+
|
|
142
|
+
if isinstance(node, KeywordName):
|
|
143
|
+
name_token = node.get_token(RobotToken.KEYWORD_NAME)
|
|
144
|
+
if name_token is not None and range in range_from_token(name_token):
|
|
145
|
+
url = self.build_url(
|
|
146
|
+
str(document.uri.to_path().name),
|
|
147
|
+
(),
|
|
148
|
+
document,
|
|
149
|
+
namespace,
|
|
150
|
+
name_token.value,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return [self.open_documentation_code_action(url)]
|
|
154
|
+
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
# ------------------------------------------------------------------
|
|
158
|
+
# Tier 2 model-based collection
|
|
159
|
+
# ------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
def _collect_from_model(
|
|
162
|
+
self,
|
|
163
|
+
document: TextDocument,
|
|
164
|
+
range: Range,
|
|
165
|
+
context: CodeActionContext,
|
|
166
|
+
namespace: Namespace,
|
|
167
|
+
model: SemanticModel,
|
|
168
|
+
) -> Optional[List[Union[Command, CodeAction]]]:
|
|
169
|
+
"""Mirror legacy three-branch logic (import / keyword-call / keyword-def)
|
|
170
|
+
purely off the SemanticModel — no AST walks, no `find_keyword`.
|
|
171
|
+
|
|
172
|
+
Position checks use SemanticTokens; URL inputs read from pre-resolved
|
|
173
|
+
statement fields (`import_name`, `keyword_doc`, `name`).
|
|
174
|
+
"""
|
|
175
|
+
# SemanticModel uses 1-indexed lines; LSP positions are 0-indexed.
|
|
176
|
+
stmt = model.statement_at(range.start.line + 1)
|
|
177
|
+
if stmt is None:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
# Branch 1: Library / Resource import — gated on context.only at entry.
|
|
181
|
+
if (
|
|
182
|
+
context.only
|
|
183
|
+
and isinstance(stmt, ImportStatement)
|
|
184
|
+
and stmt.import_type in (ImportType.LIBRARY, ImportType.RESOURCE)
|
|
185
|
+
and CodeActionKind.SOURCE.value in context.only
|
|
186
|
+
):
|
|
187
|
+
return self._import_action_from_model(stmt, document, range, namespace)
|
|
188
|
+
|
|
189
|
+
# Branch 2: keyword call / fixture / template.
|
|
190
|
+
if isinstance(stmt, KeywordCallStatement):
|
|
191
|
+
if range.start != range.end:
|
|
192
|
+
return None
|
|
193
|
+
kw_doc = stmt.keyword_doc
|
|
194
|
+
if kw_doc is None:
|
|
195
|
+
return None
|
|
196
|
+
if not self._cursor_on_keyword_reference(range.start, stmt):
|
|
197
|
+
return None
|
|
198
|
+
if not (context.only and CodeActionKind.SOURCE.value in context.only):
|
|
199
|
+
return None
|
|
200
|
+
return self._build_keyword_action(kw_doc, document, namespace)
|
|
201
|
+
|
|
202
|
+
# Branch 3: keyword definition header — no context.only check
|
|
203
|
+
# (legacy doesn't gate this branch either).
|
|
204
|
+
if isinstance(stmt, DefinitionStatement) and stmt.kind is NodeKind.KEYWORD_DEF:
|
|
205
|
+
name_tok = next((t for t in stmt.tokens if t.kind is TokenKind.KEYWORD_NAME), None)
|
|
206
|
+
if name_tok is None or range not in name_tok.range:
|
|
207
|
+
return None
|
|
208
|
+
url = self.build_url(
|
|
209
|
+
str(document.uri.to_path().name),
|
|
210
|
+
(),
|
|
211
|
+
document,
|
|
212
|
+
namespace,
|
|
213
|
+
name_tok.value,
|
|
214
|
+
)
|
|
215
|
+
return [self.open_documentation_code_action(url)]
|
|
216
|
+
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
def _import_action_from_model(
|
|
220
|
+
self,
|
|
221
|
+
stmt: ImportStatement,
|
|
222
|
+
document: TextDocument,
|
|
223
|
+
range: Range,
|
|
224
|
+
namespace: Namespace,
|
|
225
|
+
) -> Optional[List[Union[Command, CodeAction]]]:
|
|
226
|
+
"""Library / Resource import branch built off SemanticTokens.
|
|
227
|
+
|
|
228
|
+
- The import path lives in the IMPORT_NAME token (cursor-position check).
|
|
229
|
+
- Library `args` are the ARGUMENT tokens BEFORE the optional WITH NAME
|
|
230
|
+
marker (CONTROL_FLOW); anything after is the alias and must be
|
|
231
|
+
excluded — matches RF's `LibraryImport.args` semantics.
|
|
232
|
+
- Resource imports never carry args (RF API returns ()).
|
|
233
|
+
"""
|
|
234
|
+
name_tok = next((t for t in stmt.tokens if t.kind is TokenKind.IMPORT_NAME), None)
|
|
235
|
+
if name_tok is None or range not in name_tok.range:
|
|
236
|
+
return None
|
|
237
|
+
|
|
238
|
+
if stmt.import_type is ImportType.LIBRARY:
|
|
239
|
+
arg_values: List[str] = []
|
|
240
|
+
for tok in stmt.tokens:
|
|
241
|
+
if tok.kind is TokenKind.CONTROL_FLOW:
|
|
242
|
+
break # WITH NAME — everything after is the alias
|
|
243
|
+
if tok.kind is TokenKind.ARGUMENT:
|
|
244
|
+
arg_values.append(tok.value)
|
|
245
|
+
args: Tuple[str, ...] = tuple(arg_values)
|
|
246
|
+
else:
|
|
247
|
+
args = ()
|
|
248
|
+
|
|
249
|
+
url = self.build_url(stmt.import_name or "", args, document, namespace)
|
|
250
|
+
return [self.open_documentation_code_action(url)]
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def _cursor_on_keyword_reference(pos: Position, stmt: KeywordCallStatement) -> bool:
|
|
254
|
+
"""Cursor is within the NAMESPACE / SEPARATOR / KEYWORD SemanticTokens
|
|
255
|
+
that make up the keyword reference (BDD prefix excluded). Mirrors
|
|
256
|
+
the legacy `position.is_in_range(range_from_token(keyword_token))`
|
|
257
|
+
after the BDD-prefix strip that
|
|
258
|
+
`get_keyworddoc_and_token_from_position` does.
|
|
259
|
+
"""
|
|
260
|
+
return any(
|
|
261
|
+
pos in t.range
|
|
262
|
+
for t in stmt.tokens
|
|
263
|
+
if t.kind in (TokenKind.NAMESPACE, TokenKind.SEPARATOR, TokenKind.KEYWORD)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def _build_keyword_action(
|
|
267
|
+
self,
|
|
268
|
+
kw_doc: KeywordDoc,
|
|
269
|
+
document: TextDocument,
|
|
270
|
+
namespace: Namespace,
|
|
271
|
+
) -> Optional[List[Union[Command, CodeAction]]]:
|
|
272
|
+
"""Resolve the LibraryEntry that owns `kw_doc` and build the
|
|
273
|
+
Open-Documentation action. Shared between legacy and model paths so
|
|
274
|
+
the URL construction stays identical."""
|
|
275
|
+
entry: Optional[LibraryEntry] = None
|
|
276
|
+
|
|
277
|
+
if kw_doc.libtype == "LIBRARY":
|
|
278
|
+
entry = next(
|
|
279
|
+
(v for v in namespace.libraries.values() if v.library_doc == kw_doc.parent),
|
|
280
|
+
None,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
elif kw_doc.libtype == "RESOURCE":
|
|
284
|
+
entry = next(
|
|
285
|
+
(v for v in namespace.resources.values() if v.library_doc == kw_doc.parent),
|
|
286
|
+
None,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
self_libdoc = namespace.library_doc
|
|
290
|
+
if entry is None and self_libdoc == kw_doc.parent:
|
|
291
|
+
entry = LibraryEntry(
|
|
292
|
+
self_libdoc.name,
|
|
293
|
+
str(document.uri.to_path().name),
|
|
294
|
+
self_libdoc,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
if entry is None:
|
|
298
|
+
return None
|
|
299
|
+
|
|
300
|
+
url = self.build_url(
|
|
301
|
+
entry.import_name,
|
|
302
|
+
entry.args,
|
|
303
|
+
document,
|
|
304
|
+
namespace,
|
|
305
|
+
kw_doc.name,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return [self.open_documentation_code_action(url)]
|
|
309
|
+
|
|
310
|
+
def open_documentation_code_action(self, url: str) -> CodeAction:
|
|
311
|
+
return CodeAction(
|
|
312
|
+
"Open Documentation",
|
|
313
|
+
kind=CodeActionKind.SOURCE,
|
|
314
|
+
command=Command("Open Documentation", "robotcode.showDocumentation", [url]),
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def build_url(
|
|
318
|
+
self,
|
|
319
|
+
name: str,
|
|
320
|
+
args: Tuple[Any, ...],
|
|
321
|
+
document: TextDocument,
|
|
322
|
+
namespace: Namespace,
|
|
323
|
+
target: Optional[str] = None,
|
|
324
|
+
) -> str:
|
|
325
|
+
base_dir = document.uri.to_path().parent
|
|
326
|
+
|
|
327
|
+
workspace_folder = self.parent.workspace.get_workspace_folder(document.uri)
|
|
328
|
+
if workspace_folder is not None:
|
|
329
|
+
try:
|
|
330
|
+
base_dir = base_dir.relative_to(workspace_folder.uri.to_path())
|
|
331
|
+
except ValueError:
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
robot_variables = resolve_robot_variables(
|
|
335
|
+
str(namespace.imports_manager.root_folder),
|
|
336
|
+
str(base_dir),
|
|
337
|
+
namespace.imports_manager.get_resolvable_command_line_variables(),
|
|
338
|
+
variables=namespace.get_resolvable_variables(),
|
|
339
|
+
)
|
|
340
|
+
try:
|
|
341
|
+
name = robot_variables.replace_string(name, ignore_errors=False)
|
|
342
|
+
|
|
343
|
+
args = tuple(robot_variables.replace_string(v, ignore_errors=False) for v in args)
|
|
344
|
+
|
|
345
|
+
except (SystemExit, KeyboardInterrupt):
|
|
346
|
+
raise
|
|
347
|
+
except BaseException:
|
|
348
|
+
pass
|
|
349
|
+
|
|
350
|
+
url_args = "::".join(args) if args else ""
|
|
351
|
+
|
|
352
|
+
base_url = f"http://localhost:{self.parent.http_server.port}"
|
|
353
|
+
params = urllib.parse.urlencode(
|
|
354
|
+
{
|
|
355
|
+
"name": name,
|
|
356
|
+
"args": url_args,
|
|
357
|
+
"basedir": str(base_dir),
|
|
358
|
+
"theme": "${theme}",
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
return f"{base_url}/?&{params}{f'#{target}' if target else ''}"
|
|
363
|
+
|
|
364
|
+
@rpc_method(name="robot/documentationServer/convertUri", param_type=ConvertUriParams, threaded=True)
|
|
365
|
+
def _convert_uri(self, uri: str, *args: Any, **kwargs: Any) -> Optional[str]:
|
|
366
|
+
real_uri = Uri(uri)
|
|
367
|
+
|
|
368
|
+
folder = self.parent.workspace.get_workspace_folder(real_uri)
|
|
369
|
+
|
|
370
|
+
if folder:
|
|
371
|
+
path = real_uri.to_path().relative_to(folder.uri.to_path())
|
|
372
|
+
|
|
373
|
+
return f"http://localhost:{self.parent.http_server.port}/{path.as_posix()}"
|
|
374
|
+
|
|
375
|
+
return None
|