diff --git a/.idea/misc.xml b/.idea/misc.xml index 28e84c20f..c7b0a3074 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,9 +6,14 @@ + + + + + diff --git a/app/src/main/assets/data/common/gradle-plugin.zip b/app/src/main/assets/data/common/gradle-plugin.zip index 4ed14ebc6..ed342832b 100644 Binary files a/app/src/main/assets/data/common/gradle-plugin.zip and b/app/src/main/assets/data/common/gradle-plugin.zip differ diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index a5bca5145..2674950d4 100755 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -88,6 +88,7 @@ import com.itsaky.androidide.managers.PreferenceManager; import com.itsaky.androidide.managers.ToolsManager; import com.itsaky.androidide.models.DiagnosticGroup; import com.itsaky.androidide.models.LogLine; +import com.itsaky.androidide.models.PrefBasedJavaServerSettings; import com.itsaky.androidide.models.SaveResult; import com.itsaky.androidide.models.SearchResult; import com.itsaky.androidide.models.SheetOption; @@ -1293,9 +1294,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F .getProjectPath () ).toPath () ); + final var params = new InitializeParams (workspaceRoots); javaLanguageServer.connectClient (client); + javaLanguageServer.applySettings (PrefBasedJavaServerSettings.getInstance ()); javaLanguageServer.initialize (params); } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java index 7b0d2dd8e..1870e8d67 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java @@ -92,7 +92,7 @@ public class BuildPreferences extends BasePreferenceFragment implements Preferen "--warning-mode all" }; final boolean[] checked = { - getPrefManager().isStracktraceEnabled(), + getPrefManager().isStackTraceEnabled (), getPrefManager().isGradleInfoEnabled(), getPrefManager().isGradleDebugEnabled(), getPrefManager().isGradleScanEnabled(), diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java index 77b1e51ba..bbc4176ee 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java @@ -20,15 +20,18 @@ package com.itsaky.androidide.fragments.preferences; import android.os.Bundle; import android.view.LayoutInflater; import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; +import androidx.preference.PreferenceCategory; import androidx.preference.SwitchPreference; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.R; +import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.databinding.LayoutTextSizeSliderBinding; import com.itsaky.androidide.models.ConstantsBridge; +import com.itsaky.androidide.models.PrefBasedJavaServerSettings; import com.itsaky.androidide.utils.DialogUtils; import static com.itsaky.androidide.managers.PreferenceManager.*; +import static com.itsaky.androidide.models.PrefBasedJavaServerSettings.*; public class EditorPreferences extends BasePreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { @@ -37,11 +40,19 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere super.onCreatePreferences(p1, p2); if(getContext() == null) return; - final PreferenceScreen screen = getPreferenceScreen(); - final Preference fontSize = new Preference(getContext()); - final Preference nonPrintable = new Preference(getContext()); - final Preference tabSize = new Preference(getContext()); - final SwitchPreference drawHex = new SwitchPreference(getContext()); + final var screen = getPreferenceScreen(); + final var commonCategory = new PreferenceCategory (getContext ()); + final var javaCategory = new PreferenceCategory (getContext ()); + + final var fontSize = new Preference(getContext()); + final var nonPrintable = new Preference(getContext()); + final var tabSize = new Preference(getContext()); + final var drawHex = new SwitchPreference(getContext()); + + final var javaMatchLower = new SwitchPreference (getContext ()); + + screen.addPreference (commonCategory); + screen.addPreference (javaCategory); fontSize.setIcon(R.drawable.ic_text_size); fontSize.setKey(KEY_EDITOR_FONT_SIZE); @@ -62,34 +73,50 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere drawHex.setKey(KEY_EDITOR_DRAW_HEX); drawHex.setTitle(R.string.idepref_editor_drawhexcolors_title); drawHex.setSummary(R.string.idepref_editor_drawhexcolors_summary); - - screen.addPreference(fontSize); - screen.addPreference(nonPrintable); - screen.addPreference(tabSize); - screen.addPreference(drawHex); + + javaMatchLower.setKey (KEY_JAVA_PREF_MATCH_LOWER); + javaMatchLower.setIcon (R.drawable.ic_text_lower); + javaMatchLower.setTitle (getString(R.string.idepref_java_matchLower_title)); + javaMatchLower.setSummary (getString(R.string.idepref_java_matchLower_summary)); + + commonCategory.setTitle (getString(R.string.idepref_editor_category_common)); + commonCategory.addPreference(fontSize); + commonCategory.addPreference(nonPrintable); + commonCategory.addPreference(tabSize); + commonCategory.addPreference(drawHex); + + javaCategory.setTitle (getString(R.string.idepref_editor_category_java)); + javaCategory.addPreference (javaMatchLower); + setPreferenceScreen(screen); fontSize.setOnPreferenceClickListener(this); nonPrintable.setOnPreferenceClickListener(this); tabSize.setOnPreferenceClickListener(this); drawHex.setOnPreferenceChangeListener(this); + javaMatchLower.setOnPreferenceChangeListener (this); drawHex.setChecked(getPrefManager().getBoolean(KEY_EDITOR_DRAW_HEX, true)); + javaMatchLower.setChecked (getPrefManager ().getBoolean (KEY_JAVA_PREF_MATCH_LOWER, false)); } @Override - public boolean onPreferenceChange(Preference p1, Object p2) { - if(p1.getKey().equals(KEY_EDITOR_DRAW_HEX)) { - boolean drawHex = (Boolean) p2; - getPrefManager().putBoolean(KEY_EDITOR_DRAW_HEX, drawHex); + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean value = (boolean) newValue; + if(preference.getKey().equals(KEY_EDITOR_DRAW_HEX)) { + getPrefManager().putBoolean(KEY_EDITOR_DRAW_HEX, value); ConstantsBridge.EDITOR_PREF_DRAW_HEX_CHANGED = true; + } else if (preference.getKey ().equals (KEY_JAVA_PREF_MATCH_LOWER)) { + getPrefManager ().putBoolean (KEY_JAVA_PREF_MATCH_LOWER, value); + final var javaServer = StudioApp.getInstance ().getJavaLanguageServer (); + javaServer.applySettings (PrefBasedJavaServerSettings.getInstance ()); } return true; } @Override - public boolean onPreferenceClick(Preference p1) { - final String key = p1.getKey(); + public boolean onPreferenceClick(Preference preference) { + final String key = preference.getKey(); if(key.equals(KEY_EDITOR_FONT_SIZE)) { showTextSizeDialog(); } else if(key.equals(KEY_EDITOR_PRINTABLE_CHARS)) { @@ -115,7 +142,7 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere builder.setTitle(R.string.title_tab_size); builder.setSingleChoiceItems(sizes, current, (d, i) -> { d.dismiss(); - + // Reversing the logic applied above... int tabSize = (i + 1) * 2; if(tabSize < 2 || tabSize > 8) tabSize = 4; @@ -165,26 +192,26 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere "Line breaks" }; final boolean[] checked = { - getPrefManager().getBoolean(KEY_EDITORFLAG_WS_LEADING, true), - getPrefManager().getBoolean(KEY_EDITORFLAG_WS_TRAILING, false), - getPrefManager().getBoolean(KEY_EDITORFLAG_WS_INNER, true), - getPrefManager().getBoolean(KEY_EDITORFLAG_WS_EMPTY_LINE, true), - getPrefManager().getBoolean(KEY_EDITORFLAG_LINE_BREAK, true) + getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_LEADING, true), + getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_TRAILING, false), + getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_INNER, true), + getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, true), + getPrefManager().getBoolean(KEY_EDITOR_FLAG_LINE_BREAK, true) }; final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ()); builder.setTitle(R.string.idepref_editor_paintingflags_title); builder.setMultiChoiceItems(labels, checked, (dialog, which, isChecked) -> { if(which == 0) { - getPrefManager().putBoolean(KEY_EDITORFLAG_WS_LEADING, isChecked); + getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_LEADING, isChecked); } else if(which == 1) { - getPrefManager().putBoolean(KEY_EDITORFLAG_WS_TRAILING, isChecked); + getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_TRAILING, isChecked); } else if(which == 2) { - getPrefManager().putBoolean(KEY_EDITORFLAG_WS_INNER, isChecked); + getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_INNER, isChecked); } else if(which == 3) { - getPrefManager().putBoolean(KEY_EDITORFLAG_WS_EMPTY_LINE, isChecked); + getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, isChecked); } else if(which == 4) { - getPrefManager().putBoolean(KEY_EDITORFLAG_LINE_BREAK, isChecked); + getPrefManager().putBoolean(KEY_EDITOR_FLAG_LINE_BREAK, isChecked); } ConstantsBridge.EDITOR_PREF_FLAGS_CHANGED = true; @@ -193,4 +220,4 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere builder.setCancelable(false); builder.show(); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/models/PrefBasedJavaServerSettings.java b/app/src/main/java/com/itsaky/androidide/models/PrefBasedJavaServerSettings.java new file mode 100644 index 000000000..1864d80f9 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/models/PrefBasedJavaServerSettings.java @@ -0,0 +1,55 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.models; + +import androidx.annotation.NonNull; + +import com.itsaky.androidide.app.StudioApp; +import com.itsaky.androidide.managers.PreferenceManager; +import com.itsaky.lsp.java.models.DefaultJavaServerSettings; + +/** + * Server settings for the java language server. + * @author Akash Yadav + */ +public class PrefBasedJavaServerSettings extends DefaultJavaServerSettings { + + public static final String KEY_JAVA_PREF_MATCH_LOWER = "idepref_editor_java_matchLower"; + + private static PrefBasedJavaServerSettings instance; + + private final PreferenceManager prefs; + + public PrefBasedJavaServerSettings () { + this.prefs = StudioApp.getInstance ().getPrefManager (); + } + + @NonNull + public static PrefBasedJavaServerSettings getInstance () { + if (instance == null) { + instance = new PrefBasedJavaServerSettings (); + } + + return instance; + } + + @Override + public boolean shouldMatchAllLowerCase () { + return prefs.getBoolean (KEY_JAVA_PREF_MATCH_LOWER, false); + } +} diff --git a/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java b/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java index aac75b8c7..a81d7b783 100755 --- a/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java +++ b/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java @@ -241,7 +241,7 @@ public class IDEService { args.add("--init-script"); args.add(Environment.INIT_SCRIPT.getAbsolutePath()); - if (prefs.isStracktraceEnabled()) { + if (prefs.isStackTraceEnabled ()) { args.add("--stacktrace"); } if (prefs.isGradleInfoEnabled()) { diff --git a/app/src/main/java/com/itsaky/androidide/views/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/views/CodeEditorView.java index e788471e6..3f17f61e1 100644 --- a/app/src/main/java/com/itsaky/androidide/views/CodeEditorView.java +++ b/app/src/main/java/com/itsaky/androidide/views/CodeEditorView.java @@ -19,7 +19,6 @@ package com.itsaky.androidide.views; import android.annotation.SuppressLint; import android.content.Context; import android.view.LayoutInflater; -import android.view.ViewTreeObserver; import android.widget.FrameLayout; import androidx.annotation.NonNull; @@ -291,23 +290,23 @@ public class CodeEditorView extends FrameLayout { if (flagsChanged) { int flags = 0; - if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_LEADING, true)) { + if (prefs.getBoolean (PreferenceManager.KEY_EDITOR_FLAG_WS_LEADING, true)) { flags |= CodeEditor.FLAG_DRAW_WHITESPACE_LEADING; } - if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_TRAILING, false)) { + if (prefs.getBoolean (PreferenceManager.KEY_EDITOR_FLAG_WS_TRAILING, false)) { flags |= CodeEditor.FLAG_DRAW_WHITESPACE_TRAILING; } - if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_INNER, true)) { + if (prefs.getBoolean (PreferenceManager.KEY_EDITOR_FLAG_WS_INNER, true)) { flags |= CodeEditor.FLAG_DRAW_WHITESPACE_INNER; } - if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_EMPTY_LINE, true)) { + if (prefs.getBoolean (PreferenceManager.KEY_EDITOR_FLAG_WS_EMPTY_LINE, true)) { flags |= CodeEditor.FLAG_DRAW_WHITESPACE_FOR_EMPTY_LINE; } - if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_LINE_BREAK, true)) { + if (prefs.getBoolean (PreferenceManager.KEY_EDITOR_FLAG_LINE_BREAK, true)) { flags |= CodeEditor.FLAG_DRAW_LINE_SEPARATOR; } diff --git a/app/src/main/res/drawable/ic_text_lower.xml b/app/src/main/res/drawable/ic_text_lower.xml new file mode 100644 index 000000000..aec47d537 --- /dev/null +++ b/app/src/main/res/drawable/ic_text_lower.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 542c53ced..62daf1b8a 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -355,5 +355,9 @@ Compiler module installation failed. The error was:\n%s Compiler module installed successfully! Expand selection - + Match completions in lower case + If enabled, then typing a lower case or upper case letters will suggest class names as well as scope members. + Common + Java + diff --git a/common/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java b/common/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java index 342b20814..de7b68703 100755 --- a/common/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java +++ b/common/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java @@ -23,10 +23,8 @@ import android.content.SharedPreferences; public class PreferenceManager { - private SharedPreferences prefs; + private final SharedPreferences prefs; - public static final int LOGSENDER_LATEST_VERSION = 102; - public static final String KEY_FRAMEWORK_DOWNLOADED = "framework_downloaded"; public static final String KEY_FRAMEWORK_INSTALLED = "framework_installed"; public static final String KEY_IS_FIRST_PROJECT_BUILD = "project_isFirstBuild"; @@ -35,26 +33,22 @@ public class PreferenceManager { public static final String KEY_CONFIRM_PROJECT_OPEN = "idepref_general_confirmProjectOpen"; public static final String KEY_TERMINAL_USE_SYSTEM_SHELL = "idepref_general_terminalShell"; - public static final String KEY_EDITORFLAG_WS_LEADING = "idepref_editor_wsLeading"; - public static final String KEY_EDITORFLAG_WS_TRAILING = "idepref_editor_wsTrailing"; - public static final String KEY_EDITORFLAG_WS_INNER = "idepref_editor_wsInner"; - public static final String KEY_EDITORFLAG_WS_EMPTY_LINE = "idepref_editor_wsEmptyLine"; - public static final String KEY_EDITORFLAG_LINE_BREAK = "idepref_editor_lineBreak"; + public static final String KEY_EDITOR_FLAG_WS_LEADING = "idepref_editor_wsLeading"; + public static final String KEY_EDITOR_FLAG_WS_TRAILING = "idepref_editor_wsTrailing"; + public static final String KEY_EDITOR_FLAG_WS_INNER = "idepref_editor_wsInner"; + public static final String KEY_EDITOR_FLAG_WS_EMPTY_LINE = "idepref_editor_wsEmptyLine"; + public static final String KEY_EDITOR_FLAG_LINE_BREAK = "idepref_editor_lineBreak"; public static final String KEY_EDITOR_DRAW_HEX = "idepref_editor_drawHexColors"; public static final String KEY_EDITOR_FONT_SIZE = "idepref_editor_fontSize"; public static final String KEY_EDITOR_PRINTABLE_CHARS = "idepref_editor_nonPrintableFlags"; public static final String KEY_EDITOR_TAB_SIZE = "idepref_editor_tabSize"; - public static final String KEY_GRADLE_CURRENT_DIR = "idepref_customGradle_currentFolder"; - public static final String KEY_GRADLE_FILENAMES = "idepref_customGradle_folderNames"; - public static final String KEY_GRADLECMD_STACKTRACRE = "idepref_gradleCmd_stacktrace"; - public static final String KEY_GRADLECMD_DEBUG = "idepref_gradleCmd_debug"; - public static final String KEY_GRADLECMD_SCAN = "idepref_gradleCmd_scan"; - public static final String KEY_GRADLECMD_INFO = "idepref_gradleCmd_info"; - public static final String KEY_GRADLECMD_WARNINGMODE = "idepref_gradleCmd_warningMode"; - - public static final String KEY_LOGSENDER_CURRENT_VERSION = "logsender_currentVersion"; - + public static final String KEY_GRADLE_CMD_STACK_TRACE = "idepref_gradleCmd_stacktrace"; + public static final String KEY_GRADLE_CMD_DEBUG = "idepref_gradleCmd_debug"; + public static final String KEY_GRADLE_CMD_SCAN = "idepref_gradleCmd_scan"; + public static final String KEY_GRADLE_CMD_INFO = "idepref_gradleCmd_info"; + public static final String KEY_GRADLE_CMD_WARNING_MODE = "idepref_gradleCmd_warningMode"; + public static final String KEY_LAST_OPENED_PROJECT = "ide_last_project"; public static final String NO_OPENED_PROJECT = ""; @@ -135,44 +129,44 @@ public class PreferenceManager { return isFrameworkDownloaded() && getBoolean(KEY_FRAMEWORK_INSTALLED); } - public boolean isStracktraceEnabled() { - return getBoolean(KEY_GRADLECMD_STACKTRACRE); + public boolean isStackTraceEnabled () { + return getBoolean(KEY_GRADLE_CMD_STACK_TRACE); } public boolean isGradleInfoEnabled() { - return getBoolean(KEY_GRADLECMD_INFO, true); + return getBoolean(KEY_GRADLE_CMD_INFO, true); } public boolean isGradleDebugEnabled() { - return getBoolean(KEY_GRADLECMD_DEBUG); + return getBoolean(KEY_GRADLE_CMD_DEBUG); } public boolean isGradleScanEnabled() { - return getBoolean(KEY_GRADLECMD_SCAN); + return getBoolean(KEY_GRADLE_CMD_SCAN); } public boolean isGradleWarningEnabled() { - return getBoolean(KEY_GRADLECMD_WARNINGMODE); + return getBoolean(KEY_GRADLE_CMD_WARNING_MODE); } public PreferenceManager setGradleStacktraceEnabled(boolean enabled) { - return putBoolean(KEY_GRADLECMD_STACKTRACRE, enabled); + return putBoolean(KEY_GRADLE_CMD_STACK_TRACE, enabled); } public PreferenceManager setGradleInfoEnabled(boolean enabled) { - return putBoolean(KEY_GRADLECMD_INFO, enabled); + return putBoolean(KEY_GRADLE_CMD_INFO, enabled); } public PreferenceManager setGradleDebugEnabled(boolean enabled) { - return putBoolean(KEY_GRADLECMD_DEBUG, enabled); + return putBoolean(KEY_GRADLE_CMD_DEBUG, enabled); } public PreferenceManager setGradleScanEnabled(boolean enabled) { - return putBoolean(KEY_GRADLECMD_SCAN, enabled); + return putBoolean(KEY_GRADLE_CMD_SCAN, enabled); } public PreferenceManager setGradleWarningEnabled(boolean enabled) { - return putBoolean(KEY_GRADLECMD_WARNINGMODE, enabled); + return putBoolean(KEY_GRADLE_CMD_WARNING_MODE, enabled); } public int getEditorTabSize() { diff --git a/lsp/api/src/main/java/com/itsaky/lsp/api/AbstractServiceProvider.java b/lsp/api/src/main/java/com/itsaky/lsp/api/AbstractServiceProvider.java new file mode 100644 index 000000000..6a96b6f3d --- /dev/null +++ b/lsp/api/src/main/java/com/itsaky/lsp/api/AbstractServiceProvider.java @@ -0,0 +1,45 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.lsp.api; + +import com.itsaky.lsp.util.DefaultServerSettings; + +/** + * @author Akash Yadav + */ +public class AbstractServiceProvider implements ConfigurableServiceProvider { + + private IServerSettings settings; + + public AbstractServiceProvider () { + this.settings = new DefaultServerSettings (); + } + + @Override + public void applySettings (IServerSettings settings) { + if (settings == null) { + settings = new DefaultServerSettings (); + } + + this.settings = settings; + } + + public IServerSettings getSettings () { + return settings; + } +} diff --git a/lsp/api/src/main/java/com/itsaky/lsp/api/ConfigurableServiceProvider.java b/lsp/api/src/main/java/com/itsaky/lsp/api/ConfigurableServiceProvider.java new file mode 100644 index 000000000..d29790e23 --- /dev/null +++ b/lsp/api/src/main/java/com/itsaky/lsp/api/ConfigurableServiceProvider.java @@ -0,0 +1,35 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.lsp.api; + +import androidx.annotation.NonNull; + +/** + * Base class for service providers which can be configured using + * {@link IServerSettings}. + * + * @author Akash Yadav + */ +public interface ConfigurableServiceProvider { + + /** + * Apply the given settings to this service provider. + * @param settings The settings to apply. If {@code null}, {@link com.itsaky.lsp.util.DefaultServerSettings} should be used. + */ + void applySettings (IServerSettings settings); +} diff --git a/lsp/api/src/main/java/com/itsaky/lsp/api/IServerSettings.java b/lsp/api/src/main/java/com/itsaky/lsp/api/IServerSettings.java index b0875cf46..0733ebfcd 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/api/IServerSettings.java +++ b/lsp/api/src/main/java/com/itsaky/lsp/api/IServerSettings.java @@ -73,4 +73,12 @@ public interface IServerSettings { * @return {@code true} if enabled, {@code false} otherwise. */ boolean codeAnalysisEnabled (); + + /** + * Called by the completions provider to check if + * it should match partial names in all lowercase. + * + * @return {@code true} if enabled, {@code false} otherwise. + */ + boolean shouldMatchAllLowerCase (); } diff --git a/lsp/api/src/main/java/com/itsaky/lsp/util/DefaultServerSettings.kt b/lsp/api/src/main/java/com/itsaky/lsp/util/DefaultServerSettings.kt index 872613a82..1cf8460ec 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/util/DefaultServerSettings.kt +++ b/lsp/api/src/main/java/com/itsaky/lsp/util/DefaultServerSettings.kt @@ -24,7 +24,7 @@ import com.itsaky.lsp.api.IServerSettings * * @author Akash Yadav */ -class DefaultServerSettings : IServerSettings { +open class DefaultServerSettings : IServerSettings { override fun completionsEnabled(): Boolean = true override fun codeActionsEnabled(): Boolean = true override fun smartSelectionsEnabled(): Boolean = true @@ -32,4 +32,6 @@ class DefaultServerSettings : IServerSettings { override fun referencesEnabled(): Boolean = true override fun definitionsEnabled(): Boolean = true override fun codeAnalysisEnabled(): Boolean = true + + override fun shouldMatchAllLowerCase(): Boolean = false } \ No newline at end of file diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java b/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java index 3b0f7bb11..9f61964dc 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/JavaLanguageServer.java @@ -178,7 +178,7 @@ public class JavaLanguageServer implements ILanguageServer, IDocumentHandler { return new NoCompletionsProvider (); } - return new CompletionProvider (getCompiler ()); + return new CompletionProvider (getCompiler (), this.settings); } @NonNull diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/models/DefaultJavaServerSettings.java b/lsp/java/src/main/java/com/itsaky/lsp/java/models/DefaultJavaServerSettings.java new file mode 100644 index 000000000..38571336c --- /dev/null +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/models/DefaultJavaServerSettings.java @@ -0,0 +1,27 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.lsp.java.models; + +import com.itsaky.lsp.util.DefaultServerSettings; + +/** + * Default settings for the java language server. + * @author Akash Yadav + */ +public class DefaultJavaServerSettings extends DefaultServerSettings { +} diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java index cfdee721f..8deeccda5 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java @@ -23,7 +23,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.itsaky.androidide.utils.Logger; +import com.itsaky.lsp.api.AbstractServiceProvider; import com.itsaky.lsp.api.ICompletionProvider; +import com.itsaky.lsp.api.IServerSettings; import com.itsaky.lsp.java.compiler.CompileTask; import com.itsaky.lsp.java.compiler.CompilerProvider; import com.itsaky.lsp.java.FileStore; @@ -92,7 +94,7 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.Types; -public class CompletionProvider implements ICompletionProvider { +public class CompletionProvider extends AbstractServiceProvider implements ICompletionProvider { private final CompilerProvider compiler; @@ -102,7 +104,10 @@ public class CompletionProvider implements ICompletionProvider { private Path completingFile; private long cursor; - public CompletionProvider (CompilerProvider compiler) { + public CompletionProvider (CompilerProvider compiler, IServerSettings settings) { + super (); + applySettings (settings); + this.compiler = compiler; } @@ -239,8 +244,11 @@ public class CompletionProvider implements ICompletionProvider { CompletionResult list = new CompletionResult(); list.setItems (completeUsingScope (task, path, partial, endsWithParen)); addStaticImports(task, path.getCompilationUnit(), partial, endsWithParen, list); - if (!list.isIncomplete () && partial.length() > 0 && Character.isUpperCase(partial.charAt(0))) { - addClassNames(path.getCompilationUnit(), partial, list); + if (!list.isIncomplete ()) { + final boolean allLower = getSettings ().shouldMatchAllLowerCase (); + if (allLower || (partial.length () > 0 && Character.isUpperCase (partial.charAt (0)))) { + addClassNames(path.getCompilationUnit(), partial, list, allLower); + } } addKeywords(path, partial, list); return list; @@ -257,7 +265,7 @@ public class CompletionProvider implements ICompletionProvider { keywords = METHOD_BODY_KEYWORDS; } for (String k : keywords) { - if (StringSearch.matchesPartialName(k, partial)) { + if (StringSearch.matchesPartialName(k, partial, getSettings ().shouldMatchAllLowerCase ())) { list.getItems ().add(keyword(k)); } } @@ -281,7 +289,7 @@ public class CompletionProvider implements ICompletionProvider { Trees trees = Trees.instance(task.task); List list = new ArrayList<> (); Scope scope = trees.getScope(path); - Predicate filter = name -> StringSearch.matchesPartialName(name, partial); + Predicate filter = name -> StringSearch.matchesPartialName(name, partial, true); for (Element member : ScopeHelper.scopeMembers(task, scope, filter)) { if (member.getKind() == ElementKind.METHOD) { ExecutableElement method = (ExecutableElement) member; @@ -311,7 +319,7 @@ public class CompletionProvider implements ICompletionProvider { for (Element member : type.getEnclosedElements()) { if (!member.getModifiers().contains(Modifier.STATIC)) continue; if (!memberMatchesImport(id.getIdentifier(), member)) continue; - if (!StringSearch.matchesPartialName(member.getSimpleName(), partial)) continue; + if (!StringSearch.matchesPartialName(member.getSimpleName(), partial, getSettings ().shouldMatchAllLowerCase ())) continue; if (member.getKind() == ElementKind.METHOD) { putMethod((ExecutableElement) member, methods); } else { @@ -330,14 +338,14 @@ public class CompletionProvider implements ICompletionProvider { } private boolean importMatchesPartial(@NonNull Name staticImport, String partial) { - return staticImport.contentEquals("*") || StringSearch.matchesPartialName(staticImport, partial); + return staticImport.contentEquals("*") || StringSearch.matchesPartialName(staticImport, partial, getSettings ().shouldMatchAllLowerCase ()); } private boolean memberMatchesImport(@NonNull Name staticImport, Element member) { return staticImport.contentEquals("*") || staticImport.contentEquals(member.getSimpleName()); } - private void addClassNames(@NonNull CompilationUnitTree root, String partial, @NonNull CompletionResult list) { + private void addClassNames(@NonNull CompilationUnitTree root, String partial, @NonNull CompletionResult list, boolean allLower) { String packageName = Objects.toString(root.getPackageName(), ""); Set uniques = new HashSet<> (); int previousSize = list.getItems ().size(); @@ -349,13 +357,13 @@ public class CompletionProvider implements ICompletionProvider { .collect (Collectors.toSet ()); for (String className : compiler.packagePrivateTopLevelTypes(packageName)) { - if (!StringSearch.matchesPartialName(className, partial)) continue; + if (!StringSearch.matchesPartialName(className, partial, allLower)) continue; list.getItems ().add(classItem(imports, file, className)); uniques.add(className); } for (String className : compiler.publicTopLevelTypes()) { - if (!StringSearch.matchesPartialName(simpleName(className), partial)) continue; + if (!StringSearch.matchesPartialName(simpleName(className), partial, allLower)) continue; if (uniques.contains(className)) continue; if (list.getItems ().size() > MAX_COMPLETION_ITEMS) { list.setIncomplete (true); @@ -371,14 +379,15 @@ public class CompletionProvider implements ICompletionProvider { } final ClassTree c = (ClassTree) t; - if (c.getModifiers ().getFlags ().isEmpty ()) { - // This is a package private class defined in the same file - final String name = packageName + "." + c.getSimpleName (); - list.getItems ().add (classItem (name)); - if (list.getItems ().size () > MAX_COMPLETION_ITEMS) { - list.setIncomplete (true); - break; - } + if (!StringSearch.matchesPartialName (c.getSimpleName () == null ? "" : c.getSimpleName (), partial, allLower)) { + continue; + } + + final String name = packageName + "." + c.getSimpleName (); + list.getItems ().add (classItem (name)); + if (list.getItems ().size () > MAX_COMPLETION_ITEMS) { + list.setIncomplete (true); + break; } } @@ -437,7 +446,7 @@ public class CompletionProvider implements ICompletionProvider { Map> methods = new HashMap<> (); for (Element member : task.task.getElements().getAllMembers(typeElement)) { if (member.getKind() == ElementKind.CONSTRUCTOR) continue; - if (!StringSearch.matchesPartialName(member.getSimpleName(), partial)) continue; + if (!StringSearch.matchesPartialName(member.getSimpleName(), partial, getSettings ().shouldMatchAllLowerCase ())) continue; if (!trees.isAccessible(scope, member, type)) continue; if (isStatic != member.getModifiers().contains(Modifier.STATIC)) continue; if (member.getKind() == ElementKind.METHOD) { @@ -530,7 +539,7 @@ public class CompletionProvider implements ICompletionProvider { List list = new ArrayList<> (); Map> methods = new HashMap<> (); for (Element member : task.task.getElements().getAllMembers(typeElement)) { - if (!StringSearch.matchesPartialName(member.getSimpleName(), partial)) continue; + if (!StringSearch.matchesPartialName(member.getSimpleName(), partial, getSettings ().shouldMatchAllLowerCase ())) continue; if (member.getKind() != ElementKind.METHOD) continue; if (!trees.isAccessible(scope, member, type)) continue; if (!isStatic && member.getModifiers().contains(Modifier.STATIC)) continue; @@ -587,7 +596,7 @@ public class CompletionProvider implements ICompletionProvider { List list = new ArrayList<> (); for (Element member : task.task.getElements().getAllMembers(element)) { if (member.getKind() != ElementKind.ENUM_CONSTANT) continue; - if (!StringSearch.matchesPartialName(member.getSimpleName(), partial)) continue; + if (!StringSearch.matchesPartialName(member.getSimpleName(), partial, getSettings ().shouldMatchAllLowerCase ())) continue; list.add(item(task, member)); } diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/StringSearch.java b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/StringSearch.java index 78b0917fc..8e2f8ef9e 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/utils/StringSearch.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/utils/StringSearch.java @@ -29,6 +29,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Arrays; +import java.util.Locale; import java.util.Objects; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -444,9 +445,14 @@ public class StringSearch { } public static boolean matchesPartialName (CharSequence candidate, CharSequence partialName) { + return matchesPartialName (candidate, partialName, false); + } + + public static boolean matchesPartialName (CharSequence candidate, CharSequence partialName, boolean allLower) { if (candidate.length () < partialName.length ()) { return false; } + for (int i = 0; i < partialName.length (); i++) { char char1 = candidate.charAt (i); char char2 = partialName.charAt (i); @@ -455,7 +461,7 @@ public class StringSearch { // The make all the characters lower case // So if the first character is upper case, we could assume that the user is expecting a class name // Otherwise a variable name - if (i != 0) { + if (allLower || i != 0) { char1 = Character.toLowerCase (char1); char2 = Character.toLowerCase (char2); }