diff --git a/main/src/main/scala/sbt/internals/parser/SplitExpressionsNoBlankies.scala b/main/src/main/scala/sbt/internals/parser/SplitExpressionsNoBlankies.scala index 381efc9d2..d89ffd8b4 100644 --- a/main/src/main/scala/sbt/internals/parser/SplitExpressionsNoBlankies.scala +++ b/main/src/main/scala/sbt/internals/parser/SplitExpressionsNoBlankies.scala @@ -4,26 +4,28 @@ package parser import java.io.File -import SplitExpressionsNoBlankies._ +import sbt.internals.parser.SplitExpressionsNoBlankies._ + import scala.annotation.tailrec import scala.reflect.runtime.universe._ private[sbt] object SplitExpressionsNoBlankies { val END_OF_LINE_CHAR = '\n' val END_OF_LINE = String.valueOf(END_OF_LINE_CHAR) + private[parser] val NOT_FOUND_INDEX = -1 private[sbt] val FAKE_FILE = new File("fake") } private[sbt] case class SplitExpressionsNoBlankies(file: File, lines: Seq[String]) { - //settingsTrees needed for "session save" + //settingsTrees,modifiedContent needed for "session save" 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)], String) = { - import scala.reflect.runtime._ + import sbt.internals.parser.MissingBracketHandler._ + import sbt.internals.parser.XmlContent._ import scala.compat.Platform.EOL - import MissingBracketHandler._ - import XmlContent._ + import scala.reflect.runtime._ import scala.tools.reflect.{ ToolBox, ToolBoxError } val mirror = universe.runtimeMirror(this.getClass.getClassLoader) @@ -83,7 +85,7 @@ private[sbt] case class SplitExpressionsNoBlankies(file: File, lines: Seq[String if (t.pos.isDefined) { val originalStatement = modifiedContent.substring(t.pos.start, t.pos.end) val statement = parseStatementAgain(t, originalStatement) - val numberLines = statement.count(c => c == END_OF_LINE_CHAR) + val numberLines = countLines(statement) Some((statement, t, LineRange(t.pos.line - 1, t.pos.line + numberLines))) } else { None @@ -91,11 +93,13 @@ private[sbt] case class SplitExpressionsNoBlankies(file: File, lines: Seq[String val statementsTreeLineRange = statements flatMap convertStatement (imports map convertImport, statementsTreeLineRange.map(t => (t._1, t._3)), statementsTreeLineRange.map(t => (t._1, t._2)), modifiedContent) } + + private def countLines(statement: String) = statement.count(c => c == END_OF_LINE_CHAR) } /** * Scala parser cuts last bracket - - * @see http://stackoverflow.com/questions/25547149/scala-parser-cuts-last-bracket + * @see https://github.com/scala/scala/pull/3991 */ private[sbt] object MissingBracketHandler { /** @@ -105,7 +109,7 @@ private[sbt] object MissingBracketHandler { * @param positionLine - number of start position line * @param fileName - file name * @param originalException - original exception - * @return + * @return missing text */ private[sbt] def findMissingText(content: String, positionEnd: Int, positionLine: Int, fileName: String, originalException: Throwable): String = { findClosingBracketIndex(content, positionEnd) match { @@ -131,7 +135,7 @@ private[sbt] object MissingBracketHandler { */ private[sbt] def findClosingBracketIndex(content: String, from: Int): Option[Int] = { val index = content.indexWhere(c => c == '}' || c == ')', from) - if (index == -1) { + if (index == NOT_FOUND_INDEX) { None } else { Some(index) @@ -152,6 +156,17 @@ private[sbt] object MissingBracketHandler { * */ private[sbt] object XmlContent { + + private val OPEN_PARENTHESIS = '{' + + private val OPEN_CURLY_BRACKET = '(' + + private val DOUBLE_SLASH = "//" + + private val OPEN_BRACKET = s" $OPEN_CURLY_BRACKET " + + private val CLOSE_BRACKET = " ) " + /** * * @param original - file content @@ -206,7 +221,7 @@ private[sbt] object XmlContent { private def searchForTagName(text: String, startIndex: Int, endIndex: Int) = { val subs = text.substring(startIndex, endIndex) val spaceIndex = subs.indexWhere(c => c.isWhitespace, 1) - if (spaceIndex == -1) { + if (spaceIndex == NOT_FOUND_INDEX) { subs } else { subs.substring(0, spaceIndex) @@ -222,7 +237,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) - if (endIndex == -1) { + if (endIndex == NOT_FOUND_INDEX) { acc } else { val xmlFragment = findModifiedOpeningTag(content, offsetIndex, endIndex) @@ -233,7 +248,7 @@ private[sbt] object XmlContent { private def findModifiedOpeningTag(content: String, offsetIndex: Int, endIndex: Int): Option[(String, Int, Int)] = { val startIndex = content.substring(offsetIndex, endIndex).lastIndexOf("<") - if (startIndex == -1) { + if (startIndex == NOT_FOUND_INDEX) { None } else { val tagName = searchForTagName(content, startIndex + 1 + offsetIndex, endIndex) @@ -249,7 +264,7 @@ private[sbt] object XmlContent { private def searchForOpeningIndex(text: String, closeTagStartIndex: Int, tagName: String) = { val subs = text.substring(0, closeTagStartIndex) val index = subs.lastIndexOf(s"<$tagName>") - if (index == -1) { + if (index == NOT_FOUND_INDEX) { subs.lastIndexOf(s"<$tagName ") } else { index @@ -265,11 +280,11 @@ private[sbt] object XmlContent { @tailrec private def findNotModifiedOpeningTags(content: String, current: Int, acc: Seq[(String, Int, Int)]): Seq[(String, Int, Int)] = { val closeTagStartIndex = content.indexOf("", closeTagStartIndex) - if (closeTagEndIndex == -1) { + if (closeTagEndIndex == NOT_FOUND_INDEX) { findNotModifiedOpeningTags(content, closeTagStartIndex + 2, acc) } else { val xmlFragment = findNotModifiedOpeningTag(content, closeTagStartIndex, closeTagEndIndex) @@ -301,39 +316,46 @@ 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, 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) + val (correctedStmt, shouldAddCloseBrackets, wasXml, _) = addBracketsIfNecessary(statements) + val closeIfNecessaryCorrectedStmt = + if (shouldAddCloseBrackets && wasXml) { + correctedStmt.head +: CLOSE_BRACKET +: correctedStmt.tail + } else { + correctedStmt + } + closeIfNecessaryCorrectedStmt.reverse.mkString + } + + 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 (newAddCloseBrackets, newAcc) = if (isXml) { - if (wasXml) { - (addCloseBrackets, bAcc) - } else { - if (areBracketsNecessary(previous)) { - (true, " ( " +: bAcc) - } else { - (false, bAcc) - } - } - } else if (addCloseBrackets) { - (false, " ) " +: bAcc) + val (newShouldAddCloseBracket, newStmtAcc) = if (isXml) { + addOpenBracketIfNecessary(accStmt, shouldAddCloseBracket, prvWasXml, prvStmt) + } else if (shouldAddCloseBracket) { + (false, CLOSE_BRACKET +: accStmt) } else { - (false, bAcc) + (false, accStmt) } - (content +: newAcc, newAddCloseBrackets, isXml, content) + (stmt +: newStmtAcc, newShouldAddCloseBracket, isXml, stmt) } } - val closeIfNecessaryBuilder = - if (addCloseBrackets && wasXml) { - builder.head +: " ) " +: builder.tail - } else { - builder - } - closeIfNecessaryBuilder.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) + } + } + /** * Add to head if option is not empty * @param ts - seq @@ -348,7 +370,7 @@ private[sbt] object XmlContent { val tagName = content.substring(closeTagStartIndex + 2, closeTagEndIndex) if (xml.Utility.isName(tagName)) { val openTagIndex = searchForOpeningIndex(content, closeTagStartIndex, tagName) - if (openTagIndex == -1) { + if (openTagIndex == NOT_FOUND_INDEX) { None } else { xmlFragmentOption(content, openTagIndex, closeTagEndIndex + 1) @@ -379,13 +401,13 @@ private[sbt] object XmlContent { * @return are brackets necessary? */ private def areBracketsNecessary(statement: String): Boolean = { - val doubleSlash = statement.indexOf("//") + val doubleSlash = statement.indexOf(DOUBLE_SLASH) val endOfLine = statement.indexOf(END_OF_LINE) - if (doubleSlash == -1 || (doubleSlash < endOfLine)) { - val roundBrackets = statement.lastIndexOf("(") - val braces = statement.lastIndexOf("{") + if (doubleSlash == NOT_FOUND_INDEX || (doubleSlash < endOfLine)) { + val roundBrackets = statement.lastIndexOf(OPEN_CURLY_BRACKET) + val braces = statement.lastIndexOf(OPEN_PARENTHESIS) val max = roundBrackets.max(braces) - if (max == -1) { + if (max == NOT_FOUND_INDEX) { true } else { val trimmed = statement.substring(max + 1).trim