Corrections for "session save"

This commit is contained in:
andrzej.jozwik@gmail.com 2014-09-15 16:31:50 +02:00 committed by Eugene Yokota
parent 4a33fd2225
commit c720a973a6
12 changed files with 338 additions and 33 deletions

View File

@ -12,6 +12,8 @@ import compiler.Eval
import SessionSettings._
import scala.collection.immutable.SortedMap
final case class SessionSettings(currentBuild: URI, currentProject: Map[URI, String], original: Seq[Setting[_]], append: SessionMap, rawAppend: Seq[Setting[_]], currentEval: () => Eval) {
assert(currentProject contains currentBuild, "Current build (" + currentBuild + ") not associated with a current project.")
def setCurrent(build: URI, project: String, eval: () => Eval): SessionSettings = copy(currentBuild = build, currentProject = currentProject.updated(build, project), currentEval = eval)
@ -100,21 +102,23 @@ object SessionSettings {
}
}
val (_, oldShifted, replace, lineMap) = ((0, List[Setting[_]](), List[SessionSetting](), Map.empty[Int, (Int, List[String])]) /: inFile) {
val (_, oldShifted, replace, lineMap) = ((0, List[Setting[_]](), List[SessionSetting](), SortedMap.empty[Int, List[(Int, List[String])]](Ordering[Int].reverse)) /: inFile) {
case ((offs, olds, repl, lineMap), s) =>
val RangePosition(_, r @ LineRange(start, end)) = s.pos
settings find (_._1.key == s.key) match {
case Some(ss @ (ns, newLines)) if !ns.init.dependencies.contains(ns.key) =>
val shifted = ns withPos RangePosition(path, LineRange(start - offs, start - offs + newLines.size))
(offs + end - start - newLines.size, shifted :: olds, ss :: repl, lineMap + (start -> (end, newLines)))
val head = (end, newLines)
val seq = lineMap.getOrElse(start, List())
(offs + end - start - newLines.size, shifted :: olds, ss :: repl, lineMap + (start -> (head +: seq)))
case _ =>
val shifted = s withPos RangePosition(path, r shift -offs)
(offs, shifted :: olds, repl, lineMap)
}
}
val newSettings = settings diff replace
val oldContentWithIndex = IO.readLines(writeTo).zipWithIndex
val exist: List[String] = toLines(oldContentWithIndex, lineMap)
val oldContent = IO.readLines(writeTo)
val exist: List[String] = SessionSettingsNoBlankies.oldLinesToNew(oldContent, lineMap)
val adjusted = if (!newSettings.isEmpty && needsTrailingBlank(exist)) exist :+ "" else exist
val lines = adjusted ++ newSettings.flatMap(_._2 ::: "" :: Nil)
IO.writeLines(writeTo, lines)
@ -126,18 +130,6 @@ object SessionSettings {
(newWithPos.reverse, other ++ oldShifted)
}
private[sbt] def toLines(oldContentWithIndex: List[(String,Int)], lineMap: Map[Int, (Int, List[String])]): List[String] = {
val (tmpLines, _) = ((List[String](), 1) /: oldContentWithIndex) {
case ((accLines, n), (line, m)) if n == m + 1 =>
lineMap.get(n) match {
case Some(Pair(end, lines)) => (lines reverse_::: accLines, end)
case None => (line :: accLines, n + 1)
}
case (res, _) => res
}
tmpLines.reverse
}
def needsTrailingBlank(lines: Seq[String]) = !lines.isEmpty && !lines.takeRight(1).exists(_.trim.isEmpty)
def printAllSettings(s: State): State =
withSettings(s) { session =>

View File

@ -0,0 +1,79 @@
package sbt
import java.io.File
import scala.collection.immutable.SortedMap
import scala.reflect.runtime.universe._
object SessionSettingsNoBlankies {
val REVERSE_ORDERING_INT = Ordering[Int].reverse
def oldLinesToNew(content: List[String], lineMap: SortedMap[Int, List[(Int, List[String])]]): List[String] =
if (lineMap.isEmpty) {
content
} else {
val head = lineMap.head
val newContent = toNewContent(content, head)
oldLinesToNew(newContent, lineMap.tail)
}
private def toNewContent(content: List[String], tuple: (Int, List[(Int, List[String])])): List[String] = {
val (from, newSettingSeq) = tuple
val newTreeStringSeqMap = newSettingSeq.seq.map {
case (_, lines) => toTreeStringMap(lines)
}
val to = newSettingSeq.map(_._1).max
val originalLine = content.slice(from - 1, to - 1)
val operations = newTreeStringSeqMap.flatMap {
map =>
map.flatMap {
case (name, (startIndex, statement)) =>
val validLines = cutExpression(originalLine, name)
val treeStringMap = toTreeStringMap(validLines)
treeStringMap.get(name).map {
case (t, oldContent) =>
(startIndex, oldContent, statement)
}
}
}
val statements = originalLine.mkString("\n")
val sortedOperations = operations.sortBy(_._1)(REVERSE_ORDERING_INT)
val newContent = sortedOperations.foldLeft(statements) {
case (acc, (startIndex, old, newStatement)) =>
acc.replace(old, newStatement)
}
val newLines = newContent.lines.toList
content.take(from - 1) ++ newLines ++ content.drop(to - 1)
}
private def cutExpression(l: List[String], name: String): List[String] = l match {
case h +: t =>
val array = h.split(";").filter(_.contains(name))
array.mkString(";") +: t
case _ =>
l
}
private def toTreeStringMap(lines: List[String]) = {
val trees = SplitExpressionsNoBlankies(new File("fake"), lines).settingsTrees
val seq = trees.map {
case (statement, tree) =>
(extractSettingName(tree), (tree.pos.start, statement))
}
seq.toMap
}
private def extractSettingName(tree: Tree): String = {
tree.children match {
case h :: _ =>
extractSettingName(h)
case _ =>
tree.toString()
}
}
}

View File

@ -4,6 +4,7 @@ import java.io.File
import scala.annotation.tailrec
import SplitExpressionsNoBlankies._
import scala.reflect.runtime.universe._
object SplitExpressionsNoBlankies {
val END_OF_LINE_CHAR = '\n'
@ -11,11 +12,12 @@ object SplitExpressionsNoBlankies {
}
case class SplitExpressionsNoBlankies(file: File, lines: Seq[String]) {
val (imports, settings) = splitExpressions(file, lines)
//settingsTrees needed for "session save"
val (imports, settings, settingsTrees) = splitExpressions(file, lines)
private def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)]) = {
private def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)]) = {
import scala.reflect.runtime._
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBoxError
import scala.tools.reflect.ToolBox
import scala.compat.Platform.EOL
@ -34,12 +36,14 @@ case class SplitExpressionsNoBlankies(file: File, lines: Seq[String]) {
toolbox.parse(merged)
} catch {
case e: ToolBoxError =>
ConsoleLogger(System.err).trace(e)
val seq = toolbox.frontEnd.infos.map { i =>
s"""[$fileName]:${i.pos.line}: ${i.msg}"""
}
throw new MessageOnlyException(
s"""${seq.mkString(EOL)}""".stripMargin)
s"""======
|$merged
|======
|${seq.mkString(EOL)}""".stripMargin)
}
val parsedTrees = parsed match {
case Block(stmt, expr) =>
@ -73,19 +77,18 @@ case class SplitExpressionsNoBlankies(file: File, lines: Seq[String]) {
statement
}
def convertStatement(t: Tree): Option[(String, LineRange)] =
def convertStatement(t: Tree): Option[(String, Tree, LineRange)] =
if (t.pos.isDefined) {
val originalStatement = merged.substring(t.pos.start, t.pos.end)
val statement = parseStatementAgain(t, originalStatement)
val numberLines = statement.count(c => c == END_OF_LINE_CHAR)
Some((statement, LineRange(t.pos.line - 1, t.pos.line + numberLines)))
Some((statement, t, LineRange(t.pos.line - 1, t.pos.line + numberLines)))
} else {
None
}
(imports map convertImport, statements flatMap convertStatement)
val statementsTreeLineRange = statements flatMap convertStatement
(imports map convertImport, statementsTreeLineRange.map(t => (t._1, t._3)), statementsTreeLineRange.map(t => (t._1, t._2)))
}
}
/**
@ -143,14 +146,14 @@ private[sbt] object BugInParser {
/**
* #ToolBox#parse(String) will fail for xml sequence:
* <pre>
* val xml = <div>txt</div>
* <a>rr</a>
* </pre>
* At least brackets have to be added
* val xml = <div>txt</div>
* <a>rr</a>
* </pre>
* At least brackets have to be added
* <pre>
* val xml = (<div>txt</div>
* <a>rr</a>)
* </pre>
* val xml = (<div>txt</div>
* <a>rr</a>)
* </pre>
*/
private object XmlContent {
/**

View File

@ -0,0 +1,27 @@
name := "newName"
libraryDependencies := Seq("org.scala-sbt" %% "sbinary" % "0.4.1")
lazy val checkPom = taskKey[Unit]("check pom to ensure no <type> sections are generated"); checkPom := {
val pomFile = makePom.value
val pom = xml.XML.loadFile(pomFile)
val tpe = pom \\ "type"
if(!tpe.isEmpty)
sys.error("Expected no <type> sections, got: " + tpe + " in \n\n" + pom)
};scalacOptions := Seq("-deprecation")
val b = ( <b/>)
val a = <aaa>
</aaa>
/*
*/
organization := "jozwikr" // OK
scalaVersion := "2.9.2"
organization := "ololol"

View File

@ -0,0 +1,7 @@
<settings>
<setting>
<start>1</start>
<end>2</end>
<set>name := "alaMaKota"</set>
</setting>
</settings>

View File

@ -0,0 +1,27 @@
name := "alaMaKota"
libraryDependencies := Seq("org.scala-sbt" %% "sbinary" % "0.4.1")
lazy val checkPom = taskKey[Unit]("check pom to ensure no <type> sections are generated"); checkPom := {
val pomFile = makePom.value
val pom = xml.XML.loadFile(pomFile)
val tpe = pom \\ "type"
if(!tpe.isEmpty)
sys.error("Expected no <type> sections, got: " + tpe + " in \n\n" + pom)
};scalacOptions := Seq("-deprecation")
val b = ( <b/>)
val a = <aaa>
</aaa>
/*
*/
organization := "jozwikr" // OK
scalaVersion := "2.9.2"
organization := "ololol"

View File

@ -0,0 +1,12 @@
<settings>
<setting>
<start>1</start>
<end>2</end>
<set>name := "alaMaKota"</set>
</setting>
<setting>
<start>25</start>
<end>26</end>
<set>organization := "scalania"</set>
</setting>
</settings>

View File

@ -0,0 +1,27 @@
name := "alaMaKota"
libraryDependencies := Seq("org.scala-sbt" %% "sbinary" % "0.4.1")
lazy val checkPom = taskKey[Unit]("check pom to ensure no <type> sections are generated"); checkPom := {
val pomFile = makePom.value
val pom = xml.XML.loadFile(pomFile)
val tpe = pom \\ "type"
if(!tpe.isEmpty)
sys.error("Expected no <type> sections, got: " + tpe + " in \n\n" + pom)
};scalacOptions := Seq("-deprecation")
val b = ( <b/>)
val a = <aaa>
</aaa>
/*
*/
organization := "jozwikr" // OK
scalaVersion := "2.9.2"
organization := "scalania"

View File

@ -0,0 +1,24 @@
name := "newName";scalaVersion := "2.9.2"; organization := "jozwikr"
libraryDependencies := Seq("org.scala-sbt" %% "sbinary" % "0.4.1")
lazy val checkPom = taskKey[Unit]("check pom to ensure no <type> sections are generated"); checkPom := {
val pomFile = makePom.value
val pom = xml.XML.loadFile(pomFile)
val tpe = pom \\ "type"
if(!tpe.isEmpty)
sys.error("Expected no <type> sections, got: " + tpe + " in \n\n" + pom)
};scalacOptions := Seq("-deprecation")
val b = ( <b/>)
val a = <aaa>
</aaa>
/*
*/

View File

@ -0,0 +1,7 @@
<settings>
<setting>
<start>1</start>
<end>2</end>
<set>name := "alaMaKota"</set>
</setting>
</settings>

View File

@ -0,0 +1,24 @@
name := "alaMaKota";scalaVersion := "2.9.2"; organization := "jozwikr"
libraryDependencies := Seq("org.scala-sbt" %% "sbinary" % "0.4.1")
lazy val checkPom = taskKey[Unit]("check pom to ensure no <type> sections are generated"); checkPom := {
val pomFile = makePom.value
val pom = xml.XML.loadFile(pomFile)
val tpe = pom \\ "type"
if(!tpe.isEmpty)
sys.error("Expected no <type> sections, got: " + tpe + " in \n\n" + pom)
};scalacOptions := Seq("-deprecation")
val b = ( <b/>)
val a = <aaa>
</aaa>
/*
*/

View File

@ -0,0 +1,76 @@
package sbt
import java.io.{ File, FilenameFilter }
import org.specs2.matcher.MatchResult
import scala.collection.GenTraversableOnce
import scala.collection.immutable.{ SortedMap, TreeMap }
import scala.io.Source
import scala.xml.XML
abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec {
protected val rootPath = getClass.getResource("").getPath + folder
println(s"Reading files from: $rootPath")
protected val rootDir = new File(rootPath)
"SessionSettings " should {
"Be identical for empty map " in {
def unit(f: File) = Seq((Source.fromFile(f).getLines().toSeq, SortedMap.empty[Int, List[(Int, List[String])]]))
runTestOnFiles(unit)
}
"Replace statements " in {
runTestOnFiles(replace)
}
}
private def runTestOnFiles(expectedResultAndMap: File => Seq[(Seq[String], SortedMap[Int, List[(Int, List[String])]])]): MatchResult[GenTraversableOnce[File]] = {
val allFiles = rootDir.listFiles(new FilenameFilter() {
def accept(dir: File, name: String) = name.endsWith(".sbt.txt")
}).toList
foreach(allFiles) {
file =>
val originalLines = Source.fromFile(file).getLines().toList
foreach(expectedResultAndMap(file)) {
case (expectedResult, map) =>
val result = SessionSettingsNoBlankies.oldLinesToNew(originalLines, map)
expectedResult === result
}
}
}
protected def replace(f: File) = {
val dirs = rootDir.listFiles(new FilenameFilter() {
def accept(dir: File, name: String) = {
val startsWith = f.getName + "_"
name.startsWith(startsWith)
}
}).toList
dirs.flatMap {
dir =>
val files = dir.listFiles(new FilenameFilter {
override def accept(dir: File, name: String) = name.endsWith(".xml")
})
files.map { xmlFile =>
val xml = XML.loadFile(xmlFile)
val result = Source.fromFile(xmlFile.getAbsolutePath + ".result").getLines().toSeq
val tupleCollection = (xml \\ "settings" \\ "setting").map {
node =>
val set = (node \\ "set").text
val start = (node \\ "start").text.toInt
val end = (node \\ "end").text.toInt
(start, (end, List(set)))
}.toList
val map = tupleCollection.groupBy(el => el._1).map {
case (k, seq) => (k, seq.map(el => el._2))
}
(result, TreeMap(map.toArray: _*)(SessionSettingsNoBlankies.REVERSE_ORDERING_INT))
}
}
}
}
class SessionSettingsSpec extends AbstractSessionSettingsSpec("../session-settings")