-
Notifications
You must be signed in to change notification settings - Fork 145
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #573 from exoego/scala3
Implement Scala 3 support
- Loading branch information
Showing
19 changed files
with
304 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
maxColumn = 140 | ||
align.preset = more | ||
version=2.7.5 | ||
version = 3.6.0 | ||
project.layout = StandardConvention | ||
runner.dialect = scala213source3 | ||
|
||
# Added to minimize changes | ||
docstrings.style = keep |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,8 @@ val repo = "better-files" | |
inThisBuild( | ||
List( | ||
organization.withRank(KeyRanks.Invisible) := "better.files", | ||
homepage := Some(url(s"https://github.com/$username/$repo")), | ||
licenses := List("MIT" -> url(s"https://github.com/$username/$repo/blob/master/LICENSE")), | ||
homepage := Some(url(s"https://github.com/$username/$repo")), | ||
licenses := List("MIT" -> url(s"https://github.com/$username/$repo/blob/master/LICENSE")), | ||
developers := List( | ||
Developer( | ||
id = username, | ||
|
@@ -18,15 +18,15 @@ inThisBuild( | |
) | ||
|
||
lazy val commonSettings = Seq( | ||
organization := s"com.github.$username", | ||
scalaVersion := crossScalaVersions.value.find(_.startsWith("2.12")).get, | ||
crossScalaVersions := Seq("2.11.12", "2.12.13", "2.13.5"), // when you change this line, also change .travis.yml | ||
crossVersion := CrossVersion.binary, | ||
scalacOptions := myScalacOptions(scalaVersion.value, scalacOptions.value), | ||
organization := s"com.github.$username", | ||
scalaVersion := crossScalaVersions.value.find(_.startsWith("2.12")).get, | ||
crossScalaVersions := Seq("2.11.12", "2.12.13", "2.13.5", "3.2.0"), // when you change this line, also change .travis.yml | ||
crossVersion := CrossVersion.binary, | ||
scalacOptions := myScalacOptions(scalaVersion.value, scalacOptions.value), | ||
Compile / doc / scalacOptions += "-groups", | ||
libraryDependencies += Dependencies.scalatest, | ||
Compile / compile := (Compile / compile).dependsOn(formatAll).value, | ||
Test / test := (Test / test).dependsOn(checkFormat).value, | ||
Test / test := (Test / test).dependsOn(checkFormat).value, | ||
formatAll := { | ||
(Compile / scalafmt).value | ||
(Test / scalafmt).value | ||
|
@@ -51,22 +51,32 @@ def myScalacOptions(scalaVersion: String, suggestedOptions: Seq[String]): Seq[St | |
lazy val core = (project in file("core")) | ||
.settings(commonSettings: _*) | ||
.settings( | ||
name := repo, | ||
name := repo, | ||
description := "Simple, safe and intuitive I/O in Scala", | ||
libraryDependencies ++= Seq( | ||
Dependencies.scalaReflect(scalaVersion.value), | ||
Dependencies.commonsio, | ||
Dependencies.fastjavaio, | ||
Dependencies.shapeless | ||
) | ||
Dependencies.fastjavaio | ||
) ++ (CrossVersion.partialVersion(scalaVersion.value) match { | ||
case Some((2, _)) => | ||
Seq( | ||
Dependencies.shapeless, | ||
Dependencies.scalaReflect(scalaVersion.value) | ||
) | ||
case _ => Seq.empty | ||
}) | ||
) | ||
|
||
lazy val akka = (project in file("akka")) | ||
.settings(commonSettings: _*) | ||
.settings( | ||
name := s"$repo-akka", | ||
name := s"$repo-akka", | ||
description := "Reactive file watcher using Akka actors", | ||
libraryDependencies += Dependencies.akka | ||
libraryDependencies += (CrossVersion.partialVersion(scalaVersion.value) match { | ||
// scala-steward:off | ||
case Some((2, 11)) => "com.typesafe.akka" %% "akka-actor" % "2.5.32" | ||
// scala-steward:on | ||
case _ => Dependencies.akka | ||
}) | ||
) | ||
.dependsOn(core % "test->test;compile->compile") | ||
|
||
|
@@ -80,12 +90,12 @@ lazy val root = (project in file(".")) | |
.aggregate(core, akka) | ||
|
||
lazy val docSettings = Seq( | ||
autoAPIMappings := true, | ||
autoAPIMappings := true, | ||
ScalaUnidoc / unidoc / unidocProjectFilter := inProjects(core, akka), | ||
siteSourceDirectory := baseDirectory.value / "site", | ||
ScalaUnidoc / siteSubdirName := "latest/api", | ||
siteSourceDirectory := baseDirectory.value / "site", | ||
ScalaUnidoc / siteSubdirName := "latest/api", | ||
addMappingsToSiteDir(ScalaUnidoc / packageDoc / mappings, ScalaUnidoc / siteSubdirName), | ||
git.remoteRepo := s"[email protected]:$username/$repo.git", | ||
git.remoteRepo := s"[email protected]:$username/$repo.git", | ||
ghpagesPushSite / envVars += ("SBT_GHPAGES_COMMIT_MESSAGE" -> s"Publishing Scaladoc [CI SKIP]") | ||
) | ||
|
||
|
90 changes: 90 additions & 0 deletions
90
core/src/main/scala-2/better/files/ResourceScalaCompat.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package better.files | ||
|
||
import scala.language.experimental.macros | ||
import scala.reflect.macros.{ReificationException, blackbox} | ||
|
||
private[files] trait ResourceScalaCompat { | ||
|
||
/** Look up class resource files. | ||
* | ||
* This Resource looks up resources relative to the JVM class file for `T`, | ||
* using [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]]. | ||
* For example, if `com.example.ExampleClass` is given for `T`, then resource files will be searched for in the `com/example` folder containing `ExampleClass.class`. | ||
* | ||
* If you want to look up resource files relative to the call site instead (that is, you want a class to look up one of its own resources), use the `my` method instead. | ||
* | ||
* @example {{{ Resource.at[YourClass].url("config.properties") }}} | ||
* @tparam T The class, trait, or object to look up from. Objects must be written with a `.type` suffix, such as `Resource.at[SomeObject.type]`. | ||
* @return A Resource for `T`. | ||
* @see [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]] | ||
*/ | ||
def at[T]: Resource = macro Macros.atStaticImpl[T] | ||
|
||
/** Look up class resource files. | ||
* | ||
* This Resource looks up resources from the given Class, | ||
* using [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]]. | ||
* For example, if `classOf[com.example.ExampleClass]` is given for `clazz`, then resource files will be searched for | ||
* in the `com/example` folder containing `ExampleClass.class`. | ||
* | ||
* If you want to look up resource files relative to the call site instead (that is, you want your class to look up one of its own resources), | ||
* use the `my` method instead. | ||
* | ||
* @example {{{ Resource.at(Class.forName("your.AppClass")).url("config.properties") }}} | ||
* | ||
* In this example, a file named `config.properties` is expected to appear alongside the file `AppClass.class` in the package `your`. | ||
* @param clazz The class to look up from. | ||
* @return A Resource for `clazz`. | ||
* @see [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]] | ||
*/ | ||
def at(clazz: Class[_]): Resource = macro Macros.atDynamicImpl | ||
|
||
/** Look up own resource files. | ||
* | ||
* This Resource looks up resources from the [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html Class]] surrounding the call, | ||
* using [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]]. | ||
* For example, if `my` is called from `com.example.ExampleClass`, | ||
* then resource files will be searched for in the `com/example` folder containing `ExampleClass.class`. | ||
* | ||
* @example {{{ Resource.my.url("config.properties") }}} | ||
* @return A Resource for the call site. | ||
* @see [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]] | ||
*/ | ||
def my: Resource = macro Macros.myImpl | ||
} | ||
|
||
/** Implementations of the `Resource.at` macros. This is needed because `Class#getResource` is caller-sensitive; | ||
* calls to it must appear in user code, ''not'' in better-files. | ||
*/ | ||
private[files] final class Macros(val c: blackbox.Context) { | ||
|
||
import c.universe._ | ||
import c.Expr | ||
|
||
def atStaticImpl[T](implicit T: WeakTypeTag[T]): Expr[Resource] = { | ||
val rtc = Expr[Class[_]] { | ||
try { | ||
c.reifyRuntimeClass(T.tpe, concrete = true) | ||
} catch { | ||
case _: ReificationException => c.abort(c.enclosingPosition, s"${T.tpe} is not a concrete type") | ||
} | ||
} | ||
atDynamicImpl(rtc) | ||
} | ||
|
||
def atDynamicImpl(clazz: Expr[Class[_]]): Expr[Resource] = | ||
reify { | ||
new Resource { | ||
override def url(name: String) = Option(clazz.splice.getResource(name)) | ||
} | ||
} | ||
|
||
def myImpl: Expr[Resource] = { | ||
val rtc = c.reifyEnclosingRuntimeClass | ||
if (rtc.isEmpty) { | ||
// The documentation for reifyEnclosingRuntimeClass claims that this is somehow possible!? | ||
c.abort(c.enclosingPosition, "this location doesn't correspond to a Java class file") | ||
} | ||
atDynamicImpl(Expr[Class[_]](rtc)) | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
core/src/main/scala-3/better/files/ResourceScalaCompat.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package better.files | ||
|
||
import scala.quoted.{Quotes, Expr} | ||
import scala.quoted.* | ||
|
||
private[files] trait ResourceScalaCompat { | ||
|
||
/** Look up class resource files. | ||
* | ||
* This Resource looks up resources relative to the JVM class file for `T`, using | ||
* [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]]. For example, if | ||
* `com.example.ExampleClass` is given for `T`, then resource files will be searched for in the `com/example` folder containing | ||
* `ExampleClass.class`. | ||
* | ||
* If you want to look up resource files relative to the call site instead (that is, you want a class to look up one of its own | ||
* resources), use the `my` method instead. | ||
* | ||
* @example | ||
* {{{Resource.at[YourClass].url("config.properties")}}} | ||
* @tparam T | ||
* The class, trait, or object to look up from. Objects must be written with a `.type` suffix, such as `Resource.at[SomeObject.type]`. | ||
* @return | ||
* A Resource for `T`. | ||
* @see | ||
* [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]] | ||
*/ | ||
inline def at[T]: Resource = ${ Macros.atStaticImpl[T] } | ||
|
||
/** Look up class resource files. | ||
* | ||
* This Resource looks up resources from the given Class, using | ||
* [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]]. For example, if | ||
* `classOf[com.example.ExampleClass]` is given for `clazz`, then resource files will be searched for in the `com/example` folder | ||
* containing `ExampleClass.class`. | ||
* | ||
* If you want to look up resource files relative to the call site instead (that is, you want your class to look up one of its own | ||
* resources), use the `my` method instead. | ||
* | ||
* @example | ||
* {{{Resource.at(Class.forName("your.AppClass")).url("config.properties")}}} | ||
* | ||
* In this example, a file named `config.properties` is expected to appear alongside the file `AppClass.class` in the package `your`. | ||
* @param clazz | ||
* The class to look up from. | ||
* @return | ||
* A Resource for `clazz`. | ||
* @see | ||
* [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]] | ||
*/ | ||
def at(clazz: Class[_]): Resource = new Resource { | ||
override def url(name: String) = Option(clazz.getResource(name)) | ||
} | ||
|
||
/** Look up own resource files. | ||
* | ||
* This Resource looks up resources from the [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html Class]] surrounding the | ||
* call, using [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]]. For | ||
* example, if `my` is called from `com.example.ExampleClass`, then resource files will be searched for in the `com/example` folder | ||
* containing `ExampleClass.class`. | ||
* | ||
* @example | ||
* {{{Resource.my.url("config.properties")}}} | ||
* @return | ||
* A Resource for the call site. | ||
* @see | ||
* [[https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html#getResource(java.lang.String) Class#getResource]] | ||
*/ | ||
inline def my: Resource = ${ Macros.myImpl } | ||
} | ||
|
||
private[files] object Macros { | ||
|
||
def atStaticImpl[T: Type](using qc: Quotes): Expr[Resource] = { | ||
import qc.reflect.* | ||
val tpe = TypeRepr.of[T] | ||
val typeSymbolStr = tpe.typeSymbol.toString | ||
if (typeSymbolStr.startsWith("class ") || typeSymbolStr.startsWith("module class ")) { | ||
val baseClass = tpe.baseClasses.head | ||
return '{ | ||
new Resource { | ||
override def url(name: String) = Option( | ||
Class | ||
.forName(${ | ||
Expr(baseClass.fullName) | ||
}) | ||
.getResource(name) | ||
) | ||
} | ||
} | ||
} else { | ||
report.errorAndAbort(s"${tpe.show} is not a concrete type") | ||
} | ||
} | ||
|
||
def myImpl(using qc: Quotes): Expr[Resource] = { | ||
import qc.reflect.* | ||
var callee = Symbol.spliceOwner | ||
while (callee != null && callee != Symbol.noSymbol) { | ||
callee = callee.owner | ||
if (callee.isClassDef) { | ||
return '{ | ||
new Resource { | ||
override def url(name: String) = Option( | ||
Class | ||
.forName(${ | ||
Expr(callee.fullName) | ||
}) | ||
.getResource(name) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
report.errorAndAbort("this location doesn't correspond to a Java class file") | ||
} | ||
} |
Oops, something went wrong.