Skip to content

Commit

Permalink
Escape backticks in bash/zsh completion
Browse files Browse the repository at this point in the history
  • Loading branch information
lwronski committed Nov 24, 2021
1 parent 44b600f commit d1243d7
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 7 deletions.
4 changes: 3 additions & 1 deletion annotations/shared/src/main/scala/caseapp/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ object ValueDescription {
}

/** Help message for the annotated argument
* @messageMd
* not used by case-app itself, only there as a convenience for case-app users
*/
final case class HelpMessage(message: String) extends StaticAnnotation
final case class HelpMessage(message: String, messageMd: String = "") extends StaticAnnotation

/** Name for the annotated case class of arguments E.g. MyApp
*/
Expand Down
4 changes: 3 additions & 1 deletion core/shared/src/main/scala/caseapp/core/complete/Bash.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package caseapp.core.complete

import scala.util.Properties

object Bash {

val shellName: String =
Expand All @@ -19,7 +21,7 @@ object Bash {
}

private def escape(s: String): String =
s.replace("\"", "\\\"")
s.replace("\"", "\\\"").replace("`", "\\`").split("\\r?\\n").headOption.getOrElse("")
def print(items: Seq[CompletionItem]): String = {
val newLine = System.lineSeparator()
val b = new StringBuilder
Expand Down
8 changes: 5 additions & 3 deletions core/shared/src/main/scala/caseapp/core/complete/Zsh.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.security.MessageDigest
import dataclass.data

import scala.collection.mutable
import scala.util.Properties

object Zsh {

Expand Down Expand Up @@ -34,22 +35,23 @@ object Zsh {
else
res
}

private def escape(s: String): String =
s.replace("'", "\\'").replace("`", "\\`").split("\\r?\\n").headOption.getOrElse("")
private def defs(item: CompletionItem): Seq[String] = {
val (options, arguments) = item.values.partition(_.startsWith("-"))
val optionsOutput =
if (options.isEmpty) Nil
else {
val escapedOptions = options
val desc = item.description.map(":" + _.replace("'", "\\'")).getOrElse("")
val desc = item.description.map(desc => ":" + escape(desc)).getOrElse("")
options.map { opt =>
"\"" + opt + desc + "\""
}
}
val argumentsOutput =
if (arguments.isEmpty) Nil
else {
val desc = item.description.map(":" + _.replace("'", "\\'")).getOrElse("")
val desc = item.description.map(desc => ":" + escape(desc)).getOrElse("")
arguments.map("'" + _.replace(":", "\\:") + desc + "'")
}
optionsOutput ++ argumentsOutput
Expand Down
14 changes: 13 additions & 1 deletion tests/shared/src/test/scala/caseapp/CompletionDefinitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,30 @@ object CompletionDefinitions {
@Name("g") @HelpMessage("A pattern") glob: String = "",
@Name("d") count: Int = 0
)
case class BackTickOptions(
@HelpMessage(
"""A pattern with backtick `--`
|with multiline
|""".stripMargin
) backtick: String = "",
@Name("d") count: Int = 0
)
object First extends Command[FirstOptions] {
def run(options: FirstOptions, args: RemainingArgs): Unit = ???
}
object Second extends Command[SecondOptions] {
def run(options: SecondOptions, args: RemainingArgs): Unit = ???
}
object BackTick extends Command[BackTickOptions] {
def run(options: BackTickOptions, args: RemainingArgs): Unit = ???
}

object Prog extends CommandsEntryPoint {
def progName = "prog"
def commands = Seq(
First,
Second
Second,
BackTick
)
}
}
Expand Down
30 changes: 29 additions & 1 deletion tests/shared/src/test/scala/caseapp/CompletionTests.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package caseapp

import caseapp.core.complete.CompletionItem
import caseapp.core.complete.{Bash, CompletionItem, Zsh}
import utest._

object CompletionTests extends TestSuite {
Expand Down Expand Up @@ -110,6 +110,7 @@ object CompletionTests extends TestSuite {
test {
val res = Prog.complete(Seq(""), 0)
val expected = List(
CompletionItem("back-tick", None, Nil),
CompletionItem("first", None, Nil),
CompletionItem("second", None, Nil)
)
Expand Down Expand Up @@ -140,6 +141,33 @@ object CompletionTests extends TestSuite {
)
assert(res == expected)
}

test("bash") {
val res = Prog.complete(Seq("back-tick", "-"), 1)
val expected = List(
CompletionItem("--backtick", Some("A pattern with backtick `--`"), Nil),
CompletionItem("--count", None, List("-d"))
)
assert(res == expected)

val compRely = Bash.print(res)
val expectedCompRely = """"--backtick -- A pattern with backtick \`--\`"""".stripMargin

assert(compRely.contains(expectedCompRely))
}
test("zsh") {
val res = Prog.complete(Seq("back-tick", "-"), 1)
val expected = List(
CompletionItem("--backtick", Some("A pattern with backtick `--`"), Nil),
CompletionItem("--count", None, List("-d"))
)
assert(res == expected)

val compRely = Zsh.print(res)
val expectedCompRely = """"--backtick:A pattern with backtick \`--\`"""".stripMargin

assert(compRely.contains(expectedCompRely))
}
}

test("commands with default") {
Expand Down

0 comments on commit d1243d7

Please sign in to comment.