aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala92
1 files changed, 92 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala
new file mode 100644
index 000000000..c42c808fe
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala
@@ -0,0 +1,92 @@
+package dotty.tools.dotc
+package rewrite
+
+import util.{SourceFile, Positions}
+import Positions.Position
+import core.Contexts.{Context, FreshContext}
+import collection.mutable
+
+/** Handles rewriting of Scala2 files to Dotty */
+object Rewrites {
+ private class PatchedFiles extends mutable.HashMap[SourceFile, Patches]
+
+ private case class Patch(pos: Position, replacement: String) {
+ def delta = replacement.length - (pos.end - pos.start)
+ }
+
+ private class Patches(source: SourceFile) {
+ private val pbuf = new mutable.ListBuffer[Patch]()
+
+ def addPatch(pos: Position, replacement: String): Unit =
+ pbuf += Patch(pos, replacement)
+
+ def apply(cs: Array[Char]): Array[Char] = {
+ val delta = pbuf.map(_.delta).sum
+ val patches = pbuf.toList.sortBy(_.pos.start)
+ if (patches.nonEmpty)
+ patches reduceLeft {(p1, p2) =>
+ assert(p1.pos.end <= p2.pos.start, s"overlapping patches: $p1 and $p2")
+ p2
+ }
+ val ds = new Array[Char](cs.length + delta)
+ def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
+ def copy(upTo: Int): Int = {
+ val untouched = upTo - inIdx
+ Array.copy(cs, inIdx, ds, outIdx, untouched)
+ outIdx + untouched
+ }
+ ps match {
+ case patch @ Patch(pos, replacement) :: ps1 =>
+ val outNew = copy(pos.start)
+ replacement.copyToArray(ds, outNew)
+ loop(ps1, pos.end, outNew + replacement.length)
+ case Nil =>
+ val outNew = copy(cs.length)
+ assert(outNew == ds.length, s"$outNew != ${ds.length}")
+ }
+ }
+ loop(patches, 0, 0)
+ ds
+ }
+
+ def writeBack(): Unit = {
+ val out = source.file.output
+ val chars = apply(source.underlying.content)
+ val bytes = new String(chars).getBytes
+ out.write(bytes)
+ out.close()
+ }
+ }
+
+ /** If -rewrite is set, record a patch that replaces the range
+ * given by `pos` in `source` by `replacement`
+ */
+ def patch(source: SourceFile, pos: Position, replacement: String)(implicit ctx: Context): Unit =
+ for (rewrites <- ctx.settings.rewrite.value)
+ rewrites.patched
+ .getOrElseUpdate(source, new Patches(source))
+ .addPatch(pos, replacement)
+
+ /** Patch position in `ctx.compilationUnit.source`. */
+ def patch(pos: Position, replacement: String)(implicit ctx: Context): Unit =
+ patch(ctx.compilationUnit.source, pos, replacement)
+
+ /** If -rewrite is set, apply all patches and overwrite patched source files.
+ */
+ def writeBack()(implicit ctx: Context) =
+ for (rewrites <- ctx.settings.rewrite.value; source <- rewrites.patched.keys) {
+ ctx.echo(s"[patched file ${source.file.path}]")
+ rewrites.patched(source).writeBack()
+ }
+}
+
+/** A completely encapsulated class representing rewrite state, used
+ * as an optional setting.
+ */
+class Rewrites {
+ import Rewrites._
+ private val patched = new PatchedFiles
+}
+
+
+