Skip to content

Commit 07fcf4b

Browse files
committed
Recognize custom file extensions
1 parent 353d49f commit 07fcf4b

File tree

10 files changed

+148
-19
lines changed

10 files changed

+148
-19
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTUtils.java

+22-2
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,13 @@ public static ICompilationUnit getFakeCompilationUnit(String uri) {
257257
}
258258

259259
static ICompilationUnit getFakeCompilationUnit(URI uri, IProgressMonitor monitor) {
260-
if (uri == null || !"file".equals(uri.getScheme()) || !uri.getPath().endsWith(".java")) {
260+
if (uri == null || !"file".equals(uri.getScheme())) {
261261
return null;
262262
}
263263
java.nio.file.Path path = Paths.get(uri);
264+
if (!isJavaFile(path)) {
265+
return null;
266+
}
264267
//Only support existing standalone java files
265268
if (!java.nio.file.Files.isReadable(path)) {
266269
return null;
@@ -275,7 +278,7 @@ static ICompilationUnit getFakeCompilationUnit(URI uri, IProgressMonitor monitor
275278
IProject project = JavaLanguageServerPlugin.getProjectsManager().getDefaultProject();
276279
if (project == null || !project.isAccessible()) {
277280
String fileName = path.getFileName().toString();
278-
if (fileName.endsWith(".java") || fileName.endsWith(".class")) {
281+
if (JDTUtils.isJavaFile(fileName) || fileName.endsWith(".class")) {
279282
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
280283
}
281284
WorkingCopyOwner owner = new WorkingCopyOwner() {
@@ -657,6 +660,23 @@ private boolean find(IJavaElement element, final ASTNode[] nodes, SimpleName nod
657660
}
658661
}
659662

663+
public static boolean isJavaFile(java.nio.file.Path path) {
664+
try {
665+
return path != null && isJavaFile(path.toFile().getName());
666+
} catch (Exception e) {
667+
JavaLanguageServerPlugin.logException(e.getMessage(), e);
668+
}
669+
return false;
670+
}
671+
672+
public static boolean isJavaFile(IPath path) {
673+
return path != null && isJavaFile(path.lastSegment());
674+
}
675+
676+
public static boolean isJavaFile(String name) {
677+
return name != null && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(name);
678+
}
679+
660680
/**
661681
* Enumeration for determining the location of a Java element. Either returns
662682
* with the name range only, or the extended source range around the name of the

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/framework/protobuf/ProtobufSupport.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.eclipse.jdt.core.JavaCore;
4040
import org.eclipse.jdt.core.JavaModelException;
4141
import org.eclipse.jdt.ls.core.internal.ActionableNotification;
42+
import org.eclipse.jdt.ls.core.internal.JDTUtils;
4243
import org.eclipse.jdt.ls.core.internal.JavaClientConnection.JavaLanguageClient;
4344
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
4445
import org.eclipse.jdt.ls.core.internal.ProgressReport;
@@ -94,7 +95,7 @@ public void onDidProjectsImported(IProgressMonitor monitor) {
9495
JavaLanguageServerPlugin.getProjectsManager().getConnection().sendActionableNotification(notification);
9596
}
9697
}
97-
98+
9899
/**
99100
* Find all the Protobuf source output directories of the given project.
100101
* @param project project.
@@ -133,7 +134,7 @@ private boolean containsJavaFiles(Set<File> generatedDirectories) {
133134

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

139140
if (containsJavaFile) {
@@ -194,7 +195,7 @@ private static void runGenerateProtobufTasks(String projectName, IProgressMonito
194195
try {
195196
build.get().withConnection(connection -> {
196197
connection.newBuild().forTasks("generateProto", "generateTestProto").run();
197-
return null;
198+
return null;
198199
}, monitor);
199200
} catch (Exception e) {
200201
JavaLanguageServerPlugin.logException(e);

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseDocumentLifeCycleHandler.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,9 @@ public static void handleFileRenameForTypeDeclaration(String documentUri) {
387387
IProblem renameProblem = desiredProblem.get();
388388
String newName = renameProblem.getArguments()[1];
389389
String oldName = cu.getElementName();
390-
String newUri = documentUri.replace(oldName, newName + ".java");
390+
int index = oldName.lastIndexOf(".");
391+
String extension = index > 0 ? oldName.substring(index) : ".java";
392+
String newUri = documentUri.replace(oldName, newName + extension);
391393
WorkspaceEdit edit = new WorkspaceEdit(List.of(Either.forRight(new RenameFile(documentUri, newUri))));
392394
edit.setChanges(Collections.emptyMap());
393395
final boolean applyNow = JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isWorkspaceApplyEditSupported();

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseInitHandler.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@
1919
import java.util.ArrayList;
2020
import java.util.Collection;
2121
import java.util.Collections;
22+
import java.util.List;
2223
import java.util.Map;
2324

2425
import org.eclipse.core.resources.ResourcesPlugin;
2526
import org.eclipse.core.runtime.CoreException;
2627
import org.eclipse.core.runtime.IPath;
28+
import org.eclipse.core.runtime.Platform;
29+
import org.eclipse.core.runtime.content.IContentType;
2730
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
2831
import org.eclipse.core.runtime.preferences.InstanceScope;
32+
import org.eclipse.jdt.core.JavaCore;
33+
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
2934
import org.eclipse.jdt.ls.core.internal.IConstants;
3035
import org.eclipse.jdt.ls.core.internal.JSONUtility;
3136
import org.eclipse.jdt.ls.core.internal.JVMConfigurator;
@@ -57,7 +62,6 @@ public BaseInitHandler(ProjectsManager projectsManager, PreferenceManager prefer
5762
this.projectsManager = projectsManager;
5863
}
5964

60-
@SuppressWarnings("unchecked")
6165
public InitializeResult initialize(InitializeParams param) {
6266
logInfo("Initializing Java Language Server " + JavaLanguageServerPlugin.getVersion());
6367
InitializeResult result = new InitializeResult();
@@ -70,6 +74,7 @@ public InitializeResult initialize(InitializeParams param) {
7074
return result;
7175
}
7276

77+
@SuppressWarnings("unchecked")
7378
public Map<?, ?> handleInitializationOptions(InitializeParams param) {
7479
Map<?, ?> initializationOptions = this.getInitializationOptions(param);
7580
Map<String, Object> extendedClientCapabilities = getInitializationOption(initializationOptions, "extendedClientCapabilities", Map.class);
@@ -109,7 +114,6 @@ public InitializeResult initialize(InitializeParams param) {
109114
rootPaths.add(workspaceLocation);
110115
}
111116
if (initializationOptions.get(SETTINGS_KEY) instanceof Map<?, ?> settings) {
112-
@SuppressWarnings("unchecked")
113117
Preferences prefs = Preferences.createFrom((Map<String, Object>) settings);
114118
prefs.setRootPaths(rootPaths);
115119
preferenceManager.update(prefs);

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/FileEventHandler.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ private static boolean isFileNameRenameEvent(FileRename event) {
305305
}
306306

307307
return (oldPath.toFile().isFile() || newPath.toFile().isFile())
308-
&& oldPath.lastSegment().endsWith(".java") && newPath.lastSegment().endsWith(".java")
308+
&& JDTUtils.isJavaFile(oldPath.lastSegment()) && JDTUtils.isJavaFile(newPath.lastSegment())
309309
&& Objects.equals(oldPath.removeLastSegments(1), newPath.removeLastSegments(1));
310310
}
311311

@@ -331,7 +331,7 @@ private static boolean isMoveEvent(FileRename event) {
331331
return false;
332332
}
333333

334-
return oldPath.toFile().isFile() && oldPath.lastSegment().endsWith(".java")
334+
return oldPath.toFile().isFile() && JDTUtils.isJavaFile(oldPath.lastSegment())
335335
&& Objects.equals(oldPath.lastSegment(), newPath.lastSegment());
336336
}
337337

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/WorkspaceEventsHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ private void cleanUpDiagnostics(String uri) {
174174

175175
private void discardWorkingCopies(String parentUri) {
176176
IPath parentPath = ResourceUtils.filePathFromURI(parentUri);
177-
if (parentPath != null && !parentPath.lastSegment().endsWith(".java")) {
177+
if (parentPath != null && !JDTUtils.isJavaFile(parentPath)) {
178178
ICompilationUnit[] workingCopies = JavaCore.getWorkingCopies(null);
179179
for (ICompilationUnit workingCopy : workingCopies) {
180180
IResource resource = workingCopy.getResource();

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ private static File findNearbyNonEmptyFile(File nioFile) throws IOException {
520520
try (Stream<java.nio.file.Path> walk = Files.walk(directory, 1)) {
521521
Optional<java.nio.file.Path> found = walk.filter(Files::isRegularFile).filter(file -> {
522522
try {
523-
return file.toString().endsWith(".java") && !Objects.equals(nioFile.getName(), file.toFile().getName()) && Files.size(file) > 0;
523+
return JDTUtils.isJavaFile(file) && !Objects.equals(nioFile.getName(), file.toFile().getName()) && Files.size(file) > 0;
524524
} catch (IOException e) {
525525
return false;
526526
}
@@ -672,7 +672,7 @@ public FileVisitResult preVisitDirectory(java.nio.file.Path dirPath, BasicFileAt
672672
return FileVisitResult.TERMINATE;
673673
}
674674

675-
if (javaFile == null && f.getName().endsWith(".java")) {
675+
if (javaFile == null && JDTUtils.isJavaFile(f.getName())) {
676676
javaFile = f;
677677
}
678678
}

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/PreferenceManager.java

+47-6
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@
1212
*******************************************************************************/
1313
package org.eclipse.jdt.ls.core.internal.preferences;
1414

15-
import java.io.File;
1615
import java.io.IOException;
1716
import java.io.StringWriter;
1817
import java.io.Writer;
19-
import java.net.URI;
18+
import java.util.ArrayList;
2019
import java.util.Collections;
21-
import java.util.HashSet;
2220
import java.util.Hashtable;
2321
import java.util.LinkedHashMap;
2422
import java.util.List;
@@ -28,13 +26,13 @@
2826
import java.util.stream.Collectors;
2927

3028
import org.apache.commons.lang3.StringUtils;
31-
import org.eclipse.core.resources.IMarker;
32-
import org.eclipse.core.resources.IProject;
3329
import org.eclipse.core.resources.IResource;
3430
import org.eclipse.core.runtime.CoreException;
3531
import org.eclipse.core.runtime.ISafeRunnable;
3632
import org.eclipse.core.runtime.ListenerList;
33+
import org.eclipse.core.runtime.Platform;
3734
import org.eclipse.core.runtime.SafeRunner;
35+
import org.eclipse.core.runtime.content.IContentType;
3836
import org.eclipse.core.runtime.preferences.DefaultScope;
3937
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
4038
import org.eclipse.core.runtime.preferences.InstanceScope;
@@ -43,6 +41,7 @@
4341
import org.eclipse.jdt.core.JavaCore;
4442
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
4543
import org.eclipse.jdt.core.manipulation.JavaManipulation;
44+
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
4645
import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType;
4746
import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType.CodeTemplateVariableResolver;
4847
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationMessages;
@@ -54,7 +53,6 @@
5453
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
5554
import org.eclipse.jdt.ls.core.internal.IConstants;
5655
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
57-
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
5856
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
5957
import org.eclipse.jdt.ls.core.internal.StatusFactory;
6058
import org.eclipse.jdt.ls.core.internal.handlers.BaseDiagnosticsHandler;
@@ -258,6 +256,49 @@ public void update(Preferences preferences) {
258256
JavaLanguageServerPlugin.getInstance().getClientConnection().publishDiagnostics(diagnostics);
259257
}
260258
}
259+
if (!oldPreferences.getFilesAssociations().equals(preferences.getFilesAssociations())) {
260+
configureContentTypes(preferences);
261+
}
262+
}
263+
264+
// only for test purpose
265+
public static void configureContentTypes(Preferences preferences) {
266+
if (preferences != null && preferences.getFilesAssociations() != null) {
267+
IContentType javaSourceContentType = Platform.getContentTypeManager().getContentType(JavaCore.JAVA_SOURCE_CONTENT_TYPE);
268+
if (javaSourceContentType != null) {
269+
List<String> toRemove = new ArrayList<>();
270+
String[] specs = javaSourceContentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
271+
for (String spec : specs) {
272+
if (!SuffixConstants.EXTENSION_java.equals(spec)) {
273+
toRemove.add(spec);
274+
}
275+
}
276+
List<String> toAdd = new ArrayList<>();
277+
for (String spec : preferences.getFilesAssociations()) {
278+
if (toRemove.contains(spec)) {
279+
toRemove.remove(spec);
280+
} else {
281+
toAdd.add(spec);
282+
}
283+
}
284+
for (String spec : toRemove) {
285+
try {
286+
javaSourceContentType.removeFileSpec(spec, IContentType.FILE_EXTENSION_SPEC);
287+
} catch (CoreException e) {
288+
JavaLanguageServerPlugin.logException(e);
289+
}
290+
}
291+
for (String spec : toAdd) {
292+
try {
293+
javaSourceContentType.addFileSpec(spec, IContentType.FILE_EXTENSION_SPEC);
294+
} catch (CoreException e) {
295+
JavaLanguageServerPlugin.logException(e);
296+
}
297+
}
298+
} else {
299+
JavaLanguageServerPlugin.logInfo("There is no java source content type.");
300+
}
301+
}
261302
}
262303

263304
private void preferencesChanged(Preferences oldPreferences, Preferences newPreferences) {

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java

+41
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ public class Preferences {
120120
* Tab Size
121121
*/
122122
public static final String JAVA_CONFIGURATION_TABSIZE = "java.format.tabSize";
123+
124+
/**
125+
* Files associations to languages
126+
*/
127+
public static final String JAVA_CONFIGURATION_ASSOCIATIONS = "java.associations";
123128
/**
124129
* Specifies Java Execution Environments.
125130
*/
@@ -589,6 +594,7 @@ public class Preferences {
589594
private static Map<String, List<String>> nonnullClasspathStorage = new HashMap<>();
590595
private static Map<String, List<String>> nullableClasspathStorage = new HashMap<>();
591596
private static Map<String, List<String>> nonnullbydefaultClasspathStorage = new HashMap<>();
597+
private List<String> filesAssociations = new ArrayList<>();
592598

593599
private Map<String, Object> configuration;
594600
private Severity incompleteClasspathSeverity;
@@ -1330,9 +1336,36 @@ public static Preferences createFrom(Map<String, Object> configuration) {
13301336
prefs.setChainCompletionEnabled(chainCompletionEnabled);
13311337
List<String> diagnosticFilter = getList(configuration, JAVA_DIAGNOSTIC_FILER, Collections.emptyList());
13321338
prefs.setDiagnosticFilter(diagnosticFilter);
1339+
Object object = getValue(configuration, JAVA_CONFIGURATION_ASSOCIATIONS);
1340+
Set<String> associations = new HashSet<>();
1341+
if (object instanceof Map map) {
1342+
try {
1343+
Map<String, String> element = map;
1344+
element.forEach((k, v) -> {
1345+
// Java LS only support a small subset of the glob pattern syntax (*.xxx)
1346+
if ("java".equals(v) && validateFilePattern(k)) {
1347+
associations.add(k.substring(2));
1348+
}
1349+
});
1350+
} catch (Exception e) {
1351+
JavaLanguageServerPlugin.logException(e);
1352+
}
1353+
}
1354+
prefs.setFilesAssociations(new ArrayList<>(associations));
13331355
return prefs;
13341356
}
13351357

1358+
private static boolean validateFilePattern(String filename) {
1359+
if (filename != null && filename.startsWith("*.") && filename.length() > 2) {
1360+
String ext = filename.substring(2);
1361+
if (!ext.contains("?") && !ext.contains("*")) {
1362+
return true;
1363+
}
1364+
}
1365+
JavaLanguageServerPlugin.logInfo("Pattern '" + filename + "' is not supported.");
1366+
return false;
1367+
}
1368+
13361369
/**
13371370
* Sets the new value of the enabled clean ups.
13381371
*
@@ -2582,4 +2615,12 @@ public List<String> getDiagnosticFilter() {
25822615
public void setDiagnosticFilter(List<String> diagnosticFilter) {
25832616
this.diagnosticFilter = diagnosticFilter;
25842617
}
2618+
2619+
public List<String> getFilesAssociations() {
2620+
return filesAssociations;
2621+
}
2622+
2623+
public void setFilesAssociations(List<String> filesAssociations) {
2624+
this.filesAssociations = filesAssociations;
2625+
}
25852626
}

org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandlerTest.java

+20
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.eclipse.jdt.core.IJavaProject;
5757
import org.eclipse.jdt.core.JavaCore;
5858
import org.eclipse.jdt.core.JavaModelException;
59+
import org.eclipse.jdt.internal.core.util.Util;
5960
import org.eclipse.jdt.launching.IVMInstall;
6061
import org.eclipse.jdt.launching.IVMInstallChangedListener;
6162
import org.eclipse.jdt.launching.JavaRuntime;
@@ -68,6 +69,7 @@
6869
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest;
6970
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
7071
import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences;
72+
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
7173
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
7274
import org.eclipse.lsp4j.ClientCapabilities;
7375
import org.eclipse.lsp4j.DidChangeConfigurationCapabilities;
@@ -462,6 +464,24 @@ public void testMissingResourceOperations() throws Exception {
462464
assertFalse(preferences.isResourceOperationSupported());
463465
}
464466

467+
// https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3222
468+
@Test
469+
public void testFilesAssociations() throws Exception {
470+
try {
471+
ClientPreferences mockCapabilies = mock(ClientPreferences.class);
472+
when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies);
473+
List<String> fileAssociations = new ArrayList<>();
474+
fileAssociations.add("maxj");
475+
preferences.setFilesAssociations(fileAssociations);
476+
PreferenceManager.configureContentTypes(preferences);
477+
assertTrue(Util.isJavaLikeFileName("Test.maxj"));
478+
} finally {
479+
preferences.getFilesAssociations().clear();
480+
PreferenceManager.configureContentTypes(preferences);
481+
assertFalse(Util.isJavaLikeFileName("Test.maxj"));
482+
}
483+
}
484+
465485
private void removeExclusionPattern(IJavaProject javaProject) throws JavaModelException {
466486
IClasspathEntry[] classpath = javaProject.getRawClasspath();
467487
for (int i = 0; i < classpath.length; i++) {

0 commit comments

Comments
 (0)