All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.build.internals.Name.scala Maven / Gradle / Ivy

There is a newer version: 1.5.3
Show newest version
package scala.build.internal

// adapted from https://github.com/com-lihaoyi/Ammonite/blob/9be39debc367abad5f5541ef58f4b986b2a8d045/amm/util/src/main/scala/ammonite/util/Model.scala#L45-L110

import scala.reflect.NameTransformer

case class Name(raw: String) {
  assert(
    NameTransformer.decode(raw) == raw,
    "Name() must be created with un-encoded text"
  )
  assert(raw.charAt(0) != '`', "Cannot create already-backticked identifiers")
  override def toString = s"Name($backticked)"
  def encoded           = NameTransformer.encode(raw)
  def backticked        = Name.backtickWrap(encoded)
}

object Name {

  def decoded(name: String) = NameTransformer.decode(name)

  val alphaKeywords = Set(
    "abstract",
    "case",
    "catch",
    "class",
    "def",
    "do",
    "else",
    "extends",
    "false",
    "finally",
    "final",
    "finally",
    "forSome",
    "for",
    "if",
    "implicit",
    "import",
    "lazy",
    "match",
    "new",
    "null",
    "object",
    "override",
    "package",
    "private",
    "protected",
    "return",
    "sealed",
    "super",
    "this",
    "throw",
    "trait",
    "try",
    "true",
    "type",
    "val",
    "var",
    "while",
    "with",
    "yield",
    "_",
    "macro"
  )
  val symbolKeywords = Set(
    ":",
    ";",
    "=>",
    "=",
    "<-",
    "<:",
    "<%",
    ">:",
    "#",
    "@",
    "\u21d2",
    "\u2190"
  )
  val blockCommentStart = "/*"
  val lineCommentStart  = "//"

  /** Custom implementation of ID parsing, instead of using the ScalaParse version. This lets us
    * avoid loading FastParse and ScalaParse entirely if we're running a cached script, which shaves
    * off 200-300ms of startup time.
    */
  def backtickWrap(s: String) =
    if (s.isEmpty) "``"
    else if (s(0) == '`' && s.last == '`') s
    else {
      val chunks = s.split("_", -1)
      def validOperator(c: Char) =
        c.getType == Character.MATH_SYMBOL ||
        c.getType == Character.OTHER_SYMBOL ||
        "!#%&*+-/:<=>?@\\^|~".contains(c)
      val validChunks = chunks.zipWithIndex.forall { case (chunk, index) =>
        chunk.forall(c => c.isLetter || c.isDigit || c == '$') ||
        (
          chunk.forall(validOperator) &&
          // operators can only come last
          index == chunks.length - 1 &&
          // but cannot be preceded by only a _
          !(chunks.lift(index - 1).exists(_ == "") && index - 1 == 0)
        )
      }

      val firstLetterValid = s(0).isLetter || s(0) == '_' || s(0) == '$' || validOperator(s(0))

      val valid =
        validChunks &&
        firstLetterValid &&
        !alphaKeywords.contains(s) &&
        !symbolKeywords.contains(s) &&
        !s.contains(blockCommentStart) &&
        !s.contains(lineCommentStart)

      if (valid) s else "`" + s + '`'
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy