diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | build/build.scala | 1 | ||||
-rw-r--r-- | examples/scalatex-example/Foo.scala | 4 | ||||
-rw-r--r-- | examples/scalatex-example/docs/build/build.scala | 14 | ||||
-rw-r--r-- | examples/scalatex-example/docs/readme.scalatex | 21 | ||||
-rw-r--r-- | examples/scalatex-example/docs/static/style.css | 32 | ||||
-rw-r--r-- | libraries/scalatex/AbstractMain.scala | 34 | ||||
-rw-r--r-- | libraries/scalatex/build/build.scala | 16 | ||||
-rw-r--r-- | libraries/scalatex/lib.scala | 76 | ||||
-rw-r--r-- | stage2/libraries.scala | 3 | ||||
-rw-r--r-- | stage2/plugins/Scalatex.scala | 77 |
11 files changed, 278 insertions, 1 deletions
@@ -18,3 +18,4 @@ examples/scalajs-react-example/server/public/generated .cbt-loop.tmp test/simple/src_generated examples/scalapb-example/src_generated +ScalatexGenerated.scala diff --git a/build/build.scala b/build/build.scala index c596245..cf2527a 100644 --- a/build/build.scala +++ b/build/build.scala @@ -25,6 +25,7 @@ class Build(val context: Context) extends Shared with Scalariform with PublishLo context.cbtHome / "stage2" / "DirectoryDependency.scala", context.cbtHome / "stage2" / "LazyDependency.scala", context.cbtHome / "stage2" / "plugins" / "ScalaTest.scala", + context.cbtHome / "stage2" / "plugins" / "Scalatex.scala", context.cbtHome / "stage2" / "libraries.scala", context.cbtHome / "stage2" / "plugins.scala", context.cbtHome / "stage2" / "ports.scala" diff --git a/examples/scalatex-example/Foo.scala b/examples/scalatex-example/Foo.scala new file mode 100644 index 0000000..e8bfc40 --- /dev/null +++ b/examples/scalatex-example/Foo.scala @@ -0,0 +1,4 @@ +package cbt_example.scalatex +object Foo{ + def foo(i: Int, j: Int) = i * j + i * j +} diff --git a/examples/scalatex-example/docs/build/build.scala b/examples/scalatex-example/docs/build/build.scala new file mode 100644 index 0000000..51b158f --- /dev/null +++ b/examples/scalatex-example/docs/build/build.scala @@ -0,0 +1,14 @@ +package cbt_examples.scalatex_example_build +import cbt._ +import java.io._ +import java.nio.file._ +import java.nio.file.attribute._ +class Build(val context: Context) extends Scalatex{ + override def dependencies = super.dependencies ++ Seq( + DirectoryDependency( projectDirectory / ".." ) + ) + override def run = { + scalatex.apply + super.run + } +} diff --git a/examples/scalatex-example/docs/readme.scalatex b/examples/scalatex-example/docs/readme.scalatex new file mode 100644 index 0000000..11f432e --- /dev/null +++ b/examples/scalatex-example/docs/readme.scalatex @@ -0,0 +1,21 @@ +@import scalatags.Text.all._ +@h1("cbt.http.client") +@p + @ul + @li + http client library + @li + super simple and easy to use + @li + type-safe and resource-leak-safe + @li + no dependencies besides JDK + @li + complete (offers everything @pre(`class`:="inline"){java.net.URLConnection} does) + @repl + cbt_example.scalatex.Foo.foo(1,2) + +@comment{ + @h3 + Usage example +}
\ No newline at end of file diff --git a/examples/scalatex-example/docs/static/style.css b/examples/scalatex-example/docs/static/style.css new file mode 100644 index 0000000..36547e0 --- /dev/null +++ b/examples/scalatex-example/docs/static/style.css @@ -0,0 +1,32 @@ +@import url('https://fonts.googleapis.com/css?family=Muli'); +body{ + font-family: 'Muli', sans-serif; + width: 600px; + left: 50%; + margin-left: -300px; + position: absolute; +} + +h1, h2, h3, h4, h5, h6{ + color: #007bbf; +} + +.path-readme li{ + font-size: 30px; +} + +pre{ + display: block; + border-top: 0px; + border-bottom: 0px; + border-left: 0; + border-right: 0; + border-color: #007bbf; + border-style: solid; + padding: 5px 0; +} + +pre.inline{ + display: inline; + border: 0; +} diff --git a/libraries/scalatex/AbstractMain.scala b/libraries/scalatex/AbstractMain.scala new file mode 100644 index 0000000..ab876d9 --- /dev/null +++ b/libraries/scalatex/AbstractMain.scala @@ -0,0 +1,34 @@ +package cbt.plugins.scalatex.runtime + +import java.io.File +import java.nio.file.{ Files, Paths } +import scalatags.Text.all._ +trait AbstractMain{ + def dependencyClasspath: Seq[File] + def files: Seq[(String,SeqFrag[Frag])] + def htmlTarget: File + def render: SeqFrag[Frag] => String => String + def main(args: Array[String]): Unit = { + val changed = files.flatMap{ + case (name, contents) => + val path = htmlTarget.toPath.resolve(name + ".html") + import Files._ + createDirectories(path.getParent) + val newContents = render(contents)(name) + if( !exists(path) || new String( Files.readAllBytes( path ) ) != newContents ){ + write( path, newContents.getBytes ) + Some( path ) + } else None + } + if( args.lift(0).map(_=="open").getOrElse(false) ) + changed.headOption.foreach{ file => + System.out.println(file) + val old = Option(System.getProperty("apple.awt.UIElement")) + System.setProperty("apple.awt.UIElement", "true") + java.awt.Desktop.getDesktop().open(file.toFile) + old.foreach(System.setProperty("apple.awt.UIElement", _)) + } + else System.out.println(changed.mkString("\\n")) + } + val repl = new cbt.plugins.scalatex.runtime.repl( this.getClass.getClassLoader, dependencyClasspath: _* ) +} diff --git a/libraries/scalatex/build/build.scala b/libraries/scalatex/build/build.scala new file mode 100644 index 0000000..0ca9b76 --- /dev/null +++ b/libraries/scalatex/build/build.scala @@ -0,0 +1,16 @@ +package scalatex_build +import cbt._ +class Build(val context: Context) extends BaseBuild{ + override def dependencies = ( + super.dependencies ++ // don't forget super.dependencies here for scala-library, etc. + Seq( + libraries.cbt.eval + ) ++ + Resolver( mavenCentral, sonatypeReleases ).bind( + ScalaDependency( "com.lihaoyi", "scalatex-api", "0.3.7" ), + ScalaDependency( "com.lihaoyi", "scalatex-site", "0.3.7" ), + ScalaDependency("org.cvogt","scala-extensions","0.5.0"), + ScalaDependency("org.scalameta", "scalameta", "1.6.0") + ) + ) +} diff --git a/libraries/scalatex/lib.scala b/libraries/scalatex/lib.scala new file mode 100644 index 0000000..018329a --- /dev/null +++ b/libraries/scalatex/lib.scala @@ -0,0 +1,76 @@ +package cbt.plugins.scalatex.runtime + +import scalatags.Text.TypedTag +import scalatags.Text.all._ + +import cbt.eval.Eval +import java.net._ +import java.io._ +import org.cvogt.scala._ + +object hl extends scalatex.site.Highlighter + +object `package`{ + def comment(any: =>Any): Unit = () + def plain = span(fontFamily:="monospace") + def highlight(code: String) = + raw( """<pre class="highlight language-scala"><code>""" ++ code.stripIndent.trim ++ "</code></pre>" ) + def layout: SeqFrag[Frag] => String => String = { contents => path => + import scalatex._ + import scalatags.Text.tags2._ + import java.nio.file.{Paths, Files} + import scalatags.stylesheet._ + val parts = path.split(File.separator) + val base = ("../" * (parts.size - 1)).mkString + val classes = parts.inits.toList.reverse.drop(1).map( parts => "path-" ++ parts.mkString("-") ).mkString(" ") + html( + head( + link(rel:="stylesheet", `type`:="text/css", href := base+"style.css") + ), + body( + `class` := classes, + raw("\n"), contents, raw("\n") + ) + ).render + } +} + +class repl(classLoader: ClassLoader, dependencyClasspath: java.io.File*) { repl => + private val eval = new Eval(){ + override lazy val impliedClassPath: List[String] = dependencyClasspath.map(_.toString).to + override def classLoader = repl.classLoader + } + /** + * repl session, inspired by tut. + * + * Example: code="1 + 1" returns + * "scala> 1 + 1 + * res0: Int = 2" + */ + def apply(code: String) = { + // adapted from https://github.com/olafurpg/scalafmt/blob/a12141b5ce285a60886abab0d96044c617e20b51/readme/Readme.scala + import scala.meta._ + import scala.util._ + val expressions = s"{$code}".parse[Stat].get.asInstanceOf[Term.Block].stats + val output = Try(eval[Any](code)) match { + case Success( value ) => "res0: " ++ value.getClass.getName ++ " = " ++ (value match { + case v: String => "\"" ++ v ++ "\"" + case other => other.toString + }) + case Failure( e ) => e.getMessage + } + + val lineSeparator = String.format("%n") + val prefix = code.lines.filterNot("\t ".contains).mkString(lineSeparator).commonLinePrefix + val result = ( + expressions + .map(prefix ++ _.toString) + .map("scala> " ++ _.stripIndent.prefixLines(" ").trimLeft) + .mkString("\n") + ++ + "\n" ++ output.trim + ) + + hl.scala(result) + } +} diff --git a/stage2/libraries.scala b/stage2/libraries.scala index ceafd75..08a7a74 100644 --- a/stage2/libraries.scala +++ b/stage2/libraries.scala @@ -15,8 +15,9 @@ class libraries( context: Context, scalaVersion: String, scalaMajorVersion: Stri def file = dep( "file" ) def interfaces = dep( "interfaces" ) def proguard = dep( "proguard" ) - def scalatestRunner = dep( "scalatest-runner" ) def reflect = dep( "reflect" ) + def scalatestRunner = dep( "scalatest-runner" ) + def scalatex = dep( "scalatex" ) } object scala { private def _maven = ( g: String, a: String, v: String ) => { diff --git a/stage2/plugins/Scalatex.scala b/stage2/plugins/Scalatex.scala new file mode 100644 index 0000000..6276332 --- /dev/null +++ b/stage2/plugins/Scalatex.scala @@ -0,0 +1,77 @@ +package cbt +import java.io.File +import java.nio.file.Files +trait Scalatex extends BaseBuild { + override def dependencies = super.dependencies :+ libraries.cbt.scalatex + + def scalatex = ( + Scalatex.apply( + lib, + Math.max( context.cbtLastModified, ( dependencies ++ context.parentBuild ).map( _.lastModified ).max ) + ) + .config( + lib.autoRelative( + lib.sourceFiles( sources, _.string endsWith ".scalatex" ) + ++ projectDirectory.listFiles.toVector.filter( _.toString.endsWith( ".scalatex" ) ) + ).map { + case ( file, name ) => file -> name.stripPrefix( File.separator ).stripSuffix( ".scalatex" ) + }, + lib.autoRelative( Seq( projectDirectory / "static" ) ), + target / "html", + projectDirectory / "src_generated" / "scalatex", + dependencyClasspath + ) + ) +} +/** @param htmlTarget directory where the html files are generated + */ +object Scalatex { + case class apply( + lib: Lib, parentsLastModified: Long + )( + implicit + logger: Logger, transientCache: java.util.Map[AnyRef, AnyRef], classLoaderCache: cbt.ClassLoaderCache + ) { + case class config( + scalatexFiles: Seq[( File, String )], + staticFiles: Seq[( File, String )], + htmlTarget: File, + srcGenerated: File, + dependencyClasspath: ClassPath, + transform: String = "cbt.plugins.scalatex.runtime.layout", // function String => String + linkStaticFile: Boolean = true, + `package`: String = "cbt.plugins.scalatex.runtime" + ) { + def apply = { + staticFiles.map { + case ( file, relative ) => file -> htmlTarget / relative + }.collect { + case ( from, to ) if from.isDirectory => to.mkdirs + case ( from, to ) if !from.isDirectory && !to.exists => Files.createLink( to.toPath, from.toPath ) + } + lib.cached( + srcGenerated, + ( parentsLastModified +: scalatexFiles.map( _._1 ).map( _.lastModified ) ).max + ) { () => + lib.writeIfChanged( + srcGenerated / "ScalatexGenerated.scala", + s"""// Last generated at ${java.time.ZonedDateTime.now} +package ${`package`} +object ScalatexGenerate extends cbt.plugins.scalatex.runtime.AbstractMain{ + def dependencyClasspath: Seq[java.io.File] = Seq( + ${dependencyClasspath.files.map( _.quote ).mkString( ",\n " )} + ) + def htmlTarget = ${htmlTarget.quote} + import scalatags.Text.all._ + def files = Seq[(String, scalatags.Text.all.SeqFrag[scalatags.Text.all.Frag])]( + ${scalatexFiles.map { case ( from, to ) => s"${to.quote} -> scalatex.twf(${from.string.quote})" }.mkString( ",\n " )} + ) + def render: scalatags.Text.all.SeqFrag[scalatags.Text.all.Frag] => String => String = $transform +} +""".trim ++ "\n" + ) + } + } + } + } +} |