Skip to content

Commit

Permalink
Derived prompts redesign
Browse files Browse the repository at this point in the history
Additionally some better formatting settings,
fixes to rendering, and minor updates
  • Loading branch information
keynmol committed Nov 28, 2024
1 parent 8e8f920 commit 1fb314f
Show file tree
Hide file tree
Showing 24 changed files with 1,235 additions and 282 deletions.
16 changes: 11 additions & 5 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ fileOverride {
"glob:**.sbt" {
runner.dialect = scala212source3
}

"glob:**/project/**.*" {
"glob:**/project/plugins.sbt" {
runner.dialect = scala212source3
newlines.topLevelStatementBlankLines = [
{
blanks = 1,
minBreaks = 0
}
]
}
}

"glob:**/snapshots-sbt-plugin/**.*" {
runner.dialect = scala212source3
}
rewrite {
trailingCommas.style = "always"
}
40 changes: 20 additions & 20 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ inThisBuild(
sonatypeCredentialHost := "s01.oss.sonatype.org",
resolvers ++= Resolver.sonatypeOssRepos("releases"),
homepage := Some(
url("https://github.com/neandertech/cue4s")
url("https://github.com/neandertech/cue4s"),
),
startYear := Some(2023),
licenses := List(
"Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")
"Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0"),
),
developers := List(
Developer(
"keynmol",
"Anton Sviridov",
"velbm@pm.me",
url("https://blog.indoorvivants.com")
)
url("https://blog.indoorvivants.com"),
),
),
version := (if (!sys.env.contains("CI")) "dev" else version.value)
)
version := (if (!sys.env.contains("CI")) "dev" else version.value),
),
)

val Versions = new {
Expand All @@ -41,7 +41,7 @@ val Versions = new {
lazy val munitSettings = Seq(
libraryDependencies += {
"org.scalameta" %%% "munit" % Versions.munit % Test
}
},
)

lazy val root = project
Expand All @@ -55,7 +55,7 @@ lazy val core = projectMatrix
.in(file("modules/core"))
.defaultAxes(defaults*)
.settings(
name := "cue4s"
name := "cue4s",
)
.settings(munitSettings)
.jvmPlatform(Versions.scalaVersions)
Expand All @@ -70,7 +70,7 @@ lazy val core = projectMatrix
libraryDependencies += "com.lihaoyi" %%% "fansi" % Versions.fansi,
libraryDependencies +=
"net.java.dev.jna" % "jna" % Versions.jna,
nativeConfig ~= (_.withIncrementalCompilation(true))
nativeConfig ~= (_.withIncrementalCompilation(true)),
)
.enablePlugins(SnapshotsPlugin)
.settings(superMatrix)
Expand All @@ -79,7 +79,7 @@ lazy val catsEffect = projectMatrix
.in(file("modules/cats-effect"))
.defaultAxes(defaults*)
.settings(
name := "cue4s-cats-effect"
name := "cue4s-cats-effect",
)
.dependsOn(core)
.settings(munitSettings)
Expand All @@ -92,7 +92,7 @@ lazy val catsEffect = projectMatrix
scalaJSUseMainModuleInitializer := true,
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
libraryDependencies += "org.typelevel" %%% "cats-effect" % Versions.catsEffect,
nativeConfig ~= (_.withIncrementalCompilation(true))
nativeConfig ~= (_.withIncrementalCompilation(true)),
)
.enablePlugins(SnapshotsPlugin)

Expand All @@ -103,7 +103,7 @@ lazy val example = projectMatrix
.enablePlugins(JavaAppPackaging)
.settings(
name := "example",
noPublish
noPublish,
)
.settings(munitSettings)
.jvmPlatform(Versions.scalaVersions)
Expand All @@ -115,7 +115,7 @@ lazy val example = projectMatrix
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
nativeConfig ~= (_.withIncrementalCompilation(true)
.withSourceLevelDebuggingConfig(SourceLevelDebuggingConfig.enabled)),
Compile / mainClass := Some("cue4s_example.sync")
Compile / mainClass := Some("cue4s_example.sync"),
)
.settings(superMatrix)

Expand All @@ -142,7 +142,7 @@ lazy val exampleCatsEffect = projectMatrix
.enablePlugins(JavaAppPackaging)
.settings(
name := "example",
noPublish
noPublish,
)
.settings(munitSettings)
.jvmPlatform(Versions.scalaVersions)
Expand All @@ -152,7 +152,7 @@ lazy val exampleCatsEffect = projectMatrix
scalaJSUseMainModuleInitializer := true,
Compile / mainClass := Some("example.catseffect.ioExample"),
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
nativeConfig ~= (_.withIncrementalCompilation(true))
nativeConfig ~= (_.withIncrementalCompilation(true)),
)

lazy val docs =
Expand All @@ -164,7 +164,7 @@ lazy val docs =

val noPublish = Seq(
publish / skip := true,
publishLocal / skip := true
publishLocal / skip := true,
)

val defaults =
Expand All @@ -174,7 +174,7 @@ val scalafixRules = Seq(
"OrganizeImports",
"DisableSyntax",
"LeakingImplicitClassVal",
"NoValInForComprehension"
"NoValInForComprehension",
).mkString(" ")

val CICommands = Seq(
Expand All @@ -186,14 +186,14 @@ val CICommands = Seq(
"scalafmtCheckAll",
"scalafmtSbtCheck",
s"scalafix --check $scalafixRules",
"headerCheck"
"headerCheck",
).mkString(";")

val PrepareCICommands = Seq(
s"scalafix --rules $scalafixRules",
"scalafmtAll",
"scalafmtSbt",
"headerCreate"
"headerCreate",
).mkString(";")

addCommandAlias("ci", CICommands)
Expand All @@ -202,7 +202,7 @@ addCommandAlias("preCI", PrepareCICommands)

addCommandAlias(
"testSnapshots",
"""set Test/envVars += ("SNAPSHOTS_INTERACTIVE" -> "true"); test"""
"""set Test/envVars += ("SNAPSHOTS_INTERACTIVE" -> "true"); test""",
)

addCommandAlias("checkDocs", "docs/mdoc --in README.md")
Expand Down
6 changes: 4 additions & 2 deletions modules/cats-effect/src/main/scala/PromptsIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ class PromptsIO private (
prompt: Prompt[A]
): IO[Completion[A]] =
val inputProvider = InputProvider(out)
val handler = prompt.handler(terminal, out, colors)
val framework = prompt.framework(terminal, out, colors)

// TODO: provide native CE interface here
IO.executionContext
.flatMap: ec =>
IO.fromFuture(IO(inputProvider.evaluateFuture(handler)(using ec)))
IO.fromFuture(
IO(inputProvider.evaluateFuture(framework.handler)(using ec))
)
.guarantee(IO(terminal.cursorShow()))
.guarantee(IO(inputProvider.close()))

Expand Down
8 changes: 4 additions & 4 deletions modules/core/src/main/scala-jvm-native/PromptsPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ private trait PromptsPlatform:
prompt match
case p: Prompt[?] =>
try
val handler = p.asInstanceOf[Prompt[R]].handler(terminal, out, colors)
val framework = p.asInstanceOf[Prompt[R]].framework(terminal, out, colors)

inputProvider.evaluate(handler)
inputProvider.evaluate(framework.handler)
finally
terminal.cursorShow()
inputProvider.close()
Expand All @@ -45,9 +45,9 @@ private trait PromptsPlatform:
createTerminal: Output => Terminal = Terminal.ansi(_),
colors: Boolean = true
)(using ExecutionContext): Future[Completion[R]] =
val handler = prompt.handler(createTerminal(out), out, colors)
val framework = prompt.framework(createTerminal(out), out, colors)

val f = inputProvider.evaluateFuture(handler)
val f = inputProvider.evaluateFuture(framework.handler)
f.onComplete(_ => terminal.cursorShow())
f.onComplete(_ => inputProvider.close())
f
Expand Down
74 changes: 27 additions & 47 deletions modules/core/src/main/scala/InteractiveMultipleChoice.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ private[cue4s] class InteractiveMultipleChoice(
terminal: Terminal,
out: Output,
colors: Boolean,
windowSize: Int
) extends PromptFramework(terminal, out):
windowSize: Int,
) extends PromptFramework[List[String]](terminal, out):
import InteractiveMultipleChoice.*

override type PromptState = State
override type Result = List[String]

private lazy val preSelected =
prompt.alts.zipWithIndex.filter(_._1._2).map(_._2)
Expand All @@ -42,16 +41,19 @@ private[cue4s] class InteractiveMultipleChoice(
display = InfiniscrollableState(
showing = Some(altsWithIndex.map(_._2) -> 0),
windowStart = 0,
windowSize = windowSize
)
windowSize = windowSize,
),
)

private lazy val altMapping = altsWithIndex.map(_.swap).toMap

private lazy val formatting = TextFormatting(colors)
import formatting.*

override def renderState(st: State): List[String] =
override def renderState(
st: State,
error: Option[PromptError],
): List[String] =
val lines = List.newBuilder[String]

st.status match
Expand Down Expand Up @@ -80,7 +82,7 @@ private[cue4s] class InteractiveMultipleChoice(
else if filtered.size > st.display.windowSize && idx == st.display.windowSize - 1 &&
filtered.indexOf(id) != filtered.size - 1
then s"$alt".bold
else s" $alt"
else s" $alt",
)
end if
end match
Expand All @@ -90,8 +92,7 @@ private[cue4s] class InteractiveMultipleChoice(

if ids.isEmpty then lines += "nothing selected".underline
else
ids.toList.sorted
.map(altMapping)
ids
.foreach: value =>
lines += s"" + value.bold

Expand All @@ -104,57 +105,36 @@ private[cue4s] class InteractiveMultipleChoice(
lines.result()
end renderState

override def handleEvent(event: Event): Next[List[String]] =
override def result(state: State): Either[PromptError, List[String]] =
Right(state.selected.toList.sorted.map(altMapping).toList)

override def handleEvent(event: Event) =
event match
case Event.Init =>
terminal.cursorHide()
printPrompt()
Next.Continue
case Event.Init => PromptAction.Start

case Event.Key(KeyEvent.UP) =>
stateTransition(_.updateDisplay(_.up))
printPrompt()
Next.Continue
PromptAction.Update(_.updateDisplay(_.up))

case Event.Key(KeyEvent.DOWN) =>
stateTransition(_.updateDisplay(_.down))
printPrompt()
Next.Continue
PromptAction.Update(_.updateDisplay(_.down))

case Event.Key(KeyEvent.ENTER) =>
stateTransition(_.finish)
currentState().status match
case Status.Canceled => Next.Stop
case Status.Running => Next.Continue
case Status.Finished(ids) =>
val stringValues = ids.toList.sorted.map(altMapping.apply)
printPrompt()
terminal.cursorShow()
Next.Done(stringValues)
PromptAction.Submit(result => state => state.finish(result))

case Event.Key(KeyEvent.TAB) =>
stateTransition(_.toggle)
printPrompt()
Next.Continue
PromptAction.Update(_.toggle)

case Event.Key(KeyEvent.DELETE) =>
stateTransition(_.trimText)
printPrompt()
Next.Continue
PromptAction.Update(_.trimText)

case Event.Char(which) =>
stateTransition(_.addText(which.toChar))
printPrompt()
Next.Continue
PromptAction.Update(_.addText(which.toChar))

case Event.Interrupt =>
stateTransition(_.cancel)
printPrompt()
terminal.cursorShow()
Next.Stop
PromptAction.UpdateAndStop(_.cancel)

case _ =>
Next.Continue
PromptAction.Continue
end match
end handleEvent

Expand All @@ -163,21 +143,21 @@ end InteractiveMultipleChoice
private[cue4s] object InteractiveMultipleChoice:
enum Status:
case Running
case Finished(ids: Set[Int])
case Finished(ids: List[String])
case Canceled

case class State(
text: String,
selected: Set[Int],
all: List[(String, Int)],
status: Status,
display: InfiniscrollableState
display: InfiniscrollableState,
):

def updateDisplay(f: InfiniscrollableState => InfiniscrollableState) =
copy(display = f(display))

def finish = copy(status = Status.Finished(selected))
def finish(result: List[String]) = copy(status = Status.Finished(result))

def cancel = copy(status = Status.Canceled)

Expand All @@ -198,7 +178,7 @@ private[cue4s] object InteractiveMultipleChoice:
val newFiltered = all.filter((alt, _) =>
newText.trim.isEmpty || alt
.toLowerCase()
.contains(newText.toLowerCase().trim())
.contains(newText.toLowerCase().trim()),
)
if newFiltered.nonEmpty then
display.showing match
Expand Down
Loading

0 comments on commit 1fb314f

Please sign in to comment.