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 9fd3578 WIP.
9fd3578 is described below
commit 9fd357817a6f22f223746c04e606db584d81eb2e
Author: Aaron Radzinski <[email protected]>
AuthorDate: Tue Aug 25 23:12:42 2020 -0700
WIP.
---
.../nlpcraft/common/ascii/NCAsciiTable.scala | 152 +++++++++++++++++----
.../model/tools/cmdline/NCCommandLine.scala | 74 +++++++---
.../nlpcraft/common/ascii/NCAsciiTableSpec.scala | 6 +-
3 files changed, 182 insertions(+), 50 deletions(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ascii/NCAsciiTable.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ascii/NCAsciiTable.scala
index db86fec..6bde0cf 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ascii/NCAsciiTable.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ascii/NCAsciiTable.scala
@@ -33,9 +33,10 @@ class NCAsciiTable {
/**
* Cell style.
*/
- private sealed case class Style(
+ private final case class Style(
var leftPad: Int = 1, // >= 0
var rightPad: Int = 1, // >= 0
+ var maxWidth: Int = Int.MaxValue, // > 0
var align: String = "center" // center, left, right
) {
/** Gets overall padding (left + right). */
@@ -57,7 +58,7 @@ class NCAsciiTable {
for (e ← sty.split(',')) {
val a = e.split(":")
- assume(a.length == 2, s"Invalid cell style: ${e.trim}")
+ require(a.length == 2, s"Invalid cell style: ${e.trim}")
val a0 = a(0).trim
val a1 = a(1).trim
@@ -65,20 +66,29 @@ class NCAsciiTable {
a0 match {
case "leftPad" ⇒ cs.leftPad = a1.toInt
case "rightPad" ⇒ cs.rightPad = a1.toInt
- case "align" ⇒ cs.align = a1
+ case "maxWidth" ⇒ cs.maxWidth = a1.toInt
+ case "align" ⇒ cs.align = a1.toLowerCase
case _ ⇒ assert(assertion = false, s"Invalid style:
${e.trim}")
}
}
}
+ require(cs.leftPad >= 0, "Style 'leftPad' must >= 0.")
+ require(cs.rightPad >= 0, "Style 'rightPad' must >= 0.")
+ require(cs.maxWidth > 0, "Style 'maxWidth' must > 0.")
+ require(cs.align == "center" || cs.align == "left" || cs.align ==
"right", "Style 'align' must be 'left', 'right' or 'center'.")
+
cs
}
}
/**
* Cell holder.
+ *
+ * @param style
+ * @param lines Lines that are already cut up per `style`, if required.
*/
- private sealed case class Cell(style: Style, lines: Seq[String]) {
+ private final case class Cell(style: Style, lines: Seq[String]) {
// Cell's calculated width including padding.
lazy val width: Int =
if (height > 0)
@@ -97,8 +107,8 @@ class NCAsciiTable {
top: Int = 0,
right: Int = 0,
bottom: Int = 0,
- left: Int = 0) {
- }
+ left: Int = 0
+ )
// Table drawing symbols.
private val HDR_HOR = "="
@@ -119,31 +129,28 @@ class NCAsciiTable {
private var margin = Margin()
/**
- * Flag indicating whether or not to draw inside horizontal lines
+ * Global flag indicating whether or not to draw inside horizontal lines
* between individual rows.
*/
var insideBorder = false
/**
- * Flag indicating whether of not to automatically draw horizontal lines
+ * Global Flag indicating whether of not to automatically draw horizontal
lines
* for multiline rows.
*/
var autoBorder = true
/**
- * Maximum width of the cell. If any line in the cell exceeds this width
- * it will be cut in two or more lines.
- *
- * '''NOTE''': it doesn't include into account the padding. Only the actual
- * string length is counted.
+ * If lines exceeds the style's maximum width it will be broken up
+ * either by nearest space (by whole words) or mid-word.
*/
- var maxCellWidth: Int = Int.MaxValue
+ var breakUpByWords = true
- /** Row style. */
- var rowStyle: String = DFLT_ROW_STYLE
+ /** Default row style. */
+ var defaultRowStyle: String = DFLT_ROW_STYLE
- /** Header style. */
- var headerStyle: String = DFLT_HEADER_STYLE
+ /** Default header style. */
+ var defaultHeaderStyle: String = DFLT_HEADER_STYLE
// Dash drawing.
private def dash(ch: String, len: Int): String = (for (_ ← 1 to len) yield
ch).mkString("")
@@ -198,6 +205,25 @@ class NCAsciiTable {
}
/**
+ * Adds row (one or more row cells) with a given style.
+ *
+ * @param style Style to use.
+ * @param cells Row cells. For multi-line cells - use `Seq(...)`.
+ */
+ def +/(style: String, cells: Any*): NCAsciiTable = {
+ startRow()
+
+ cells foreach {
+ case i: Iterable[_] ⇒ addStyledRowCell(style, i.iterator.toSeq: _*)
+ case a ⇒ addStyledRowCell(style, a)
+ }
+
+ endRow()
+
+ this
+ }
+
+ /**
* Adds row.
*
* @param cells Row cells.
@@ -227,12 +253,39 @@ class NCAsciiTable {
}
/**
+ * Adds styled header (one or more header cells).
+ *
+ * @param style Style to use.
+ * @param cells Header cells. For multi-line cells - use `Seq(...)`.
+ */
+ def #/(style: String, cells: Any*): NCAsciiTable = {
+ cells foreach {
+ case i: Iterable[_] ⇒ addStyledHeaderCell(style, i.iterator.toSeq:
_*)
+ case a ⇒ addStyledHeaderCell(style, a)
+ }
+
+ this
+ }
+
+ /**
* Adds headers.
*
* @param cells Header cells.
*/
def addHeaders(cells: java.util.List[Any]): NCAsciiTable = {
- cells.asScala.foreach(p ⇒ addHeaderCell(p))
+ cells.asScala.foreach(addHeaderCell(_))
+
+ this
+ }
+
+ /**
+ * Adds headers with the given `style`.
+ *
+ * @param style Style top use.
+ * @param cells Header cells.
+ */
+ def addStyledHeaders(style: String, cells: java.util.List[Any]):
NCAsciiTable = {
+ cells.asScala.foreach(addHeaderCell(style, _))
this
}
@@ -245,31 +298,72 @@ class NCAsciiTable {
/**
*
+ * @param maxWidth
+ * @param lines
+ * @return
+ */
+ private def breakUpByNearestSpace(maxWidth: Int, lines: Seq[Any]):
Seq[String] = ???
+
+ /**
+ *
* @param style
* @param lines
* @return
*/
- private def mkRowCell(style: String, lines: Any*): Cell =
- Cell(Style(style), (for (line ← lines) yield
x(line).grouped(maxCellWidth)).flatten)
+ private def mkRowCell(style: String, lines: Any*): Cell = {
+ val st = Style(style)
+
+ Cell(
+ st,
+ if (breakUpByWords)
+ breakUpByNearestSpace(st.maxWidth, lines)
+ else
+ (for (line ← lines) yield x(line).grouped(st.maxWidth)).flatten
+ )
+ }
/**
- * Adds single header cell.
+ * Adds single header cell with the default style..
*
* @param lines One or more cell lines.
*/
def addHeaderCell(lines: Any*): NCAsciiTable = {
- hdr :+= mkRowCell(headerStyle, lines: _*)
+ hdr :+= mkRowCell(defaultHeaderStyle, lines: _*)
this
}
/**
- * Adds single row cell.
+ * Adds single row cell with the default style.
*
* @param lines One or more row cells. Multiple lines will be printed on
separate lines.
*/
def addRowCell(lines: Any*): NCAsciiTable = {
- curRow :+= mkRowCell(rowStyle, lines: _*)
+ curRow :+= mkRowCell(defaultRowStyle, lines: _*)
+
+ this
+ }
+
+ /**
+ * Adds single header cell with the default style..
+ *
+ * @param style Style to use.
+ * @param lines One or more cell lines.
+ */
+ def addStyledHeaderCell(style: String, lines: Any*): NCAsciiTable = {
+ hdr :+= mkRowCell(style, lines: _*)
+
+ this
+ }
+
+ /**
+ * Adds single row cell with the default style.
+ *
+ * @param style Style to use.
+ * @param lines One or more row cells. Multiple lines will be printed on
separate lines.
+ */
+ def addStyledRowCell(style: String, lines: Any*): NCAsciiTable = {
+ curRow :+= mkRowCell(style, lines: _*)
this
}
@@ -352,12 +446,12 @@ class NCAsciiTable {
for (_ ← 0 until margin.top)
tbl ++= " \n"
- def mkRow(crs: String, cor: String): String =
+ def mkAsciiLine(crs: String, cor: String): String =
s"${space(margin.left)}$crs${dash(cor,
tableW)}$crs${space(margin.right)}\n"
// Print header, if any.
if (isHdr) {
- tbl ++= mkRow(HDR_CRS, HDR_HOR)
+ tbl ++= mkAsciiLine(HDR_CRS, HDR_HOR)
for (i ← 0 until hdrH) {
// Left margin and '|'.
@@ -378,10 +472,10 @@ class NCAsciiTable {
tbl ++= s"${space(margin.right)}\n"
}
- tbl ++= mkRow(HDR_CRS, HDR_HOR)
+ tbl ++= mkAsciiLine(HDR_CRS, HDR_HOR)
}
else
- tbl ++= mkRow(ROW_CRS, ROW_HOR)
+ tbl ++= mkAsciiLine(ROW_CRS, ROW_HOR)
// Print rows, if any.
if (rows.nonEmpty) {
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCommandLine.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCommandLine.scala
index 26458cd..f215ccd 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCommandLine.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCommandLine.scala
@@ -21,6 +21,8 @@ import org.apache.nlpcraft.common.ascii.NCAsciiTable
import org.apache.nlpcraft.common.util.NCUtils
import org.apache.nlpcraft.common.version.NCVersion
+import scala.collection.mutable
+
/**
* 'nlpcraft' script entry point.
*/
@@ -33,12 +35,13 @@ object NCCommandLine extends App {
// Single CLI command.
case class Command(
+ id: String,
names: Seq[String],
synopsis: String,
- desc: String = "",
+ desc: Option[String] = None,
params: Seq[Parameter] = Seq.empty,
examples: Seq[Example] = Seq.empty,
- body: Seq[String] => Unit
+ body: (Command, Seq[String]) => Unit
) {
final val extNames = names.flatMap(name => // Safeguard against
"common" errors.
Seq(
@@ -49,6 +52,15 @@ object NCCommandLine extends App {
s"\\$name"
)
)
+
+ /**
+ *
+ * @param paramId
+ * @param cliParams
+ * @return
+ */
+ def isParamPresent(paramId: String, cliParams: Seq[String]): Boolean =
+ params.find(_.id ==
paramId).get.names.intersect(cliParams).nonEmpty
}
// Single command's example.
case class Example(
@@ -57,6 +69,7 @@ object NCCommandLine extends App {
)
// Single command's parameter.
case class Parameter(
+ id: String,
names: Seq[String],
valueDesc: Option[String] = None,
arity: (Int, Int) = (1, 1), // Mandatory by default.
@@ -66,22 +79,24 @@ object NCCommandLine extends App {
// All supported commands.
private final val CMDS = Seq(
Command(
+ id = "help",
names = Seq("help", "?"),
synopsis = s"Displays manual page for $SCRIPT_NAME.",
- desc =
- s"""
- |By default, without '-all' or '-cmd' parameters, displays
the abbreviated form of manual
- |only listing the commands without parameters or examples.
- |""".stripMargin,
+ desc = Some(
+ s"By default, without '-all' or '-cmd' parameters, displays
the abbreviated form of manual " +
+ s"only listing the commands without parameters or examples."
+ ),
body = cmdHelp,
params = Seq(
Parameter(
+ id = "cmd",
names = Seq("--cmd", "-c"),
valueDesc = Some("{cmd}"),
arity = (0, 3),
desc = "Set of commands to show the manual for."
),
Parameter(
+ id = "all",
names = Seq("--all", "-a"),
arity = (0, 1),
desc = "Flag to show full manual for all commands."
@@ -89,25 +104,27 @@ object NCCommandLine extends App {
)
),
Command(
+ id = "ver",
names = Seq("version", "ver"),
synopsis = s"Displays version of $SCRIPT_NAME runtime.",
body = cmdVersion
),
Command(
+ id = "repl",
names = Seq("repl"),
synopsis = s"Starts '$SCRIPT_NAME' in interactive REPL mode.",
body = cmdRepl
)
)
- private final val HELP_CMD = CMDS.find(_.names.contains("help")).get
- private final val DFLT_CMD = CMDS.find(_.names.contains("repl")).get
+ private final val HELP_CMD = CMDS.find(_.id == "help").get
+ private final val DFLT_CMD = CMDS.find(_.id == "repl").get
/**
- *
+ * @param cmd Command descriptor.
* @param params Parameters, if any, for this command.
*/
- private def cmdHelp(params: Seq[String]): Unit = {
+ private def cmdHelp(cmd: Command, params: Seq[String]): Unit = {
log(
s""" |NAME
| $SCRIPT_NAME - command line interface to control
NLPCraft.
@@ -118,14 +135,33 @@ object NCCommandLine extends App {
|COMMANDS""".stripMargin
)
- if (params.isEmpty) {
- val tbl = NCAsciiTable().margin(left = 4)
+ val tbl = NCAsciiTable().margin(left = 4)
+
+ tbl.maxCellWidth = 55
+ if (params.isEmpty) // Default - show abbreviated help.
CMDS.foreach(cmd => tbl += (cmd.names.mkString(", "),
cmd.synopsis))
+ else if (cmd.isParamPresent("all", params)) { // Show a full format
help for all commands.
+ CMDS.foreach(cmd => {
+ var lines = mutable.Buffer.empty[String]
- log(tbl.toString)
+ if (cmd.desc.isDefined)
+ lines += cmd.synopsis + " " + cmd.desc.get
+ else
+ lines += cmd.synopsis
+
+ lines += ""
+
+ if (cmd.params.nonEmpty) {
+ lines += "PARAMETERS"
+ }
+
+ tbl += (cmd.names.mkString(", "), lines)
+ })
}
+ log(tbl.toString)
+
@@ -178,17 +214,19 @@ object NCCommandLine extends App {
/**
*
+ * @param cmd Command descriptor.
* @param params Parameters, if any, for this command.
*/
- private def cmdRepl(params: Seq[String]): Unit = {
+ private def cmdRepl(cmd: Command, params: Seq[String]): Unit = {
}
/**
*
+ * @param cmd Command descriptor.
* @param params Parameters, if any, for this command.
*/
- private def cmdVersion(params: Seq[String]): Unit = {
+ private def cmdVersion(cmd: Command, params: Seq[String]): Unit = {
// Nothing - common header with version will be printed before anyways.
}
@@ -215,12 +253,12 @@ object NCCommandLine extends App {
log()
if (args.isEmpty)
- NCCommandLine.DFLT_CMD.body(Seq.empty)
+ NCCommandLine.DFLT_CMD.body(DFLT_CMD, Seq.empty)
else {
val cmdName = args.head
CMDS.find(_.extNames.contains(cmdName)) match {
- case Some(cmd) => cmd.body(args.tail)
+ case Some(cmd) => cmd.body(cmd, args.tail)
case None =>
error(s"Unknown command: $cmdName")
errorHelp()
diff --git
a/nlpcraft/src/test/scala/org/apache/nlpcraft/common/ascii/NCAsciiTableSpec.scala
b/nlpcraft/src/test/scala/org/apache/nlpcraft/common/ascii/NCAsciiTableSpec.scala
index 8081b9d..2abb4b1 100644
---
a/nlpcraft/src/test/scala/org/apache/nlpcraft/common/ascii/NCAsciiTableSpec.scala
+++
b/nlpcraft/src/test/scala/org/apache/nlpcraft/common/ascii/NCAsciiTableSpec.scala
@@ -27,7 +27,7 @@ class NCAsciiTableSpec {
def test() {
val t = NCAsciiTable()
- t.headerStyle = "leftPad: 10, rightPad: 5"
+ t.defaultHeaderStyle = "leftPad: 10, rightPad: 5"
t.margin(5, 5, 5, 5)
@@ -45,7 +45,7 @@ class NCAsciiTableSpec {
def testWithSequenceHeader() {
val t = NCAsciiTable()
- t.headerStyle = "leftPad: 10, rightPad: 5"
+ t.defaultHeaderStyle = "leftPad: 10, rightPad: 5"
t.margin(5, 5, 5, 5)
@@ -65,7 +65,7 @@ class NCAsciiTableSpec {
val t = NCAsciiTable()
- t.headerStyle = "leftPad: 10, rightPad: 5"
+ t.defaultHeaderStyle = "leftPad: 10, rightPad: 5"
t #= (Seq("Header 1", "Header 2", "Header 3"): _*)