Powerline-shell

Posted on Fri 14 August 2020 in Shell

Update 13/nov/2021 After spending a few minutes reading the docs, I realize you can override the default segments without modifying the existing ones. Check my segments for an example. I give more details at the end of this post (Better customization section).

A few days ago, I found this gem in Github: Powerline-shell. This application is written in Python. Powerline-shell allows you to customize your prompt by adding several pieces of information (called segments), like:

  • Name of the virtual environment
  • Git status
  • User name and host name
  • Current working directory
  • etc.

It is similar to Powerline, other application for the same purpose. Although, I find Powerline-shell so much easier to customize. This post explains how to install poweline-shell and customize your shell so that it will look like this:

powerline

After configuring powerline-shell, your prompt will look like this:

master 2⬆ 1✔ 7✎ 4? [dev] auraham@rocket ~/git/repo $

This means that:

  • We are in the master branch.
  • There are 2 pending commits.
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
  • There is 1 staged file (i.e., one file ready to be committed):
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   sample
  • There are 7 modified files (i.e., changes not staged for commit):
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   git_commands.md
    modified:   learninggit.md
    modified:   ../themes/Flex-minimal/templates/author.html
    modified:   ../themes/Flex-minimal/templates/categories.html
    modified:   ../themes/Flex-minimal/templates/category.html
    modified:   ../themes/Flex-minimal/templates/tag.html
    modified:   ../themes/Flex-minimal/templates/tags.html
  • There are 4 untracked files:
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    build_model_v1.ipynb
    build_model_v1.nbdata
    ml_notes.md
    pages/index_prev.md

Installation

First, install powerline-shell and its dependencies as follows:

sudo apt-get install fonts-powerline
pip3 install powerline-shell

Then, add the following to your .bashrc file:

function _update_ps1() {
  PS1=$(powerline-shell $?)
}

if [[ $TERM != linux && ! $PROMPT_COMMAND =~ _update_ps1 ]]; then
  PROMPT_COMMAND="_update_ps1; $PROMPT_COMMAND"
fi

If you use other shell (fish, zsh, tcsh), please check the documentation.

Customization

Initialize the configuration files as follows:

mkdir -p ~/.config/powerline-shell && \
powerline-shell --generate-config > ~/.config/powerline-shell/config.json

config.json specifies the segments and the theme. This is my current setup:

{                                                   
  "theme": "/home/auraham/.config/powerline-shell/themes/rocket.py",   # custom theme
  "mode": "flat",    # do not use unicode characters
  "segments": [
    "git",
    "virtual_env",
    "username",
    "hostname",
    "ssh",
    "cwd",
    "hg",
    "jobs",
    "root"
  ],  
  "cwd": {
    "full_cwd": true,    # shows full path
    "max_depth": 10,     # how many dirs to show before using "..."
    "mode": "plain"      # do not split the path into individual segments
  }
} 

The "segments" option determines the segments that will be in your shell and their corresponding position. For instance, I prefer to put "git" before anything else. Otherwise, the git segment would change as I move among directories with cd. By default, powerline-shell uses a Unicode character like ">" at the end of each segment. Personally, I do not like that symbol. To change this, you can use the "mode": "flat" option. Then, I define the theme using the "theme" option (my theme is shown below). Finally, I changed the way the current working directory is displayed. Here, "full_cwd" shows the full path. Also, "mode": "plain" allows us to show the path as a single string, otherwise, the path will be divided into individual segments. Finally, "max_depth": 10 indicates the number of directories to show in the prompt before using "..." to shorten it.

~/Desktop $ mkdir -p a/b/c/d/f/g/h/i/j/k/l/m
~/Desktop $ cd a/b/c/d/f/g/h/i/j/k/l/m/
~/Desktop/…/f/g/h/i/j/k/l/m $ 

The themes are located in ~/.config/powerline-shell/themes. This is my custom theme called rocket.py:

class DefaultColor(object):
    """
    This class should have the default colors for every segment.
    Please test every new segment with this theme first.
    """
    # RESET is not a real color code. It is used as in indicator
    # within the code that any foreground / background color should
    # be cleared
    RESET = -1

    USERNAME_FG = 250
    USERNAME_BG = 240
    USERNAME_ROOT_BG = 124

    # change
    USERNAME_FG = 162
    USERNAME_BG = 232
    USERNAME_ROOT_BG = 124

    HOSTNAME_FG = 250      
    HOSTNAME_BG = 238

    # change
    HOSTNAME_FG = 39
    HOSTNAME_BG = 232

    HOME_SPECIAL_DISPLAY = True
    HOME_BG = 31  # blueish
    HOME_FG = 15  # white
    PATH_BG = 232
    PATH_FG = 250
    CWD_FG = 67
    SEPARATOR_FG = 244


    READONLY_BG = 124
    READONLY_FG = 254

    SSH_BG = 166  # medium orange
    SSH_FG = 254

    REPO_CLEAN_BG = 35
    REPO_CLEAN_FG = 0 
    REPO_DIRTY_BG = 88
    REPO_DIRTY_FG = 15  

    # change
    REPO_CLEAN_BG = 232
    REPO_CLEAN_FG = 35
    REPO_DIRTY_BG = 232
    REPO_DIRTY_FG = 162

    JOBS_FG = 39
    JOBS_BG = 238

    CMD_PASSED_BG = 232
    CMD_PASSED_FG = 245
    CMD_FAILED_BG = 161

    CMD_FAILED_FG = 197
    CMD_FAILED_BG = 232

    SVN_CHANGES_BG = 148
    SVN_CHANGES_FG = 22  

    GIT_AHEAD_BG = 240
    GIT_AHEAD_FG = 250
    GIT_BEHIND_BG = 240
    GIT_BEHIND_FG = 250
    GIT_STAGED_BG = 2
    GIT_STAGED_FG = 15
    GIT_NOTSTAGED_BG = 4
    GIT_NOTSTAGED_FG = 15
    GIT_UNTRACKED_BG = 52
    GIT_UNTRACKED_FG = 15
    GIT_CONFLICTED_BG = 9
    GIT_CONFLICTED_FG = 15

    # change
    GIT_NOTSTAGED_BG = 232 
    GIT_NOTSTAGED_FG = 30
    # change
    GIT_AHEAD_BG = 232 
    GIT_AHEAD_FG = 61
    # change
    GIT_UNTRACKED_BG = 232
    GIT_UNTRACKED_FG = 244
    # change
    GIT_STAGED_BG = 232
    GIT_STAGED_FG = 2

    GIT_STASH_BG = 221
    GIT_STASH_FG = 0

    VIRTUAL_ENV_BG = 35
    VIRTUAL_ENV_FG = 00

    # change
    VIRTUAL_ENV_BG = 232
    VIRTUAL_ENV_FG = 35

    BATTERY_NORMAL_BG = 22
    BATTERY_NORMAL_FG = 7
    BATTERY_LOW_BG = 196
    BATTERY_LOW_FG = 7

    AWS_PROFILE_FG = 39
    AWS_PROFILE_BG = 238

    TIME_FG = 250
    TIME_BG = 238


class Color(DefaultColor):
    """
    This subclass is required when the user chooses to use 'default' theme.
    Because the segments require a 'Color' class for every theme.
    """
    pass

As it can be seen, it is just a Python class with a bunch of variables that define the color of each segment. The colors must be specified using Xterm-256 color codes.

Additional changes

Many of the segments use an empty space as separator. However, I decided to remove them. To do so, you must edit the Python scripts. You can get the location of these scripts using:

pip3 show powerline-shell
Location: /home/auraham/.virtualenvs/dev/lib/python3.5/site-packages

Thus, the scripts that define the segments are here:

/home/auraham/.virtualenvs/dev/lib/python3.5/site-packages/powerline_shell/segments

Below, you will find the scripts and the changes made on each file:

segments/jobs.py:29:            self.powerline.append('%d ' % self.num_jobs,             # change
segments/virtual_env.py:18:        self.powerline.append(" [" + env_name + "] ", fg, bg)   # change
segments/hostname.py:18:                host_prompt = r"@\h"                # change
segments/git.py:83:        self.powerline.append("" + symbol + self.branch + "", fg, bg)  # change
segments/root.py:8:            'bash': '\\$',    # change
segments/username.py:10:            user_prompt = r"\u"    # change
utils.py:76:        return unicode_(self[_key]) if int(self[_key]) >= 1 else u''  # change
utils.py:81:                s = u" {}{}".format(self.n_or_empty(_key), self.symbols[_key])  # change

Better customization

In the previous section we took the long and error-prone way for customizing the segments. The documentation explains we can define our own segments. In the following, we explain how to create a custom segment.

Let's copy root.py from this path

$ cp /home/auraham/.virtualenvs/dev/lib/python3.5/site-packages/powerline_shell/segments/root.py ~/git/segments

We need to update the relative path for importing packages. For example, we need to change this line:

from ..utils import BasicSegment

as follows:

from powerlinr_shell.utils import BasicSegment

Next, we can customize the segment. In my setup, I like to use > instead of $. We can specify that marker as follows:

class Segment(BasicSegment):
    def add_to_powerline(self):
        powerline = self.powerline
        root_indicators = {
            'bash': '>',  # '\\$',
            'tcsh': ' %# ',
            'zsh': ' %# ',
            'bare': ' $ ',
        }
    # ...

Now, we need to specify the location of our segments in the configuration. To do so, update ~/.config/powerline-shell/config.json as shown below:

{                                                                                              
  "theme": "~/git/segments/rocket.py", 
  "mode": "flat",
  "segments": [
    "~/git/segments/time.py",
    "~/git/segments/git.py",
    "~/git/segments/virtual_env.py",
    "~/git/segments/username.py",
    "~/git/segments/hostname.py",
    "~/git/segments/ssh.py",
    "~/git/segments/cwd.py",
    "~/git/segments/hg.py",
    "~/git/segments/jobs.py",
    "~/git/segments/newline.py",
    "~/git/segments/root.py"
  ],  
  "cwd": {
    "full_cwd": true,
    "max_depth": 10, 
    "mode": "plain" 
  },  
  "time": {
    "format": "%H:%M:%S"
  }
}

Notice that segments contains paths. If you want to use the default root, then include root in segments. On the other hand, if you want to use your own implementation of root, then provide its path.

A nice thing of this approach is that ~/git/segments contains all our custom segments and our theme.