Skip to content

Instantly share code, notes, and snippets.

@agemooij
Last active July 29, 2016 21:36
Show Gist options
  • Save agemooij/0630539c8757f6d45133 to your computer and use it in GitHub Desktop.
Save agemooij/0630539c8757f6d45133 to your computer and use it in GitHub Desktop.
My SBT prompt config

My customized SBT shell prompt

Example screenshot

A heavily customied SBT shell prompt that shows the following information:

  • a handy marker to show you are in SBT
  • the current Git status (a clean working directory is green, a dirty one is yellow)
  • the current project, including the root project of a multi-project build

Requirements

This plugin uses some special icon-like characters (the segment separator and the Git branch icon) that are not supported by most fonts. In order for this theme to render correctly, you will need a Powerline-patched font.

You can also just replace these characters with less high-maintenance ones.

How to use

This plugin depends on the sbt-git plugin so, before doing anything else, add it to your plugins.sbt file.

Then just copy the ShellPrompt.scala file to the project folder of your project and reload/restart SBT.

Make sure your file is saved using UTF-8 encoding to prevent the special

Customization

If you want to customize the colors used in this theme, have a look at these handy references:

Future plans

I might get around to turning this into a full public SBT plugin but, knowing myself and my history with planning these things, don't hold your breath ;)

Credits

Initial idea shamelessly stolen from the Play framework, all credits to them! Heavily customized aftwards based on my lightly customized version of the Oh-My-Zsh "agnoster" theme.

Enjoy!
Age (@agemooij)

import sbt._
import Keys._
/*
* Initial idea shamelessly stolen from the Play framework, all credits to them!
* Heavily customized aftwards based on my lightly customized version of the
* Oh-My-Zsh "agnoster" theme.
*
* This theme uses some special icon-like characters (the segment separator
* and the Git branch icon) that are not supported by most fonts. In order
* for this theme to render correctly, you will need a
* [Powerline-patched font](https://github.com/Lokaltog/powerline-fonts).
*
* If you want to customize the colors used in this theme, have a look at
* these handy references:
*
* - [Ansi escape code reference](http://misc.flogisoft.com/bash/tip_colors_and_formatting)
* - [Graphical overview of the Ansi 256 color codes](http://www.calmar.ws/vim/color-output.png)
*
* Enjoy!
* Age (@agemooij)
*/
object ShellPrompt extends Plugin with Ansi256ColorSupport with PromptSegmentSupport with GitSupport {
override def settings = Seq(
shellPrompt := { state =>
val extracted = Project.extract(state)
import extracted._
import com.typesafe.sbt.SbtGit._
val project = currentRef.project
val root = rootProject(currentRef.build)
val path = if (project == root) project else s"${root}/${project}"
val branch = " " + extracted.get(GitKeys.gitCurrentBranch)
prompt(
List(
PromptSegment("SBT", 26, 235),
if (isGitDirty(state, extracted)) PromptSegment(branch, 214, 235) else PromptSegment(branch, 34, 235),
PromptSegment(path, 235, 250)
),
""
)
}
)
}
trait GitSupport {
import com.typesafe.sbt.SbtGit._
def isGitDirty(state: State, extracted: Extracted): Boolean = {
val (_, runner) = extracted.runTask(GitKeys.gitRunner, state)
val dir = extracted.get(baseDirectory)
val result = runner("diff-index", "HEAD", "--")(dir, NoOpSbtLogger)
!result.trim.isEmpty
}
}
case class PromptSegment(text: String, bg: Int, fg: Int)
trait PromptSegmentSupport extends Ansi256ColorSupport {
def prompt(segments: Seq[PromptSegment], separator: String): String = {
val combined = segments.foldLeft("") { (combined, segment) =>
if (combined.isEmpty)
s"${combined}${bg(segment.bg)}${fg(segment.fg)} ${segment.text} ${fg(segment.bg)}"
else
s"${combined}${bg(segment.bg)}${separator}${fg(segment.fg)} ${segment.text} ${fg(segment.bg)}"
}
s"${combined}${defaultBg}${separator}${defaultFg} "
}
}
trait AnsiColorSupport {
val defaultFg = "\u001b[39m"
val defaultBg = "\u001b[49m"
val resetColors = defaultBg + defaultFg
lazy val isANSISupported = {
Option(System.getProperty("sbt.log.noformat")).map(_ != "true").orElse {
Option(System.getProperty("os.name"))
.map(_.toLowerCase)
.filter(_.contains("windows"))
.map(_ => false)
}.getOrElse(true)
}
}
trait Ansi256ColorSupport extends AnsiColorSupport {
def fg(code: Int): String = {
require(code >= 0 && code <= 255)
s"\u001b[38;5;${code}m"
}
def bg(code: Int): String = {
require(code >= 0 && code <= 255)
s"\u001b[48;5;${code}m"
}
/** Little util for potential theme writers to get a quick overview of the supported ANSI256 color escape sequences. */
def printSupportedColors: Unit = {
for (i <- 0 to 255) {
println(s"${fg(i)} foreground (${i}) $defaultFg ${bg(i)} background (${i}) $defaultBg")
}
}
}
object NoOpSbtLogger extends Logger {
def trace(t: => Throwable): Unit = {}
def success(message: => String): Unit = {}
def log(level: Level.Value, message: => String): Unit = {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment