From 5932f740f3d5f164a21a01d31a61e4c8a4faf48f Mon Sep 17 00:00:00 2001 From: benluiwj Date: Mon, 6 Apr 2026 22:38:13 +0800 Subject: [PATCH] fix: workspace expansion for settings urls --- .../internal/preferences/Preferences.java | 120 +++++++++++------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java index d1497c6c1c..e7368134bc 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java @@ -40,6 +40,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.apache.commons.lang3.text.StrLookup; +import org.apache.commons.lang3.text.StrSubstitutor; import org.eclipse.core.internal.resources.PreferenceInitializer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -114,7 +116,8 @@ public class Preferences { public static final String JAVA_SYMBOLS_INCLUDE_SOURCE_METHOD_DECLARATIONS = "java.symbols.includeSourceMethodDeclarations"; /** - * Include generated code (e.g. Lombok getters, setters, constructors) in document symbols. + * Include generated code (e.g. Lombok getters, setters, constructors) in + * document symbols. */ public static final String JAVA_SYMBOLS_INCLUDE_GENERATED_CODE = "java.symbols.includeGeneratedCode"; @@ -357,7 +360,8 @@ public class Preferences { public static final String COMPLETION_MATCH_CASE_MODE_KEY = "java.completion.matchCase"; /** - * Preference key to specify whether text edit of completion item can be lazily resolved. + * Preference key to specify whether text edit of completion item can be lazily + * resolved. */ public static final String COMPLETION_LAZY_RESOLVE_TEXT_EDIT_ENABLED_KEY = "java.completion.lazyResolveTextEdit.enabled"; @@ -561,10 +565,11 @@ public class Preferences { public static final String CHAIN_COMPLETION_KEY = "java.completion.chain.enabled"; /** - * Preference key to set the scope value to use when searching java code. Allowed value are + * Preference key to set the scope value to use when searching java code. + * Allowed value are * * Any other unknown value will be treated as all. */ @@ -789,7 +794,7 @@ static Severity fromString(String value, Severity defaultSeverity) { String val = value.toLowerCase(); try { return valueOf(val); - } catch(Exception e) { + } catch (Exception e) { //fall back to default severity } } @@ -808,14 +813,14 @@ public MessageType toMessageType() { } public static enum FeatureStatus { - disabled, interactive, automatic ; + disabled, interactive, automatic; static FeatureStatus fromString(String value, FeatureStatus defaultStatus) { if (value != null) { String val = value.toLowerCase(); try { return valueOf(val); - } catch(Exception e) { + } catch (Exception e) { //fall back to default severity } } @@ -831,7 +836,7 @@ static SearchScope fromString(String value, SearchScope defaultScope) { String val = value.toLowerCase(); try { return valueOf(val); - } catch(Exception e) { + } catch (Exception e) { //fall back to default severity } } @@ -909,9 +914,7 @@ public boolean equals(Object obj) { return false; } ReferencedLibraries other = (ReferencedLibraries) obj; - return Objects.equals(include, other.include) - && Objects.equals(exclude, other.exclude) - && Objects.equals(sources, other.sources); + return Objects.equals(include, other.include) && Objects.equals(exclude, other.exclude) && Objects.equals(sources, other.sources); } } @@ -1058,7 +1061,8 @@ private static List getClasspathSubStringFromArtifact(String artifact) { /** * Create a {@link Preferences} model from a {@link Map} configuration. * - * @param configuration the configuration map to apply + * @param configuration + * the configuration map to apply * @return a new Preferences object with the configuration applied */ public static Preferences createFrom(Map configuration) { @@ -1068,11 +1072,16 @@ public static Preferences createFrom(Map configuration) { /** * Creates a deep copy of this Preferences object. * - *

Collections are deep copied to prevent shared state. Note that:

+ *

+ * Collections are deep copied to prevent shared state. Note that: + *

*
    - *
  • The configuration map itself is copied, but values within it are not (shallow copy of values)
  • - *
  • RuntimeEnvironment objects are copied by reference (shared between original and clone)
  • - *
  • IPath objects are copied by reference (safe since IPath is immutable)
  • + *
  • The configuration map itself is copied, but values within it are not + * (shallow copy of values)
  • + *
  • RuntimeEnvironment objects are copied by reference (shared between + * original and clone)
  • + *
  • IPath objects are copied by reference (safe since IPath is + * immutable)
  • *
* * @return a new Preferences object with the same values as this one @@ -1202,11 +1211,7 @@ public Preferences clone() { // Deep copy complex objects if (this.referencedLibraries != null) { - prefs.referencedLibraries = new ReferencedLibraries( - new HashSet<>(this.referencedLibraries.getInclude()), - new HashSet<>(this.referencedLibraries.getExclude()), - new HashMap<>(this.referencedLibraries.getSources()) - ); + prefs.referencedLibraries = new ReferencedLibraries(new HashSet<>(this.referencedLibraries.getInclude()), new HashSet<>(this.referencedLibraries.getExclude()), new HashMap<>(this.referencedLibraries.getSources())); } // Copy collection fields (these are typically not modified after creation) @@ -1218,11 +1223,15 @@ public Preferences clone() { } /** - * Create an updated {@link Preferences} model from an existing preferences and a {@link Map} with partial configuration. - * Only the settings present in the configuration will be updated, all other settings will be preserved from the existing preferences. + * Create an updated {@link Preferences} model from an existing preferences and + * a {@link Map} with partial configuration. Only the settings present in the + * configuration will be updated, all other settings will be preserved from the + * existing preferences. * - * @param existing the existing preferences to update - * @param configuration the partial configuration with updated settings + * @param existing + * the existing preferences to update + * @param configuration + * the partial configuration with updated settings * @return a new Preferences object with updated values */ @SuppressWarnings("unchecked") @@ -1248,8 +1257,7 @@ public static Preferences updateFrom(Preferences existing, Map c // Now update only the fields that are present in the partial configuration if (containsKey(configuration, CONFIGURATION_UPDATE_BUILD_CONFIGURATION_KEY)) { String updateBuildConfiguration = getString(configuration, CONFIGURATION_UPDATE_BUILD_CONFIGURATION_KEY, null); - prefs.setUpdateBuildConfigurationStatus( - FeatureStatus.fromString(updateBuildConfiguration, existing.updateBuildConfigurationStatus)); + prefs.setUpdateBuildConfigurationStatus(FeatureStatus.fromString(updateBuildConfiguration, existing.updateBuildConfigurationStatus)); } if (containsKey(configuration, IMPORT_GRADLE_ENABLED)) { @@ -1445,12 +1453,10 @@ public static Preferences updateFrom(Preferences existing, Map c if (containsKey(configuration, JAVA_COMPLETION_GUESS_METHOD_ARGUMENTS_KEY)) { Object guessMethodArguments = getValue(configuration, JAVA_COMPLETION_GUESS_METHOD_ARGUMENTS_KEY); if (guessMethodArguments instanceof Boolean b) { - prefs.setGuessMethodArgumentsMode(b ? CompletionGuessMethodArgumentsMode.INSERT_BEST_GUESSED_ARGUMENTS : - CompletionGuessMethodArgumentsMode.INSERT_PARAMETER_NAMES); + prefs.setGuessMethodArgumentsMode(b ? CompletionGuessMethodArgumentsMode.INSERT_BEST_GUESSED_ARGUMENTS : CompletionGuessMethodArgumentsMode.INSERT_PARAMETER_NAMES); } else { String guessMethodArgumentsMode = getString(configuration, JAVA_COMPLETION_GUESS_METHOD_ARGUMENTS_KEY, null); - prefs.setGuessMethodArgumentsMode(CompletionGuessMethodArgumentsMode.fromString(guessMethodArgumentsMode, - existing.guessMethodArguments)); + prefs.setGuessMethodArgumentsMode(CompletionGuessMethodArgumentsMode.fromString(guessMethodArgumentsMode, existing.guessMethodArguments)); } } @@ -1847,7 +1853,7 @@ public static Preferences updateFrom(Preferences existing, Map c if (containsKey(configuration, JAVA_CLEANUPS_ACTIONS_ON_SAVE_DEPRECATED) || containsKey(configuration, JAVA_CLEANUPS_ACTIONS)) { List cleanupActionsTemp = getList(configuration, JAVA_CLEANUPS_ACTIONS_ON_SAVE_DEPRECATED, Collections.emptyList()); List cleanupActions = getList(configuration, JAVA_CLEANUPS_ACTIONS, Collections.emptyList()); - if(cleanupActions.isEmpty() && !cleanupActionsTemp.isEmpty()) { + if (cleanupActions.isEmpty() && !cleanupActionsTemp.isEmpty()) { cleanupActions = cleanupActionsTemp; } prefs.setCleanUpActions(cleanupActions); @@ -1941,7 +1947,7 @@ private static boolean validateFilePattern(String filename) { * Sets the new value of the enabled clean ups. * * @param enabledCleanUps - * the new list of enabled clean ups + * the new list of enabled clean ups */ public void setCleanUpActions(List enabledCleanUps) { this.cleanUpActions = enabledCleanUps; @@ -1987,12 +1993,12 @@ public Preferences setGradleUserHome(String gradleUserHome) { } public Preferences setFormatterUrl(String formatterUrl) { - this.formatterUrl = ResourceUtils.expandPath(formatterUrl); + this.formatterUrl = ResourceUtils.expandPath(expandWorkspacePath(formatterUrl)); return this; } public Preferences setSettingsUrl(String settingsUrl) { - this.settingsUrl = ResourceUtils.expandPath(settingsUrl); + this.settingsUrl = ResourceUtils.expandPath(expandWorkspacePath(settingsUrl)); return this; } @@ -3038,8 +3044,8 @@ public boolean isChainCompletionEnabled() { } /** - * update the null analysis options of all projects based on the null analysis mode - * Returns the list of enabled clean ups. + * update the null analysis options of all projects based on the null analysis + * mode Returns the list of enabled clean ups. * * @return the list of enabled clean ups */ @@ -3084,8 +3090,12 @@ private boolean updateAnnotationNullAnalysisOptions(boolean enabled) { /** * update the null analysis options of given project - * @param javaProject the java project to update the annotation-based null analysis options - * @param enabled specific whether the null analysis is enabled + * + * @param javaProject + * the java project to update the annotation-based null + * analysis options + * @param enabled + * specific whether the null analysis is enabled * @return whether the options of the given project are changed or not */ public boolean updateAnnotationNullAnalysisOptions(IJavaProject javaProject, boolean enabled) { @@ -3222,10 +3232,15 @@ private String findTypeInProject(IJavaProject javaProject, String annotationType } /** - * generates the null analysis options of the given nonnull type and nullable type - * @param nonnullType the given nonnull type - * @param nullableType the given nullable type - * @return the map contains the null analysis options, if both given types are null, will return default null analysis options + * generates the null analysis options of the given nonnull type and nullable + * type + * + * @param nonnullType + * the given nonnull type + * @param nullableType + * the given nullable type + * @return the map contains the null analysis options, if both given types are + * null, will return default null analysis options */ private Map generateProjectNullAnalysisOptions(String nonnullType, String nullableType, String nonnullbydefaultType) { Map options = new HashMap<>(); @@ -3291,4 +3306,21 @@ public void setSearchScope(SearchScope value) { public SearchScope getSearchScope() { return searchScope; } + + private String expandWorkspacePath(String path) { + if (path == null || rootPaths == null || rootPaths.isEmpty()) + return path; + StrLookup workspaceResolver = new StrLookup() { + @Override + public String lookup(String key) { + if (key == "workspace") { + String prop = getRootPaths().iterator().next().toOSString(); + return prop; + } + return null; + } + }; + StrSubstitutor strSubstitutor = new StrSubstitutor(workspaceResolver); + return strSubstitutor.replace(formatterUrl); + } }