aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2019-09-22 14:25:43 -0400
committerJakob Odersky <jakob@odersky.com>2019-09-22 14:59:47 -0400
commit1907b94f7e6a38fc4a537b01ca795c46b1843c18 (patch)
tree5b9874e89d737bba1ddac750f3ad9c624cb79e48
parentb41784f73ccf55afdaaac0113f13ab13e0d39887 (diff)
downloadcommando-1907b94f7e6a38fc4a537b01ca795c46b1843c18.tar.gz
commando-1907b94f7e6a38fc4a537b01ca795c46b1843c18.tar.bz2
commando-1907b94f7e6a38fc4a537b01ca795c46b1843c18.zip
Add usage information
-rw-r--r--commando/src/Command.scala71
-rw-r--r--commando/src/Main.scala11
2 files changed, 78 insertions, 4 deletions
diff --git a/commando/src/Command.scala b/commando/src/Command.scala
index eaf7804..0f65afb 100644
--- a/commando/src/Command.scala
+++ b/commando/src/Command.scala
@@ -5,7 +5,7 @@ object Command {
case class ParseError(message: String) extends RuntimeException(message)
}
-class Command(val name: String) {
+class Command(val name: String, val description: String = "") {
import Command._
import collection.mutable
@@ -15,7 +15,8 @@ class Command(val name: String) {
var argName: String,
var acceptsArg: Boolean,
var requiresArg: Boolean,
- var action: Option[String] => Unit
+ var action: Option[String] => Unit,
+ var info: String
)
private case class PositionalParameter(
@@ -30,6 +31,7 @@ class Command(val name: String) {
class NamedBuilder(param: NamedParameter) {
def action(fct: () => Unit) = { param.action = opt => fct(); this }
+ def info(text: String) = { param.info = text; this }
def arg(name: String) = {
param.argName = name; param.acceptsArg = true; param.requiresArg = true;
@@ -45,9 +47,11 @@ class Command(val name: String) {
def action(fct: String => Unit) = {
param.action = opt => fct(opt.get); this
}
+ def info(text: String) = { param.info = text; this }
}
class NamedOptArgBuilder(param: NamedParameter) {
def action(fct: Option[String] => Unit) = { param.action = fct; this }
+ def info(text: String) = { param.info = text; this }
}
class PositionalBuilder(param: PositionalParameter) {
@@ -59,7 +63,7 @@ class Command(val name: String) {
def named(name: String, short: Char = 0): NamedBuilder = {
val shortName = if (short == 0) None else Some(short)
- val param = NamedParameter(name, shortName, "", false, false, _ => ())
+ val param = NamedParameter(name, shortName, "", false, false, _ => (), "")
namedParams += param
new NamedBuilder(param)
}
@@ -240,4 +244,65 @@ class Command(val name: String) {
|""".stripMargin
}
+ def usage(): String = {
+ val named = namedParams.result()
+ val positional = posParams.result()
+
+ val b = new mutable.StringBuilder
+
+ // headline
+ b.append("Usage: ")
+ b.append(name);
+ if (!named.isEmpty) {
+ b.append(" [options]")
+ }
+ for (pos <- positional) {
+ b.append(" ")
+ if (pos.optional) {
+ b.append("["); b.append(pos.name); b.append("]")
+ } else {
+ b.append(pos.name)
+ }
+ if (pos.repeated) {
+ b.append("...")
+ }
+ }
+
+ // command description
+ if (!description.isEmpty) {
+ b.append("\n\n")
+ b.append(description)
+ }
+
+ // parameters
+ if (!named.isEmpty) {
+ b.append("\n\nOptions:\n")
+
+ named.sortBy(_.name).foreach { param =>
+ val short = param.short.map(c => s" -$c, ").getOrElse(" ")
+ val long = if (param.requiresArg) {
+ s"--${param.name}=${param.argName}"
+ } else if (param.acceptsArg) {
+ s"--${param.name}[=${param.argName}]"
+ } else {
+ s"--${param.name}"
+ }
+
+ b.append("\n")
+ if (long.length <= 22) {
+ b.append(short)
+ b.append(f"$long%-22s ")
+ b.append(param.info)
+ } else {
+ b.append(short)
+ b.append(f"$long\n")
+ b.append(" " * 30)
+ b.append(param.info)
+ }
+
+ }
+ }
+ b.result()
+ }
+
}
diff --git a/commando/src/Main.scala b/commando/src/Main.scala
index 3270019..043a9f2 100644
--- a/commando/src/Main.scala
+++ b/commando/src/Main.scala
@@ -2,11 +2,13 @@ package example
object Main extends App {
- val cmd = new commando.Command("xorc") {
+ val cmd = new commando.Command("xorc", "") {
+
val version = named("version", 'V')
.action(() => println("version 1"))
named("verbose", 'v')
+ .info("Set verbosity level. This option may be repeated.")
.optionalArg("level")
.action(level => println(s"level $level"))
@@ -19,8 +21,15 @@ object Main extends App {
.repeat()
val comp = named("completion")
+ .info(
+ s"Print bash completion. Add ${name} --completions to your bashrc to get completions."
+ )
.action(() => println(completion()))
+ named("help", 'h')
+ .info("print a help message")
+ .action(() => println(usage()))
+
}
cmd.parse(args) match {
case None =>