mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Provide proper instances of XmlRegistry to completion providers with the Lookup API
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.itsaky.androidide.language
|
||||
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.lsp.models.CompletionItem
|
||||
import com.itsaky.androidide.lsp.models.CompletionParams
|
||||
@@ -24,13 +25,18 @@ import com.itsaky.androidide.lsp.models.FailureType.COMPLETION
|
||||
import com.itsaky.androidide.lsp.models.LSPFailure
|
||||
import com.itsaky.androidide.models.Position
|
||||
import com.itsaky.androidide.progress.ProcessCancelledException
|
||||
import com.itsaky.androidide.projects.ProjectManager
|
||||
import com.itsaky.androidide.projects.api.AndroidModule
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import com.itsaky.androidide.xml.resources.ResourceTableRegistry
|
||||
import com.itsaky.androidide.xml.versions.ApiVersions
|
||||
import com.itsaky.androidide.xml.widgets.WidgetTable
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionHelper
|
||||
import io.github.rosemoe.sora.text.CharPosition
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
import java.nio.file.Path
|
||||
import java.util.function.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* Common implementation of completion provider which requests completions to provided language
|
||||
@@ -59,6 +65,32 @@ class CommonCompletionProvider(private val server: ILanguageServer) {
|
||||
): List<CompletionItem> {
|
||||
val completionResult =
|
||||
try {
|
||||
val module = ProjectManager.findModuleForFile(file)
|
||||
if (module != null && module is AndroidModule) {
|
||||
val lookup = Lookup.DEFAULT
|
||||
val versions = module.getApiVersions()
|
||||
if (versions != null) {
|
||||
lookup.update(ApiVersions.COMPLETION_LOOKUP_KEY, versions)
|
||||
}
|
||||
|
||||
val widgets = module.getWidgetTable()
|
||||
if (widgets != null) {
|
||||
lookup.update(WidgetTable.COMPLETION_LOOKUP_KEY, widgets)
|
||||
}
|
||||
|
||||
val moduleResources = module.getSourceResourceTables()
|
||||
if (moduleResources.isNotEmpty()) {
|
||||
lookup.update(ResourceTableRegistry.COMPLETION_MODULE_RES_LOOKUP_KEY, moduleResources)
|
||||
}
|
||||
|
||||
val frameworkResources = module.getFrameworkResourceTable()
|
||||
if (frameworkResources != null) {
|
||||
lookup.update(
|
||||
ResourceTableRegistry.COMPLETION_FRAMEWORK_RES_LOOKUP_KEY,
|
||||
frameworkResources
|
||||
)
|
||||
}
|
||||
}
|
||||
val prefix = CompletionHelper.computePrefix(content, position) { prefixMatcher.test(it) }
|
||||
val params =
|
||||
CompletionParams(Position(position.line, position.column, position.index), file)
|
||||
|
||||
+7
-25
@@ -249,30 +249,12 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
val threads = mutableListOf<Thread>()
|
||||
threads.add(
|
||||
Thread {
|
||||
val registry = ResourceTableRegistry.getInstance()
|
||||
if (platformDir != null) {
|
||||
registry.forPlatformDir(platformDir)
|
||||
}
|
||||
|
||||
mainSourceSet?.sourceProvider?.resDirectories?.forEach { registry.forResourceDir(it) }
|
||||
getFrameworkResourceTable()
|
||||
getSourceResourceTables()
|
||||
}
|
||||
)
|
||||
|
||||
if (platformDir != null) {
|
||||
threads.add(
|
||||
Thread {
|
||||
val registry = ApiVersionsRegistry.getInstance()
|
||||
registry.forPlatformDir(platformDir)
|
||||
}
|
||||
)
|
||||
|
||||
threads.add(
|
||||
Thread {
|
||||
val registry = WidgetTableRegistry.getInstance()
|
||||
registry.forPlatformDir(platformDir)
|
||||
}
|
||||
)
|
||||
}
|
||||
threads.add(Thread(this::getApiVersions))
|
||||
threads.add(Thread(this::getWidgetTable))
|
||||
|
||||
for (thread in threads) {
|
||||
thread.start()
|
||||
@@ -310,7 +292,7 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the [ResourceTable] instance for this module's compile SDK.
|
||||
*
|
||||
@@ -321,10 +303,10 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
|
||||
if (platformDir != null) {
|
||||
return ResourceTableRegistry.getInstance().forPlatformDir(platformDir)
|
||||
}
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the resource tables for this module as well as it's dependent modules.
|
||||
*
|
||||
|
||||
@@ -58,6 +58,7 @@ dependencies {
|
||||
implementation project(path: ':logger')
|
||||
implementation project(path: ':build-tools:jdt')
|
||||
|
||||
api project(path: ':lookup')
|
||||
api project(path: ':build-tools:aaptcompiler')
|
||||
|
||||
testImplementation tests.junit
|
||||
|
||||
+6
-1
@@ -18,6 +18,7 @@
|
||||
package com.itsaky.androidide.xml.resources
|
||||
|
||||
import com.android.aaptcompiler.ResourceTable
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
import com.itsaky.androidide.xml.registry.XmlRegistry
|
||||
import com.itsaky.androidide.xml.resources.internal.DefaultResourceTableRegistry
|
||||
import java.io.File
|
||||
@@ -30,6 +31,10 @@ import java.io.File
|
||||
interface ResourceTableRegistry : XmlRegistry<ResourceTable> {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic val COMPLETION_MODULE_RES_LOOKUP_KEY = Lookup.Key<Set<ResourceTable>>()
|
||||
@JvmStatic val COMPLETION_FRAMEWORK_RES_LOOKUP_KEY = Lookup.Key<ResourceTable>()
|
||||
|
||||
@JvmStatic fun getInstance(): ResourceTableRegistry = DefaultResourceTableRegistry
|
||||
}
|
||||
|
||||
@@ -47,7 +52,7 @@ interface ResourceTableRegistry : XmlRegistry<ResourceTable> {
|
||||
* @param dir The resource directory to remove the resource table entry for.
|
||||
*/
|
||||
fun removeTable(dir: File)
|
||||
|
||||
|
||||
override fun forPlatformDir(platform: File): ResourceTable? {
|
||||
return forResourceDir(File(platform, "data/res"))
|
||||
}
|
||||
|
||||
+9
-3
@@ -17,17 +17,23 @@
|
||||
|
||||
package com.itsaky.androidide.xml.versions
|
||||
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
|
||||
/**
|
||||
* API information about classes.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface ApiVersions {
|
||||
|
||||
|
||||
companion object {
|
||||
@JvmStatic val COMPLETION_LOOKUP_KEY = Lookup.Key<ApiVersions>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the information about the class with the given name.
|
||||
*
|
||||
* @param name The fully qualified name of the class.
|
||||
*/
|
||||
fun getClass(name: String) : ClassInfo?
|
||||
}
|
||||
fun getClass(name: String): ClassInfo?
|
||||
}
|
||||
|
||||
+9
-3
@@ -17,17 +17,23 @@
|
||||
|
||||
package com.itsaky.androidide.xml.widgets
|
||||
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
|
||||
/**
|
||||
* A widget table holds information about all widgets defined in the `widgets.txt`.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface WidgetTable {
|
||||
|
||||
|
||||
companion object {
|
||||
@JvmStatic val COMPLETION_LOOKUP_KEY = Lookup.Key<WidgetTable>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the widget for the given fully qualified name. For example, `android.widget.TextView`.
|
||||
*
|
||||
* @return The widget or `null`.
|
||||
*/
|
||||
fun getWidget(name: String) : Widget?
|
||||
}
|
||||
fun getWidget(name: String): Widget?
|
||||
}
|
||||
|
||||
@@ -39,6 +39,15 @@ public interface Lookup {
|
||||
*/
|
||||
<T> void register(Class<T> klass, T instance);
|
||||
|
||||
/**
|
||||
* Updates the service for the given class. Registers if not already registered.
|
||||
*
|
||||
* @param klass The class to update the service for.
|
||||
* @param instance The instance of the service.
|
||||
* @param <T> The type of the service.
|
||||
*/
|
||||
<T> void update(Class<T> klass, T instance);
|
||||
|
||||
/**
|
||||
* Unregister the service for given class.
|
||||
*
|
||||
@@ -84,6 +93,15 @@ public interface Lookup {
|
||||
@Nullable
|
||||
<T> T lookup(Key<T> key);
|
||||
|
||||
/**
|
||||
* Updates the service for the given key. Registers if not already registered.
|
||||
*
|
||||
* @param key The key to update the service for.
|
||||
* @param instance The instance of the service.
|
||||
* @param <T> The type of the service.
|
||||
*/
|
||||
<T> void update(Key<T> key, T instance);
|
||||
|
||||
/**
|
||||
* The key that is used to register services in the table.
|
||||
*
|
||||
|
||||
@@ -47,6 +47,11 @@ public final class DefaultLookup implements Lookup {
|
||||
register(key, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void update(final Class<T> klass, final T instance) {
|
||||
update(key(klass), instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void unregister(final Class<T> klass) {
|
||||
unregister(key(klass));
|
||||
@@ -58,13 +63,9 @@ public final class DefaultLookup implements Lookup {
|
||||
return lookup(key(klass));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> void register(final Key<T> key, final T instance) {
|
||||
final T existing = (T) services.put(key, instance);
|
||||
if (existing != null) {
|
||||
throw new ServiceRegisteredException();
|
||||
}
|
||||
registerOrUpdate(key, instance, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,12 +80,26 @@ public final class DefaultLookup implements Lookup {
|
||||
return (T) services.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void update(final Key<T> key, final T instance) {
|
||||
registerOrUpdate(key, instance, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void registerOrUpdate(final Key<T> key, final T instance, boolean update) {
|
||||
final T existing = (T) services.put(key, instance);
|
||||
if (existing != null && !update) {
|
||||
services.put(key, existing);
|
||||
throw new ServiceRegisteredException();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
private <T> Key<T> key(Class<T> klass) {
|
||||
final var key = keyTable.get(klass);
|
||||
if (key == null) {
|
||||
// Returning a new key instance will always make all the methods above return null
|
||||
// Returning a new key instance will always make the lookup methods above return null
|
||||
return new Key<>();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,18 @@ class LookupTest {
|
||||
@Test
|
||||
fun `test with class`() {
|
||||
val lookup = Lookup.DEFAULT
|
||||
val service = TestServiceImpl()
|
||||
lookup.register(TestService::class.java, service)
|
||||
|
||||
// Test register
|
||||
var service = TestServiceImpl()
|
||||
lookup.register(TestService::class.java, service)
|
||||
lookup.lookup(TestService::class.java).apply {
|
||||
assertThat(this).isNotNull()
|
||||
assertThat(this).isEqualTo(service)
|
||||
}
|
||||
|
||||
// Test update
|
||||
service = TestServiceImpl()
|
||||
lookup.update(TestService::class.java, service)
|
||||
lookup.lookup(TestService::class.java).apply {
|
||||
assertThat(this).isNotNull()
|
||||
assertThat(this).isEqualTo(service)
|
||||
@@ -46,15 +55,27 @@ class LookupTest {
|
||||
fun `test with key`() {
|
||||
val lookup = Lookup.DEFAULT
|
||||
val key = Key<TestService>()
|
||||
val service = TestServiceImpl()
|
||||
lookup.register(key, service)
|
||||
|
||||
// Test register
|
||||
var service = TestServiceImpl()
|
||||
lookup.register(key, service)
|
||||
lookup.lookup(key).apply {
|
||||
assertThat(this).isNotNull()
|
||||
assertThat(this).isEqualTo(service)
|
||||
}
|
||||
|
||||
// Service not registered with the Class object. Must return null.
|
||||
lookup.lookup(TestService::class.java).apply { assertThat(this).isNull() }
|
||||
|
||||
// Test update
|
||||
service = TestServiceImpl()
|
||||
lookup.update(key, service)
|
||||
lookup.lookup(key).apply {
|
||||
assertThat(this).isNotNull()
|
||||
assertThat(this).isEqualTo(service)
|
||||
}
|
||||
|
||||
// Not service with the Class object. Must return null.
|
||||
// Service not registered with the Class object. Must return null.
|
||||
lookup.lookup(TestService::class.java).apply { assertThat(this).isNull() }
|
||||
|
||||
lookup.unregister(key)
|
||||
|
||||
Reference in New Issue
Block a user