Skip to content

Commit 2a4e728

Browse files
committed
refactor(language-server): pure SemanticModel for signature help and code action
Both LSP feature paths now read everything off the SemanticModel — no RF AST walks, no ModelHelper position helpers. SemanticToken carries a precomputed LSP Range so consumers can use native operators (`pos in tok.range`, `range in tok.range`, `tok.range.extend(...)`) instead of per-coordinate arithmetic. Output unchanged.
1 parent 0768c84 commit 2a4e728

3 files changed

Lines changed: 352 additions & 140 deletions

File tree

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

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
DefinitionStatement,
2929
ImportStatement,
3030
KeywordCallStatement,
31-
SemanticToken,
3231
)
3332
from robotcode.robot.utils.ast import get_node_at_position, range_from_token
3433

@@ -44,24 +43,6 @@ class ConvertUriParams(CamelSnakeMixin):
4443
uri: str
4544

4645

47-
def _range_in_semantic_token(rng: Range, tok: SemanticToken) -> bool:
48-
"""Return True iff both endpoints of `rng` lie within `tok`'s span.
49-
50-
LSP `Range` is 0-indexed, `SemanticToken.line` is 1-indexed; both
51-
`col_offset` and `character` are 0-indexed. Mirrors the legacy
52-
`range in range_from_token(token)` semantics, including the inclusive
53-
end (`<=`) so a cursor exactly at the end of the token still matches.
54-
"""
55-
line0 = tok.line - 1
56-
end_col = tok.col_offset + tok.length
57-
return (
58-
rng.start.line == line0
59-
and tok.col_offset <= rng.start.character <= end_col
60-
and rng.end.line == line0
61-
and tok.col_offset <= rng.end.character <= end_col
62-
)
63-
64-
6546
class RobotCodeActionDocumentationProtocolPart(RobotLanguageServerProtocolPart, ModelHelper):
6647
_logger = LoggingDescriptor()
6748

@@ -222,7 +203,7 @@ def _collect_from_model(
222203
# (legacy doesn't gate this branch either).
223204
if isinstance(stmt, DefinitionStatement) and stmt.kind is NodeKind.KEYWORD_DEF:
224205
name_tok = next((t for t in stmt.tokens if t.kind is TokenKind.KEYWORD_NAME), None)
225-
if name_tok is None or not _range_in_semantic_token(range, name_tok):
206+
if name_tok is None or range not in name_tok.range:
226207
return None
227208
url = self.build_url(
228209
str(document.uri.to_path().name),
@@ -251,7 +232,7 @@ def _import_action_from_model(
251232
- Resource imports never carry args (RF API returns ()).
252233
"""
253234
name_tok = next((t for t in stmt.tokens if t.kind is TokenKind.IMPORT_NAME), None)
254-
if name_tok is None or not _range_in_semantic_token(range, name_tok):
235+
if name_tok is None or range not in name_tok.range:
255236
return None
256237

257238
if stmt.import_type is ImportType.LIBRARY:
@@ -270,21 +251,17 @@ def _import_action_from_model(
270251

271252
@staticmethod
272253
def _cursor_on_keyword_reference(pos: Position, stmt: KeywordCallStatement) -> bool:
273-
"""Cursor is within the union of NAMESPACE + SEPARATOR + KEYWORD
274-
SemanticTokens — i.e. inside the keyword reference excluding any
275-
BDD prefix. Mirrors the legacy
276-
`position.is_in_range(range_from_token(keyword_token))` after the
277-
BDD-prefix strip that `get_keyworddoc_and_token_from_position` does.
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.
278259
"""
279-
relevant = [t for t in stmt.tokens if t.kind in (TokenKind.NAMESPACE, TokenKind.SEPARATOR, TokenKind.KEYWORD)]
280-
if not relevant:
281-
return False
282-
line0 = relevant[0].line - 1 # SemanticToken.line is 1-indexed
283-
if pos.line != line0:
284-
return False
285-
start_col = min(t.col_offset for t in relevant)
286-
end_col = max(t.col_offset + t.length for t in relevant)
287-
return start_col <= pos.character <= end_col
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+
)
288265

289266
def _build_keyword_action(
290267
self,

0 commit comments

Comments
 (0)