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

replpp.package.scala Maven / Gradle / Ivy

import java.io.File
import java.lang.System.lineSeparator
import java.net.URL
import java.nio.file.Path
import scala.annotation.tailrec
import scala.collection.mutable

package object replpp {
  val PredefCodeEnvVar = "SCALA_REPL_PP_PREDEF_CODE"
  val VerboseEnvVar    = "SCALA_REPL_PP_VERBOSE"
  lazy val globalPredefFile = os.home / ".scala-repl-pp.sc"

  /** verbose mode can either be enabled via the config, or the environment variable `SCALA_REPL_PP_VERBOSE=true` */
  def verboseEnabled(config: Config): Boolean = {
    config.verbose ||
      sys.env.get(VerboseEnvVar).getOrElse("false").toLowerCase.trim == "true"
  }

  def compilerArgs(config: Config): Array[String] = {
    val compilerArgs = Array.newBuilder[String]
    compilerArgs ++= Array("-classpath", classpath(config))
    compilerArgs += "-explain" // verbose scalac error messages
    compilerArgs += "-deprecation"
    if (config.nocolors) compilerArgs ++= Array("-color", "never")
    compilerArgs.result()
  }

  def classpath(config: Config): String = {
    val fromJavaClassPathProperty = System.getProperty("java.class.path")
    val fromDependencies = dependencyFiles(config).mkString(pathSeparator)
    val fromClassLoaderHierarchy =
      jarsFromClassLoaderRecursively(classOf[replpp.ReplDriver].getClassLoader)
        .map(_.getFile)
        .mkString(pathSeparator)

    Seq(fromClassLoaderHierarchy, fromDependencies, fromJavaClassPathProperty).mkString(pathSeparator)
  }

  private def dependencyFiles(config: Config): Seq[File] = {
    val predefCode = allPredefCode(config)
    val scriptCode = config.scriptFile.map(os.read).getOrElse("")
    val allDependencies = config.dependencies ++
      UsingDirectives.findDeclaredDependencies(s"$predefCode\n$scriptCode")
    Dependencies.resolveOptimistically(allDependencies, verboseEnabled(config))
  }

  private def jarsFromClassLoaderRecursively(classLoader: ClassLoader): Seq[URL] = {
    classLoader match {
      case cl: java.net.URLClassLoader =>
        jarsFromClassLoaderRecursively(cl.getParent) ++ cl.getURLs
      case _ =>
        Seq.empty
    }
  }

  def allPredefCode(config: Config): String = {
    val resultLines = Seq.newBuilder[String]
    val visited = mutable.Set.empty[os.Path]

    def handlePredefCodeWithoutPredefFiles() = {
      val codeLines =
        lines(config.predefCode) ++                       // `--predefCode` parameter
        lines(Option(System.getenv(PredefCodeEnvVar))) ++ // ~/.scala-repl-pp.sc file
        globalPredefFileLines                             // `SCALA_REPL_PP_PREDEF_CODE` env var

      val importedFiles = UsingDirectives.findImportedFilesRecursively(codeLines, os.pwd)
      importedFiles.foreach { file =>
        resultLines ++= os.read.lines(file)
      }
      visited ++= importedFiles
      resultLines ++= codeLines
    }

    def handlePredefFiles() = {
      config.predefFiles.foreach { file =>
        val importedFiles = UsingDirectives.findImportedFilesRecursively(file, visited.toSet)
        visited ++= importedFiles
        importedFiles.foreach { file =>
          resultLines ++= os.read.lines(file)
        }

        resultLines ++= os.read.lines(file)
        visited += file
      }
    }

    if (config.predefFilesBeforePredefCode) {
      handlePredefFiles()
      handlePredefCodeWithoutPredefFiles()
    } else {
      handlePredefCodeWithoutPredefFiles()
      handlePredefFiles()
    }

    config.scriptFile.foreach { file =>
      val importedFiles = UsingDirectives.findImportedFilesRecursively(file, visited.toSet)
      visited ++= importedFiles
      importedFiles.foreach { file =>
        resultLines ++= os.read.lines(file)
      }
    }

    resultLines.result()
      .filterNot(_.trim.startsWith(UsingDirectives.FileDirective))
      .mkString(lineSeparator)
  }

  private def lines(str: String): Seq[String] =
    str.split(lineSeparator)

  private def lines(strMaybe: Option[String]): Seq[String] =
    strMaybe.map(lines).getOrElse(Seq.empty)

  /**
    * resolve absolute or relative paths to an absolute path
    * - if given pathStr is an absolute path, just take that
    * - if it's a relative path, use given base path to resolve it to an absolute path
    */
  def resolveFile(base: os.Path, pathStr: String): os.Path = {
    if (Path.of(pathStr).isAbsolute) os.Path(pathStr)
    else base / os.RelPath(pathStr)
  }

  private def globalPredefFileLines: Seq[String] = {
    if (os.exists(globalPredefFile))
      os.read.lines(globalPredefFile)
    else
      Seq.empty
  }

  // ":" on unix
  val pathSeparator = java.io.File.pathSeparator
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy