Console: autocomplete ALL THE THINGS!

This commit is contained in:
Yair Morgenstern
2024-01-04 10:35:07 +02:00
parent e049427b75
commit aa18e826df
2 changed files with 40 additions and 26 deletions

View File

@ -10,24 +10,24 @@ import com.unciv.models.stats.Stat
internal fun String.toCliInput() = this.lowercase().replace(" ","-") internal fun String.toCliInput() = this.lowercase().replace(" ","-")
/** Returns the string to *add* to the existing command */ /** Returns the string to *add* to the existing command */
fun getAutocompleteString(lastWord:String, allOptions:Iterable<String>):String? { fun getAutocompleteString(lastWord: String, allOptions: Collection<String>):String? {
val matchingOptions = allOptions.filter { it.toCliInput().startsWith(lastWord.toCliInput()) } val matchingOptions = allOptions.filter { it.toCliInput().startsWith(lastWord.toCliInput()) }
if (matchingOptions.isEmpty()) return null if (matchingOptions.isEmpty()) return null
if (matchingOptions.size == 1) return matchingOptions.first().removePrefix(lastWord) if (matchingOptions.size == 1) return matchingOptions.first().drop(lastWord.length)
val firstOption = matchingOptions.first() val firstOption = matchingOptions.first()
for ((index, char) in firstOption.withIndex()) { for ((index, char) in firstOption.withIndex()) {
if (matchingOptions.any { it.lastIndex < index } || if (matchingOptions.any { it.lastIndex < index } ||
matchingOptions.any { it[index] != char }) matchingOptions.any { it[index] != char })
return firstOption.substring(0, index).removePrefix(lastWord) return firstOption.substring(0, index).drop(lastWord.length)
} }
return firstOption.removePrefix(lastWord) return firstOption.drop(lastWord.length)
} }
interface ConsoleCommand { interface ConsoleCommand {
fun handle(console: DevConsolePopup, params: List<String>): DevConsoleResponse fun handle(console: DevConsolePopup, params: List<String>): DevConsoleResponse
/** Returns the string to *add* to the existing command */ /** Returns the string to *add* to the existing command */
fun autocomplete(params: List<String>): String? = "" fun autocomplete(console: DevConsolePopup, params: List<String>): String? = ""
} }
class ConsoleHintException(val hint:String):Exception() class ConsoleHintException(val hint:String):Exception()
@ -44,6 +44,36 @@ class ConsoleAction(val format: String, val action: (console: DevConsolePopup, p
DevConsoleResponse.error(errorException.error) DevConsoleResponse.error(errorException.error)
} }
} }
override fun autocomplete(console: DevConsolePopup, params: List<String>): String? {
if (params.isEmpty()) return null
val formatParams = format.split(" ").drop(2).map {
it.removeSurrounding("<",">").removeSurrounding("[","]").removeSurrounding("\"")
}
if (formatParams.size < params.size) return null
val formatParam = formatParams[params.lastIndex]
val lastParam = params.last()
val options = when (formatParam) {
"civName" -> console.gameInfo.civilizations.map { it.civName }
"unitName" -> console.gameInfo.ruleset.units.keys
"promotionName" -> console.gameInfo.ruleset.unitPromotions.keys
"improvementName" -> console.gameInfo.ruleset.tileImprovements.keys
"featureName" -> console.gameInfo.ruleset.terrains.values.filter { it.type == TerrainType.TerrainFeature }.map { it.name }
"stat" -> Stat.names()
else -> listOf()
}
return getAutocompleteString(lastParam, options)
}
fun validateFormat(format: String, params:List<String>){
val allParams = format.split(" ")
val requiredParamsAmount = allParams.count { it.startsWith('<') }
val optionalParamsAmount = allParams.count { it.startsWith('[') }
if (params.size < requiredParamsAmount || params.size > requiredParamsAmount + optionalParamsAmount)
throw ConsoleHintException("Format: $format")
}
} }
interface ConsoleCommandNode : ConsoleCommand { interface ConsoleCommandNode : ConsoleCommand {
@ -57,10 +87,10 @@ interface ConsoleCommandNode : ConsoleCommand {
return handler.handle(console, params.drop(1)) return handler.handle(console, params.drop(1))
} }
override fun autocomplete(params: List<String>): String? { override fun autocomplete(console: DevConsolePopup, params: List<String>): String? {
if (params.isEmpty()) return null if (params.isEmpty()) return null
val firstParam = params[0] val firstParam = params[0]
if (firstParam in subcommands) return subcommands[firstParam]!!.autocomplete(params.drop(1)) if (firstParam in subcommands) return subcommands[firstParam]!!.autocomplete(console, params.drop(1))
return getAutocompleteString(firstParam, subcommands.keys) return getAutocompleteString(firstParam, subcommands.keys)
} }
} }
@ -74,15 +104,6 @@ class ConsoleCommandRoot : ConsoleCommandNode {
) )
} }
fun validateFormat(format: String, params:List<String>){
val allParams = format.split(" ")
val requiredParamsAmount = allParams.count { it.startsWith('<') }
val optionalParamsAmount = allParams.count { it.startsWith('[') }
if (params.size < requiredParamsAmount || params.size > requiredParamsAmount + optionalParamsAmount)
throw ConsoleHintException("Format: $format")
}
class ConsoleUnitCommands : ConsoleCommandNode { class ConsoleUnitCommands : ConsoleCommandNode {
override val subcommands = hashMapOf<String, ConsoleCommand>( override val subcommands = hashMapOf<String, ConsoleCommand>(
@ -269,13 +290,4 @@ class ConsoleCivCommands : ConsoleCommandNode {
DevConsoleResponse.OK DevConsoleResponse.OK
} }
) )
override fun autocomplete(params: List<String>): String? {
if (params.isNotEmpty())
when (params[0]){
"addstat" -> if (params.size == 2)
return getAutocompleteString(params[1], Stat.names())
}
return super.autocomplete(params)
}
} }

View File

@ -85,7 +85,9 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
private fun getAutocomplete(): String { private fun getAutocomplete(): String {
val params = getParams(textField.text) val params = getParams(textField.text)
return commandRoot.autocomplete(params) ?: "" val result = commandRoot.autocomplete(this, params)
if (result.isNullOrEmpty()) return ""
return "$result "
} }
internal fun getCivByName(name: String) = gameInfo.civilizations.firstOrNull { it.civName.toCliInput() == name.toCliInput() } internal fun getCivByName(name: String) = gameInfo.civilizations.firstOrNull { it.civName.toCliInput() == name.toCliInput() }