mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-23 04:28:55 -04:00
cli: auto completions
This commit is contained in:
parent
805edb8590
commit
7191fdcb76
@ -16,6 +16,6 @@ package de.bixilon.minosoft.commands.errors.literal
|
||||
import de.bixilon.minosoft.commands.errors.ReaderError
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
class TrailingTextArgument(
|
||||
class TrailingTextError(
|
||||
reader: CommandReader,
|
||||
) : ReaderError(reader, reader.pointer, reader.length)
|
@ -14,7 +14,7 @@
|
||||
package de.bixilon.minosoft.commands.nodes
|
||||
|
||||
import de.bixilon.minosoft.commands.errors.DeadEndError
|
||||
import de.bixilon.minosoft.commands.errors.literal.TrailingTextArgument
|
||||
import de.bixilon.minosoft.commands.errors.literal.TrailingTextError
|
||||
import de.bixilon.minosoft.commands.stack.CommandStack
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
@ -47,7 +47,7 @@ abstract class CommandNode(
|
||||
try {
|
||||
executeChild(child, reader, stack)
|
||||
if (reader.canPeek()) {
|
||||
throw TrailingTextArgument(reader)
|
||||
throw TrailingTextError(reader)
|
||||
}
|
||||
return
|
||||
} catch (error: Throwable) {
|
||||
@ -78,7 +78,7 @@ abstract class CommandNode(
|
||||
try {
|
||||
val childSuggestions = child.getSuggestions(reader, stack)
|
||||
if (reader.canPeek()) {
|
||||
throw TrailingTextArgument(reader)
|
||||
throw TrailingTextError(reader)
|
||||
}
|
||||
parserSucceeds++
|
||||
|
||||
@ -115,7 +115,7 @@ abstract class CommandNode(
|
||||
protected fun checkForDeadEnd(reader: CommandReader) {
|
||||
if (children.isEmpty()) {
|
||||
if (reader.canPeek()) {
|
||||
throw TrailingTextArgument(reader)
|
||||
throw TrailingTextError(reader)
|
||||
} else {
|
||||
throw DeadEndError(reader)
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ open class TextInputElement(
|
||||
textElement.unmark()
|
||||
}
|
||||
|
||||
private fun _set(value: String) {
|
||||
protected fun _set(value: String) {
|
||||
val previous = _value.toString()
|
||||
val next = _value.replace(0, _value.length, value)
|
||||
_pointer = value.length
|
||||
|
@ -27,7 +27,7 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
|
||||
import de.bixilon.minosoft.util.KUtil
|
||||
|
||||
class NodeSuggestionsElement(guiRenderer: GUIRenderer, position: Vec2i) : Popper(guiRenderer, position) {
|
||||
class NodeSuggestionsElement(guiRenderer: GUIRenderer, position: Vec2i, val inputElement: NodeTextInputElement) : Popper(guiRenderer, position) {
|
||||
private var suggestionText = Array(MAX_SUGGESTIONS) { TextElement(guiRenderer, ChatComponent.EMPTY).apply { prefMaxSize = Vec2i(300, Font.TOTAL_CHAR_HEIGHT) } }
|
||||
private var textCount = 0
|
||||
private var offset = 0
|
||||
@ -56,6 +56,15 @@ class NodeSuggestionsElement(guiRenderer: GUIRenderer, position: Vec2i) : Popper
|
||||
field = value
|
||||
}
|
||||
|
||||
val activeSuggestion: Any?
|
||||
get() {
|
||||
val suggestions = suggestions ?: return null
|
||||
if (suggestions.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
return suggestions[offset]
|
||||
}
|
||||
|
||||
|
||||
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
|
||||
super.forceRender(offset, consumer, options)
|
||||
@ -114,6 +123,9 @@ class NodeSuggestionsElement(guiRenderer: GUIRenderer, position: Vec2i) : Popper
|
||||
if (type == KeyChangeTypes.RELEASE) {
|
||||
return super.onKey(key, type)
|
||||
}
|
||||
if (key == KeyCodes.KEY_ENTER || key == KeyCodes.KEY_KP_ENTER || key == KeyCodes.KEY_TAB) {
|
||||
return applySuggestion()
|
||||
}
|
||||
val offset = when (key) {
|
||||
KeyCodes.KEY_UP -> -1
|
||||
KeyCodes.KEY_PAGE_UP -> -5
|
||||
@ -134,6 +146,11 @@ class NodeSuggestionsElement(guiRenderer: GUIRenderer, position: Vec2i) : Popper
|
||||
offset = 0
|
||||
}
|
||||
|
||||
fun applySuggestion(): Boolean {
|
||||
inputElement.updateSuggestion(activeSuggestion ?: return false)
|
||||
return true
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_SUGGESTIONS = 10
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
|
||||
import de.bixilon.minosoft.util.KUtil
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
@ -48,7 +49,7 @@ class NodeTextInputElement(
|
||||
) : TextInputElement(guiRenderer, value, maxLength, cursorStyles, editable, onChange, background, shadow, scale, cutAtSize, parent) {
|
||||
private var showError = false
|
||||
private val errorElement = NodeErrorElement(guiRenderer, Vec2i.EMPTY)
|
||||
private val suggestions = NodeSuggestionsElement(guiRenderer, Vec2i.EMPTY)
|
||||
private val suggestions = NodeSuggestionsElement(guiRenderer, Vec2i.EMPTY, this)
|
||||
|
||||
|
||||
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
|
||||
@ -113,4 +114,10 @@ class NodeTextInputElement(
|
||||
suggestions.onClose()
|
||||
errorElement.onClose()
|
||||
}
|
||||
|
||||
fun updateSuggestion(suggestion: Any) {
|
||||
val string = suggestion.toString()
|
||||
_set(value + string.substring(KUtil.getOverlappingText(value, string), string.length))
|
||||
forceApply()
|
||||
}
|
||||
}
|
||||
|
@ -294,4 +294,22 @@ object KUtil {
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
fun getOverlappingText(start: String, end: String): Int {
|
||||
var overlapping = 0
|
||||
|
||||
shift@ for (shift in 1..end.length) {
|
||||
if (start.length < shift) {
|
||||
break
|
||||
}
|
||||
for (i in 0 until shift) {
|
||||
if (end.codePointAt(i) != start.codePointAt(start.length - (shift - i))) {
|
||||
continue@shift
|
||||
}
|
||||
}
|
||||
overlapping = shift
|
||||
}
|
||||
|
||||
return overlapping
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ package de.bixilon.minosoft.commands.nodes
|
||||
import de.bixilon.minosoft.commands.errors.DeadEndError
|
||||
import de.bixilon.minosoft.commands.errors.literal.ExpectedLiteralArgument
|
||||
import de.bixilon.minosoft.commands.errors.literal.InvalidLiteralArgumentError
|
||||
import de.bixilon.minosoft.commands.errors.literal.TrailingTextArgument
|
||||
import de.bixilon.minosoft.commands.errors.literal.TrailingTextError
|
||||
import de.bixilon.minosoft.commands.errors.reader.ExpectedWhitespaceError
|
||||
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParseError
|
||||
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParser
|
||||
@ -90,7 +90,7 @@ internal class ExecutionChildReadingTest {
|
||||
|
||||
@Test
|
||||
fun testTrailingData() {
|
||||
assertThrows<TrailingTextArgument> { (createCommand().execute(CommandReader("0_literal test"), CommandStack())) }
|
||||
assertThrows<TrailingTextError> { (createCommand().execute(CommandReader("0_literal test"), CommandStack())) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -115,6 +115,6 @@ internal class ExecutionChildReadingTest {
|
||||
|
||||
@Test
|
||||
fun testTrailingTextEmptyRootNode() {
|
||||
assertThrows<TrailingTextArgument> { RootNode().execute(CommandReader("trailing"), CommandStack()) }
|
||||
assertThrows<TrailingTextError> { RootNode().execute(CommandReader("trailing"), CommandStack()) }
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ package de.bixilon.minosoft.commands.nodes
|
||||
|
||||
import de.bixilon.minosoft.commands.errors.DeadEndError
|
||||
import de.bixilon.minosoft.commands.errors.literal.InvalidLiteralArgumentError
|
||||
import de.bixilon.minosoft.commands.errors.literal.TrailingTextArgument
|
||||
import de.bixilon.minosoft.commands.errors.literal.TrailingTextError
|
||||
import de.bixilon.minosoft.commands.errors.reader.ExpectedWhitespaceError
|
||||
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParseError
|
||||
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParser
|
||||
@ -95,7 +95,7 @@ internal class SuggestionChildReadingTest {
|
||||
|
||||
@Test
|
||||
fun testTrailingData() {
|
||||
assertThrows<TrailingTextArgument> { (createCommand().getSuggestions(CommandReader("2_literal test"), CommandStack())) }
|
||||
assertThrows<TrailingTextError> { (createCommand().getSuggestions(CommandReader("2_literal test"), CommandStack())) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -110,7 +110,7 @@ internal class SuggestionChildReadingTest {
|
||||
|
||||
@Test
|
||||
fun test2TrailingData() {
|
||||
assertThrows<TrailingTextArgument> { (createCommand().getSuggestions(CommandReader("2_literal 1_literal_2 test"), CommandStack())) }
|
||||
assertThrows<TrailingTextError> { (createCommand().getSuggestions(CommandReader("2_literal 1_literal_2 test"), CommandStack())) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -135,7 +135,7 @@ internal class SuggestionChildReadingTest {
|
||||
|
||||
@Test
|
||||
fun testTrailingTextEmptyNode() {
|
||||
assertThrows<TrailingTextArgument> { RootNode().getSuggestions(CommandReader("trailing"), CommandStack()) }
|
||||
assertThrows<TrailingTextError> { RootNode().getSuggestions(CommandReader("trailing"), CommandStack()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -48,4 +48,39 @@ internal class KUtilTest {
|
||||
fun testInvalidSizeArrayModify() {
|
||||
assertThrows<IllegalArgumentException> { KUtil.modifyArrayIndex(0, 0) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonOverlappingString() {
|
||||
assertEquals(KUtil.getOverlappingText("test", "invalid"), 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyOverlappingText() {
|
||||
assertEquals(KUtil.getOverlappingText("", ""), 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFullyOverlapping() {
|
||||
assertEquals(KUtil.getOverlappingText("next", "next"), 4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSingleChatOverlapping() {
|
||||
assertEquals(KUtil.getOverlappingText("next", "test"), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSingleChatOverlapping2() {
|
||||
assertEquals(KUtil.getOverlappingText("n", "nix"), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSingleChatOverlapping3() {
|
||||
assertEquals(KUtil.getOverlappingText("nix", "x"), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSingleChatOverlapping4() {
|
||||
assertEquals(KUtil.getOverlappingText("nix", "ix"), 2)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user