Skip to content

Commit

Permalink
feat: Check if project is a MicroProfile, Qute, etc project to map file
Browse files Browse the repository at this point in the history
with a language server

Fixes #850

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Sep 28, 2023
1 parent 28559b5 commit 54c9c86
Show file tree
Hide file tree
Showing 17 changed files with 245 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package com.redhat.devtools.intellij.lsp4ij;

import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;

import javax.annotation.Nonnull;
import java.util.AbstractMap;

public class ContentTypeToLanguageServerDefinition extends AbstractMap.SimpleEntry<Language, LanguageServersRegistry.LanguageServerDefinition> {

private final DocumentMatcher documentMatcher;
public ContentTypeToLanguageServerDefinition(@Nonnull Language language,
@Nonnull LanguageServersRegistry.LanguageServerDefinition provider) {
DocumentMatcher documentMatcher, @Nonnull LanguageServersRegistry.LanguageServerDefinition provider) {
super(language, provider);
this.documentMatcher = documentMatcher;
}

public boolean isEnabled() {
return true;
}

public boolean matches(Document document, VirtualFile file, Project project) {
return documentMatcher.match(document, file, project);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.redhat.devtools.intellij.lsp4ij;

import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

public class DefaultDocumentMatcher implements DocumentMatcher{

public static final DocumentMatcher INSTANCE = new DefaultDocumentMatcher();

@Override
public boolean match(Document document, VirtualFile file, Project fileProject) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.redhat.devtools.intellij.lsp4ij;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;

public interface DocumentMatcher {

boolean match(Document document, VirtualFile file, Project fileProject);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import com.intellij.openapi.extensions.AbstractExtensionPointBean;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.serviceContainer.BaseKeyedLazyInstance;
import com.intellij.util.xmlb.annotations.Attribute;
import org.jetbrains.annotations.Nullable;

public class LanguageMappingExtensionPointBean extends AbstractExtensionPointBean {
public class LanguageMappingExtensionPointBean extends BaseKeyedLazyInstance<DocumentMatcher> {
public static final ExtensionPointName<LanguageMappingExtensionPointBean> EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.languageMapping");

@Attribute("id")
Expand All @@ -15,4 +18,21 @@ public class LanguageMappingExtensionPointBean extends AbstractExtensionPointBea

@Attribute("serverId")
public String serverId;

@Attribute("documentMatcher")
public String documentMatcher;

public DocumentMatcher getDocumentMatcher() {
try {
return super.getInstance();
}
catch(Exception e) {
return DefaultDocumentMatcher.INSTANCE;
}
}

@Override
protected @Nullable String getImplementationClassName() {
return documentMatcher;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public abstract static class LanguageServerDefinition {
enum Scope {
project, application
}

private static final int DEFAULT_LAST_DOCUMENTED_DISCONNECTED_TIMEOUT = 5;

public final @Nonnull
Expand All @@ -50,6 +51,9 @@ enum Scope {
public final boolean isSingleton;
public final @Nonnull
Map<Language, String> languageIdMappings;

private DocumentMatcher documentMatcher;

public final String description;
public final int lastDocumentDisconnectedTimeout;
private boolean enabled;
Expand All @@ -63,7 +67,7 @@ public LanguageServerDefinition(@Nonnull String id, @Nonnull String label, Strin
this.isSingleton = isSingleton;
this.lastDocumentDisconnectedTimeout = lastDocumentDisconnectedTimeout != null && lastDocumentDisconnectedTimeout > 0 ? lastDocumentDisconnectedTimeout : DEFAULT_LAST_DOCUMENTED_DISCONNECTED_TIMEOUT;
this.languageIdMappings = new ConcurrentHashMap<>();
this.scope = scope == null || scope.isBlank()? Scope.application : Scope.valueOf(scope);
this.scope = scope == null || scope.isBlank() ? Scope.application : Scope.valueOf(scope);
setEnabled(true);
}

Expand Down Expand Up @@ -164,6 +168,7 @@ public Class<? extends LanguageServer> getServerInterface() {
}
return super.getServerInterface();
}

}

private static LanguageServersRegistry INSTANCE = null;
Expand Down Expand Up @@ -194,7 +199,8 @@ 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));
DocumentMatcher documentMatcher = extension.getDocumentMatcher();
languageMappings.add(new LanguageMapping(language, extension.id, extension.serverId, documentMatcher));
}
}

Expand All @@ -205,7 +211,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$
}
Expand All @@ -229,14 +235,15 @@ List<ContentTypeToLanguageServerDefinition> findProviderFor(final @NonNull Langu
.collect(Collectors.toList());
}

private void registerAssociation(@NonNull LanguageServerDefinition serverDefinition, @NonNull LanguageMapping mapping) {

public void registerAssociation(@Nonnull Language language,
@Nonnull LanguageServerDefinition serverDefinition, @Nullable String languageId) {
@Nonnull 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, mapping.getDocumentMatcher(), serverDefinition));
}

public List<ContentTypeToLanguageServerDefinition> getContentTypeToLSPExtensions() {
Expand Down Expand Up @@ -264,46 +271,39 @@ private static class LanguageMapping {
public final Language language;
@Nullable
public final String languageId;
private final DocumentMatcher documentMatcher;

public LanguageMapping(@Nonnull Language language, @Nonnull String id, @Nullable String languageId) {
public LanguageMapping(@Nonnull Language language, @Nonnull String id, @Nullable String languageId, DocumentMatcher documentMatcher) {
this.language = language;
this.id = id;
this.languageId = languageId;
this.documentMatcher = documentMatcher;
}

}

/**
* @param file
* @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);
public DocumentMatcher getDocumentMatcher() {
return documentMatcher;
}
}

/**
* @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);
}


private Set<LanguageServerDefinition> getAvailableLSFor(Language language) {
Set<LanguageServerDefinition> res = new HashSet<>();
if (language != null) {
for (ContentTypeToLanguageServerDefinition mapping : this.connections) {
if (language.isKindOf(mapping.getKey())) {
res.add(mapping.getValue());
public boolean matches(@Nonnull Document document, VirtualFile file,
Project project, @Nonnull LanguageServerDefinition serverDefinition) {
Language language = LSPIJUtils.getDocumentLanguage(document, project);
if (language == null) {
return false;
}
for (ContentTypeToLanguageServerDefinition mapping : this.connections) {
if (language.isKindOf(mapping.getKey()) && serverDefinition.equals(mapping.getValue())) {
if (mapping.matches(document, file, project)) {
return true;
}
}
}
return res;
return false;
}

public Set<LanguageServerDefinition> getAllDefinitions() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ private Collection<LanguageServerWrapper> getLSWrappers(@Nonnull Document docume
res.addAll(startedServers.stream()
.filter(wrapper -> {
try {
return wrapper.isEnabled() && (wrapper.isConnectedTo(path) || LanguageServersRegistry.getInstance().matches(document, wrapper.serverDefinition, project));
return wrapper.isEnabled() && (wrapper.isConnectedTo(path) || LanguageServersRegistry.getInstance().matches(document, file, project, wrapper.serverDefinition));
} catch (ProcessCanceledException cancellation) {
throw cancellation;
} catch (Exception e) {
Expand Down Expand Up @@ -198,9 +198,11 @@ private Collection<LanguageServerWrapper> getLSWrappers(@Nonnull Document docume
}
final Project fileProject = file != null ? LSPIJUtils.getProject(file) : null;
if (fileProject != null) {
LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition);
startedServers.add(wrapper);
res.add(wrapper);
if(mapping.matches(document, file, fileProject)) {
LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition);
startedServers.add(wrapper);
res.add(wrapper);
}
}
}
processedContentTypes.add(contentType);
Expand Down Expand Up @@ -246,17 +248,6 @@ private List<LanguageServerWrapper> getStartedLSWrappers(Predicate<LanguageServe
}


private Collection<LanguageServerWrapper> getMatchingStartedWrappers(@Nonnull VirtualFile file,
@Nullable Predicate<ServerCapabilities> 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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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.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 implements DocumentMatcher {
@Override
public boolean match(Document document, VirtualFile file, Project fileProject) {
Module module = LSPIJUtils.getModule(file);
return module != null && QuarkusModuleUtil.isQuarkusModule(module);
}
}
Original file line number Diff line number Diff line change
@@ -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 {

}
Original file line number Diff line number Diff line change
@@ -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(Document document, VirtualFile file, Project fileProject) {
if (!matchFile(file, fileProject)) {
return false;
}
return super.match(document, file, fileProject);
}

private boolean matchFile(VirtualFile file, Project fileProject) {
return QuarkusModuleUtil.isQuarkusPropertiesFile(file, fileProject) || QuarkusModuleUtil.isQuarkusYAMLFile(file, fileProject);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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:
* <ul>
Expand All @@ -33,10 +35,6 @@
* </ul>
*/
public class QuteLanguageSubstitutor extends LanguageSubstitutor {
protected boolean isTemplate(VirtualFile file, Module module) {
return file.getPath().contains("templates") &&
ModuleRootManager.getInstance(module).getFileIndex().isInSourceContent(file);
}

protected boolean isQuteModule(Module module) {
OrderEnumerator libraries = ModuleRootManager.getInstance(module).orderEntries().librariesOnly();
Expand Down Expand Up @@ -70,7 +68,7 @@ private Module findModule(VirtualFile file) {
public @Nullable Language getLanguage(@NotNull VirtualFile file, @NotNull Project project) {
Module module = ModuleUtilCore.findModuleForFile(file, project);
if (module != null) {
if (isTemplate(file, module) && isQuteModule(module)) {
if (isQuteTemplate(file, module) && isQuteModule(module)) {
return QuteLanguage.INSTANCE;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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.qute.psi.utils.PsiQuteProjectUtils;

public class AbstractQuteDocumentMatcher implements DocumentMatcher {

@Override
public boolean match(Document document, VirtualFile file, Project fileProject) {
Module module = LSPIJUtils.getModule(file);
return module != null && PsiQuteProjectUtils.hasQuteSupport(module);
}
}
Loading

0 comments on commit 54c9c86

Please sign in to comment.