diff --git a/main/src/main/scala/sbt/internals/parser/SbtParser.scala b/main/src/main/scala/sbt/internals/parser/SbtParser.scala index 785492e7b..08d71c736 100644 --- a/main/src/main/scala/sbt/internals/parser/SbtParser.scala +++ b/main/src/main/scala/sbt/internals/parser/SbtParser.scala @@ -23,10 +23,13 @@ private[sbt] object SbtParser { sealed trait ParsedSbtFileExpressions { /** The set of parsed import expressions. */ def imports: Seq[(String, Int)] + /** The set of parsed defintions and/or sbt build settings. */ def settings: Seq[(String, LineRange)] + /** The set of scala tree's for parsed definitions/settings and the underlying string representation.. */ def settingsTrees: Seq[(String, Tree)] + /** Represents the changes we had to perform to the sbt file so that XML will parse correctly. */ def modifiedContent: String } @@ -200,9 +203,9 @@ private[sbt] object XmlContent { private val DOUBLE_SLASH = "//" - private val OPEN_BRACKET = s" $OPEN_CURLY_BRACKET " + private val OPEN_BRACKET = s"$OPEN_CURLY_BRACKET" - private val CLOSE_BRACKET = " ) " + private val CLOSE_BRACKET = ")" /** * @@ -222,24 +225,25 @@ private[sbt] object XmlContent { * Cut file for normal text - xml - normal text - xml .... * @param content - content * @param ts - import/statements - * @return (text,true) - is xml (text,false) - if normal text + * @return Seq - Right(xml,whiteSpaces) - for xml, Left(statement) - for text */ - private def splitFile(content: String, ts: Seq[(String, Int, Int)]): Seq[(String, Boolean)] = { - val (statements, index) = ts.foldLeft((Seq.empty[(String, Boolean)], 0)) { - (accSeqIndex, el) => - val (statement, startIndex, endIndex) = el - val (accSeq, index) = accSeqIndex - val textStatementOption = if (index >= startIndex) { - None + private def splitFile(content: String, ts: Seq[(String, Int, Int)]): Seq[Either[(String), (String, String)]] = { + val (statements, lastIndex) = ts.foldLeft((Seq.empty[Either[(String), (String, String)]], 0)) { + case ((accSeq, index), (statement, startIndex, endIndex)) => + val toAdd = if (index >= startIndex) { + Seq(Right((statement, ""))) } else { val s = content.substring(index, startIndex) - Some((s, false)) + if (s.trim.isEmpty) { + Seq(Right((statement, s))) + } else { + Seq(Right((statement, "")), Left(s)) + } } - val newAccSeq = (statement, true) +: addOptionToCollection(accSeq, textStatementOption) - (newAccSeq, endIndex) + (toAdd ++ accSeq, endIndex) } - val endOfFile = content.substring(index, content.length) - val withEndOfFile = (endOfFile, false) +: statements + val endOfFile = content.substring(lastIndex, content.length) + val withEndOfFile = if (endOfFile.isEmpty) statements else Left(endOfFile) +: statements withEndOfFile.reverse } @@ -265,6 +269,8 @@ private[sbt] object XmlContent { } } + private val CLOSE_XML_TAG = "/>" + /** * Modified Opening Tag - * @param offsetIndex - index @@ -273,7 +279,7 @@ private[sbt] object XmlContent { */ @tailrec private def findModifiedOpeningTags(content: String, offsetIndex: Int, acc: Seq[(String, Int, Int)]): Seq[(String, Int, Int)] = { - val endIndex = content.indexOf("/>", offsetIndex) + val endIndex = content.indexOf(CLOSE_XML_TAG, offsetIndex) if (endIndex == NOT_FOUND_INDEX) { acc } else { @@ -352,46 +358,40 @@ private[sbt] object XmlContent { * @return content with xml with brackets */ private def addExplicitXmlContent(content: String, xmlParts: Seq[(String, Int, Int)]): String = { - val statements: Seq[(String, Boolean)] = splitFile(content, xmlParts) - val (correctedStmt, shouldAddCloseBrackets, wasXml, _) = addBracketsIfNecessary(statements) - val closeIfNecessaryCorrectedStmt = - if (shouldAddCloseBrackets && wasXml) { - correctedStmt.head +: CLOSE_BRACKET +: correctedStmt.tail - } else { - correctedStmt - } - closeIfNecessaryCorrectedStmt.reverse.mkString - } + val statements = splitFile(content, xmlParts) - def addBracketsIfNecessary(statements: Seq[(String, Boolean)]): (Seq[String], Boolean, Boolean, String) = { - statements.foldLeft((Seq.empty[String], false, false, "")) { - case ((accStmt, shouldAddCloseBracket, prvWasXml, prvStmt), (stmt, isXml)) => - if (stmt.trim.isEmpty) { - (stmt +: accStmt, shouldAddCloseBracket, prvWasXml, stmt) - } else { - val (newShouldAddCloseBracket, newStmtAcc) = if (isXml) { - addOpenBracketIfNecessary(accStmt, shouldAddCloseBracket, prvWasXml, prvStmt) - } else if (shouldAddCloseBracket) { - (false, CLOSE_BRACKET +: accStmt) - } else { - (false, accStmt) - } - - (stmt +: newStmtAcc, newShouldAddCloseBracket, isXml, stmt) + val (_, seq, lastAdd) = statements.foldLeft[(Option[Either[(String), (String, String)]], Seq[String], Boolean)]((None, Seq.empty[String], false)) { + case ((previousOption, acc, add), element) => + val (newAcc, newAdd) = (element, previousOption) match { + case (Left(text), _) => + val accWithClose = if (add) { + addCloseBracket(acc) + } else { + acc + } + (text +: accWithClose, false) + case (Right((xml, nonXml)), Some(Left(text))) => + val (accWithOpen, added) = if (areBracketsNecessary(text)) { + (OPEN_BRACKET +: acc, true) + } else { + (acc, false) + } + (xml +: (nonXml +: accWithOpen), added) + case (Right((xml, nonXml)), _) => + (xml +: (nonXml +: acc), add) } + (Some(element), newAcc, newAdd) } + + val correctedSeq = if (lastAdd) { + addCloseBracket(seq) + } else { + seq + } + correctedSeq.reverse.mkString } - private def addOpenBracketIfNecessary(stmtAcc: Seq[String], shouldAddCloseBracket: Boolean, prvWasXml: Boolean, prvStatement: String) = - if (prvWasXml) { - (shouldAddCloseBracket, stmtAcc) - } else { - if (areBracketsNecessary(prvStatement)) { - (true, OPEN_BRACKET +: stmtAcc) - } else { - (false, stmtAcc) - } - } + private def addCloseBracket(statements: Seq[String]) = CLOSE_BRACKET +: statements /** * Add to head if option is not empty @@ -439,8 +439,15 @@ private[sbt] object XmlContent { */ private def areBracketsNecessary(statement: String): Boolean = { val doubleSlash = statement.indexOf(DOUBLE_SLASH) - val endOfLine = statement.indexOf(END_OF_LINE) - if (doubleSlash == NOT_FOUND_INDEX || (doubleSlash < endOfLine)) { + + if (doubleSlash != NOT_FOUND_INDEX) { + val endOfLine = statement.indexOf(END_OF_LINE, doubleSlash) + if (endOfLine == NOT_FOUND_INDEX) { + false + } else { + areBracketsNecessary(statement.substring(endOfLine)) + } + } else { val roundBrackets = statement.lastIndexOf(OPEN_CURLY_BRACKET) val braces = statement.lastIndexOf(OPEN_PARENTHESIS) val max = roundBrackets.max(braces) @@ -450,8 +457,7 @@ private[sbt] object XmlContent { val trimmed = statement.substring(max + 1).trim trimmed.nonEmpty } - } else { - false + } } } \ No newline at end of file diff --git a/main/src/test/resources/fail-format/.gitignore b/main/src/test/resources/fail-format/.gitignore new file mode 100644 index 000000000..f59ec20aa --- /dev/null +++ b/main/src/test/resources/fail-format/.gitignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/main/src/test/resources/old-format/21.sbt.txt b/main/src/test/resources/old-format/21.sbt.txt new file mode 100644 index 000000000..c4cfc8585 --- /dev/null +++ b/main/src/test/resources/old-format/21.sbt.txt @@ -0,0 +1,72 @@ + +name := "scala-stm" + +organization := "org.scala-stm" + +version := "0.8-SNAPSHOT" + +scalaVersion := "2.11.2" + +crossScalaVersions := Seq("2.11.2", "2.10.4", "2.9.3") + +libraryDependencies += ("org.scalatest" %% "scalatest" % "[1.5,)" % "test") + +libraryDependencies += ("junit" % "junit" % "4.5" % "test") + +// skip exhaustive tests +testOptions += Tests.Argument("-l", "slow") + +// test of TxnExecutor.transformDefault must be run by itself +parallelExecution in Test := false + +//////////////////// +// publishing + +pomExtra := + http://nbronson.github.com/scala-stm/ + + + BSD + https://github.com/nbronson/scala-stm/blob/master/LICENSE.txt + repo + + + + scm:git:git@github.com:nbronson/scala-stm.git + git@github.com:nbronson/scala-stm.git + + + + nbronson + Nathan Bronson + ngbronson@gmail.com + + + +publishMavenStyle := true + +publishTo <<= (version) { v: String => + val base = "https://oss.sonatype.org/" + if (v.trim.endsWith("SNAPSHOT")) + Some("snapshots" at base + "content/repositories/snapshots/") + else + Some("releases" at base + "service/local/staging/deploy/maven2/") + } + +// exclude scalatest from the Maven POM +pomPostProcess := { xi: scala.xml.Node => + import scala.xml._ + val badDeps = (xi \\ "dependency") filter { + x => (x \ "artifactId").text != "scala-library" + } toSet + def filt(root: Node): Node = root match { + case x: Elem => { + val ch = x.child filter { !badDeps(_) } map { filt(_) } + Elem(x.prefix, x.label, x.attributes, x.scope, ch: _*) + } + case x => x + } + filt(xi) + } + +credentials += Credentials(Path.userHome / ".ivy2" / ".credentials") diff --git a/main/src/test/resources/session-settings-quick/3.sbt.txt b/main/src/test/resources/session-settings-quick/3.sbt.txt new file mode 100644 index 000000000..6db83e79c --- /dev/null +++ b/main/src/test/resources/session-settings-quick/3.sbt.txt @@ -0,0 +1,16 @@ +import sbt._ + +val scmpom = taskKey[xml.NodeBuffer]("Node buffer") + +scmpom := + git@github.com:mohiva/play-html-compressor.git + scm:git:git@github.com:mohiva/play-html-compressor.git + + + + akkie + Christian Kaps + http://mohiva.com + + + \ No newline at end of file diff --git a/main/src/test/resources/session-settings-quick/3.sbt.txt_1/1.set b/main/src/test/resources/session-settings-quick/3.sbt.txt_1/1.set new file mode 100644 index 000000000..3e2aa8d48 --- /dev/null +++ b/main/src/test/resources/session-settings-quick/3.sbt.txt_1/1.set @@ -0,0 +1 @@ +scmpom := OK \ No newline at end of file diff --git a/main/src/test/resources/session-settings-quick/3.sbt.txt_1/1.set.result b/main/src/test/resources/session-settings-quick/3.sbt.txt_1/1.set.result new file mode 100644 index 000000000..c992f0b5a --- /dev/null +++ b/main/src/test/resources/session-settings-quick/3.sbt.txt_1/1.set.result @@ -0,0 +1,5 @@ +import sbt._ + +val scmpom = taskKey[xml.NodeBuffer]("Node buffer") + +scmpom := (OK) \ No newline at end of file diff --git a/main/src/test/resources/session-settings-quick/4.sbt.txt b/main/src/test/resources/session-settings-quick/4.sbt.txt deleted file mode 100644 index 5d1bcd5b2..000000000 --- a/main/src/test/resources/session-settings-quick/4.sbt.txt +++ /dev/null @@ -1,17 +0,0 @@ -k1 := {} - -k2 := {} - -k3 := { - - - val x = "hi" - () -} - -k4 := { }; k5 := () - -k1 <<= k1 map {_ => sys.error("k1")} - -k4 := { val x = k4.value; () } - diff --git a/main/src/test/resources/session-settings-quick/4.sbt.txt_1/1.set b/main/src/test/resources/session-settings-quick/4.sbt.txt_1/1.set deleted file mode 100644 index 2a3525578..000000000 --- a/main/src/test/resources/session-settings-quick/4.sbt.txt_1/1.set +++ /dev/null @@ -1 +0,0 @@ -k4 := () diff --git a/main/src/test/resources/session-settings-quick/4.sbt.txt_1/1.set.result b/main/src/test/resources/session-settings-quick/4.sbt.txt_1/1.set.result deleted file mode 100644 index 3f5ec19f4..000000000 --- a/main/src/test/resources/session-settings-quick/4.sbt.txt_1/1.set.result +++ /dev/null @@ -1,15 +0,0 @@ -k1 := {} - -k2 := {} - -k3 := { - - - val x = "hi" - () -} - -k4 := (); k5 := () - -k1 <<= k1 map {_ => sys.error("k1")} - diff --git a/main/src/test/resources/session-settings/3.sbt.txt_1/1.set.result b/main/src/test/resources/session-settings/3.sbt.txt_1/1.set.result index 6b1956fb1..2c18733df 100644 --- a/main/src/test/resources/session-settings/3.sbt.txt_1/1.set.result +++ b/main/src/test/resources/session-settings/3.sbt.txt_1/1.set.result @@ -2,4 +2,4 @@ import sbt._ val scmpom = taskKey[xml.NodeBuffer]("Node buffer") -scmpom := ( OK ) \ No newline at end of file +scmpom := OK; \ No newline at end of file diff --git a/main/src/test/scala/sbt/internals/parser/EvaluateConfigurationsOriginal.scala b/main/src/test/scala/sbt/internals/parser/EvaluateConfigurationsOriginal.scala index b7c53c7c5..71580529f 100644 --- a/main/src/test/scala/sbt/internals/parser/EvaluateConfigurationsOriginal.scala +++ b/main/src/test/scala/sbt/internals/parser/EvaluateConfigurationsOriginal.scala @@ -2,40 +2,16 @@ package sbt.internals.parser import java.io.File -import sbt.LineRange +import sbt.{ EvaluateConfigurations, LineRange } import scala.annotation.tailrec +@deprecated("This class is be removed. Only for test backward compatibility", "1.0") object EvaluateConfigurationsOriginal { - private[this] def isSpace = (c: Char) => Character isWhitespace c - private[this] def fstS(f: String => Boolean): ((String, Int)) => Boolean = { case (s, i) => f(s) } - private[this] def firstNonSpaceIs(lit: String) = (_: String).view.dropWhile(isSpace).startsWith(lit) - private[this] def or[A](a: A => Boolean, b: A => Boolean): A => Boolean = in => a(in) || b(in) - def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)]) = { - val blank = (_: String).forall(isSpace) - val isImport = firstNonSpaceIs("import ") - val comment = firstNonSpaceIs("//") - val blankOrComment = or(blank, comment) - val importOrBlank = fstS(or(blankOrComment, isImport)) - - val (imports, settings) = lines.zipWithIndex span importOrBlank - (imports filterNot fstS(blankOrComment), groupedLines(settings, blank, blankOrComment)) + EvaluateConfigurations.splitExpressions(lines) } - def groupedLines(lines: Seq[(String, Int)], delimiter: String => Boolean, skipInitial: String => Boolean): Seq[(String, LineRange)] = - { - val fdelim = fstS(delimiter) - @tailrec def group0(lines: Seq[(String, Int)], accum: Seq[(String, LineRange)]): Seq[(String, LineRange)] = - if (lines.isEmpty) accum.reverse - else { - val start = lines dropWhile fstS(skipInitial) - val (next, tail) = start.span { case (s, _) => !delimiter(s) } - val grouped = if (next.isEmpty) accum else (next.map(_._1).mkString("\n"), LineRange(next.head._2, next.last._2 + 1)) +: accum - group0(tail, grouped) - } - group0(lines, Nil) - } } \ No newline at end of file diff --git a/main/src/test/scala/sbt/internals/parser/SessionSettingsSpec.scala b/main/src/test/scala/sbt/internals/parser/SessionSettingsSpec.scala index db64a065a..aa5007020 100644 --- a/main/src/test/scala/sbt/internals/parser/SessionSettingsSpec.scala +++ b/main/src/test/scala/sbt/internals/parser/SessionSettingsSpec.scala @@ -70,4 +70,4 @@ abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec class SessionSettingsSpec extends AbstractSessionSettingsSpec("session-settings") -//class SessionSettingsQuickSpec extends AbstractSessionSettingsSpec("session-settings-quick", true) \ No newline at end of file +class SessionSettingsQuickSpec extends AbstractSessionSettingsSpec("session-settings-quick") \ No newline at end of file diff --git a/main/src/test/scala/sbt/internals/parser/SplitExpressionsFilesTest.scala b/main/src/test/scala/sbt/internals/parser/SplitExpressionsFilesTest.scala index 704e12366..bf566aa54 100644 --- a/main/src/test/scala/sbt/internals/parser/SplitExpressionsFilesTest.scala +++ b/main/src/test/scala/sbt/internals/parser/SplitExpressionsFilesTest.scala @@ -12,6 +12,8 @@ import scala.tools.reflect.ToolBoxError class SplitExpressionsFilesTest extends AbstractSplitExpressionsFilesTest("/old-format/") +//class SplitExpressionsFilesFailedTest extends AbstractSplitExpressionsFilesTest("/fail-format/") + abstract class AbstractSplitExpressionsFilesTest(pathName: String) extends Specification { case class SplitterComparison(oldSplitterResult: util.Try[(Seq[(String, Int)], Seq[LineRange])], newSplitterResult: util.Try[(Seq[(String, Int)], Seq[LineRange])])