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 544a3d1  WIP.
544a3d1 is described below

commit 544a3d15b43623d407ab841001649b0f189d9664
Author: Aaron Radzinski <[email protected]>
AuthorDate: Fri Oct 2 20:15:25 2020 -0700

    WIP.
---
 .../nlpcraft/model/tools/cmdline/NCCli.scala       | 121 +++++++++++++++------
 .../model/tools/cmdline/NCCliServerBeacon.scala    |   3 +-
 2 files changed, 89 insertions(+), 35 deletions(-)

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 83bf229..2b4256b 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,6 +38,7 @@ import java.lang.ProcessBuilder.Redirect
 import java.lang.management.ManagementFactory
 import java.text.DateFormat
 import java.util.Date
+import java.util.regex.PatternSyntaxException
 
 import org.apache.nlpcraft.common.util.NCUtils.IntTimeUnits
 import org.jline.builtins.Completers.TreeCompleter
@@ -55,6 +56,7 @@ import scala.compat.java8.OptionConverters._
 import scala.collection.JavaConverters._
 import scala.compat.Platform.currentTime
 import scala.util.Try
+import scala.util.control.Exception.ignoring
 
 /**
  * 'nlpcraft' script entry point.
@@ -63,6 +65,7 @@ object NCCli extends App {
     private final val NAME = "Apache NLPCraft CLI"
 
     private final val SRV_BEACON_PATH = ".nlpcraft/server_beacon"
+    private final val HIST_PATH = ".nlpcraft/cli_history"
 
     private final lazy val VER = NCVersion.getCurrent
     private final lazy val JAVA = U.sysEnv("NLPCRAFT_CLI_JAVA").getOrElse(new 
File(SystemUtils.getJavaHome,s"bin/java${if (SystemUtils.IS_OS_UNIX) "" else 
".exe"}").getAbsolutePath)
@@ -292,7 +295,7 @@ object NCCli extends App {
         ),
         Command(
             id = "help",
-            names = Seq("help", "?"),
+            names = Seq("help"),
             synopsis = s"Displays manual page for '$SCRIPT_NAME'.",
             desc = Some(
                 s"By default, without '-all' or '-cmd' parameters, displays 
the abbreviated form of manual " +
@@ -354,6 +357,12 @@ object NCCli extends App {
             id = "repl",
             names = Seq("repl"),
             synopsis = s"Starts '$SCRIPT_NAME' in interactive REPL mode.",
+            desc = Some(
+                s"REPL mode supports all the same commands as command line 
mode. " +
+                s"REPL is the default mode for when '$SCRIPT_NAME' is started 
without parameters. " +
+                s"In REPL mode you need to put values that can have spaces 
(like JSON or file paths) " +
+                s"inside of single or double quotes both of which can be 
escaped using '\\' character, when necessary."
+            ),
             body = cmdRepl
         )
     ).sortBy(_.id)
@@ -449,7 +458,7 @@ object NCCli extends App {
         checkFilePath(igniteCfgPath)
 
         loadServerBeacon() match {
-            case Some((b, _)) ⇒ throw new IllegalStateException(s"Existing 
local server (pid ${c(b.pid)}) detected.")
+            case Some(b) ⇒ throw new IllegalStateException(s"Existing local 
server (pid ${c(b.pid)}) detected.")
             case None ⇒ ()
         }
 
@@ -494,7 +503,7 @@ object NCCli extends App {
             else {
                 log(s"Server is starting ")
 
-                def getServerBeacon = loadServerBeacon().map(_._1).orNull
+                def getServerBeacon = loadServerBeacon().orNull
 
                 var beacon = getServerBeacon
                 var online = false
@@ -559,7 +568,10 @@ object NCCli extends App {
     private def cmdPingServer(cmd: Command, args: Seq[Argument], repl: 
Boolean): Unit = {
         val endpoint = args.find(_.parameter.id == "endpoint") match {
             case Some(arg) ⇒ new URL(arg.value.get).toURI.toString
-            case None ⇒ "http://localhost:8081";
+            case None ⇒ loadServerBeacon() match {
+                case Some(beacon) ⇒ s"https://${beacon.restEndpoint}";
+                case None ⇒ throw new IllegalStateException(s"Cannot detect 
locally running REST server.")
+            }
         }
         val num = args.find(_.parameter.id == "number") match {
             case Some(arg) ⇒
@@ -620,7 +632,7 @@ object NCCli extends App {
      *
      * @return
      */
-    private def loadServerBeacon(): Option[(NCCliServerBeacon, ProcessHandle)] 
=
+    private def loadServerBeacon(): Option[NCCliServerBeacon] =
         try {
             val rawObj = managed(
                 new ObjectInputStream(
@@ -634,7 +646,17 @@ object NCCli extends App {
 
             val beacon = rawObj.asInstanceOf[NCCliServerBeacon]
 
-            ProcessHandle.of(beacon.pid).asScala.map(beacon → _)
+            ProcessHandle.of(beacon.pid).asScala match {
+                case Some(ph) ⇒
+                    beacon.ph = ph
+
+                    Some(beacon)
+                case None ⇒
+                    // Attempt to clean up stale beacon file.
+                    new File(SystemUtils.getUserHome, SRV_BEACON_PATH).delete()
+
+                    None
+            }
         }
         catch {
             case _: Exception ⇒ None
@@ -647,10 +669,10 @@ object NCCli extends App {
      */
     private def cmdStopServer(cmd: Command, args: Seq[Argument], repl: 
Boolean): Unit = {
         loadServerBeacon() match {
-            case Some((beacon, ph)) ⇒
+            case Some(beacon) ⇒
                 val pid = beacon.pid
 
-                if (ph.destroy())
+                if (beacon.ph.destroy())
                     logln(s"Local REST server (pid ${c(pid)}) has been 
stopped.")
                 else
                     error(s"Failed to stop the local REST server (pid 
${c(pid)}).")
@@ -831,7 +853,7 @@ object NCCli extends App {
      */
     private def cmdGetServer(cmd: Command, args: Seq[Argument], repl: 
Boolean): Unit = {
         loadServerBeacon() match {
-            case Some((beacon, _)) ⇒ logln(s"Local REST 
server:\n${mkServerBeaconTable(beacon).toString}")
+            case Some(beacon) ⇒ logln(s"Local REST 
server:\n${mkServerBeaconTable(beacon).toString}")
             case None ⇒ error(s"Cannot detect local REST server.")
         }
     }
@@ -844,11 +866,11 @@ object NCCli extends App {
      */
     private def cmdRepl(cmd: Command, args: Seq[Argument], repl: Boolean): 
Unit = {
         loadServerBeacon() match {
-            case Some((beacon, _)) ⇒ logln(s"Local REST server 
detected:\n${mkServerBeaconTable(beacon).toString}")
+            case Some(beacon) ⇒ logln(s"Local REST server 
detected:\n${mkServerBeaconTable(beacon).toString}")
             case None ⇒ ()
         }
 
-        logln(s"Type '${c("?")}' or '${c("? -c=repl")}' to get help.")
+        logln(s"Type '${c("help")}' or '${c("help -c=repl")}' to get help.")
         logln(s"Type '${c("quit")}' to exit.")
 
         val QUITS = Seq(
@@ -857,7 +879,10 @@ object NCCli extends App {
 
         var exit = false
 
+        val appName = s"$NAME ver. ${VER.version}"
+
         val term = TerminalBuilder.builder()
+            .name(appName)
             .system(true)
             .dumb(true)
             .jansi(true)
@@ -866,6 +891,7 @@ object NCCli extends App {
         val parser = new DefaultParser()
 
         parser.setEofOnUnclosedBracket(Bracket.CURLY, Bracket.ROUND, 
Bracket.SQUARE)
+        parser.setRegexCommand("*")
 
         //val cmdNames = CMDS.flatMap(_.names)
 
@@ -879,13 +905,22 @@ object NCCli extends App {
 
         val reader = LineReaderBuilder
             .builder
+            .appName(appName)
             .terminal(term)
-//            .completer(completer)
+            .completer(completer)
             .parser(parser)
-//            .variable(LineReader.SECONDARY_PROMPT_PATTERN, s"${g(">>")} ")
+            .variable(LineReader.SECONDARY_PROMPT_PATTERN, s"${g(">>")} ")
             .variable(LineReader.INDENTATION, 2)
             .build
 
+        reader.setOpt(LineReader.Option.AUTO_FRESH_LINE)
+        reader.unsetOpt(LineReader.Option.INSERT_TAB)
+        reader.setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION)
+        reader.setVariable(
+            LineReader.HISTORY_FILE,
+            new File(SystemUtils.getUserHome, HIST_PATH).getAbsolutePath
+        )
+
         new AutosuggestionWidgets(reader).enable()
 
         while (!exit) {
@@ -893,7 +928,8 @@ object NCCli extends App {
                 try
                     reader.readLine(s"${g(">")} ")
                 catch {
-                    case _: UserInterruptException ⇒ null
+                    case _: PatternSyntaxException ⇒ "" // Guard against JLine 
hiccups.
+                    case _: UserInterruptException ⇒ "" // Ignore.
                     case _: EndOfFileException ⇒ null
                 }
 
@@ -902,21 +938,27 @@ object NCCli extends App {
             else {
                 val line = rawLine.trim()
 
-                try {
-                    doCommand(splitBySpace(line), repl = true)
-                }
-                catch {
-                    case e: SplitError ⇒
-                        val idx = e.index
-                        val lineX = line.substring(0, idx) + 
r(line.substring(idx, idx + 1) ) + line.substring(idx + 1)
-                        val dashX = c("-" * idx) + r("^") + c("-" * 
(line.length - idx - 1))
-
-                        error(s"Uneven quotes or brackets:")
-                        error(s"  ${r("+-")} $lineX")
-                        error(s"  ${r("+-")} $dashX")
-                }
+                if (line.nonEmpty)
+                    try {
+                        doCommand(splitBySpace(line), repl = true)
+                    }
+                    catch {
+                        case e: SplitError ⇒
+                            val idx = e.index
+                            val lineX = line.substring(0, idx) + 
r(line.substring(idx, idx + 1) ) + line.substring(idx + 1)
+                            val dashX = c("-" * idx) + r("^") + c("-" * 
(line.length - idx - 1))
+
+                            error(s"Uneven quotes or brackets:")
+                            error(s"  ${r("+-")} $lineX")
+                            error(s"  ${r("+-")} $dashX")
+                    }
             }
         }
+
+        // Save command history.
+        ignoring(classOf[IOException]) {
+            reader.getHistory.save()
+        }
     }
 
     /**
@@ -978,7 +1020,7 @@ object NCCli extends App {
      */
     private def unknownCommand(cmd: String): Unit = {
         error(s"Unknown command: ${y(cmd)}")
-        error(s"Use '${c("?")}' command to read the manual.")
+        error(s"Use '${c("help")}' command to read the manual.")
     }
 
     /**
@@ -1154,24 +1196,32 @@ object NCCli extends App {
         }
 
     /**
-     * Processes a single command defined by the given arguments.
      *
      * @param args
-     * @param repl Whether or not called from 'repl' command.
+     * @param repl
      */
-    private def doCommand(args: Seq[String], repl: Boolean): Unit = {
-        // Process 'no-ansi' command first, if any, and remove it from the 
list.
+    private def processAnsi(args: Seq[String], repl: Boolean): Unit = {
         args.find(arg ⇒ NO_ANSI_CMD.names.contains(arg)) match {
             case Some(_) ⇒ NO_ANSI_CMD.body(NO_ANSI_CMD, Seq.empty, repl)
             case None ⇒ ()
         }
-        // Process 'ansi' command first, if any, and remove it from the list.
         args.find(arg ⇒ ANSI_CMD.names.contains(arg)) match {
             case Some(_) ⇒ ANSI_CMD.body(ANSI_CMD, Seq.empty, repl)
             case None ⇒ ()
         }
+    }
 
-        // Remove 'no-ansi' command from the argument list, if any.
+    /**
+     * Processes a single command defined by the given arguments.
+     *
+     * @param args
+     * @param repl Whether or not called from 'repl' command.
+     */
+    private def doCommand(args: Seq[String], repl: Boolean): Unit = {
+        // Process 'no-ansi' and 'ansi' commands first.
+        processAnsi(args, repl)
+
+        // Remove 'no-ansi' and 'ansi' commands from the argument list, if any.
         val xargs = args.filter(arg ⇒ !NO_ANSI_CMD.names.contains(arg) && 
!ANSI_CMD.names.contains(arg))
 
         if (xargs.nonEmpty) {
@@ -1198,6 +1248,9 @@ object NCCli extends App {
      * @param args
      */
     private def boot(args: Array[String]): Unit = {
+        // Process 'no-ansi' and 'ansi' commands first (before ASCII title is 
shown).
+        processAnsi(args, repl = false)
+
         title()
 
         if (args.isEmpty)
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliServerBeacon.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliServerBeacon.scala
index 76a46ec..c31ad6d 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliServerBeacon.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliServerBeacon.scala
@@ -30,6 +30,7 @@ case class NCCliServerBeacon(
     restEndpoint: String,
     upLink: String,
     downLink: String,
-    startMs: Long
+    startMs: Long,
+    @transient var ph: ProcessHandle = null
 )
 

Reply via email to