mirror of https://github.com/sbt/sbt.git
tab completion fixes and cleanup
This commit is contained in:
parent
69a04326b9
commit
e498b9bd3a
|
|
@ -12,35 +12,48 @@ package sbt
|
|||
|
||||
object Act
|
||||
{
|
||||
// this does not take delegation into account
|
||||
val GlobalString = "*"
|
||||
|
||||
// this does not take aggregation into account
|
||||
def scopedKey(index: KeyIndex, current: ProjectRef, defaultConfig: ProjectRef => Option[String], keyMap: Map[String, AttributeKey[_]]): Parser[ScopedKey[_]] =
|
||||
{
|
||||
for {
|
||||
proj <- optProjectRef(index, current)
|
||||
confAmb <- config( index configs proj )
|
||||
(key, conf) <- key(index, proj, configs(confAmb, defaultConfig, proj), keyMap) }
|
||||
yield
|
||||
ScopedKey( Scope( Select(proj), toAxis(conf map ConfigKey.apply, Global), Global, Global), key )
|
||||
keyConf <- key(index, proj, configs(confAmb, defaultConfig, proj), keyMap)
|
||||
taskExtra <- taskExtrasParser(keyMap, IMap.empty) }
|
||||
yield {
|
||||
val (key, conf) = keyConf
|
||||
val (task, extra) = taskExtra
|
||||
ScopedKey( Scope( Select(proj), toAxis(conf map ConfigKey.apply, Global), task, extra), key )
|
||||
}
|
||||
}
|
||||
def examplesStrict(p: Parser[String], exs: Set[String]): Parser[String] =
|
||||
p examples exs filter exs
|
||||
|
||||
|
||||
def optionalAxis[T](p: Parser[T], ifNone: ScopeAxis[T]): Parser[ScopeAxis[T]] =
|
||||
p.? map { opt => toAxis(opt, ifNone) }
|
||||
def toAxis[T](opt: Option[T], ifNone: ScopeAxis[T]): ScopeAxis[T] =
|
||||
opt match { case Some(t) => Select(t); case None => ifNone }
|
||||
def defaultConfig(data: Settings[Scope])(project: ProjectRef): Option[String] =
|
||||
thisProject in project get data flatMap( _.configurations.headOption.map(_.name))
|
||||
|
||||
def config(confs: Set[String]): Parser[Option[String]] =
|
||||
token( examplesStrict(ID, confs) <~ ':' ).?
|
||||
token( (examplesStrict(ID, confs) | GlobalString) <~ ':' ).?
|
||||
|
||||
def configs(explicit: Option[String], defaultConfig: ProjectRef => Option[String], proj: ProjectRef): List[Option[String]] =
|
||||
if(explicit.isDefined) explicit :: Nil else None :: defaultConfig(proj) :: Nil
|
||||
explicit match
|
||||
{
|
||||
case None => None :: defaultConfig(proj) :: Nil
|
||||
case Some(GlobalString) => None :: Nil
|
||||
case Some(_) => explicit :: Nil
|
||||
}
|
||||
|
||||
def key(index: KeyIndex, proj: ProjectRef, confs: Seq[Option[String]], keyMap: Map[String,AttributeKey[_]]): Parser[(AttributeKey[_], Option[String])] =
|
||||
{
|
||||
val confMap = confs map { conf => (conf, index.keys(proj, conf)) } toMap;
|
||||
val allKeys = (Set.empty[String] /: confMap.values)(_ ++ _)
|
||||
val filteredKeys = allKeys.filter(Command.validID)
|
||||
token(ID examples filteredKeys).flatMap { keyString =>
|
||||
token(ID examples allKeys).flatMap { keyString =>
|
||||
val conf = confMap.flatMap { case (key, value) => if(value contains keyString) key :: Nil else Nil } headOption;
|
||||
getKey(keyMap, keyString, k => (k, conf.flatMap(identity)))
|
||||
}
|
||||
|
|
@ -51,6 +64,45 @@ object Act
|
|||
case None => failure("Invalid key: " + keyString)
|
||||
}
|
||||
|
||||
val spacedComma = token(OptSpace ~ ',' ~ OptSpace)
|
||||
|
||||
def taskExtrasParser(knownKeys: Map[String, AttributeKey[_]], knownValues: IMap[AttributeKey, Set]): Parser[(ScopeAxis[AttributeKey[_]], ScopeAxis[AttributeMap])] =
|
||||
{
|
||||
val extras = extrasParser(knownKeys, knownValues)
|
||||
val taskAndExtra =
|
||||
optionalAxis(taskAxisParser(knownKeys), Global) flatMap { taskAxis =>
|
||||
if(taskAxis.isSelect)
|
||||
optionalAxis(spacedComma ~> extras, Global) map { x => (taskAxis, x) }
|
||||
else
|
||||
extras map { x => (taskAxis, Select(x)) }
|
||||
}
|
||||
val base = token('(') ~> taskAndExtra <~ token(')')
|
||||
base ?? ( (Global, Global) )
|
||||
}
|
||||
|
||||
def taskAxisParser(knownKeys: Map[String, AttributeKey[_]]): Parser[AttributeKey[_]] =
|
||||
token("for" ~ Space) ~> knownIDParser(knownKeys)
|
||||
|
||||
def extrasParser(knownKeys: Map[String, AttributeKey[_]], knownValues: IMap[AttributeKey, Set]): Parser[AttributeMap] =
|
||||
{
|
||||
val validKeys = knownKeys.filter { case (_, key) => knownValues get key exists(!_.isEmpty) }
|
||||
if(validKeys.isEmpty)
|
||||
failure("")
|
||||
else
|
||||
rep1sep( extraParser(validKeys, knownValues), spacedComma) map AttributeMap.apply
|
||||
}
|
||||
|
||||
def extraParser(knownKeys: Map[String, AttributeKey[_]], knownValues: IMap[AttributeKey, Set]): Parser[AttributeEntry[_]] =
|
||||
{
|
||||
val keyp = knownIDParser(knownKeys) <~ token(':' ~ OptSpace)
|
||||
keyp flatMap { case key: AttributeKey[t] =>
|
||||
val valueMap: Map[String,t] = knownValues(key).map( v => (v.toString, v)).toMap
|
||||
knownIDParser(valueMap) map { value => AttributeEntry(key, value) }
|
||||
}
|
||||
}
|
||||
def knownIDParser[T](knownKeys: Map[String, T]): Parser[T] =
|
||||
token(examplesStrict(ID, knownKeys.keys.toSet)) map knownKeys
|
||||
|
||||
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ProjectRef] =
|
||||
{
|
||||
val uris = index.buildURIs
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package sbt
|
|||
import classpath.ClasspathUtilities
|
||||
import scala.annotation.tailrec
|
||||
import collection.mutable
|
||||
import complete.DefaultParsers.validID
|
||||
import Compiler.{Compilers,Inputs}
|
||||
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
||||
import Keys.{appConfiguration, baseDirectory, configuration, streams, thisProject, thisProjectRef}
|
||||
|
|
@ -225,7 +226,7 @@ object Index
|
|||
def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] =
|
||||
{
|
||||
// AttributeEntry + the checked type test 'value: Task[_]' ensures that the cast is correct.
|
||||
// (scalac couldn't determine that 'key' is of type AttributeKey[Task[_]] on its own and a type match didn't still required the cast)
|
||||
// (scalac couldn't determine that 'key' is of type AttributeKey[Task[_]] on its own and a type match still required the cast)
|
||||
val pairs = for( scope <- data.scopes; AttributeEntry(key, value: Task[_]) <- data.data(scope).entries ) yield
|
||||
(value, ScopedKey(scope, key.asInstanceOf[AttributeKey[Task[_]]])) // unclear why this cast is needed even with a type test in the above filter
|
||||
pairs.toMap[Task[_], ScopedKey[Task[_]]]
|
||||
|
|
@ -235,7 +236,7 @@ object Index
|
|||
val multiMap = settings.data.values.flatMap(_.keys).toList.distinct.groupBy(_.label)
|
||||
val duplicates = multiMap collect { case (k, x1 :: x2 :: _) => k }
|
||||
if(duplicates.isEmpty)
|
||||
multiMap.mapValues(_.head)
|
||||
multiMap.collect { case (k, v) if validID(k) => (k, v.head) } toMap;
|
||||
else
|
||||
error(duplicates.mkString("AttributeKey ID collisions detected for '", "', '", "'"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ private[sbt] final class ArbitraryCommand(val parser: State => Parser[() => Stat
|
|||
|
||||
object Command
|
||||
{
|
||||
def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString;
|
||||
def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString;
|
||||
|
||||
import DefaultParsers._
|
||||
|
||||
|
|
@ -54,8 +54,7 @@ object Command
|
|||
|
||||
def custom(parser: State => Parser[() => State], help: Seq[Help] = Nil): Command = new ArbitraryCommand(parser, help, AttributeMap.empty)
|
||||
|
||||
def validID(name: String) =
|
||||
Parser(OpOrID)(name).resultEmpty.isDefined
|
||||
def validID(name: String) = DefaultParsers.matches(OpOrID, name)
|
||||
|
||||
def applyEffect[T](p: Parser[T])(f: T => State): Parser[() => State] =
|
||||
p map { t => () => f(t) }
|
||||
|
|
@ -93,7 +92,8 @@ object Command
|
|||
def commandError(command: String, msg: String, index: Int): String =
|
||||
{
|
||||
val (line, modIndex) = extractLine(command, index)
|
||||
msg + "\n" + line + "\n" + pointerSpace(msg, modIndex) + "^"
|
||||
val point = pointerSpace(command, modIndex)
|
||||
msg + "\n" + line + "\n" + point + "^"
|
||||
}
|
||||
def extractLine(s: String, i: Int): (String, Int) =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -356,10 +356,10 @@ object Defaults
|
|||
{ state =>
|
||||
import DefaultParsers._
|
||||
def distinctParser(exs: Set[String]): Parser[Seq[String]] =
|
||||
token(Space ~> NotSpace.examples(exs)).flatMap(ex => distinctParser(exs - ex).map(ex +: _)) ?? Nil
|
||||
token(Space ~> (NotSpace - "--").examples(exs) ).flatMap(ex => distinctParser(exs - ex).map(ex +: _)) ?? Nil
|
||||
val tests = savedLines(state, resolved, definedTests)
|
||||
val selectTests = distinctParser(tests.toSet) // todo: proper IDs
|
||||
val options = (Space ~> "--" ~> spaceDelimited("<arg>")) ?? Nil
|
||||
val options = (token(Space ~> "--") ~> spaceDelimited("<option>")) ?? Nil
|
||||
selectTests ~ options
|
||||
}
|
||||
def savedLines(state: State, reader: ScopedKey[_], readFrom: Scoped): Seq[String] =
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package sbt
|
|||
|
||||
import java.net.URI
|
||||
import Project.ScopedKey
|
||||
import complete.DefaultParsers.validID
|
||||
|
||||
object KeyIndex
|
||||
{
|
||||
|
|
@ -51,6 +52,8 @@ private final class KeyIndex0(val data: Map[URI, Map[Option[String], Map[ Option
|
|||
private[this] def getOr[A,B](m: Map[A,B], key: A, or: B): B = m.getOrElse(key, or)
|
||||
|
||||
def add(scoped: ScopedKey[_]): ExtendableKeyIndex =
|
||||
if(validID(scoped.key.label)) add0(scoped) else this
|
||||
private[this] def add0(scoped: ScopedKey[_]): ExtendableKeyIndex =
|
||||
scoped.scope match
|
||||
{
|
||||
case Scope(Select(ref: ResolvedReference), config, _, _) => addRef(ref, config, scoped.key)
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ object BuiltinCommands
|
|||
}
|
||||
|
||||
def multiParser(s: State): Parser[Seq[String]] =
|
||||
( token(';' ~> OptSpace) flatMap { _ => token(matched(s.combinedParser) <~ OptSpace ) } ).+
|
||||
( token(';' ~> OptSpace) flatMap { _ => matched(s.combinedParser) <~ token(OptSpace) } ).+
|
||||
def multiApplied(s: State) =
|
||||
Command.applyEffect( multiParser(s) )( _ ::: s )
|
||||
|
||||
|
|
|
|||
|
|
@ -163,10 +163,13 @@ sealed trait ScopeAxis[+S] {
|
|||
}
|
||||
def toOption: Option[S] = foldStrict(Some.apply, None, None)
|
||||
def map[T](f: S => T): ScopeAxis[T] = foldStrict(s => Select(f(s)), Global, This)
|
||||
def isSelect: Boolean = false
|
||||
}
|
||||
case object This extends ScopeAxis[Nothing]
|
||||
case object Global extends ScopeAxis[Nothing]
|
||||
final case class Select[S](s: S) extends ScopeAxis[S]
|
||||
final case class Select[S](s: S) extends ScopeAxis[S] {
|
||||
override def isSelect = true
|
||||
}
|
||||
object ScopeAxis
|
||||
{
|
||||
implicit def scopeAxisToScope(axis: ScopeAxis[Nothing]): Scope =
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ trait AttributeMap
|
|||
def contains[T](k: AttributeKey[T]): Boolean
|
||||
def put[T](k: AttributeKey[T], value: T): AttributeMap
|
||||
def keys: Iterable[AttributeKey[_]]
|
||||
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap
|
||||
def ++(o: AttributeMap): AttributeMap
|
||||
def entries: Iterable[AttributeEntry[_]]
|
||||
def isEmpty: Boolean
|
||||
|
|
@ -32,6 +33,8 @@ trait AttributeMap
|
|||
object AttributeMap
|
||||
{
|
||||
val empty: AttributeMap = new BasicAttributeMap(Map.empty)
|
||||
def apply(entries: Iterable[AttributeEntry[_]]): AttributeMap = empty ++ entries
|
||||
def apply(entries: AttributeEntry[_]*): AttributeMap = empty ++ entries
|
||||
implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) {
|
||||
def apply[T](key: AttributeKey[T]): T = map(key)
|
||||
}
|
||||
|
|
@ -45,6 +48,11 @@ private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
|
|||
def contains[T](k: AttributeKey[T]) = backing.contains(k)
|
||||
def put[T](k: AttributeKey[T], value: T): AttributeMap = new BasicAttributeMap( backing.updated(k, value) )
|
||||
def keys: Iterable[AttributeKey[_]] = backing.keys
|
||||
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap =
|
||||
{
|
||||
val newBacking = (backing /: o) { case (b, AttributeEntry(key, value)) => b.updated(key, value) }
|
||||
new BasicAttributeMap(newBacking)
|
||||
}
|
||||
def ++(o: AttributeMap): AttributeMap =
|
||||
o match {
|
||||
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
|
||||
|
|
|
|||
Loading…
Reference in New Issue