Introduce InstanceExtensionPoint

This commit is contained in:
Him188 2020-10-20 13:47:43 +08:00
parent 2084f8154f
commit 58af1b3354
9 changed files with 60 additions and 20 deletions

View File

@ -2,6 +2,7 @@ package net.mamoe.mirai.console.command.parse
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.extensions.CommandCallParserProvider
import net.mamoe.mirai.console.internal.command.flattenCommandComponents import net.mamoe.mirai.console.internal.command.flattenCommandComponents
import net.mamoe.mirai.console.util.ConsoleExperimentalApi import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
@ -20,4 +21,6 @@ public object SpaceSeparatedCommandCallParser : CommandCallParser {
valueArguments = flatten.drop(1).map(::InvariantCommandValueArgument) valueArguments = flatten.drop(1).map(::InvariantCommandValueArgument)
) )
} }
public object Provider : CommandCallParserProvider(SpaceSeparatedCommandCallParser)
} }

View File

@ -6,6 +6,7 @@ import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable
import net.mamoe.mirai.console.command.parse.CommandCall import net.mamoe.mirai.console.command.parse.CommandCall
import net.mamoe.mirai.console.command.parse.CommandValueArgument import net.mamoe.mirai.console.command.parse.CommandValueArgument
import net.mamoe.mirai.console.extensions.CommandCallResolverProvider
import net.mamoe.mirai.console.util.ConsoleExperimentalApi import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.cast import net.mamoe.mirai.console.util.cast
import net.mamoe.mirai.console.util.safeCast import net.mamoe.mirai.console.util.safeCast
@ -14,6 +15,8 @@ import java.util.*
@ConsoleExperimentalApi @ConsoleExperimentalApi
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
public object BuiltInCommandCallResolver : CommandCallResolver { public object BuiltInCommandCallResolver : CommandCallResolver {
public object Provider : CommandCallResolverProvider(BuiltInCommandCallResolver)
override fun resolve(call: CommandCall): ResolvedCommandCall? { override fun resolve(call: CommandCall): ResolvedCommandCall? {
val callee = CommandManager.matchCommand(call.calleeName) ?: return null val callee = CommandManager.matchCommand(call.calleeName) ?: return null

View File

@ -24,6 +24,12 @@ public open class AbstractExtensionPoint<T : Extension>(
public override val extensionType: KClass<T>, public override val extensionType: KClass<T>,
) : ExtensionPoint<T> ) : ExtensionPoint<T>
public open class InstanceExtensionPoint<E : InstanceExtension<T>, T>(
extensionType: KClass<E>,
public vararg val builtinImplementations: E,
) : AbstractExtensionPoint<E>(extensionType)
/** /**
* 表示一个 [SingletonExtension] [ExtensionPoint] * 表示一个 [SingletonExtension] [ExtensionPoint]
*/ */

View File

@ -11,13 +11,15 @@ package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCallParser import net.mamoe.mirai.console.command.parse.CommandCallParser
import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.command.parse.SpaceSeparatedCommandCallParser
import net.mamoe.mirai.console.extension.InstanceExtension import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.console.extension.InstanceExtensionPoint
/** /**
* The provider of [CommandCallParser] * The provider of [CommandCallParser]
*/ */
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
public interface CommandCallParserProvider : InstanceExtension<CommandCallParser> { public open class CommandCallParserProvider(override val instance: CommandCallParser) : InstanceExtension<CommandCallParser> {
public companion object ExtensionPoint : AbstractExtensionPoint<CommandCallParserProvider>(CommandCallParserProvider::class) public companion object ExtensionPoint :
InstanceExtensionPoint<CommandCallParserProvider, CommandCallParser>(CommandCallParserProvider::class, SpaceSeparatedCommandCallParser.Provider)
} }

View File

@ -10,11 +10,13 @@
package net.mamoe.mirai.console.extensions package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.resolve.BuiltInCommandCallResolver
import net.mamoe.mirai.console.command.resolve.CommandCallResolver import net.mamoe.mirai.console.command.resolve.CommandCallResolver
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
import net.mamoe.mirai.console.extension.InstanceExtension import net.mamoe.mirai.console.extension.InstanceExtension
import net.mamoe.mirai.console.extension.InstanceExtensionPoint
@ExperimentalCommandDescriptors @ExperimentalCommandDescriptors
public interface CommandCallResolverProvider : InstanceExtension<CommandCallResolver> { public open class CommandCallResolverProvider(override val instance: CommandCallResolver) : InstanceExtension<CommandCallResolver> {
public companion object ExtensionPoint : AbstractExtensionPoint<CommandCallResolverProvider>(CommandCallResolverProvider::class) public companion object ExtensionPoint :
InstanceExtensionPoint<CommandCallResolverProvider, CommandCallResolver>(CommandCallResolverProvider::class, BuiltInCommandCallResolver.Provider)
} }

View File

@ -29,7 +29,7 @@ import kotlin.reflect.KClass
*/ */
public interface SingletonExtensionSelector : FunctionExtension { public interface SingletonExtensionSelector : FunctionExtension {
public data class Registry<T : Extension>( public data class Registry<T : Extension>(
val plugin: Plugin, val plugin: Plugin?,
val extension: T, val extension: T,
) )
@ -55,11 +55,11 @@ public interface SingletonExtensionSelector : FunctionExtension {
instances.isEmpty() -> BuiltInSingletonExtensionSelector instances.isEmpty() -> BuiltInSingletonExtensionSelector
instances.size == 1 -> { instances.size == 1 -> {
instances.single().also { (plugin, ext) -> instances.single().also { (plugin, ext) ->
MiraiConsole.mainLogger.info { "Loaded SingletonExtensionSelector: $ext from ${plugin.name}" } MiraiConsole.mainLogger.info { "Loaded SingletonExtensionSelector: $ext from ${plugin?.name ?: "<builtin>"}" }
}.extension }.extension
} }
else -> { else -> {
error("Found too many SingletonExtensionSelectors: ${instances.joinToString { (p, i) -> "'$i' from '${p.name}'" }}. Check your plugins and ensure there is only one external SingletonExtensionSelectors") error("Found too many SingletonExtensionSelectors: ${instances.joinToString { (p, i) -> "'$i' from '${p?.name ?: "<builtin>"}'" }}. Check your plugins and ensure there is only one external SingletonExtensionSelectors")
} }
} }
} }

View File

@ -52,7 +52,7 @@ internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector {
val candidatesList = candidates.toList() val candidatesList = candidates.toList()
for ((index, candidate) in candidatesList.withIndex()) { for ((index, candidate) in candidatesList.withIndex()) {
MiraiConsole.mainLogger.info { "${index + 1}. '${candidate.extension}' from '${candidate.plugin.name}'" } MiraiConsole.mainLogger.info { "${index + 1}. '${candidate.extension}' from '${candidate.plugin?.name ?: "<builtin>"}'" }
} }
MiraiConsole.mainLogger.info { "Please choose a number from 1 to ${candidatesList.count()}" } MiraiConsole.mainLogger.info { "Please choose a number from 1 to ${candidatesList.count()}" }

View File

@ -25,10 +25,10 @@ import kotlin.reflect.KClass
*/ */
internal object GlobalComponentStorage : AbstractConcurrentComponentStorage() internal object GlobalComponentStorage : AbstractConcurrentComponentStorage()
internal interface ExtensionRegistry<out E : Extension> { internal interface ExtensionRegistry<out E : Extension> {
val plugin: Plugin val plugin: Plugin?
val extension: E val extension: E
operator fun component1(): Plugin { operator fun component1(): Plugin? {
return this.plugin return this.plugin
} }
@ -38,21 +38,27 @@ internal interface ExtensionRegistry<out E : Extension> {
} }
internal class LazyExtensionRegistry<out E : Extension>( internal class LazyExtensionRegistry<out E : Extension>(
override val plugin: Plugin, override val plugin: Plugin?,
initializer: () -> E, initializer: () -> E,
) : ExtensionRegistry<E> { ) : ExtensionRegistry<E> {
override val extension: E by lazy { initializer() } override val extension: E by lazy { initializer() }
} }
internal data class DataExtensionRegistry<out E : Extension>( internal data class DataExtensionRegistry<out E : Extension>(
override val plugin: Plugin, override val plugin: Plugin?,
override val extension: E, override val extension: E,
) : ExtensionRegistry<E> ) : ExtensionRegistry<E>
internal abstract class AbstractConcurrentComponentStorage : ComponentStorage { internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal fun <T : Extension> ExtensionPoint<out T>.getExtensions(): Set<ExtensionRegistry<T>> { internal fun <T : Extension> ExtensionPoint<out T>.getExtensions(): Set<ExtensionRegistry<T>> {
return instances.getOrPut(this, ::CopyOnWriteArraySet) as Set<ExtensionRegistry<T>> val userDefined = instances.getOrPut(this, ::CopyOnWriteArraySet) as Set<ExtensionRegistry<T>>
val builtins = if (this is InstanceExtensionPoint<*, *>) {
this.builtinImplementations.mapTo(HashSet()) { DataExtensionRegistry(null, it) } as Set<ExtensionRegistry<T>>
} else null
return builtins?.plus(userDefined) ?: userDefined
} }
internal fun mergeWith(another: AbstractConcurrentComponentStorage) { internal fun mergeWith(another: AbstractConcurrentComponentStorage) {
@ -71,7 +77,7 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution @kotlin.internal.LowPriorityInOverloadResolution
internal inline fun <T : Extension> ExtensionPoint<out T>.withExtensions(block: T.(plugin: Plugin) -> Unit) { internal inline fun <T : Extension> ExtensionPoint<out T>.withExtensions(block: T.(plugin: Plugin?) -> Unit) {
contract { contract {
callsInPlace(block) callsInPlace(block)
} }
@ -131,11 +137,11 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
internal fun <T : Extension> ExtensionPoint<out T>.throwExtensionException( internal fun <T : Extension> ExtensionPoint<out T>.throwExtensionException(
extension: T, extension: T,
plugin: Plugin, plugin: Plugin?,
throwable: Throwable, throwable: Throwable,
) { ) {
throw ExtensionException( throw ExtensionException(
"Exception while executing extension '${extension.kClassQualifiedNameOrTip}' provided by plugin '${plugin.name}', registered for '${this.extensionType.qualifiedName}'", "Exception while executing extension '${extension.kClassQualifiedNameOrTip}' provided by plugin '${plugin?.name ?: "<builtin>"}', registered for '${this.extensionType.qualifiedName}'",
throwable throwable
) )
} }
@ -145,7 +151,7 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution @kotlin.internal.LowPriorityInOverloadResolution
internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T, plugin: Plugin) -> Unit): Unit = internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T, plugin: Plugin?) -> Unit): Unit =
withExtensions(block) withExtensions(block)
val instances: MutableMap<ExtensionPoint<*>, MutableSet<ExtensionRegistry<*>>> = ConcurrentHashMap() val instances: MutableMap<ExtensionPoint<*>, MutableSet<ExtensionRegistry<*>>> = ConcurrentHashMap()
@ -157,6 +163,15 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(DataExtensionRegistry(plugin, extensionInstance)) instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(DataExtensionRegistry(plugin, extensionInstance))
} }
@JvmName("contribute1")
fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
plugin: Plugin?,
extensionInstance: T,
) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(DataExtensionRegistry(plugin, extensionInstance))
}
override fun <T : Extension> contribute( override fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>, extensionPoint: ExtensionPoint<T>,
plugin: Plugin, plugin: Plugin,
@ -164,4 +179,13 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
) { ) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(LazyExtensionRegistry(plugin, lazyInstance)) instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(LazyExtensionRegistry(plugin, lazyInstance))
} }
@JvmName("contribute1")
fun <T : Extension> contribute(
extensionPoint: ExtensionPoint<T>,
plugin: Plugin?,
lazyInstance: () -> T,
) {
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(LazyExtensionRegistry(plugin, lazyInstance))
}
} }

View File

@ -148,7 +148,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
var count = 0 var count = 0
GlobalComponentStorage.run { GlobalComponentStorage.run {
PluginLoaderProvider.useExtensions { ext, plugin -> PluginLoaderProvider.useExtensions { ext, plugin ->
logger.info { "Loaded PluginLoader ${ext.instance} from ${plugin.name}" } logger.info { "Loaded PluginLoader ${ext.instance} from ${plugin?.name ?: "<builtin>"}" }
_pluginLoaders.add(ext.instance) _pluginLoaders.add(ext.instance)
count++ count++
} }