From 8bfdfd318396519a78ef7d6101ed6379b9a338ca Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 29 Apr 2022 16:40:46 +0200 Subject: [PATCH] Provide qute.nativeImages.enabled setting Fixes #629 Signed-off-by: azerr --- .../qute/commons/ResolvedJavaTypeInfo.java | 25 +++ .../qute/commons/TemplateDataAnnotation.java | 43 ++++ .../qute/jdt/QuteSupportForTemplate.java | 40 +++- .../qute/jdt/internal/QuteJavaConstants.java | 21 +- .../qute/commons/ResolvedJavaTypeInfo.java | 25 +++ .../qute/commons/TemplateDataAnnotation.java | 43 ++++ .../redhat/qute/ls/QuteLanguageServer.java | 2 +- .../TemplateFileTextDocumentService.java | 6 +- .../redhat/qute/project/JavaMemberResult.java | 47 +++++ .../qute/project/QuteProjectRegistry.java | 125 ++++++++++-- .../project/datamodel/JavaDataModelCache.java | 9 +- .../redhat/qute/services/QuteCompletions.java | 20 +- .../redhat/qute/services/QuteDiagnostics.java | 115 +++++++---- .../qute/services/QuteLanguageService.java | 30 +-- .../QuteCompletionsForExpression.java | 192 ++++++++++-------- .../services/diagnostics/QuteErrorCode.java | 15 +- .../redhat/qute/settings/BaseSettings.java | 12 ++ .../settings/QuteGeneralClientSettings.java | 84 +++++++- .../settings/QuteNativeImagesSettings.java | 109 ++++++++++ .../test/java/com/redhat/qute/QuteAssert.java | 13 +- 20 files changed, 800 insertions(+), 176 deletions(-) create mode 100644 qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeImagesSettings.java diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java index 713ea9b86..8317be15f 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java @@ -40,6 +40,8 @@ public class ResolvedJavaTypeInfo extends JavaTypeInfo { private Boolean isIterable; + private List templateDataAnnotations; + /** * Returns list of extended types. * @@ -178,6 +180,28 @@ private synchronized boolean computeIsIterable() { return iterable; } + /** + * Set the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @param templateDataAnnotations the list of '@io.quarkus.qute.TemplateData' + * annotations for the Java type. + */ + public void setTemplateDataAnnotations(List templateDataAnnotations) { + this.templateDataAnnotations = templateDataAnnotations; + } + + /** + * Returns the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @return the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + */ + public List getTemplateDataAnnotations() { + return templateDataAnnotations; + } + @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this); @@ -185,6 +209,7 @@ public String toString() { b.add("signature", this.getSignature()); b.add("iterableOf", this.getIterableOf()); b.add("iterableType", this.getIterableType()); + b.add("templateDataAnnotations", this.getTemplateDataAnnotations()); return b.toString(); } diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java new file mode 100644 index 000000000..cecb84bd5 --- /dev/null +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.commons; + +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * '@io.quarkus.qute.TemplateData' annotation. + * + * @author Angelo ZERR + * + */ +public class TemplateDataAnnotation { + + /** + * If set to true do not automatically analyze superclasses. + */ + private Boolean ignoreSuperclasses; + + public boolean isIgnoreSuperclasses() { + return ignoreSuperclasses != null && ignoreSuperclasses.booleanValue(); + } + + public void setIgnoreSuperclasses(Boolean ignoreSuperclasses) { + this.ignoreSuperclasses = ignoreSuperclasses; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("ignoreSuperclasses", this.isIgnoreSuperclasses()); + return b.toString(); + } +} diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java index d7c784f0c..3145b8e68 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java @@ -26,6 +26,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IField; @@ -54,12 +55,14 @@ import com.redhat.qute.commons.QuteProjectParams; import com.redhat.qute.commons.QuteResolvedJavaTypeParams; import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.TemplateDataAnnotation; import com.redhat.qute.commons.datamodel.DataModelParameter; import com.redhat.qute.commons.datamodel.DataModelProject; import com.redhat.qute.commons.datamodel.DataModelTemplate; import com.redhat.qute.commons.datamodel.QuteDataModelProjectParams; import com.redhat.qute.commons.usertags.QuteUserTagParams; import com.redhat.qute.commons.usertags.UserTagInfo; +import com.redhat.qute.jdt.internal.QuteJavaConstants; import com.redhat.qute.jdt.internal.resolver.AbstractTypeResolver; import com.redhat.qute.jdt.internal.resolver.ClassFileTypeResolver; import com.redhat.qute.jdt.internal.resolver.CompilationUnitTypeResolver; @@ -67,6 +70,7 @@ import com.redhat.qute.jdt.internal.template.JavaTypesSearch; import com.redhat.qute.jdt.internal.template.QuarkusIntegrationForQute; import com.redhat.qute.jdt.internal.template.TemplateDataSupport; +import com.redhat.qute.jdt.utils.AnnotationUtils; import com.redhat.qute.jdt.utils.IJDTUtils; import com.redhat.qute.jdt.utils.JDTQuteProjectUtils; @@ -352,7 +356,11 @@ public ResolvedJavaTypeInfo getResolvedJavaType(QuteResolvedJavaTypeParams param ITypeResolver typeResolver = createTypeResolver(type); - // 1) Collect fields + // 1) Collect annotations + // - @TemplateData + List templateDataAnnotations = collectTemplateDataAnnotations(type); + + // 2) Collect fields List fieldsInfo = new ArrayList<>(); // Standard fields @@ -376,7 +384,7 @@ public ResolvedJavaTypeInfo getResolvedJavaType(QuteResolvedJavaTypeParams param } } - // 2) Collect methods + // 3) Collect methods List methodsInfo = new ArrayList<>(); Map invalidMethods = new HashMap<>(); IMethod[] methods = type.getMethods(); @@ -409,7 +417,7 @@ public ResolvedJavaTypeInfo getResolvedJavaType(QuteResolvedJavaTypeParams param } } else { // ex : String implements CharSequence, .... - ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(monitor); + ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(monitor); IType[] allSuperTypes = typeHierarchy.getSupertypes(type); extendedTypes = Stream.of(allSuperTypes) // .map(superType -> superType.getFullyQualifiedName()) // @@ -427,9 +435,35 @@ public ResolvedJavaTypeInfo getResolvedJavaType(QuteResolvedJavaTypeParams param resolvedType.setMethods(methodsInfo); resolvedType.setInvalidMethods(invalidMethods); resolvedType.setExtendedTypes(extendedTypes); + resolvedType.setTemplateDataAnnotations(templateDataAnnotations); return resolvedType; } + private static List collectTemplateDataAnnotations(IType type) throws JavaModelException { + List templateDataAnnotations = null; + IAnnotation[] annotations = type.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (AnnotationUtils.isMatchAnnotation(annotation, QuteJavaConstants.TEMPLATE_DATA_ANNOTATION)) { + if (templateDataAnnotations == null) { + templateDataAnnotations = new ArrayList<>(); + } + templateDataAnnotations.add(createTemplateData(annotation)); + } + } + return templateDataAnnotations; + } + + private static TemplateDataAnnotation createTemplateData(IAnnotation templateDataAnnotation) + throws JavaModelException { + TemplateDataAnnotation templateData = new TemplateDataAnnotation(); + String ignoreSuperclasses = AnnotationUtils.getAnnotationMemberValue(templateDataAnnotation, + QuteJavaConstants.TEMPLATE_DATA_ANNOTATION_IGNORE_SUPER_CLASSES); + if ("true".equals(ignoreSuperclasses)) { + templateData.setIgnoreSuperclasses(Boolean.TRUE); + } + return templateData; + } + private String getFullQualifiedName(IProgressMonitor monitor, IJavaProject javaProject, String name) throws JavaModelException { if (name.indexOf('.') != -1) { diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java index a3e61b958..3948ad311 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java @@ -21,21 +21,32 @@ public class QuteJavaConstants { public static final String JAVAX_INJECT_NAMED_ANNOTATION = "javax.inject.Named"; + public static final String LOCATION_ANNOTATION = "io.quarkus.qute.Location"; + + public static final String TEMPLATE_CLASS = "io.quarkus.qute.Template"; + + public static final String ENGINE_BUILDER_CLASS = "io.quarkus.qute.EngineBuilder"; + + public static final String VALUE_ANNOTATION_NAME = "value"; + + // @CheckedTemplate + public static final String CHECKED_TEMPLATE_ANNOTATION = "io.quarkus.qute.CheckedTemplate"; public static final String OLD_CHECKED_TEMPLATE_ANNOTATION = "io.quarkus.qute.api.CheckedTemplate"; + // @TemplateExtension + public static final String TEMPLATE_EXTENSION_ANNOTATION = "io.quarkus.qute.TemplateExtension"; public static final String TEMPLATE_EXTENSION_ANNOTATION_NAMESPACE = "namespace"; public static final String TEMPLATE_EXTENSION_ANNOTATION_MATCH_NAME = "matchName"; - - public static final String LOCATION_ANNOTATION = "io.quarkus.qute.Location"; - public static final String TEMPLATE_CLASS = "io.quarkus.qute.Template"; + // @TemplateData - public static final String ENGINE_BUILDER_CLASS = "io.quarkus.qute.EngineBuilder"; + public static final String TEMPLATE_DATA_ANNOTATION = "io.quarkus.qute.TemplateData"; + + public static final String TEMPLATE_DATA_ANNOTATION_IGNORE_SUPER_CLASSES = "ignoreSuperclasses"; - public static final String VALUE_ANNOTATION_NAME = "value"; } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java index 713ea9b86..8317be15f 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java @@ -40,6 +40,8 @@ public class ResolvedJavaTypeInfo extends JavaTypeInfo { private Boolean isIterable; + private List templateDataAnnotations; + /** * Returns list of extended types. * @@ -178,6 +180,28 @@ private synchronized boolean computeIsIterable() { return iterable; } + /** + * Set the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @param templateDataAnnotations the list of '@io.quarkus.qute.TemplateData' + * annotations for the Java type. + */ + public void setTemplateDataAnnotations(List templateDataAnnotations) { + this.templateDataAnnotations = templateDataAnnotations; + } + + /** + * Returns the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @return the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + */ + public List getTemplateDataAnnotations() { + return templateDataAnnotations; + } + @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this); @@ -185,6 +209,7 @@ public String toString() { b.add("signature", this.getSignature()); b.add("iterableOf", this.getIterableOf()); b.add("iterableType", this.getIterableType()); + b.add("templateDataAnnotations", this.getTemplateDataAnnotations()); return b.toString(); } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java new file mode 100644 index 000000000..cecb84bd5 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.commons; + +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * '@io.quarkus.qute.TemplateData' annotation. + * + * @author Angelo ZERR + * + */ +public class TemplateDataAnnotation { + + /** + * If set to true do not automatically analyze superclasses. + */ + private Boolean ignoreSuperclasses; + + public boolean isIgnoreSuperclasses() { + return ignoreSuperclasses != null && ignoreSuperclasses.booleanValue(); + } + + public void setIgnoreSuperclasses(Boolean ignoreSuperclasses) { + this.ignoreSuperclasses = ignoreSuperclasses; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("ignoreSuperclasses", this.isIgnoreSuperclasses()); + return b.toString(); + } +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java index a9e0b97db..3c0e0269b 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java @@ -166,7 +166,7 @@ public synchronized void updateSettings(Object initializationOptionsSettings) { // Some inlay hint settings changed, ask the client to refresh all inlay hints. getLanguageClient().refreshInlayHints(); } - if (result.isValidationSettingsChanged()) { + if (result.isValidationSettingsChanged() || result.isNativeImagesSettingsChanged()) { // Some validation settings changed textDocumentService.validationSettingsChanged(); } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java index 23db65a0b..2a367748f 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java @@ -121,7 +121,8 @@ public CompletableFuture, CompletionList>> completio (cancelChecker, template) -> { return getQuteLanguageService() .doComplete(template, params.getPosition(), sharedSettings.getCompletionSettings(), - sharedSettings.getFormattingSettings(), cancelChecker) // + sharedSettings.getFormattingSettings(), sharedSettings.getNativeImagesSettings(), + cancelChecker) // .thenApply(list -> { return Either.forRight(list); }); @@ -283,7 +284,8 @@ private void triggerValidationFor(QuteTextDocument document) { ResolvingJavaTypeContext resolvingJavaTypeContext = new ResolvingJavaTypeContext(template, quteLanguageServer.getDataModelCache()); List diagnostics = getQuteLanguageService().doDiagnostics(template, - getSharedSettings().getValidationSettings(template.getUri()), resolvingJavaTypeContext, + getSharedSettings().getValidationSettings(template.getUri()), + getSharedSettings().getNativeImagesSettings(), resolvingJavaTypeContext, () -> template.checkCanceled()); // Diagnostics has been collected, before diagnostics publishing, check if the diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/JavaMemberResult.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/JavaMemberResult.java index 2d54ee59d..f7971e7ac 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/JavaMemberResult.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/JavaMemberResult.java @@ -24,6 +24,9 @@ public class JavaMemberResult { private JavaMemberInfo member; private boolean matchParameters; private boolean matchVirtualMethod; + private boolean supportNativeImages; + + private boolean fromSuperClasses; /** * Returns the Java member which matches the part name and null otherwise. @@ -87,4 +90,48 @@ public void setMatchVirtualMethod(boolean matchVirtualMethod) { this.matchVirtualMethod = matchVirtualMethod; } + /** + * Returns true if the founded Java member can support native images context and + * false otherwise. + * + * @return true if the founded Java member can support native images context and + * false otherwise. + */ + public boolean isSupportNativeImages() { + return supportNativeImages; + } + + /** + * Set true if the founded Java member can support native images context and + * false otherwise. + * + * @param supportNativeImages true if the founded Java member can support native + * images context and false otherwise. + */ + public void setSupportNativeImages(boolean supportNativeImages) { + this.supportNativeImages = supportNativeImages; + } + + /** + * Returns true if the founded Java member comes from a super class and false + * otherwise. + * + * @return true if the founded Java member comes from a super class and false + * otherwise. + */ + public boolean isFromSuperClasses() { + return fromSuperClasses; + } + + /** + * Set true if the founded Java member comes from a super class and false + * otherwise. + * + * @param fromSuperClasses true if the founded Java member comes from a super + * class and false otherwise. + */ + public void setFromSuperClasses(boolean fromSuperClasses) { + this.fromSuperClasses = fromSuperClasses; + } + } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java index 5cab20b03..fe5cac7de 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java @@ -62,6 +62,7 @@ import com.redhat.qute.project.datamodel.resolvers.TypeValueResolver; import com.redhat.qute.project.datamodel.resolvers.ValueResolver; import com.redhat.qute.project.datamodel.resolvers.ValueResolversRegistry; +import com.redhat.qute.settings.QuteNativeImagesSettings; import com.redhat.qute.utils.StringUtils; /** @@ -292,10 +293,14 @@ private CompletableFuture> getMethodValueResolvers(Str public CompletableFuture getDataModelTemplate(Template template) { String projectUri = template.getProjectUri(); if (StringUtils.isEmpty(projectUri)) { - // The project uri is not already get (it occurs when Qute template is opened and the project information takes some times). + // The project uri is not already get (it occurs when Qute template is opened + // and the project information takes some times). // Load the project information and call the data model. return template.getProjectFuture() // .thenCompose(project -> { + if (project == null) { + return null; + } return getDataModelTemplate(template, project.getUri()); }); } @@ -350,6 +355,69 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, String property, if (baseType == null) { return null; } + JavaMemberInfo member = findPropertyWithJavaReflection(baseType, property, projectUri); + if (member != null) { + return member; + } + return findValueResolver(baseType, property, projectUri); + } + + public JavaMemberResult findProperty(ResolvedJavaTypeInfo baseType, String property, boolean nativeImages, + String projectUri) { + JavaMemberResult result = new JavaMemberResult(); + + if (!nativeImages) { + + // Find member with Java reflection. + JavaMemberInfo member = findPropertyWithJavaReflection(baseType, property, projectUri); + if (member != null) { + result.setFromSuperClasses(!baseType.equals(member.getJavaTypeInfo())); + result.setSupportNativeImages(QuteNativeImagesSettings.canSupportNativeImages(baseType)); + result.setMember(member); + return result; + } + + // Find member with value resolvers. + member = findValueResolver(baseType, property, projectUri); + if (member != null) { + result.setSupportNativeImages(true); + result.setMember(member); + } + return result; + } + + // Find member with value resolvers. + JavaMemberInfo member = findValueResolver(baseType, property, projectUri); + if (member != null) { + result.setSupportNativeImages(true); + result.setMember(member); + return result; + } + + // Find member with Java reflection. + member = findPropertyWithJavaReflection(baseType, property, projectUri); + if (member != null) { + result.setFromSuperClasses(!baseType.equals(member.getJavaTypeInfo())); + result.setSupportNativeImages(QuteNativeImagesSettings.canSupportNativeImages(baseType)); + result.setMember(member); + return result; + } + return result; + } + + /** + * Returns the Java member from the given base type wich matches the given + * property by using Java reflection and null otherwise. + * + * @param baseType the Java type. + * @param property the property member. + * @param projectUri the project Uri. + * + * @return the Java member from the given base type wich matches the given + * property by using Java reflection and null otherwise. + */ + private JavaMemberInfo findPropertyWithJavaReflection(ResolvedJavaTypeInfo baseType, String property, + String projectUri) { if (baseType.isIterable() && !baseType.isArray()) { // Expression uses iterable type // {@java.util.List @@ -375,20 +443,15 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, String property, for (String superType : baseType.getExtendedTypes()) { ResolvedJavaTypeInfo resolvedSuperType = resolveJavaType(superType, projectUri).getNow(null); if (resolvedSuperType != null) { - JavaMemberInfo superMemberInfo = findMember(resolvedSuperType, property, projectUri); + JavaMemberInfo superMemberInfo = findPropertyWithJavaReflection(resolvedSuperType, property, + projectUri); if (superMemberInfo != null) { return superMemberInfo; } } } } - // Search in value resolver - String literalType = LiteralSupport.getLiteralJavaType(property); - if (literalType != null) { - // ex : @java.lang.Integer(base : T[]) : T (see qute-resolvers.jsonc) - property = "@" + literalType; - } - return findValueResolver(property, baseType, projectUri); + return null; } /** @@ -462,6 +525,7 @@ protected static JavaMethodInfo findMethod(ResolvedJavaTypeInfo baseType, String * @param namespace the namespace part and null otherwise. * @param methodName the method name to search. * @param parameterTypes the parameter types of the method to search. + * @param nativeImages the native images context. * @param projectUri the project Uri. * * @return the Java method from the given Java type baseType which @@ -469,27 +533,42 @@ protected static JavaMethodInfo findMethod(ResolvedJavaTypeInfo baseType, String * parameter types parameterTypes. */ public JavaMemberResult findMethod(ResolvedJavaTypeInfo baseType, String namespace, String methodName, - List parameterTypes, String projectUri) { + List parameterTypes, boolean nativeImages, String projectUri) { // Search in the java root type JavaMemberResult result = new JavaMemberResult(); - if (baseType != null) { + + if (!nativeImages && baseType != null) { + // In NO native images context, search with Java reflection at the begin if (findMethod(baseType, methodName, parameterTypes, result, projectUri)) { + result.setSupportNativeImages(QuteNativeImagesSettings.canSupportNativeImages(baseType)); return result; } + } - // Search in value resolvers + // Search in template extension value resolvers retrieved by @TemplateExtension + List dynamicResolvers = getMethodValueResolvers(projectUri).getNow(null); + if (findMethodResolver(baseType, namespace, methodName, parameterTypes, dynamicResolvers, result, projectUri)) { + result.setSupportNativeImages(true); + return result; + } + + if (baseType != null) { // Search in static value resolvers (ex : orEmpty, take, etc) List staticResolvers = valueResolversRegistry.getResolvers(); if (findMethodResolver(baseType, null, methodName, parameterTypes, staticResolvers, result, projectUri)) { + result.setSupportNativeImages(true); return result; } - } - // Search in template extension value resolvers retrieved by @TemplateExtension - List dynamicResolvers = getMethodValueResolvers(projectUri).getNow(null); - if (findMethodResolver(baseType, namespace, methodName, parameterTypes, dynamicResolvers, result, projectUri)) { - return result; + if (nativeImages) { + // In native images context, search with Java reflection at the end + if (findMethod(baseType, methodName, parameterTypes, result, projectUri)) { + result.setSupportNativeImages(QuteNativeImagesSettings.canSupportNativeImages(baseType)); + return result; + } + } } + return result; } @@ -526,6 +605,7 @@ private boolean findMethod(ResolvedJavaTypeInfo baseType, String methodName, ResolvedJavaTypeInfo resolvedSuperType = resolveJavaType(superType, projectUri).getNow(null); if (resolvedSuperType != null) { if (findMethod(resolvedSuperType, methodName, parameterTypes, result, projectUri)) { + result.setFromSuperClasses(true); return true; } } @@ -623,9 +703,14 @@ private static boolean isEmpty(String value) { return value == null || value.isEmpty(); } - public MethodValueResolver findValueResolver(String property, ResolvedJavaTypeInfo resolvedType, - String projectUri) { - List resolvers = getResolversFor(resolvedType, projectUri); + public MethodValueResolver findValueResolver(ResolvedJavaTypeInfo baseType, String property, String projectUri) { + // Search in value resolver + String literalType = LiteralSupport.getLiteralJavaType(property); + if (literalType != null) { + // ex : @java.lang.Integer(base : T[]) : T (see qute-resolvers.jsonc) + property = "@" + literalType; + } + List resolvers = getResolversFor(baseType, projectUri); for (MethodValueResolver resolver : resolvers) { if (isMatchMethod(resolver, property)) { return resolver; diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java index 699667a72..2d856b266 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java @@ -257,6 +257,11 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, String property, return projectRegistry.findMember(baseType, property, projectUri); } + public JavaMemberResult findProperty(Part part, ResolvedJavaTypeInfo baseType, + boolean nativeImages, String projectUri) { + return projectRegistry.findProperty(baseType, part.getPartName(), nativeImages, projectUri); + } + public boolean hasNamespace(String namespace, String projectUri) { return projectRegistry.hasNamespace(namespace, projectUri); } @@ -275,8 +280,8 @@ public CompletableFuture getNamespaceResolverInfo(String } public JavaMemberResult findMethod(ResolvedJavaTypeInfo baseType, String namespace, String methodName, - List parameterTypes, String projectUri) { - return projectRegistry.findMethod(baseType, namespace, methodName, parameterTypes, projectUri); + List parameterTypes, boolean nativeImages, String projectUri) { + return projectRegistry.findMethod(baseType, namespace, methodName, parameterTypes, nativeImages, projectUri); } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java index a8056bf6e..4361ed8bf 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java @@ -40,6 +40,7 @@ import com.redhat.qute.services.completions.QuteCompletionsForSnippets; import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; +import com.redhat.qute.settings.QuteNativeImagesSettings; /** * The Qute completions @@ -74,16 +75,17 @@ public QuteCompletions(JavaDataModelCache javaCache, SnippetRegistryProvider doComplete(Template template, Position position, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { + QuteNativeImagesSettings nativeImagesSettings, CancelChecker cancelChecker) { CompletionRequest completionRequest = null; try { completionRequest = new CompletionRequest(template, position, completionSettings, formattingSettings); @@ -112,7 +114,7 @@ public CompletableFuture doComplete(Template template, Position expression = ((Part) node).getParent().getParent(); } return completionForExpression.doCompleteExpression(completionRequest, expression, nodeExpression, template, - offset, completionSettings, formattingSettings, cancelChecker); + offset, completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } else if (node.getKind() == NodeKind.Text) { // The completion is triggered in text node (before node) Section parent = node.getParentSection(); @@ -149,7 +151,7 @@ public CompletableFuture doComplete(Template template, Position if (nbBrackets % 2 != 0) { // The completion is triggered in text node after bracket '{' character return completionForExpression.doCompleteExpression(completionRequest, null, node, template, offset, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } return EMPTY_FUTURE_COMPLETION; } @@ -165,7 +167,7 @@ public CompletableFuture doComplete(Template template, Position if (parameter.isAfterAssign(offset)) { // {# let name=| return completionForExpression.doCompleteExpression(completionRequest, null, null, template, offset, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } } return collectSnippetSuggestions(completionRequest); diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java index a9f198329..eab964013 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java @@ -66,6 +66,7 @@ import com.redhat.qute.services.diagnostics.DiagnosticDataFactory; import com.redhat.qute.services.diagnostics.QuteDiagnosticsForSyntax; import com.redhat.qute.services.diagnostics.QuteErrorCode; +import com.redhat.qute.settings.QuteNativeImagesSettings; import com.redhat.qute.settings.QuteValidationSettings; import com.redhat.qute.utils.QutePositionUtility; import com.redhat.qute.utils.StringUtils; @@ -148,13 +149,15 @@ public QuteDiagnostics(JavaDataModelCache javaCache) { /** * Validate the given Qute template. * - * @param template the Qute template. - * @param validationSettings the validation settings. - * @param cancelChecker the cancel checker. + * @param template the Qute template. + * @param validationSettings the validation settings. + * @param nativeImagesSettings the native images settings. + * @param cancelChecker the cancel checker. * @return the result of the validation. */ public List doDiagnostics(Template template, QuteValidationSettings validationSettings, - ResolvingJavaTypeContext resolvingJavaTypeContext, CancelChecker cancelChecker) { + QuteNativeImagesSettings nativeImagesSettings, ResolvingJavaTypeContext resolvingJavaTypeContext, + CancelChecker cancelChecker) { cancelChecker.checkCanceled(); if (validationSettings == null) { validationSettings = QuteValidationSettings.DEFAULT; @@ -181,8 +184,8 @@ public List doDiagnostics(Template template, QuteValidationSettings } // Validate data model try { - validateDataModel(template, template, validationSettings, resolvingJavaTypeContext, new ResolutionContext(), - diagnostics); + validateDataModel(template, template, validationSettings, nativeImagesSettings, resolvingJavaTypeContext, + new ResolutionContext(), diagnostics); } catch (CancellationException e) { throw e; } catch (Exception e) { @@ -193,8 +196,8 @@ public List doDiagnostics(Template template, QuteValidationSettings } private void validateDataModel(Node parent, Template template, QuteValidationSettings validationSettings, - ResolvingJavaTypeContext resolvingJavaTypeContext, ResolutionContext currentContext, - List diagnostics) { + QuteNativeImagesSettings nativeImagesSettings, ResolvingJavaTypeContext resolvingJavaTypeContext, + ResolutionContext currentContext, List diagnostics) { ResolutionContext previousContext = currentContext; List children = parent.getChildren(); for (Node node : children) { @@ -251,7 +254,8 @@ private void validateDataModel(Node parent, Template template, QuteValidationSet if (expression != null) { // Validate object, property, method parts from the expression ResolvedJavaTypeInfo result = validateExpression(expression, section, template, - validationSettings, previousContext, resolvingJavaTypeContext, diagnostics); + validationSettings, nativeImagesSettings, previousContext, resolvingJavaTypeContext, + diagnostics); switch (section.getSectionKind()) { case FOR: case EACH: @@ -281,14 +285,14 @@ private void validateDataModel(Node parent, Template template, QuteValidationSet break; } case Expression: { - validateExpression((Expression) node, null, template, validationSettings, previousContext, - resolvingJavaTypeContext, diagnostics); + validateExpression((Expression) node, null, template, validationSettings, nativeImagesSettings, + previousContext, resolvingJavaTypeContext, diagnostics); break; } default: } - validateDataModel(node, template, validationSettings, resolvingJavaTypeContext, currentContext, - diagnostics); + validateDataModel(node, template, validationSettings, nativeImagesSettings, resolvingJavaTypeContext, + currentContext, diagnostics); } } @@ -376,8 +380,9 @@ private static void validateIncludeSection(IncludeSection includeSection, List diagnostics) { + QuteValidationSettings validationSettings, QuteNativeImagesSettings nativeImagesSettings, + ResolutionContext resolutionContext, ResolvingJavaTypeContext resolvingJavaTypeContext, + List diagnostics) { try { String projectUri = template.getProjectUri(); String literalJavaType = expression.getLiteralJavaType(); @@ -402,7 +407,8 @@ private ResolvedJavaTypeInfo validateExpression(Expression expression, Section o if (expressionChild.getKind() == NodeKind.ExpressionParts) { Parts parts = (Parts) expressionChild; resolvedJavaType = validateExpressionParts(parts, ownerSection, template, projectUri, - validationSettings, resolutionContext, resolvingJavaTypeContext, diagnostics); + validationSettings, nativeImagesSettings, resolutionContext, resolvingJavaTypeContext, + diagnostics); } } return resolvedJavaType; @@ -416,8 +422,9 @@ private ResolvedJavaTypeInfo validateExpression(Expression expression, Section o } private ResolvedJavaTypeInfo validateExpressionParts(Parts parts, Section ownerSection, Template template, - String projectUri, QuteValidationSettings validationSettings, ResolutionContext resolutionContext, - ResolvingJavaTypeContext resolvingJavaTypeContext, List diagnostics) { + String projectUri, QuteValidationSettings validationSettings, QuteNativeImagesSettings nativeImagesSettings, + ResolutionContext resolutionContext, ResolvingJavaTypeContext resolvingJavaTypeContext, + List diagnostics) { ResolvedJavaTypeInfo baseType = null; String namespace = null; for (int i = 0; i < parts.getChildCount(); i++) { @@ -477,7 +484,7 @@ private ResolvedJavaTypeInfo validateExpressionParts(Parts parts, Section ownerS } baseType = validateMemberPart(current, ownerSection, template, projectUri, validationSettings, - resolutionContext, baseType, iter, diagnostics, resolvingJavaTypeContext); + nativeImagesSettings, resolutionContext, baseType, iter, diagnostics, resolvingJavaTypeContext); break; } } @@ -629,19 +636,20 @@ private ResolvedJavaTypeInfo validateObjectPart(String namespace, ObjectPart obj * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, Template template, - String projectUri, QuteValidationSettings validationSettings, ResolutionContext resolutionContext, - ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iterableOfType, List diagnostics, - ResolvingJavaTypeContext resolvingJavaTypeContext) { + String projectUri, QuteValidationSettings validationSettings, QuteNativeImagesSettings nativeImagesSettings, + ResolutionContext resolutionContext, ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iterableOfType, + List diagnostics, ResolvingJavaTypeContext resolvingJavaTypeContext) { if (part.getPartKind() == PartKind.Method) { // Validate method part // ex : {foo.method(1, 2)} return validateMethodPart((MethodPart) part, ownerSection, template, projectUri, validationSettings, - resolutionContext, baseType, iterableOfType, diagnostics, resolvingJavaTypeContext); + nativeImagesSettings, resolutionContext, baseType, iterableOfType, diagnostics, + resolvingJavaTypeContext); } // Validate property part // ex : {foo.property} return validatePropertyPart((PropertyPart) part, ownerSection, template, projectUri, resolutionContext, - baseType, iterableOfType, diagnostics, resolvingJavaTypeContext); + baseType, iterableOfType, nativeImagesSettings, diagnostics, resolvingJavaTypeContext); } /** @@ -654,6 +662,7 @@ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, * @param resolutionContext the resolution context. * @param baseType the base object type. * @param iterableOfType the iterable of type. + * @param nativeImagesSettings * @param diagnostics the diagnostic list to fill. * @param resolvingJavaTypeContext the resolving Java type context. * @@ -661,12 +670,13 @@ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, */ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section ownerSection, Template template, String projectUri, ResolutionContext resolutionContext, ResolvedJavaTypeInfo baseType, - ResolvedJavaTypeInfo iterableOfType, List diagnostics, - ResolvingJavaTypeContext resolvingJavaTypeContext) { + ResolvedJavaTypeInfo iterableOfType, QuteNativeImagesSettings nativeImagesSettings, + List diagnostics, ResolvingJavaTypeContext resolvingJavaTypeContext) { if (baseType == null) { return null; } - JavaMemberInfo javaMember = javaCache.findMember(part, baseType, projectUri); + JavaMemberResult result = javaCache.findProperty(part, baseType, nativeImagesSettings.isEnabled(), projectUri); + JavaMemberInfo javaMember = result.getMember(); if (javaMember == null) { // ex : {@org.acme.Item item} // "{item.XXXX} @@ -675,9 +685,28 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own part.getPartName(), baseType.getSignature()); diagnostics.add(diagnostic); return null; + } else if (nativeImagesSettings.isEnabled()) { + if (!result.isSupportNativeImages()) { + Range range = QutePositionUtility.createRange(part); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.PropertyNotSupportedInNativeImages, part.getPartName(), baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + boolean ignoreSuperclasses = nativeImagesSettings.isIgnoreSuperclasses(baseType); + if (ignoreSuperclasses && result.isFromSuperClasses()) { + Range range = QutePositionUtility.createRange(part); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.SuperPropertyNotSupportedInNativeImages, part.getPartName(), + baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } } + String memberType = javaMember.resolveJavaElementType(iterableOfType); - return validateJavaTypePart(part, ownerSection, projectUri, diagnostics, resolvingJavaTypeContext, memberType, null); + return validateJavaTypePart(part, ownerSection, projectUri, diagnostics, resolvingJavaTypeContext, memberType, + null); } /** @@ -687,6 +716,7 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own * @param ownerSection the owner section and null otherwise. * @param template the template. * @param projectUri the project Uri. + * @param nativeImagesSettings * @param resolutionContext the resolution context. * @param baseType the base object type. * @param iterableOfType the iterable of type. @@ -696,9 +726,9 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section ownerSection, Template template, - String projectUri, QuteValidationSettings validationSettings, ResolutionContext resolutionContext, - ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iter, List diagnostics, - ResolvingJavaTypeContext resolvingJavaTypeContext) { + String projectUri, QuteValidationSettings validationSettings, QuteNativeImagesSettings nativeImagesSettings, + ResolutionContext resolutionContext, ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iter, + List diagnostics, ResolvingJavaTypeContext resolvingJavaTypeContext) { if (methodPart.isInfixNotation()) { // ex : foo or '1' // The method part used with infix notation must have one parameter @@ -718,8 +748,8 @@ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section o ResolvedJavaTypeInfo result = null; Expression expression = parameter.getJavaTypeExpression(); if (expression != null) { - result = validateExpression(expression, ownerSection, template, validationSettings, resolutionContext, - resolvingJavaTypeContext, diagnostics); + result = validateExpression(expression, ownerSection, template, validationSettings, + nativeImagesSettings, resolutionContext, resolvingJavaTypeContext, diagnostics); } if (result == null) { undefinedType = true; @@ -739,7 +769,8 @@ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section o // to the computed parameter types String methodName = methodPart.getPartName(); String namespace = methodPart.getNamespace(); - JavaMemberResult result = javaCache.findMethod(baseType, namespace, methodName, parameterTypes, projectUri); + JavaMemberResult result = javaCache.findMethod(baseType, namespace, methodName, parameterTypes, + nativeImagesSettings.isEnabled(), projectUri); JavaMethodInfo method = (JavaMethodInfo) result.getMember(); if (method == null) { QuteErrorCode errorCode = QuteErrorCode.UnknownMethod; @@ -772,6 +803,22 @@ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section o Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, errorCode, methodName, arg); diagnostics.add(diagnostic); return null; + } else if (nativeImagesSettings.isEnabled()) { + if (!result.isSupportNativeImages()) { + Range range = QutePositionUtility.createRange(methodPart); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.MethodNotSupportedInNativeImages, method.getName(), baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + boolean ignoreSuperclasses = nativeImagesSettings.isIgnoreSuperclasses(baseType); + if (ignoreSuperclasses && result.isFromSuperClasses()) { + Range range = QutePositionUtility.createRange(methodPart); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.SuperMethodNotSupportedInNativeImages, method.getName(), baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } } boolean matchVirtualMethod = result.isMatchVirtualMethod(); diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java index 83878c697..afff8654b 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java @@ -41,6 +41,7 @@ import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; import com.redhat.qute.settings.QuteInlayHintSettings; +import com.redhat.qute.settings.QuteNativeImagesSettings; import com.redhat.qute.settings.QuteValidationSettings; import com.redhat.qute.settings.SharedSettings; @@ -85,17 +86,19 @@ public QuteLanguageService(JavaDataModelCache javaCache) { /** * Returns completion list for the given position * - * @param template the Qute template - * @param position the position where completion was triggered - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. - * @param cancelChecker the cancel checker + * @param template the Qute template + * @param position the position where completion was triggered + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native image settings. + * @param cancelChecker the cancel checker * @return completion list for the given position */ public CompletableFuture doComplete(Template template, Position position, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { - return completions.doComplete(template, position, completionSettings, formattingSettings, cancelChecker); + QuteNativeImagesSettings nativeImagesSettings, CancelChecker cancelChecker) { + return completions.doComplete(template, position, completionSettings, formattingSettings, nativeImagesSettings, + cancelChecker); } public CompletableFuture> getCodeLens(Template template, SharedSettings settings, @@ -138,14 +141,17 @@ public List findSymbolInformations(Template template, CancelC /** * Validate the given Qute template. * - * @param template the Qute template. - * @param validationSettings the validation settings. - * @param cancelChecker the cancel checker. + * @param template the Qute template. + * @param validationSettings the validation settings. + * @param nativeImagesSettings the native image settings. + * @param cancelChecker the cancel checker. * @return the result of the validation. */ public List doDiagnostics(Template template, QuteValidationSettings validationSettings, - ResolvingJavaTypeContext resolvingJavaTypeFutures, CancelChecker cancelChecker) { - return diagnostics.doDiagnostics(template, validationSettings, resolvingJavaTypeFutures, cancelChecker); + QuteNativeImagesSettings nativeImagesSettings, ResolvingJavaTypeContext resolvingJavaTypeFutures, + CancelChecker cancelChecker) { + return diagnostics.doDiagnostics(template, validationSettings, nativeImagesSettings, resolvingJavaTypeFutures, + cancelChecker); } public List findReferences(Template template, Position position, ReferenceContext context, diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java index 65856aa9a..7c0e50c98 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java @@ -61,6 +61,7 @@ import com.redhat.qute.project.datamodel.resolvers.ValueResolver; import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; +import com.redhat.qute.settings.QuteNativeImagesSettings; import com.redhat.qute.utils.QutePositionUtility; import com.redhat.qute.utils.StringUtils; import com.redhat.qute.utils.UserTagUtils; @@ -87,31 +88,32 @@ public QuteCompletionsForExpression(QuteCompletionForTagSection completionForTag * Returns the completion result of the given offset inside the given * expression. * - * @param expression the expression where the completion has been - * triggered and null otherwise. - * @param nodeExpression the node expression where the completion has been - * triggered and null otherwise. - * @param template the owner template. - * @param offset the offset where the completion has been triggered. - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. - * - * @param cancelChecker the cancel checker. + * @param expression the expression where the completion has been + * triggered and null otherwise. + * @param nodeExpression the node expression where the completion has been + * triggered and null otherwise. + * @param template the owner template. + * @param offset the offset where the completion has been + * triggered. + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native images settings. + * @param cancelChecker the cancel checker. * * @return the completion result as future. */ public CompletableFuture doCompleteExpression(CompletionRequest completionRequest, Expression expression, Node nodeExpression, Template template, int offset, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { + QuteNativeImagesSettings nativeImagesSettings, CancelChecker cancelChecker) { if (nodeExpression == null) { // ex : { | } return doCompleteExpressionForObjectPart(completionRequest, expression, null, null, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } else if (expression == null) { // ex : {| return doCompleteExpressionForObjectPart(completionRequest, null, null, nodeExpression, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } if (nodeExpression.getKind() == NodeKind.ExpressionPart) { @@ -120,13 +122,13 @@ public CompletableFuture doCompleteExpression(CompletionRequest case Object: // ex : { ite|m } return doCompleteExpressionForObjectPart(null, expression, part.getNamespace(), part, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); case Property: { // ex : { item.n| } // ex : { item.n|ame } Parts parts = part.getParent(); return doCompleteExpressionForMemberPart(part, parts, template, false, completionSettings, - formattingSettings); + formattingSettings, nativeImagesSettings); } case Method: { // ex : { item.getN|ame() } @@ -135,12 +137,12 @@ public CompletableFuture doCompleteExpression(CompletionRequest if (methodPart.isInParameters(offset)) { // ex : { item.getName(|) } return doCompleteExpressionForObjectPart(null, expression, null, null, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } // ex : { item.getN|ame() } Parts parts = part.getParent(); return doCompleteExpressionForMemberPart(part, parts, template, methodPart.isInfixNotation(), - completionSettings, formattingSettings); + completionSettings, formattingSettings, nativeImagesSettings); } default: break; @@ -157,7 +159,7 @@ public CompletableFuture doCompleteExpression(CompletionRequest Parts parts = (Parts) nodeExpression; Part part = parts.getPartAt(offset + 1); return doCompleteExpressionForObjectPart(null, expression, parts.getNamespace(), part, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } case '.': { // ex : { item.| } @@ -166,7 +168,7 @@ public CompletableFuture doCompleteExpression(CompletionRequest Parts parts = (Parts) nodeExpression; Part part = parts.getPartAt(offset + 1); return doCompleteExpressionForMemberPart(part, parts, template, false, completionSettings, - formattingSettings); + formattingSettings, nativeImagesSettings); } case ' ': { // Infix notation @@ -180,12 +182,12 @@ public CompletableFuture doCompleteExpression(CompletionRequest && ((MethodPart) previousPart).isOperator()) { // ex : { item ?: |name } return doCompleteExpressionForObjectPart(null, expression, parts.getNamespace(), part, offset, - template, completionSettings, formattingSettings, cancelChecker); + template, completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } // ex : { item | } // ex : { item |name } return doCompleteExpressionForMemberPart(part, parts, template, true, completionSettings, - formattingSettings); + formattingSettings, nativeImagesSettings); } } } @@ -196,20 +198,21 @@ public CompletableFuture doCompleteExpression(CompletionRequest * Returns the completion result for Java fields, methods of the Java type * class, interface of the given part part * - * @param part the part. - * @param parts the owner parts. - * @param template the owner template. - * @param infixNotation true if the completion generates completion items - * for infix notation (with spaces) and false - * otherwise. - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. + * @param part the part. + * @param parts the owner parts. + * @param template the owner template. + * @param infixNotation true if the completion generates completion items + * for infix notation (with spaces) and false + * otherwise. + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native image settings. * * @return the completion list. */ private CompletableFuture doCompleteExpressionForMemberPart(Part part, Parts parts, Template template, boolean infixNotation, QuteCompletionSettings completionSettings, - QuteFormattingSettings formattingSettings) { + QuteFormattingSettings formattingSettings, QuteNativeImagesSettings nativeImagesSettings) { int start = part != null ? part.getStart() : parts.getEnd(); int end = part != null ? part.getEnd() : parts.getEnd(); String projectUri = template.getProjectUri(); @@ -230,13 +233,14 @@ private CompletableFuture doCompleteExpressionForMemberPart(Part return EMPTY_COMPLETION; } return doCompleteForJavaTypeMembers(resolvedIterableType, start, end, template, - infixNotation, completionSettings, formattingSettings); + infixNotation, completionSettings, formattingSettings, + nativeImagesSettings); }); } // Completion for member of the given Java class // ex : org.acme.Item CompletionList list = doCompleteForJavaTypeMembers(resolvedType, start, end, template, - infixNotation, completionSettings, formattingSettings); + infixNotation, completionSettings, formattingSettings, nativeImagesSettings); return CompletableFuture.completedFuture(list); }); @@ -246,43 +250,49 @@ private CompletableFuture doCompleteExpressionForMemberPart(Part * Returns the completion result for Java fields, methods of the given Java type * class, interface resolvedType * - * @param resolvedType the Java class, interface. - * @param start the part start index to replace. - * @param end the part end index to replace. - * @param template the owner Qute template. - * @param infixNotation true if the completion generates completion items - * for infix notation (with spaces) and false - * otherwise. - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. + * @param baseType the Java class, interface. + * @param start the part start index to replace. + * @param end the part end index to replace. + * @param template the owner Qute template. + * @param infixNotation true if the completion generates completion items + * for infix notation (with spaces) and false + * otherwise. + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native image settings. * * @return the completion list. */ - private CompletionList doCompleteForJavaTypeMembers(ResolvedJavaTypeInfo resolvedType, int start, int end, + private CompletionList doCompleteForJavaTypeMembers(ResolvedJavaTypeInfo baseType, int start, int end, Template template, boolean infixNotation, QuteCompletionSettings completionSettings, - QuteFormattingSettings formattingSettings) { + QuteFormattingSettings formattingSettings, QuteNativeImagesSettings nativeImagesSettings) { CompletionList list = new CompletionList(); list.setItems(new ArrayList<>()); Range range = QutePositionUtility.createRange(start, end, template); String projectUri = template.getProjectUri(); Set existingProperties = new HashSet<>(); + Set existingMethodSignatures = new HashSet<>(); - if (!infixNotation) { - // Completion for Java fields - fillCompletionFields(resolvedType, range, projectUri, existingProperties, list); - } + boolean ignoreSuperclasses = nativeImagesSettings.isIgnoreSuperclasses(baseType); + if (canSupportJavaReflection(baseType, nativeImagesSettings)) { + // Not in native images, fields and methods from Java reflection must be shown. - // Completion for Java methods - Set existingMethodSignatures = new HashSet<>(); - fillCompletionMethods(resolvedType, range, projectUri, infixNotation, completionSettings, formattingSettings, - existingProperties, existingMethodSignatures, list); + if (!infixNotation) { + // Completion for Java fields + fillCompletionFields(baseType, range, ignoreSuperclasses, projectUri, existingProperties, list); + } + + // Completion for Java methods + fillCompletionMethods(baseType, range, ignoreSuperclasses, projectUri, infixNotation, completionSettings, + formattingSettings, existingProperties, existingMethodSignatures, list); + } // Completion for virtual methods (from value resolvers) // - Static value resolvers (orEmpty, etc) // - Dynamic value resolvers (from @TemplateExtension) - List resolvers = javaCache.getResolversFor(resolvedType, projectUri); + List resolvers = javaCache.getResolversFor(baseType, projectUri); for (MethodValueResolver method : resolvers) { if (method.isValidName() && isValidInfixNotation(method, infixNotation)) { fillCompletionMethod(method, range, infixNotation, completionSettings, formattingSettings, list); @@ -291,19 +301,28 @@ private CompletionList doCompleteForJavaTypeMembers(ResolvedJavaTypeInfo resolve return list; } + private static boolean canSupportJavaReflection(ResolvedJavaTypeInfo baseType, + QuteNativeImagesSettings nativeImagesSettings) { + if (nativeImagesSettings.isEnabled()) { + return QuteNativeImagesSettings.canSupportNativeImages(baseType); + } + return true; + } + /** * Fill completion list list with the methods of the given Java * type resolvedType. * * @param resolvedType the Java type class, interface. * @param range the range. + * @param ignoreSuperclasses * @param projectUri the project Uri * @param existingProperties the existing properties and method, field * signature. * @param list the completion list. */ - private void fillCompletionFields(ResolvedJavaTypeInfo resolvedType, Range range, String projectUri, - Set existingProperties, CompletionList list) { + private void fillCompletionFields(ResolvedJavaTypeInfo resolvedType, Range range, boolean ignoreSuperclasses, + String projectUri, Set existingProperties, CompletionList list) { // Fill completion with Java type fields. for (JavaFieldInfo field : resolvedType.getFields()) { String fieldName = field.getName(); @@ -318,14 +337,17 @@ private void fillCompletionFields(ResolvedJavaTypeInfo resolvedType, Range range existingProperties.add(fieldName); } } - // Fill completion with extended Java type fields. - List extendedTypes = resolvedType.getExtendedTypes(); - if (extendedTypes != null) { - for (String extendedType : extendedTypes) { - ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) - .getNow(null); - if (resolvedExtendedType != null) { - fillCompletionFields(resolvedExtendedType, range, projectUri, existingProperties, list); + if (!ignoreSuperclasses) { + // Fill completion with extended Java type fields. + List extendedTypes = resolvedType.getExtendedTypes(); + if (extendedTypes != null) { + for (String extendedType : extendedTypes) { + ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) + .getNow(null); + if (resolvedExtendedType != null) { + fillCompletionFields(resolvedExtendedType, range, ignoreSuperclasses, projectUri, + existingProperties, list); + } } } } @@ -358,6 +380,7 @@ private static CompletionItem fillCompletionField(JavaFieldInfo field, String na * * @param resolvedType the Java type class, interface. * @param range the range. + * @param ignoreSuperclasses * @param projectUri the project Uri * @param infixNotation true if the completion generates completion items * for infix notation (with spaces) and false @@ -368,9 +391,10 @@ private static CompletionItem fillCompletionField(JavaFieldInfo field, String na * signature. * @param list the completion list. */ - private void fillCompletionMethods(ResolvedJavaTypeInfo resolvedType, Range range, String projectUri, - boolean infixNotation, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - Set existingProperties, Set existingMethodSignatures, CompletionList list) { + private void fillCompletionMethods(ResolvedJavaTypeInfo resolvedType, Range range, boolean ignoreSuperclasses, + String projectUri, boolean infixNotation, QuteCompletionSettings completionSettings, + QuteFormattingSettings formattingSettings, Set existingProperties, + Set existingMethodSignatures, CompletionList list) { for (JavaMethodInfo method : resolvedType.getMethods()) { if (isValidInfixNotation(method, infixNotation)) { String methodSignature = method.getSignature(); @@ -396,14 +420,18 @@ private void fillCompletionMethods(ResolvedJavaTypeInfo resolvedType, Range rang } } } - List extendedTypes = resolvedType.getExtendedTypes(); - if (extendedTypes != null) { - for (String extendedType : extendedTypes) { - ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) - .getNow(null); - if (resolvedExtendedType != null) { - fillCompletionMethods(resolvedExtendedType, range, projectUri, infixNotation, completionSettings, - formattingSettings, existingProperties, existingMethodSignatures, list); + + if (!ignoreSuperclasses) { + List extendedTypes = resolvedType.getExtendedTypes(); + if (extendedTypes != null) { + for (String extendedType : extendedTypes) { + ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) + .getNow(null); + if (resolvedExtendedType != null) { + fillCompletionMethods(resolvedExtendedType, range, ignoreSuperclasses, projectUri, + infixNotation, completionSettings, formattingSettings, existingProperties, + existingMethodSignatures, list); + } } } } @@ -501,7 +529,7 @@ private static String createMethodSnippet(JavaMethodInfo method, boolean infixNo private CompletableFuture doCompleteExpressionForObjectPart(CompletionRequest completionRequest, Expression expression, String namespace, Node part, int offset, Template template, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { + QuteNativeImagesSettings nativeImagesSettings, CancelChecker cancelChecker) { // Completion for root object int partStart = part != null && part.getKind() != NodeKind.Text ? part.getStart() : offset; int partEnd = part != null && part.getKind() != NodeKind.Text ? part.getEnd() : offset; @@ -523,7 +551,8 @@ private CompletableFuture doCompleteExpressionForObjectPart(Comp // Collect declared model inside section, let, etc Set existingVars = new HashSet<>(); doCompleteExpressionForObjectPartWithParentNodes(part, expression != null ? expression : part, range, - offset, template.getProjectUri(), existingVars, completionSettings, formattingSettings, list); + offset, template.getProjectUri(), existingVars, completionSettings, formattingSettings, + nativeImagesSettings, list); // Section tag if (completionRequest != null) { @@ -624,7 +653,8 @@ public void doCompleteNamespaceResolvers(String namespace, Template template, Ra private void doCompleteExpressionForObjectPartWithParentNodes(Node part, Node node, Range range, int offset, String projectUri, Set existingVars, QuteCompletionSettings completionSettings, - QuteFormattingSettings formattingSettings, CompletionList list) { + QuteFormattingSettings formattingSettings, QuteNativeImagesSettings nativeImagesSettings, + CompletionList list) { Section section = node != null ? node.getParentSection() : null; if (section == null) { return; @@ -728,9 +758,11 @@ private void doCompleteExpressionForObjectPartWithParentNodes(Node part, Node no ResolvedJavaTypeInfo withJavaTypeInfo = javaCache.resolveJavaType(object, projectUri) .getNow(null); if (withJavaTypeInfo != null) { - fillCompletionFields(withJavaTypeInfo, range, projectUri, existingVars, list); - fillCompletionMethods(withJavaTypeInfo, range, projectUri, false, completionSettings, - formattingSettings, existingVars, new HashSet<>(), list); + boolean ignoreSuperclasses = nativeImagesSettings.isIgnoreSuperclasses(withJavaTypeInfo); + fillCompletionFields(withJavaTypeInfo, range, ignoreSuperclasses, projectUri, existingVars, + list); + fillCompletionMethods(withJavaTypeInfo, range, ignoreSuperclasses, projectUri, false, + completionSettings, formattingSettings, existingVars, new HashSet<>(), list); } } break; @@ -739,7 +771,7 @@ private void doCompleteExpressionForObjectPartWithParentNodes(Node part, Node no } } doCompleteExpressionForObjectPartWithParentNodes(part, section, range, offset, projectUri, existingVars, - completionSettings, formattingSettings, list); + completionSettings, formattingSettings, nativeImagesSettings, list); } private void doCompleteExpressionForObjectPartWithParameterAlias(Template template, Range range, diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java index 2773c93f4..a304b3700 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java @@ -25,7 +25,7 @@ public enum QuteErrorCode implements IQuteErrorCode { // Error code for operator InvalidOperator("Invalid `{0}` operator for section `#{1}`. Allowed operators are `{2}`."), // - + // Error code for object, property, method parts UndefinedObject("`{0}` cannot be resolved to an object."), // UnknownType("`{0}` cannot be resolved to a type."), // @@ -38,10 +38,17 @@ public enum QuteErrorCode implements IQuteErrorCode { InvalidVirtualMethod( "The virtual method `{0}` in the type `{1}` is not applicable for the base object `{2}` type."), // InfixNotationParameterRequired("A parameter for the infix notation method `{0}` is required."), // - InvalidMethodInfixNotation("The method `{0}` cannot be used with infix notation, because it has not `1` parameter."), // - + InvalidMethodInfixNotation( + "The method `{0}` cannot be used with infix notation, because it has not `1` parameter."), // + MethodNotSupportedInNativeImages( + "Method `{0}` cannot be used in native images context. The `{1}` Java type should be annotated with `@io.quarkus.qute.TemplateData`."), // + SuperMethodNotSupportedInNativeImages( + "Super method `{0}` cannot be used in native images context. The `{1}` Java type is annotated with `@io.quarkus.qute.TemplateData#ignoreSuperclasses = true`."), // UnknownProperty("`{0}` cannot be resolved or is not a field of `{1}` Java type."), // - + PropertyNotSupportedInNativeImages( + "Property `{0}` cannot be used in native images context. The `{1}` Java type should be annotated with `@io.quarkus.qute.TemplateData`."), // + SuperPropertyNotSupportedInNativeImages( + "Super property `{0}` cannot be used in native images context. The `{1}` Java type is annotated with `@io.quarkus.qute.TemplateData#ignoreSuperclasses = true`."), // // Error code for #for / #each section IterationError("Iteration error: '{'{0}'}' resolved to [{1}] which is not iterable."), diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java index 17adc99e4..2e3e43390 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java @@ -25,10 +25,13 @@ public class BaseSettings { private final QuteValidationSettings validationSettings; + private final QuteNativeImagesSettings nativeImagesSettings; + public BaseSettings() { this.validationSettings = new QuteValidationSettings(); this.codeLensSettings = new QuteCodeLensSettings(); this.inlayHintSettings = new QuteInlayHintSettings(); + this.nativeImagesSettings = new QuteNativeImagesSettings(); } /** @@ -57,4 +60,13 @@ public QuteInlayHintSettings getInlayHintSettings() { public QuteValidationSettings getValidationSettings() { return validationSettings; } + + /** + * Returns the Qute native images settings. + * + * @return the Qute native images settings. + */ + public QuteNativeImagesSettings getNativeImagesSettings() { + return nativeImagesSettings; + } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java index c98ce842f..7dcba925a 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java @@ -40,10 +40,22 @@ public class QuteGeneralClientSettings { private QuteValidationSettings validation; + private QuteNativeImagesSettings nativeImages; + + /** + * Returns the code lens settings. + * + * @return the code lens settings. + */ public QuteCodeLensSettings getCodeLens() { return codeLens; } + /** + * Returns the inlay hint settings. + * + * @return the inlay hint settings. + */ public QuteInlayHintSettings getInlayHint() { return inlayHint; } @@ -57,6 +69,15 @@ public QuteValidationSettings getValidation() { return validation; } + /** + * Returns the native images settings. + * + * @return the native images settings. + */ + public QuteNativeImagesSettings getNativeImages() { + return nativeImages; + } + /** * Set the CodeLens setting. * @@ -66,6 +87,11 @@ public void setCodeLens(QuteCodeLensSettings codeLens) { this.codeLens = codeLens; } + /** + * Set the inlay hint settings. + * + * @param inlayHint the inlay hint settings. + */ public void setInlayHint(QuteInlayHintSettings inlayHint) { this.inlayHint = inlayHint; } @@ -79,6 +105,15 @@ public void setValidation(QuteValidationSettings validation) { this.validation = validation; } + /** + * Set the native images settings. + * + * @param nativeImages the native images settings. + */ + public void setNativeImages(QuteNativeImagesSettings nativeImages) { + this.nativeImages = nativeImages; + } + public Map getWorkspaceFolders() { return workspaceFolders; } @@ -109,11 +144,14 @@ public static class SettingsUpdateState { private final boolean inlayHintSettingsChanged; + private boolean nativeImagesSettingsChanged; + public SettingsUpdateState(boolean validationSettingsChanged, boolean codeLensSettingsChanged, - boolean inlayHintSettingsChanged) { + boolean inlayHintSettingsChanged, boolean nativeImagesSettingsChanged) { this.validationSettingsChanged = validationSettingsChanged; this.codeLensSettingsChanged = codeLensSettingsChanged; this.inlayHintSettingsChanged = inlayHintSettingsChanged; + this.nativeImagesSettingsChanged = nativeImagesSettingsChanged; } /** @@ -143,6 +181,14 @@ public boolean isInlayHintSettingsChanged() { return inlayHintSettingsChanged; } + /** + * Returns true if native images settings changed and false otherwise. + * + * @return true if native images settings changed and false otherwise. + */ + public boolean isNativeImagesSettingsChanged() { + return nativeImagesSettingsChanged; + } } /** @@ -176,7 +222,14 @@ public static SettingsUpdateState update(SharedSettings sharedSettings, QuteGene validationSettingsChanged = true; } - return new SettingsUpdateState(validationSettingsChanged, codeLensSettingsChanged, inlayHintSettingsChanged); + // Update native images settings + boolean nativeImagesSettingsChanged = updateNativeImagesSettings(sharedSettings, clientSettings); + if (workspaceChanged) { + nativeImagesSettingsChanged = true; + } + + return new SettingsUpdateState(validationSettingsChanged, codeLensSettingsChanged, inlayHintSettingsChanged, + nativeImagesSettingsChanged); } private static boolean updateCodeLensSettings(SharedSettings sharedSettings, @@ -254,4 +307,31 @@ private static boolean updateValidationSettings(BaseSettings sharedSettings, Qut return false; } + private static boolean updateNativeImagesSettings(SharedSettings sharedSettings, + QuteGeneralClientSettings clientSettings) { + // Global nativeImages settings + boolean nativeImagesSettingsChanged = updateNativeImagesSettings(sharedSettings, + clientSettings.getNativeImages()); + // Workspace folder nativeImages settings + Map workspaceFolders = clientSettings.getWorkspaceFolders(); + if (workspaceFolders != null) { + for (Map.Entry entry : workspaceFolders + .entrySet()) { + String workspaceFolderUri = entry.getKey(); + BaseSettings settings = sharedSettings.getWorkspaceFolderSettings(workspaceFolderUri); + nativeImagesSettingsChanged |= updateNativeImagesSettings(settings, entry.getValue().getNativeImages()); + } + } + return nativeImagesSettingsChanged; + } + + private static boolean updateNativeImagesSettings(BaseSettings sharedSettings, + QuteNativeImagesSettings nativeImages) { + if (nativeImages != null && !nativeImages.equals(sharedSettings.getNativeImagesSettings())) { + sharedSettings.getNativeImagesSettings().update(nativeImages); + return true; + } + return false; + } + } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeImagesSettings.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeImagesSettings.java new file mode 100644 index 000000000..dc8625169 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeImagesSettings.java @@ -0,0 +1,109 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.settings; + +import java.util.List; + +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.TemplateDataAnnotation; + +/** + * Qute native image settings. + * + * @author Angelo ZERR + * + */ +public class QuteNativeImagesSettings { + + private boolean enabled; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Update the native images settings with the given new native images settings. + * + * @param newNativeImages the new native images settings. + */ + public void update(QuteNativeImagesSettings newNativeImages) { + this.setEnabled(newNativeImages.isEnabled()); + } + + /** + * Returns true if the given Java type can support native images and false + * otherwise. + * + * @param baseType the Java type. + * + * @return true if the given Java type can support native images and false + * otherwise. + */ + public static boolean canSupportNativeImages(ResolvedJavaTypeInfo baseType) { + return baseType != null && baseType.getTemplateDataAnnotations() != null + && !baseType.getTemplateDataAnnotations().isEmpty(); + } + + /** + * Returns true if super classes must be ignored while searching fields method + * of the given Java base type and false otherwise. + * + * @param baseType the Java type. + * @return true if super classes must be ignored while searching fields method + * of the given Java base type and false otherwise. + */ + public boolean isIgnoreSuperclasses(ResolvedJavaTypeInfo baseType) { + if (!isEnabled()) { + // In NO native images context, Java reflection is allowed, in other words super + // classes must not be ignored. + return false; + } + // Here we are in native images context, the Java Type must be annotated with + // @TempateData#ignoreSuperclasses : + + // @TemplateData(ignoreSuperclasses = true) + // public class Book extends PanacheEntity + List annotations = baseType != null ? baseType.getTemplateDataAnnotations() : null; + if (annotations == null) { + return false; + } + return annotations.stream() // + .anyMatch(TemplateDataAnnotation::isIgnoreSuperclasses); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (enabled ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QuteNativeImagesSettings other = (QuteNativeImagesSettings) obj; + if (enabled != other.enabled) + return false; + return true; + } + +} \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java index 09dd5c256..a1999a288 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java @@ -82,6 +82,7 @@ import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; import com.redhat.qute.settings.QuteInlayHintSettings; +import com.redhat.qute.settings.QuteNativeImagesSettings; import com.redhat.qute.settings.QuteValidationSettings; import com.redhat.qute.settings.SharedSettings; import com.redhat.qute.utils.StringUtils; @@ -164,10 +165,11 @@ public static void testCompletionFor(String value, boolean snippetSupport, Strin completionSettings.setCapabilities(completionCapabilities); QuteFormattingSettings formattingSettings = new QuteFormattingSettings(); + QuteNativeImagesSettings nativeImagesSettings = new QuteNativeImagesSettings(); QuteLanguageService languageService = new QuteLanguageService(new JavaDataModelCache(projectRegistry)); CompletionList list = languageService - .doComplete(template, position, completionSettings, formattingSettings, () -> { + .doComplete(template, position, completionSettings, formattingSettings, nativeImagesSettings, () -> { }).get(); // no duplicate labels @@ -287,13 +289,20 @@ public static void testDiagnosticsFor(String value, QuteValidationSettings valid public static void testDiagnosticsFor(String value, String fileUri, String templateId, String projectUri, String templateBaseDir, boolean filter, QuteValidationSettings validationSettings, Diagnostic... expected) { + testDiagnosticsFor(value, fileUri, templateId, projectUri, templateBaseDir, filter, validationSettings, + new QuteNativeImagesSettings(), expected); + } + + public static void testDiagnosticsFor(String value, String fileUri, String templateId, String projectUri, + String templateBaseDir, boolean filter, QuteValidationSettings validationSettings, + QuteNativeImagesSettings nativeImagesSettings, Diagnostic... expected) { QuteProjectRegistry projectRegistry = new MockQuteProjectRegistry(); Template template = createTemplate(value, fileUri, projectUri, templateBaseDir, projectRegistry); template.setTemplateId(templateId); JavaDataModelCache javaCache = new JavaDataModelCache(projectRegistry); QuteLanguageService languageService = new QuteLanguageService(javaCache); - List actual = languageService.doDiagnostics(template, validationSettings, + List actual = languageService.doDiagnostics(template, validationSettings, nativeImagesSettings, new ResolvingJavaTypeContext(template, javaCache), () -> { }); if (expected == null) {