Skip to content

Commit

Permalink
Recognize custom file extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
snjeza committed Aug 7, 2024
1 parent 353d49f commit c1c1e16
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,13 @@ public static ICompilationUnit getFakeCompilationUnit(String uri) {
}

static ICompilationUnit getFakeCompilationUnit(URI uri, IProgressMonitor monitor) {
if (uri == null || !"file".equals(uri.getScheme()) || !uri.getPath().endsWith(".java")) {
if (uri == null || !"file".equals(uri.getScheme())) {
return null;
}
java.nio.file.Path path = Paths.get(uri);
if (!isJavaFile(path)) {
return null;
}
//Only support existing standalone java files
if (!java.nio.file.Files.isReadable(path)) {
return null;
Expand All @@ -275,7 +278,7 @@ static ICompilationUnit getFakeCompilationUnit(URI uri, IProgressMonitor monitor
IProject project = JavaLanguageServerPlugin.getProjectsManager().getDefaultProject();
if (project == null || !project.isAccessible()) {
String fileName = path.getFileName().toString();
if (fileName.endsWith(".java") || fileName.endsWith(".class")) {
if (JDTUtils.isJavaFile(fileName) || fileName.endsWith(".class")) {
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
}
WorkingCopyOwner owner = new WorkingCopyOwner() {
Expand Down Expand Up @@ -657,6 +660,23 @@ private boolean find(IJavaElement element, final ASTNode[] nodes, SimpleName nod
}
}

public static boolean isJavaFile(java.nio.file.Path path) {
try {
return path != null && isJavaFile(path.toFile().getName());
} catch (Exception e) {
JavaLanguageServerPlugin.logException(e.getMessage(), e);
}
return false;
}

public static boolean isJavaFile(IPath path) {
return path != null && isJavaFile(path.lastSegment());
}

public static boolean isJavaFile(String name) {
return name != null && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(name);
}

/**
* Enumeration for determining the location of a Java element. Either returns
* with the name range only, or the extended source range around the name of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.ActionableNotification;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaClientConnection.JavaLanguageClient;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ProgressReport;
Expand Down Expand Up @@ -94,7 +95,7 @@ public void onDidProjectsImported(IProgressMonitor monitor) {
JavaLanguageServerPlugin.getProjectsManager().getConnection().sendActionableNotification(notification);
}
}

/**
* Find all the Protobuf source output directories of the given project.
* @param project project.
Expand Down Expand Up @@ -133,7 +134,7 @@ private boolean containsJavaFiles(Set<File> generatedDirectories) {

try (Stream<Path> walkStream = Files.walk(dir.toPath())) {
boolean containsJavaFile = walkStream.filter(p -> p.toFile().isFile()).anyMatch(f -> {
return f.toString().endsWith(".java");
return JDTUtils.isJavaFile(f);
});

if (containsJavaFile) {
Expand Down Expand Up @@ -194,7 +195,7 @@ private static void runGenerateProtobufTasks(String projectName, IProgressMonito
try {
build.get().withConnection(connection -> {
connection.newBuild().forTasks("generateProto", "generateTestProto").run();
return null;
return null;
}, monitor);
} catch (Exception e) {
JavaLanguageServerPlugin.logException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.ls.core.internal.IConstants;
import org.eclipse.jdt.ls.core.internal.JSONUtility;
import org.eclipse.jdt.ls.core.internal.JVMConfigurator;
Expand Down Expand Up @@ -57,7 +62,6 @@ public BaseInitHandler(ProjectsManager projectsManager, PreferenceManager prefer
this.projectsManager = projectsManager;
}

@SuppressWarnings("unchecked")
public InitializeResult initialize(InitializeParams param) {
logInfo("Initializing Java Language Server " + JavaLanguageServerPlugin.getVersion());
InitializeResult result = new InitializeResult();
Expand All @@ -70,6 +74,7 @@ public InitializeResult initialize(InitializeParams param) {
return result;
}

@SuppressWarnings("unchecked")
public Map<?, ?> handleInitializationOptions(InitializeParams param) {
Map<?, ?> initializationOptions = this.getInitializationOptions(param);
Map<String, Object> extendedClientCapabilities = getInitializationOption(initializationOptions, "extendedClientCapabilities", Map.class);
Expand Down Expand Up @@ -109,7 +114,6 @@ public InitializeResult initialize(InitializeParams param) {
rootPaths.add(workspaceLocation);
}
if (initializationOptions.get(SETTINGS_KEY) instanceof Map<?, ?> settings) {
@SuppressWarnings("unchecked")
Preferences prefs = Preferences.createFrom((Map<String, Object>) settings);
prefs.setRootPaths(rootPaths);
preferenceManager.update(prefs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ private static boolean isFileNameRenameEvent(FileRename event) {
}

return (oldPath.toFile().isFile() || newPath.toFile().isFile())
&& oldPath.lastSegment().endsWith(".java") && newPath.lastSegment().endsWith(".java")
&& JDTUtils.isJavaFile(oldPath.lastSegment()) && JDTUtils.isJavaFile(newPath.lastSegment())
&& Objects.equals(oldPath.removeLastSegments(1), newPath.removeLastSegments(1));
}

Expand All @@ -331,7 +331,7 @@ private static boolean isMoveEvent(FileRename event) {
return false;
}

return oldPath.toFile().isFile() && oldPath.lastSegment().endsWith(".java")
return oldPath.toFile().isFile() && JDTUtils.isJavaFile(oldPath.lastSegment())
&& Objects.equals(oldPath.lastSegment(), newPath.lastSegment());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private void cleanUpDiagnostics(String uri) {

private void discardWorkingCopies(String parentUri) {
IPath parentPath = ResourceUtils.filePathFromURI(parentUri);
if (parentPath != null && !parentPath.lastSegment().endsWith(".java")) {
if (parentPath != null && !JDTUtils.isJavaFile(parentPath)) {
ICompilationUnit[] workingCopies = JavaCore.getWorkingCopies(null);
for (ICompilationUnit workingCopy : workingCopies) {
IResource resource = workingCopy.getResource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ private static File findNearbyNonEmptyFile(File nioFile) throws IOException {
try (Stream<java.nio.file.Path> walk = Files.walk(directory, 1)) {
Optional<java.nio.file.Path> found = walk.filter(Files::isRegularFile).filter(file -> {
try {
return file.toString().endsWith(".java") && !Objects.equals(nioFile.getName(), file.toFile().getName()) && Files.size(file) > 0;
return JDTUtils.isJavaFile(file) && !Objects.equals(nioFile.getName(), file.toFile().getName()) && Files.size(file) > 0;
} catch (IOException e) {
return false;
}
Expand Down Expand Up @@ -672,7 +672,7 @@ public FileVisitResult preVisitDirectory(java.nio.file.Path dirPath, BasicFileAt
return FileVisitResult.TERMINATE;
}

if (javaFile == null && f.getName().endsWith(".java")) {
if (javaFile == null && JDTUtils.isJavaFile(f.getName())) {
javaFile = f;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.preferences;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -28,13 +26,13 @@
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
Expand All @@ -43,6 +41,7 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
import org.eclipse.jdt.core.manipulation.JavaManipulation;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType;
import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType.CodeTemplateVariableResolver;
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationMessages;
Expand All @@ -54,7 +53,6 @@
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.ls.core.internal.IConstants;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.StatusFactory;
import org.eclipse.jdt.ls.core.internal.handlers.BaseDiagnosticsHandler;
Expand Down Expand Up @@ -258,6 +256,49 @@ public void update(Preferences preferences) {
JavaLanguageServerPlugin.getInstance().getClientConnection().publishDiagnostics(diagnostics);
}
}
if (!oldPreferences.getFilesAssociations().equals(preferences.getFilesAssociations())) {
configureContentTypes(preferences);
}
}

// only for test purpose
public static void configureContentTypes(Preferences preferences) {
if (preferences != null && preferences.getFilesAssociations() != null) {
IContentType javaSourceContentType = Platform.getContentTypeManager().getContentType(JavaCore.JAVA_SOURCE_CONTENT_TYPE);
if (javaSourceContentType != null) {
List<String> toRemove = new ArrayList<>();
String[] specs = javaSourceContentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
for (String spec : specs) {
if (!SuffixConstants.EXTENSION_java.equals(spec)) {
toRemove.add(spec);
}
}
List<String> toAdd = new ArrayList<>();
for (String spec : preferences.getFilesAssociations()) {
if (toRemove.contains(spec)) {
toRemove.remove(spec);
} else {
toAdd.add(spec);
}
}
for (String spec : toRemove) {
try {
javaSourceContentType.removeFileSpec(spec, IContentType.FILE_EXTENSION_SPEC);
} catch (CoreException e) {
JavaLanguageServerPlugin.logException(e);
}
}
for (String spec : toAdd) {
try {
javaSourceContentType.addFileSpec(spec, IContentType.FILE_EXTENSION_SPEC);
} catch (CoreException e) {
JavaLanguageServerPlugin.logException(e);
}
}
} else {
JavaLanguageServerPlugin.logInfo("There is not java source content type");
}
}
}

private void preferencesChanged(Preferences oldPreferences, Preferences newPreferences) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ public class Preferences {
* Tab Size
*/
public static final String JAVA_CONFIGURATION_TABSIZE = "java.format.tabSize";

/**
* Files associations to languages
*/
public static final String JAVA_CONFIGURATION_ASSOCIATIONS = "java.associations";
/**
* Specifies Java Execution Environments.
*/
Expand Down Expand Up @@ -589,6 +594,7 @@ public class Preferences {
private static Map<String, List<String>> nonnullClasspathStorage = new HashMap<>();
private static Map<String, List<String>> nullableClasspathStorage = new HashMap<>();
private static Map<String, List<String>> nonnullbydefaultClasspathStorage = new HashMap<>();
private List<String> filesAssociations = new ArrayList<>();

private Map<String, Object> configuration;
private Severity incompleteClasspathSeverity;
Expand Down Expand Up @@ -1330,9 +1336,36 @@ public static Preferences createFrom(Map<String, Object> configuration) {
prefs.setChainCompletionEnabled(chainCompletionEnabled);
List<String> diagnosticFilter = getList(configuration, JAVA_DIAGNOSTIC_FILER, Collections.emptyList());
prefs.setDiagnosticFilter(diagnosticFilter);
Object object = getValue(configuration, JAVA_CONFIGURATION_ASSOCIATIONS);
Set<String> associations = new HashSet<>();
if (object instanceof Map map) {
try {
Map<String, String> element = map;
element.forEach((k, v) -> {
// Java LS only support a small subset of the glob pattern syntax (*.xxx)
if ("java".equals(v) && validateFileName(k)) {
associations.add(k.substring(2));
}
});
} catch (Exception e) {
JavaLanguageServerPlugin.logException(e);
}
}
prefs.setFilesAssociations(new ArrayList<>(associations));
return prefs;
}

private static boolean validateFileName(String filename) {
if (filename != null && filename.startsWith("*.") && filename.length() > 2) {
String ext = filename.substring(2);
if (!ext.contains("?") && !ext.contains("*")) {
return true;
}
}
JavaLanguageServerPlugin.logInfo("Pattern '" + filename + "' is not supported.");
return false;
}

/**
* Sets the new value of the enabled clean ups.
*
Expand Down Expand Up @@ -2582,4 +2615,12 @@ public List<String> getDiagnosticFilter() {
public void setDiagnosticFilter(List<String> diagnosticFilter) {
this.diagnosticFilter = diagnosticFilter;
}

public List<String> getFilesAssociations() {
return filesAssociations;
}

public void setFilesAssociations(List<String> filesAssociations) {
this.filesAssociations = filesAssociations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstallChangedListener;
import org.eclipse.jdt.launching.JavaRuntime;
Expand All @@ -68,6 +69,7 @@
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest;
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.lsp4j.ClientCapabilities;
import org.eclipse.lsp4j.DidChangeConfigurationCapabilities;
Expand Down Expand Up @@ -462,6 +464,24 @@ public void testMissingResourceOperations() throws Exception {
assertFalse(preferences.isResourceOperationSupported());
}

// https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3222
@Test
public void testFilesAssociations() throws Exception {
try {
ClientPreferences mockCapabilies = mock(ClientPreferences.class);
when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies);
List<String> fileAssociations = new ArrayList<>();
fileAssociations.add("maxj");
preferences.setFilesAssociations(fileAssociations);
PreferenceManager.configureContentTypes(preferences);
assertTrue(Util.isJavaLikeFileName("Test.maxj"));
} finally {
preferences.getFilesAssociations().clear();
PreferenceManager.configureContentTypes(preferences);
assertFalse(Util.isJavaLikeFileName("Test.maxj"));
}
}

private void removeExclusionPattern(IJavaProject javaProject) throws JavaModelException {
IClasspathEntry[] classpath = javaProject.getRawClasspath();
for (int i = 0; i < classpath.length; i++) {
Expand Down

0 comments on commit c1c1e16

Please sign in to comment.