sbt: customize Shell Prompt with git branch (round 2)

Posted on Sunday, March 20, 2016


g


I recently did a post on customizing the sbt command line prompt see http://www.whiteboardcoder.com/2016/03/sbt-customize-shell-prompt-in-sbt.html [1]

Now I want to tweak my code a bit to list the current git branch I am on.

I was inspired by this github project that uses powerline fonts. https://github.com/agemooij/sbt-prompt [2]






This project has some pretty good results and even has a nice glyph for branch.

For me, though, I want to make my own setup so I can tweak it as my needs change.




Git branch


To get just the current branch name in git you can run this command.


   > git rev-parse --abbrev-ref HEAD






Now I just need to incorporate that into my ~/.sbt/0.13/global.sbt file


   > vi ~/.sbt/0.13/global.sbt




Here is my updated code


shellPrompt := { state =>
  def textColor(color: Int)      = { s"\033[38;5;${color}m" }
  def backgroundColor(color:Int) = { s"\033[48;5;${color}m" }
  def reset                      = { s"\033[0m" }
  def gitBranch      = Process("git rev-parse --abbrev-ref HEAD").lines.head

  def formatText(str: String)(txtColor: Int, backColor: Int) = {
    s"${textColor(txtColor)}${backgroundColor(backColor)}${str}${reset}"
  }
  val red    = 1
  val green  = 2
  val yellow = 11
  val white  = 15
  val black  = 16
  val orange = 166

  formatText(s"[${name.value}]")(white, orange) +
  formatText(s"  $gitBranch  ")(black, yellow) +
  "\n " +
  formatText("\u276f")(orange, black) +
  formatText("\u276f")(orange, black) +
  formatText("\u276f ")(orange, black)
}




And here are the results.



When I reload sbt it grabs the correct branch I am currently on.



The only thing I would want to add to this is a branch glyph like this



I imagine the best way to do that is to find a Unicode glyph close to that and use FontForge to tweak my current Font I use.

I have used FontForge in the past to tweak my current Font and I have some note on it http://www.whiteboardcoder.com/2015/02/fontforge.html [3] I also have a short video I made on the subject https://www.youtube.com/watch?v=eDPC1e546wg [4]






Find the Unicode Character


First let me find the Unicode Character that is closes to the branch symbol.

One tool you can use to try and find Unicode fonts is http://shapecatcher.com/ [5]






Go there draw something and click Recognize







After a few tries I really did not get what I wanted.  But still it’s a pretty cool tool.



Poking around I found a few possibles.

Unicode
Symbol
\u2387
\uE0A0




OK why the E0A0? 




I opened one of the fonts in FontForge to see

That is the one that Powerline uses.  It is also in the Supplementary Special Purpose Plan of Unicode see https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Special-purpose_Plane [6]

I am not really sure what that means, is it just an unused section of Unicode?

At any rate I think it best to use the same Unicode character the powerline folks are using.


Updating my code


If I update my code


shellPrompt := { state =>
  def textColor(color: Int)      = { s"\033[38;5;${color}m" }
  def backgroundColor(color:Int) = { s"\033[48;5;${color}m" }
  def reset                      = { s"\033[0m" }
  def gitBranch  = "\uE0A0 " + Process("git rev-parse --abbrev-ref HEAD").lines.head

  def formatText(str: String)(txtColor: Int, backColor: Int) = {
    s"${textColor(txtColor)}${backgroundColor(backColor)}${str}${reset}"
  }
  val red    = 1
  val green  = 2
  val yellow = 11
  val white  = 15
  val black  = 16
  val orange = 166

  formatText(s"[${name.value}]")(white, orange) +
  formatText(s"$gitBranch ")(black, yellow) +
  "\n " +
  formatText("\u276f")(orange, black) +
  formatText("\u276f")(orange, black) +
  formatText("\u276f ")(orange, black)
}









I get nothing since my font is not have this glyph.

I am going to fix that with Font-Forge by simply copying down the powerline fonts and use Font-Forge to copy and paste a few glyphs.

Run this command to download the powerfonts from github


   > git clone https://github.com/powerline/fonts.git


I would suggest using one of the downloaded powerline fonts, if you can get it to work for you.   Please do it!  The rest of this article is me fighting with FontForge/Windows/Cygwin to try and get what I want working…







Fight the good Font fight


OK after a lot of fighting with my own font here is the procedure I came up with the fix my own setup (windows 7 + Cygwin)

First of all Cygwin is finicky with the fonts it will can use.  They need to be monospaced and all glyphs must have the same set width. 


Here is my procedure

1.   Shut down Cygwin
2.   Remove all Sans Typewriter fonts from C:\Windows\Fonts
3.   Download a new copy of Lucida Sans Typewriter from http://www.myfontfree.com/download-lucida-sans-typewriter-zip37263.htm (mostly because I just need a nice fresh one to alter)
4.   Open the download Font in FontForge and also Open one of the Powerline Fonts.  (also in my case open the prior Font I had tweaked)
5.   Copy over my own personal odd Unicode Glyph to the new font.


Click on View then GoTo


Enter U+276f and click OK



Right click on the Glyph and select Copy.

Then paste over in the same Unicode Glyph in the new font.


6.  In the Powerline font goto U+E0A0





Now copy these Glyphs over to the new font





Copy them to the same location

7. Fix the Font Width





Select Edit à  Select à Glyphs Worth Outputing.

This should select all the Glyphs that matter.



Select Metrics and click Set Width




Set width to 1234 and click OK


8. Make sure it is monospaced






Click on element and Font Info.




Select OS/2 and click on Panose





Select Monospaced for Proportion and click OK.


9. Make the font



Select File and click Generate Fonts





Save to the Desktop and click Generate





You may get an error like this click Generate.


10. Copy Font to C:\Windows\Fonts
11. Open Cygwin and change font to the new updated font.



(note:  I was doing so many tests at one point I just had to reboot my machine as it had messed with my Fonts too much).





Test


You can test to see if a Unicode character will work by running this command.


   > echo -e '\u276f'




Hey it worked!





For some reason the E0xx range does not work on the command line (thought it does actually work in sbt just fine for some reason)





Back to tweaking my sbt



shellPrompt := { state =>
  //Special Characters
  val branch = "\uE0A0"
  val lock   = "\uE0A2"
  val rArrow = "\uE0B0"
  val lArrow = "\uE0B2"

  def textColor(color: Int)      = { s"\033[38;5;${color}m" }
  def backgroundColor(color:Int) = { s"\033[48;5;${color}m" }
  def reset                      = { s"\033[0m" }
  def gitBranch   = s" $branch ${Process("git rev-parse --abbrev-ref HEAD").lines.head}"


  def formatText(str: String)(txtColor: Int, backColor: Int) = {
    s"${textColor(txtColor)}${backgroundColor(backColor)}${str}${reset}"
  }
  val red    = 1
  val green  = 2
  val yellow = 11
  val white  = 15
  val black  = 16
  val orange = 166

  formatText(s"[${name.value}]")(white, orange) +
  formatText(s"$gitBranch ")(black, yellow) +
  "\n " +
  formatText("\u276f")(orange, black) +
  formatText("\u276f")(orange, black) +
  formatText("\u276f ")(orange, black)
}



Here are the results.




There is a bug though… If there is not git repo in the directory it crashes.  Let me fix that.





Process Builder



I have never fiddled with Process builder so whatever I make here my be a little hacky… so bear with me.

I found a good example how to use it at http://stackoverflow.com/questions/15411728/scala-process-capture-standard-out-and-exit-code [9] the post from Rogach



def runCommand(cmd: Seq[String]): (Int, String, String) = {
  val stdout = new ByteArrayOutputStream
  val stderr = new ByteArrayOutputStream
  val stdoutWriter = new PrintWriter(stdout)
  val stderrWriter = new PrintWriter(stderr)
  val exitValue = cmd.!(ProcessLogger(stdoutWriter.println, stderrWriter.println))
  stdoutWriter.close()
  stderrWriter.close()
  (exitValue, stdout.toString, stderr.toString)
}


Copying this code over to my sbt file does not seem to work for me.  I am getting some conflicts.   I think it wants to use the sbt.ProcessBuilder vs the Scala.sys.process.ProcessBuilder.



Here are a few resources I found while fighting with this.

(this had an empty ProcessLogger)


(this seems to be the code which I cannot seem to find on github… but I know sbt is going through a lot of changes at the moment… anyway lots to learn)

(External Process)





At any rate here is what I came up with.


import java.io.{ByteArrayOutputStream, PrintWriter}
import sbt.{ProcessBuilder, ProcessLogger}

def runCommand(cmd: Seq[String]): (Int, String, String) = {
  val stdout = new ByteArrayOutputStream
  val stderr = new ByteArrayOutputStream
  val stdoutWriter = new PrintWriter(stdout)
  val stderrWriter = new PrintWriter(stderr)

  val exitValue = cmd.!(new ProcessLogger {
    def info (s: => String) { stdoutWriter.print(s) }
    def error (s: => String) { stderrWriter.print(s) }
    def buffer[T] (f: => T): T = f
  })

  stdoutWriter.close()
  stderrWriter.close()
  (exitValue, stdout.toString, stderr.toString)
}

def gitBranch = {
  runCommand("git rev-parse --abbrev-ref HEAD".split(" ").toSeq) match {
    case (exit, _, _)  if(exit != 0)  => "No-Git"
    case (_, stdout, _)               => stdout
  }
}

shellPrompt := { state =>
  //Special Characters
  val branch = "\uE0A0"
  val lock   = "\uE0A2"
  val rArrow = "\uE0B0"
  val lArrow = "\uE0B2"

  def textColor(color: Int)      = { s"\033[38;5;${color}m" }
  def backgroundColor(color:Int) = { s"\033[48;5;${color}m" }
  def reset                      = { s"\033[0m" }

  def formatText(str: String)(txtColor: Int, backColor: Int) = {
    s"${textColor(txtColor)}${backgroundColor(backColor)}${str}${reset}"
  }
  val red    = 1
  val green  = 2
  val yellow = 11
  val white  = 15
  val black  = 16
  val orange = 166

  formatText(s"[${name.value}]")(white, orange) +
  formatText(s" $branch $gitBranch ")(black, yellow) +
  "\n " +
  formatText("\u276f")(orange, black) +
  formatText("\u276f")(orange, black) +
  formatText("\u276f ")(orange, black)
}




And here are the results if I have a git repo.



In this case called test-branch.

Here are the results if I do not have a git repo








PowerLine like?


I updated my font with the power line symbols and I wonder if I can use them to get the nice angles.  

Here is my code that does that.


import java.io.{ByteArrayOutputStream, PrintWriter}
import sbt.{ProcessBuilder, ProcessLogger}

def runCommand(cmd: Seq[String]): (Int, String, String) = {
  val stdout = new ByteArrayOutputStream
  val stderr = new ByteArrayOutputStream
  val stdoutWriter = new PrintWriter(stdout)
  val stderrWriter = new PrintWriter(stderr)

  val exitValue = cmd.!(new ProcessLogger {
    def info (s: => String) { stdoutWriter.print(s) }
    def error (s: => String) { stderrWriter.print(s) }
    def buffer[T] (f: => T): T = f
  })

  stdoutWriter.close()
  stderrWriter.close()
  (exitValue, stdout.toString, stderr.toString)
}

def gitBranch = {
  runCommand("git rev-parse --abbrev-ref HEAD".split(" ").toSeq) match {
    case (exit, _, _)  if(exit != 0)  => "No-Git"
    case (_, stdout, _)               => stdout
  }
}

shellPrompt := { state =>
  //Special Characters
  val branch = "\uE0A0"
  val lock   = "\uE0A2"
  val rArrow = "\uE0B0"
  val lArrow = "\uE0B2"

  def textColor(color: Int)      = { s"\033[38;5;${color}m" }
  def backgroundColor(color:Int) = { s"\033[48;5;${color}m" }
  def reset                      = { s"\033[0m" }

  def formatText(str: String)(txtColor: Int, backColor: Int) = {
    s"${textColor(txtColor)}${backgroundColor(backColor)}${str}${reset}"
  }
  val red    = 1
  val green  = 2
  val yellow = 11
  val white  = 15
  val black  = 16
  val orange = 166

  formatText(s"[${name.value}]")(white, orange) +
  formatText(rArrow)(orange, yellow) +
  formatText(s" $branch $gitBranch ")(black, yellow) +
  formatText(rArrow)(yellow, black) +
  "\n " +
  formatText("\u276f")(orange, black) +
  formatText("\u276f")(orange, black) +
  formatText("\u276f ")(orange, black)
}









Hey it worked cool!




I also put this code up as a gist on github at https://gist.github.com/patmandenver/71839aaf63c71f4d6cd2





References


[1]        sbt: customize the shell prompt in sbt
                Accessed 03/2016
[2]        github stb prompt
                Accessed 03/2016
[3]        FontForge
                Accessed 03/2016
[4]        Video: FontForge and Cygwin
                Accessed 03/2016
[5]        Shapecatcher
                Accessed 03/2016
[6]        Unicode Special-Purpose Plane
                Accessed 03/2016
[7]        Finding common font problems automagically
                Accessed 03/2016
[8]        Scala Process Builder
                Accessed 03/2016
[9]        Scala Process - Capture Standard Out and Exit Code
                Accessed 03/2016
[10]      Empty ProcesLogger Example
Accessed 03/2016
[11]      Scala Process Builder
Accessed 03/2016
[12]      External Process
                Accessed 03/2016



No comments:

Post a Comment