mirror of https://github.com/sbt/sbt.git
Remember the range for settings read from .sbt files
This commit is contained in:
parent
5fa93ca9f9
commit
a52d36987c
|
|
@ -10,7 +10,7 @@ package sbt
|
||||||
import complete.DefaultParsers.validID
|
import complete.DefaultParsers.validID
|
||||||
import Compiler.Compilers
|
import Compiler.Compilers
|
||||||
import Keys.{globalBaseDirectory, globalPluginsDirectory, globalSettingsDirectory, stagingDirectory, Streams}
|
import Keys.{globalBaseDirectory, globalPluginsDirectory, globalSettingsDirectory, stagingDirectory, Streams}
|
||||||
import Project.{ScopedKey, Setting, SourceCoord}
|
import Project.{ScopedKey, Setting}
|
||||||
import Keys.{globalBaseDirectory, Streams}
|
import Keys.{globalBaseDirectory, Streams}
|
||||||
import Scope.GlobalScope
|
import Scope.GlobalScope
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
@ -73,8 +73,8 @@ object EvaluateConfigurations
|
||||||
def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String], offset: Int): ClassLoader => Seq[Setting[_]] =
|
def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String], offset: Int): ClassLoader => Seq[Setting[_]] =
|
||||||
{
|
{
|
||||||
val (importExpressions, settingExpressions) = splitExpressions(lines)
|
val (importExpressions, settingExpressions) = splitExpressions(lines)
|
||||||
val settings = addOffset(offset, settingExpressions) map { case (settingExpression,line) =>
|
val settings = addOffsetToRange(offset, settingExpressions) map { case (settingExpression,range) =>
|
||||||
evaluateSetting(eval, name, (imports.map(s => (s, -1)) ++ addOffset(offset, importExpressions)), settingExpression, line)
|
evaluateSetting(eval, name, (imports.map(s => (s, -1)) ++ addOffset(offset, importExpressions)), settingExpression, range)
|
||||||
}
|
}
|
||||||
flatten(settings)
|
flatten(settings)
|
||||||
}
|
}
|
||||||
|
|
@ -82,24 +82,26 @@ object EvaluateConfigurations
|
||||||
loader => mksettings.flatMap(_ apply loader)
|
loader => mksettings.flatMap(_ apply loader)
|
||||||
def addOffset(offset: Int, lines: Seq[(String,Int)]): Seq[(String,Int)] =
|
def addOffset(offset: Int, lines: Seq[(String,Int)]): Seq[(String,Int)] =
|
||||||
lines.map { case (s, i) => (s, i + offset) }
|
lines.map { case (s, i) => (s, i + offset) }
|
||||||
|
def addOffsetToRange(offset: Int, ranges: Seq[(String,LineRange)]): Seq[(String,LineRange)] =
|
||||||
|
ranges.map { case (s, r) => (s, r shift offset) }
|
||||||
|
|
||||||
def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, line: Int): ClassLoader => Seq[Setting[_]] =
|
def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, range: LineRange): ClassLoader => Seq[Setting[_]] =
|
||||||
{
|
{
|
||||||
val result = try {
|
val result = try {
|
||||||
eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some("sbt.Project.SettingsDefinition"), line = line)
|
eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some("sbt.Project.SettingsDefinition"), line = range.start)
|
||||||
} catch {
|
} catch {
|
||||||
case e: sbt.compiler.EvalException => throw new MessageOnlyException(e.getMessage)
|
case e: sbt.compiler.EvalException => throw new MessageOnlyException(e.getMessage)
|
||||||
}
|
}
|
||||||
loader => {
|
loader => {
|
||||||
val coord = SourceCoord(name, line + 1)
|
val pos = RangePosition(name, range shift 1)
|
||||||
result.getValue(loader).asInstanceOf[Project.SettingsDefinition].settings map (_ withPos coord)
|
result.getValue(loader).asInstanceOf[Project.SettingsDefinition].settings map (_ withPos pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private[this] def isSpace = (c: Char) => Character isWhitespace c
|
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 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 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)
|
private[this] def or[A](a: A => Boolean, b: A => Boolean): A => Boolean = in => a(in) || b(in)
|
||||||
def splitExpressions(lines: Seq[String]): (Seq[(String,Int)], Seq[(String,Int)]) =
|
def splitExpressions(lines: Seq[String]): (Seq[(String,Int)], Seq[(String,LineRange)]) =
|
||||||
{
|
{
|
||||||
val blank = (_: String).forall(isSpace)
|
val blank = (_: String).forall(isSpace)
|
||||||
val isImport = firstNonSpaceIs("import ")
|
val isImport = firstNonSpaceIs("import ")
|
||||||
|
|
@ -110,16 +112,16 @@ object EvaluateConfigurations
|
||||||
val (imports, settings) = lines.zipWithIndex span importOrBlank
|
val (imports, settings) = lines.zipWithIndex span importOrBlank
|
||||||
(imports filterNot fstS( blankOrComment ), groupedLines(settings, blank, blankOrComment))
|
(imports filterNot fstS( blankOrComment ), groupedLines(settings, blank, blankOrComment))
|
||||||
}
|
}
|
||||||
def groupedLines(lines: Seq[(String,Int)], delimiter: String => Boolean, skipInitial: String => Boolean): Seq[(String,Int)] =
|
def groupedLines(lines: Seq[(String,Int)], delimiter: String => Boolean, skipInitial: String => Boolean): Seq[(String,LineRange)] =
|
||||||
{
|
{
|
||||||
val fdelim = fstS(delimiter)
|
val fdelim = fstS(delimiter)
|
||||||
@tailrec def group0(lines: Seq[(String,Int)], accum: Seq[(String,Int)]): Seq[(String,Int)] =
|
@tailrec def group0(lines: Seq[(String,Int)], accum: Seq[(String,LineRange)]): Seq[(String,LineRange)] =
|
||||||
if(lines.isEmpty) accum.reverse
|
if(lines.isEmpty) accum.reverse
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val start = lines dropWhile fstS( skipInitial )
|
val start = lines dropWhile fstS( skipInitial )
|
||||||
val (next, tail) = start.span { case (s,_) => !delimiter(s) }
|
val (next, tail) = start.span { case (s,_) => !delimiter(s) }
|
||||||
val grouped = if(next.isEmpty) accum else (next.map(_._1).mkString("\n"), next.head._2) +: accum
|
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(tail, grouped)
|
||||||
}
|
}
|
||||||
group0(lines, Nil)
|
group0(lines, Nil)
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ object BuiltinCommands
|
||||||
def set = Command(SetCommand, setBrief, setDetailed)(setParser) { case (s, (all, arg)) =>
|
def set = Command(SetCommand, setBrief, setDetailed)(setParser) { case (s, (all, arg)) =>
|
||||||
val extracted = Project extract s
|
val extracted = Project extract s
|
||||||
import extracted._
|
import extracted._
|
||||||
val settings = EvaluateConfigurations.evaluateSetting(session.currentEval(), "<set>", imports(extracted), arg, 0)(currentLoader)
|
val settings = EvaluateConfigurations.evaluateSetting(session.currentEval(), "<set>", imports(extracted), arg, LineRange(0,0))(currentLoader)
|
||||||
val newSession = if(all) Project.setAll(extracted, settings) else setThis(s, extracted, settings, arg)
|
val newSession = if(all) Project.setAll(extracted, settings) else setThis(s, extracted, settings, arg)
|
||||||
reapply(newSession, structure, s)
|
reapply(newSession, structure, s)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ object Project extends Init[Scope] with ProjectExtra
|
||||||
val comp = compiled(structure.settings, actual)(structure.delegates, structure.scopeLocal, display)
|
val comp = compiled(structure.settings, actual)(structure.delegates, structure.scopeLocal, display)
|
||||||
val definedAt = comp get scoped map { c =>
|
val definedAt = comp get scoped map { c =>
|
||||||
def fmt(s: Setting[_]) = s.pos match {
|
def fmt(s: Setting[_]) = s.pos match {
|
||||||
case SourceCoord(fileName, line) => Some(fileName + ":" + line)
|
case pos: FilePosition => Some(pos.path + ":" + pos.startLine)
|
||||||
case NoPosition => None
|
case NoPosition => None
|
||||||
}
|
}
|
||||||
val posDefined = c.settings.map(fmt).flatten
|
val posDefined = c.settings.map(fmt).flatten
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
sealed trait SourcePosition
|
||||||
|
|
||||||
|
sealed trait FilePosition {
|
||||||
|
def path: String
|
||||||
|
def startLine: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
case object NoPosition extends SourcePosition
|
||||||
|
|
||||||
|
final case class LinePosition(path: String, startLine: Int) extends SourcePosition with FilePosition
|
||||||
|
|
||||||
|
final case class LineRange(start: Int, end: Int) {
|
||||||
|
def shift(n: Int) = new LineRange(start + n, end + n)
|
||||||
|
}
|
||||||
|
|
||||||
|
final case class RangePosition(path: String, range: LineRange) extends SourcePosition with FilePosition {
|
||||||
|
def startLine = range.start
|
||||||
|
}
|
||||||
|
|
@ -255,14 +255,10 @@ trait Init[Scope]
|
||||||
def mapKey(g: MapScoped): Setting[T] = new Setting(g(key), init, pos)
|
def mapKey(g: MapScoped): Setting[T] = new Setting(g(key), init, pos)
|
||||||
def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = new Setting(key, init(t => f(key,t)), pos)
|
def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = new Setting(key, init(t => f(key,t)), pos)
|
||||||
def mapConstant(g: MapConstant): Setting[T] = new Setting(key, init mapConstant g, pos)
|
def mapConstant(g: MapConstant): Setting[T] = new Setting(key, init mapConstant g, pos)
|
||||||
def withPos(pos: SourceCoord) = new Setting(key, init, pos)
|
def withPos(pos: SourcePosition) = new Setting(key, init, pos)
|
||||||
override def toString = "setting(" + key + ") at " + pos
|
override def toString = "setting(" + key + ") at " + pos
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait SourcePosition
|
|
||||||
case object NoPosition extends SourcePosition
|
|
||||||
final case class SourceCoord(fileName: String, line: Int) extends SourcePosition
|
|
||||||
|
|
||||||
// mainly for reducing generated class count
|
// mainly for reducing generated class count
|
||||||
private[this] def validateReferencedT(g: ValidateRef) =
|
private[this] def validateReferencedT(g: ValidateRef) =
|
||||||
new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateReferenced g }
|
new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateReferenced g }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue