mirror of https://github.com/sbt/sbt.git
Merge pull request #2855 from eed3si9n/wip/buildkey_fix
Add build-level keys to the tab completion
This commit is contained in:
commit
5141a4524a
|
|
@ -48,22 +48,12 @@ object Act {
|
|||
new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask)
|
||||
}
|
||||
|
||||
val projectKeys =
|
||||
for {
|
||||
rawProject <- optProjectRef(index, current)
|
||||
proj = resolveProject(rawProject, current)
|
||||
confAmb <- config(index configs proj)
|
||||
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
|
||||
} yield taskKeyExtra(proj, confAmb, partialMask)
|
||||
|
||||
val build = Some(BuildRef(current.build))
|
||||
val buildKeys =
|
||||
for {
|
||||
confAmb <- config(index configs build)
|
||||
partialMask = ScopeMask(false, confAmb.isExplicit, false, false)
|
||||
} yield taskKeyExtra(build, confAmb, partialMask)
|
||||
|
||||
buildKeys combinedWith projectKeys map (_.flatten)
|
||||
for {
|
||||
rawProject <- optProjectRef(index, current)
|
||||
proj = resolveProject(rawProject, current)
|
||||
confAmb <- config(index configs proj)
|
||||
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
|
||||
} yield taskKeyExtra(proj, confAmb, partialMask)
|
||||
}
|
||||
def makeScopedKey(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], extra: ScopeAxis[AttributeMap], key: AttributeKey[_]): ScopedKey[_] =
|
||||
ScopedKey(Scope(toAxis(proj, Global), toAxis(conf map ConfigKey.apply, Global), toAxis(task, Global), extra), key)
|
||||
|
|
@ -77,16 +67,11 @@ object Act {
|
|||
selectFromValid(ss filter isValid(data), default)
|
||||
}
|
||||
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
|
||||
selectByTask(selectByConfig(ss)) partition isBuildKey match {
|
||||
case (_, Seq(single)) => success(single)
|
||||
case (Seq(single), Seq()) => success(single)
|
||||
case (Seq(), Seq()) => default
|
||||
case (buildKeys, projectKeys) => failure("Ambiguous keys: " + showAmbiguous(keys(buildKeys ++ projectKeys)))
|
||||
selectByTask(selectByConfig(ss)) match {
|
||||
case Seq() => default
|
||||
case Seq(single) => success(single)
|
||||
case multi => failure("Ambiguous keys: " + showAmbiguous(keys(multi)))
|
||||
}
|
||||
private def isBuildKey(parsed: ParsedKey): Boolean = parsed.key.scope.project match {
|
||||
case Select(_: BuildReference) => true
|
||||
case _ => false
|
||||
}
|
||||
private[this] def keys(ss: Seq[ParsedKey]): Seq[ScopedKey[_]] = ss.map(_.key)
|
||||
def selectByConfig(ss: Seq[ParsedKey]): Seq[ParsedKey] =
|
||||
ss match {
|
||||
|
|
@ -148,7 +133,16 @@ object Act {
|
|||
token(ID !!! "Expected key" examples dropHyphenated(keys)) flatMap { keyString =>
|
||||
getKey(keyMap, keyString, idFun)
|
||||
}
|
||||
keyParser(index.keys(proj, conf, task))
|
||||
// Fixes sbt/sbt#2460 and sbt/sbt#2851
|
||||
// The parser already accepts build-level keys.
|
||||
// This queries the key index so tab completion will list the build-level keys.
|
||||
val buildKeys: Set[String] =
|
||||
proj match {
|
||||
case Some(ProjectRef(uri, id)) => index.keys(Some(BuildRef(uri)), conf, task)
|
||||
case _ => Set()
|
||||
}
|
||||
val keys: Set[String] = index.keys(proj, conf, task) ++ buildKeys
|
||||
keyParser(keys)
|
||||
}
|
||||
|
||||
def getKey[T](keyMap: Map[String, AttributeKey[_]], keyString: String, f: AttributeKey[_] => T): Parser[T] =
|
||||
|
|
@ -315,4 +309,4 @@ object Act {
|
|||
final object Omitted extends ParsedAxis[Nothing]
|
||||
final class ParsedValue[T](val value: T) extends ParsedAxis[T]
|
||||
def value[T](t: Parser[T]): Parser[ParsedAxis[T]] = t map { v => new ParsedValue(v) }
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ object ParseKey extends Properties("Key parser test") {
|
|||
parseExpected(structure, string, expected, mask)
|
||||
}
|
||||
|
||||
property("An unspecified project axis resolves to the current project or the build of the current project") =
|
||||
property("An unspecified project axis resolves to the current project") =
|
||||
forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) =>
|
||||
import skm.{ structure, key }
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ object ParseKey extends Properties("Key parser test") {
|
|||
("Current: " + structure.current) |:
|
||||
parse(structure, string) {
|
||||
case Left(err) => false
|
||||
case Right(sk) => sk.scope.project == Select(structure.current) || sk.scope.project == Select(BuildRef(structure.current.build))
|
||||
case Right(sk) => sk.scope.project == Select(structure.current)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
### Bug fixes
|
||||
|
||||
- Fixes regressions in sbt 0.13.11 - 0.13.13 that processed build-level keys incorrectly. [#2851][2851]/[#2460][2460] by [@eed3si9n]
|
||||
|
||||
[#2851]: https://github.com/sbt/sbt/issues/2851
|
||||
[#2460]: https://github.com/sbt/sbt/issues/2460
|
||||
[@eed3si9n]: https://github.com/eed3si9n
|
||||
[@dwijnand]: https://github.com/dwijnand
|
||||
[@Duhemm]: https://github.com/Duhemm
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import complete.{ Completion, Completions, DefaultParsers, Parser }
|
||||
import DefaultParsers._
|
||||
import Command.applyEffect
|
||||
import CommandUtil._
|
||||
|
||||
lazy val root = (project in file(".")).
|
||||
enablePlugins(FooPlugin).
|
||||
settings(
|
||||
commands += checkCompletionsCommand
|
||||
)
|
||||
|
||||
// This checks the tab completion lists build-level keys
|
||||
def checkCompletionsCommand = Command.make("checkCompletions")(completionsParser)
|
||||
def completionsParser(state: State) =
|
||||
{
|
||||
val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => (nq +: s).mkString }
|
||||
val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted)
|
||||
applyEffect(token(quotedOrUnquotedSingleArgument ?? "" examples ("", " ")))(runCompletions(state))
|
||||
}
|
||||
def runCompletions(state: State)(input: String): State = {
|
||||
val xs = Parser.completions(state.combinedParser, input, 9).get map {
|
||||
c => if (c.isEmpty) input else input + c.append
|
||||
} map { c =>
|
||||
c.replaceAll("\n", " ")
|
||||
}
|
||||
println(xs)
|
||||
assert(xs == Set("myTask"))
|
||||
state
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import sbt._
|
||||
|
||||
object FooPlugin extends AutoPlugin {
|
||||
override def trigger = noTrigger
|
||||
object autoImport {
|
||||
val myTask = taskKey[Unit]("My task")
|
||||
}
|
||||
|
||||
import autoImport._
|
||||
|
||||
override def buildSettings = super.buildSettings ++ Seq(
|
||||
myTask := println("Called my task")
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
> checkCompletions my
|
||||
|
|
@ -126,9 +126,6 @@ sealed trait RichParser[A] {
|
|||
|
||||
/** Applies the original parser, applies `f` to the result to get the next parser, and applies that parser and uses its result for the overall result. */
|
||||
def flatMap[B](f: A => Parser[B]): Parser[B]
|
||||
|
||||
/** Applied both the original parser and `b` on the same input and returns the results produced by each parser */
|
||||
def combinedWith(b: Parser[A]): Parser[Seq[A]]
|
||||
}
|
||||
|
||||
/** Contains Parser implementation helper methods not typically needed for using parsers. */
|
||||
|
|
@ -312,11 +309,6 @@ trait ParserMain {
|
|||
def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg)
|
||||
def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString)
|
||||
def flatMap[B](f: A => Parser[B]) = bindParser(a, f)
|
||||
def combinedWith(b: Parser[A]): Parser[Seq[A]] =
|
||||
if (a.valid)
|
||||
if (b.valid) new CombiningParser(a, b) else a.map(Seq(_))
|
||||
else
|
||||
b.map(Seq(_))
|
||||
}
|
||||
|
||||
implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c)
|
||||
|
|
@ -641,24 +633,6 @@ private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidPar
|
|||
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
||||
override def toString = "(" + a + " || " + b + ")"
|
||||
}
|
||||
private final class CombiningParser[T](a: Parser[T], b: Parser[T]) extends ValidParser[Seq[T]] {
|
||||
lazy val result: Option[Seq[T]] = (a.result.toSeq ++ b.result.toSeq) match { case Seq() => None; case seq => Some(seq) }
|
||||
def completions(level: Int) = a.completions(level) ++ b.completions(level)
|
||||
def derive(i: Char) =
|
||||
(a.valid, b.valid) match {
|
||||
case (true, true) => new CombiningParser(a derive i, b derive i)
|
||||
case (true, false) => a derive i map (Seq(_))
|
||||
case (false, true) => b derive i map (Seq(_))
|
||||
case (false, false) => new Invalid(mkFailure("No valid parser available."))
|
||||
}
|
||||
def resultEmpty =
|
||||
(a.resultEmpty, b.resultEmpty) match {
|
||||
case (Value(ra), Value(rb)) => Value(Seq(ra, rb))
|
||||
case (Value(ra), _) => Value(Seq(ra))
|
||||
case (_, Value(rb)) => Value(Seq(rb))
|
||||
case _ => Value(Nil)
|
||||
}
|
||||
}
|
||||
private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] {
|
||||
assert(a.nonEmpty)
|
||||
lazy val resultEmpty: Result[Seq[T]] =
|
||||
|
|
|
|||
|
|
@ -107,15 +107,6 @@ object ParserTest extends Properties("Completing Parser") {
|
|||
property("repeatDep requires at least one token") = !matches(repeat, "")
|
||||
property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head)
|
||||
property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" "))
|
||||
property("combined parser gives completion of both parsers") = {
|
||||
val prefix = "fix"
|
||||
val p1Suffixes = Set("", "ated", "ation")
|
||||
val p2Suffixes = Set("es", "ed")
|
||||
val p1: Parser[String] = p1Suffixes map (suffix => (prefix + suffix): Parser[String]) reduce (_ | _)
|
||||
val p2: Parser[String] = p2Suffixes map (suffix => (prefix + suffix): Parser[String]) reduce (_ | _)
|
||||
val suggestions: Set[Completion] = p1Suffixes ++ p2Suffixes map (new Suggestion(_))
|
||||
checkAll(prefix, p1 combinedWith p2, Completions(suggestions))
|
||||
}
|
||||
}
|
||||
object ParserExample {
|
||||
val ws = charClass(_.isWhitespace)+
|
||||
|
|
|
|||
Loading…
Reference in New Issue