diff options
author | Martin Odersky <odersky@gmail.com> | 2016-03-04 12:23:10 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-03-12 16:08:36 +0100 |
commit | 12d895587444d5d59e7d75dfaf7b85deb61e99e0 (patch) | |
tree | e8f880f79911366d448ff6cc6cdb82fdd846ac6a /src/dotty/tools/dotc/rewrite/Rewrites.scala | |
parent | c1e263bebf7e73239e7faa3e48d75a0a7df45d76 (diff) | |
download | dotty-12d895587444d5d59e7d75dfaf7b85deb61e99e0.tar.gz dotty-12d895587444d5d59e7d75dfaf7b85deb61e99e0.tar.bz2 dotty-12d895587444d5d59e7d75dfaf7b85deb61e99e0.zip |
Better encapsulation
No more leaking ofMove PatchedFiles in a settings option. Move all
patch classes into a `Rewrites` object.
Diffstat (limited to 'src/dotty/tools/dotc/rewrite/Rewrites.scala')
-rw-r--r-- | src/dotty/tools/dotc/rewrite/Rewrites.scala | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/rewrite/Rewrites.scala b/src/dotty/tools/dotc/rewrite/Rewrites.scala new file mode 100644 index 000000000..27dafebcf --- /dev/null +++ b/src/dotty/tools/dotc/rewrite/Rewrites.scala @@ -0,0 +1,96 @@ +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) + patches.iterator.sliding(2, 1).foreach(ps => + assert(ps(0).pos.end <= ps(1).pos.start, s"overlapping patches: ${ps(0)} and ${ps(1)}")) + 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.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 = + ctx.settings.rewrite.value match { + case Some(rewrites: Rewrites) => + rewrites.patched.get(source) match { + case Some(ps) => + ps.addPatch(pos, replacement) + case None => + rewrites.patched(source) = new Patches(source) + patch(source, pos, replacement) + } + case _ => + } + + /** If -rewrite is set, apply all patches and overwrite patched source files. + */ + def writeBack()(implicit ctx: Context) = + ctx.settings.rewrite.value match { + case Some(rewrites: Rewrites) => + for (source <- rewrites.patched.keys) { + ctx.println(s"[patched file ${source.file.path}]") + rewrites.patched(source).writeBack() + } + case _ => + } +} + +/** A completely encapsulated class representing rewrite state, used + * as an optional setting. + */ +class Rewrites { + import Rewrites._ + private val patched = new PatchedFiles +} + + + |