This is an automated email from the ASF dual-hosted git repository.
aradzinski pushed a commit to branch NLPCRAFT-108
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-108 by this push:
new a40ece7 WIP.
a40ece7 is described below
commit a40ece7c52b3b95ecfce58deb25565d176eca94c
Author: Aaron Radzinski <[email protected]>
AuthorDate: Mon Oct 5 21:41:44 2020 -0700
WIP.
---
.../nlpcraft/common/ansi/NCAnsiProgressBar.scala | 43 ++---
.../nlpcraft/common/ansi/NCAnsiSpinner.scala | 14 +-
.../scala/org/apache/nlpcraft/common/package.scala | 51 +++---
.../org/apache/nlpcraft/common/util/NCUtils.scala | 9 +
.../nlpcraft/model/tools/cmdline/NCCli.scala | 190 +++++++++++++++------
5 files changed, 214 insertions(+), 93 deletions(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
index a1e0e05..1b08615 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
@@ -19,6 +19,7 @@ package org.apache.nlpcraft.common.ansi
import java.io.PrintWriter
+import org.apache.nlpcraft.common._
import NCAnsi._
import org.apache.nlpcraft.common.ansi.NCAnsiProgressBar._
@@ -42,10 +43,10 @@ class NCAnsiProgressBar(
@volatile private var tick = 0
//noinspection ZeroIndexToHead
- private final val PB_LEFT = s"$ansiBlueFg${CHAR_SET(0)}$ansiReset"
- private final val PB_RIGHT = s"$ansiBlueFg${CHAR_SET(3)}$ansiReset"
- private final val PB_EMPTY =s"$ansiWhiteFg${CHAR_SET(2)}$ansiReset"
- private final val PB_FULL = s"$ansiRedFg$ansiBold${CHAR_SET(1)}$ansiReset"
+ private final val PB_LEFT = s"$B${CHAR_SET(0)}$RST"
+ private final val PB_RIGHT = s"$B${CHAR_SET(3)}$RST"
+ private final val PB_EMPTY =s"$W${CHAR_SET(2)}$RST"
+ private final val PB_FULL = s"$R$BO${CHAR_SET(1)}$RST"
/**
*
@@ -62,7 +63,7 @@ class NCAnsiProgressBar(
def start(): Unit = {
tick = 0
- if (useAnsi) {
+ if (useAnsi) out.synchronized {
// Hide cursor to avoid blinking.
out.print(ansiCursorHide)
@@ -80,20 +81,22 @@ class NCAnsiProgressBar(
def ticked(): Unit = {
tick += 1
- if (useAnsi) {
- clean()
-
- val bar = if (tick == 1) 1 else Math.round((tick.toFloat /
totalTicks.toFloat) * dispSize)
-
- out.print(PB_LEFT)
- for (i ← 0 until dispSize)
- out.print(if (i < bar) PB_FULL else PB_EMPTY)
- out.print(PB_RIGHT)
- out.flush()
- }
- else if (tick == 1 || tick % (totalTicks / dispSize) == 0) {
- out.print(NON_ANSI_CHAR)
- out.flush()
+ out.synchronized {
+ if (useAnsi) {
+ clean()
+
+ val bar = if (tick == 1) 1 else Math.round((tick.toFloat /
totalTicks.toFloat) * dispSize)
+
+ out.print(PB_LEFT)
+ for (i ← 0 until dispSize)
+ out.print(if (i < bar) PB_FULL else PB_EMPTY)
+ out.print(PB_RIGHT)
+ out.flush()
+ }
+ else if (tick == 1 || tick % (totalTicks / dispSize) == 0) {
+ out.print(NON_ANSI_CHAR)
+ out.flush()
+ }
}
}
@@ -109,7 +112,7 @@ class NCAnsiProgressBar(
* Stops progress bar.
*/
def stop(): Unit = {
- if (useAnsi && clearOnComplete) {
+ if (useAnsi && clearOnComplete) out.synchronized {
clean()
// Show cursor.
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
index fe8733c..7af9b8f 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
@@ -73,11 +73,13 @@ class NCAnsiSpinner(out: PrintWriter, useAnsi: Boolean =
true) {
out.flush()
while (!t.isInterrupted) {
- if (frame > 0)
- clean()
+ out.synchronized {
+ if (frame > 0)
+ clean()
- out.print(s"$prefix$ansiCyanFg${CHAR_SET(frame %
CHAR_SET.size)}$ansiReset$suffix")
- out.flush()
+ out.print(s"$prefix$ansiCyanFg${CHAR_SET(frame %
CHAR_SET.size)}$ansiReset$suffix")
+ out.flush()
+ }
lastLength = U.stripAnsi(prefix).length + 1 +
U.stripAnsi(suffix).length
@@ -89,7 +91,7 @@ class NCAnsiSpinner(out: PrintWriter, useAnsi: Boolean =
true) {
thread.start()
}
- else {
+ else out.synchronized {
out.print("... ")
out.flush()
}
@@ -100,7 +102,7 @@ class NCAnsiSpinner(out: PrintWriter, useAnsi: Boolean =
true) {
def stop(): Unit = {
U.stopThread(thread)
- if (useAnsi && frame > 0) {
+ if (useAnsi && frame > 0) out.synchronized {
clean()
// Show cursor.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
index d266c4b..e82f220 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
@@ -41,27 +41,40 @@ package object common {
final val MDL_META_ALL_ALIASES_KEY = "__NLPCRAFT_MDL_META_ALL_ALIASES"
final val TOK_META_ALIASES_KEY = "__NLPCRAFT_TOK_META_ALIASES"
- // Real foreground color shortcuts...
- def g(s: Any): String = s"$ansiGreenFg${s.toString}$ansiReset"
- def r(s: Any): String = s"$ansiRedFg${s.toString}$ansiReset"
- def c(s: Any): String = s"$ansiCyanFg${s.toString}$ansiReset"
- def y(s: Any): String = s"$ansiYellowFg${s.toString}$ansiReset"
- def w(s: Any): String = s"$ansiWhiteFg${s.toString}$ansiReset"
- def b(s: Any): String = s"$ansiBlueFg${s.toString}$ansiReset"
- def k(s: Any): String = s"$ansiBlackFg${s.toString}$ansiReset"
-
- // Real background color shortcuts...
- def gb(s: Any): String = s"$ansiGreenBg${s.toString}$ansiReset"
- def rb(s: Any): String = s"$ansiRedBg${s.toString}$ansiReset"
- def cb(s: Any): String = s"$ansiCyanBg${s.toString}$ansiReset"
- def yb(s: Any): String = s"$ansiYellowBg${s.toString}$ansiReset"
- def wb(s: Any): String = s"$ansiWhiteBg${s.toString}$ansiReset"
- def bb(s: Any): String = s"$ansiBlueBg${s.toString}$ansiReset"
- def kb(s: Any): String = s"$ansiBlackBg${s.toString}$ansiReset"
+ def G: String = ansiGreenFg
+ def R: String = ansiRedFg
+ def C: String = ansiCyanFg
+ def Y: String = ansiYellowFg
+ def W: String = ansiWhiteFg
+ def B: String = ansiBlueFg
+ def BO: String = ansiBold
+ def K: String = ansiBlackFg
+ def GB: String = ansiGreenBg
+ def RB: String = ansiRedBg
+ def CB: String = ansiCyanBg
+ def YB: String = ansiYellowBg
+ def WB: String = ansiWhiteBg
+ def BB: String = ansiBlueBg
+ def KB: String = ansiBlackBg
+ def RST: String = ansiReset
+ def g(s: Any): String = s"$G${s.toString}$RST"
+ def r(s: Any): String = s"$R${s.toString}$RST"
+ def c(s: Any): String = s"$C${s.toString}$RST"
+ def y(s: Any): String = s"$Y${s.toString}$RST"
+ def w(s: Any): String = s"$W${s.toString}$RST"
+ def b(s: Any): String = s"$B${s.toString}$RST"
+ def k(s: Any): String = s"$K${s.toString}$RST"
+ def gb(s: Any): String = s"$GB${s.toString}$RST"
+ def rb(s: Any): String = s"$RB${s.toString}$RST"
+ def cb(s: Any): String = s"$CB${s.toString}$RST"
+ def yb(s: Any): String = s"$YB${s.toString}$RST"
+ def wb(s: Any): String = s"$WB${s.toString}$RST"
+ def bb(s: Any): String = s"$BB${s.toString}$RST"
+ def kb(s: Any): String = s"$KB${s.toString}$RST"
// Real color effect shortcuts...
- def rv(s: Any): String = s"$ansiReversed${s.toString}$ansiReset"
- def bo(s: Any): String = s"$ansiBold${s.toString}$ansiReset"
+ def rv(s: Any): String = s"$ansiReversed${s.toString}$RST"
+ def bo(s: Any): String = s"$ansiBold${s.toString}$RST"
/**
* Pimps integers with KB, MB, GB units of measure.
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index df80d9a..731db6c 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -946,6 +946,15 @@ object NCUtils extends LazyLogging {
}
/**
+ * Makes thread.
+ *
+ * @param name Name.
+ * @param body Thread body.
+ */
+ def mkThread(name: String, body: Runnable): Thread =
+ mkThread(name) { _ ⇒ body.run() }
+
+ /**
* System-wide process of normalizing emails (trim & lower case).
*
* @param email Email to normalize.
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
index f46a7bb..3aea6d1 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
@@ -38,16 +38,19 @@ import java.util
import java.util.Date
import java.io._
import java.nio.charset.StandardCharsets
+import java.nio.file.Paths
import java.util.regex.Pattern
import org.apache.commons.io.input.{ReversedLinesFileReader, Tailer,
TailerListenerAdapter}
import org.apache.http.util.EntityUtils
+import org.jline.builtins.Commands
import org.jline.reader.Completer
import org.jline.reader.impl.DefaultParser
import org.jline.terminal.{Terminal, TerminalBuilder}
import org.jline.reader.{Candidate, EndOfFileException, LineReader,
LineReaderBuilder, ParsedLine, UserInterruptException}
import org.jline.reader.impl.DefaultParser.Bracket
import org.jline.reader.impl.history.DefaultHistory
+import org.jline.utils.InfoCmp.Capability
import org.jline.widget.AutosuggestionWidgets
import resource.managed
@@ -69,32 +72,33 @@ object NCCli extends App {
// TODO: this needs to be loaded dynamically from OpenAPI spec.
private final val REST_PATHS = Seq(
- "signin",
- "signout",
- "cancel",
- "check",
- "clear",
- "clear",
- "company/add",
- "company/get",
- "company/update",
- "company/token/reset",
- "company/delete",
- "user/get",
- "user/add",
- "user/update",
- "user/delete",
- "user/admin",
- "user/passwd/reset",
- "user/all",
- "feedback/add",
- "feedback/all",
- "feedback/delete",
- "probe/all",
- "model/sugsyn",
- "ask",
- "ask/sync",
+ "clear/conversation" -> "Clears conversation STM",
+ "clear/dialog" -> "Clears dialog flow",
+ "model/sugsyn" -> "Runs model synonym suggestion tool",
+ "check" -> "Gets status and result of submitted requests",
+ "cancel" -> "Cancels a question",
+ "ask" -> "Asks a question",
+ "ask/sync" -> "Asks a question in synchronous mode",
+ "user/get" -> "Gets current user information",
+ "user/all" -> "Gets all users",
+ "user/update" -> "Updates regular user",
+ "user/delete" -> "Deletes user",
+ "user/admin" -> "Updates user admin permissions",
+ "user/passwd/reset" -> "Resets password for the user",
+ "user/add" -> "Adds new user",
+ "company/get" -> "Gets current user company information",
+ "company/add" -> "Adds new company",
+ "company/update" -> "Updates company data",
+ "company/delete" -> "Deletes company",
+ "company/token/reset" -> "Resets company probe auth token",
+ "feedback/add" -> "Adds feedback",
+ "feedback/delete" -> "Deletes feedback",
+ "feedback/all" -> "Gets all feedback",
+ "signin" -> "Signs in and obtains new access token",
+ "signout" -> "Signs out and releases access token",
+ "probe/all" -> "Gets all probes"
)
+ .sortBy(_._1)
// Number of server services that need to be started + 1 for log marker.
// Used for progress bar functionality.
@@ -129,8 +133,8 @@ object NCCli extends App {
case class SplitError(index: Int) extends Exception
case class NoLocalServer() extends IllegalStateException(s"Cannot detect
locally running REST server.")
case class MissingParameter(cmd: Command, paramId: String) extends
IllegalArgumentException(
- s"Missing mandatory parameter: $ansiCyanFg${"'" + cmd.params.find(_.id
== paramId).get.names.head + "'"}$ansiReset. " +
- s"Type $ansiCyanFg${"'help --cmd="}${c(cmd.name) + "'"}$ansiReset to
get help."
+ s"Missing mandatory parameter: $C${"'" + cmd.params.find(_.id ==
paramId).get.names.head + "'"}$RST, " +
+ s"type $C${"'help --cmd="}${c(cmd.name) + "'"}$RST to get help."
)
case class HttpError(httpCode: Int) extends IllegalStateException(s"REST
error (HTTP ${c(httpCode)}).")
@@ -211,8 +215,9 @@ object NCCli extends App {
value = Some("path"),
desc =
s"REST path, e.g. ${y("'signin'")} or
${y("'ask/sync'")}. " +
- s"Note that you don't need supply '/' at the
beginning." +
- s"See more details at
https://nlpcraft.apache.org/using-rest.html"
+ s"Note that you don't need supply '/' at the
beginning. " +
+ s"See more details at
https://nlpcraft.apache.org/using-rest.html " +
+ s"In REPL mode, hit ${rv(" Tab ")} to see
auto-suggestion for possible REST paths."
),
Parameter(
id = "json",
@@ -367,6 +372,32 @@ object NCCli extends App {
body = cmdInfoServer
),
Command(
+ name = "cls",
+ synopsis = s"Clears terminal screen.",
+ body = cmdCls
+ ),
+ Command(
+ name = "nano",
+ synopsis = s"Runs built-in ${y("'nano'")} editor.",
+ body = cmdNano,
+ params = Seq(
+ Parameter(
+ id = "file",
+ names = Seq("--file", "-f"),
+ value = Some("path"),
+ optional = true,
+ desc =
+ s"File to open with built-in ${y("'nano'")} editor.
Relative paths will based off user home directory."
+ )
+ ),
+ examples = Seq(
+ Example(
+ usage = Seq(s"$PROMPT $SCRIPT_NAME nano -f=my_model.yml"),
+ desc = s"Opens ${y("'my_model.yml'")} file in built-in
nano editor."
+ )
+ )
+ ),
+ Command(
name = "no-ansi",
synopsis = s"Disables usage of ANSI escape codes for colors &
terminal controls.",
desc = Some(
@@ -502,6 +533,7 @@ object NCCli extends App {
private final val ANSI_CMD = CMDS.find(_.name == "ansi").get
private final val QUIT_CMD = CMDS.find(_.name == "quit").get
private final val HELP_CMD = CMDS.find(_.name == "help").get
+ private final val REST_CMD = CMDS.find(_.name == "rest").get
private final val STOP_SRV_CMD = CMDS.find(_.name == "stop-server").get
private final val START_SRV_CMD = CMDS.find(_.name == "start-server").get
@@ -697,7 +729,7 @@ object NCCli extends App {
500.ms
)
- U.mkThread("server-start-progress-bar") { _ ⇒ tailer }.start()
+ U.mkThread("server-start-progress-bar", tailer).start()
var beacon: NCCliServerBeacon = null
var online = false
@@ -775,9 +807,9 @@ object NCCli extends App {
for (_ ← 0 to lines)
tail ::= in.readLine()
- logln(bb(w(s"+----< ${ansiBlackFg}Last $lines server
log lines $ansiWhiteFg>---")))
+ logln(bb(w(s"+----< ${K}Last $lines server log lines
$W>---")))
tail.foreach(line ⇒ logln(s"${bb(w("| "))} $line"))
- logln(bb(w(s"+----< ${ansiBlackFg}Last $lines server
log lines $ansiWhiteFg>---")))
+ logln(bb(w(s"+----< ${K}Last $lines server log lines
$W>---")))
}
catch {
case e: Exception ⇒ error(s"Failed to read log file:
${e.getLocalizedMessage}")
@@ -1152,14 +1184,38 @@ object NCCli extends App {
* @param args Arguments, if any, for this command.
* @param repl Whether or not executing from REPL.
*/
+ private def cmdCls(cmd: Command, args: Seq[Argument], repl: Boolean): Unit
=
+ term.puts(Capability.clear_screen)
+
+ /**
+ *
+ * @param cmd Command descriptor.
+ * @param args Arguments, if any, for this command.
+ * @param repl Whether or not executing from REPL.
+ */
+ private def cmdNano(cmd: Command, args: Seq[Argument], repl: Boolean):
Unit =
+ Commands.nano(term,
+ System.out,
+ System.err,
+ Paths.get(""),
+ Array(args.map(_.value.get): _*)
+ )
+
+ /**
+ *
+ * @param cmd Command descriptor.
+ * @param args Arguments, if any, for this command.
+ * @param repl Whether or not executing from REPL.
+ */
private def cmdRest(cmd: Command, args: Seq[Argument], repl: Boolean):
Unit = {
val path = args.find(_.parameter.id == "path").getOrElse(throw
MissingParameter(cmd, "path")).value.get
val rawJson = args.find(_.parameter.id == "json").getOrElse(throw
MissingParameter(cmd, "json")).value.get
- if (!REST_PATHS.contains(path))
- throw new IllegalArgumentException(s"Unknown REST path: $path")
+ // TODO: verify JSON
+
+ if (!REST_PATHS.exists(_._1 == path))
+ throw new IllegalArgumentException(s"Unknown REST path
$C'$path'$RST, type ${c("'help --cmd=rest'")} to get help.")
- val json = stripQuotes(rawJson)
val endpoint = getRestEndpointFromBeacon
val handler = new ResponseHandler[HttpRestResponse] {
@@ -1175,12 +1231,24 @@ object NCCli extends App {
}
}
- val resp = httpPost(endpoint, path, handler, json)
+ val resp = httpPost(endpoint, path, handler, stripQuotes(rawJson))
- val code = if (resp.code == 200) g("200") else r(resp.code)
+ // Ack HTTP response code.
+ logln(s"HTTP ${if (resp.code == 200) g("200") else r(resp.code)}")
- logln(s"HTTP $code")
- logln(resp.data)
+ if (resp.code == 200) {
+ if (path == "signin") {
+ // TODO
+ }
+ else if (path == "signout") {
+ // TODO
+ }
+ }
+
+ if (resp.code == 200 || resp.code == 400)
+ logln(resp.data) // TODO
+ else
+ logln(resp.data)
}
/**
@@ -1196,8 +1264,6 @@ object NCCli extends App {
parser.setEofOnUnclosedBracket(Bracket.CURLY, Bracket.ROUND,
Bracket.SQUARE)
parser.setEofOnUnclosedQuote(true)
- parser.setRegexCommand("")
- parser.setRegexVariable("")
val completer = new Completer {
private val cmds = CMDS.map(c ⇒ c.name → c.synopsis)
@@ -1210,8 +1276,8 @@ object NCCli extends App {
* @param completed
* @return
*/
- private def mkCandidate(id: String, disp: String, desc: String,
completed: Boolean): Candidate =
- new Candidate(id, disp, null, desc, null, null, completed)
+ private def mkCandidate(id: String, disp: String, grp: String,
desc: String, completed: Boolean): Candidate =
+ new Candidate(id, disp, grp, desc, null, null, completed)
override def complete(reader: LineReader, line: ParsedLine,
candidates: util.List[Candidate]): Unit = {
val words = line.words().asScala
@@ -1221,7 +1287,13 @@ object NCCli extends App {
val name = n._1
val desc = n._2.substring(0, n._2.length - 1) //
Remove last '.'.
- mkCandidate(name, name, desc, completed = true)
+ mkCandidate(
+ id = name,
+ disp = name,
+ grp = null,
+ desc = desc,
+ completed = true
+ )
}).asJava)
else {
val cmd = words.head
@@ -1232,7 +1304,13 @@ object NCCli extends App {
val hasVal = param.value.isDefined
val names =
param.names.filter(_.startsWith("--")) // Skip shorthands from auto-completion.
- names.map(name ⇒ mkCandidate(if (hasVal) name
+ "=" else name, name,null, !hasVal))
+ names.map(name ⇒ mkCandidate(
+ id = if (hasVal) name + "=" else name,
+ disp = name,
+ grp = null,
+ desc = null,
+ completed = !hasVal)
+ )
})
.asJava
@@ -1241,7 +1319,21 @@ object NCCli extends App {
// For 'help' - add additional auto-completion candidates.
if (cmd == HELP_CMD.name)
- candidates.addAll(CMDS.map(c ⇒
s"--cmd=${c.name}").map(s ⇒ mkCandidate(s, s, null, completed = true)).asJava)
+ candidates.addAll(CMDS.map(c ⇒
s"--cmd=${c.name}").map(s ⇒
+ mkCandidate(
+ id = s,
+ disp = s,
+ grp = null,
+ desc = null,
+ completed = true
+ ))
+ .asJava
+ )
+
+ // For 'rest' - add additional auto-completion candidates.
+ if (cmd == REST_CMD.name) {
+
+ }
}
}
}
@@ -1292,7 +1384,7 @@ object NCCli extends App {
catch {
case _: UserInterruptException ⇒ "" // Ignore.
case _: EndOfFileException ⇒ null
- case e: Exception ⇒ e.printStackTrace(); "" // Guard against
JLine hiccups.
+ case e: Exception ⇒ "" // Guard against JLine hiccups.
}
if (rawLine == null || QUIT_CMD.name == rawLine.trim)
@@ -1388,7 +1480,7 @@ object NCCli extends App {
*
*/
private def unknownCommand(cmd: String): Unit = {
- val c2 = y(s"'$cmd'")
+ val c2 = c(s"'$cmd'")
val h2 = c(s"'help'")
error(s"Unknown command $c2, type $h2 to get help.")
@@ -1620,6 +1712,8 @@ object NCCli extends App {
term = TerminalBuilder.builder()
.name(NAME)
.system(true)
+ .nativeSignals(true)
+ .signalHandler(Terminal.SignalHandler.SIG_IGN)
.dumb(true)
.jansi(true)
.build()