Jline added. UX fixed
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ shadowJar {
|
||||
dependencies {
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
||||
implementation "io.ktor:ktor-network:3.4.2"
|
||||
implementation "org.jline:jline:3.30.6"
|
||||
implementation project(':common')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,63 @@
|
||||
package input
|
||||
|
||||
import org.jline.reader.EndOfFileException
|
||||
import org.jline.reader.LineReader
|
||||
import org.jline.reader.LineReaderBuilder
|
||||
import org.jline.reader.UserInterruptException
|
||||
import org.jline.reader.impl.history.DefaultHistory
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
import org.jline.reader.impl.completer.StringsCompleter
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.ArrayDeque
|
||||
import java.util.Scanner
|
||||
import runner.CommandValidator
|
||||
|
||||
/**
|
||||
* Реализация [input.IOManager] для чтения потока ввода/файла.
|
||||
* Поддерживает корректную обработку Ctrl+C и Ctrl+D.
|
||||
*/
|
||||
class IOManager() {
|
||||
val isInteractive = true
|
||||
val inputQueue = ArrayDeque<Scanner>()
|
||||
val fileHistory = ArrayDeque<String>()
|
||||
class IOManager {
|
||||
val isInteractive: Boolean = true
|
||||
val commandValidator: CommandValidator = CommandValidator()
|
||||
|
||||
init {
|
||||
inputQueue.push(Scanner(System.`in`))
|
||||
private val terminal = TerminalBuilder.builder()
|
||||
.system(true)
|
||||
.build()
|
||||
|
||||
private val history = DefaultHistory()
|
||||
|
||||
private val lineReader: LineReader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
.history(history)
|
||||
.completer(StringsCompleter(commandValidator.getKnownCommands()))
|
||||
.option(LineReader.Option.HISTORY_IGNORE_DUPS, true)
|
||||
.option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)
|
||||
.build()
|
||||
|
||||
private val scriptQueue = ArrayDeque<Scanner>()
|
||||
private val fileHistory = ArrayDeque<String>()
|
||||
|
||||
/**
|
||||
* Читает строку.
|
||||
* Если есть активный скрипт — читает из него.
|
||||
* Иначе — интерактивный ввод через JLine.
|
||||
*/
|
||||
fun readLine(prompt: String): String? {
|
||||
// Режим скрипта
|
||||
while (scriptQueue.isNotEmpty()) {
|
||||
val scanner = scriptQueue.peek()
|
||||
if (scanner.hasNextLine()) {
|
||||
return scanner.nextLine()
|
||||
}
|
||||
scanner.close()
|
||||
scriptQueue.pop()
|
||||
fileHistory.pop()
|
||||
}
|
||||
|
||||
return try {
|
||||
lineReader.readLine(prompt)
|
||||
} catch (e: UserInterruptException) {
|
||||
""
|
||||
} catch (e: EndOfFileException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun addScriptScanner(filePath: String) {
|
||||
@@ -23,53 +65,15 @@ class IOManager() {
|
||||
printErrConsole("Скрипт уже был выполнен: $filePath")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
fileHistory.push(filePath)
|
||||
inputQueue.push(Scanner(File(filePath), Charsets.UTF_8))
|
||||
scriptQueue.push(Scanner(File(filePath), Charsets.UTF_8))
|
||||
} catch (e: FileNotFoundException) {
|
||||
printErrConsole("Файл не найден: $filePath")
|
||||
}
|
||||
}
|
||||
|
||||
// сделать основной Scanner
|
||||
// при инициализации стандартный сканер добавляется
|
||||
// стек input, один (console) открыт постоянно
|
||||
// если файл - в стек кидается Scanner
|
||||
fun print(text: String) = println(text)
|
||||
|
||||
/**
|
||||
* Читает строку с промптом ☭.
|
||||
*/
|
||||
fun readLine(prompt: String): String? {
|
||||
while (inputQueue.isNotEmpty()) {
|
||||
if (inputQueue.size == 1) kotlin.io.print(prompt)
|
||||
val topInDeque = inputQueue.peek()
|
||||
if (topInDeque.hasNextLine()) {
|
||||
|
||||
return topInDeque.nextLine().toString()
|
||||
}
|
||||
if (inputQueue.size > 1) {
|
||||
topInDeque.close()
|
||||
inputQueue.pop()
|
||||
fileHistory.pop()
|
||||
|
||||
// snap back to reality
|
||||
continue
|
||||
}
|
||||
inputQueue.pop()
|
||||
print("Ввод закончен.")
|
||||
break
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun print(text: String) {
|
||||
println(text)
|
||||
}
|
||||
|
||||
fun printErrConsole(text: String) {
|
||||
System.err.println(text)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
fun printErrConsole(text: String) = System.err.println(text)
|
||||
}
|
||||
@@ -18,6 +18,8 @@ class CommandValidator {
|
||||
"exit"
|
||||
)
|
||||
|
||||
fun getKnownCommands(): Set<String> {return knownCommands}
|
||||
|
||||
fun validate(command: String, args: List<String>?): Pair<Boolean, String?> {
|
||||
if (command !in knownCommands){
|
||||
return Pair(false, "Неизвестная команда: '$command'. Введите 'help' для справки.")
|
||||
|
||||
Reference in New Issue
Block a user