diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractDocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractDocumentMatcher.java new file mode 100644 index 000000000..fa37cac05 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractDocumentMatcher.java @@ -0,0 +1,60 @@ +package com.redhat.devtools.intellij.lsp4ij; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.NonBlockingReadAction; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.concurrency.AppExecutorUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.concurrency.CancellablePromise; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; + +public abstract class AbstractDocumentMatcher implements DocumentMatcher { + + private class CompletableFutureWrapper extends CompletableFuture { + + public CompletableFutureWrapper(CancellablePromise promise) { + // On error + promise.onError(ex -> { + if (ex instanceof ProcessCanceledException || ex instanceof CancellationException) { + // Case 2: cancel the completable future + this.cancel(true); + } else { + // Other case..., mark the completable future as error + this.completeExceptionally(ex); + } + }); + // On sucess + promise.onSuccess(value -> { + this.complete(value); + }); + } + } + + @Override + public @NotNull CompletableFuture matchAsync(@NotNull VirtualFile file, @NotNull Project project) { + var action = ReadAction.nonBlocking(() -> { + return match(file, project); + }); + if (DumbService.getInstance(project).isDumb()) { + action = action.inSmartMode(project); + } + action = action.coalesceBy(AbstractDocumentMatcher.class, file.getUrl()); + var promise = action + .submit(AppExecutorUtil.getAppExecutorService()); + return new CompletableFutureWrapper(promise); + } + + @Override + public boolean shouldBeMatchedAsynchronously(@NotNull Project project) { + if (!ApplicationManager.getApplication().isReadAccessAllowed()) { + return true; + } + return DumbService.getInstance(project).isDumb(); + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java index 703cefb03..020318c42 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java @@ -1,17 +1,37 @@ package com.redhat.devtools.intellij.lsp4ij; import com.intellij.lang.Language; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; -import javax.annotation.Nonnull; import java.util.AbstractMap; +import java.util.concurrent.CompletableFuture; public class ContentTypeToLanguageServerDefinition extends AbstractMap.SimpleEntry { - public ContentTypeToLanguageServerDefinition(@Nonnull Language language, - @Nonnull LanguageServersRegistry.LanguageServerDefinition provider) { + + private final DocumentMatcher documentMatcher; + + public ContentTypeToLanguageServerDefinition(@NotNull Language language, + @NotNull LanguageServersRegistry.LanguageServerDefinition provider, + @NotNull DocumentMatcher documentMatcher) { super(language, provider); + this.documentMatcher = documentMatcher; + } + + public boolean match(VirtualFile file, Project project) { + return documentMatcher.match(file, project); + } + + public boolean shouldBeMatchedAsynchronously(Project project) { + return documentMatcher.shouldBeMatchedAsynchronously(project); } public boolean isEnabled() { - return true; + return getValue().isEnabled(); + } + + public @NotNull CompletableFuture matchAsync(VirtualFile file, Project project) { + return documentMatcher.matchAsync(file, project); } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentMatcher.java new file mode 100644 index 000000000..9384951ce --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentMatcher.java @@ -0,0 +1,21 @@ +package com.redhat.devtools.intellij.lsp4ij; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.concurrency.CancellablePromise; + +import java.util.concurrent.CompletableFuture; + +public interface DocumentMatcher { + + boolean match(@NotNull VirtualFile file, @NotNull Project project); + + default @NotNull CompletableFuture matchAsync(@NotNull VirtualFile file, @NotNull Project project) { + return CompletableFuture.completedFuture(match(file,project)); + } + + default boolean shouldBeMatchedAsynchronously(@NotNull Project project) { + return false; + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java index 7bfedcf8a..ba1d74778 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java @@ -2,9 +2,15 @@ import com.intellij.openapi.extensions.AbstractExtensionPointBean; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.serviceContainer.BaseKeyedLazyInstance; import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LanguageMappingExtensionPointBean extends BaseKeyedLazyInstance { + + private static final DocumentMatcher DEFAULT_DOCUMENT_MATCHER = (file,project) -> true; -public class LanguageMappingExtensionPointBean extends AbstractExtensionPointBean { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.languageMapping"); @Attribute("id") @@ -15,4 +21,21 @@ public class LanguageMappingExtensionPointBean extends AbstractExtensionPointBea @Attribute("serverId") public String serverId; + + @Attribute("documentMatcher") + public String documentMatcher; + + public @NotNull DocumentMatcher getDocumentMatcher() { + try { + return super.getInstance(); + } + catch(Exception e) { + return DEFAULT_DOCUMENT_MATCHER; + } + } + + @Override + protected @Nullable String getImplementationClassName() { + return documentMatcher; + } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java index 882255e1a..80d0c2574 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java @@ -20,6 +20,7 @@ import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.jsonrpc.validation.NonNull; import org.eclipse.lsp4j.services.LanguageServer; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -194,7 +195,7 @@ private void initialize() { for (LanguageMappingExtensionPointBean extension : LanguageMappingExtensionPointBean.EP_NAME.getExtensions()) { Language language = Language.findLanguageByID(extension.language); if (language != null) { - languageMappings.add(new LanguageMapping(language, extension.id, extension.serverId)); + languageMappings.add(new LanguageMapping(language, extension.id, extension.serverId, extension.getDocumentMatcher())); } } @@ -205,7 +206,7 @@ private void initialize() { for (LanguageMapping mapping : languageMappings) { LanguageServerDefinition lsDefinition = servers.get(mapping.languageId); if (lsDefinition != null) { - registerAssociation(mapping.language, lsDefinition, mapping.languageId); + registerAssociation(lsDefinition, mapping); } else { LOGGER.warn("server '" + mapping.id + "' not available"); //$NON-NLS-1$ //$NON-NLS-2$ } @@ -223,20 +224,21 @@ public Icon getServerIcon(String serverId) { * @return the {@link LanguageServerDefinition}s directly associated to the given content-type. * This does not include the one that match transitively as per content-type hierarchy */ - List findProviderFor(final @NonNull Language contentType) { + List findProviderFor(final @NotNull Language contentType) { return connections.stream() .filter(entry -> contentType.isKindOf(entry.getKey())) .collect(Collectors.toList()); } - public void registerAssociation(@Nonnull Language language, - @Nonnull LanguageServerDefinition serverDefinition, @Nullable String languageId) { + public void registerAssociation(@NotNull LanguageServerDefinition serverDefinition, @Nullable LanguageMapping mapping) { + @NotNull Language language = mapping.language; + @Nullable String languageId = mapping.languageId; if (languageId != null) { serverDefinition.registerAssociation(language, languageId); } - connections.add(new ContentTypeToLanguageServerDefinition(language, serverDefinition)); + connections.add(new ContentTypeToLanguageServerDefinition(language, serverDefinition, mapping.getDocumentMatcher())); } public List getContentTypeToLSPExtensions() { @@ -258,19 +260,25 @@ LanguageServerDefinition getDefinition(@NonNull String languageServerId) { */ private static class LanguageMapping { - @Nonnull + @NotNull public final String id; - @Nonnull + @NotNull public final Language language; @Nullable public final String languageId; - public LanguageMapping(@Nonnull Language language, @Nonnull String id, @Nullable String languageId) { + private final DocumentMatcher documentMatcher; + + public LanguageMapping(@NotNull Language language, @Nullable String id, @Nullable String languageId, @NotNull DocumentMatcher documentMatcher) { this.language = language; this.id = id; this.languageId = languageId; + this.documentMatcher = documentMatcher; } + public DocumentMatcher getDocumentMatcher() { + return documentMatcher; + } } /** @@ -278,19 +286,20 @@ public LanguageMapping(@Nonnull Language language, @Nonnull String id, @Nullable * @param serverDefinition * @return whether the given serverDefinition is suitable for the file */ - public boolean matches(@Nonnull VirtualFile file, @NonNull LanguageServerDefinition serverDefinition, - Project project) { - return getAvailableLSFor(LSPIJUtils.getFileLanguage(file, project)).contains(serverDefinition); - } - - /** - * @param document - * @param serverDefinition - * @return whether the given serverDefinition is suitable for the file - */ - public boolean matches(@Nonnull Document document, @Nonnull LanguageServerDefinition serverDefinition, - Project project) { - return getAvailableLSFor(LSPIJUtils.getDocumentLanguage(document, project)).contains(serverDefinition); + public boolean matches(VirtualFile file, + Project project, @Nonnull LanguageServerDefinition serverDefinition) { + Language language = LSPIJUtils.getFileLanguage(file, project); + if (language == null) { + return false; + } + for (ContentTypeToLanguageServerDefinition mapping : this.connections) { + if (language.isKindOf(mapping.getKey()) && serverDefinition.equals(mapping.getValue())) { + if (mapping.match(file, project)) { + return true; + } + } + } + return false; } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java index 3f2720ecf..27560f856 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java @@ -51,29 +51,41 @@ private LanguageServiceAccessor(Project project) { @NotNull public CompletableFuture> getLanguageServers(@NotNull VirtualFile file, - Predicate filter) { + Predicate filter) { URI uri = LSPIJUtils.toUri(file); if (uri == null) { return CompletableFuture.completedFuture(Collections.emptyList()); } + + // Collect started (or not) language servers which matches the given file. + CompletableFuture> matchedServers = getMatchedLanguageServersWrappers(file); + if (matchedServers.isDone() && matchedServers.getNow(Collections.emptyList()).isEmpty()) { + // None language servers matches the given file + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + // Returns the language servers which match the given file, start them and connect the file to each matched language server final List servers = Collections.synchronizedList(new ArrayList<>()); try { - return CompletableFuture.allOf(getLSWrappers(file).stream().map(wrapper -> - wrapper.getInitializedServer() - .thenComposeAsync(server -> { - if (server != null && wrapper.isEnabled() && (filter == null || filter.test(wrapper.getServerCapabilities()))) { - try { - return wrapper.connect(file); - } catch (IOException ex) { - LOGGER.warn(ex.getLocalizedMessage(), ex); - } - } - return CompletableFuture.completedFuture(null); - }).thenAccept(server -> { - if (server != null) { - servers.add(new LanguageServerItem(server, wrapper)); - } - })).toArray(CompletableFuture[]::new)) + return matchedServers + .thenComposeAsync(result -> CompletableFuture.allOf(result + .stream() + .map(wrapper -> + wrapper.getInitializedServer() + .thenComposeAsync(server -> { + if (server != null && wrapper.isEnabled() && (filter == null || filter.test(wrapper.getServerCapabilities()))) { + try { + return wrapper.connect(file); + } catch (IOException ex) { + LOGGER.warn(ex.getLocalizedMessage(), ex); + } + } + return CompletableFuture.completedFuture(null); + }).thenAccept(server -> { + if (server != null) { + servers.add(new LanguageServerItem(server, wrapper)); + } + })).toArray(CompletableFuture[]::new))) .thenApply(theVoid -> servers); } catch (final ProcessCanceledException cancellation) { throw cancellation; @@ -105,8 +117,8 @@ public void projectClosing(Project project) { * Get the requested language server instance for the given file. Starts the * language server if not already started. * - * @param file the file for which the initialized LanguageServer shall be returned - * @param lsDefinition the language server definition + * @param file the file for which the initialized LanguageServer shall be returned + * @param lsDefinition the language server definition * @param capabilitiesPredicate a predicate to check capabilities * @return a LanguageServer for the given file, which is defined with provided * server ID and conforms to specified request. If @@ -145,65 +157,136 @@ private static boolean capabilitiesComply(LanguageServerWrapper wrapper, } @NotNull - private Collection getLSWrappers(@NotNull VirtualFile file) { - LinkedHashSet res = new LinkedHashSet<>(); - URI uri = LSPIJUtils.toUri(file); - if (uri == null) { - return Collections.emptyList(); + private CompletableFuture> getMatchedLanguageServersWrappers(@NotNull VirtualFile file) { + final Project fileProject = LSPIJUtils.getProject(file); + if (fileProject == null) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + MatchedLanguageMappings mappings = getMatchedLanguageMapping(file, fileProject); + if (mappings == MatchedLanguageMappings.NO_MATCH) { + // There are no mapping for the given file + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + LinkedHashSet matchedServers = new LinkedHashSet<>(); + var serverDefinitions = mappings.getMatchedLanguageServersDefinition(); + fillMatchedServers(file, serverDefinitions, matchedServers, fileProject); + + CompletableFuture> async = mappings.getAsync(); + if (async != null) { + return async + .thenApply(defs -> { + fillMatchedServers(file, defs, matchedServers, fileProject); + return matchedServers; + }); + } + return CompletableFuture.completedFuture(matchedServers); + } + + private void fillMatchedServers(@NotNull VirtualFile file, Set serverDefinitions, LinkedHashSet matchedServers, Project fileProject) { + synchronized (startedServers) { + for (var serverDefinition : serverDefinitions) { + boolean useExistingServer = false; + // Loop for started language servers + for (var startedServer : startedServers) { + if (startedServer.serverDefinition.equals(serverDefinition) + && startedServer.canOperate(file)) { + // A started language server match the file, use it + matchedServers.add(startedServer); + useExistingServer = true; + break; + } + } + if (!useExistingServer) { + // There are none started servers which matches the file, create and add it. + LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition); + startedServers.add(wrapper); + matchedServers.add(wrapper); + } + } + } + } + + private static class MatchedLanguageMappings { + + public static final MatchedLanguageMappings NO_MATCH = new MatchedLanguageMappings(Collections.emptySet(), null); + + private final Set matchedLanguageServersDefinition; + + private final CompletableFuture> async; + + public MatchedLanguageMappings(@NotNull Set matchedLanguageServersDefinition, CompletableFuture> async) { + this.matchedLanguageServersDefinition = matchedLanguageServersDefinition; + this.async = async; + } + + public @NotNull Set getMatchedLanguageServersDefinition() { + return matchedLanguageServersDefinition; + } + + public CompletableFuture> getAsync() { + return async; } - URI path = uri; + } + + private MatchedLanguageMappings getMatchedLanguageMapping(@NotNull VirtualFile file, @NotNull Project fileProject) { + + Set matchedLanguageServersDefinition = null; + Set mappingsToEvaluateAsynchronously = null; // look for running language servers via content-type Queue contentTypes = new LinkedList<>(); Set processedContentTypes = new HashSet<>(); contentTypes.add(LSPIJUtils.getFileLanguage(file, project)); - synchronized (startedServers) { - // already started compatible servers that fit request - res.addAll(startedServers.stream() - .filter(wrapper -> { - try { - return wrapper.isEnabled() && (wrapper.isConnectedTo(path) || LanguageServersRegistry.getInstance().matches(file, wrapper.serverDefinition, project)); - } catch (ProcessCanceledException cancellation) { - throw cancellation; - } catch (Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - return false; - } - }) - .filter(wrapper -> wrapper.canOperate(file)) - .collect(Collectors.toList())); - - while (!contentTypes.isEmpty()) { - Language contentType = contentTypes.poll(); - if (contentType == null || processedContentTypes.contains(contentType)) { + while (!contentTypes.isEmpty()) { + Language contentType = contentTypes.poll(); + if (contentType == null || processedContentTypes.contains(contentType)) { + continue; + } + for (ContentTypeToLanguageServerDefinition mapping : LanguageServersRegistry.getInstance() + .findProviderFor(contentType)) { + if (mapping == null || !mapping.isEnabled() || (matchedLanguageServersDefinition != null && matchedLanguageServersDefinition.contains(mapping.getValue()))) { continue; } - for (ContentTypeToLanguageServerDefinition mapping : LanguageServersRegistry.getInstance() - .findProviderFor(contentType)) { - if (mapping == null || !mapping.isEnabled()) { - continue; - } - LanguageServersRegistry.LanguageServerDefinition serverDefinition = mapping.getValue(); - if (serverDefinition == null) { - continue; + if (mapping.shouldBeMatchedAsynchronously(fileProject)) { + if (mappingsToEvaluateAsynchronously == null) { + mappingsToEvaluateAsynchronously = new HashSet<>(); } - if (startedServers.stream().anyMatch(wrapper -> wrapper.serverDefinition.equals(serverDefinition) - && wrapper.canOperate(file))) { - // we already checked a compatible LS with this definition - continue; - } - final Project fileProject = file != null ? LSPIJUtils.getProject(file) : null; - if (fileProject != null) { - LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition); - startedServers.add(wrapper); - res.add(wrapper); + mappingsToEvaluateAsynchronously.add(mapping); + } else { + if (mapping.match(file, fileProject)) { + if (matchedLanguageServersDefinition == null) { + matchedLanguageServersDefinition = new HashSet<>(); + } + matchedLanguageServersDefinition.add(mapping.getValue()); } } - processedContentTypes.add(contentType); } - return res; } + if (matchedLanguageServersDefinition != null || mappingsToEvaluateAsynchronously != null) { + CompletableFuture> async = null; + if (mappingsToEvaluateAsynchronously != null) { + final Set serverDefinitions = Collections.synchronizedSet(new HashSet<>()); + async = CompletableFuture.allOf(mappingsToEvaluateAsynchronously + .stream() + .map(mapping -> { + return mapping + .matchAsync(file, fileProject) + .thenApply(result -> { + if (result) { + serverDefinitions.add(mapping.getValue()); + } + return null; + }); + } + ) + .toArray(CompletableFuture[]::new)) + .thenApply(theVoid -> serverDefinitions); + } + return new MatchedLanguageMappings(matchedLanguageServersDefinition != null ? matchedLanguageServersDefinition : Collections.emptySet(), async); + } + return MatchedLanguageMappings.NO_MATCH; } private LanguageServerWrapper getLSWrapperForConnection(VirtualFile file, @@ -242,18 +325,6 @@ private List getStartedLSWrappers(Predicate getMatchingStartedWrappers(@NotNull VirtualFile file, - @Nullable Predicate request) { - synchronized (startedServers) { - return startedServers.stream().filter(wrapper -> wrapper.isConnectedTo(LSPIJUtils.toUri(file)) - || (LanguageServersRegistry.getInstance().matches(file, wrapper.serverDefinition, project) - && wrapper.canOperate(LSPIJUtils.getProject(file)))).filter(wrapper -> request == null - || (wrapper.getServerCapabilities() == null || request.test(wrapper.getServerCapabilities()))) - .collect(Collectors.toList()); - } - } - /** * Gets list of running LS satisfying a capability predicate. This does not * start any matching language servers, it returns the already running ones. diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java new file mode 100644 index 000000000..f727c5c58 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java @@ -0,0 +1,19 @@ +package com.redhat.devtools.intellij.quarkus.lsp; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.intellij.lsp4ij.AbstractDocumentMatcher; +import com.redhat.devtools.intellij.lsp4ij.DocumentMatcher; +import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; +import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils; + +public class AbstractQuarkusDocumentMatcher extends AbstractDocumentMatcher { + @Override + public boolean match(VirtualFile file, Project fileProject) { + Module module = LSPIJUtils.getModule(file); + return module != null && QuarkusModuleUtil.isQuarkusModule(module); + } +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusDocumentMatcherForJavaFile.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusDocumentMatcherForJavaFile.java new file mode 100644 index 000000000..fc224cc52 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusDocumentMatcherForJavaFile.java @@ -0,0 +1,12 @@ +package com.redhat.devtools.intellij.quarkus.lsp; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; + +public class QuarkusDocumentMatcherForJavaFile extends AbstractQuarkusDocumentMatcher { + +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusDocumentMatcherForPropertiesFile.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusDocumentMatcherForPropertiesFile.java new file mode 100644 index 000000000..65936bcf7 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusDocumentMatcherForPropertiesFile.java @@ -0,0 +1,21 @@ +package com.redhat.devtools.intellij.quarkus.lsp; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; + +public class QuarkusDocumentMatcherForPropertiesFile extends AbstractQuarkusDocumentMatcher { + + @Override + public boolean match(VirtualFile file, Project fileProject) { + if (!matchFile(file, fileProject)) { + return false; + } + return super.match(file, fileProject); + } + + private boolean matchFile(VirtualFile file, Project fileProject) { + return QuarkusModuleUtil.isQuarkusPropertiesFile(file, fileProject) || QuarkusModuleUtil.isQuarkusYAMLFile(file, fileProject); + } +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java index 788e0131e..ed4bb119f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java @@ -25,6 +25,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils.isQuteTemplate; + /** * Qute language substitutor to force some language file (ex:HTML, YAML, etc) to "_Qute" language when: *
    @@ -34,7 +36,7 @@ */ public class QuteLanguageSubstitutor extends LanguageSubstitutor { protected boolean isTemplate(VirtualFile file, Module module) { - return file.getPath().contains("templates") && + return isQuteTemplate(file, module) && ModuleRootManager.getInstance(module).getFileIndex().isInSourceContent(file); } diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java new file mode 100644 index 000000000..dbf053d69 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java @@ -0,0 +1,19 @@ +package com.redhat.devtools.intellij.qute.lsp; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.intellij.lsp4ij.AbstractDocumentMatcher; +import com.redhat.devtools.intellij.lsp4ij.DocumentMatcher; +import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils; + +public class AbstractQuteDocumentMatcher extends AbstractDocumentMatcher { + + @Override + public boolean match(VirtualFile file, Project fileProject) { + Module module = LSPIJUtils.getModule(file); + return module != null && PsiQuteProjectUtils.hasQuteSupport(module); + } +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForJavaFile.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForJavaFile.java new file mode 100644 index 000000000..b3dafa861 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForJavaFile.java @@ -0,0 +1,12 @@ +package com.redhat.devtools.intellij.qute.lsp; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.intellij.lsp4ij.DocumentMatcher; +import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; + +public class QuteDocumentMatcherForJavaFile extends AbstractQuteDocumentMatcher { +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java new file mode 100644 index 000000000..bc88fe1f5 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java @@ -0,0 +1,20 @@ +package com.redhat.devtools.intellij.qute.lsp; + +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils; + +import static com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils.isQuteTemplate; + +public class QuteDocumentMatcherForTemplateFile extends AbstractQuteDocumentMatcher { + + @Override + public boolean match(VirtualFile file, Project fileProject) { + if (!super.match(file, fileProject)) { + return false; + } + return isQuteTemplate(file, LSPIJUtils.getModule(file)); + } +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java index 65d3681f2..27bbcbfbf 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java @@ -14,6 +14,8 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants; @@ -121,4 +123,9 @@ public static void appendAndSlash(@NotNull StringBuilder path, @NotNull String s path.append('/'); } } + + public static boolean isQuteTemplate(VirtualFile file, Module module) { + return file.getPath().contains("templates") && + ModuleRootManager.getInstance(module).getFileIndex().isInSourceContent(file); + } } diff --git a/src/main/resources/META-INF/lsp4ij-quarkus.xml b/src/main/resources/META-INF/lsp4ij-quarkus.xml index 7394280e0..2be18868a 100644 --- a/src/main/resources/META-INF/lsp4ij-quarkus.xml +++ b/src/main/resources/META-INF/lsp4ij-quarkus.xml @@ -1,7 +1,7 @@ - - - - + + + diff --git a/src/main/resources/META-INF/lsp4ij-qute.xml b/src/main/resources/META-INF/lsp4ij-qute.xml index dd65d62f3..b1a26b051 100644 --- a/src/main/resources/META-INF/lsp4ij-qute.xml +++ b/src/main/resources/META-INF/lsp4ij-qute.xml @@ -17,8 +17,12 @@ ]]> - - + +