From a38bce8d4134bf7924d85f366bd7e4828122e319 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 13 Nov 2012 14:52:33 -0500 Subject: [PATCH] Transition to all camelCase key labels. 1. Hyphenated labels are still accepted when parsing scoped keys (so 'sbt test-only' still works) There is currently no timeline for removing this support for hyphenated keys. 2. Only camelCase is shown for tab completion. 3. AttributeKey.rawLabel provides the unnormalized label. This should only be used to implement support for accepting hyphenated keys as input for compatibility. 4. AttributeKey.normLabel provides the normalized label (hyphenated converted to camelCase) --- main/Act.scala | 11 +++++++---- main/EvaluateConfigurations.scala | 7 +++++-- main/KeyIndex.scala | 2 +- main/Main.scala | 2 +- main/SettingCompletions.scala | 2 +- util/collection/Attributes.scala | 6 +++++- util/collection/Util.scala | 10 ++++++++-- 7 files changed, 28 insertions(+), 12 deletions(-) diff --git a/main/Act.scala b/main/Act.scala index 938e5b593..dbe9fa62d 100644 --- a/main/Act.scala +++ b/main/Act.scala @@ -131,8 +131,9 @@ object Act def key(index: KeyIndex, proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], keyMap: Map[String,AttributeKey[_]]): Parser[AttributeKey[_]] = { + def dropHyphenated(keys: Set[String]): Set[String] = keys.filterNot(Util.hasHyphen) def keyParser(keys: Set[String]): Parser[AttributeKey[_]] = - token(ID !!! "Expected key" examples keys) flatMap { keyString=> + token(ID !!! "Expected key" examples dropHyphenated(keys)) flatMap { keyString=> getKey(keyMap, keyString, idFun) } keyParser(index.keys(proj, conf, task)) @@ -155,9 +156,11 @@ object Act def taskAxis(d: Option[String], tasks: Set[AttributeKey[_]], allKnown: Map[String, AttributeKey[_]]): Parser[ParsedAxis[AttributeKey[_]]] = { - val knownKeys: Map[String, AttributeKey[_]] = tasks.toSeq.map(key => (key.label, key)).toMap - val valid = allKnown ++ knownKeys - val suggested = knownKeys.keySet + val taskSeq = tasks.toSeq + def taskKeys(f: AttributeKey[_] => String): Seq[(String, AttributeKey[_])] = taskSeq.map(key => (f(key), key)) + val normKeys = taskKeys(_.label) + val valid = allKnown ++ normKeys ++ taskKeys(_.rawLabel) + val suggested = normKeys.map(_._1).toSet val keyP = filterStrings(examples(ID, suggested, "key"), valid.keySet, "key") map valid (token(value(keyP) | GlobalString ^^^ ParsedGlobal ) <~ token("::".id) ) ?? Omitted } diff --git a/main/EvaluateConfigurations.scala b/main/EvaluateConfigurations.scala index 87ba3570b..ecc4c1c2f 100644 --- a/main/EvaluateConfigurations.scala +++ b/main/EvaluateConfigurations.scala @@ -125,9 +125,12 @@ object Index settings.flatMap(s => if(s.key.key.isLocal) Nil else s.key +: s.dependencies).filter(!_.key.isLocal).toSet def attributeKeys(settings: Settings[Scope]): Set[AttributeKey[_]] = settings.data.values.flatMap(_.keys).toSet[AttributeKey[_]] - def stringToKeyMap(settings: Set[AttributeKey[_]]): Map[String, AttributeKey[_]] = + def stringToKeyMap(settings: Set[AttributeKey[_]]): Map[String, AttributeKey[_]] = + stringToKeyMap0(settings)(_.rawLabel) ++ stringToKeyMap0(settings)(_.label) + + private[this] def stringToKeyMap0(settings: Set[AttributeKey[_]])(label: AttributeKey[_] => String): Map[String, AttributeKey[_]] = { - val multiMap = settings.groupBy(_.label) + val multiMap = settings.groupBy(label) val duplicates = multiMap collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.manifest)) } collect { case (k, xs) if xs.size > 1 => (k, xs) } if(duplicates.isEmpty) multiMap.collect { case (k, v) if validID(k) => (k, v.head) } toMap; diff --git a/main/KeyIndex.scala b/main/KeyIndex.scala index 5183476d9..1e3c41699 100644 --- a/main/KeyIndex.scala +++ b/main/KeyIndex.scala @@ -62,7 +62,7 @@ trait ExtendableKeyIndex extends KeyIndex // task axis <-> key private final class AKeyIndex(val data: Relation[ Option[AttributeKey[_]], String]) { - def add(task: Option[AttributeKey[_]], key: AttributeKey[_]): AKeyIndex = new AKeyIndex(data + (task, key.label)) + def add(task: Option[AttributeKey[_]], key: AttributeKey[_]): AKeyIndex = new AKeyIndex(data + (task, key.rawLabel) + (task, key.label)) def keys(task: Option[AttributeKey[_]]): Set[String] = data.forward(task) def allKeys: Set[String] = data._2s.toSet def tasks: Set[AttributeKey[_]] = data._1s.flatten.toSet diff --git a/main/Main.scala b/main/Main.scala index 84ae3d741..866398193 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -163,7 +163,7 @@ object BuiltinCommands val extracted = Project.extract(s) import extracted._ val index = structure.index - index.keyIndex.keys(Some(currentRef)).toSeq map index.keyMap + index.keyIndex.keys(Some(currentRef)).toSeq.map(index.keyMap).distinct } def sortByLabel(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.sortBy(_.label) diff --git a/main/SettingCompletions.scala b/main/SettingCompletions.scala index 53d96081f..42e47e2bd 100644 --- a/main/SettingCompletions.scala +++ b/main/SettingCompletions.scala @@ -285,7 +285,7 @@ private[sbt] object SettingCompletions /** Transforms the hypenated key label `k` into camel-case and quotes it with backticks if it is a Scala keyword. * This is intended to be an estimate of the Scala identifier that may be used to reference the keyword in the default sbt context. */ - def keyScalaID(k: String): String = Util.quoteIfKeyword(Util.hypenToCamel(k)) + def keyScalaID(k: String): String = Util.quoteIfKeyword(Util.hyphenToCamel(k)) /** Transforms the configuration name `c` so that the first letter is capitalized and the name is quoted with backticks if it is a Scala keyword. * This is intended to be an estimate of the Scala identifier that may be used to reference the keyword in the default sbt context. */ diff --git a/util/collection/Attributes.scala b/util/collection/Attributes.scala index 0376ca76c..227e2fdd7 100644 --- a/util/collection/Attributes.scala +++ b/util/collection/Attributes.scala @@ -11,6 +11,8 @@ import scala.reflect.Manifest // a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts sealed trait AttributeKey[T] { def manifest: Manifest[T] + @deprecated("Should only be used for compatibility during the transition from hyphenated labels to camelCase labels.", "0.13.0") + def rawLabel: String def label: String def description: Option[String] def extend: Seq[AttributeKey[_]] @@ -48,13 +50,15 @@ object AttributeKey private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T]): AttributeKey[T] = new SharedAttributeKey[T] { def manifest = mf - def label = name + def rawLabel = name + val label = Util.hyphenToCamel(name) def description = description0 def extend = extend0 def rank = rank0 } private[sbt] def local[T](implicit mf: Manifest[T]): AttributeKey[T] = new AttributeKey[T] { def manifest = mf + def rawLabel = LocalLabel def label = LocalLabel def description = None def extend = Nil diff --git a/util/collection/Util.scala b/util/collection/Util.scala index 5aede5f0d..f4f6fbb50 100644 --- a/util/collection/Util.scala +++ b/util/collection/Util.scala @@ -26,8 +26,14 @@ object Util def pairID[A,B] = (a: A, b: B) => (a,b) private[this] lazy val Hypen = """-(\p{javaLowerCase})""".r - def hypenToCamel(s: String): String = - Hypen.replaceAllIn(s, _.group(1).toUpperCase) + def hasHyphen(s: String): Boolean = s.indexOf('-') >= 0 + @deprecated("Use the properly spelled version: hyphenToCamel", "0.13.0") + def hypenToCamel(s: String): String = hyphenToCamel(s) + def hyphenToCamel(s: String): String = + if(hasHyphen(s)) + Hypen.replaceAllIn(s, _.group(1).toUpperCase) + else + s private[this] lazy val Camel = """(\p{javaLowerCase})(\p{javaUpperCase})""".r def camelToHypen(s: String): String =