homes/common/.local/bin/powerline-zsh.py

326 lines
9.9 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import subprocess
import sys
import re
import argparse
def warn(msg):
print '[powerline-zsh] ', msg
class Color:
# The following link is a pretty good resources for color values:
# http://www.calmar.ws/vim/color-output.png
PATH_BG = 237 # dark grey
PATH_FG = 250 # light grey
CWD_FG = 254 # nearly-white grey
SEPARATOR_FG = 244
REPO_CLEAN_BG = 148 # a light green color
REPO_CLEAN_FG = 0 # black
REPO_DIRTY_BG = 161 # pink/red
REPO_DIRTY_FG = 15 # white
CMD_PASSED_BG = 236
CMD_PASSED_FG = 15
CMD_FAILED_BG = 161
CMD_FAILED_FG = 15
SVN_CHANGES_BG = 148
SVN_CHANGES_FG = 22 # dark green
VIRTUAL_ENV_BG = 35 # a mid-tone green
VIRTUAL_ENV_FG = 22
class Powerline:
symbols = {
'compatible': {
'separator': u'\u25B6',
'separator_thin': u'\u276F'
},
'patched': {
'separator': u'\u2B80',
'separator_thin': u'\u2B81'
},
'default': {
'separator': '',
'separator_thin': ''
}
}
LSQESCRSQ = '\\[\\e%s\\]'
reset = ' %f%k'
def __init__(self, mode='default'):
self.separator = Powerline.symbols[mode]['separator']
self.separator_thin = Powerline.symbols[mode]['separator_thin']
self.segments = []
def color(self, prefix, code):
if prefix == '38':
return '%%F{%s}' % code
elif prefix == '48':
return '%%K{%s}' % code
def fgcolor(self, code):
return self.color('38', code)
def bgcolor(self, code):
return self.color('48', code)
def append(self, segment):
self.segments.append(segment)
def draw(self):
return (''.join((s[0].draw(self, s[1]) for s in zip(self.segments, self.segments[1:] + [None])))
+ self.reset)
class Segment:
def __init__(self, powerline, content, fg, bg, separator=None, separator_fg=None):
self.powerline = powerline
self.content = content
self.fg = fg
self.bg = bg
self.separator = separator or powerline.separator
self.separator_fg = separator_fg or bg
def draw(self, powerline, next_segment=None):
if next_segment:
separator_bg = powerline.bgcolor(next_segment.bg)
else:
separator_bg = powerline.reset
return ''.join((
powerline.fgcolor(self.fg),
powerline.bgcolor(self.bg),
self.content,
separator_bg,
powerline.fgcolor(self.separator_fg),
self.separator))
def add_cwd_segment(powerline, cwd, maxdepth, cwd_only=False):
#powerline.append(' \\w ', 15, 237)
home = os.getenv('HOME')
cwd = os.getenv('PWD')
if cwd.find(home) == 0:
cwd = cwd.replace(home, '~', 1)
if cwd[0] == '/':
cwd = cwd[1:]
names = cwd.split('/')
if len(names) > maxdepth:
names = names[:2] + [''] + names[2 - maxdepth:]
if not cwd_only:
for n in names[:-1]:
powerline.append(Segment(powerline, ' %s ' % n, Color.PATH_FG, Color.PATH_BG, powerline.separator_thin, Color.SEPARATOR_FG))
powerline.append(Segment(powerline, ' %s ' % names[-1], Color.CWD_FG, Color.PATH_BG))
def get_hg_status():
has_modified_files = False
has_untracked_files = False
has_missing_files = False
output = subprocess.Popen(['hg', 'status'], stdout=subprocess.PIPE).communicate()[0]
for line in output.split('\n'):
if line == '':
continue
elif line[0] == '?':
has_untracked_files = True
elif line[0] == '!':
has_missing_files = True
else:
has_modified_files = True
return has_modified_files, has_untracked_files, has_missing_files
def add_hg_segment(powerline, cwd):
branch = os.popen('hg branch 2> /dev/null').read().rstrip()
if len(branch) == 0:
return False
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
has_modified_files, has_untracked_files, has_missing_files = get_hg_status()
if has_modified_files or has_untracked_files or has_missing_files:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
extra = ''
if has_untracked_files:
extra += '+'
if has_missing_files:
extra += '!'
branch += (' ' + extra if extra != '' else '')
powerline.append(Segment(powerline, ' %s ' % branch, fg, bg))
return True
def get_git_status():
has_pending_commits = True
has_untracked_files = False
detached_head = False
origin_position = ""
current_branch = ''
output = subprocess.Popen(['git', 'status', '-unormal'], stdout=subprocess.PIPE).communicate()[0]
for line in output.split('\n'):
origin_status = re.findall("Your branch is (ahead|behind).*?(\d+) comm", line)
if len(origin_status) > 0:
origin_position = " %d" % int(origin_status[0][1])
if origin_status[0][0] == 'behind':
origin_position += ''
if origin_status[0][0] == 'ahead':
origin_position += ''
if line.find('nothing to commit (working directory clean)') >= 0:
has_pending_commits = False
if line.find('Untracked files') >= 0:
has_untracked_files = True
if line.find('Not currently on any branch') >= 0:
detached_head = True
if line.find('On branch') >= 0:
current_branch = re.findall('On branch ([^ ]+)', line)[0]
return has_pending_commits, has_untracked_files, origin_position, detached_head, current_branch
def add_git_segment(powerline, cwd):
#cmd = "git branch 2> /dev/null | grep -e '\\*'"
p1 = subprocess.Popen(['git', 'branch'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-e', '\\*'], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0].strip()
if len(output) == 0:
return False
has_pending_commits, has_untracked_files, origin_position, detached_head, current_branch = get_git_status()
if len(current_branch) > 0:
branch = current_branch
elif detached_head:
branch = subprocess.Popen(['git', 'describe', '--all', '--contains', '--abbrev=4', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
branch = '((' + branch.communicate()[0].strip() + '))'
else:
return 'master'
branch += origin_position
if has_untracked_files:
branch += ' +'
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
if has_pending_commits:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
powerline.append(Segment(powerline, ' %s ' % branch, fg, bg))
return True
def add_svn_segment(powerline, cwd):
if not os.path.exists(os.path.join(cwd, '.svn')):
return
'''svn info:
First column: Says if item was added, deleted, or otherwise changed
' ' no modifications
'A' Added
'C' Conflicted
'D' Deleted
'I' Ignored
'M' Modified
'R' Replaced
'X' an unversioned directory created by an externals definition
'?' item is not under version control
'!' item is missing (removed by non-svn command) or incomplete
'~' versioned item obstructed by some item of a different kind
'''
#TODO: Color segment based on above status codes
try:
#cmd = '"svn status | grep -c "^[ACDIMRX\\!\\~]"'
p1 = subprocess.Popen(['svn', 'status'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-c', '^[ACDIMRX\\!\\~]'], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0].strip()
if len(output) > 0 and int(output) > 0:
changes = output.strip()
powerline.append(Segment(powerline, ' %s ' % changes, Color.SVN_CHANGES_FG, Color.SVN_CHANGES_BG))
except OSError:
return False
except subprocess.CalledProcessError:
return False
return True
def add_repo_segment(powerline, cwd):
for add_repo_segment in [add_git_segment, add_svn_segment, add_hg_segment]:
try:
if add_repo_segment(p, cwd):
return
except subprocess.CalledProcessError:
pass
except OSError:
pass
def add_virtual_env_segment(powerline, cwd):
env = os.getenv("VIRTUAL_ENV")
if env is None:
return False
env_name = os.path.basename(env)
bg = Color.VIRTUAL_ENV_BG
fg = Color.VIRTUAL_ENV_FG
powerline.append(Segment(powerline, ' %s ' % env_name, fg, bg))
return True
def add_root_indicator(powerline, error):
bg = Color.CMD_PASSED_BG
fg = Color.CMD_PASSED_FG
if int(error) != 0:
fg = Color.CMD_FAILED_FG
bg = Color.CMD_FAILED_BG
powerline.append(Segment(powerline, ' $ ', fg, bg))
def get_valid_cwd():
try:
cwd = os.getcwd()
except:
cwd = os.getenv('PWD') # This is where the OS thinks we are
parts = cwd.split(os.sep)
up = cwd
while parts and not os.path.exists(up):
parts.pop()
up = os.sep.join(parts)
try:
os.chdir(up)
except:
warn("Your current directory is invalid.")
sys.exit(1)
warn("Your current directory is invalid. Lowest valid directory: " + up)
return cwd
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--cwd-only', action="store_true")
arg_parser.add_argument('prev_error', nargs='?', default=0)
args = arg_parser.parse_args()
p = Powerline(mode='default')
cwd = get_valid_cwd()
add_virtual_env_segment(p, cwd)
#p.append(Segment(' \\u ', 250, 240))
#p.append(Segment(' \\h ', 250, 238))
add_cwd_segment(p, cwd, 5, args.cwd_only)
add_repo_segment(p, cwd)
add_root_indicator(p, args.prev_error)
sys.stdout.write(p.draw())
# vim: set expandtab: