Skip to content

Commit da1744e

Browse files
angelozerrdatho7561
authored andcommitted
Qute debugging support inside Java file
Signed-off-by: azerr <azerr@redhat.com>
1 parent 50c9111 commit da1744e

File tree

11 files changed

+760
-9
lines changed

11 files changed

+760
-9
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.acme.sample;
2+
3+
import io.quarkus.qute.CheckedTemplate;
4+
import io.quarkus.qute.TemplateContents;
5+
import io.quarkus.qute.TemplateInstance;
6+
import jakarta.ws.rs.GET;
7+
import jakarta.ws.rs.Path;
8+
import jakarta.ws.rs.QueryParam;
9+
10+
public class TemplateContentsResource {
11+
12+
@TemplateContents(value = "Hello {name}!")
13+
record Hello(String name) implements TemplateInstance {}
14+
15+
@TemplateContents("""
16+
Hello {name}!
17+
""")
18+
record Hello2(String name) implements TemplateInstance {}
19+
20+
@CheckedTemplate
21+
public static class Templates {
22+
@TemplateContents("Item is {item}")
23+
public static native TemplateInstance item(String item);
24+
}
25+
26+
@GET
27+
public TemplateInstance hello(@QueryParam("name") String name) {
28+
return new Hello(name);
29+
}
30+
31+
@GET
32+
public TemplateInstance hello2(@QueryParam("name") String name) {
33+
return new Hello2(name);
34+
}
35+
36+
@GET
37+
@Path("/item")
38+
public TemplateInstance item() {
39+
return Templates.item("foo");
40+
}
41+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Red Hat Inc. and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* which accompanies this distribution, and is available at
5+
* http://www.eclipse.org/legal/epl-v20.html
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* Red Hat Inc. - initial API and implementation
11+
*******************************************************************************/
12+
package com.redhat.qute.jdt.debug;
13+
14+
import static com.redhat.qute.jdt.QuteProjectTest.getJDTUtils;
15+
import static com.redhat.qute.jdt.QuteProjectTest.loadMavenProject;
16+
import static org.junit.Assert.assertEquals;
17+
18+
import org.eclipse.core.runtime.NullProgressMonitor;
19+
import org.junit.Test;
20+
21+
import com.redhat.qute.jdt.QuteProjectTest.QuteMavenProjectName;
22+
import com.redhat.qute.jdt.QuteSupportForDebug;
23+
24+
public class DebugGetStartLineTest {
25+
26+
@Test
27+
public void validStartLine() throws Exception {
28+
loadMavenProject(QuteMavenProjectName.qute_record);
29+
30+
// Simple string test with record
31+
// @TemplateContents(value = "Hello {name}!")
32+
// record Hello(String name) implements TemplateInstance {}
33+
assertStartLine(loc("org.acme.sample.TemplateContentsResource$Hello", "io.quarkus.qute.TemplateContents"), 11);
34+
35+
// Text block test with record
36+
// @TemplateContents("""
37+
// Hello {name}!
38+
// """)
39+
// record Hello2(String name) implements TemplateInstance {}
40+
assertStartLine(loc("org.acme.sample.TemplateContentsResource$Hello2", "io.quarkus.qute.TemplateContents"), 15);
41+
42+
// Simple string test with @CheckedTemplate
43+
// @CheckedTemplate
44+
// public static class Templates {
45+
// @TemplateContents("Item is {item}")
46+
// public static native TemplateInstance item(String item);
47+
// }
48+
assertStartLine(
49+
loc("org.acme.sample.TemplateContentsResource$Templates", "item", "io.quarkus.qute.TemplateContents"),
50+
21);
51+
}
52+
53+
@Test
54+
public void invalidStartLine() throws Exception {
55+
loadMavenProject(QuteMavenProjectName.qute_record);
56+
57+
// Invalid recod type
58+
assertStartLine(loc("org.acme.sample.TemplateContentsResource$HelloXXX", "io.quarkus.qute.TemplateContents"),
59+
null);
60+
61+
// Invalid annotation
62+
assertStartLine(loc("org.acme.sample.TemplateContentsResource$Hello", "io.quarkus.qute.TemplateContentsXXX"),
63+
null);
64+
}
65+
66+
private static void assertStartLine(JavaSourceLocationArguments args, Integer expected) {
67+
JavaSourceLocationResponse response = QuteSupportForDebug.getInstance().resolveJavaSource(args, getJDTUtils(),
68+
new NullProgressMonitor());
69+
Integer actual = response != null ? response.getStartLine() : null;
70+
assertEquals(expected, actual);
71+
}
72+
73+
private static JavaSourceLocationArguments loc(String typeName, String annotation) {
74+
return loc(typeName, null, annotation);
75+
}
76+
77+
private static JavaSourceLocationArguments loc(String typeName, String method, String annotation) {
78+
JavaSourceLocationArguments args = new JavaSourceLocationArguments();
79+
args.setTypeName(typeName);
80+
args.setMethod(method);
81+
args.setAnnotation(annotation);
82+
return args;
83+
}
84+
}

qute.jdt/com.redhat.qute.jdt.test/src/main/java/com/redhat/qute/jdt/template/TemplateGetResolvedJavaTypeTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void list() throws Exception {
152152
Assert.assertNotNull(extendedTypes);
153153
Assert.assertEquals(2, extendedTypes.size());
154154
assertExtendedTypes("java.util.List", "java.lang.Object", extendedTypes);
155-
assertExtendedTypes("java.util.List", "java.util.Collection<E>", extendedTypes);
155+
assertExtendedTypes("java.util.List", "java.util.SequencedCollection<E>", extendedTypes);
156156

157157
// List
158158
params = new QuteResolvedJavaTypeParams("List", QuteMavenProjectName.qute_quickstart);

qute.jdt/com.redhat.qute.jdt/META-INF/MANIFEST.MF

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Export-Package: com.redhat.qute.commons,
2525
com.redhat.qute.commons.jaxrs,
2626
com.redhat.qute.commons.usertags,
2727
com.redhat.qute.jdt,
28+
com.redhat.qute.jdt.debug,
2829
com.redhat.qute.jdt.internal.java;x-friends:="com.redhat.qute.jdt.test",
2930
com.redhat.qute.jdt.internal.ls;x-friends:="com.redhat.qute.jdt.test",
3031
com.redhat.qute.jdt.internal.template;x-friends:="com.redhat.qute.jdt.test",

qute.jdt/com.redhat.qute.jdt/plugin.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
</delegateCommandHandler>
3838
</extension>
3939

40+
<!-- Delegate command handler for Qute debugging -->
41+
<extension point="org.eclipse.jdt.ls.core.delegateCommandHandler">
42+
<delegateCommandHandler class="com.redhat.qute.jdt.internal.ls.QuteSupportForDebugDelegateCommandHandler">
43+
<command id="qute/debug/resolveJavaSource"/>
44+
</delegateCommandHandler>
45+
</extension>
46+
4047
<!-- =========== Qute core & Quarkus Integration =========== -->
4148

4249
<!-- Template root path providers for Qute core (src/main/resources/templates) -->
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Red Hat Inc. and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* which accompanies this distribution, and is available at
5+
* http://www.eclipse.org/legal/epl-v20.html
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* Red Hat Inc. - initial API and implementation
11+
*******************************************************************************/
12+
package com.redhat.qute.jdt;
13+
14+
import java.util.logging.Level;
15+
import java.util.logging.Logger;
16+
17+
import org.eclipse.core.resources.IProject;
18+
import org.eclipse.core.resources.IWorkspaceRoot;
19+
import org.eclipse.core.resources.ResourcesPlugin;
20+
import org.eclipse.core.runtime.IProgressMonitor;
21+
import org.eclipse.jdt.core.ICompilationUnit;
22+
import org.eclipse.jdt.core.IJavaProject;
23+
import org.eclipse.jdt.core.IType;
24+
import org.eclipse.jdt.core.JavaCore;
25+
import org.eclipse.jdt.core.dom.AST;
26+
import org.eclipse.jdt.core.dom.ASTParser;
27+
import org.eclipse.jdt.core.dom.CompilationUnit;
28+
29+
import com.redhat.qute.jdt.debug.JavaSourceLocationArguments;
30+
import com.redhat.qute.jdt.debug.JavaSourceLocationResponse;
31+
import com.redhat.qute.jdt.internal.debug.QuteTemplateASTVisitor;
32+
import com.redhat.qute.jdt.utils.IJDTUtils;
33+
34+
/**
35+
* AST-based implementation for resolving Java source locations referenced from
36+
* Qute templates.
37+
*
38+
* Supported: - annotation-based template resolution - StringLiteral and
39+
* TextBlock - class and record - inner types - optional method filtering
40+
*/
41+
public class QuteSupportForDebug {
42+
43+
private static final Logger LOGGER = Logger.getLogger(QuteSupportForDebug.class.getName());
44+
45+
private static final QuteSupportForDebug INSTANCE = new QuteSupportForDebug();
46+
47+
public static QuteSupportForDebug getInstance() {
48+
return INSTANCE;
49+
}
50+
51+
public JavaSourceLocationResponse resolveJavaSource(JavaSourceLocationArguments args, IJDTUtils utils,
52+
IProgressMonitor monitor) {
53+
try {
54+
LOGGER.log(Level.INFO, "Resolving Java source location of " + args.getJavaElementUri());
55+
IType type = findType(args.getTypeName(), monitor);
56+
if (type == null) {
57+
return null;
58+
}
59+
60+
ICompilationUnit cu = type.getCompilationUnit();
61+
if (cu == null) {
62+
return null;
63+
}
64+
65+
CompilationUnit ast = parse(cu);
66+
67+
String[] typePath = splitTypePath(args.getTypeName());
68+
69+
QuteTemplateASTVisitor visitor = new QuteTemplateASTVisitor(ast, typePath, args.getMethod(),
70+
args.getAnnotation());
71+
72+
ast.accept(visitor);
73+
74+
int startLine = visitor.getStartLine();
75+
if (startLine == -1) {
76+
return null;
77+
}
78+
79+
String javaFileUri = utils.toUri(cu);
80+
LOGGER.log(Level.INFO, "Resolved Java source location of " + args.getJavaElementUri() + ", javaFileUri="
81+
+ javaFileUri + ", startLine=" + startLine);
82+
83+
return new JavaSourceLocationResponse(javaFileUri, startLine);
84+
85+
} catch (Exception e) {
86+
LOGGER.log(Level.SEVERE, "Failed to resolve Java source location of " + args.getJavaElementUri(), e);
87+
return null;
88+
}
89+
}
90+
91+
/* ---------------------------------------------------------------------- */
92+
/* Helpers */
93+
/* ---------------------------------------------------------------------- */
94+
95+
private static IType findType(String className, IProgressMonitor monitor) {
96+
try {
97+
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
98+
99+
for (IProject project : root.getProjects()) {
100+
if (!project.isOpen() || !project.hasNature(JavaCore.NATURE_ID)) {
101+
continue;
102+
}
103+
104+
IJavaProject javaProject = JavaCore.create(project);
105+
IType type = javaProject.findType(className, monitor);
106+
if (type != null) {
107+
return type;
108+
}
109+
}
110+
} catch (Exception e) {
111+
LOGGER.log(Level.SEVERE, "Error while finding type " + className, e);
112+
}
113+
return null;
114+
}
115+
116+
/**
117+
* Removes the package and splits on both '.' and '$' to support inner types.
118+
*/
119+
private static String[] splitTypePath(String typeName) {
120+
int idx = typeName.lastIndexOf('.');
121+
String simple = (idx == -1) ? typeName : typeName.substring(idx + 1);
122+
return simple.split("[.$]");
123+
}
124+
125+
private static CompilationUnit parse(ICompilationUnit cu) {
126+
ASTParser parser = ASTParser.newParser(AST.JLS_Latest);
127+
parser.setSource(cu);
128+
parser.setResolveBindings(false);
129+
return (CompilationUnit) parser.createAST(null);
130+
}
131+
}

0 commit comments

Comments
 (0)