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 8, 2024
1 parent 353d49f commit 2ae2cf6
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 19 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 @@ -387,7 +387,9 @@ public static void handleFileRenameForTypeDeclaration(String documentUri) {
IProblem renameProblem = desiredProblem.get();
String newName = renameProblem.getArguments()[1];
String oldName = cu.getElementName();
String newUri = documentUri.replace(oldName, newName + ".java");
int index = oldName.lastIndexOf(".");
String extension = index > 0 ? oldName.substring(index) : ".java";
String newUri = documentUri.replace(oldName, newName + extension);
WorkspaceEdit edit = new WorkspaceEdit(List.of(Either.forRight(new RenameFile(documentUri, newUri))));
edit.setChanges(Collections.emptyMap());
final boolean applyNow = JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isWorkspaceApplyEditSupported();
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 no 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) && validateFilePattern(k)) {
associations.add(k.substring(2));
}
});
} catch (Exception e) {
JavaLanguageServerPlugin.logException(e);
}
}
prefs.setFilesAssociations(new ArrayList<>(associations));
return prefs;
}

private static boolean validateFilePattern(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 2ae2cf6

Please sign in to comment.