Skip to content

Commit

Permalink
feat: Qute: Cannot locate hyphenated template name
Browse files Browse the repository at this point in the history
Fixes redhat-developer/quarkus-ls#975

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Jul 27, 2024
1 parent 3114a5e commit f55035d
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.acme.sample;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import io.quarkus.qute.CheckedTemplate;
import io.quarkus.qute.TemplateExtension;
import io.quarkus.qute.TemplateInstance;

@Path("items")
public class ItemResource {

@CheckedTemplate(defaultName=CheckedTemplate.ELEMENT_NAME)
static class Templates {
static native TemplateInstance HelloWorld(String name);

}

@CheckedTemplate(defaultName=CheckedTemplate.HYPHENATED_ELEMENT_NAME)
static class Templates2 {
static native TemplateInstance HelloWorld(String name);

}

@CheckedTemplate(defaultName=CheckedTemplate.UNDERSCORED_ELEMENT_NAME)
static class Templates3 {
static native TemplateInstance HelloWorld(String name);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ public class QuteJavaConstants {

public static final String CHECKED_TEMPLATE_ANNOTATION_IGNORE_FRAGMENTS = "ignoreFragments";
public static final String CHECKED_TEMPLATE_ANNOTATION_BASE_PATH = "basePath";

public static final String CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME = "defaultName";
public static final String CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_HYPHENATED_ELEMENT_NAME = "<<hyphenated element name>>";
public static final String CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_UNDERSCORED_ELEMENT_NAME = "<<underscored element name>>";

// @TemplateExtension

public static final String TEMPLATE_EXTENSION_ANNOTATION = "io.quarkus.qute.TemplateExtension";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.util.PsiTreeUtil;
import com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants;
import com.redhat.devtools.intellij.qute.psi.utils.*;
import com.redhat.devtools.lsp4ij.LSPIJUtils;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils;
import com.redhat.devtools.intellij.qute.psi.internal.AnnotationLocationSupport;
import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils;
import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils;
import com.redhat.devtools.intellij.qute.psi.utils.PsiTypeUtils;
import com.redhat.devtools.intellij.qute.psi.utils.TemplatePathInfo;
import org.eclipse.lsp4j.Range;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -40,8 +37,7 @@
import java.util.logging.Logger;

import static com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants.*;
import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.getBasePath;
import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.isIgnoreFragments;
import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.*;
import static com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils.*;

/**
Expand Down Expand Up @@ -122,7 +118,7 @@ public void visitField(PsiField node) {
.getLocationExpressionFromConstructorParameter(node.getName());
}
String fieldName = node.getName();
collectTemplateLink(null, node, locationExpression, getTypeDeclaration(node), null, fieldName, false);
collectTemplateLink(null, node, locationExpression, getTypeDeclaration(node), null, fieldName, false, TemplateNameStrategy.ELEMENT_NAME);
}
super.visitField(node);
}
Expand Down Expand Up @@ -172,8 +168,9 @@ private void visitClassType(PsiClass node) {
// public static native TemplateInstance book(Book book);
boolean ignoreFragments = isIgnoreFragments(annotation);
String basePath = getBasePath(annotation);
TemplateNameStrategy templateNameStrategy = getDefaultName(annotation);
for (PsiMethod method : node.getMethods()) {
collectTemplateLink(basePath, method, node, ignoreFragments);
collectTemplateLink(basePath, method, node, ignoreFragments, templateNameStrategy);
}
}
}
Expand All @@ -191,7 +188,7 @@ private void visitClassType(PsiClass node) {
private void visitRecordType(PsiClass node) {
if (isImplementTemplateInstance(node)) {
String recordName = node.getName();
collectTemplateLink(null, node, null, node, null, recordName, false);
collectTemplateLink(null, node, null, node, null, recordName, false, TemplateNameStrategy.ELEMENT_NAME);
}
}

Expand Down Expand Up @@ -219,25 +216,25 @@ private static PsiClass getTypeDeclaration(PsiElement node) {
return PsiTreeUtil.getParentOfType(node, PsiClass.class);
}

private void collectTemplateLink(String basePath, PsiMethod methodDeclaration, PsiClass type, boolean ignoreFragments) {
private void collectTemplateLink(String basePath, PsiMethod methodDeclaration, PsiClass type, boolean ignoreFragments, TemplateNameStrategy templateNameStrategy) {
String className = null;
boolean innerClass = levelTypeDecl > 1;
if (innerClass) {
className = PsiTypeUtils.getSimpleClassName(typeRoot.getName());
}
String methodName = methodDeclaration.getName();
collectTemplateLink(basePath, methodDeclaration, null, type, className, methodName, ignoreFragments);
collectTemplateLink(basePath, methodDeclaration, null, type, className, methodName, ignoreFragments,templateNameStrategy);
}

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

TemplatePathInfo templatePathInfo = location != null
? PsiQuteProjectUtils.getTemplatePath(null, basePath, null, location, ignoreFragment)
: PsiQuteProjectUtils.getTemplatePath(null, basePath, className, fieldOrMethodName, ignoreFragment);
? PsiQuteProjectUtils.getTemplatePath(null, basePath, null, location, ignoreFragment, templateNameStrategy)
: PsiQuteProjectUtils.getTemplatePath(null, basePath, className, fieldOrMethodName, ignoreFragment, templateNameStrategy);
String templateUriWithoutBaseDir = templatePathInfo.getTemplateUri();

boolean definedSuffix = location != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils;
import com.redhat.devtools.intellij.qute.psi.utils.PsiTypeUtils;

import com.redhat.devtools.intellij.qute.psi.utils.TemplateNameStrategy;
import com.redhat.devtools.intellij.qute.psi.utils.TemplatePathInfo;
import com.redhat.qute.commons.datamodel.DataModelBaseTemplate;
import com.redhat.qute.commons.datamodel.DataModelFragment;
Expand Down Expand Up @@ -81,7 +82,8 @@ protected void processAnnotation(PsiElement javaElement, PsiAnnotation checkedTe
PsiClass type = (PsiClass) javaElement;
boolean ignoreFragments = isIgnoreFragments(checkedTemplateAnnotation);
String basePath = getBasePath(checkedTemplateAnnotation);
collectDataModelTemplateForCheckedTemplate(type, context.getRelativeTemplateBaseDir(), basePath, ignoreFragments, context.getTypeResolver(type),
TemplateNameStrategy templateNameStrategy = getDefaultName(checkedTemplateAnnotation);
collectDataModelTemplateForCheckedTemplate(type, context.getRelativeTemplateBaseDir(), basePath, ignoreFragments, templateNameStrategy, context.getTypeResolver(type),
context.getDataModelProject().getTemplates(), monitor);
}
}
Expand Down Expand Up @@ -119,12 +121,11 @@ public static boolean isIgnoreFragments(PsiAnnotation checkedTemplateAnnotation)

/**
* Returns the <code>basePath</code> value declared in the @CheckedTemplate annotation, relative to the templates root, to search the templates from.
*<code>
* @CheckedTemplate(basePath="somewhere")
*</code>
* <code>
*
* @param checkedTemplateAnnotation the CheckedTemplate annotation.
* @return the <code>basePath</code> value declared in the @CheckedTemplate annotation
* @CheckedTemplate(basePath="somewhere") </code>
*/
public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) {
String basePath = null;
Expand All @@ -146,6 +147,53 @@ public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) {
return basePath;
}

/**
* Returns the <code>defaultName</code> value declared in the @CheckedTemplate annotation.
* <code>
*
* @param checkedTemplateAnnotation the CheckedTemplate annotation.
* @return the <code>basePath</code> value declared in the @CheckedTemplate annotation
* @CheckedTemplate(defaultName="somewhere") </code>
*/
public static TemplateNameStrategy getDefaultName(PsiAnnotation checkedTemplateAnnotation) {
TemplateNameStrategy templateNameStrategy = TemplateNameStrategy.ELEMENT_NAME;
try {
for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) {
if (CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME.equalsIgnoreCase(pair.getAttributeName())) {
if (pair.getValue() != null
&& pair.getValue().getReference() != null
&& pair.getValue().getReference().resolve() != null &&
pair.getValue().getReference().resolve() instanceof PsiField field) {
Object value = field.computeConstantValue();
if (value != null) {
templateNameStrategy = getDefaultName(value.toString());
}
}
}
}
} catch (ProcessCanceledException e) {
//Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility
//TODO delete block when minimum required version is 2024.2
throw e;
} catch (IndexNotReadyException | CancellationException e) {
throw e;
} catch (Exception e) {
// Do nothing
}
return templateNameStrategy;
}

private static TemplateNameStrategy getDefaultName(String defaultName) {
switch (defaultName) {
case CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_HYPHENATED_ELEMENT_NAME:
return TemplateNameStrategy.HYPHENATED_ELEMENT_NAME;

case CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME_UNDERSCORED_ELEMENT_NAME:
return TemplateNameStrategy.UNDERSCORED_ELEMENT_NAME;
}
return TemplateNameStrategy.ELEMENT_NAME;
}

/**
* Collect data model template from @CheckedTemplate.
*
Expand All @@ -155,8 +203,14 @@ public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) {
* @param templates the data model templates to update with collect of template.
* @param monitor the progress monitor.
*/
private static void collectDataModelTemplateForCheckedTemplate(PsiClass type, String templatesBaseDir, String basePath, boolean ignoreFragments, ITypeResolver typeResolver,
List<DataModelTemplate<DataModelParameter>> templates, ProgressIndicator monitor) {
private static void collectDataModelTemplateForCheckedTemplate(PsiClass type,
String templatesBaseDir,
String basePath,
boolean ignoreFragments,
TemplateNameStrategy templateNameStrategy,
ITypeResolver typeResolver,
List<DataModelTemplate<DataModelParameter>> templates,
ProgressIndicator monitor) {
boolean innerClass = type.getContainingClass() != null;
String className = !innerClass ? null
: PsiTypeUtils.getSimpleClassName(type.getContainingFile().getName());
Expand All @@ -166,7 +220,7 @@ private static void collectDataModelTemplateForCheckedTemplate(PsiClass type, St
PsiMethod[] methods = type.getMethods();
for (PsiMethod method : methods) {
// src/main/resources/templates/${className}/${methodName}.qute.html
TemplatePathInfo templatePathInfo = getTemplatePath(templatesBaseDir, basePath, className, method.getName(), ignoreFragments);
TemplatePathInfo templatePathInfo = getTemplatePath(templatesBaseDir, basePath, className, method.getName(), ignoreFragments, templateNameStrategy);

// Get or create template
String templateUri = templatePathInfo.getTemplateUri();
Expand Down Expand Up @@ -234,7 +288,11 @@ public static void collectParameters(PsiMethod method, ITypeResolver typeResolve
for (int i = 0; i < parameters.getParametersCount(); i++) {
DataModelParameter parameter = createParameterDataModel(parameters.getParameter(i),
varargs && i == parameters.getParametersCount() - 1, typeResolver);
templateOrFragment.getParameters().add(parameter);
if (templateOrFragment.getParameter(parameter.getKey()) == null) {
// Add parameter if it doesn't exist
// to avoid parameters duplication
templateOrFragment.getParameters().add(parameter);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.redhat.devtools.intellij.qute.psi.template.datamodel.SearchContext;
import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils;

import com.redhat.devtools.intellij.qute.psi.utils.TemplateNameStrategy;
import com.redhat.qute.commons.datamodel.DataModelParameter;
import com.redhat.qute.commons.datamodel.DataModelTemplate;

Expand Down Expand Up @@ -106,7 +107,10 @@ private static AnnotationLocationSupport getAnnotationLocationSupport(PsiFile co
}

private static void collectDataModelTemplateForTemplateField(PsiField field,
List<DataModelTemplate<DataModelParameter>> templates, String relativeTemplateBaseDir, String location, ProgressIndicator monitor) {
List<DataModelTemplate<DataModelParameter>> templates,
String relativeTemplateBaseDir,
String location,
ProgressIndicator monitor) {
DataModelTemplate<DataModelParameter> template = createTemplateDataModel(field, relativeTemplateBaseDir, location, monitor);
templates.add(template);
}
Expand All @@ -117,7 +121,7 @@ private static DataModelTemplate<DataModelParameter> createTemplateDataModel(Psi
String location = locationFromConstructorParameter != null ? locationFromConstructorParameter : getLocation(field);
String fieldName = field.getName();
// src/main/resources/templates/${methodName}.qute.html
String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, location != null ? location : fieldName, true).getTemplateUri();
String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, location != null ? location : fieldName, true, TemplateNameStrategy.ELEMENT_NAME).getTemplateUri();

// Create template data model with:
// - template uri : Qute template file which must be bind with data model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.redhat.devtools.intellij.qute.psi.template.datamodel.AbstractInterfaceImplementationDataModelProvider;
import com.redhat.devtools.intellij.qute.psi.template.datamodel.SearchContext;
import com.redhat.devtools.intellij.qute.psi.utils.PsiTypeUtils;
import com.redhat.devtools.intellij.qute.psi.utils.TemplateNameStrategy;
import com.redhat.qute.commons.datamodel.DataModelParameter;
import com.redhat.qute.commons.datamodel.DataModelTemplate;

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

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

// Create template data model with:
// - template uri : Qute template file which must be bind with data model.
Expand Down
Loading

0 comments on commit f55035d

Please sign in to comment.