From 7ec5cad057e58e02d5097ce1005bcecadb65cbfa Mon Sep 17 00:00:00 2001 From: marcin Date: Tue, 17 Dec 2024 13:27:30 +0000 Subject: [PATCH] feat: Hilla endpoints (#196) Added Hilla BrowserCallable endpoints --- .../endpoints/VaadinEndpointsProvider.kt | 41 +++-------------- .../endpoints/VaadinFlowEndpointsProvider.kt | 44 +++++++++++++++++++ .../endpoints/VaadinHillaEndpointsProvider.kt | 27 ++++++++++++ .../endpoints/VaadinImplicitUsageProvider.kt | 1 + .../vaadin/plugin/endpoints/VaadinModel.kt | 27 +++++++++++- .../vaadin/plugin/endpoints/VaadinRoute.kt | 2 +- .../plugin/endpoints/VaadinUrlResolver.kt | 2 +- .../com/vaadin/plugin/utils/VaadinIcons.kt | 2 + .../META-INF/vaadin-with-microservices.xml | 4 +- .../META-INF/vaadin-with-ultimate.xml | 4 ++ src/main/resources/vaadin/icons/hilla.svg | 1 + 11 files changed, 115 insertions(+), 40 deletions(-) create mode 100644 src/main/kotlin/com/vaadin/plugin/endpoints/VaadinFlowEndpointsProvider.kt create mode 100644 src/main/kotlin/com/vaadin/plugin/endpoints/VaadinHillaEndpointsProvider.kt create mode 100644 src/main/resources/vaadin/icons/hilla.svg diff --git a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinEndpointsProvider.kt b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinEndpointsProvider.kt index 0896306..845b8c9 100644 --- a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinEndpointsProvider.kt +++ b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinEndpointsProvider.kt @@ -3,30 +3,21 @@ package com.vaadin.plugin.endpoints import com.intellij.microservices.endpoints.EndpointType import com.intellij.microservices.endpoints.EndpointsFilter import com.intellij.microservices.endpoints.EndpointsProvider -import com.intellij.microservices.endpoints.EndpointsProvider.Status -import com.intellij.microservices.endpoints.FrameworkPresentation import com.intellij.microservices.endpoints.HTTP_SERVER_TYPE import com.intellij.microservices.endpoints.ModuleEndpointsFilter -import com.intellij.microservices.endpoints.presentation.HttpUrlPresentation -import com.intellij.microservices.url.UrlPath -import com.intellij.navigation.ItemPresentation import com.intellij.openapi.project.Project import com.intellij.openapi.util.ModificationTracker import com.intellij.psi.PsiElement import com.intellij.uast.UastModificationTracker -import com.vaadin.plugin.utils.VaadinIcons import com.vaadin.plugin.utils.hasVaadin -internal class VaadinEndpointsProvider : EndpointsProvider { - override val endpointType: EndpointType = HTTP_SERVER_TYPE - - override val presentation: FrameworkPresentation = - FrameworkPresentation("Vaadin", "Vaadin Flow", VaadinIcons.VAADIN_BLUE) +abstract class VaadinEndpointsProvider : EndpointsProvider { - override fun getStatus(project: Project): Status { - if (hasVaadin(project)) return Status.HAS_ENDPOINTS + override val endpointType: EndpointType = HTTP_SERVER_TYPE - return Status.UNAVAILABLE + override fun getStatus(project: Project): EndpointsProvider.Status { + if (hasVaadin(project)) return EndpointsProvider.Status.HAS_ENDPOINTS + return EndpointsProvider.Status.UNAVAILABLE } override fun getModificationTracker(project: Project): ModificationTracker { @@ -37,7 +28,7 @@ internal class VaadinEndpointsProvider : EndpointsProvider { @@ -48,27 +39,7 @@ internal class VaadinEndpointsProvider : EndpointsProvider { + if (filter !is ModuleEndpointsFilter) return emptyList() + if (!hasVaadin(filter.module)) return emptyList() + + return findFlowRoutes(project, filter.transitiveSearchScope) + } + + override fun getEndpointPresentation(group: VaadinRoute, endpoint: VaadinRoute): ItemPresentation { + return HttpUrlPresentation(normalizeUrl(group.urlMapping), group.locationString, VaadinIcons.VAADIN_BLUE) + } + + private fun normalizeUrl(urlMapping: String): String { + val urlString = run { + if (urlMapping.isBlank()) return@run "/" + if (!urlMapping.startsWith("/")) return@run "/$urlMapping" + return@run urlMapping + } + + return parseVaadinUrlMapping(urlString).getPresentation(VaadinUrlRenderer) + } + + private object VaadinUrlRenderer : UrlPath.PathSegmentRenderer { + override fun visitVariable(variable: UrlPath.PathSegment.Variable): String { + return "{${variable.variableName}}" + } + } +} diff --git a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinHillaEndpointsProvider.kt b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinHillaEndpointsProvider.kt new file mode 100644 index 0000000..8d868db --- /dev/null +++ b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinHillaEndpointsProvider.kt @@ -0,0 +1,27 @@ +package com.vaadin.plugin.endpoints + +import com.intellij.microservices.endpoints.EndpointsFilter +import com.intellij.microservices.endpoints.FrameworkPresentation +import com.intellij.microservices.endpoints.ModuleEndpointsFilter +import com.intellij.microservices.endpoints.presentation.HttpUrlPresentation +import com.intellij.navigation.ItemPresentation +import com.intellij.openapi.project.Project +import com.vaadin.plugin.utils.VaadinIcons +import com.vaadin.plugin.utils.hasVaadin + +internal class VaadinHillaEndpointsProvider : VaadinEndpointsProvider() { + + override val presentation: FrameworkPresentation = + FrameworkPresentation("Vaadin-Hilla", "Vaadin Hilla", VaadinIcons.HILLA) + + override fun getEndpointGroups(project: Project, filter: EndpointsFilter): Iterable { + if (filter !is ModuleEndpointsFilter) return emptyList() + if (!hasVaadin(filter.module)) return emptyList() + + return findHillaEndpoints(project, filter.transitiveSearchScope) + } + + override fun getEndpointPresentation(group: VaadinRoute, endpoint: VaadinRoute): ItemPresentation { + return HttpUrlPresentation(group.urlMapping, group.locationString, VaadinIcons.HILLA) + } +} diff --git a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinImplicitUsageProvider.kt b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinImplicitUsageProvider.kt index 11357c2..0fe19f6 100644 --- a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinImplicitUsageProvider.kt +++ b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinImplicitUsageProvider.kt @@ -17,6 +17,7 @@ internal class VaadinImplicitUsageProvider : ImplicitUsageProvider { !element.isAnnotationType && (AnnotationUtil.isAnnotated(element, VAADIN_ROUTE, 0) || AnnotationUtil.isAnnotated(element, VAADIN_TAG, 0) || + AnnotationUtil.isAnnotated(element, HILLA_BROWSER_CALLABLE, 0) || InheritanceUtil.isInheritor(element, VAADIN_APP_SHELL_CONFIGURATOR)) } diff --git a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinModel.kt b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinModel.kt index 60844d8..7e550b0 100644 --- a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinModel.kt +++ b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinModel.kt @@ -15,8 +15,9 @@ internal const val VAADIN_ROUTE = "com.vaadin.flow.router.Route" internal const val VAADIN_APP_SHELL_CONFIGURATOR = "com.vaadin.flow.component.page.AppShellConfigurator" internal const val VAADIN_ID = "com.vaadin.flow.component.template.Id" internal const val VAADIN_TAG = "com.vaadin.flow.component.Tag" +internal const val HILLA_BROWSER_CALLABLE = "com.vaadin.hilla.BrowserCallable" -internal fun findVaadinRoutes(project: Project, scope: GlobalSearchScope): Collection { +internal fun findFlowRoutes(project: Project, scope: GlobalSearchScope): Collection { val vaadinRouteClass = JavaPsiFacade.getInstance(project).findClass(VAADIN_ROUTE, ProjectScope.getLibrariesScope(project)) ?: return emptyList() @@ -42,3 +43,27 @@ internal fun findVaadinRoutes(project: Project, scope: GlobalSearchScope): Colle return routes.toList() } + +internal fun findHillaEndpoints(project: Project, scope: GlobalSearchScope): Collection { + val hillaBrowserCallableClass = + JavaPsiFacade.getInstance(project).findClass(HILLA_BROWSER_CALLABLE, ProjectScope.getLibrariesScope(project)) + ?: return emptyList() + + val endpoints = ArrayList() + + AnnotatedElementsSearch.searchPsiClasses(hillaBrowserCallableClass, scope) + .forEach( + Processor { psiClass -> + val uClass = psiClass.toUElementOfType() + val sourcePsi = uClass?.sourcePsi + val className = psiClass.name + + if (sourcePsi == null || className == null) return@Processor true + + endpoints.add(VaadinRoute(className, className, PsiAnchor.create(sourcePsi))) + + true + }) + + return endpoints.toList() +} diff --git a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinRoute.kt b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinRoute.kt index 60349ed..4f45f96 100644 --- a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinRoute.kt +++ b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinRoute.kt @@ -2,6 +2,6 @@ package com.vaadin.plugin.endpoints import com.intellij.psi.PsiAnchor -internal class VaadinRoute(val urlMapping: String, val locationString: String, val anchor: PsiAnchor) { +class VaadinRoute(val urlMapping: String, val locationString: String, val anchor: PsiAnchor) { fun isValid(): Boolean = anchor.retrieve() != null } diff --git a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinUrlResolver.kt b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinUrlResolver.kt index d4fa400..b4d8474 100644 --- a/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinUrlResolver.kt +++ b/src/main/kotlin/com/vaadin/plugin/endpoints/VaadinUrlResolver.kt @@ -43,7 +43,7 @@ internal class VaadinUrlResolver(private val project: Project) : UrlResolver { } internal val VAADIN_ROUTES_SEARCH: SourceLibSearchProvider, Module> = - SourceLibSearchProvider("VAADIN_ROUTES") { p, _, scope -> findVaadinRoutes(p, scope).toList() } + SourceLibSearchProvider("VAADIN_ROUTES") { p, _, scope -> findFlowRoutes(p, scope).toList() } private fun getAllModuleVariants(project: Project): Sequence { val modules = ModuleManager.getInstance(project).modules diff --git a/src/main/kotlin/com/vaadin/plugin/utils/VaadinIcons.kt b/src/main/kotlin/com/vaadin/plugin/utils/VaadinIcons.kt index 3f5c08a..23912da 100644 --- a/src/main/kotlin/com/vaadin/plugin/utils/VaadinIcons.kt +++ b/src/main/kotlin/com/vaadin/plugin/utils/VaadinIcons.kt @@ -12,6 +12,8 @@ class VaadinIcons { val VAADIN = IconLoader.getIcon("$RESOURCE_PATH/vaadin.svg", VaadinIcons::class.java.classLoader) + val HILLA = IconLoader.getIcon("$RESOURCE_PATH/hilla.svg", VaadinIcons::class.java.classLoader) + val DEBUG_HOTSWAP = IconLoader.getIcon("$RESOURCE_PATH/debug.svg", VaadinIcons::class.java.classLoader) val RERUN_HOTSWAP = IconLoader.getIcon("$RESOURCE_PATH/rerun.svg", VaadinIcons::class.java.classLoader) diff --git a/src/main/resources/META-INF/vaadin-with-microservices.xml b/src/main/resources/META-INF/vaadin-with-microservices.xml index b1171fb..44c79b0 100644 --- a/src/main/resources/META-INF/vaadin-with-microservices.xml +++ b/src/main/resources/META-INF/vaadin-with-microservices.xml @@ -1,9 +1,9 @@ - + + - diff --git a/src/main/resources/META-INF/vaadin-with-ultimate.xml b/src/main/resources/META-INF/vaadin-with-ultimate.xml index ef64a58..9ca70ee 100644 --- a/src/main/resources/META-INF/vaadin-with-ultimate.xml +++ b/src/main/resources/META-INF/vaadin-with-ultimate.xml @@ -1,3 +1,7 @@ + + + + diff --git a/src/main/resources/vaadin/icons/hilla.svg b/src/main/resources/vaadin/icons/hilla.svg new file mode 100644 index 0000000..fab9603 --- /dev/null +++ b/src/main/resources/vaadin/icons/hilla.svg @@ -0,0 +1 @@ + \ No newline at end of file