This commit is contained in:
andrzej.jozwik@gmail.com 2014-09-29 22:40:44 +02:00 committed by Eugene Yokota
parent 48be806dd4
commit 9294351e24
6 changed files with 86 additions and 143 deletions

View File

@ -6,15 +6,11 @@ package sbt
import java.io.File
import java.net.URI
import Def.{ ScopedKey, Setting }
import Project._
import Types.Endo
import compiler.Eval
import SessionSettings._
import scala.collection.immutable.SortedMap
/**
* Represents (potentially) transient settings added into a build via commands/user.
*
@ -85,16 +81,18 @@ object SessionSettings {
type SessionSetting = (Setting[_], List[String])
type SessionMap = Map[ProjectRef, Seq[SessionSetting]]
/** This will re-evaluate all Setting[_]'s on this session against the current build state and
* return the new build state.
*/
/**
* This will re-evaluate all Setting[_]'s on this session against the current build state and
* return the new build state.
*/
def reapply(session: SessionSettings, s: State): State =
BuiltinCommands.reapply(session, Project.structure(s), s)
/** This will clear any user-added session settings for a given build state and return the new build state.
*
* Note: Does not clear `rawAppend` settings
*/
/**
* This will clear any user-added session settings for a given build state and return the new build state.
*
* Note: Does not clear `rawAppend` settings
*/
def clearSettings(s: State): State =
withSettings(s)(session => reapply(session.copy(append = session.append - session.current), s))
/** This will clear ALL transient session settings in a given build state, returning the new build state. */
@ -128,9 +126,6 @@ object SessionSettings {
if (newSession.append.isEmpty && !oldSettings.isEmpty)
oldState.log.warn("Discarding " + pluralize(oldSettings.size, " session setting") + ". Use 'session save' to persist session settings.")
}
def removeRanges[T](in: Seq[T], ranges: Seq[(Int, Int)]): Seq[T] =
{
val asSet = (Set.empty[Int] /: ranges) { case (s, (hi, lo)) => s ++ (hi to lo) }
@ -151,11 +146,12 @@ object SessionSettings {
saveSomeSettings(s)(_ == current)
}
/** Saves session settings to disk if they match the filter.
* @param s The build state
* @param include A filter function to determine which project's settings to persist.
* @return The new build state.
*/
/**
* Saves session settings to disk if they match the filter.
* @param s The build state
* @param include A filter function to determine which project's settings to persist.
* @return The new build state.
*/
def saveSomeSettings(s: State)(include: ProjectRef => Boolean): State =
withSettings(s) { session =>
val newSettings =
@ -182,23 +178,21 @@ object SessionSettings {
}
}
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 (_, oldShifted, replace, statements) = ((0, List[Setting[_]](), List[SessionSetting](), List[List[String]]()) /: inFile) {
case ((offs, olds, repl, statements), 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))
val head = (end, newLines)
val seq = lineMap.getOrElse(start, List())
(offs + end - start - newLines.size, shifted :: olds, ss :: repl, lineMap + (start -> (head +: seq)))
(offs + end - start - newLines.size, shifted :: olds, ss :: repl, newLines +: statements)
case _ =>
val shifted = s withPos RangePosition(path, r shift -offs)
(offs, shifted :: olds, repl, lineMap)
(offs, shifted :: olds, repl, statements)
}
}
val newSettings = settings diff replace
val oldContent = IO.readLines(writeTo)
val exist: List[String] = SessionSettingsNoBlankies.oldLinesToNew(oldContent, lineMap)
val exist: List[String] = SessionSettingsNoBlankies.oldLinesToNew(oldContent, statements)
val adjusted = if (!newSettings.isEmpty && needsTrailingBlank(exist)) exist :+ "" else exist
val lines = adjusted ++ newSettings.flatMap(_._2 ::: "" :: Nil)
IO.writeLines(writeTo, lines)

View File

@ -1,10 +1,7 @@
package sbt
import java.io.File
import sbt.internals.parser.{ XmlContent, SplitExpressionsNoBlankies }
import scala.collection.immutable.SortedMap
import scala.reflect.runtime.universe._
import sbt.internals.parser.{ XmlContent, SplitExpressionsNoBlankies }
object SessionSettingsNoBlankies {
@ -12,65 +9,43 @@ 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], setCommands: (Int, List[(Int, List[String])])): List[String] = {
val (from, newSettings) = setCommands
val newTreeStringsMap = newSettings.map {
case (_, lines) => toTreeStringMap(lines)
}
val to = newSettings.map(_._1).max
val originalLine = content.slice(from - 1, to - 1)
val operations = newTreeStringsMap.flatMap {
map =>
def oldLinesToNew(lines: List[String], setCommands: List[List[String]]): List[String] = {
val split = SplitExpressionsNoBlankies(FAKE_FILE, lines)
val recordedCommand = setCommands.flatMap {
command =>
val map = toTreeStringMap(command)
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)
case (name, (startPos, statement)) =>
split.settingsTrees.foldLeft(Seq.empty[(Int, String, String)]) {
case (acc, (statement, tree)) =>
val treeName = extractSettingName(tree)
if (name == treeName) {
val replacement = if (acc.isEmpty) {
command.mkString("\n")
} else {
""
}
(tree.pos.start, statement, replacement) +: acc
} else {
acc
}
}
}
}
val statements = XmlContent.handleXmlContent(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 sortedRecordedCommand = recordedCommand.sortBy(_._1)(REVERSE_ORDERING_INT)
val newContent = sortedRecordedCommand.foldLeft(split.modifiedContent) {
case (acc, (from, old, replacement)) =>
val before = acc.substring(0, from)
val after = acc.substring(from + old.length, acc.length)
before + replacement + after
// acc.replace(old, replacement)
}
val newLines = newContent.lines.toList
content.take(from - 1) ++ newLines ++ content.drop(to - 1)
newContent.lines.toList
}
private[sbt] def cutExpression(l: List[String], name: String): List[String] = l match {
case h +: t =>
val statements = SplitExpressionsNoBlankies(FAKE_FILE, l).settingsTrees
val lastIndex = statements.lastIndexWhere {
tuple => extractSettingName(tuple._2) == name
}
val (statement, tree) = statements(lastIndex)
if (tree.pos.end >= h.length) {
l
} else {
statement +: t
}
case _ =>
l
}
private def toTreeStringMap(lines: List[String]) = {
val split = SplitExpressionsNoBlankies(FAKE_FILE, lines)
private def toTreeStringMap(command: List[String]) = {
val split = SplitExpressionsNoBlankies(FAKE_FILE, command)
val trees = split.settingsTrees
val seq = trees.map {
case (statement, tree) =>

View File

@ -4,9 +4,8 @@ package parser
import java.io.File
import sbt.internals.parser.SplitExpressionsNoBlankies._
import SplitExpressionsNoBlankies._
import scala.annotation.tailrec
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
private[sbt] object SplitExpressionsNoBlankies {
@ -17,14 +16,14 @@ private[sbt] object SplitExpressionsNoBlankies {
private[sbt] case class SplitExpressionsNoBlankies(file: File, lines: Seq[String]) {
//settingsTrees needed for "session save"
val (imports, settings, settingsTrees) = splitExpressions(file, lines)
val (imports, settings, settingsTrees, modifiedContent) = splitExpressions(file, lines)
private def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)]) = {
import sbt.internals.parser.BugInParser._
import sbt.internals.parser.XmlContent._
private def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)], String) = {
import scala.reflect.runtime._
import scala.compat.Platform.EOL
import BugInParser._
import XmlContent._
import scala.tools.reflect.{ ToolBox, ToolBoxError }
val mirror = universe.runtimeMirror(this.getClass.getClassLoader)
@ -90,7 +89,7 @@ private[sbt] case class SplitExpressionsNoBlankies(file: File, lines: Seq[String
None
}
val statementsTreeLineRange = statements flatMap convertStatement
(imports map convertImport, statementsTreeLineRange.map(t => (t._1, t._3)), statementsTreeLineRange.map(t => (t._1, t._2)))
(imports map convertImport, statementsTreeLineRange.map(t => (t._1, t._3)), statementsTreeLineRange.map(t => (t._1, t._2)), modifiedContent)
}
}
@ -302,31 +301,32 @@ private[sbt] object XmlContent {
*/
private def addExplicitXmlContent(content: String, xmlParts: Seq[(String, Int, Int)]): String = {
val statements: Seq[(String, Boolean)] = splitFile(content, xmlParts)
val (builder, wasPreviousXml, wasXml, _) = statements.foldLeft((Seq.empty[String], false, false, "")) {
(acc, el) =>
val (bAcc, wasXml, _, previous) = acc
val (content, isXml) = el
val contentEmpty = content.trim.isEmpty
val (isNotCommentedXml, newAcc) = if (isXml) {
if (!wasXml) {
if (areBracketsNecessary(previous)) {
(true, " ( " +: bAcc)
} else {
(false, bAcc)
}
} else {
(true, bAcc)
}
} else if (wasXml && !contentEmpty) {
(false, " ) " +: bAcc)
val (builder, addCloseBrackets, wasXml, _) = statements.foldLeft((Seq.empty[String], false, false, "")) {
case ((bAcc, addCloseBrackets, wasXml, previous), (content, isXml)) =>
if (content.trim.isEmpty) {
(content +: bAcc, addCloseBrackets, wasXml, content)
} else {
(false, bAcc)
}
val (newAddCloseBrackets, newAcc) = if (isXml) {
if (wasXml) {
(addCloseBrackets, bAcc)
} else {
if (areBracketsNecessary(previous)) {
(true, " ( " +: bAcc)
} else {
(false, bAcc)
}
}
} else if (addCloseBrackets) {
(false, " ) " +: bAcc)
} else {
(false, bAcc)
}
(content +: newAcc, isNotCommentedXml || (wasXml && contentEmpty), isXml, content)
(content +: newAcc, newAddCloseBrackets, isXml, content)
}
}
val closeIfNecessaryBuilder =
if (wasPreviousXml && !wasXml) {
if (addCloseBrackets && wasXml) {
builder.head +: " ) " +: builder.tail
} else {
builder

View File

@ -18,10 +18,10 @@ val a = <aaa>
*/
organization := "jozwikr" // OK
organization := "scalania" // OK
scalaVersion := "2.9.2"
organization := "scalania"

View File

@ -1,23 +0,0 @@
package sbt
import sbt.internals.parser.AbstractSpec
class SessionSettingsCutExpressionSpec extends AbstractSpec {
"Cut expression " should {
"Cut only statement which we are interesting " in {
val name = "scalaVersion"
val expression = s"""$name := "2.9.2""""
val line = s"""name := "newName";$expression; organization := "jozwikr""""
SessionSettingsNoBlankies.cutExpression(List(line), name) must_== List(expression)
}
"Do not cut not valid expression " in {
val name = "k4"
val line = s"$name := { val x = $name.value; () }"
SessionSettingsNoBlankies.cutExpression(List(line), name) must_== List(line)
}
}
}

View File

@ -6,7 +6,6 @@ import org.specs2.matcher.MatchResult
import sbt.internals.parser.{ AbstractSpec, SplitExpressionsNoBlankies }
import scala.collection.GenTraversableOnce
import scala.collection.immutable.{ SortedMap, TreeMap }
import scala.io.Source
import scala.xml.XML
@ -17,7 +16,7 @@ abstract class AbstractSessionSettingsSpec(folder: String, printDetails: Boolean
"SessionSettings " should {
"Be identical for empty map " in {
def unit(f: File) = Seq((Source.fromFile(f).getLines().toList, SortedMap.empty[Int, List[(Int, List[String])]]))
def unit(f: File) = Seq((Source.fromFile(f).getLines().toList, Nil))
runTestOnFiles(unit)
}
@ -26,7 +25,7 @@ abstract class AbstractSessionSettingsSpec(folder: String, printDetails: Boolean
}
}
private def runTestOnFiles(expectedResultAndMap: File => Seq[(List[String], SortedMap[Int, List[(Int, List[String])]])]): MatchResult[GenTraversableOnce[File]] = {
private def runTestOnFiles(expectedResultAndMap: File => Seq[(List[String], List[List[String]])]): MatchResult[GenTraversableOnce[File]] = {
val allFiles = rootDir.listFiles(new FilenameFilter() {
def accept(dir: File, name: String) = name.endsWith(".sbt.txt")
@ -64,12 +63,10 @@ abstract class AbstractSessionSettingsSpec(folder: String, printDetails: Boolean
val set = (node \\ "set").text
val start = (node \\ "start").text.toInt
val end = (node \\ "end").text.toInt
(start, (end, List(set)))
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))
(result, tupleCollection)
}
}
}