new render program prototype mostly finished
This commit is contained in:
parent
afd9900397
commit
cd3dd70b45
2 changed files with 441 additions and 189 deletions
555
render.py
555
render.py
|
@ -1,213 +1,518 @@
|
|||
#! /usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
SYM_GIT = "\ue0a0"
|
||||
SYM_LOCK = "\ue0a2"
|
||||
COLOR_LOAD_IDLE = 10
|
||||
COLOR_LOAD_OK = 2
|
||||
COLOR_LOAD_WARN = 3
|
||||
COLOR_LOAD_ERROR = 1
|
||||
COLOR_USER_USER = 10
|
||||
COLOR_USER_ROOT = 196
|
||||
COLOR_SESSION_LOCAL = 7
|
||||
COLOR_SESSION_REMOTE = 202
|
||||
COLOR_TERM_SCREEN = 27
|
||||
|
||||
def style_color(fg, bg):
|
||||
return '\033[38;5;%dm\033[48;5;%dm' %(fg, bg)
|
||||
COLOR_CLOCK = 27
|
||||
COLOR_MEMSWAP = 8
|
||||
COLOR_SPACE_OK = 2
|
||||
COLOR_SPACE_WARN = 3
|
||||
COLOR_SPACE_ERROR = 1
|
||||
|
||||
COLOR_DIR_RW = 34
|
||||
COLOR_DIR_RO = 202
|
||||
COLOR_DIR_NO = 196
|
||||
|
||||
COLOR_GIT_CLEAN = 10
|
||||
COLOR_GIT_DIRTY = 1
|
||||
COLOR_GIT_MERGE = 196
|
||||
COLOR_GIT_UNTRACKED = 202
|
||||
COLOR_GIT_MODIFIED = 1
|
||||
COLOR_GIT_STAGED = 10
|
||||
|
||||
COLOR_PROMPT_OK = 14
|
||||
COLOR_PROMPT_ERROR = 1
|
||||
|
||||
MOUNT_IGNORE_FS = ["iso9660", "tmpfs"]
|
||||
MOUNT_IGNORE_DIR = ["/dev", "/proc", "/sys"]
|
||||
|
||||
GIT_BLACKLIST = ["/var/paludis/repositories"]
|
||||
|
||||
def style_color(fg, bg=0):
|
||||
return "\033[38;5;%dm\033[48;5;%dm" %(fg, bg)
|
||||
|
||||
def style_bold():
|
||||
return '\033[1m'
|
||||
return "\033[1m"
|
||||
|
||||
def style_reset():
|
||||
return '\033[0m'
|
||||
return "\033[0m"
|
||||
|
||||
class LoginPart:
|
||||
user_fg = 34
|
||||
root_fg = 196
|
||||
remote_fg = 202
|
||||
local_fg = 7
|
||||
screen_fg = 27
|
||||
bg = 0
|
||||
def colored_strlen(raw):
|
||||
"""
|
||||
Returns string length without ANSI control escapes.
|
||||
"""
|
||||
|
||||
l = 0
|
||||
reading_escape = False
|
||||
|
||||
for c in raw:
|
||||
if c == "\x1b":
|
||||
reading_escape = True
|
||||
continue
|
||||
if reading_escape and c == "m":
|
||||
reading_escape = False
|
||||
continue
|
||||
|
||||
if not reading_escape:
|
||||
l += 1
|
||||
|
||||
return l
|
||||
|
||||
def si_number(raw):
|
||||
if raw > 2**40:
|
||||
raw /= 2**40
|
||||
si = "Ti"
|
||||
elif raw > 2**30:
|
||||
raw /= 2**30
|
||||
si = "Gi"
|
||||
elif raw > 2**20:
|
||||
raw /= 2**20
|
||||
si = "Mi"
|
||||
elif raw > 2**10:
|
||||
raw /= 2**10
|
||||
si = "ki"
|
||||
else:
|
||||
si = ""
|
||||
|
||||
return (raw, si)
|
||||
|
||||
def space_string(free):
|
||||
"""
|
||||
Returns a formatting string suitable for printing out free space: float for >1k, int otherwise.
|
||||
"""
|
||||
|
||||
return "%.2f%s" if free > 1024 else "%d%s"
|
||||
|
||||
class Sysload:
|
||||
def __init__(self):
|
||||
self.user = os.getlogin()
|
||||
self.remote = bool(os.getenv('SSH_CLIENT'))
|
||||
self.sign = ' ⇄ ' if self.remote else '@'
|
||||
self.host = socket.gethostname()
|
||||
self.screen = os.getenv('WINDOW')
|
||||
loadavg = open("/proc/loadavg").read().split()
|
||||
cores = os.sysconf(os.sysconf_names["SC_NPROCESSORS_ONLN"])
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return len(self.user) + len(self.host) + len(self.screen) + len(self.sign) + 1
|
||||
self.load1 = float(loadavg[0]) / cores
|
||||
self.load5 = float(loadavg[1]) / cores
|
||||
self.load15 = float(loadavg[2]) / cores
|
||||
self.tasks_ready, self.tasks_total = [int(x) for x in loadavg[3].split("/")]
|
||||
|
||||
|
||||
class Settings:
|
||||
def __init__(self, argv):
|
||||
try:
|
||||
self.return_value = int(argv[1])
|
||||
self.term_width = int(argv[2])
|
||||
self.load_warn = float(argv[3])
|
||||
self.load_error = float(argv[4])
|
||||
self.space_warn = float(argv[5])
|
||||
self.space_error = float(argv[6])
|
||||
self.is_root = os.geteuid() == 0
|
||||
except:
|
||||
print("Usage: %s return_value $COLUMNS load_warn load_error space_warn space_error")
|
||||
print(" e.g. %s 1 $COLUMNS 1.25 2.5 0.15 0.05")
|
||||
print()
|
||||
|
||||
raise
|
||||
|
||||
def get_load_color(self, load):
|
||||
if load > self.load_error:
|
||||
return COLOR_LOAD_ERROR
|
||||
elif load > self.load_warn:
|
||||
return COLOR_LOAD_WARN
|
||||
elif load > 0.1:
|
||||
return COLOR_LOAD_OK
|
||||
else:
|
||||
return COLOR_LOAD_IDLE
|
||||
|
||||
def get_space_color(self, free, total):
|
||||
free = free / total
|
||||
|
||||
if free < self.space_error:
|
||||
return COLOR_SPACE_ERROR
|
||||
elif free < self.space_warn:
|
||||
return COLOR_SPACE_WARN
|
||||
else:
|
||||
return COLOR_SPACE_OK
|
||||
|
||||
class Part:
|
||||
def __init__(self):
|
||||
self.fragments = []
|
||||
|
||||
def __str__(self):
|
||||
output = ''
|
||||
return "".join(self.fragments)
|
||||
|
||||
if os.geteuid() == 0:
|
||||
output += style_color(self.root_fg, self.bg)
|
||||
def __len__(self):
|
||||
return colored_strlen(str(self))
|
||||
|
||||
class LoginPart(Part):
|
||||
"""
|
||||
username@hostname:screen
|
||||
- username is green for users, red for root
|
||||
- @ is a literal "@" if this is a local session, " ⇄ " when remote (ssh)
|
||||
- hostname is colored according to sysload
|
||||
- screen is the screen window ID if available
|
||||
"""
|
||||
|
||||
def __init__(self, settings, sysload):
|
||||
Part.__init__(self)
|
||||
is_remote = bool(os.getenv("SSH_CLIENT"))
|
||||
screen = os.getenv("WINDOW")
|
||||
|
||||
# username
|
||||
if settings.is_root:
|
||||
self.fragments.append(style_color(COLOR_USER_ROOT))
|
||||
else:
|
||||
output += style_color(self.user_fg, self.bg)
|
||||
self.fragments.append(style_color(COLOR_USER_USER))
|
||||
|
||||
output += self.user
|
||||
self.fragments.append(style_bold())
|
||||
user = os.getlogin()
|
||||
self.fragments.append(user)
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
if self.remote:
|
||||
output += style_color(self.remote_fg, self.bg)
|
||||
# sign
|
||||
if is_remote:
|
||||
self.fragments.append(style_color(COLOR_SESSION_REMOTE))
|
||||
else:
|
||||
output += style_color(self.local_fg, self.bg)
|
||||
self.fragments.append(style_color(COLOR_SESSION_LOCAL))
|
||||
|
||||
output += self.sign
|
||||
output += self.host
|
||||
sign = " ⇄ " if is_remote else "@"
|
||||
self.fragments.append(sign)
|
||||
|
||||
if self.screen:
|
||||
output += style_reset()
|
||||
output += ':'
|
||||
output += style_color(self.screen_fg, self.bg)
|
||||
output += self.screen
|
||||
# hostname
|
||||
self.fragments.append(style_color(settings.get_load_color(sysload.load1)))
|
||||
hostname = socket.gethostname()
|
||||
self.fragments.append(hostname)
|
||||
|
||||
output += style_reset()
|
||||
|
||||
return output
|
||||
# screen window ID
|
||||
if screen:
|
||||
self.fragments.append(style_reset())
|
||||
self.fragments.append(":")
|
||||
self.fragments.append(style_bold())
|
||||
self.fragments.append(style_color(COLOR_TERM_SCREEN))
|
||||
self.fragments.append(screen)
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
def list_homes():
|
||||
with open('/etc/passwd') as f:
|
||||
with open("/etc/passwd") as f:
|
||||
for line in f:
|
||||
tokens = line.split(':')
|
||||
tokens = line.split(":")
|
||||
|
||||
if tokens[5] != '/dev/null':
|
||||
if tokens[5] != "/dev/null":
|
||||
yield (tokens[5], tokens[0])
|
||||
|
||||
class Dir:
|
||||
writable_fg = 34
|
||||
fg = 196
|
||||
bg = 0
|
||||
|
||||
def __init__(self, path, name=None, slash='/'):
|
||||
def __init__(self, path, name=None, slash="/"):
|
||||
self.path = path
|
||||
self.slash = slash
|
||||
self.lock = not os.access(path, os.W_OK)
|
||||
self.truncated = False
|
||||
self.name = os.path.basename(path) if name is None else name
|
||||
|
||||
if name is None:
|
||||
name = os.path.basename(path)
|
||||
if os.access(path, os.W_OK):
|
||||
self.color = COLOR_DIR_RW
|
||||
elif os.access(path, os.X_OK):
|
||||
self.color = COLOR_DIR_RO
|
||||
else:
|
||||
self.color = COLOR_DIR_NO
|
||||
|
||||
self.raw_str = name + ('' if self.lock else '')
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return len(self.raw_str) + len(self.slash)
|
||||
def __len__(self):
|
||||
return len(self.name) + len(self.slash)
|
||||
|
||||
def truncate(self):
|
||||
self.truncated = True
|
||||
|
||||
if self.raw_str:
|
||||
self.raw_str = self.raw_str[0]
|
||||
if self.name:
|
||||
self.name = self.name[0] + "…"
|
||||
|
||||
def __str__(self):
|
||||
output = self.slash
|
||||
output = [self.slash]
|
||||
|
||||
if self.truncated:
|
||||
output += style_bold()
|
||||
output.append(style_bold())
|
||||
|
||||
if self.raw_str:
|
||||
output += style_color(self.fg if self.lock else self.writable_fg, self.bg)
|
||||
output += self.raw_str
|
||||
output.append(style_color(self.color))
|
||||
output.append(self.name)
|
||||
|
||||
output += style_reset()
|
||||
output.append(style_reset())
|
||||
|
||||
return output
|
||||
return "".join(output)
|
||||
|
||||
class PathPart:
|
||||
class PathPart(Part):
|
||||
"""
|
||||
/path/to/cwd
|
||||
|
||||
Contructs a list of Dirs from the root to CWD. When fit is called, some Dirs may be shortened.
|
||||
"""
|
||||
def __init__(self, term_width):
|
||||
Part.__init__(self)
|
||||
self.term_width = term_width
|
||||
self.segments = []
|
||||
self.dirs = []
|
||||
|
||||
homes = dict(list_homes())
|
||||
dirs = os.getcwd().split('/')[1:]
|
||||
path = ''
|
||||
dirs = os.getcwd().split("/")[1:]
|
||||
path = ""
|
||||
|
||||
for dir in dirs:
|
||||
path += '/' + dir
|
||||
path += "/" + dir
|
||||
|
||||
if path in homes:
|
||||
self.segments = []
|
||||
self.dirs = []
|
||||
|
||||
if homes[path] == os.getlogin():
|
||||
self.segments.append(Dir(path, '', '~'))
|
||||
self.dirs.append(Dir(path, "", "~"))
|
||||
else:
|
||||
self.segments.append(Dir(path, homes[path], '~'))
|
||||
self.dirs.append(Dir(path, homes[path], "~"))
|
||||
|
||||
else:
|
||||
self.segments.append(Dir(path))
|
||||
self.dirs.append(Dir(path))
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return sum(s.length for s in self.segments)
|
||||
def full_length(self):
|
||||
return sum(len(dir) for dir in self.dirs)
|
||||
|
||||
def truncate(self, length):
|
||||
for s in self.segments:
|
||||
if self.length > length:
|
||||
s.truncate()
|
||||
else:
|
||||
break
|
||||
def shrink_fit(self, line):
|
||||
self.fragments = []
|
||||
|
||||
def truncate_fit(self, line):
|
||||
space = self.term_width - sum(part.length for part in line if part is not self)
|
||||
self.truncate(space)
|
||||
available_space = self.term_width - sum(len(part) for part in line if part is not self) - len(line) + 1 # account for single-space separators
|
||||
|
||||
def __str__(self):
|
||||
return ''.join(str(s) for s in self.segments)
|
||||
for dir in self.dirs:
|
||||
if self.full_length > available_space:
|
||||
dir.truncate()
|
||||
|
||||
self.fragments.append(str(dir))
|
||||
|
||||
def command(cmd):
|
||||
"""
|
||||
Executes a command, returns stdout, suppresses stderr."
|
||||
"""
|
||||
|
||||
s = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
s.wait()
|
||||
|
||||
return s.stdout.read().decode("utf-8")
|
||||
|
||||
class GitPart(Part):
|
||||
"""
|
||||
↘2 ↗4 ⚠master ?11 ✎6 ✉10
|
||||
|
||||
- 2 commits are available for pulling (remote is ahead)
|
||||
- 4 commits are available for pushing (local is ahead)
|
||||
- we're merging
|
||||
- branch name is master
|
||||
- 11 untracked filed
|
||||
- 6 modified files
|
||||
- 10 modified and staged files
|
||||
|
||||
Numeric elements are hidden if the value is 0.
|
||||
"""
|
||||
|
||||
class GitPart:
|
||||
def __init__(self):
|
||||
...
|
||||
Part.__init__(self)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return len(str(self))
|
||||
branch_name = command("git name-rev --name-only --no-undefined --always HEAD").strip()
|
||||
|
||||
def __str__(self):
|
||||
return 'GIT'
|
||||
if branch_name:
|
||||
self.fragments.append("| ")
|
||||
|
||||
class Padding:
|
||||
count_ahead = command("git log --oneline @{u}..").count("\n")
|
||||
count_behind = command("git log --oneline ..@{u}").count("\n")
|
||||
git_dir = command("git rev-parse --git-dir").strip()
|
||||
merging = os.path.exists(os.path.join(git_dir, "MERGE_HEAD"))
|
||||
untracked = command("git ls-files --other --exclude-standard").count("\n")
|
||||
modified = command("git diff --name-only").count("\n")
|
||||
staged = command("git diff --name-only --staged").count("\n")
|
||||
|
||||
if count_ahead:
|
||||
self.fragments.append("↘%d " %(count_ahead))
|
||||
|
||||
if count_behind:
|
||||
self.fragments.append("↗%d " %(count_behind))
|
||||
|
||||
if merging:
|
||||
self.fragments.append(style_color(COLOR_GIT_MERGE))
|
||||
self.fragments.append(style_bold())
|
||||
self.fragments.append("⚠")
|
||||
elif modified or staged:
|
||||
self.fragments.append(style_color(COLOR_GIT_DIRTY))
|
||||
else:
|
||||
self.fragments.append(style_color(COLOR_GIT_CLEAN))
|
||||
|
||||
self.fragments.append(branch_name)
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
if untracked:
|
||||
self.fragments.append(style_color(COLOR_GIT_UNTRACKED))
|
||||
self.fragments.append(" ?%d" %(untracked))
|
||||
|
||||
if modified:
|
||||
self.fragments.append(style_color(COLOR_GIT_MODIFIED))
|
||||
self.fragments.append(" ✎%d" %(modified))
|
||||
|
||||
if staged:
|
||||
self.fragments.append(style_color(COLOR_GIT_STAGED))
|
||||
self.fragments.append(" ✉%d" %(staged))
|
||||
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
class Padding(Part):
|
||||
def __init__(self, term_width):
|
||||
Part.__init__(self)
|
||||
self.term_width = term_width
|
||||
self.length = 0
|
||||
|
||||
def expand_fit(self, line):
|
||||
self.length = self.term_width - sum(part.length for part in line if part is not self) - len(line) + 1
|
||||
length = self.term_width - sum(len(part) for part in line if part is not self) - len(line) + 1 # account for single-space separators
|
||||
|
||||
def __str__(self):
|
||||
return ' ' * self.length
|
||||
self.fragments = [" " * length]
|
||||
|
||||
class StatsPart:
|
||||
clock = 27
|
||||
space_good = 34
|
||||
space_warn = 208
|
||||
space_bad = 196
|
||||
class StatsPart(Part):
|
||||
"""
|
||||
[ clock | sysload tasks | m:memory (?s:swap?) mountpoint_spaces...
|
||||
- clock shows current day and time (in DD HH:MM)
|
||||
- sysload is current loadavg divided by amount of cores (in 1)
|
||||
- tasks is the total amount of tasks on the system
|
||||
- memory shows current free memory (in octets)
|
||||
- swap shows remaining swap space but only if any is actually used
|
||||
- mountpoint_spaces list one free space per mountpoint
|
||||
"""
|
||||
def __init__(self, settings, sysload):
|
||||
Part.__init__(self)
|
||||
|
||||
def __init__(self, red_thresh, yellow_thresh, warning_only=True):
|
||||
self.red = red_thresh
|
||||
self.yellow = yellow_thresh
|
||||
# clock
|
||||
self.fragments.append(datetime.datetime.now().strftime("[ %%d \033[38;5;%dm%%H:%%M\033[0m | " %(COLOR_CLOCK)))
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return len(str(self))
|
||||
# sysload
|
||||
self.fragments.append(style_color(settings.get_load_color(sysload.load1)))
|
||||
self.fragments.append("%.2f " %(sysload.load1))
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
def __str__(self):
|
||||
return '[ 13:34 | 0.53 | r4.20M'
|
||||
# tasks
|
||||
self.fragments.append("%d | " %(sysload.tasks_total))
|
||||
|
||||
if __name__ == '__main__':
|
||||
red_thresh = float(sys.argv[1])
|
||||
yellow_thresh = float(sys.argv[2])
|
||||
term_width = int(sys.argv[3])
|
||||
exit_status = int(sys.argv[4])
|
||||
# memory (and swap, if used)
|
||||
mem_total = 0
|
||||
mem_free = 0
|
||||
swap_total = 0
|
||||
swap_free = 0
|
||||
|
||||
lp = LoginPart()
|
||||
pp = PathPart(term_width)
|
||||
with open("/proc/meminfo") as f:
|
||||
for line in f:
|
||||
if line.startswith("MemTotal"):
|
||||
mem_total = int(line.split()[1]) * 1024
|
||||
elif line.startswith("MemAvailable"):
|
||||
mem_free = int(line.split()[1]) * 1024
|
||||
elif line.startswith("SwapTotal"):
|
||||
swap_total = int(line.split()[1]) * 1024
|
||||
elif line.startswith("SwapFree"):
|
||||
swap_free = int(line.split()[1]) * 1024
|
||||
|
||||
self.fragments.append(style_color(COLOR_MEMSWAP))
|
||||
self.fragments.append("m")
|
||||
self.fragments.append(style_color(settings.get_space_color(mem_free, mem_total)))
|
||||
mem_free_si = si_number(mem_free)
|
||||
self.fragments.append(space_string(mem_free) %(mem_free_si[0], mem_free_si[1]))
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
if swap_total - swap_free > 2**20:
|
||||
self.fragments.append(style_color(COLOR_MEMSWAP))
|
||||
self.fragments.append(" s")
|
||||
self.fragments.append(style_color(settings.get_space_color(swap_free, swap_total)))
|
||||
swap_free_si = si_number(swap_free)
|
||||
self.fragments.append(space_string(swap_free) %(swap_free_si[0], swap_free_si[1]))
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
# mountpoints
|
||||
names = []
|
||||
|
||||
with open("/proc/self/mounts") as f:
|
||||
for line in f:
|
||||
_, dir, type, options, *rest = line.split()
|
||||
|
||||
# skip non-storage mounts
|
||||
if type in MOUNT_IGNORE_FS:
|
||||
continue
|
||||
|
||||
if any([dir.startswith(d) for d in MOUNT_IGNORE_DIR]):
|
||||
continue
|
||||
|
||||
if "rw" not in options.split(","):
|
||||
continue
|
||||
|
||||
# /proc/self/mounts uses a literal \\040 string to escape spaces
|
||||
dir = dir.replace("\\040", " ")
|
||||
|
||||
basename = os.path.basename(dir)
|
||||
|
||||
if basename:
|
||||
short_name = " "
|
||||
|
||||
for c in basename:
|
||||
short_name += c
|
||||
|
||||
if short_name not in names:
|
||||
break
|
||||
else:
|
||||
short_name = " /"
|
||||
|
||||
stat = os.statvfs(dir)
|
||||
|
||||
stor_total = stat.f_blocks * stat.f_bsize
|
||||
stor_free = stat.f_bavail * stat.f_bsize
|
||||
|
||||
self.fragments.append(short_name)
|
||||
self.fragments.append(style_color(settings.get_space_color(stor_free, stor_total)))
|
||||
stor_free_si = si_number(stor_free)
|
||||
self.fragments.append(space_string(stor_free) %(stor_free_si[0], stor_free_si[1]))
|
||||
self.fragments.append(style_reset())
|
||||
|
||||
class InputPart(Part):
|
||||
"""
|
||||
$
|
||||
or
|
||||
❌42:$
|
||||
|
||||
$ is either a literal "$" (user) or a "#" (root)
|
||||
"""
|
||||
|
||||
def __init__(self, settings):
|
||||
Part.__init__(self)
|
||||
self.fragments.append("\n")
|
||||
|
||||
if settings.return_value:
|
||||
self.fragments.append("%d" %(settings.return_value))
|
||||
self.fragments.append(":")
|
||||
|
||||
self.fragments.append(style_color(COLOR_PROMPT_ERROR if settings.return_value else COLOR_PROMPT_OK))
|
||||
self.fragments.append("#" if settings.is_root else "$")
|
||||
self.fragments.append(style_reset())
|
||||
self.fragments.append(" ")
|
||||
|
||||
if __name__ == "__main__":
|
||||
settings = Settings(sys.argv)
|
||||
sysload = Sysload()
|
||||
|
||||
lp = LoginPart(settings, sysload)
|
||||
pp = PathPart(settings.term_width)
|
||||
gp = GitPart()
|
||||
pad = Padding(term_width)
|
||||
sp = StatsPart(red_thresh, yellow_thresh)
|
||||
pad = Padding(settings.term_width)
|
||||
sp = StatsPart(settings, sysload)
|
||||
|
||||
line = [lp, pp, gp, pad, sp]
|
||||
pp.truncate_fit(line)
|
||||
pad.expand_fit(line)
|
||||
top_line = [lp, pp, gp, pad, sp]
|
||||
prompt = InputPart(settings)
|
||||
|
||||
line_str = ' '.join(str(part) for part in line)
|
||||
sys.stderr.write(line_str)
|
||||
pp.shrink_fit(top_line)
|
||||
pad.expand_fit(top_line)
|
||||
|
||||
top_line_str = " ".join(str(part) for part in top_line)
|
||||
sys.stderr.write(top_line_str)
|
||||
sys.stderr.write(str(prompt))
|
||||
sys.stderr.flush()
|
||||
|
|
59
zsh-init
59
zsh-init
|
@ -1,20 +1,11 @@
|
|||
#! /bin/zsh
|
||||
|
||||
autoload -U colors && colors
|
||||
|
||||
OVER_PROMPT_CFG="/etc/over/prompt.cfg"
|
||||
|
||||
if [[ -a "$OVER_PROMPT_CFG" ]]; then
|
||||
source "$OVER_PROMPT_CFG"
|
||||
fi
|
||||
|
||||
function strlen {
|
||||
local PLAIN
|
||||
PLAIN="$(echo $1 | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")"
|
||||
|
||||
echo ${#PLAIN}
|
||||
}
|
||||
|
||||
function set_title {
|
||||
if [[ ${TERM} == "screen-bce" || ${TERM} == "screen" ]]; then
|
||||
print -Pn "\033k\033${@}\033\134"
|
||||
|
@ -29,55 +20,11 @@ function preexec {
|
|||
}
|
||||
|
||||
function precmd {
|
||||
local CUT OVER_OPTS RAW_DATA LOGIN_PART STATS_PART DATA TOP_LEFT TOP_RIGHT PADDING PADDING_SIZE GIT_BRANCH COLOR
|
||||
set -A OVER_OPTS ${(s. .)OVER_PROMPT_OPTS}
|
||||
|
||||
PS1="$(print "%(?.%{\e[1;36m%}.%{\e[1;31m%}%?%{\e[0m%}:%{\e[1;31m%})%(\!.#.$)%{\e[0m%} ")"
|
||||
|
||||
RAW_DATA="$(/usr/share/over-prompt/data $OVER_OPTS[1] $OVER_OPTS[2] $OVER_OPTS[3])"
|
||||
|
||||
GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD 2> /dev/null)"
|
||||
|
||||
if [[ -n "$GIT_BRANCH" ]]; then
|
||||
# rebuild index
|
||||
git update-index -q --ignore-submodules --refresh
|
||||
|
||||
# so that I can check if there are unstaged changes
|
||||
git diff-files --quiet --ignore-submodules
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
COLOR="%{\e[1;32m%}"
|
||||
else
|
||||
COLOR="%{\e[1;31m%}"
|
||||
fi
|
||||
|
||||
RPS1="$(print "$COLOR$GIT_BRANCH%{\e[0m%}")"
|
||||
else
|
||||
unset RPS1
|
||||
fi
|
||||
|
||||
if [[ -n "$RAW_DATA" ]]; then
|
||||
set -A DATA ${(s.:::.)RAW_DATA}
|
||||
LOGIN_PART=${DATA[1]}
|
||||
STATS_PART=${DATA[2]}
|
||||
|
||||
TOP_LEFT=$(print -P "%(\!.\e[1;31m.\e[1;32m)%n\e[0m@$LOGIN_PART")
|
||||
TOP_RIGHT=$(print -P "[ \e[1;36m%T\e[0m | $STATS_PART")
|
||||
PADDING_SIZE=$(($COLUMNS - $(strlen "$TOP_LEFT") - $(strlen "$TOP_RIGHT")))
|
||||
|
||||
# if [[ $PADDING_SIZE -lt 0 ]]; then
|
||||
# CUT=$((0 - $PADDING_SIZE))
|
||||
# TOP_LEFT="${TOP_LEFT[$CUT,-1]}"
|
||||
# fi
|
||||
|
||||
PADDING=$(printf " "%.0s {1..$PADDING_SIZE})
|
||||
|
||||
print "$TOP_LEFT$PADDING$TOP_RIGHT"
|
||||
else
|
||||
print -P "\e[5;31m!!! unable to run /usr/share/over-prompt/data\e[0m"
|
||||
fi
|
||||
python3 ./render.py $? $COLUMNS 1.25 2.5 0.15 0.05
|
||||
|
||||
set_title "${PWD}"
|
||||
}
|
||||
|
||||
unset OVER_PROMPT_CFG
|
||||
unset PS1
|
||||
unset RPS1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue