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

View File

@ -1,10 +1,7 @@
package sbt package sbt
import java.io.File
import sbt.internals.parser.{ XmlContent, SplitExpressionsNoBlankies }
import scala.collection.immutable.SortedMap
import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe._
import sbt.internals.parser.{ XmlContent, SplitExpressionsNoBlankies }
object SessionSettingsNoBlankies { object SessionSettingsNoBlankies {
@ -12,65 +9,43 @@ object SessionSettingsNoBlankies {
val REVERSE_ORDERING_INT = Ordering[Int].reverse val REVERSE_ORDERING_INT = Ordering[Int].reverse
def oldLinesToNew(content: List[String], lineMap: SortedMap[Int, List[(Int, List[String])]]): List[String] = def oldLinesToNew(lines: List[String], setCommands: List[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 =>
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 = 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 newLines = newContent.lines.toList
content.take(from - 1) ++ newLines ++ content.drop(to - 1)
}
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) val split = SplitExpressionsNoBlankies(FAKE_FILE, lines)
val recordedCommand = setCommands.flatMap {
command =>
val map = toTreeStringMap(command)
map.flatMap {
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 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)
}
newContent.lines.toList
}
private def toTreeStringMap(command: List[String]) = {
val split = SplitExpressionsNoBlankies(FAKE_FILE, command)
val trees = split.settingsTrees val trees = split.settingsTrees
val seq = trees.map { val seq = trees.map {
case (statement, tree) => case (statement, tree) =>

View File

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

View File

@ -18,10 +18,10 @@ val a = <aaa>
*/ */
organization := "jozwikr" // OK organization := "scalania" // OK
scalaVersion := "2.9.2" 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 sbt.internals.parser.{ AbstractSpec, SplitExpressionsNoBlankies }
import scala.collection.GenTraversableOnce import scala.collection.GenTraversableOnce
import scala.collection.immutable.{ SortedMap, TreeMap }
import scala.io.Source import scala.io.Source
import scala.xml.XML import scala.xml.XML
@ -17,7 +16,7 @@ abstract class AbstractSessionSettingsSpec(folder: String, printDetails: Boolean
"SessionSettings " should { "SessionSettings " should {
"Be identical for empty map " in { "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) 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() { val allFiles = rootDir.listFiles(new FilenameFilter() {
def accept(dir: File, name: String) = name.endsWith(".sbt.txt") 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 set = (node \\ "set").text
val start = (node \\ "start").text.toInt val start = (node \\ "start").text.toInt
val end = (node \\ "end").text.toInt val end = (node \\ "end").text.toInt
(start, (end, List(set))) List(set)
}.toList }.toList
val map = tupleCollection.groupBy(el => el._1).map {
case (k, seq) => (k, seq.map(el => el._2)) (result, tupleCollection)
}
(result, TreeMap(map.toArray: _*)(SessionSettingsNoBlankies.REVERSE_ORDERING_INT))
} }
} }
} }