From 1ebac0a8334ff8c654a6d258a2ff1e8a2c3435e7 Mon Sep 17 00:00:00 2001 From: azerr Date: Tue, 19 Sep 2023 11:06:32 +0200 Subject: [PATCH] Qute support for multi module project Fixes #930 Signed-off-by: azerr --- qute.jdt/com.redhat.qute.jdt/plugin.xml | 1 + .../com/redhat/qute/commons/ProjectInfo.java | 27 ++++- .../qute/jdt/QuteSupportForTemplate.java | 36 ++++++ ...portForTemplateDelegateCommandHandler.java | 7 ++ .../qute/jdt/utils/JDTQuteProjectUtils.java | 27 ++++- .../com/redhat/qute/commons/ProjectInfo.java | 27 ++++- .../redhat/qute/ls/QuteLanguageServer.java | 58 +++++---- .../qute/ls/api/QuteProjectInfoProvider.java | 34 ++++++ .../template/sections/IncludeSection.java | 32 ++++- .../com/redhat/qute/project/QuteProject.java | 8 ++ .../qute/project/QuteProjectRegistry.java | 111 +++++++++++------- .../redhat/qute/services/QuteDiagnostics.java | 6 +- .../qute/services/QuteDocumentLink.java | 2 +- .../QuteCompletionForTemplateIds.java | 7 ++ .../test/java/com/redhat/qute/QuteAssert.java | 4 +- .../qute/project/MockQuteLanguageClient.java | 16 +++ .../redhat/qute/project/MockQuteProject.java | 28 +++-- .../qute/project/MockQuteProjectRegistry.java | 10 +- .../qute/project/QuteQuickStartProject.java | 28 ++--- .../qute/project/multiple/QuteProjectA.java | 62 ++++++++++ .../qute/project/multiple/QuteProjectB.java | 65 ++++++++++ ...rateTemplateContentCommandHandlerTest.java | 3 +- .../QuteCompletionWithIncludeSectionTest.java | 78 ++++++++++++ ...QuteDiagnosticsWithIncludeSectionTest.java | 46 ++++++++ .../AbstractQuteDiagnosticsInProjectTest.java | 18 ++- .../QuteDocumentLinkTest.java | 2 +- .../multiple/QuteDocumentLinkTest.java | 42 +++++++ .../src/main/resources/templates/root.html | 3 + .../src/main/resources/templates/index.html | 9 ++ 29 files changed, 683 insertions(+), 114 deletions(-) create mode 100644 qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectA.java create mode 100644 qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectB.java create mode 100644 qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/multiple/QuteCompletionWithIncludeSectionTest.java create mode 100644 qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/multiple/QuteDiagnosticsWithIncludeSectionTest.java rename qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/{ => documentlink}/QuteDocumentLinkTest.java (94%) create mode 100644 qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/multiple/QuteDocumentLinkTest.java create mode 100644 qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-a/src/main/resources/templates/root.html create mode 100644 qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-b/src/main/resources/templates/index.html diff --git a/qute.jdt/com.redhat.qute.jdt/plugin.xml b/qute.jdt/com.redhat.qute.jdt/plugin.xml index 005536026..6e95ae033 100644 --- a/qute.jdt/com.redhat.qute.jdt/plugin.xml +++ b/qute.jdt/com.redhat.qute.jdt/plugin.xml @@ -12,6 +12,7 @@ + diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ProjectInfo.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ProjectInfo.java index 2bcb3fc87..e048ca38f 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ProjectInfo.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ProjectInfo.java @@ -11,6 +11,8 @@ *******************************************************************************/ package com.redhat.qute.commons; +import java.util.List; + /** * Project information where a Qute template belongs to. * @@ -23,11 +25,14 @@ public class ProjectInfo { private String templateBaseDir; + private List projectDependencyUris; + public ProjectInfo() { } - public ProjectInfo(String uri, String templateBaseDir) { - setUri(uri); + public ProjectInfo(String projectUri, List projectDependencies, String templateBaseDir) { + setUri(projectUri); + setProjectDependencyUris(projectDependencies); setTemplateBaseDir(templateBaseDir); } @@ -49,6 +54,24 @@ public void setUri(String uri) { this.uri = uri; } + /** + * Returns the project dependency Uris. + * + * @return the project dependency Uris. + */ + public List getProjectDependencyUris() { + return projectDependencyUris; + } + + /** + * Set the project dependency Uris. + * + * @param projectDependencyUris the project dependency Uris. + */ + public void setProjectDependencyUris(List projectDependencyUris) { + this.projectDependencyUris = projectDependencyUris; + } + /** * Returns the Qute templates base directory and null otherwise. * 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 24c8e8872..488d7d4c7 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 @@ -17,6 +17,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -99,6 +100,28 @@ public static QuteSupportForTemplate getInstance() { return INSTANCE; } + /** + * Returns the list of Qute projects from the workspace. + * + * @param utils the JDT LS utility. + * @param monitor the progress monitor. + * + * @return the list of Qute projects from teh workspace. + */ + public List getProjects(IJDTUtils utils, IProgressMonitor monitor) { + List quteProjects = new ArrayList<>(); + IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + // Loop for project from the workspace + for (IProject project : allProjects) { + IJavaProject javaProject = getJavaProject(project); + if (isQuteProject(javaProject)) { + // It is a Qute project + quteProjects.add(JDTQuteProjectUtils.getProjectInfo(javaProject)); + } + } + return quteProjects; + } + /** * Returns the project information for the given project Uri. * @@ -405,6 +428,19 @@ private static IProject findProject(String resourceUri, IJDTUtils utils) { */ private static boolean isQuteProject(IProject project) { IJavaProject javaProject = getJavaProject(project); + return isQuteProject(javaProject); + } + + /** + * Returns true if the given Java project is a Java Qute project and false + * otherwise. + * + * @param project the Java project. + * + * @return true if the given Java project is a Java Qute project and false + * otherwise. + */ + private static boolean isQuteProject(IJavaProject javaProject) { if (javaProject == null) { return false; } diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/ls/QuteSupportForTemplateDelegateCommandHandler.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/ls/QuteSupportForTemplateDelegateCommandHandler.java index 01125ed6c..d73c38999 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/ls/QuteSupportForTemplateDelegateCommandHandler.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/ls/QuteSupportForTemplateDelegateCommandHandler.java @@ -74,6 +74,7 @@ public class QuteSupportForTemplateDelegateCommandHandler extends AbstractQuteDe private static final String CLASS_NAME_ATTR = "className"; + private static final String QUTE_TEMPLATE_PROJECTS_COMMAND_ID = "qute/template/projects"; private static final String QUTE_TEMPLATE_PROJECT_COMMAND_ID = "qute/template/project"; private static final String QUTE_TEMPLATE_PROJECT_DATA_MODEL_COMMAND_ID = "qute/template/projectDataModel"; @@ -93,6 +94,8 @@ public class QuteSupportForTemplateDelegateCommandHandler extends AbstractQuteDe @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { switch (commandId) { + case QUTE_TEMPLATE_PROJECTS_COMMAND_ID: + return getProjects(commandId, monitor); case QUTE_TEMPLATE_PROJECT_COMMAND_ID: return getProjectInfo(arguments, commandId, monitor); case QUTE_TEMPLATE_PROJECT_DATA_MODEL_COMMAND_ID: @@ -113,6 +116,10 @@ public Object executeCommand(String commandId, List arguments, IProgress return null; } + private static List getProjects(String commandId, IProgressMonitor monitor) { + return QuteSupportForTemplate.getInstance().getProjects(JDTUtilsLSImpl.getInstance(), monitor); + } + private static ProjectInfo getProjectInfo(List arguments, String commandId, IProgressMonitor monitor) { QuteProjectParams params = createQuteProjectParams(arguments, commandId); return QuteSupportForTemplate.getInstance().getProjectInfo(params, JDTUtilsLSImpl.getInstance(), monitor); diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/JDTQuteProjectUtils.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/JDTQuteProjectUtils.java index 1cdacaeae..b33760809 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/JDTQuteProjectUtils.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/JDTQuteProjectUtils.java @@ -11,11 +11,18 @@ *******************************************************************************/ package com.redhat.qute.jdt.utils; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; @@ -30,6 +37,8 @@ */ public class JDTQuteProjectUtils { + private static final Logger LOGGER = Logger.getLogger(JDTQuteProjectUtils.class.getName()); + private static final String TEMPLATES_BASE_DIR = "src/main/resources/templates/"; private JDTQuteProjectUtils() { @@ -40,7 +49,20 @@ public static ProjectInfo getProjectInfo(IJavaProject javaProject) { IProject project = javaProject.getProject(); String projectUri = getProjectURI(project); String templateBaseDir = project.getFile(TEMPLATES_BASE_DIR).getLocationURI().toString(); - return new ProjectInfo(projectUri, templateBaseDir); + // Project dependencies + List projectDependencies = Collections.emptyList(); + try { + String[] requiredProjectNames = javaProject.getRequiredProjectNames(); + if (requiredProjectNames != null) { + projectDependencies = Arrays.asList(requiredProjectNames); + } + } catch (JavaModelException e) { + // Should never occurs + LOGGER.log(Level.SEVERE, + "Error while getting project dependencies for '" + javaProject.getElementName() + "' Java project.", + e); + } + return new ProjectInfo(projectUri, projectDependencies, templateBaseDir); } /** @@ -84,7 +106,8 @@ public static boolean hasQuteSupport(IJavaProject javaProject) { return JDTTypeUtils.findType(javaProject, QuteJavaConstants.ENGINE_BUILDER_CLASS) != null; } - public static TemplatePathInfo getTemplatePath(String className, String methodOrFieldName, boolean ignoreFragments) { + public static TemplatePathInfo getTemplatePath(String className, String methodOrFieldName, + boolean ignoreFragments) { String fragmentId = null; StringBuilder templateUri = new StringBuilder(TEMPLATES_BASE_DIR); if (className != null) { diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ProjectInfo.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ProjectInfo.java index 2bcb3fc87..e048ca38f 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ProjectInfo.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ProjectInfo.java @@ -11,6 +11,8 @@ *******************************************************************************/ package com.redhat.qute.commons; +import java.util.List; + /** * Project information where a Qute template belongs to. * @@ -23,11 +25,14 @@ public class ProjectInfo { private String templateBaseDir; + private List projectDependencyUris; + public ProjectInfo() { } - public ProjectInfo(String uri, String templateBaseDir) { - setUri(uri); + public ProjectInfo(String projectUri, List projectDependencies, String templateBaseDir) { + setUri(projectUri); + setProjectDependencyUris(projectDependencies); setTemplateBaseDir(templateBaseDir); } @@ -49,6 +54,24 @@ public void setUri(String uri) { this.uri = uri; } + /** + * Returns the project dependency Uris. + * + * @return the project dependency Uris. + */ + public List getProjectDependencyUris() { + return projectDependencyUris; + } + + /** + * Set the project dependency Uris. + * + * @param projectDependencyUris the project dependency Uris. + */ + public void setProjectDependencyUris(List projectDependencyUris) { + this.projectDependencyUris = projectDependencyUris; + } + /** * Returns the Qute templates base directory and null otherwise. * 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 5076dcd54..c361d4130 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 @@ -32,7 +32,6 @@ import org.eclipse.lsp4j.SetTraceParams; import org.eclipse.lsp4j.WorkDoneProgressCreateParams; import org.eclipse.lsp4j.WorkDoneProgressNotification; -import org.eclipse.lsp4j.WorkspaceFolder; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; @@ -108,11 +107,12 @@ public class QuteLanguageServer implements LanguageServer, ProcessLanguageServer private QuteLanguageClientAPI languageClient; private QuteCapabilityManager capabilityManager; - private List workspaceFolders; public QuteLanguageServer() { this.sharedSettings = new SharedSettings(); - this.projectRegistry = new QuteProjectRegistry(this, this, this, this, this, this, this, this); + this.projectRegistry = new QuteProjectRegistry(this, this, this, this, this, this, this, this, // + () -> capabilityManager.getClientCapabilities() + .isWorkDoneProgressSupported() ? this : null); this.quteLanguageService = new QuteLanguageService(projectRegistry); this.textDocumentService = new QuteTextDocumentService(this); this.workspaceService = new QuteWorkspaceService(this); @@ -135,7 +135,6 @@ public CompletableFuture initialize(InitializeParams params) { projectRegistry.setDidChangeWatchedFilesSupported( capabilityManager.getClientCapabilities().isDidChangeWatchedFilesRegistered()); - workspaceFolders = params.getWorkspaceFolders(); InitializeResult initializeResult = new InitializeResult(serverCapabilities); return CompletableFuture.completedFuture(initializeResult); @@ -163,19 +162,13 @@ public void initialized(InitializedParams params) { * Try to load the Qute project for each workspace folder. */ private void loadQuteProjects() { - if (workspaceFolders == null || workspaceFolders.isEmpty()) { - // No workspace folders. - return; - } - CompletableFuture.runAsync(() -> { - for (WorkspaceFolder workspaceFolder : workspaceFolders) { - // Get the LSP client progress support - ProgressSupport progressSupport = capabilityManager.getClientCapabilities() - .isWorkDoneProgressSupported() ? this : null; - // Try to load the Qute project of the current workspace folder - projectRegistry.tryToLoadQuteProject(workspaceFolder, progressSupport); - } - }); + getLanguageClient().getProjects() + .thenAccept(projects -> { + if (projects != null && !projects.isEmpty()) { + // There are some Qute projects in the workspace, load them + projectRegistry.loadQuteProjects(projects); + } + }); } /** @@ -267,6 +260,30 @@ public SharedSettings getSharedSettings() { return sharedSettings; } + // Project requests / notifications + + @Override + public CompletableFuture> getProjects() { + return getLanguageClient().getProjects(); + } + + @Override + public void projectAdded(ProjectInfo project) { + projectRegistry.projectAdded(project); + } + + @Override + public void projectRemoved(ProjectInfo project) { + projectRegistry.projectRemoved(project); + } + + @Override + public CompletableFuture getProjectInfo(QuteProjectParams params) { + return getLanguageClient().getProjectInfo(params); + } + + // Other requests / notifications + @Override public CompletableFuture> getJavaTypes(QuteJavaTypesParams params) { return getLanguageClient().getJavaTypes(params); @@ -282,11 +299,6 @@ public CompletableFuture getResolvedJavaType(QuteResolvedJ return getLanguageClient().getResolvedJavaType(params); } - @Override - public CompletableFuture getProjectInfo(QuteProjectParams params) { - return getLanguageClient().getProjectInfo(params); - } - @Override public CompletableFuture getJavadoc(QuteJavadocParams params) { return getLanguageClient().getJavadoc(params); @@ -355,7 +367,7 @@ public void notifyProgress(String progressId, WorkDoneProgressNotification notif } @Override - public void telemetryEvent (Object object) { + public void telemetryEvent(Object object) { getLanguageClient().telemetryEvent(object); } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/api/QuteProjectInfoProvider.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/api/QuteProjectInfoProvider.java index 29c168327..a64f240f9 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/api/QuteProjectInfoProvider.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/api/QuteProjectInfoProvider.java @@ -11,8 +11,10 @@ *******************************************************************************/ package com.redhat.qute.ls.api; +import java.util.Collection; import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; import com.redhat.qute.commons.ProjectInfo; @@ -26,6 +28,38 @@ */ public interface QuteProjectInfoProvider { + /** + * Returns all Qute projects from the workspace. + * + * @return all Qute projects from the workspace. + */ + @JsonRequest("qute/template/projects") + CompletableFuture> getProjects(); + + /** + * Notification received when a Qute project is added in the workspace. + * + * @param project the Qute project which is added in the workspace. + */ + @JsonNotification("qute/template/project/added") + void projectAdded(ProjectInfo project); + + /** + * Notification received when a Qute project is closed / removed from the + * workspace. + * + * @param project the Qute project which is closed / removed from the workspace. + */ + @JsonNotification("qute/template/project/removed") + void projectRemoved(ProjectInfo project); + + /** + * Returns the Qute project from the given template file uri parameter. + * + * @param params the template file uri parameter. + * @return + */ @JsonRequest("qute/template/project") CompletableFuture getProjectInfo(QuteProjectParams params); + } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/sections/IncludeSection.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/sections/IncludeSection.java index 53cae4178..a23fde8d0 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/sections/IncludeSection.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/sections/IncludeSection.java @@ -53,12 +53,42 @@ public Path getReferencedTemplateFile() { if (referencedTemplateId == null) { return null; } - QuteProject project = getOwnerTemplate().getProject(); if (project == null) { return null; } + // 1. Try to get a valid template from the Qute project + Path templatePath = getReferencedTemplateFile(referencedTemplateId, project); + if (templatePath != null && Files.exists(templatePath)) { + // Returns the valid template from the Qute project + return templatePath; + } + + if (!project.getProjectDependencies().isEmpty()) { + // 2. The Qute project have some project dependencies, try to get a valid from + // those dependencies + for (QuteProject projectDependency : project.getProjectDependencies()) { + if (projectDependency != null) { + Path dependencyTemplatePath = getReferencedTemplateFile(referencedTemplateId, projectDependency); + if (dependencyTemplatePath != null && Files.exists(dependencyTemplatePath)) { + return dependencyTemplatePath; + } + } + } + } + + // Returns the invalid template from the Qute project + return templatePath; + } + /** + * Returns the referenced template file defined in the first parameter of the + * section and null otherwise. + * + * @return the referenced template file defined in the first parameter of the + * section and null otherwise. + */ + private static Path getReferencedTemplateFile(String referencedTemplateId, QuteProject project) { Path templateBaseDir = project.getTemplateBaseDir(); if (templateBaseDir == null) { return null; diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java index 069b592c6..0f0b00d1c 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java @@ -116,6 +116,8 @@ public class QuteProject { private final JavaDataModelCache javaCache; + private List projectDependencies; + public QuteProject(ProjectInfo projectInfo, QuteProjectRegistry projectRegistry, TemplateValidator validator) { this.uri = projectInfo.getUri(); @@ -132,6 +134,7 @@ public QuteProject(ProjectInfo projectInfo, QuteProjectRegistry projectRegistry, // ONLY if LSP client cannot support DidChangeWatchedFiles. this.watcher = !projectRegistry.isDidChangeWatchedFilesSupported() ? createFilesWatcher(this) : null; this.javaCache = new JavaDataModelCache(this); + this.projectDependencies = new ArrayList<>(); } private static QuteProjectFilesWatcher createFilesWatcher(QuteProject project) { @@ -210,6 +213,10 @@ public boolean isTemplateOpened(String templateId) { public String getUri() { return uri; } + + public List getProjectDependencies() { + return projectDependencies; + } /** * Returns the insert parameter list with the given name @@ -1507,5 +1514,6 @@ public CompletableFuture getJavadoc(JavaMemberInfo javaMemberInfo, JavaT return projectRegistry.getJavadoc(new QuteJavadocParams(typeName, getUri(), javaMemberInfo.getName(), signature, hasMarkdown ? DocumentFormat.Markdown : DocumentFormat.PlainText)); } + } \ No newline at end of file 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 b77864ad2..4780d2fae 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 @@ -22,6 +22,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; import org.eclipse.lsp4j.DidChangeWatchedFilesParams; import org.eclipse.lsp4j.FileEvent; @@ -30,7 +31,6 @@ import org.eclipse.lsp4j.WorkDoneProgressCreateParams; import org.eclipse.lsp4j.WorkDoneProgressEnd; import org.eclipse.lsp4j.WorkDoneProgressReport; -import org.eclipse.lsp4j.WorkspaceFolder; import org.eclipse.lsp4j.jsonrpc.messages.Either; import com.redhat.qute.commons.JavaTypeInfo; @@ -95,13 +95,15 @@ public class QuteProjectRegistry private final TemplateValidator validator; + private final Supplier progressSupportProvider; + private boolean didChangeWatchedFilesSupported; public QuteProjectRegistry(QuteProjectInfoProvider projectInfoProvider, QuteJavaTypesProvider javaTypeProvider, QuteJavaDefinitionProvider definitionProvider, QuteResolvedJavaTypeProvider resolvedClassProvider, QuteDataModelProjectProvider dataModelProvider, QuteUserTagProvider userTagsProvider, QuteJavadocProvider javadocProvider, - TemplateValidator validator) { + TemplateValidator validator, Supplier progressSupportProvider) { this.projectInfoProvider = projectInfoProvider; this.javaTypeProvider = javaTypeProvider; this.definitionProvider = definitionProvider; @@ -112,6 +114,7 @@ public QuteProjectRegistry(QuteProjectInfoProvider projectInfoProvider, QuteJava this.javadocProvider = javadocProvider; this.valueResolversRegistry = new ValueResolversRegistry(); this.validator = validator; + this.progressSupportProvider = progressSupportProvider; } /** @@ -401,64 +404,68 @@ public CompletableFuture getProjectInfo(QuteProjectParams params) { return projectInfoProvider.getProjectInfo(params); } + public void loadQuteProjects(Collection projects) { + // 1. Load all Qute projects + for (ProjectInfo projectInfo : projects) { + // Load the Qute project + loadQuteProject(projectInfo); + } + // 2. Update project dependencies + for (ProjectInfo projectInfo : projects) { + if (projectInfo.getProjectDependencyUris() != null && !projectInfo.getProjectDependencyUris().isEmpty()) { + QuteProject project = getProject(projectInfo.getUri()); + for (String projectDependencyUri : projectInfo.getProjectDependencyUris()) { + QuteProject projectDependency = getProject(projectDependencyUri); + if (projectDependency != null) { + project.getProjectDependencies().add(projectDependency); + } + } + } + } + + } + /** - * Try to load the Qute project of the given workspace folder. + * Load the Qute project from the given Qute project information. * - * @param workspaceFolder the workspace folder. - * @param progressSupport the LSP client progress support and null otherwise. + * @param projectInfo the project information. */ - public void tryToLoadQuteProject(WorkspaceFolder workspaceFolder, ProgressSupport progressSupport) { - String projectName = workspaceFolder.getName(); - + private QuteProject loadQuteProject(ProjectInfo projectInfo) { + // Get the LSP client progress support (or null if LSP client cannot support + // progress) + ProgressSupport progressSupport = progressSupportProvider.get(); + String projectName = projectInfo.getUri(); String progressId = createAndStartProgress(projectName, progressSupport); - // Load Qute project from the Java component (collect Java data model) - String projectUri = workspaceFolder.getUri(); - QuteProjectParams params = new QuteProjectParams(projectUri); - getProjectInfo(params) - .thenAccept(projectInfo -> { - if (projectInfo == null) { - // The workspace folder is not a Qute project, end the process - endProgress(progressId, progressSupport); - return; - } - - // The workspace folder is a Qute project, load the data model from Java - // component - QuteProject project = getProject(projectInfo, false); + QuteProject project = getProject(projectInfo, false); + if (progressSupport != null) { + WorkDoneProgressReport report = new WorkDoneProgressReport(); + report.setMessage("Loading data model for '" + projectName + "' Qute project."); + report.setPercentage(10); + progressSupport.notifyProgress(progressId, report); + } + project.getDataModelProject() + .thenAccept(dataModel -> { + // The Java data model is collected for the project, validate all templates of + // the project if (progressSupport != null) { WorkDoneProgressReport report = new WorkDoneProgressReport(); - report.setMessage("Loading data model for '" + projectName + "' Qute project."); - report.setPercentage(10); + report.setMessage( + "Validating Qute templates for '" + projectName + "' Qute project."); + report.setPercentage(80); progressSupport.notifyProgress(progressId, report); } - project.getDataModelProject() - .thenAccept(dataModel -> { - // The Java data model is collected for the project, validate all templates of - // the project - if (progressSupport != null) { - WorkDoneProgressReport report = new WorkDoneProgressReport(); - report.setMessage( - "Validating Qute templates for '" + projectName + "' Qute project."); - report.setPercentage(80); - progressSupport.notifyProgress(progressId, report); - } - // Validate Qute templates - project.validateClosedTemplates(); - - // End progress - endProgress(progressId, progressSupport); + // Validate Qute templates + project.validateClosedTemplates(); - }).exceptionally((a) -> { - endProgress(progressId, progressSupport); - return null; - }); + // End progress + endProgress(progressId, progressSupport); }).exceptionally((a) -> { endProgress(progressId, progressSupport); return null; }); - + return project; } private static String createAndStartProgress(String projectName, ProgressSupport progressSupport) { @@ -485,4 +492,18 @@ private static void endProgress(String progressId, ProgressSupport progressSuppo progressSupport.notifyProgress(progressId, end); } } + + public void projectAdded(ProjectInfo project) { + loadQuteProject(project); + } + + public void projectRemoved(ProjectInfo projectInfo) { + String projectUri = projectInfo.getUri(); + QuteProject project = getProject(projectUri); + if (project != null) { + project.dispose(); + projects.remove(projectUri); + } + } + } \ No newline at end of file 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 1d78856e3..6e87c42ee 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 @@ -278,7 +278,7 @@ private void validateDataModel(Node parent, Template template, QuteValidationSet } switch (section.getSectionKind()) { case INCLUDE: - validateIncludeSection((IncludeSection) section, diagnostics); + validateIncludeSection((IncludeSection) section, project, diagnostics); break; case CASE: case IS: @@ -461,13 +461,13 @@ private static boolean canChangeContext(Section section) { * Validate #include section. * * @param includeSection the include section + * @param project * @param diagnostics the diagnostics to fill. */ - private static void validateIncludeSection(IncludeSection includeSection, List diagnostics) { + private static void validateIncludeSection(IncludeSection includeSection, QuteProject project, List diagnostics) { Parameter templateParameter = includeSection.getTemplateParameter(); if (templateParameter != null) { // Validate template id, only if project exists. - QuteProject project = includeSection.getOwnerTemplate().getProject(); if (project != null) { // include defines a template to include // ex : {#include base} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDocumentLink.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDocumentLink.java index e19c62a61..052ff0003 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDocumentLink.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDocumentLink.java @@ -78,7 +78,7 @@ private void findDocumentLinks(Node node, Template template, List if (range != null) { Path templateFile = includeSection.getReferencedTemplateFile(); if (templateFile != null) { - String target = templateFile.toUri().toString(); + String target = templateFile.toUri().toASCIIString(); links.add(new DocumentLink(range, target != null ? target : "")); } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionForTemplateIds.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionForTemplateIds.java index a4df5e9d2..4aca22e89 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionForTemplateIds.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionForTemplateIds.java @@ -75,7 +75,14 @@ public CompletableFuture doCompleteTemplateId(CompletionRequest String currentTemplateId = template.getTemplateId(); Range range = getReplaceRange(completionRequest.getNode(), completionRequest.getOffset(), template); + + // Add all templates from the Qute project List documents = new ArrayList<>(project.getDocuments()); + // Add all templates from the Qute project dependencies + for (QuteProject projectDependency : project.getProjectDependencies()) { + documents.addAll(projectDependency.getDocuments()); + } + // Sort template ids to show at first the root files of the // src/main/resources/templates folder (ex : base,main). Collections.sort(documents, TEMPLATE_ID_COMPARATOR); 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 637580f1a..4026f918f 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 @@ -112,7 +112,7 @@ public class QuteAssert { public static final String TEMPLATE_BASE_DIR = "src/test/resources/templates"; - private static final String FILE_URI = "test.qute"; + public static final String FILE_URI = "test.qute"; public static final int USER_TAG_SIZE = 9 /* * #input, #bundleStyle, #form, #title, #simpleTitle, #user, #formElement, @@ -1049,7 +1049,7 @@ private static Template createTemplate(String value, String fileUri, String proj QuteProjectRegistry projectRegistry) { Template template = TemplateParser.parse(value, fileUri != null ? fileUri : FILE_URI); template.setProjectUri(projectUri); - projectRegistry.getProject(new ProjectInfo(projectUri, templateBaseDir)); + projectRegistry.getProject(new ProjectInfo(projectUri, Collections.emptyList(), templateBaseDir)); template.setProjectRegistry(projectRegistry); return template; } diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteLanguageClient.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteLanguageClient.java index 54d394fe2..3cffeab2e 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteLanguageClient.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteLanguageClient.java @@ -12,6 +12,7 @@ package com.redhat.qute.project; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -167,4 +168,19 @@ public CompletableFuture generateMissingJavaMember(GenerateMissin public CompletableFuture getJavadoc(QuteJavadocParams params) { return CompletableFuture.completedFuture(null); } + + @Override + public CompletableFuture> getProjects() { + return CompletableFuture.completedFuture(null); + } + + @Override + public void projectAdded(ProjectInfo project) { + + } + + @Override + public void projectRemoved(ProjectInfo project) { + + } } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProject.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProject.java index 8e09564b1..fe1b2b4fd 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProject.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProject.java @@ -52,11 +52,16 @@ public abstract class MockQuteProject extends QuteProject { public MockQuteProject(ProjectInfo projectInfo, QuteProjectRegistry projectRegistry) { super(projectInfo, projectRegistry, null); - this.typesCache = createTypes(); - this.resolvedTypesCache = createResolvedTypes(); - this.templates = createTemplates(); - this.valueResolvers = createValueResolvers(); - this.namespaceResolverInfos = createNamespaceResolverInfos(); + this.typesCache = new ArrayList<>(); + this.fillJavaTypes(typesCache); + this.resolvedTypesCache = new ArrayList<>(); + this.fillResolvedJavaTypes(resolvedTypesCache); + this.templates = new ArrayList<>(); + this.fillTemplates(templates); + this.valueResolvers = new ArrayList<>(); + this.fillValueResolvers(valueResolvers); + this.namespaceResolverInfos = new HashMap<>(); + this.fillNamespaceResolverInfos(namespaceResolverInfos); } public ResolvedJavaTypeInfo getResolvedJavaTypeSync(String typeName) { @@ -160,15 +165,15 @@ protected static ValueResolverInfo createValueResolver(String namespace, String return resolver; } - protected abstract List createTypes(); + protected abstract void fillJavaTypes(List types); - protected abstract List createResolvedTypes(); + protected abstract void fillResolvedJavaTypes(List resolvedJavaTypes); - protected abstract List> createTemplates(); + protected abstract void fillTemplates(List> templates); - protected abstract List createValueResolvers(); + protected abstract void fillValueResolvers(List valueResolvers); - protected abstract Map createNamespaceResolverInfos(); + protected abstract void fillNamespaceResolverInfos(Map namespaces); public JavaMethodInfo getMethodValueResolver(String typeName, String methodName) { try { @@ -184,4 +189,7 @@ public JavaMethodInfo getMethodValueResolver(String typeName, String methodName) return null; } + protected static String getProjectPath(String projectUri) { + return "src/test/resources/projects/" + projectUri; + } } diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProjectRegistry.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProjectRegistry.java index df9ebcf93..66c5ff677 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProjectRegistry.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/MockQuteProjectRegistry.java @@ -35,6 +35,8 @@ 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.project.multiple.QuteProjectA; +import com.redhat.qute.project.multiple.QuteProjectB; public class MockQuteProjectRegistry extends QuteProjectRegistry { @@ -47,7 +49,7 @@ public class MockQuteProjectRegistry extends QuteProjectRegistry { public static final Range JAVA_STATIC_METHOD_RANGE = new Range(new Position(3, 3), new Position(3, 3)); public MockQuteProjectRegistry() { - super(null, null, null, null, null, null, null, null); + super(null, null, null, null, null, null, null, null, () -> null); super.setDidChangeWatchedFilesSupported(true); } @@ -56,6 +58,12 @@ protected QuteProject createProject(ProjectInfo projectInfo) { if (QuteQuickStartProject.PROJECT_URI.equals(projectInfo.getUri())) { return new QuteQuickStartProject(projectInfo, this); } + if (QuteProjectA.PROJECT_URI.equals(projectInfo.getUri())) { + return new QuteProjectA(this); + } + if (QuteProjectB.PROJECT_URI.equals(projectInfo.getUri())) { + return new QuteProjectB(this); + } return super.createProject(projectInfo); } diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java index 60850fad4..c910088cb 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java @@ -11,7 +11,6 @@ *******************************************************************************/ package com.redhat.qute.project; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -56,11 +55,9 @@ public QuteQuickStartProject(ProjectInfo projectInfo, QuteProjectRegistry projec } @Override - protected List createResolvedTypes() { - List cache = new ArrayList<>(); + protected void fillResolvedJavaTypes(List cache) { createBinaryTypes(cache); createSourceTypes(cache); - return cache; } private void createBinaryTypes(List cache) { @@ -348,11 +345,9 @@ private void createSourceTypes(List cache) { } @Override - protected List> createTemplates() { - List> templates = new ArrayList<>(); + protected void fillTemplates(List> templates) { createItemsTemplate(templates); createItemsNativeTemplate(templates); - return templates; } private static void createItemsTemplate(List> templates) { @@ -445,8 +440,7 @@ private static void createItemsNativeTemplate(List createValueResolvers() { - List resolvers = new ArrayList<>(); + protected void fillValueResolvers(List resolvers) { // Type value resolvers resolvers.add(createValueResolver("inject", "plexux", null, @@ -507,7 +501,8 @@ protected List createValueResolvers() { ValueResolverKind.Renarde, false, false)); // Web bundler 'bundle" field as global - resolvers.add(createValueResolver(null, "bundle", null, "util.Globals", "bundle : java.util.Map", + resolvers.add(createValueResolver(null, "bundle", null, "util.Globals", + "bundle : java.util.Map", ValueResolverKind.TemplateGlobal, true)); // Type-safe Message Bundles support @@ -518,7 +513,7 @@ protected List createValueResolvers() { hello_nameData.setMessage("Hello {name ?: 'Qute'}"); hello_name.setData(hello_nameData); resolvers.add(hello_name); - + ValueResolverInfo hello = createValueResolver("msg2", null, null, "org.acme.App2Messages", "hello() : java.lang.String", ValueResolverKind.Message, false, false); @@ -526,27 +521,22 @@ protected List createValueResolvers() { helloData.setMessage("Hello!"); hello.setData(helloData); resolvers.add(hello); - - return resolvers; + } @Override - protected List createTypes() { - List cache = new ArrayList<>(); + protected void fillJavaTypes(List cache) { createJavaTypeInfo("java.util.List", JavaTypeKind.Interface, cache); createJavaTypeInfo("java.util.Map", JavaTypeKind.Interface, cache); - return cache; } @Override - protected Map createNamespaceResolverInfos() { - Map infos = new HashMap<>(); + protected void fillNamespaceResolverInfos(Map infos) { NamespaceResolverInfo inject = new NamespaceResolverInfo(); inject.setNamespaces(Arrays.asList("inject", "cdi")); inject.setDescription( "A CDI bean annotated with `@Named` can be referenced in any template through `cdi` and/or `inject` namespaces."); inject.setUrl("https://quarkus.io/guides/qute-reference#injecting-beans-directly-in-templates"); infos.put("inject", inject); - return infos; } } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectA.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectA.java new file mode 100644 index 000000000..443ca92ae --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectA.java @@ -0,0 +1,62 @@ +/******************************************************************************* +* Copyright (c) 2023 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.project.multiple; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.redhat.qute.commons.JavaTypeInfo; +import com.redhat.qute.commons.ProjectInfo; +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.datamodel.DataModelParameter; +import com.redhat.qute.commons.datamodel.DataModelTemplate; +import com.redhat.qute.commons.datamodel.resolvers.NamespaceResolverInfo; +import com.redhat.qute.commons.datamodel.resolvers.ValueResolverInfo; +import com.redhat.qute.project.MockQuteProject; +import com.redhat.qute.project.QuteProjectRegistry; + +public class QuteProjectA extends MockQuteProject { + + public final static String PROJECT_URI = "project-a"; + + public QuteProjectA(QuteProjectRegistry projectRegistry) { + super(new ProjectInfo(PROJECT_URI, Collections.emptyList(), getProjectPath(PROJECT_URI) + "/src/main/resources/templates"), + projectRegistry); + } + + @Override + protected void fillJavaTypes(List types) { + + } + + @Override + protected void fillResolvedJavaTypes(List resolvedJavaTypes) { + + } + + @Override + protected void fillTemplates(List> templates) { + + } + + @Override + protected void fillValueResolvers(List valueResolvers) { + + } + + @Override + protected void fillNamespaceResolverInfos(Map namespaces) { + + } + +} diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectB.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectB.java new file mode 100644 index 000000000..a31210a7e --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/multiple/QuteProjectB.java @@ -0,0 +1,65 @@ +/******************************************************************************* +* Copyright (c) 2023 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.project.multiple; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.redhat.qute.commons.JavaTypeInfo; +import com.redhat.qute.commons.ProjectInfo; +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.datamodel.DataModelParameter; +import com.redhat.qute.commons.datamodel.DataModelTemplate; +import com.redhat.qute.commons.datamodel.resolvers.NamespaceResolverInfo; +import com.redhat.qute.commons.datamodel.resolvers.ValueResolverInfo; +import com.redhat.qute.project.MockQuteProject; +import com.redhat.qute.project.QuteProjectRegistry; + +public class QuteProjectB extends MockQuteProject { + + public final static String PROJECT_URI = "project-b"; + + public QuteProjectB(QuteProjectRegistry projectRegistry) { + super(new ProjectInfo(PROJECT_URI, Collections.emptyList(), getProjectPath(PROJECT_URI) + "/src/main/resources/templates"), + projectRegistry); + // project-b dependends from project-a + super.getProjectDependencies().add(projectRegistry.getProject( + new ProjectInfo(QuteProjectA.PROJECT_URI, Collections.emptyList(), ""))); + } + + @Override + protected void fillJavaTypes(List types) { + + } + + @Override + protected void fillResolvedJavaTypes(List resolvedJavaTypes) { + + } + + @Override + protected void fillTemplates(List> templates) { + + } + + @Override + protected void fillValueResolvers(List valueResolvers) { + + } + + @Override + protected void fillNamespaceResolverInfos(Map namespaces) { + + } + +} diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/commands/QuteGenerateTemplateContentCommandHandlerTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/commands/QuteGenerateTemplateContentCommandHandlerTest.java index 2c5d89239..cc0f6ae04 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/commands/QuteGenerateTemplateContentCommandHandlerTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/commands/QuteGenerateTemplateContentCommandHandlerTest.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; @@ -34,7 +35,7 @@ public void generateItem() throws InterruptedException, ExecutionException, Exce private QuteProjectRegistry createProjectRegistry() { QuteProjectRegistry projectRegistry = new MockQuteProjectRegistry(); - projectRegistry.getProject(new ProjectInfo(PROJECT_URI, TEMPLATE_BASE_DIR)); + projectRegistry.getProject(new ProjectInfo(PROJECT_URI, Collections.emptyList(), TEMPLATE_BASE_DIR)); return projectRegistry; } diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/multiple/QuteCompletionWithIncludeSectionTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/multiple/QuteCompletionWithIncludeSectionTest.java new file mode 100644 index 000000000..e98ecadd3 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/multiple/QuteCompletionWithIncludeSectionTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* +* Copyright (c) 2023 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.services.completions.multiple; + +import static com.redhat.qute.QuteAssert.c; +import static com.redhat.qute.QuteAssert.r; + +import org.eclipse.lsp4j.CompletionItem; +import org.junit.jupiter.api.Test; + +import com.redhat.qute.QuteAssert; +import com.redhat.qute.project.multiple.QuteProjectB; + +/** + * Tests for Qute completion with #include section and project dependencies. + * + * @author Angelo ZERR + * + */ +public class QuteCompletionWithIncludeSectionTest { + + @Test + public void includeTemplateIds() throws Exception { + String template = "{#include |} \r\n" + + " |\r\n" + + "{/include}"; + + // Without snippet + testCompletionFor(template, // + false, // no snippet support + QuteProjectB.PROJECT_URI, + 2, // + c("root", "root", r(0, 10, 0, 10)), + c("index", "index", r(0, 10, 0, 10))); + + } + + @Test + public void includeTemplateIdsSelf() throws Exception { + String template = "{#include |} \r\n" + + " |\r\n" + + "{/include}"; + + // Without snippet + testCompletionFor(template, // + "src/test/resources/projects/project-b/src/main/resources/templates/index.html", + false, // no snippet support + QuteProjectB.PROJECT_URI, + 1, // + c("root", "root", r(0, 10, 0, 10)) // , + // c("index", "index", r(0, 10, 0, 10)) + ); + + } + + private static void testCompletionFor(String value, boolean snippetSupport, + String projectUri, Integer expectedCount, CompletionItem... expectedItems) + throws Exception { + testCompletionFor(value, QuteAssert.FILE_URI, snippetSupport, projectUri, expectedCount, expectedItems); + } + + private static void testCompletionFor(String value, String fileUri, boolean snippetSupport, + String projectUri, Integer expectedCount, CompletionItem... expectedItems) + throws Exception { + QuteAssert.testCompletionFor(value, snippetSupport, fileUri, null, projectUri, "", expectedCount, + expectedItems); + } + +} \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/multiple/QuteDiagnosticsWithIncludeSectionTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/multiple/QuteDiagnosticsWithIncludeSectionTest.java new file mode 100644 index 000000000..fdf91a827 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/multiple/QuteDiagnosticsWithIncludeSectionTest.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* 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.services.diagnostics.multiple; + +import org.eclipse.lsp4j.Diagnostic; +import org.junit.jupiter.api.Test; + +import com.redhat.qute.QuteAssert; +import com.redhat.qute.project.multiple.QuteProjectB; + +/** + * Test with #include section and project dependencies. + * + * @author Angelo ZERR + * + */ +public class QuteDiagnosticsWithIncludeSectionTest { + + @Test + public void templateFromProjectBFoundInProjectA() throws Exception { + String template = "{#include root.html }\r\n" + + "{/include}"; + testDiagnosticsFor(template, QuteProjectB.PROJECT_URI); + } + + @Test + public void templateFromProjectBFoundInProjectAdWithShortSyntax() throws Exception { + String template = "{#include root }\r\n" + + "{/include}"; + testDiagnosticsFor(template, QuteProjectB.PROJECT_URI); + } + + public static void testDiagnosticsFor(String value, String projectUri, Diagnostic... expected) { + QuteAssert.testDiagnosticsFor(value, QuteAssert.FILE_URI, null, projectUri, null, false, + null, expected); + } +} diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/project/AbstractQuteDiagnosticsInProjectTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/project/AbstractQuteDiagnosticsInProjectTest.java index 1197881d8..3b16697cb 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/project/AbstractQuteDiagnosticsInProjectTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/project/AbstractQuteDiagnosticsInProjectTest.java @@ -18,6 +18,9 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -53,10 +56,23 @@ public QuteQuickStartProjectLanguageServer() { @Override public CompletableFuture getProjectInfo(QuteProjectParams params) { + ProjectInfo projectInfo = createQuickStartProject(); + return CompletableFuture.completedFuture(projectInfo); + } + + @Override + public CompletableFuture> getProjects() { + Collection projects = Arrays.asList(createQuickStartProject()); + return CompletableFuture.completedFuture(projects); + } + + private ProjectInfo createQuickStartProject() { ProjectInfo projectInfo = new ProjectInfo(QuteQuickStartProject.PROJECT_URI, + Collections.emptyList(), FileUtils.toUri(templatesPath)); - return CompletableFuture.completedFuture(projectInfo); + return projectInfo; }; + } private final boolean didChangeWatchedFilesSupported; diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/QuteDocumentLinkTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/QuteDocumentLinkTest.java similarity index 94% rename from qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/QuteDocumentLinkTest.java rename to qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/QuteDocumentLinkTest.java index 7dc6300bd..15dff57e8 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/QuteDocumentLinkTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/QuteDocumentLinkTest.java @@ -9,7 +9,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package com.redhat.qute.services; +package com.redhat.qute.services.documentlink; import static com.redhat.qute.QuteAssert.dl; import static com.redhat.qute.QuteAssert.r; diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/multiple/QuteDocumentLinkTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/multiple/QuteDocumentLinkTest.java new file mode 100644 index 000000000..ec489e276 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/documentlink/multiple/QuteDocumentLinkTest.java @@ -0,0 +1,42 @@ +/******************************************************************************* +* Copyright (c) 2023 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.services.documentlink.multiple; + +import static com.redhat.qute.QuteAssert.dl; +import static com.redhat.qute.QuteAssert.r; +import static com.redhat.qute.QuteAssert.testDocumentLinkFor; + +import org.junit.jupiter.api.Test; + +import com.redhat.qute.project.multiple.QuteProjectB; + +/** + * Tests for Qute document link and project dependencies. + * + * @author Angelo ZERR + * + */ +public class QuteDocumentLinkTest { + + @Test + public void includeExistingTemplate() throws Exception { + // "src/main/resources/templates/index.html" from project-b references + // "src/main/resources/templates/root.html" from project-a + String template = "{#include root }\r\n" + + "{/include}"; + testDocumentLinkFor(template, "src/main/resources/index.html", // + QuteProjectB.PROJECT_URI, "", + dl(r(0, 10, 0, 14), + "src/test/resources/projects/project-a/src/main/resources/templates/root.html")); + } + +} diff --git a/qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-a/src/main/resources/templates/root.html b/qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-a/src/main/resources/templates/root.html new file mode 100644 index 000000000..54720a427 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-a/src/main/resources/templates/root.html @@ -0,0 +1,3 @@ +hi from root template in project-a + +

{#insert /}

diff --git a/qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-b/src/main/resources/templates/index.html b/qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-b/src/main/resources/templates/index.html new file mode 100644 index 000000000..b65d534af --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/resources/projects/project-b/src/main/resources/templates/index.html @@ -0,0 +1,9 @@ +{#include root} + message from project-b +

+ {#inputLabel title="auto complete works fine in input label" /} +

+

+ {#inputLabelText title="title property works but not the auto complete" /} +

+{/include} \ No newline at end of file