Skip to content

Commit 4b91778

Browse files
committed
fix: when the cursor is on an object lookup expression, return its type instead of the type of the key
1 parent fb90eb4 commit 4b91778

3 files changed

Lines changed: 94 additions & 0 deletions

File tree

.changeset/loose-dragons-sit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"rumble-lsp-wrapper": minor
3+
---
4+
5+
fix: when the cursor is on an object lookup expression, return its type instead of the type of the key

packages/rumble-lsp-wrapper/src/main/java/org/jsoniq/lsp/wrapper/handlers/TypeAtPosition.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.rumbledb.expressions.Expression;
1616
import org.rumbledb.expressions.Node;
1717
import org.rumbledb.expressions.module.MainModule;
18+
import org.rumbledb.expressions.postfix.ObjectLookupExpression;
1819

1920
public final class TypeAtPosition implements RequestHandler {
2021
public static final String REQUEST_TYPE = "type-at-position";
@@ -78,6 +79,11 @@ public Result findType(String query, URI documentUri, Position position) {
7879
}
7980

8081
private static Expression findExpression(Node root, Position position) {
82+
Candidate objectLookupKeyCandidate = findBestObjectLookupKeyCandidate(root, position, null);
83+
if (objectLookupKeyCandidate != null) {
84+
return objectLookupKeyCandidate.expression();
85+
}
86+
8187
Candidate bestEnding = findBestCandidate(root, position, true, null);
8288
if (bestEnding != null) {
8389
return bestEnding.expression();
@@ -87,6 +93,30 @@ private static Expression findExpression(Node root, Position position) {
8793
return bestContaining == null ? null : bestContaining.expression();
8894
}
8995

96+
private static Candidate findBestObjectLookupKeyCandidate(Node node, Position position, Candidate best) {
97+
if (node == null) {
98+
return best;
99+
}
100+
101+
if (node instanceof ObjectLookupExpression objectLookupExpression) {
102+
Expression lookupExpression = objectLookupExpression.getLookupExpression();
103+
Candidate lookupCandidate = lookupExpression == null ? null : Candidate.create(lookupExpression);
104+
Candidate objectLookupCandidate = Candidate.create(objectLookupExpression);
105+
boolean positionIsInsideLookupKey = lookupCandidate != null
106+
&& objectLookupCandidate != null
107+
&& lookupCandidate.matches(position, false);
108+
109+
if (positionIsInsideLookupKey) {
110+
best = Candidate.prefer(best, objectLookupCandidate, false);
111+
}
112+
}
113+
114+
for (Node child : node.getChildren()) {
115+
best = findBestObjectLookupKeyCandidate(child, position, best);
116+
}
117+
return best;
118+
}
119+
90120
private static Candidate findBestCandidate(
91121
Node node,
92122
Position position,

packages/rumble-lsp-wrapper/src/test/java/org/jsoniq/lsp/wrapper/TypeAtPositionTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,65 @@ void returnsNestedObjectLookupType() {
4848
assertEquals("xs:integer", result.sequenceType().itemType().fields().get("value").toString());
4949
}
5050

51+
@Test
52+
void returnsObjectLookupExpressionTypeWhenPositionIsInsideLookupKey() {
53+
String query = """
54+
declare variable $a := {
55+
"name": "ada",
56+
"details": {
57+
"tlf": 1233.2
58+
}
59+
};
60+
($a.details.tlf)
61+
""";
62+
int detailsOffset = query.indexOf("details.tlf") + "det".length();
63+
64+
TypeAtPosition.Result result = this.typeAtPosition.findType(
65+
query,
66+
positionAtOffset(query, detailsOffset)
67+
);
68+
69+
assertNotNull(result.sequenceType());
70+
assertEquals("object", result.sequenceType().itemType().kind());
71+
assertEquals("xs:decimal", result.sequenceType().itemType().fields().get("tlf").toString());
72+
assertEquals(
73+
new Range(
74+
positionAtOffset(query, query.indexOf("$a.details")),
75+
positionAtOffset(query, query.indexOf("$a.details") + "$a.details".length())
76+
),
77+
result.range()
78+
);
79+
}
80+
81+
@Test
82+
void returnsNestedObjectLookupExpressionTypeWhenPositionIsInsideNestedLookupKey() {
83+
String query = """
84+
declare variable $a := {
85+
"name": "ada",
86+
"details": {
87+
"tlf": 1233.2
88+
}
89+
};
90+
($a.details.tlf)
91+
""";
92+
int tlfOffset = query.indexOf("tlf)") + "t".length();
93+
94+
TypeAtPosition.Result result = this.typeAtPosition.findType(
95+
query,
96+
positionAtOffset(query, tlfOffset)
97+
);
98+
99+
assertNotNull(result.sequenceType());
100+
assertEquals("xs:decimal", result.sequenceType().toString());
101+
assertEquals(
102+
new Range(
103+
positionAtOffset(query, query.indexOf("$a.details.tlf")),
104+
positionAtOffset(query, query.indexOf("$a.details.tlf") + "$a.details.tlf".length())
105+
),
106+
result.range()
107+
);
108+
}
109+
51110
@Test
52111
void returnsSmallestExpressionContainingPositionWhenNothingEndsThere() {
53112
String query = "1 + 20";

0 commit comments

Comments
 (0)