From 7ea50a2aacf3da427118a9af331d02480ffc073e Mon Sep 17 00:00:00 2001 From: Iurii Malchenko Date: Sun, 23 May 2021 06:12:49 +0300 Subject: [PATCH] compat layer for scala 2.12 --- .github/workflows/ci.yml | 12 ++++- build.sbt | 7 ++- project/ScalaOptions.scala | 11 ++--- project/ScalaVersions.scala | 1 + .../app/tulz/diff/compat/IndexedSeqView.scala | 28 +++++++++++ .../diff/compat/IndexedSeqViewOfCharOps.scala | 9 ++++ .../compat/IndexedSeqViewOfStringOps.scala | 9 ++++ .../app/tulz/diff/compat/package.scala | 24 ++++++++++ .../app/tulz/diff/compat/package.scala | 7 +++ .../app/tulz/diff/compat/package.scala | 7 +++ .../main/scala/app/tulz/diff/MyersDiff.scala | 4 +- .../scala/app/tulz/diff/MyersInterpret.scala | 30 ++++++------ .../main/scala/app/tulz/diff/SeqDiff.scala | 3 +- .../main/scala/app/tulz/diff/StringDiff.scala | 3 +- .../main/scala/app/tulz/diff/TokenDiff.scala | 8 ++-- .../app/tulz/diff/util/DiffCollapse.scala | 17 ++++--- .../scala/app/tulz/diff/util/DiffLog.scala | 9 ++-- .../app/tulz/diff/util/DiffPrettier.scala | 1 + .../app/tulz/diff/util/DiffTokenize.scala | 47 ++++++++++--------- .../scala/app/tulz/diff/util/ListScan.scala | 2 +- 20 files changed, 166 insertions(+), 73 deletions(-) create mode 100644 stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqView.scala create mode 100644 stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfCharOps.scala create mode 100644 stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfStringOps.scala create mode 100644 stringdiff/src/main/scala-2.12/app/tulz/diff/compat/package.scala create mode 100644 stringdiff/src/main/scala-2.13/app/tulz/diff/compat/package.scala create mode 100644 stringdiff/src/main/scala-3/app/tulz/diff/compat/package.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d85d99..9c3f67e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.13.6, 3.0.0] + scala: [2.12.13, 2.13.6, 3.0.0] java: [openjdk@1.11.0] runs-on: ${{ matrix.os }} steps: @@ -101,6 +101,16 @@ jobs: ~/Library/Caches/Coursier/v1 key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + - name: Download target directories (2.12.13) + uses: actions/download-artifact@v2 + with: + name: target-${{ matrix.os }}-2.12.13-${{ matrix.java }} + + - name: Inflate target directories (2.12.13) + run: | + tar xf targets.tar + rm targets.tar + - name: Download target directories (2.13.6) uses: actions/download-artifact@v2 with: diff --git a/build.sbt b/build.sbt index b188b29..b3d3285 100644 --- a/build.sbt +++ b/build.sbt @@ -8,6 +8,7 @@ inThisBuild( scalaVersion := ScalaVersions.v213, description := "String diff for Scala", crossScalaVersions := Seq( + ScalaVersions.v212, ScalaVersions.v213, ScalaVersions.v3 ), @@ -22,7 +23,8 @@ inThisBuild( "PGP_SECRET" -> s"$${{ secrets.PGP_SECRET }}", "SONATYPE_PASSWORD" -> s"$${{ secrets.SONATYPE_PASSWORD }}", "SONATYPE_USERNAME" -> s"$${{ secrets.SONATYPE_USERNAME }}" - )) + )), + versionScheme := Some("early-semver") ) ) @@ -39,7 +41,8 @@ lazy val stringdiff = .settings( ScalaOptions.fixOptions, libraryDependencies ++= Seq( - "org.scalatest" %%% "scalatest" % "3.2.9" % Test + "org.scala-lang.modules" %%% "scala-collection-compat" % "2.4.4", + "org.scalatest" %%% "scalatest" % "3.2.9" % Test ) ) diff --git a/project/ScalaOptions.scala b/project/ScalaOptions.scala index ab94c9f..954d45e 100644 --- a/project/ScalaOptions.scala +++ b/project/ScalaOptions.scala @@ -12,14 +12,9 @@ object ScalaOptions { "-Wunused:params" ) )), - scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, _)) => - Seq( - "-Ymacro-annotations" - ) - case Some((3, _)) => Seq() - case _ => Seq() - }), + scalacOptions ++= CrossVersion.partialVersion(scalaVersion.value).collect { case (2, 13) => + "-Ymacro-annotations" + }, (Compile / doc / scalacOptions) ~= (_.filterNot( Set( "-scalajs", diff --git a/project/ScalaVersions.scala b/project/ScalaVersions.scala index 9df0604..53de1b3 100644 --- a/project/ScalaVersions.scala +++ b/project/ScalaVersions.scala @@ -1,4 +1,5 @@ object ScalaVersions { + val v212 = "2.12.13" val v213 = "2.13.6" val v3 = "3.0.0" } diff --git a/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqView.scala b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqView.scala new file mode 100644 index 0000000..5baeb03 --- /dev/null +++ b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqView.scala @@ -0,0 +1,28 @@ +package app.tulz.diff +package compat + +import scala.collection.SeqView +import scala.collection.immutable.Range + +class IndexedSeqView[A]( + private[compat] val underlying: SeqView[A, _] +) { + def apply(index: Int): A = underlying(index) + + def size: Int = underlying.length + + def isEmpty: Boolean = underlying.isEmpty + + def concat(other: IndexedSeqView[A]): IndexedSeqView[A] = + new IndexedSeqView((underlying ++ other.underlying).view) + + def take(n: Int): IndexedSeqView[A] = new IndexedSeqView(underlying.take(n)) + + def slice(from: Int, until: Int): IndexedSeqView[A] = new IndexedSeqView(underlying.slice(from, until)) + + def toIndexedSeq: IndexedSeq[A] = underlying.toIndexedSeq + + def indices: Range = underlying.indices + + def forall(predicate: A => Boolean): Boolean = underlying.forall(predicate) +} diff --git a/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfCharOps.scala b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfCharOps.scala new file mode 100644 index 0000000..60462d8 --- /dev/null +++ b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfCharOps.scala @@ -0,0 +1,9 @@ +package app.tulz.diff.compat + +class IndexedSeqViewOfCharOps( + underlying: IndexedSeqView[Char] +) { + + def mkString: String = underlying.underlying.mkString + +} diff --git a/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfStringOps.scala b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfStringOps.scala new file mode 100644 index 0000000..3cbd4bc --- /dev/null +++ b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/IndexedSeqViewOfStringOps.scala @@ -0,0 +1,9 @@ +package app.tulz.diff.compat + +class IndexedSeqViewOfStringOps( + underlying: IndexedSeqView[String] +) { + + def mkString: String = underlying.underlying.mkString + +} diff --git a/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/package.scala b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/package.scala new file mode 100644 index 0000000..239e657 --- /dev/null +++ b/stringdiff/src/main/scala-2.12/app/tulz/diff/compat/package.scala @@ -0,0 +1,24 @@ +package app.tulz.diff + +import scala.collection.SeqView +import scala.language.implicitConversions + +package object compat { + + implicit def indexedSeq_ViewToIndexedSeqView[A]( + underlying: SeqView[A, IndexedSeq[A]] + ): IndexedSeqView[A] = new IndexedSeqView[A](underlying) + + implicit def string_ViewToIndexedSeqView( + underlying: SeqView[Char, String] + ): IndexedSeqView[Char] = new IndexedSeqView[Char](underlying) + + implicit def indexedSeqViewOfCharOps( + underlying: IndexedSeqView[Char] + ): IndexedSeqViewOfCharOps = new IndexedSeqViewOfCharOps(underlying) + + implicit def indexedSeqViewOfStringOps( + underlying: IndexedSeqView[String] + ): IndexedSeqViewOfStringOps = new IndexedSeqViewOfStringOps(underlying) + +} diff --git a/stringdiff/src/main/scala-2.13/app/tulz/diff/compat/package.scala b/stringdiff/src/main/scala-2.13/app/tulz/diff/compat/package.scala new file mode 100644 index 0000000..6cb4b64 --- /dev/null +++ b/stringdiff/src/main/scala-2.13/app/tulz/diff/compat/package.scala @@ -0,0 +1,7 @@ +package app.tulz.diff + +package object compat { + + type IndexedSeqView[+A] = scala.collection.IndexedSeqView[A] + +} diff --git a/stringdiff/src/main/scala-3/app/tulz/diff/compat/package.scala b/stringdiff/src/main/scala-3/app/tulz/diff/compat/package.scala new file mode 100644 index 0000000..6cb4b64 --- /dev/null +++ b/stringdiff/src/main/scala-3/app/tulz/diff/compat/package.scala @@ -0,0 +1,7 @@ +package app.tulz.diff + +package object compat { + + type IndexedSeqView[+A] = scala.collection.IndexedSeqView[A] + +} diff --git a/stringdiff/src/main/scala/app/tulz/diff/MyersDiff.scala b/stringdiff/src/main/scala/app/tulz/diff/MyersDiff.scala index c283384..a6dadad 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/MyersDiff.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/MyersDiff.scala @@ -1,6 +1,6 @@ package app.tulz.diff -import scala.collection.IndexedSeqView +import compat._ object MyersDiff { @@ -79,7 +79,7 @@ object MyersDiff { } } } - rec(ss1.toIndexedSeq.view, ss2.toIndexedSeq.view, 0, 0) + rec(ss1, ss2, 0, 0) } sealed trait Operation extends Product with Serializable diff --git a/stringdiff/src/main/scala/app/tulz/diff/MyersInterpret.scala b/stringdiff/src/main/scala/app/tulz/diff/MyersInterpret.scala index 35e5ed1..fa150b2 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/MyersInterpret.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/MyersInterpret.scala @@ -3,7 +3,7 @@ package app.tulz.diff import app.tulz.diff.MyersDiff.Operation import scala.collection.mutable.ListBuffer -import scala.collection.IndexedSeqView +import compat._ object MyersInterpret { @@ -23,13 +23,13 @@ object MyersInterpret { (Start +: ops).zip(ops :+ End).foreach { case (Start, Delete(deleteFrom, deleteCount)) => if (deleteFrom > 0) { - buffer.addOne( + buffer.append( InBoth( s1.take(deleteFrom) ) ) } - buffer.addOne( + buffer.append( InFirst( s1.slice(deleteFrom, deleteFrom + deleteCount) ) @@ -37,13 +37,13 @@ object MyersInterpret { case (Start, Insert(insertAt, insertFrom, insertCount)) => if (insertAt > 0) { - buffer.addOne( + buffer.append( InBoth( s1.take(insertFrom) ) ) } - buffer.addOne( + buffer.append( InSecond( s2.slice(insertFrom, insertFrom + insertCount) ) @@ -51,13 +51,13 @@ object MyersInterpret { case (Delete(deleteFrom, deleteCount), Insert(insertAt, insertFrom, insertCount)) => if (insertAt > deleteFrom + deleteCount) { - buffer.addOne( + buffer.append( InBoth( s1.slice(deleteFrom + deleteCount, insertAt) ) ) } - buffer.addOne( + buffer.append( InSecond( s2.slice(insertFrom, insertFrom + insertCount) ) @@ -65,13 +65,13 @@ object MyersInterpret { case (Insert(insertAt, _, _), Delete(deleteFrom, deleteCount)) => if (deleteFrom > insertAt) { - buffer.addOne( + buffer.append( InBoth( s1.slice(insertAt, deleteFrom) ) ) } - buffer.addOne( + buffer.append( InFirst( s1.slice(deleteFrom, deleteFrom + deleteCount) ) @@ -79,13 +79,13 @@ object MyersInterpret { case (Insert(insertAt1, _, _), Insert(insertAt2, insertFrom2, insertCount2)) => if (insertAt2 > insertAt1) { - buffer.addOne( + buffer.append( InBoth( s1.slice(insertAt1, insertAt2) ) ) } - buffer.addOne( + buffer.append( InSecond( s2.slice(insertFrom2, insertFrom2 + insertCount2) ) @@ -93,13 +93,13 @@ object MyersInterpret { case (Delete(deleteFrom1, deleteCount1), Delete(deleteFrom2, deleteCount2)) => if (deleteFrom2 > deleteFrom1 + deleteCount1) { - buffer.addOne( + buffer.append( InBoth( s1.slice(deleteFrom1 + deleteCount1, deleteFrom2) ) ) } - buffer.addOne( + buffer.append( InFirst( s1.slice(deleteFrom2, deleteFrom2 + deleteCount2) ) @@ -107,7 +107,7 @@ object MyersInterpret { case (Insert(insertAt, _, _), End) => if (s1.size > insertAt) { - buffer.addOne( + buffer.append( InBoth( s1.slice(insertAt, s1.size) ) @@ -116,7 +116,7 @@ object MyersInterpret { case (Delete(deleteFrom, deleteCount), End) => if (s1.size > deleteFrom + deleteCount) { - buffer.addOne( + buffer.append( InBoth( s1.slice(deleteFrom + deleteCount, s1.size) ) diff --git a/stringdiff/src/main/scala/app/tulz/diff/SeqDiff.scala b/stringdiff/src/main/scala/app/tulz/diff/SeqDiff.scala index 1a675ef..08d9531 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/SeqDiff.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/SeqDiff.scala @@ -1,8 +1,7 @@ package app.tulz.diff import app.tulz.diff.util.DiffCollapse - -import scala.collection.IndexedSeqView +import compat._ object SeqDiff { diff --git a/stringdiff/src/main/scala/app/tulz/diff/StringDiff.scala b/stringdiff/src/main/scala/app/tulz/diff/StringDiff.scala index 62be14c..d685a5f 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/StringDiff.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/StringDiff.scala @@ -2,6 +2,7 @@ package app.tulz.diff import app.tulz.diff.format.DiffFormat import app.tulz.diff.util.DiffCollapse +import compat._ object StringDiff { @@ -9,7 +10,7 @@ object StringDiff { s1: String, s2: String, collapse: Boolean = true - ): String = ansi(s1, s2) + ): String = ansi(s1, s2, collapse) def ansi( s1: String, diff --git a/stringdiff/src/main/scala/app/tulz/diff/TokenDiff.scala b/stringdiff/src/main/scala/app/tulz/diff/TokenDiff.scala index e9bed4b..2f90974 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/TokenDiff.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/TokenDiff.scala @@ -4,7 +4,7 @@ import app.tulz.diff.format.DiffFormat import app.tulz.diff.util.DiffCollapse import app.tulz.diff.util.DiffPrettier import app.tulz.diff.util.DiffTokenize - +import compat._ import scala.collection.mutable.ListBuffer object TokenDiff { @@ -15,13 +15,13 @@ object TokenDiff { var pos = 0 whitespace.findAllMatchIn(s).foreach { m => if (m.start > pos) { - buffer.addOne(s.substring(pos, m.start)) + buffer.append(s.substring(pos, m.start)) } - buffer.addOne(s.substring(m.start, m.end)) + buffer.append(s.substring(m.start, m.end)) pos = m.end } if (pos < s.length) { - buffer.addOne(s.substring(pos)) + buffer.append(s.substring(pos)) } buffer.toIndexedSeq } diff --git a/stringdiff/src/main/scala/app/tulz/diff/util/DiffCollapse.scala b/stringdiff/src/main/scala/app/tulz/diff/util/DiffCollapse.scala index d329355..fe771ba 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/util/DiffCollapse.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/util/DiffCollapse.scala @@ -1,12 +1,11 @@ -package app.tulz.diff.util - -import app.tulz.diff.DiffElement -import app.tulz.diff.DiffElement.Diff -import app.tulz.diff.DiffElement.InBoth -import app.tulz.diff.DiffElement.InFirst -import app.tulz.diff.DiffElement.InSecond - -import scala.collection.IndexedSeqView +package app.tulz.diff +package util + +import DiffElement.Diff +import DiffElement.InBoth +import DiffElement.InFirst +import DiffElement.InSecond +import compat._ private[diff] object DiffCollapse { diff --git a/stringdiff/src/main/scala/app/tulz/diff/util/DiffLog.scala b/stringdiff/src/main/scala/app/tulz/diff/util/DiffLog.scala index 3aac125..f467cc2 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/util/DiffLog.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/util/DiffLog.scala @@ -1,9 +1,8 @@ -package app.tulz.diff.util +package app.tulz.diff +package util -import app.tulz.diff.DiffElement -import app.tulz.diff.format.DiffFormat - -import scala.collection.IndexedSeqView +import format.DiffFormat +import compat._ private[diff] object DiffLog { diff --git a/stringdiff/src/main/scala/app/tulz/diff/util/DiffPrettier.scala b/stringdiff/src/main/scala/app/tulz/diff/util/DiffPrettier.scala index c643c51..e4821b5 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/util/DiffPrettier.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/util/DiffPrettier.scala @@ -3,6 +3,7 @@ package app.tulz.diff.util import app.tulz.diff.DiffElement import app.tulz.diff.DiffElement.Diff import app.tulz.diff.DiffElement.InBoth +import scala.collection.compat._ private[diff] object DiffPrettier { diff --git a/stringdiff/src/main/scala/app/tulz/diff/util/DiffTokenize.scala b/stringdiff/src/main/scala/app/tulz/diff/util/DiffTokenize.scala index 4b35172..c2e660f 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/util/DiffTokenize.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/util/DiffTokenize.scala @@ -1,12 +1,13 @@ -package app.tulz.diff.util +package app.tulz.diff +package util -import app.tulz.diff.DiffElement -import app.tulz.diff.DiffElement.InBoth -import app.tulz.diff.DiffElement.InFirst -import app.tulz.diff.DiffElement.InSecond +import DiffElement.InBoth +import DiffElement.InFirst +import DiffElement.InSecond import scala.collection.mutable.ListBuffer -import scala.collection.IndexedSeqView +import scala.collection.compat._ +import compat._ private[diff] object DiffTokenize { @@ -17,14 +18,14 @@ private[diff] object DiffTokenize { ): List[DiffElement[IndexedSeqView[String]]] = { ListScan.withBuffer[DiffElement[IndexedSeqView[String]], DiffElement[IndexedSeqView[String]]](diff) { (list, buffer) => val (nonFirstSecond, firstSecondAndRest) = list.span(!_.inFirstOrSecond) - buffer.addAll(nonFirstSecond) + buffer.appendAll(nonFirstSecond) val (firstSecond, rest) = firstSecondAndRest.span(_.inFirstOrSecond) val (first, second) = firstSecond.partition(p) if (first.nonEmpty && second.nonEmpty) { - buffer.addAll(transform(first, second)) + buffer.appendAll(transform(first, second)) } else { - buffer.addAll(first) - buffer.addAll(second) + buffer.appendAll(first) + buffer.appendAll(second) } rest } @@ -158,7 +159,7 @@ private[diff] object DiffTokenize { samePrefix(work1, work2).map { case (prefix, rest1, rest2) => work1 = rest1 work2 = rest2 - buffer.addAll(prefix) + buffer.appendAll(prefix) } sameSuffix(work1, work2).foreach { case (rest1, rest2, suffix) => work1 = rest1 @@ -168,25 +169,25 @@ private[diff] object DiffTokenize { prefixIsSuffix(work1, work2) .map { case (prefixSuffix, rest1, rest2) => - buffer.addAll(rest2) - buffer.addAll(prefixSuffix) - buffer.addAll(rest1) - buffer.addAll(bufferSuffix) + buffer.appendAll(rest2) + buffer.appendAll(prefixSuffix) + buffer.appendAll(rest1) + buffer.appendAll(bufferSuffix) () } .orElse { suffixIsPrefix(work1, work2).map { case (rest1, rest2, suffixPrefix) => - buffer.addAll(rest1) - buffer.addAll(suffixPrefix) - buffer.addAll(rest2) - buffer.addAll(bufferSuffix) + buffer.appendAll(rest1) + buffer.appendAll(suffixPrefix) + buffer.appendAll(rest2) + buffer.appendAll(bufferSuffix) () } } .getOrElse { - buffer.addAll(work1) - buffer.addAll(work2) - buffer.addAll(bufferSuffix) + buffer.appendAll(work1) + buffer.appendAll(work2) + buffer.appendAll(bufferSuffix) () } @@ -269,7 +270,7 @@ private[diff] object DiffTokenize { private val whitespace = "\\s+".r private object Whitespace { def unapply(s: IndexedSeqView[String]): Option[IndexedSeqView[String]] = { - Some(s).filter(_.forall(whitespace.matches)) + Some(s).filter(_.forall(whitespace.findFirstMatchIn(_).isDefined)) } } diff --git a/stringdiff/src/main/scala/app/tulz/diff/util/ListScan.scala b/stringdiff/src/main/scala/app/tulz/diff/util/ListScan.scala index 77decd0..e85a113 100644 --- a/stringdiff/src/main/scala/app/tulz/diff/util/ListScan.scala +++ b/stringdiff/src/main/scala/app/tulz/diff/util/ListScan.scala @@ -7,7 +7,7 @@ private[diff] object ListScan { def apply[A, B](list: List[A])(f: List[A] => (List[B], List[A])): List[B] = { withBuffer[A, B](list) { (list, buffer) => val (toBuffer, newWork) = f(list) - buffer.addAll(toBuffer) + buffer.appendAll(toBuffer) newWork } }