diff --git a/util/complete/TypeString.scala b/util/complete/TypeString.scala new file mode 100644 index 000000000..976b672e2 --- /dev/null +++ b/util/complete/TypeString.scala @@ -0,0 +1,77 @@ +package sbt.complete + + import DefaultParsers._ + import TypeString._ + +/** Basic representation of types parsed from Manifest.toString. +* This can only represent the structure of parameterized types. +* All other types are represented by a TypeString with an empty `args`. */ +private[sbt] final class TypeString(val base: String, val args: List[TypeString]) +{ + override def toString = + if(base.startsWith(FunctionName)) + args.dropRight(1).mkString("(", ",", ")") + " => " + args.last + else if(base.startsWith(TupleName)) + args.mkString("(",",",")") + else + cleanupTypeName(base) + (if(args.isEmpty) "" else args.mkString("[", ",", "]")) +} + +private[sbt] object TypeString +{ + /** Makes the string representation of a type as returned by Manifest.toString more readable.*/ + def cleanup(typeString: String): String = + parse(typeString, typeStringParser) match { + case Right(ts) => ts.toString + case Left(err) => typeString + } + + /** Makes a fully qualified type name provided by Manifest.toString more readable. + * The argument should be just a name (like scala.Tuple2) and not a full type (like scala.Tuple2[Int,Boolean])*/ + def cleanupTypeName(base: String): String = + dropPrefix(base).replace('$', '.') + + /** Removes prefixes from a fully qualified type name that are unnecessary in the presence of standard imports for an sbt setting. + * This does not use the compiler and is therefore a conservative approximation.*/ + def dropPrefix(base: String): String = + if(base.startsWith(SbtPrefix)) + base.substring(SbtPrefix.length) + else if(base.startsWith(CollectionPrefix)) + { + val simple = base.substring(CollectionPrefix.length) + if(ShortenCollection(simple)) simple else base + } + else if(base.startsWith(ScalaPrefix)) + base.substring(ScalaPrefix.length) + else if(base.startsWith(JavaPrefix)) + base.substring(JavaPrefix.length) + else + TypeMap.getOrElse(base, base) + + final val CollectionPrefix = "scala.collection." + final val FunctionName = "scala.Function" + final val TupleName = "scala.Tuple" + final val SbtPrefix = "sbt." + final val ScalaPrefix = "scala." + final val JavaPrefix = "java.lang." + /* scala.collection.X -> X */ + val ShortenCollection = Set("Seq", "List", "Set", "Map", "Iterable") + val TypeMap = Map( + "java.io.File" -> "File", + "java.net.URL" -> "URL", + "java.net.URI" -> "URI" + ) + + /** A Parser that extracts basic structure from the string representation of a type from Manifest.toString. + * This is rudimentary and essentially only decomposes the string into names and arguments for parameterized types. + * */ + lazy val typeStringParser: Parser[TypeString] = + { + def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$' + lazy val fullScalaID = identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character") ) + lazy val tpe: Parser[TypeString] = + for( id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) yield + new TypeString(id, args.toList.flatten) + tpe + } +} \ No newline at end of file