Skip to content

Commit f55035d

Browse files
committed
feat: Qute: Cannot locate hyphenated template name
Fixes redhat-developer/quarkus-ls#975 Signed-off-by: azerr <azerr@redhat.com>
1 parent 3114a5e commit f55035d

File tree

12 files changed

+308
-73
lines changed

12 files changed

+308
-73
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.acme.sample;
2+
3+
import java.math.BigDecimal;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import jakarta.ws.rs.GET;
9+
import jakarta.ws.rs.Path;
10+
11+
import io.quarkus.qute.CheckedTemplate;
12+
import io.quarkus.qute.TemplateExtension;
13+
import io.quarkus.qute.TemplateInstance;
14+
15+
@Path("items")
16+
public class ItemResource {
17+
18+
@CheckedTemplate(defaultName=CheckedTemplate.ELEMENT_NAME)
19+
static class Templates {
20+
static native TemplateInstance HelloWorld(String name);
21+
22+
}
23+
24+
@CheckedTemplate(defaultName=CheckedTemplate.HYPHENATED_ELEMENT_NAME)
25+
static class Templates2 {
26+
static native TemplateInstance HelloWorld(String name);
27+
28+
}
29+
30+
@CheckedTemplate(defaultName=CheckedTemplate.UNDERSCORED_ELEMENT_NAME)
31+
static class Templates3 {
32+
static native TemplateInstance HelloWorld(String name);
33+
}
34+
35+
36+
}

src/main/java/com/redhat/devtools/intellij/qute/psi/internal/QuteJavaConstants.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ public class QuteJavaConstants {
5252

5353
public static final String CHECKED_TEMPLATE_ANNOTATION_IGNORE_FRAGMENTS = "ignoreFragments";
5454
public static final String CHECKED_TEMPLATE_ANNOTATION_BASE_PATH = "basePath";
55-
55+
public static final String CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME = "defaultName";
56+
public static final String CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_HYPHENATED_ELEMENT_NAME = "<<hyphenated element name>>";
57+
public static final String CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_UNDERSCORED_ELEMENT_NAME = "<<underscored element name>>";
58+
5659
// @TemplateExtension
5760

5861
public static final String TEMPLATE_EXTENSION_ANNOTATION = "io.quarkus.qute.TemplateExtension";

src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@
2222
import com.intellij.psi.impl.source.PsiClassReferenceType;
2323
import com.intellij.psi.util.PsiTreeUtil;
2424
import com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants;
25+
import com.redhat.devtools.intellij.qute.psi.utils.*;
2526
import com.redhat.devtools.lsp4ij.LSPIJUtils;
2627
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils;
2728
import com.redhat.devtools.intellij.qute.psi.internal.AnnotationLocationSupport;
28-
import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils;
29-
import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils;
30-
import com.redhat.devtools.intellij.qute.psi.utils.PsiTypeUtils;
31-
import com.redhat.devtools.intellij.qute.psi.utils.TemplatePathInfo;
3229
import org.eclipse.lsp4j.Range;
3330
import org.jetbrains.annotations.NotNull;
3431
import org.jetbrains.annotations.Nullable;
@@ -40,8 +37,7 @@
4037
import java.util.logging.Logger;
4138

4239
import static com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants.*;
43-
import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.getBasePath;
44-
import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.isIgnoreFragments;
40+
import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.*;
4541
import static com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils.*;
4642

4743
/**
@@ -122,7 +118,7 @@ public void visitField(PsiField node) {
122118
.getLocationExpressionFromConstructorParameter(node.getName());
123119
}
124120
String fieldName = node.getName();
125-
collectTemplateLink(null, node, locationExpression, getTypeDeclaration(node), null, fieldName, false);
121+
collectTemplateLink(null, node, locationExpression, getTypeDeclaration(node), null, fieldName, false, TemplateNameStrategy.ELEMENT_NAME);
126122
}
127123
super.visitField(node);
128124
}
@@ -172,8 +168,9 @@ private void visitClassType(PsiClass node) {
172168
// public static native TemplateInstance book(Book book);
173169
boolean ignoreFragments = isIgnoreFragments(annotation);
174170
String basePath = getBasePath(annotation);
171+
TemplateNameStrategy templateNameStrategy = getDefaultName(annotation);
175172
for (PsiMethod method : node.getMethods()) {
176-
collectTemplateLink(basePath, method, node, ignoreFragments);
173+
collectTemplateLink(basePath, method, node, ignoreFragments, templateNameStrategy);
177174
}
178175
}
179176
}
@@ -191,7 +188,7 @@ private void visitClassType(PsiClass node) {
191188
private void visitRecordType(PsiClass node) {
192189
if (isImplementTemplateInstance(node)) {
193190
String recordName = node.getName();
194-
collectTemplateLink(null, node, null, node, null, recordName, false);
191+
collectTemplateLink(null, node, null, node, null, recordName, false, TemplateNameStrategy.ELEMENT_NAME);
195192
}
196193
}
197194

@@ -219,25 +216,25 @@ private static PsiClass getTypeDeclaration(PsiElement node) {
219216
return PsiTreeUtil.getParentOfType(node, PsiClass.class);
220217
}
221218

222-
private void collectTemplateLink(String basePath, PsiMethod methodDeclaration, PsiClass type, boolean ignoreFragments) {
219+
private void collectTemplateLink(String basePath, PsiMethod methodDeclaration, PsiClass type, boolean ignoreFragments, TemplateNameStrategy templateNameStrategy) {
223220
String className = null;
224221
boolean innerClass = levelTypeDecl > 1;
225222
if (innerClass) {
226223
className = PsiTypeUtils.getSimpleClassName(typeRoot.getName());
227224
}
228225
String methodName = methodDeclaration.getName();
229-
collectTemplateLink(basePath, methodDeclaration, null, type, className, methodName, ignoreFragments);
226+
collectTemplateLink(basePath, methodDeclaration, null, type, className, methodName, ignoreFragments,templateNameStrategy);
230227
}
231228

232229
private void collectTemplateLink(String basePath, PsiElement fieldOrMethod, PsiLiteralValue locationAnnotation, PsiClass type, String className,
233-
String fieldOrMethodName, boolean ignoreFragment) {
230+
String fieldOrMethodName, boolean ignoreFragment, TemplateNameStrategy templateNameStrategy) {
234231
try {
235232
String location = locationAnnotation != null && locationAnnotation.getValue() instanceof String ? (String) locationAnnotation.getValue() : null;
236233
Module project = utils.getModule();
237234

238235
TemplatePathInfo templatePathInfo = location != null
239-
? PsiQuteProjectUtils.getTemplatePath(null, basePath, null, location, ignoreFragment)
240-
: PsiQuteProjectUtils.getTemplatePath(null, basePath, className, fieldOrMethodName, ignoreFragment);
236+
? PsiQuteProjectUtils.getTemplatePath(null, basePath, null, location, ignoreFragment, templateNameStrategy)
237+
: PsiQuteProjectUtils.getTemplatePath(null, basePath, className, fieldOrMethodName, ignoreFragment, templateNameStrategy);
241238
String templateUriWithoutBaseDir = templatePathInfo.getTemplateUri();
242239

243240
boolean definedSuffix = location != null;

src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/CheckedTemplateSupport.java

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils;
3434
import com.redhat.devtools.intellij.qute.psi.utils.PsiTypeUtils;
3535

36+
import com.redhat.devtools.intellij.qute.psi.utils.TemplateNameStrategy;
3637
import com.redhat.devtools.intellij.qute.psi.utils.TemplatePathInfo;
3738
import com.redhat.qute.commons.datamodel.DataModelBaseTemplate;
3839
import com.redhat.qute.commons.datamodel.DataModelFragment;
@@ -81,7 +82,8 @@ protected void processAnnotation(PsiElement javaElement, PsiAnnotation checkedTe
8182
PsiClass type = (PsiClass) javaElement;
8283
boolean ignoreFragments = isIgnoreFragments(checkedTemplateAnnotation);
8384
String basePath = getBasePath(checkedTemplateAnnotation);
84-
collectDataModelTemplateForCheckedTemplate(type, context.getRelativeTemplateBaseDir(), basePath, ignoreFragments, context.getTypeResolver(type),
85+
TemplateNameStrategy templateNameStrategy = getDefaultName(checkedTemplateAnnotation);
86+
collectDataModelTemplateForCheckedTemplate(type, context.getRelativeTemplateBaseDir(), basePath, ignoreFragments, templateNameStrategy, context.getTypeResolver(type),
8587
context.getDataModelProject().getTemplates(), monitor);
8688
}
8789
}
@@ -119,12 +121,11 @@ public static boolean isIgnoreFragments(PsiAnnotation checkedTemplateAnnotation)
119121

120122
/**
121123
* Returns the <code>basePath</code> value declared in the @CheckedTemplate annotation, relative to the templates root, to search the templates from.
122-
*<code>
123-
* @CheckedTemplate(basePath="somewhere")
124-
*</code>
124+
* <code>
125125
*
126126
* @param checkedTemplateAnnotation the CheckedTemplate annotation.
127127
* @return the <code>basePath</code> value declared in the @CheckedTemplate annotation
128+
* @CheckedTemplate(basePath="somewhere") </code>
128129
*/
129130
public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) {
130131
String basePath = null;
@@ -146,6 +147,53 @@ public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) {
146147
return basePath;
147148
}
148149

150+
/**
151+
* Returns the <code>defaultName</code> value declared in the @CheckedTemplate annotation.
152+
* <code>
153+
*
154+
* @param checkedTemplateAnnotation the CheckedTemplate annotation.
155+
* @return the <code>basePath</code> value declared in the @CheckedTemplate annotation
156+
* @CheckedTemplate(defaultName="somewhere") </code>
157+
*/
158+
public static TemplateNameStrategy getDefaultName(PsiAnnotation checkedTemplateAnnotation) {
159+
TemplateNameStrategy templateNameStrategy = TemplateNameStrategy.ELEMENT_NAME;
160+
try {
161+
for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) {
162+
if (CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME.equalsIgnoreCase(pair.getAttributeName())) {
163+
if (pair.getValue() != null
164+
&& pair.getValue().getReference() != null
165+
&& pair.getValue().getReference().resolve() != null &&
166+
pair.getValue().getReference().resolve() instanceof PsiField field) {
167+
Object value = field.computeConstantValue();
168+
if (value != null) {
169+
templateNameStrategy = getDefaultName(value.toString());
170+
}
171+
}
172+
}
173+
}
174+
} catch (ProcessCanceledException e) {
175+
//Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility
176+
//TODO delete block when minimum required version is 2024.2
177+
throw e;
178+
} catch (IndexNotReadyException | CancellationException e) {
179+
throw e;
180+
} catch (Exception e) {
181+
// Do nothing
182+
}
183+
return templateNameStrategy;
184+
}
185+
186+
private static TemplateNameStrategy getDefaultName(String defaultName) {
187+
switch (defaultName) {
188+
case CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_HYPHENATED_ELEMENT_NAME:
189+
return TemplateNameStrategy.HYPHENATED_ELEMENT_NAME;
190+
191+
case CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_UNDERSCORED_ELEMENT_NAME:
192+
return TemplateNameStrategy.UNDERSCORED_ELEMENT_NAME;
193+
}
194+
return TemplateNameStrategy.ELEMENT_NAME;
195+
}
196+
149197
/**
150198
* Collect data model template from @CheckedTemplate.
151199
*
@@ -155,8 +203,14 @@ public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) {
155203
* @param templates the data model templates to update with collect of template.
156204
* @param monitor the progress monitor.
157205
*/
158-
private static void collectDataModelTemplateForCheckedTemplate(PsiClass type, String templatesBaseDir, String basePath, boolean ignoreFragments, ITypeResolver typeResolver,
159-
List<DataModelTemplate<DataModelParameter>> templates, ProgressIndicator monitor) {
206+
private static void collectDataModelTemplateForCheckedTemplate(PsiClass type,
207+
String templatesBaseDir,
208+
String basePath,
209+
boolean ignoreFragments,
210+
TemplateNameStrategy templateNameStrategy,
211+
ITypeResolver typeResolver,
212+
List<DataModelTemplate<DataModelParameter>> templates,
213+
ProgressIndicator monitor) {
160214
boolean innerClass = type.getContainingClass() != null;
161215
String className = !innerClass ? null
162216
: PsiTypeUtils.getSimpleClassName(type.getContainingFile().getName());
@@ -166,7 +220,7 @@ private static void collectDataModelTemplateForCheckedTemplate(PsiClass type, St
166220
PsiMethod[] methods = type.getMethods();
167221
for (PsiMethod method : methods) {
168222
// src/main/resources/templates/${className}/${methodName}.qute.html
169-
TemplatePathInfo templatePathInfo = getTemplatePath(templatesBaseDir, basePath, className, method.getName(), ignoreFragments);
223+
TemplatePathInfo templatePathInfo = getTemplatePath(templatesBaseDir, basePath, className, method.getName(), ignoreFragments, templateNameStrategy);
170224

171225
// Get or create template
172226
String templateUri = templatePathInfo.getTemplateUri();
@@ -234,7 +288,11 @@ public static void collectParameters(PsiMethod method, ITypeResolver typeResolve
234288
for (int i = 0; i < parameters.getParametersCount(); i++) {
235289
DataModelParameter parameter = createParameterDataModel(parameters.getParameter(i),
236290
varargs && i == parameters.getParametersCount() - 1, typeResolver);
237-
templateOrFragment.getParameters().add(parameter);
291+
if (templateOrFragment.getParameter(parameter.getKey()) == null) {
292+
// Add parameter if it doesn't exist
293+
// to avoid parameters duplication
294+
templateOrFragment.getParameters().add(parameter);
295+
}
238296
}
239297
}
240298

src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/TemplateFieldSupport.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.redhat.devtools.intellij.qute.psi.template.datamodel.SearchContext;
3838
import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils;
3939

40+
import com.redhat.devtools.intellij.qute.psi.utils.TemplateNameStrategy;
4041
import com.redhat.qute.commons.datamodel.DataModelParameter;
4142
import com.redhat.qute.commons.datamodel.DataModelTemplate;
4243

@@ -106,7 +107,10 @@ private static AnnotationLocationSupport getAnnotationLocationSupport(PsiFile co
106107
}
107108

108109
private static void collectDataModelTemplateForTemplateField(PsiField field,
109-
List<DataModelTemplate<DataModelParameter>> templates, String relativeTemplateBaseDir, String location, ProgressIndicator monitor) {
110+
List<DataModelTemplate<DataModelParameter>> templates,
111+
String relativeTemplateBaseDir,
112+
String location,
113+
ProgressIndicator monitor) {
110114
DataModelTemplate<DataModelParameter> template = createTemplateDataModel(field, relativeTemplateBaseDir, location, monitor);
111115
templates.add(template);
112116
}
@@ -117,7 +121,7 @@ private static DataModelTemplate<DataModelParameter> createTemplateDataModel(Psi
117121
String location = locationFromConstructorParameter != null ? locationFromConstructorParameter : getLocation(field);
118122
String fieldName = field.getName();
119123
// src/main/resources/templates/${methodName}.qute.html
120-
String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, location != null ? location : fieldName, true).getTemplateUri();
124+
String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, location != null ? location : fieldName, true, TemplateNameStrategy.ELEMENT_NAME).getTemplateUri();
121125

122126
// Create template data model with:
123127
// - template uri : Qute template file which must be bind with data model.

src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/TemplateRecordsSupport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.redhat.devtools.intellij.qute.psi.template.datamodel.AbstractInterfaceImplementationDataModelProvider;
1919
import com.redhat.devtools.intellij.qute.psi.template.datamodel.SearchContext;
2020
import com.redhat.devtools.intellij.qute.psi.utils.PsiTypeUtils;
21+
import com.redhat.devtools.intellij.qute.psi.utils.TemplateNameStrategy;
2122
import com.redhat.qute.commons.datamodel.DataModelParameter;
2223
import com.redhat.qute.commons.datamodel.DataModelTemplate;
2324

@@ -81,7 +82,7 @@ private static DataModelTemplate<DataModelParameter> createTemplateDataModel(Psi
8182

8283
String recordName = type.getName();
8384
// src/main/resources/templates/${recordName}.qute.html
84-
String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, recordName, true).getTemplateUri();
85+
String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, recordName, true, TemplateNameStrategy.ELEMENT_NAME).getTemplateUri();
8586

8687
// Create template data model with:
8788
// - template uri : Qute template file which must be bind with data model.

0 commit comments

Comments
 (0)