Skip to content

Commit

Permalink
Refactor Inquirer and add testing module for it #3483
Browse files Browse the repository at this point in the history
  • Loading branch information
Nereboss committed Mar 14, 2024
1 parent c16edf0 commit 8cdf80d
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import de.maibornwolff.codecharta.tools.inquirer.myPromptConfirm
import de.maibornwolff.codecharta.tools.inquirer.myPromptInput
import de.maibornwolff.codecharta.tools.inquirer.myPromptInputNumber
import de.maibornwolff.codecharta.tools.inquirer.myPromptList
import de.maibornwolff.codecharta.tools.inquirer.util.InputValidator
import de.maibornwolff.codecharta.tools.interactiveparser.ParserDialogInterface
import de.maibornwolff.codecharta.util.InputHelper
import java.io.File
Expand All @@ -26,8 +27,17 @@ class ParserDialog {
var test = listOf<String>()
session {
println("inside tester")
myPromptInput("Input a test input:", Paths.get("").toAbsolutePath().toString() + File.separator + "yourInput.cc.json", allowEmptyInput = true)
myPromptInputNumber("input a test number:", "42", allowEmptyInput = true)
myPromptInput(
"Input a test input:",
Paths.get("").toAbsolutePath().toString() + File.separator + "yourInput.cc.json",
allowEmptyInput = true,
inputValidator = InputValidator.isInputAnExistingFile()
)
myPromptInputNumber(
"input a test number:",
"42",
inputValidator = InputValidator.isInputBetweenNumbers(0, 5)
)
myPromptConfirm("Confirm the test?")
myPromptList("select any", listOf("a", "b", "c", "1", "2", "3"))
test = myPromptCheckbox("Select all parsers you want to execute:", listOf("a", "b", "c", "1", "2", "3"))
Expand All @@ -41,8 +51,7 @@ class ParserDialog {
do {
inputFileName = myPromptInput(
message = "What is the cc.json file that has to be modified?",
hint = Paths.get("").toAbsolutePath().toString() + File.separator + "yourInput.cc.json",
canContainFolders = false
hint = Paths.get("").toAbsolutePath().toString() + File.separator + "yourInput.cc.json"
)
} while (!InputHelper.isInputValidAndNotNull(arrayOf(File(inputFileName)), canInputContainFolders = false))

Expand Down
5 changes: 5 additions & 0 deletions analysis/tools/Inquirer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@ repositories {

dependencies {
implementation group: 'com.varabyte.kotter', name: 'kotter-jvm', version: kotter_version
implementation group: 'com.varabyte.kotterx', name: 'kotter-test-support-jvm', version: kotter_version
implementation project(path: ':model')
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.maibornwolff.codecharta.tools.inquirer

import com.varabyte.kotter.foundation.collections.LiveList
import com.varabyte.kotter.foundation.collections.liveListOf
import com.varabyte.kotter.foundation.input.Completions
import com.varabyte.kotter.foundation.input.Keys
Expand All @@ -16,118 +17,123 @@ import com.varabyte.kotter.foundation.text.green
import com.varabyte.kotter.foundation.text.red
import com.varabyte.kotter.foundation.text.text
import com.varabyte.kotter.foundation.text.textLine
import com.varabyte.kotter.runtime.MainRenderScope
import com.varabyte.kotter.runtime.Session
import de.maibornwolff.codecharta.util.InputHelper
import java.io.File

fun Session.myPromptInput(
message: String,
hint: String = "",
allowEmptyInput: Boolean = false,
canContainFolders: Boolean = false,
invalidInputMessage: String = "Input is invalid!",
inputValidator: (String, Boolean) -> Boolean = {_, _ -> true} //TODO: make this of a type, maybe InputHelper
inputValidator: (String) -> Boolean = { true }
): String {
var returnValue = ""
var lastUserInput = ""
var hintText = hint
var isInputValid by liveVarOf(true)
section {
bold {
green { text("? ") }; text(message)
if (isInputValid) {
black(isBright = true) { textLine(if (allowEmptyInput) " (empty input is allowed)" else "") }
} else {
red { textLine(if (returnValue.isEmpty()) " Empty input is not allowed!" else " $invalidInputMessage") }
}
}
text("> "); input(Completions(hintText), initialText = "")
drawInput(message, hintText, isInputValid, allowEmptyInput, invalidInputMessage, lastUserInput)
}.runUntilSignal {
onInputChanged { isInputValid = true }
onInputEntered {
isInputValid = InputHelper.isInputValidAndNotNull(arrayOf(File(input)), canContainFolders)
returnValue = input
isInputValid = inputValidator(input)
lastUserInput = input
if ((allowEmptyInput && input.isEmpty()) || (isInputValid && input.isNotEmpty())) {
isInputValid = true
hintText = ""
signal()
}
}
}
return returnValue
return lastUserInput
}

//TODO: should the PromptInputNumber be refactored together with PromptInput somehow?
private fun MainRenderScope.drawInput(
message: String,
hint: String,
isInputValid: Boolean,
allowEmptyInput: Boolean,
invalidInputMessage: String,
lastUserInput: String
) {
bold {
green { text("? ") }; text(message)
if (isInputValid) {
black(isBright = true) { textLine(if (allowEmptyInput) " empty input is allowed" else "") }
} else {
red { textLine(if (lastUserInput.isEmpty()) " Empty input is not allowed!" else " $invalidInputMessage") }
}
}
text("> "); input(Completions(hint), initialText = "")
}

fun Session.myPromptInputNumber(
message: String,
hint: String = "",
allowEmptyInput: Boolean = false,
invalidInputMessage: String = "Input is invalid!"
invalidInputMessage: String = "Input is invalid!",
inputValidator: (String) -> Boolean = { true }
): String {
var returnValue = ""
var lastUserInput = ""
var hintText = hint
var isInputValid by liveVarOf(true)
section {
bold {
green { text("? ") }; text(message)
if (isInputValid) {
black(isBright = true) { textLine(if (allowEmptyInput) " (empty input is allowed)" else "") }
} else {
red { textLine(if (returnValue.isEmpty()) " Empty input is not allowed!" else " $invalidInputMessage") }
}
}
text("> "); input(Completions(hintText), initialText = "")
drawInput(message, hintText, isInputValid, allowEmptyInput, invalidInputMessage, lastUserInput)
}.runUntilSignal {
onInputChanged {isInputValid = true; input = input.filter { it.isDigit() } }
onInputEntered {
// isInputValid = validityCheck... //damit kann der else fall dann weg
if (allowEmptyInput || input.isNotEmpty()) {
returnValue = input
isInputValid = inputValidator(input)
lastUserInput = input
if ((allowEmptyInput && input.isEmpty()) || (isInputValid && input.isNotEmpty())) {
isInputValid = true
hintText = ""
signal()
} else {
isInputValid = false
}
}
}
return returnValue
return lastUserInput
}

fun Session.myPromptConfirm(message: String): Boolean {
fun Session.myPromptConfirm(
message: String,
hint: String = "arrow keys to change selection"
): Boolean {
var result = true
var res by liveVarOf(true)
var choice by liveVarOf(true)
section {
green { text("? ") }; text(message)
black(isBright = true) { textLine(" (use arrow keys to change selection)") } //TODO: is note this necessary?
if (res) {
text("> "); cyan { text("[Yes]") }; textLine(" No ")
} else {
text("> Yes "); cyan { textLine("[No]") }
}
drawConfirm(message, hint, choice)
}.runUntilSignal {
onKeyPressed {
println(key)
when (key) {
Keys.LEFT -> res = true
Keys.RIGHT -> res = false
Keys.ENTER -> { result = res; signal() }
Keys.LEFT -> choice = true
Keys.RIGHT -> choice = false
Keys.ENTER -> { result = choice; signal() }
}
}
}
return result
}

fun Session.myPromptList(message: String, choices: List<String>, hint: String = ""): String {
private fun MainRenderScope.drawConfirm(message: String, hint: String, choice: Boolean) {
bold {
green { text("? ") }; text(message)
black(isBright = true) { textLine(" $hint") }
}
if (choice) {
text("> "); cyan { text("[Yes]") }; textLine(" No ")
} else {
text("> Yes "); cyan { textLine("[No]") }
}
}

fun Session.myPromptList(
message: String,
choices: List<String>,
hint: String = "arrow keys to move, ENTER to select"): String {
var result = ""
var selection by liveVarOf(0)
section {
green { text("? ") }; text(message); black(isBright = true) { textLine(" $hint") }
for (i in choices.indices) {
if (i == selection) {
cyan(isBright = true) { text("") }; cyan { textLine(choices[i]) }
} else {
textLine(" ${choices[i]}")
}
}
drawList(message, hint, choices, selection)
}.runUntilSignal {
onKeyPressed {
when (key) {
Expand All @@ -140,6 +146,22 @@ fun Session.myPromptList(message: String, choices: List<String>, hint: String =
return result
}

private fun MainRenderScope.drawList(
message: String,
hint: String,
choices: List<String>,
selection: Int
) {
bold { green { text("? ") }; text(message); black(isBright = true) { textLine(" $hint") } }
for (i in choices.indices) {
if (i == selection) {
cyan(isBright = true) { text("") }; cyan { textLine(choices[i]) }
} else {
textLine(" ${choices[i]}")
}
}
}

fun Session.myPromptCheckbox(
message: String,
choices: List<String>,
Expand All @@ -151,20 +173,7 @@ fun Session.myPromptCheckbox(
val selectedElems = liveListOf(MutableList(choices.size) { false })
var isInputValid by liveVarOf(true)
section {
green { text("? ") }; text(message)
if (isInputValid) {
black(isBright = true) { textLine(if (allowEmptyInput) " $hint (empty selection is allowed)" else " $hint") }
} else {
red { textLine(" Empty selection is not allowed!") }
}
for (i in choices.indices) {
cyan(isBright = true) { text(if (i == pos) "" else " ") }
if (selectedElems[i]) {
green { text("") }; cyan { textLine(choices[i]) }
} else {
textLine("${choices[i]}")
}
}
drawCheckbox(message, hint, isInputValid, allowEmptyInput, choices, pos, selectedElems)
}.runUntilSignal {
onKeyPressed {
isInputValid = true
Expand All @@ -186,6 +195,33 @@ fun Session.myPromptCheckbox(
return result
}

private fun MainRenderScope.drawCheckbox(
message: String,
hint: String,
isInputValid: Boolean,
allowEmptyInput: Boolean,
choices: List<String>,
pos: Int,
selectedElems: LiveList<Boolean>
) {
bold {
green { text("? ") }; text(message)
if (isInputValid) {
black(isBright = true) { textLine(if (allowEmptyInput) " $hint empty selection is allowed" else " $hint") } //TODO should this als display hint when empty selection is allowed?
} else {
red { textLine(" Empty selection is not allowed!") }
}
}
for (i in choices.indices) {
cyan(isBright = true) { text(if (i == pos) "" else " ") }
if (selectedElems[i]) {
green { text("") }; cyan { textLine(choices[i]) }
} else {
textLine("${choices[i]}")
}
}
}

private fun getSelectedElems(choices: List<String>, selection: List<Boolean>): List<String> {
val result = mutableListOf<String>()
for (i in choices.indices) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.maibornwolff.codecharta.tools.inquirer.util

class InputValidator {

companion object {

//TODO what kind of methods do we need here?
// -> create all that are currently needed for the project when integrating kotter into each parser
//
// needed checkers:
// check if the input is an existing file
// check if the input is an existing file or folder (can probably be combined with above function)
// sometimes input is list of files (or folders), check if they are all existing files
// (add more when going through the parsers)

fun isInputAnExistingFile(): (String) -> Boolean = { input ->
true
}

fun isInputAnExistingFileOrFolder(): (String) -> Boolean = { input ->
true
}

// not sure if this one is needed, its currently there for testing
fun isInputBetweenNumbers(minValue: Int, maxValue: Int): (String) -> Boolean = { input ->
val inputNumber = input.toInt()
minValue < inputNumber && inputNumber < maxValue
}
}

}
Loading

0 comments on commit 8cdf80d

Please sign in to comment.