From 02821bc2daa7e702b8a47519815bf976316f0e5c Mon Sep 17 00:00:00 2001 From: "nenka.lab" Date: Wed, 7 Oct 2026 12:45:22 +0900 Subject: [PATCH 1/2] feat: add the tooling component for editor language support --- .../xfl/msgbot/plugin/ipc/PluginService.kt | 11 + .../api/discovery/PluginManifestSchema.kt | 6 + .../plugin/api/remote/RemoteLanguageTools.kt | 166 ++++++++++++++ .../plugin/api/remote/ToolingEndpoint.kt | 196 +++++++++++++++++ .../msgbot/plugin/api/remote/ToolingWire.kt | 145 +++++++++++++ .../com/xfl/msgbot/plugin/api/remote/Wire.kt | 16 +- .../plugin/api/tooling/LanguageTools.kt | 168 +++++++++++++++ .../plugin/api/remote/ToolingSessionTest.kt | 203 ++++++++++++++++++ readme.md | 33 ++- .../msgbot/plugin/tck/ToolingConformance.kt | 103 +++++++++ .../xfl/msgbot/plugin/tck/ToolingHarness.kt | 55 +++++ .../tck/ExampleToolingConformanceTest.kt | 43 ++++ 12 files changed, 1143 insertions(+), 2 deletions(-) create mode 100644 contract/src/main/kotlin/com/xfl/msgbot/plugin/api/remote/RemoteLanguageTools.kt create mode 100644 contract/src/main/kotlin/com/xfl/msgbot/plugin/api/remote/ToolingEndpoint.kt create mode 100644 contract/src/main/kotlin/com/xfl/msgbot/plugin/api/remote/ToolingWire.kt create mode 100644 contract/src/main/kotlin/com/xfl/msgbot/plugin/api/tooling/LanguageTools.kt create mode 100644 contract/src/test/kotlin/com/xfl/msgbot/plugin/api/remote/ToolingSessionTest.kt create mode 100644 tck/src/main/kotlin/com/xfl/msgbot/plugin/tck/ToolingConformance.kt create mode 100644 tck/src/main/kotlin/com/xfl/msgbot/plugin/tck/ToolingHarness.kt create mode 100644 tck/src/test/kotlin/com/xfl/msgbot/plugin/tck/ExampleToolingConformanceTest.kt diff --git a/android/src/main/java/com/xfl/msgbot/plugin/ipc/PluginService.kt b/android/src/main/java/com/xfl/msgbot/plugin/ipc/PluginService.kt index 0c09f95..e7430df 100644 --- a/android/src/main/java/com/xfl/msgbot/plugin/ipc/PluginService.kt +++ b/android/src/main/java/com/xfl/msgbot/plugin/ipc/PluginService.kt @@ -19,6 +19,8 @@ import com.xfl.msgbot.plugin.api.engine.ScriptEngineFactory import com.xfl.msgbot.plugin.api.provider.Provider import com.xfl.msgbot.plugin.api.remote.EngineEndpoint import com.xfl.msgbot.plugin.api.remote.ProviderEndpoint +import com.xfl.msgbot.plugin.api.remote.ToolingEndpoint +import com.xfl.msgbot.plugin.api.tooling.ToolingFactory import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.RejectedExecutionException @@ -34,6 +36,7 @@ import java.util.concurrent.atomic.AtomicLong * class MyPluginService : PluginService() { * override val engines = mapOf("luaj" to ScriptEngineFactory(::LuaEngine)) * override val providers = mapOf("weather" to ::WeatherProvider) + * override val tooling = mapOf("ts-tools" to ToolingFactory(::TypeScriptTools)) * } * ``` * @@ -43,6 +46,7 @@ import java.util.concurrent.atomic.AtomicLong open class PluginService : Service() { protected open val engines: Map = emptyMap() protected open val providers: Map Provider> = emptyMap() + protected open val tooling: Map = emptyMap() private class Session( val callback: IPluginCallback, @@ -87,6 +91,12 @@ open class PluginService : Service() { onProtocolError = lost, factory = providers[component] ?: throw IllegalArgumentException("This plugin has no provider '$component'"), ) + PluginRole.TOOLING -> + ToolingEndpoint( + transport, + tooling[component] ?: throw IllegalArgumentException("This plugin has no tooling '$component'"), + onProtocolError = lost, + ) else -> throw IllegalArgumentException("Unknown role '$role'") } } catch (e: Throwable) { @@ -184,6 +194,7 @@ open class PluginService : Service() { when (endpoint) { is EngineEndpoint -> endpoint.isStopped is ProviderEndpoint -> endpoint.isStopped + is ToolingEndpoint -> endpoint.isStopped else -> true } diff --git a/contract/src/main/kotlin/com/xfl/msgbot/plugin/api/discovery/PluginManifestSchema.kt b/contract/src/main/kotlin/com/xfl/msgbot/plugin/api/discovery/PluginManifestSchema.kt index f66ae95..00a7a12 100644 --- a/contract/src/main/kotlin/com/xfl/msgbot/plugin/api/discovery/PluginManifestSchema.kt +++ b/contract/src/main/kotlin/com/xfl/msgbot/plugin/api/discovery/PluginManifestSchema.kt @@ -22,6 +22,7 @@ object PluginManifestSchema { const val TAG_LANGUAGE = "language" const val TAG_PROFILE = "profile" const val TAG_PROVIDER = "provider" + const val TAG_TOOLING = "tooling" const val TAG_OPTION = "option" /** Root attribute: the plugin's [com.xfl.msgbot.plugin.api.protocol.ProtocolVersion.CURRENT]. */ @@ -58,6 +59,10 @@ object PluginManifestSchema { /** Space-separated namespaces. Publishing an unlisted namespace is refused. */ const val ATTR_PROVIDES = "provides" + // + /** Space-separated language names (``) the tooling serves, such as `javascript typescript`. */ + const val ATTR_LANGUAGES = "languages" + //