Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor + snapshots plugin #1

Merged
merged 32 commits into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
af19c37
Start refactoring
keynmol Jan 28, 2024
eee6064
Add tracing terminal
keynmol Jan 28, 2024
59239b9
WIP
keynmol Jan 28, 2024
bab8ec5
Implement all the other methods
keynmol Feb 7, 2024
22196d3
Terminal tracing example
keynmol Feb 7, 2024
acb98e0
WIP
keynmol Feb 7, 2024
e430a14
WIP
keynmol Feb 7, 2024
d0eefdd
WIP
keynmol Feb 7, 2024
c0b4e33
Janky snapshot testing
keynmol Feb 8, 2024
ef77aef
Remove macros from generated code
keynmol Feb 8, 2024
823e13f
Make everything work on JS and separate snapshots-runtime
keynmol Feb 8, 2024
94220af
Make Snapshots a SBT plugin and commit some sins
keynmol Feb 8, 2024
af250e4
Remove snapshot diffs if tests are successful
keynmol Feb 8, 2024
a16c907
Properly separate capabilities
keynmol Feb 9, 2024
5b21ac4
Switch to sbt-snapshots
keynmol Feb 11, 2024
662c793
Rebuild snapshots
keynmol Feb 11, 2024
0a02afb
Use Process.stdout on JS
keynmol Feb 14, 2024
ab03719
Clear and return results
keynmol Feb 14, 2024
64fe16a
Update snapshots
keynmol Feb 14, 2024
b5ecb38
Correct handling of finished interactive session
keynmol Feb 14, 2024
c263a4e
Better rendering
keynmol Feb 14, 2024
ee32fe9
Rendering improvements
keynmol Feb 15, 2024
8626253
Introduce prompt chain
keynmol Feb 15, 2024
dde28e5
Simpler prompt chains
keynmol Feb 16, 2024
9919f72
Reorg
keynmol Feb 16, 2024
89df196
remove file
keynmol Feb 16, 2024
bb0bf95
Cats Effect module
keynmol Feb 23, 2024
3e79ec0
fix example running
keynmol Feb 23, 2024
b9f2bd4
Remove JVM module
keynmol Feb 23, 2024
3bc45d6
inline inp
keynmol Feb 23, 2024
3e85848
JVM works!
keynmol Jul 21, 2024
59ad63f
fix
keynmol Jul 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .scalafix.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ OrganizeImports {
importsOrder = Ascii
removeUnused = false
}
OrganizeImports.targetDialect = Scala3
OrganizeImports.removeUnused = false
4 changes: 4 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ fileOverride {
"glob:**/project/**.*" {
runner.dialect = scala212source3
}

"glob:**/snapshots-sbt-plugin/**.*" {
runner.dialect = scala212source3
}
}
121 changes: 82 additions & 39 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import scala.io.StdIn
Global / excludeLintKeys += logManager
Global / excludeLintKeys += scalaJSUseMainModuleInitializer
Global / excludeLintKeys += scalaJSLinkerConfig

inThisBuild(
List(
scalafixDependencies += "com.github.liancheng" %% "organize-imports" % Versions.organizeImports,
semanticdbEnabled := true,
semanticdbVersion := scalafixSemanticdb.revision,
scalafixScalaBinaryVersion := scalaBinaryVersion.value,
organization := "com.indoorvivants",
organizationName := "Anton Sviridov",
resolvers ++= Resolver.sonatypeOssRepos("releases"),
homepage := Some(
url("https://github.com/indoorvivants/scala-library-template")
url("https://github.com/neandertech/proompts")
),
startYear := Some(2023),
licenses := List(
Expand All @@ -29,19 +30,14 @@ inThisBuild(
)

val Versions = new {
val Scala3 = "3.3.1"
val munit = "1.0.0-M7"
val organizeImports = "0.6.0"
val scalaVersions = Seq(Scala3)
val Scala3 = "3.3.3"
val munit = "1.0.0"
val scalaVersions = Seq(Scala3)
val fansi = "0.5.0"
val jna = "5.14.0"
val catsEffect = "3.5.3"
}

// https://github.com/cb372/sbt-explicit-dependencies/issues/27
lazy val disableDependencyChecks = Seq(
unusedCompileDependenciesTest := {},
missinglinkCheck := {},
undeclaredCompileDependenciesTest := {}
)

lazy val munitSettings = Seq(
libraryDependencies += {
"org.scalameta" %%% "munit" % Versions.munit % Test
Expand All @@ -51,7 +47,8 @@ lazy val munitSettings = Seq(
lazy val root = project
.in(file("."))
.aggregate(core.projectRefs*)
.aggregate(docs.projectRefs*)
.aggregate(example.projectRefs*)
// .aggregate(docs.projectRefs*)
.settings(noPublish)

lazy val core = projectMatrix
Expand All @@ -62,34 +59,78 @@ lazy val core = projectMatrix
)
.settings(munitSettings)
.jvmPlatform(Versions.scalaVersions)
.jsPlatform(Versions.scalaVersions, disableDependencyChecks)
.nativePlatform(Versions.scalaVersions, disableDependencyChecks)
.enablePlugins(BuildInfoPlugin)
.jsPlatform(Versions.scalaVersions)
.nativePlatform(Versions.scalaVersions)
.settings(
buildInfoPackage := "com.indoorvivants.library.internal",
buildInfoKeys := Seq[BuildInfoKey](
version,
scalaVersion,
scalaBinaryVersion
),
snapshotsPackageName := "proompts",
snapshotsIntegrations += SnapshotIntegration.MUnit,
scalacOptions += "-Wunused:all",
scalaJSUseMainModuleInitializer := true,
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
libraryDependencies += "com.lihaoyi" %%% "fansi" % "0.4.0"
libraryDependencies += "com.lihaoyi" %%% "fansi" % Versions.fansi,
libraryDependencies +=
"net.java.dev.jna" % "jna" % Versions.jna,
(Compile / unmanagedSourceDirectories) ++= {
val allCombos = List("js", "jvm", "native").combinations(2).toList
val dis =
virtualAxes.value.collectFirst { case p: VirtualAxis.PlatformAxis =>
p.directorySuffix
}.get

allCombos
.filter(_.contains(dis))
.map { suff =>
val suffixes = "scala" + suff.mkString("-", "-", "")

(Compile / sourceDirectory).value / suffixes
}
},
nativeConfig ~= (_.withIncrementalCompilation(true))
)
.enablePlugins(SnapshotsPlugin)

lazy val docs = projectMatrix
.in(file("myproject-docs"))
lazy val catsEffect = projectMatrix
.in(file("modules/cats-effect"))
.defaultAxes(defaults*)
.settings(
name := "cats-effect"
)
.dependsOn(core)
.settings(munitSettings)
.jvmPlatform(Versions.scalaVersions)
.jsPlatform(Versions.scalaVersions)
// .nativePlatform(Versions.scalaVersions, disableDependencyChecks)
.settings(
snapshotsPackageName := "proompts.catseffect",
snapshotsIntegrations += SnapshotIntegration.MUnit,
scalacOptions += "-Wunused:all",
scalaJSUseMainModuleInitializer := true,
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
libraryDependencies += "org.typelevel" %%% "cats-effect" % Versions.catsEffect,
nativeConfig ~= (_.withIncrementalCompilation(true))
)
.enablePlugins(SnapshotsPlugin)

lazy val example = projectMatrix
.dependsOn(core, catsEffect)
.in(file("modules/example"))
.defaultAxes(defaults*)
.enablePlugins(JavaAppPackaging)
.settings(
mdocVariables := Map(
"VERSION" -> version.value
)
name := "example",
noPublish
)
.settings(disableDependencyChecks)
.settings(munitSettings)
.jvmPlatform(Versions.scalaVersions)
.enablePlugins(MdocPlugin)
.settings(noPublish)
.jsPlatform(Versions.scalaVersions)
// .nativePlatform(Versions.scalaVersions, disableDependencyChecks)
.settings(
scalacOptions += "-Wunused:all",
scalaJSUseMainModuleInitializer := true,
Compile / mainClass := Some("example.catseffect.ioExample"),
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
nativeConfig ~= (_.withIncrementalCompilation(true))
)

val noPublish = Seq(
publish / skip := true,
Expand All @@ -110,24 +151,26 @@ val CICommands = Seq(
"clean",
"compile",
"test",
"docs/mdoc",
"scalafmtCheckAll",
"scalafmtSbtCheck",
s"scalafix --check $scalafixRules",
"headerCheck",
"undeclaredCompileDependenciesTest",
"unusedCompileDependenciesTest",
"missinglinkCheck"
"headerCheck"
).mkString(";")

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

addCommandAlias("ci", CICommands)

addCommandAlias("preCI", PrepareCICommands)

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

Global / onChangedBuildSource := ReloadOnSourceChanges
74 changes: 74 additions & 0 deletions modules/cats-effect/src/main/scala/PromptChainIO.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package proompts.catseffect

import cats.effect.*
import proompts.*

case class PromptChainIO[A] private[catseffect] (
init: A,
terminal: Terminal,
out: Output,
colors: Boolean,
reversedSteps: List[
A => IO[A]
]
):
def prompt[R](
nextPrompt: A => Prompt[R] | IO[Prompt[R]],
updateValue: (A, R) => A | IO[A]
) =
val step =
(a: A) =>
lift(nextPrompt)(a).flatMap: prompt =>
eval(prompt): nextResult =>
lift(updateValue.tupled)(a, nextResult)

copy(reversedSteps = step :: reversedSteps)
end prompt

def evaluateIO: IO[A] =
reversedSteps.reverse.foldLeft(IO.pure(init)):
case (acc, step) =>
acc.flatMap(step)
end evaluateIO

private def lift[A, B](f: A => B | IO[B]): A => IO[B] =
a =>
f(a) match
case f: IO[?] => f.asInstanceOf[IO[B]]
case other => IO.pure(other.asInstanceOf[B])

private def eval[T, R](p: Prompt[R])(v: R => IO[T]): IO[T] =
IO.fromFuture(IO(inputProvider.evaluateFuture(handler(p))))
.flatMap(c => check(c)(v))

private def check[T, R](c: Completion[R])(v: R => IO[T]): IO[T] =
c match
case Completion.Interrupted =>
fail("interrupted")
case Completion.Error(msg) =>
fail(msg)
case Completion.Finished(value) =>
v(value)

private lazy val inputProvider = InputProvider(out)
private def handler[R](prompt: Prompt[R]) =
prompt.handler(terminal, out, colors)

private def fail(msg: String) = IO.raiseError(new RuntimeException(msg))

end PromptChainIO

extension (p: PromptChain.type)
def io[A](
init: A,
terminal: Terminal = Terminal.ansi(Output.Std),
out: Output = Output.Std,
colors: Boolean = true
): PromptChainIO[A] =
new PromptChainIO[A](
init = init,
terminal = terminal,
out = out,
colors = colors,
reversedSteps = Nil
)
81 changes: 81 additions & 0 deletions modules/core/src/main/java/ChangeMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2023 Anton Sviridov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package proompts;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Structure;
import java.util.List;
import java.util.Arrays;

public class ChangeMode {

// Define the libc interface
public static interface CLibrary extends Library {
CLibrary INSTANCE = Native.load("c", CLibrary.class);

int tcgetattr(int fd, termios termios);

int tcsetattr(int fd, int optional_actions, termios termios);

int getchar();
}

// Define the termios structure
@Structure.FieldOrder({ "c_iflag", "c_oflag", "c_cflag", "c_lflag", "c_line", "c_cc", "c_ispeed", "c_ospeed" })
public static class termios extends Structure {
public NativeLong c_iflag;
public NativeLong c_oflag;
public NativeLong c_cflag;
public NativeLong c_lflag;
public byte c_line;
public byte[] c_cc = new byte[32];
public NativeLong c_ispeed;
public NativeLong c_ospeed;
}

// Constants
public static final int STDIN_FILENO = 0;
public static final int TCSANOW = 0;
public static final int ICANON = 256;
public static final int ECHO = 0x0008;

// Function to change mode
public static termios oldt = new termios(); // store original termios

public static void changemode(int dir) {
termios newt = new termios();

if (dir == 1) {
CLibrary.INSTANCE.tcgetattr(STDIN_FILENO, oldt); // get current terminal attributes
newt.c_iflag = oldt.c_iflag;
newt.c_oflag = oldt.c_oflag;
newt.c_cflag = oldt.c_cflag;
newt.c_lflag = oldt.c_lflag;
newt.c_line = oldt.c_line;
newt.c_cc = oldt.c_cc;
newt.c_ispeed = oldt.c_ispeed;
newt.c_ospeed = oldt.c_ospeed;

newt.c_lflag.setValue(newt.c_lflag.longValue() & ~(ICANON | ECHO)); // disable canonical mode and echo
CLibrary.INSTANCE.tcsetattr(STDIN_FILENO, TCSANOW, newt); // set new terminal attributes
} else {
CLibrary.INSTANCE.tcsetattr(STDIN_FILENO, TCSANOW, oldt); // restore original terminal attributes
}
}
}
Loading
Loading