Skip to content

Commit 04a006e

Browse files
committed
feat: collect proper variable for hover with Qute debugger
Signed-off-by: azerr <azerr@redhat.com>
1 parent 30dce40 commit 04a006e

File tree

11 files changed

+283
-2
lines changed

11 files changed

+283
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ When editing `application.properties` files, you have access to:
237237

238238
## Requirements
239239

240-
* Intellij IDEA 2023.3 or more recent (we **try** to support the last 4 major IDEA releases)
240+
* Intellij IDEA 2024.3 or more recent (we **try** to support the last 4 major IDEA releases)
241241
* Java JDK (or JRE) 17 or more recent
242242

243243

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pluginSinceBuild=233.11799
1010
#pluginUntilBuild=233.*
1111
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
1212
platformType=IC
13-
platformVersion=2023.3
13+
platformVersion=2024.3
1414
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
1515
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
1616
platformBundledPlugins=com.intellij.java, org.jetbrains.idea.maven, org.jetbrains.plugins.gradle, com.intellij.properties, org.jetbrains.plugins.yaml

src/main/java/com/redhat/devtools/intellij/qute/lang/QuteASTFactory.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.intellij.psi.tree.IElementType;
2020
import com.redhat.devtools.intellij.qute.lang.psi.QuteToken;
2121
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementTypes;
22+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
2223
import org.jetbrains.annotations.NotNull;
2324
import org.jetbrains.annotations.Nullable;
2425

@@ -34,6 +35,18 @@ public class QuteASTFactory extends ASTFactory {
3435
return new OuterLanguageElementImpl(type, text);
3536
}
3637
// Qute content
38+
if (type == QuteTokenType.QUTE_EXPRESSION_OBJECT_PART) {
39+
return new QuteASTObjectPart(text);
40+
}
41+
if (type == QuteTokenType.QUTE_EXPRESSION_PROPERTY_PART) {
42+
return new QuteASTPropertyPart(text);
43+
}
44+
if (type == QuteTokenType.QUTE_EXPRESSION_METHOD_PART) {
45+
return new QuteASTMethodPart(text);
46+
}
47+
if (type == QuteTokenType.QUTE_EXPRESSION_INFIX_METHOD_PART) {
48+
return new QuteASTInfixMethodPart(text);
49+
}
3750
return new QuteToken(type, text);
3851
}
3952
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.redhat.devtools.intellij.qute.lang;
2+
3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.psi.PsiElement;
5+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
6+
import org.jetbrains.annotations.NotNull;
7+
import org.jetbrains.annotations.Nullable;
8+
9+
public class QuteASTInfixMethodPart extends QuteASTPart {
10+
11+
public QuteASTInfixMethodPart(CharSequence text) {
12+
super(QuteTokenType.QUTE_EXPRESSION_INFIX_METHOD_PART, text);
13+
}
14+
15+
public @Nullable PsiElement getRootPart() {
16+
PsiElement part = this;
17+
if (isQuteType(super.getParent(), QuteTokenType.QUTE_EXPRESSION_INFIX_METHOD_PART)) {
18+
part = super.getParent();
19+
}
20+
QuteASTObjectPart objectPart = findObjectPart(part);
21+
if (objectPart != null) {
22+
return objectPart.getRootPart();
23+
}
24+
return null;
25+
}
26+
27+
@Override
28+
protected @Nullable TextRange getTextRangeInExpression(@NotNull PsiElement root) {
29+
var infixParameter = findInfixParameter(this);
30+
if (infixParameter == null) {
31+
return null;
32+
}
33+
return new TextRange(root.getTextRange().getStartOffset(), infixParameter.getTextRange().getEndOffset());
34+
}
35+
36+
protected @Nullable PsiElement findInfixParameter(PsiElement part) {
37+
var next = part.getNextSibling();
38+
if (isQuteType(next, QuteTokenType.QUTE_EXPRESSION_WHITESPACE)) {
39+
return next.getNextSibling();
40+
}
41+
return null;
42+
}
43+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.redhat.devtools.intellij.qute.lang;
2+
3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.impl.source.tree.CompositeElement;
6+
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementType;
7+
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementTypes;
8+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
public class QuteASTMethodPart extends QuteASTPart {
13+
14+
public QuteASTMethodPart(CharSequence text) {
15+
super(QuteTokenType.QUTE_EXPRESSION_METHOD_PART, text);
16+
}
17+
18+
public @Nullable PsiElement getRootPart() {
19+
PsiElement part = this;
20+
if (isQuteType(super.getParent(), QuteTokenType.QUTE_EXPRESSION_METHOD_PART)) {
21+
part = super.getParent();
22+
}
23+
QuteASTObjectPart objectPart = findObjectPart(part);
24+
if (objectPart != null) {
25+
return objectPart.getRootPart();
26+
}
27+
return null;
28+
}
29+
30+
@Override
31+
protected @Nullable TextRange getTextRangeInExpression(@NotNull PsiElement root) {
32+
var closedBracket = findClosedBracket(this);
33+
return new TextRange(root.getTextRange().getStartOffset(), closedBracket != null ? closedBracket.getTextRange().getEndOffset() : super.getTextRange().getEndOffset());
34+
}
35+
36+
protected @Nullable PsiElement findClosedBracket(PsiElement part) {
37+
var next = part.getNextSibling();
38+
if (!isQuteType(next, QuteTokenType.QUTE_EXPRESSION_OPEN_BRACKET)) {
39+
return null;
40+
}
41+
int nbBrackets = 0;
42+
while(next != null) {
43+
if (isQuteType(next, QuteTokenType.QUTE_EXPRESSION_OPEN_BRACKET)) {
44+
nbBrackets++;
45+
} else if (isQuteType(next, QuteTokenType.QUTE_EXPRESSION_CLOSE_BRACKET)) {
46+
nbBrackets--;
47+
if (nbBrackets == 0) {
48+
return next;
49+
}
50+
}
51+
next = next.getNextSibling();
52+
}
53+
return null;
54+
}
55+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.redhat.devtools.intellij.qute.lang;
2+
3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.psi.PsiElement;
5+
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementType;
6+
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementTypes;
7+
import com.redhat.devtools.intellij.qute.lang.psi.QuteToken;
8+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
public class QuteASTObjectPart extends QuteASTPart {
13+
14+
public QuteASTObjectPart(CharSequence text) {
15+
super(QuteTokenType.QUTE_EXPRESSION_OBJECT_PART, text);
16+
}
17+
18+
public @Nullable PsiElement getRootPart() {
19+
PsiElement part = this;
20+
if (isQuteType(super.getParent(), QuteTokenType.QUTE_EXPRESSION_OBJECT_PART)) {
21+
part = super.getParent();
22+
}
23+
var previous = part.getPrevSibling();
24+
if (isQuteType(previous, QuteTokenType.QUTE_EXPRESSION_COLON_SPACE)) {
25+
previous = previous.getPrevSibling();
26+
if (isQuteType(previous, QuteTokenType.QUTE_EXPRESSION_NAMESPACE_PART)) {
27+
// ex : uri:Todos
28+
return previous;
29+
}
30+
}
31+
// ex : task
32+
return this;
33+
}
34+
35+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.redhat.devtools.intellij.qute.lang;
2+
3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.impl.source.tree.CompositeElement;
6+
import com.intellij.psi.tree.IElementType;
7+
import com.redhat.devtools.intellij.qute.lang.psi.QuteToken;
8+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
public abstract class QuteASTPart extends QuteToken {
13+
14+
public QuteASTPart(@NotNull IElementType type, CharSequence text) {
15+
super(type, text);
16+
}
17+
18+
@Override
19+
public @Nullable TextRange getTextRangeInExpression() {
20+
var root = getRootPart();
21+
if (root == null) {
22+
return null;
23+
}
24+
if (root == this) {
25+
return getTextRange();
26+
}
27+
return getTextRangeInExpression(root);
28+
}
29+
30+
protected @Nullable TextRange getTextRangeInExpression(@NotNull PsiElement root) {
31+
return new TextRange(root.getTextRange().getStartOffset(), super.getTextRange().getEndOffset());
32+
}
33+
34+
protected @Nullable QuteASTObjectPart findObjectPart(PsiElement part) {
35+
var previous = part.getPrevSibling();
36+
while(previous != null) {
37+
if (isQuteType(previous, QuteTokenType.QUTE_EXPRESSION_OBJECT_PART)) {
38+
// ex : uri:Todos
39+
QuteASTObjectPart objectPart = previous instanceof QuteASTObjectPart o ? o : null;
40+
if (objectPart == null) {
41+
var node = previous.getNode();
42+
if (node instanceof CompositeElement) {
43+
var firstNode = node.getFirstChildNode();
44+
objectPart = firstNode instanceof QuteASTObjectPart o ? o : null;
45+
}
46+
}
47+
return objectPart;
48+
} else {
49+
previous = previous.getPrevSibling();
50+
}
51+
}
52+
return null;
53+
}
54+
55+
public abstract @Nullable PsiElement getRootPart();
56+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.redhat.devtools.intellij.qute.lang;
2+
3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.impl.source.tree.CompositeElement;
6+
import com.redhat.devtools.intellij.qute.lang.psi.QuteToken;
7+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
public class QuteASTPropertyPart extends QuteASTPart {
12+
13+
public QuteASTPropertyPart(CharSequence text) {
14+
super(QuteTokenType.QUTE_EXPRESSION_PROPERTY_PART, text);
15+
}
16+
17+
public @Nullable PsiElement getRootPart() {
18+
PsiElement part = this;
19+
if (isQuteType(super.getParent(), QuteTokenType.QUTE_EXPRESSION_PROPERTY_PART)) {
20+
part = super.getParent();
21+
}
22+
QuteASTObjectPart objectPart = findObjectPart(part);
23+
if (objectPart != null) {
24+
return objectPart.getRootPart();
25+
}
26+
return null;
27+
}
28+
29+
30+
}

src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteToken.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313
*******************************************************************************/
1414
package com.redhat.devtools.intellij.qute.lang.psi;
1515

16+
import com.intellij.openapi.util.TextRange;
1617
import com.intellij.psi.HintedReferenceHost;
18+
import com.intellij.psi.PsiElement;
1719
import com.intellij.psi.PsiReference;
1820
import com.intellij.psi.PsiReferenceService;
1921
import com.intellij.psi.impl.source.tree.LeafPsiElement;
2022
import com.intellij.psi.tree.IElementType;
2123
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
2225

2326
/**
2427
* Qute token.
@@ -40,8 +43,18 @@ public QuteToken(@NotNull IElementType type, CharSequence text) {
4043
return new PsiReference[] {new QutePsiReference(getNode())};
4144
}
4245

46+
public @Nullable TextRange getTextRangeInExpression() {
47+
return null;
48+
}
49+
4350
@Override
4451
public boolean shouldAskParentForReferences(PsiReferenceService.@NotNull Hints hints) {
4552
return false;
4653
}
54+
55+
56+
protected static boolean isQuteType(@Nullable PsiElement element,
57+
@NotNull IElementType type) {
58+
return element != null && element.getNode().getElementType() == type;
59+
}
4760
}

src/main/java/com/redhat/devtools/intellij/qute/run/QuteDebugAdapterVariableSupport.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,25 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.qute.run;
1212

13+
import com.intellij.extapi.psi.ASTWrapperPsiElement;
14+
import com.intellij.openapi.editor.Document;
15+
import com.intellij.openapi.project.Project;
16+
import com.intellij.openapi.util.TextRange;
17+
import com.intellij.openapi.vfs.VirtualFile;
18+
import com.intellij.psi.PsiElement;
19+
import com.intellij.psi.PsiFile;
20+
import com.intellij.psi.impl.source.tree.CompositeElement;
21+
import com.redhat.devtools.intellij.qute.lang.QuteLanguage;
22+
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementType;
23+
import com.redhat.devtools.intellij.qute.lang.psi.QuteElementTypes;
24+
import com.redhat.devtools.intellij.qute.lang.psi.QuteToken;
25+
import com.redhat.devtools.intellij.qute.lang.psi.QuteTokenType;
26+
import com.redhat.devtools.lsp4ij.LSPIJUtils;
1327
import com.redhat.devtools.lsp4ij.dap.client.variables.providers.DebugVariablePositionProvider;
1428
import com.redhat.devtools.lsp4ij.dap.client.variables.providers.HighlighterDebugVariablePositionProvider;
1529
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterVariableSupport;
1630
import org.jetbrains.annotations.NotNull;
31+
import org.jetbrains.annotations.Nullable;
1732

1833
import java.util.Collection;
1934
import java.util.Collections;
@@ -27,4 +42,23 @@ public class QuteDebugAdapterVariableSupport extends DebugAdapterVariableSupport
2742
public @NotNull Collection<DebugVariablePositionProvider> getDebugVariablePositionProvider() {
2843
return Collections.singletonList(new QuteDebugVariablePositionProvider());
2944
}
45+
46+
@Override
47+
protected @Nullable TextRange getTextRange(@NotNull Project project, @NotNull VirtualFile file, @NotNull Document document, int offset, boolean sideEffectsAllowed) {
48+
PsiFile psiFile = LSPIJUtils.getPsiFile(file, project);
49+
if (psiFile == null) {
50+
return null;
51+
}
52+
var element = psiFile.findElementAt(offset);
53+
if (element == null || !QuteLanguage.INSTANCE.equals(element.getLanguage())) {
54+
return null;
55+
}
56+
if (element instanceof QuteToken quteToken) {
57+
return quteToken.getTextRangeInExpression();
58+
}
59+
return null;
60+
}
61+
3062
}
63+
64+

0 commit comments

Comments
 (0)