Documentation and renaming of "blankies" into somethign a bit easier to find.

* Rename SPlitExpression* to `SbtParser` denoting that is parses .sbt files
* Adds a few todos
* Document APIs for internal usage.
This commit is contained in:
Josh Suereth 2014-10-01 13:15:20 -04:00 committed by Eugene Yokota
parent e230a17d3b
commit 009426d896
6 changed files with 64 additions and 15 deletions

View File

@ -8,7 +8,7 @@ import compiler.{ Eval, EvalImports }
import complete.DefaultParsers.validID
import Def.{ ScopedKey, Setting }
import Scope.GlobalScope
import sbt.internals.parser.SplitExpressionsNoBlankies
import sbt.internals.parser.SbtParser
import scala.annotation.tailrec
@ -218,7 +218,9 @@ object EvaluateConfigurations {
*/
private[sbt] def splitExpressions(file: File, lines: Seq[String]): (Seq[(String, Int)], Seq[(String, LineRange)]) =
{
val split = SplitExpressionsNoBlankies(file, lines)
val split = SbtParser(file, lines)
// TODO - Look at pulling the parsed expression trees from the SbtParser and stitch them back into a different
// scala compiler rather than re-parsing.
(split.imports, split.settings)
}

View File

@ -130,11 +130,18 @@ object SessionSettings {
oldState.log.warn("Discarding " + pluralize(oldSettings.size, " session setting") + ". Use 'session save' to persist session settings.")
}
@deprecated("This method will no longer be public", "0.13.7")
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) }
in.zipWithIndex.flatMap { case (t, index) => if (asSet(index + 1)) Nil else t :: Nil }
}
/**
* Removes settings from the current session, by range.
* @param s The current build state.
* @param ranges A set of Low->High tuples for which settings to remove.
* @return The new build state with settings removed.
*/
def removeSettings(s: State, ranges: Seq[(Int, Int)]): State =
withSettings(s) { session =>
val current = session.current
@ -169,6 +176,7 @@ object SessionSettings {
reapply(newSession.copy(original = newSession.mergeSettings, append = Map.empty), s)
}
@deprecated("This method will no longer be publlic", "0.13.7")
def writeSettings(pref: ProjectRef, settings: List[SessionSetting], original: Seq[Setting[_]], structure: BuildStructure): (Seq[SessionSetting], Seq[Setting[_]]) = {
val project = Project.getProject(pref, structure).getOrElse(sys.error("Invalid project reference " + pref))
val writeTo: File = BuildPaths.configurationSources(project.base).headOption.getOrElse(new File(project.base, "build.sbt"))
@ -209,6 +217,7 @@ object SessionSettings {
(newWithPos.reverse, other ++ oldShifted)
}
@deprecated("This method will no longer be publlic", "0.13.7")
def needsTrailingBlank(lines: Seq[String]) = !lines.isEmpty && !lines.takeRight(1).exists(_.trim.isEmpty)
/** Prints all the user-defined SessionSettings (not raw) to System.out. */
@ -221,12 +230,14 @@ object SessionSettings {
s
}
/** Prints all the defined session settings for the current project in the given build state. */
def printSettings(s: State): State =
withSettings(s) { session =>
printSettings(session.append.getOrElse(session.current, Nil))
s
}
/** Prints all the passed in session settings */
def printSettings(settings: Seq[SessionSetting]): Unit =
for (((_, stringRep), index) <- settings.zipWithIndex)
println(" " + (index + 1) + ". " + stringRep.mkString("\n"))
@ -291,7 +302,7 @@ save, save-all
def range: Parser[(Int, Int)] = (NatBasic ~ ('-' ~> NatBasic).?).map { case lo ~ hi => (lo, hi getOrElse lo) }
/** The raw implementation of the sessoin command. */
/** The raw implementation of the session command. */
def command(s: State) = Command.applyEffect(parser) {
case p: Print => if (p.all) printAllSettings(s) else printSettings(s)
case v: Save => if (v.all) saveAllSettings(s) else saveSettings(s)

View File

@ -4,20 +4,56 @@ package parser
import java.io.File
import sbt.internals.parser.SplitExpressionsNoBlankies._
import sbt.internals.parser.SbtParser._
import scala.annotation.tailrec
import scala.reflect.runtime.universe._
private[sbt] object SplitExpressionsNoBlankies {
private[sbt] object SbtParser {
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]) {
/**
* This method soley exists to add scaladoc to members in SbtParser which
* are defined using pattern matching.
*/
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
}
/**
* An initial parser/splitter of .sbt files.
*
* This class is responsible for chunking a `.sbt` file into expression ranges
* which we can then compile using the Scala compiler.
*
* Example:
*
* {{{
* val parser = SbtParser(myFile, IO.readLines(myFile))
* // All import statements
* val imports = parser.imports
* // All other statements (val x =, or raw settings)
* val settings = parser.settings
* }}}
*
* @param file The file we're parsing (may be a dummy file)
* @param lines The parsed "lines" of the file, where each string is a line.
*/
private[sbt] case class SbtParser(file: File, lines: Seq[String]) extends ParsedSbtFileExpressions {
//settingsTrees,modifiedContent needed for "session save"
// TODO - We should look into splitting out "defintiions" vs. "settings" here instead of further string lookups, since we have the
// parsed trees.
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) = {
@ -117,7 +153,7 @@ private[sbt] object MissingBracketHandler {
case Some(index) =>
val text = content.substring(positionEnd, index + 1)
val textWithoutBracket = text.substring(0, text.length - 1)
util.Try(SplitExpressionsNoBlankies(FAKE_FILE, textWithoutBracket.lines.toSeq)) match {
util.Try(SbtParser(FAKE_FILE, textWithoutBracket.lines.toSeq)) match {
case util.Success(_) =>
text
case util.Failure(th) =>

View File

@ -6,7 +6,7 @@ import scala.reflect.runtime.universe._
private[sbt] object SbtRefactorings {
import sbt.internals.parser.SplitExpressionsNoBlankies.{ END_OF_LINE, FAKE_FILE }
import sbt.internals.parser.SbtParser.{ END_OF_LINE, FAKE_FILE }
import sbt.SessionSettings.{ SessionSetting, SbtConfigFile }
val EMPTY_STRING = ""
@ -23,7 +23,7 @@ private[sbt] object SbtRefactorings {
*/
def applySessionSettings(configFile: SbtConfigFile, commands: Seq[SessionSetting]): SbtConfigFile = {
val (file, lines) = configFile
val split = SplitExpressionsNoBlankies(FAKE_FILE, lines)
val split = SbtParser(FAKE_FILE, lines)
val recordedCommands = recordCommands(commands, split)
val sortedRecordedCommands = recordedCommands.sortBy(_._1)(REVERSE_ORDERING_INT)
@ -46,7 +46,7 @@ private[sbt] object SbtRefactorings {
if (trimmed.isEmpty) trimmed else text
}
private def recordCommands(commands: Seq[SessionSetting], split: SplitExpressionsNoBlankies) =
private def recordCommands(commands: Seq[SessionSetting], split: SbtParser) =
commands.flatMap {
case (_, command) =>
val map = toTreeStringMap(command)
@ -56,7 +56,7 @@ private[sbt] object SbtRefactorings {
}
}
private def treesToReplacements(split: SplitExpressionsNoBlankies, name: String, command: Seq[String]) =
private def treesToReplacements(split: SbtParser, name: String, command: Seq[String]) =
split.settingsTrees.foldLeft(Seq.empty[(Int, String, String)]) {
case (acc, (st, tree)) =>
val treeName = extractSettingName(tree)
@ -73,7 +73,7 @@ private[sbt] object SbtRefactorings {
}
private def toTreeStringMap(command: Seq[String]) = {
val split = SplitExpressionsNoBlankies(FAKE_FILE, command)
val split = SbtParser(FAKE_FILE, command)
val trees = split.settingsTrees
val seq = trees.map {
case (statement, tree) =>

View File

@ -18,7 +18,7 @@ class ErrorSpec extends AbstractSpec with ScalaCheck {
foreach(new File(rootPath).listFiles) { file =>
print(s"Processing ${file.getName}: ")
val buildSbt = Source.fromFile(file).getLines().mkString("\n")
SplitExpressionsNoBlankies(file, buildSbt.lines.toSeq) must throwA[MessageOnlyException].like {
SbtParser(file, buildSbt.lines.toSeq) must throwA[MessageOnlyException].like {
case exp =>
val message = exp.getMessage
println(s"${exp.getMessage}")

View File

@ -37,8 +37,8 @@ abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec
foreach(expectedResultAndMap(file)) {
case (expectedResultList, commands) =>
val resultList = SbtRefactorings.applySessionSettings((file, originalLines), commands)
val expected = SplitExpressionsNoBlankies(file, expectedResultList)
val result = SplitExpressionsNoBlankies(file, resultList._2)
val expected = SbtParser(file, expectedResultList)
val result = SbtParser(file, resultList._2)
result.settings must_== expected.settings
}