Skip to content

Commit

Permalink
starting to impl options and flags with new ArgumentConsumer
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt-MX committed Jun 28, 2024
1 parent 966c831 commit 93be473
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ package com.mattmx.ktgui.commands.declarative
import com.mattmx.ktgui.GuiManager
import com.mattmx.ktgui.commands.declarative.arg.Argument
import com.mattmx.ktgui.commands.declarative.arg.ArgumentContext
import com.mattmx.ktgui.commands.declarative.arg.ArgumentProcessor
import com.mattmx.ktgui.commands.declarative.arg.consumer.GreedyArgumentConsumer
import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer
import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer
import com.mattmx.ktgui.commands.declarative.arg.impl.FlagArgument
import com.mattmx.ktgui.commands.declarative.arg.impl.OptionArgument
import com.mattmx.ktgui.commands.declarative.arg.impl.OptionSyntax
import com.mattmx.ktgui.commands.declarative.invocation.*
import com.mattmx.ktgui.commands.declarative.syntax.*
import com.mattmx.ktgui.commands.suggestions.CommandSuggestion
Expand All @@ -29,6 +34,9 @@ open class DeclarativeCommandBuilder(
var subcommands = setOf<DeclarativeCommandBuilder>()
var expectedArguments = arrayOf<Argument<*>>()
var localArgumentSuggestions = hashMapOf<String, CommandSuggestion<*>>()
val permittedFlags = mutableSetOf<FlagArgument>()
val permittedOptions = mutableSetOf<OptionArgument<*>>()
val optionsSyntax = OptionSyntax()
var coolDown = Optional.empty<ActionCoolDown<CommandSender>>()
private set
var coolDownCallback = Optional.empty<(StorageCommandContext<*>) -> Unit>()
Expand Down Expand Up @@ -176,6 +184,19 @@ open class DeclarativeCommandBuilder(
it
}

@JavaCompatibility
fun withFlag(flagArgument: FlagArgument) = apply {
this.permittedFlags.add(flagArgument)
}

@JavaCompatibility
fun <T : Any> withOption(optionArgument: OptionArgument<T>) = apply {
this.permittedOptions.add(optionArgument)
}

operator fun FlagArgument.unaryPlus() = withFlag(this)
operator fun <T : Any> OptionArgument<T>.unaryPlus() = withOption(this)

fun getCurrentCommand(context: SuggestionInvocation<*>): Pair<SuggestionInvocation<*>, DeclarativeCommandBuilder?> {
val firstArg = context.rawArgs.firstOrNull()
?: return context to this
Expand All @@ -197,14 +218,12 @@ open class DeclarativeCommandBuilder(

val list = context.rawArgs.toMutableList()
var suggestedArgs: List<String>? = null

for (arg in expectedArguments) {
val consumed = arg.consumer.consume(list)
list.removeAll(consumed)
val processor = ArgumentProcessor(this, list)
val stringValue = arg.consume(processor) ?: continue

if (list.isEmpty()) {
// This is the last argument
suggestedArgs = getSuggestions(arg)?.getLastArgSuggestion(context)
}
suggestedArgs = getSuggestions(arg)?.getLastArgSuggestion(context)
}

// val arg = expectedArguments.getOrNull(context.rawArgs.size - 1)
Expand Down Expand Up @@ -273,8 +292,12 @@ open class DeclarativeCommandBuilder(
}

val argumentValues = hashMapOf<String, ArgumentContext<*>>()

var expectedArgumentIndex = 0
var providedArgumentIndex = 0

// todo rewrite arguments processing to handle new ArgumentConsumer.

while (providedArgumentIndex < context.rawArgs.size) {
val arg = context.rawArgs[providedArgumentIndex]

Expand Down Expand Up @@ -405,8 +428,8 @@ open class DeclarativeCommandBuilder(
if (!suggestions.isNullOrEmpty()) {
val opt = options.arguments
"${opt.suggestionsChar}${opt.suggestionsPrefix}${suggestions.joinToString(opt.suggestionsDivider)}${opt.suggestionsSuffix}"
} else "${options.arguments.typeChar}${arg.type()}${if (arg.consumer.isVarArg()) "..." else ""}"
} else "${options.arguments.typeChar}${arg.type()}${if (arg.consumer.isVarArg()) "..." else ""}"
} else "${options.arguments.typeChar}${arg.type()}}"
} else "${options.arguments.typeChar}${arg.type()}}"

// Apply descriptions
if (options.arguments.showDescriptions) {
Expand Down Expand Up @@ -434,8 +457,7 @@ open class DeclarativeCommandBuilder(
is VariableDeclarationSyntax -> {
expectedArguments += Argument<Any>(
syntax.getName(),
syntax.getType(),
if (syntax.isGreedy()) GreedyArgumentConsumer() else SingleArgumentConsumer()
syntax.getType()
).apply { optional(syntax.isOptional()) }
}

Expand All @@ -462,6 +484,9 @@ open class DeclarativeCommandBuilder(
inline operator fun String.invoke(block: DeclarativeCommandBuilder.() -> Unit) =
DeclarativeCommandBuilder(this).apply(block)

inline fun <reified S : CommandSender> String.runs(noinline block: RunnableCommandContext<S>.() -> Unit) =
DeclarativeCommandBuilder(this).runs(block)

inline fun command(name: String, block: DeclarativeCommandBuilder.() -> Unit) =
DeclarativeCommandBuilder(name).apply(block)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mattmx.ktgui.commands.declarative.arg

import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder
import com.mattmx.ktgui.commands.declarative.arg.consumer.ArgumentConsumer
import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer
import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext
import com.mattmx.ktgui.commands.declarative.invocation.InvalidArgContext
import com.mattmx.ktgui.commands.declarative.invocation.RunnableCommandContext
Expand All @@ -15,10 +15,12 @@ import java.util.*

open class Argument<T : Any>(
private var name: String,
private val typeName: String,
val consumer: ArgumentConsumer
private val typeName: String
) : Invokable<Argument<T>> {
var description: String? = null
protected set
var consumer = ArgumentConsumer.single()
protected set
var suggests = Optional.empty<CommandSuggestion<T>>()
var missingCallback = EventCallback<InvalidArgContext<*>>()
protected set
Expand Down Expand Up @@ -79,11 +81,30 @@ open class Argument<T : Any>(
return !invalidCallback.isEmpty()
}

open fun getSuggestions() {
TODO()
}

open fun getDefaultSuggestions(): List<String>? {
val context = SuggestionInvocation(Optional.empty(), "", emptyList())
return if (suggests.isPresent) {
suggests.get().getSuggestion(context)
} else {
val suggestion = CommandSuggestionRegistry.get(typeName)
if (suggestion.isPresent) suggestion.get().getSuggestion(context) else null
}
}

fun withTypeSuggestions() = apply {
suggests = CommandSuggestionRegistry.get(typeName) as Optional<CommandSuggestion<T>>
}

// todo this should be called with [validate] for efficiency, as well as the consumer
infix fun consumes(consumer: ArgumentConsumer) = apply {
this.consumer = consumer
}

open fun consume(processor: ArgumentProcessor) = this.consumer.consume(processor)

open fun getValueOfString(cmd: DeclarativeCommandBuilder, context: BaseCommandContext<*>, split: List<String>): T? {
return getValueOfString(cmd, context, split.joinToString(" "))
}
Expand All @@ -106,30 +127,17 @@ open class Argument<T : Any>(
return ArgumentContext(stringValue, Optional.ofNullable(actualValue as T?), this)
}

open fun validate(split: List<String>) = validate(split.joinToString(" "))

open fun validate(stringValue: String?) = true

open fun getDefaultSuggestions(): List<String>? {
val context = SuggestionInvocation(Optional.empty(), "", emptyList())
return if (suggests.isPresent) {
suggests.get().getSuggestion(context)
} else {
val suggestion = CommandSuggestionRegistry.get(typeName)
if (suggestion.isPresent) suggestion.get().getSuggestion(context) else null
}
}

infix fun nameEquals(other: Argument<*>) = name() == other.name()

override fun toString() =
"<$name${if (isOptional()) "?" else ""}:${typeName}${if (consumer.isVarArg()) "..." else ""}>"
"<$name${if (isOptional()) "?" else ""}:${typeName}}>"

open fun applyToClone(cloned: Argument<T>) = cloned

fun clone() : Argument<T> {
return Argument<T>(name, typeName, consumer)
return Argument<T>(name, typeName)
.let {
it.consumer = consumer
it.invalidCallback = invalidCallback.clone()
it.missingCallback = missingCallback.clone()
it.suggests = suggests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mattmx.ktgui.commands.declarative.arg

import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder
import com.mattmx.ktgui.commands.declarative.arg.impl.*

class ArgumentProcessor(
val command: DeclarativeCommandBuilder,
val args: List<String>
) {
var pointer = 0
// Should be added to the command context
val optionsAndFlags = hashMapOf<String, String>()

fun peek(i: Int) = args.getOrNull(pointer + i)
fun current() = peek(0)
fun next(): String? {
// Check if it is a flag
pointer++
while (current().let { it != null && command.optionsSyntax.match(it) }) {
val optionOrPointerId = command.optionsSyntax.removePrefixFrom(current()!!)

if (command.permittedFlags.any { it.chatName() == optionOrPointerId }) {
optionsAndFlags[optionOrPointerId] = true.toString()
pointer++
} else if (command.permittedOptions.any { it.name() == optionOrPointerId }) {

// TODO this should read the argument type args (does that make sense?)
// e.g '--test "hello world"' -> hello world

val value = peek(1) ?: continue
optionsAndFlags[optionOrPointerId] = value
pointer += 2
} else break
}

return current()
}

fun reset() {
this.pointer = 0
this.optionsAndFlags.clear()
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ fun interface ArgumentConsumer {
fun consume(processor: ArgumentProcessor): String?

companion object {
fun single() = ArgumentConsumer { processor -> processor.next() }
private val NONE = ArgumentConsumer { _ -> null }
private val SINGLE = ArgumentConsumer { processor -> processor.next() }

fun none() = NONE
fun single() = SINGLE

infix fun untilFalse(predicate: (ArgumentProcessor, String) -> Boolean) = ArgumentConsumer { processor ->
var current = processor.next()
Expand Down Expand Up @@ -38,7 +42,7 @@ fun interface ArgumentConsumer {
current = processor.next()

if (current == null) {
return@ArgumentConsumer null
return@ArgumentConsumer fullString
}

fullString += " $current"
Expand All @@ -47,12 +51,14 @@ fun interface ArgumentConsumer {
return@ArgumentConsumer fullString
}
}
null
fullString
}

infix fun untilPartial(predicate: (ArgumentProcessor, String) -> Boolean) = untilFalsePartial { p, s -> !predicate(p, s) }

fun remaining() = untilFalse { processor, _ -> processor.pointer < processor.args.size }
fun remaining() = untilPartial { processor, _ ->
processor.pointer >= processor.args.size
}

infix fun variable(amount: Int): ArgumentConsumer {
var i = amount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ package com.mattmx.ktgui.commands.declarative.arg.impl

import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder
import com.mattmx.ktgui.commands.declarative.arg.Argument
import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer
import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext

class FlagArgument(
name: String
) : Argument<Boolean>(name, "boolean", SingleArgumentConsumer()) {
) : Argument<Boolean>(name, "boolean") {

fun chatName() = name().replace("_", "-")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.mattmx.ktgui.commands.declarative.arg.impl

import com.mattmx.ktgui.commands.declarative.arg.Argument
import com.mattmx.ktgui.commands.declarative.arg.consumer.ArgumentConsumer

class OptionArgument<T : Any>(
name: String,
typeName: String,
consumer: ArgumentConsumer
) : Argument<T>(name, typeName, consumer) {
typeName: String
) : Argument<T>(name, typeName) {

fun chatName() = name().replace("_", "-")


}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class SuggestionInvocation<S : CommandSender>(
val alias: String,
val rawArgs: List<String>
) {
lateinit var consumed: List<String>
val last: String
get() = rawArgs.lastOrNull() ?: ""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.mattmx.ktgui.commands.suggestions
import com.mattmx.ktgui.commands.declarative.invocation.SuggestionInvocation

fun interface CommandSuggestion<V> {
fun getSuggestion(invocation: SuggestionInvocation<*>): List<String>?
fun getSuggestion(invocation: SuggestionInvocation<*>): Collection<String>?

fun getLastArgSuggestion(invocation: SuggestionInvocation<*>) = getSuggestion(invocation)
?.filter { it.startsWith((invocation.rawArgs.lastOrNull() ?: ""), true) }
Expand Down
Loading

0 comments on commit 93be473

Please sign in to comment.