diff --git a/lsp/api/src/main/java/com/itsaky/lsp/util/StringUtils.java b/lsp/api/src/main/java/com/itsaky/lsp/util/StringUtils.java index ea4620ae9..5f765dbc2 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/util/StringUtils.java +++ b/lsp/api/src/main/java/com/itsaky/lsp/util/StringUtils.java @@ -37,10 +37,9 @@ public class StringUtils { char char2 = partialName.charAt(i); // Match by keeping the first character of the partial name and candidate as is - // The make all the characters lower case + // Then 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 + // class name // Otherwise a variable name if (allLower || i != 0) { char1 = Character.toLowerCase(char1); 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 cf5d02778..9ad1f7a40 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 @@ -21,6 +21,7 @@ import static com.itsaky.lsp.java.utils.EditHelper.repeatSpaces; 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; @@ -59,6 +60,7 @@ import com.sun.source.tree.SwitchTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; + import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; @@ -75,6 +77,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; + import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -161,7 +164,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp public CompletionProvider(CompilerProvider compiler, IServerSettings settings) { super(); - applySettings(settings); + super.applySettings(settings); this.compiler = compiler; } diff --git a/lsp/xml/src/main/java/com/itsaky/lsp/xml/XMLLanguageServer.java b/lsp/xml/src/main/java/com/itsaky/lsp/xml/XMLLanguageServer.java index 2ca23a255..d5168d7d7 100644 --- a/lsp/xml/src/main/java/com/itsaky/lsp/xml/XMLLanguageServer.java +++ b/lsp/xml/src/main/java/com/itsaky/lsp/xml/XMLLanguageServer.java @@ -18,6 +18,7 @@ package com.itsaky.lsp.xml; import androidx.annotation.NonNull; + import com.itsaky.lsp.api.ICompletionProvider; import com.itsaky.lsp.api.IDocumentHandler; import com.itsaky.lsp.api.ILanguageClient; @@ -38,13 +39,15 @@ import com.itsaky.lsp.models.SignatureHelp; import com.itsaky.lsp.models.SignatureHelpParams; import com.itsaky.lsp.util.NoCompletionsProvider; import com.itsaky.lsp.util.NoDocumentHandler; -import com.itsaky.lsp.xml.models.DefaultXMLServerSettings; +import com.itsaky.lsp.xml.models.XMLServerSettings; import com.itsaky.lsp.xml.providers.CompletionProvider; import com.itsaky.sdk.SDKInfo; + +import org.jetbrains.annotations.Nullable; + import java.nio.file.Path; import java.util.Collections; import java.util.List; -import org.jetbrains.annotations.Nullable; /** * Language server implementation for XML files. @@ -71,8 +74,9 @@ public class XMLLanguageServer implements ILanguageServer { @NonNull public IServerSettings getSettings() { if (settings == null) { - settings = new DefaultXMLServerSettings(); + settings = XMLServerSettings.getInstance(); } + return settings; } diff --git a/lsp/xml/src/main/java/com/itsaky/lsp/xml/models/DefaultXMLServerSettings.java b/lsp/xml/src/main/java/com/itsaky/lsp/xml/models/XMLServerSettings.java similarity index 69% rename from lsp/xml/src/main/java/com/itsaky/lsp/xml/models/DefaultXMLServerSettings.java rename to lsp/xml/src/main/java/com/itsaky/lsp/xml/models/XMLServerSettings.java index 8da59fb34..ffc25092b 100644 --- a/lsp/xml/src/main/java/com/itsaky/lsp/xml/models/DefaultXMLServerSettings.java +++ b/lsp/xml/src/main/java/com/itsaky/lsp/xml/models/XMLServerSettings.java @@ -17,9 +17,20 @@ package com.itsaky.lsp.xml.models; -import com.itsaky.lsp.util.DefaultServerSettings; +import com.itsaky.lsp.util.PrefBasedServerSettings; /** * @author Akash Yadav */ -public class DefaultXMLServerSettings extends DefaultServerSettings {} +public class XMLServerSettings extends PrefBasedServerSettings { + + private static XMLServerSettings instance; + + public static XMLServerSettings getInstance() { + if (instance == null) { + instance = new XMLServerSettings(); + } + + return instance; + } +} diff --git a/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java b/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java index 0fca3fd58..247021f98 100644 --- a/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java +++ b/lsp/xml/src/main/java/com/itsaky/lsp/xml/providers/CompletionProvider.java @@ -34,7 +34,6 @@ import com.itsaky.lsp.models.CompletionItemKind; import com.itsaky.lsp.models.CompletionParams; import com.itsaky.lsp.models.CompletionResult; import com.itsaky.lsp.models.InsertTextFormat; -import com.itsaky.lsp.util.StringUtils; import com.itsaky.lsp.xml.R; import com.itsaky.sdk.SDKInfo; import com.itsaky.widgets.models.Widget; @@ -70,6 +69,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp public CompletionProvider(SDKInfo sdkInfo, IServerSettings settings) { Objects.requireNonNull(sdkInfo); Objects.requireNonNull(settings); + super.applySettings(settings); this.sdkInfo = sdkInfo; } @@ -173,7 +173,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp if (attr != null && attr.hasPossibleValues()) { Set values = attr.possibleValues; for (String value : values) { - if (StringUtils.matchesPartialName(value, prefix, matchLower)) { + if (matchesPartialName(value, prefix, matchLower)) { result.getItems().add(valueAsCompletion(value)); } } @@ -182,7 +182,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp } else { for (Map.Entry entry : attrs.getAttributes().entrySet()) { Attr attr = entry.getValue(); - if (StringUtils.matchesPartialName(attr.name, prefix, matchLower)) { + if (matchesPartialName(attr.name, prefix, matchLower)) { result.getItems().add(attrAsCompletion(attr)); } } @@ -190,15 +190,15 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp // Shortcuts for automatically declaring namespaces // These completions are proposed if you type 'androidNs', 'appNs' or 'toolsNs' // Idea is shamelessly copied from Android Studio 😂 - if (StringUtils.matchesPartialName("android", prefix, matchLower)) { + if (matchesPartialName("android", prefix, matchLower)) { result.getItems().add(createNamespaceCompletion("android", ANDROID_NS)); } - if (StringUtils.matchesPartialName("app", prefix, matchLower)) { + if (matchesPartialName("app", prefix, matchLower)) { result.getItems().add(createNamespaceCompletion("app", APP_NS)); } - if (StringUtils.matchesPartialName("tools", prefix, matchLower)) { + if (matchesPartialName("tools", prefix, matchLower)) { result.getItems().add(createNamespaceCompletion("tools", TOOLS_NS)); } } @@ -211,8 +211,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp prefix = prefix.toLowerCase(Locale.ROOT); for (var widget : this.sdkInfo.getWidgetInfo().getWidgets()) { var name = widget.simpleName.toLowerCase(Locale.ROOT); - if (StringUtils.matchesPartialName( - name, prefix, getSettings().shouldMatchAllLowerCase())) { + if (matchesPartialName(name, prefix, getSettings().shouldMatchAllLowerCase())) { result.getItems().add(widgetNameAsCompletion(widget, slash)); } } @@ -300,6 +299,23 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp return sb.toString(); } + private boolean matchesPartialName(String candidate, String partialName, boolean matchLower) { + if (candidate == null || partialName == null) { + return false; + } + + if (candidate.length() < partialName.length()) { + return false; + } + + if (matchLower) { + candidate = candidate.toLowerCase(Locale.ROOT); + partialName = partialName.toLowerCase(Locale.ROOT); + } + + return candidate.startsWith(partialName); + } + private static class IsInValueScanner { private final int cursorIndex;