|
|
#!/usr/bin/env python |
|
|
# -*- coding: utf-8 -*- |
|
|
from __future__ import print_function |
|
|
|
|
|
import argparse |
|
|
import os |
|
|
import sys |
|
|
|
|
|
py3 = sys.version_info.major == 3 |
|
|
|
|
|
|
|
|
def warn(msg): |
|
|
print('[powerline-bash] ', msg) |
|
|
|
|
|
|
|
|
if py3: |
|
|
def unicode(x): |
|
|
return x |
|
|
|
|
|
|
|
|
class Powerline: |
|
|
symbols = { |
|
|
'compatible': { |
|
|
'lock': 'RO', |
|
|
'network': 'SSH', |
|
|
'separator': u'\u25B6', |
|
|
'separator_thin': u'\u276F' |
|
|
}, |
|
|
'patched': { |
|
|
'lock': u'\uE0A2', |
|
|
'network': u'\uE0A2', |
|
|
'separator': u'\uE0B0', |
|
|
'separator_thin': u'\uE0B1' |
|
|
}, |
|
|
'flat': { |
|
|
'lock': '', |
|
|
'network': '', |
|
|
'separator': '', |
|
|
'separator_thin': '' |
|
|
}, |
|
|
} |
|
|
|
|
|
color_templates = { |
|
|
'bash': '\\[\\e%s\\]', |
|
|
'zsh': '%%{%s%%}', |
|
|
'bare': '%s', |
|
|
} |
|
|
|
|
|
def __init__(self, args, cwd): |
|
|
self.args = args |
|
|
self.cwd = cwd |
|
|
mode, shell = args.mode, args.shell |
|
|
self.color_template = self.color_templates[shell] |
|
|
self.reset = self.color_template % '[0m' |
|
|
self.lock = Powerline.symbols[mode]['lock'] |
|
|
self.network = Powerline.symbols[mode]['network'] |
|
|
self.separator = Powerline.symbols[mode]['separator'] |
|
|
self.separator_thin = Powerline.symbols[mode]['separator_thin'] |
|
|
self.segments = [] |
|
|
|
|
|
def color(self, prefix, code): |
|
|
if code is None: |
|
|
return '' |
|
|
else: |
|
|
return self.color_template % ('[%s;5;%sm' % (prefix, code)) |
|
|
|
|
|
def fgcolor(self, code): |
|
|
return self.color('38', code) |
|
|
|
|
|
def bgcolor(self, code): |
|
|
return self.color('48', code) |
|
|
|
|
|
def append(self, content, fg, bg, separator=None, separator_fg=None): |
|
|
self.segments.append((content, fg, bg, |
|
|
separator if separator is not None else self.separator, |
|
|
separator_fg if separator_fg is not None else bg)) |
|
|
|
|
|
def draw(self): |
|
|
text = (''.join(self.draw_segment(i) for i in range(len(self.segments))) |
|
|
+ self.reset) + ' ' |
|
|
if py3: |
|
|
return text |
|
|
else: |
|
|
return text.encode('utf-8') |
|
|
|
|
|
def draw_segment(self, idx): |
|
|
segment = self.segments[idx] |
|
|
next_segment = self.segments[idx + 1] if idx < len(self.segments)-1 else None |
|
|
|
|
|
return ''.join(( |
|
|
self.fgcolor(segment[1]), |
|
|
self.bgcolor(segment[2]), |
|
|
segment[0], |
|
|
self.bgcolor(next_segment[2]) if next_segment else self.reset, |
|
|
self.fgcolor(segment[4]), |
|
|
segment[3])) |
|
|
|
|
|
|
|
|
class RepoStats: |
|
|
symbols = { |
|
|
'detached': u'\u2693', |
|
|
'ahead': u'\u2B06', |
|
|
'behind': u'\u2B07', |
|
|
'staged': u'\u2714', |
|
|
'not_staged': u'\u270E', |
|
|
'untracked': u'\u003F', |
|
|
'conflicted': u'\u273C' |
|
|
} |
|
|
|
|
|
def __init__(self): |
|
|
self.ahead = 0 |
|
|
self.behind = 0 |
|
|
self.untracked = 0 |
|
|
self.not_staged = 0 |
|
|
self.staged = 0 |
|
|
self.conflicted = 0 |
|
|
|
|
|
@property |
|
|
def dirty(self): |
|
|
qualifiers = [ |
|
|
self.untracked, |
|
|
self.not_staged, |
|
|
self.staged, |
|
|
self.conflicted, |
|
|
] |
|
|
return sum(qualifiers) > 0 |
|
|
|
|
|
def __getitem__(self, _key): |
|
|
return getattr(self, _key) |
|
|
|
|
|
def n_or_empty(self, _key): |
|
|
"""Given a string name of one of the properties of this class, returns |
|
|
the value of the property as a string when the value is greater than |
|
|
1. When it is not greater than one, returns an empty string. |
|
|
|
|
|
As an example, if you want to show an icon for untracked files, but you |
|
|
only want a number to appear next to the icon when there are more than |
|
|
one untracked files, you can do: |
|
|
|
|
|
segment = repo_stats.n_or_empty("untracked") + icon_string |
|
|
""" |
|
|
return unicode(self[_key]) if int(self[_key]) > 1 else u'' |
|
|
|
|
|
def add_to_powerline(self, powerline, color): |
|
|
def add(_key, fg, bg): |
|
|
if self[_key]: |
|
|
s = u" {}{} ".format(self.n_or_empty(_key), self.symbols[_key]) |
|
|
powerline.append(s, fg, bg) |
|
|
add('ahead', color.GIT_AHEAD_FG, color.GIT_AHEAD_BG) |
|
|
add('behind', color.GIT_BEHIND_FG, color.GIT_BEHIND_BG) |
|
|
add('staged', color.GIT_STAGED_FG, color.GIT_STAGED_BG) |
|
|
add('not_staged', color.GIT_NOTSTAGED_FG, color.GIT_NOTSTAGED_BG) |
|
|
add('untracked', color.GIT_UNTRACKED_FG, color.GIT_UNTRACKED_BG) |
|
|
add('conflicted', color.GIT_CONFLICTED_FG, color.GIT_CONFLICTED_BG) |
|
|
|
|
|
|
|
|
def get_valid_cwd(): |
|
|
""" We check if the current working directory is valid or not. Typically |
|
|
happens when you checkout a different branch on git that doesn't have |
|
|
this directory. |
|
|
We return the original cwd because the shell still considers that to be |
|
|
the working directory, so returning our guess will confuse people |
|
|
""" |
|
|
# Prefer the PWD environment variable. Python's os.getcwd function follows |
|
|
# symbolic links, which is undesirable. But if PWD is not set then fall |
|
|
# back to this func |
|
|
try: |
|
|
cwd = os.getenv('PWD') or os.getcwd() |
|
|
except: |
|
|
warn("Your current directory is invalid. If you open a ticket at " + |
|
|
"https://github.com/milkbikis/powerline-shell/issues/new " + |
|
|
"we would love to help fix the issue.") |
|
|
sys.stdout.write("> ") |
|
|
sys.exit(1) |
|
|
|
|
|
parts = cwd.split(os.sep) |
|
|
up = cwd |
|
|
while parts and not os.path.exists(up): |
|
|
parts.pop() |
|
|
up = os.sep.join(parts) |
|
|
if cwd != up: |
|
|
warn("Your current directory is invalid. Lowest valid directory: " |
|
|
+ up) |
|
|
return cwd |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
arg_parser = argparse.ArgumentParser() |
|
|
arg_parser.add_argument('--cwd-mode', action='store', |
|
|
help='How to display the current directory', default='fancy', |
|
|
choices=['fancy', 'plain', 'dironly']) |
|
|
arg_parser.add_argument('--cwd-only', action='store_true', |
|
|
help='Deprecated. Use --cwd-mode=dironly') |
|
|
arg_parser.add_argument('--cwd-max-depth', action='store', type=int, |
|
|
default=5, help='Maximum number of directories to show in path') |
|
|
arg_parser.add_argument('--cwd-max-dir-size', action='store', type=int, |
|
|
help='Maximum number of letters displayed for each directory in the path') |
|
|
arg_parser.add_argument('--colorize-hostname', action='store_true', |
|
|
help='Colorize the hostname based on a hash of itself.') |
|
|
arg_parser.add_argument('--mode', action='store', default='patched', |
|
|
help='The characters used to make separators between segments', |
|
|
choices=['patched', 'compatible', 'flat']) |
|
|
arg_parser.add_argument('--shell', action='store', default='bash', |
|
|
help='Set this to your shell type', choices=['bash', 'zsh', 'bare']) |
|
|
arg_parser.add_argument('prev_error', nargs='?', type=int, default=0, |
|
|
help='Error code returned by the last command') |
|
|
args = arg_parser.parse_args() |
|
|
|
|
|
powerline = Powerline(args, get_valid_cwd())
|
|
|
|