diff --git a/.aliases.ps1 b/.aliases.ps1 old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 diff --git a/.gitmodules b/.gitmodules old mode 100755 new mode 100644 diff --git a/.poshrc.ps1 b/.poshrc.ps1 old mode 100755 new mode 100644 diff --git a/.psrc.ps1 b/.psrc.ps1 old mode 100755 new mode 100644 diff --git a/.tmux/.tmux-powerlinerc b/.tmux/.tmux-powerlinerc old mode 100755 new mode 100644 diff --git a/htop/htoprc b/htop/htoprc old mode 100755 new mode 100644 diff --git a/micro/buffers/history b/micro/buffers/history old mode 100755 new mode 100644 diff --git a/micro/settings.json b/micro/settings.json old mode 100755 new mode 100644 diff --git a/neofetch/config.conf b/neofetch/config.conf old mode 100755 new mode 100644 diff --git a/powerline-bin/bin/powerline b/powerline-bin/bin/powerline new file mode 100644 index 0000000..2c519be Binary files /dev/null and b/powerline-bin/bin/powerline differ diff --git a/powerline-bin/bin/powerline-config b/powerline-bin/bin/powerline-config new file mode 100644 index 0000000..b37517f --- /dev/null +++ b/powerline-bin/bin/powerline-config @@ -0,0 +1,22 @@ +#!/opt/python/bin/python3 +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + from powerline.commands.config import get_argparser +except ImportError: + import sys + import os + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) + from powerline.commands.config import get_argparser + +import powerline.bindings.config as config + + +if __name__ == '__main__': + parser = get_argparser() + args = parser.parse_args() + + pl = config.create_powerline_logger(args) + + args.function(pl, args) diff --git a/powerline-bin/bin/powerline-daemon b/powerline-bin/bin/powerline-daemon new file mode 100644 index 0000000..620b671 --- /dev/null +++ b/powerline-bin/bin/powerline-daemon @@ -0,0 +1,495 @@ +#!/opt/python/bin/python3 +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import socket +import os +import errno +import sys +import fcntl +import atexit +import stat + +from argparse import ArgumentParser +from select import select +from signal import signal, SIGTERM +from time import sleep +from functools import partial +from io import BytesIO +from threading import Event +from itertools import chain +from logging import StreamHandler + +from powerline.shell import ShellPowerline +from powerline.commands.main import finish_args, write_output +from powerline.lib.monotonic import monotonic +from powerline.lib.encoding import get_preferred_output_encoding, get_preferred_arguments_encoding, get_unicode_writer +from powerline.bindings.wm import wm_threads + +from powerline.commands.main import get_argparser as get_main_argparser +from powerline.commands.daemon import get_argparser as get_daemon_argparser + + +USE_FILESYSTEM = not sys.platform.lower().startswith('linux') + + +class NonInteractiveArgParser(ArgumentParser): + def print_usage(self, file=None): + raise Exception(self.format_usage()) + + def print_help(self, file=None): + raise Exception(self.format_help()) + + def exit(self, status=0, message=None): + pass + + def error(self, message): + raise Exception(self.format_usage()) + + +EOF = b'EOF\0\0' + + +class State(object): + __slots__ = ('powerlines', 'logger', 'config_loader', 'started_wm_threads', + 'ts_shutdown_event') + + def __init__(self, **kwargs): + self.logger = None + self.config_loader = None + self.started_wm_threads = {} + self.powerlines = {} + self.ts_shutdown_event = Event() + + +HOME = os.path.expanduser('~') + + +class NonDaemonShellPowerline(ShellPowerline): + def get_log_handler(self): + return StreamHandler() + + +def start_wm(args, environ, cwd, is_daemon, state): + wm_name = args.ext[0][3:] + if wm_name in state.started_wm_threads: + return b'' + thread_shutdown_event = Event() + thread = wm_threads[wm_name]( + thread_shutdown_event=thread_shutdown_event, + pl_shutdown_event=state.ts_shutdown_event, + pl_config_loader=state.config_loader, + ) + thread.start() + state.started_wm_threads[wm_name] = (thread, thread_shutdown_event) + return b'' + + +def render(args, environ, cwd, is_daemon, state): + segment_info = { + 'getcwd': lambda: cwd, + 'home': environ.get('HOME', HOME), + 'environ': environ, + 'args': args, + } + key = ( + args.ext[0], + args.renderer_module, + tuple(args.config_override) if args.config_override else None, + tuple(args.theme_override) if args.theme_override else None, + tuple(args.config_path) if args.config_path else None, + environ.get('POWERLINE_THEME_OVERRIDES', ''), + environ.get('POWERLINE_CONFIG_OVERRIDES', ''), + environ.get('POWERLINE_CONFIG_PATHS', ''), + ) + + PowerlineClass = ShellPowerline if is_daemon else NonDaemonShellPowerline + powerline = None + try: + powerline = state.powerlines[key] + except KeyError: + try: + powerline = state.powerlines[key] = PowerlineClass( + args, + logger=state.logger, + config_loader=state.config_loader, + run_once=False, + shutdown_event=state.ts_shutdown_event, + ) + if state.logger is None: + state.logger = powerline.logger + if state.config_loader is None: + state.config_loader = powerline.config_loader + except SystemExit: + # Somebody thought raising system exit was a good idea, + return '' + except Exception as e: + if powerline: + powerline.pl.exception('Failed to render {0}: {1}', str(key), str(e)) + else: + return 'Failed to render {0}: {1}'.format(str(key), str(e)) + s = BytesIO() + write_output(args, powerline, segment_info, get_unicode_writer(stream=s)) + s.seek(0) + return s.read() + + +def eintr_retry_call(func, *args, **kwargs): + while True: + try: + return func(*args, **kwargs) + except EnvironmentError as e: + if getattr(e, 'errno', None) == errno.EINTR: + continue + raise + + +def do_read(conn, timeout=2.0): + ''' Read data from the client. If the client fails to send data within + timeout seconds, abort. ''' + read = [] + end_time = monotonic() + timeout + while not read or not read[-1].endswith(b'\0\0'): + r, w, e = select((conn,), (), (conn,), timeout) + if e: + return + if monotonic() > end_time: + return + if not r: + continue + x = eintr_retry_call(conn.recv, 4096) + if x: + read.append(x) + else: + break + return b''.join(read) + + +def do_write(conn, result): + try: + eintr_retry_call(conn.sendall, result) + except Exception: + pass + + +def safe_bytes(o, encoding=get_preferred_output_encoding()): + '''Return bytes instance without ever throwing an exception.''' + try: + try: + # We are assuming that o is a unicode object + return o.encode(encoding, 'replace') + except Exception: + # Object may have defined __bytes__ (python 3) or __str__ method + # (python 2) + # This also catches problem with non_ascii_bytes.encode('utf-8') + # that first tries to decode to UTF-8 using ascii codec (and fails + # in this case) and then encode to given encoding: errors= argument + # is not used in the first stage. + return bytes(o) + except Exception as e: + return safe_bytes(str(e), encoding) + + +def parse_args(req, parser, encoding=get_preferred_arguments_encoding()): + args = [x.decode(encoding) for x in req.split(b'\0') if x] + numargs = int(args[0], 16) + shell_args = parser.parse_args(args[1:numargs + 1]) + cwd = args[numargs + 1] + environ = dict(((k, v) for k, v in (x.partition('=')[0::2] for x in args[numargs + 2:]))) + cwd = cwd or environ.get('PWD', '/') + return shell_args, environ, cwd + + +def get_answer(req, is_daemon, argparser, state): + try: + args, environ, cwd = parse_args(req, argparser) + finish_args(argparser, environ, args, is_daemon=True) + if args.ext[0].startswith('wm.'): + return safe_bytes(start_wm(args, environ, cwd, is_daemon, state)) + else: + return safe_bytes(render(args, environ, cwd, is_daemon, state)) + except Exception as e: + return safe_bytes(str(e)) + + +def do_one(sock, read_sockets, write_sockets, result_map, is_daemon, argparser, + state): + r, w, e = select( + tuple(read_sockets) + (sock,), + tuple(write_sockets), + tuple(read_sockets) + tuple(write_sockets) + (sock,), + 60.0 + ) + + if sock in e: + # We cannot accept any more connections, so we exit + raise SystemExit(1) + + for s in e: + # Discard all broken connections to clients + s.close() + read_sockets.discard(s) + write_sockets.discard(s) + + for s in r: + if s == sock: + # A client wants to connect + conn, _ = eintr_retry_call(sock.accept) + read_sockets.add(conn) + else: + # A client has sent some data + read_sockets.discard(s) + req = do_read(s) + if req == EOF: + raise SystemExit(0) + elif req: + ans = get_answer(req, is_daemon, argparser, state) + result_map[s] = ans + write_sockets.add(s) + else: + s.close() + + for s in w: + # A client is ready to receive the result + write_sockets.discard(s) + result = result_map.pop(s) + try: + do_write(s, result) + finally: + s.close() + + +def shutdown(sock, read_sockets, write_sockets, state): + '''Perform operations necessary for nicely shutting down daemon + + Specifically it + + #. Closes all sockets. + #. Notifies segments based on + :py:class:`powerline.lib.threaded.ThreadedSegment` and WM-specific + threads that daemon is shutting down. + #. Waits for threads to finish, but no more then 2 seconds total. + #. Waits so that total execution time of this function is 2 seconds in order + to allow ThreadedSegments to finish. + ''' + total_wait_time = 2 + shutdown_start_time = monotonic() + + for s in chain((sock,), read_sockets, write_sockets): + s.close() + + # Notify ThreadedSegments + state.ts_shutdown_event.set() + for thread, shutdown_event in state.started_wm_threads.values(): + shutdown_event.set() + + for thread, shutdown_event in state.started_wm_threads.values(): + wait_time = total_wait_time - (monotonic() - shutdown_start_time) + if wait_time > 0: + thread.join(wait_time) + + wait_time = total_wait_time - (monotonic() - shutdown_start_time) + sleep(wait_time) + + +def main_loop(sock, is_daemon): + sock.listen(128) + sock.setblocking(0) + + read_sockets, write_sockets = set(), set() + result_map = {} + parser = get_main_argparser(NonInteractiveArgParser) + state = State() + try: + try: + while True: + do_one( + sock, read_sockets, write_sockets, result_map, + is_daemon=is_daemon, + argparser=parser, + state=state, + ) + except KeyboardInterrupt: + raise SystemExit(0) + except SystemExit as e: + shutdown(sock, read_sockets, write_sockets, state) + raise e + return 0 + + +def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull): + try: + pid = os.fork() + if pid > 0: + # exit first parent + raise SystemExit(0) + except OSError as e: + sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + raise SystemExit(1) + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + raise SystemExit(0) + except OSError as e: + sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + raise SystemExit(1) + + # Redirect standard file descriptors. + si = open(stdin, 'rb') + so = open(stdout, 'a+b') + se = open(stderr, 'a+b', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + return True + + +def check_existing(address): + if USE_FILESYSTEM: + # We cannot bind if the socket file already exists so remove it, we + # already have a lock on pidfile, so this should be safe. + try: + os.unlink(address) + except EnvironmentError: + pass + + sock = socket.socket(family=socket.AF_UNIX) + try: + sock.bind(address) + except socket.error as e: + if getattr(e, 'errno', None) == errno.EADDRINUSE: + return None + raise + return sock + + +def kill_daemon(address): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + try: + eintr_retry_call(sock.connect, address) + except socket.error: + return False + else: + eintr_retry_call(sock.sendall, EOF) + finally: + sock.close() + return True + + +def cleanup_lockfile(pidfile, fd, *args): + try: + # Remove the directory entry for the lock file + os.unlink(pidfile) + # Close the file descriptor + os.close(fd) + except EnvironmentError: + pass + if args: + # Called in signal handler + raise SystemExit(1) + + +def lockpidfile(pidfile): + fd = os.open( + pidfile, + os.O_WRONLY | os.O_CREAT, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH + ) + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except EnvironmentError: + os.close(fd) + return None + os.lseek(fd, 0, os.SEEK_SET) + os.ftruncate(fd, 0) + os.write(fd, ('%d' % os.getpid()).encode('ascii')) + os.fsync(fd) + cleanup = partial(cleanup_lockfile, pidfile, fd) + signal(SIGTERM, cleanup) + atexit.register(cleanup) + return fd + + +def main(): + parser = get_daemon_argparser() + args = parser.parse_args() + is_daemon = False + address = None + pidfile = None + + if args.socket: + address = args.socket + if not USE_FILESYSTEM: + address = '\0' + address + else: + if USE_FILESYSTEM: + address = '/tmp/powerline-ipc-%d' + else: + # Use the abstract namespace for sockets rather than the filesystem + # (Available only in linux) + address = '\0powerline-ipc-%d' + + address = address % os.getuid() + + if USE_FILESYSTEM: + pidfile = address + '.pid' + + if args.kill: + if args.foreground or args.replace: + parser.error('--kill and --foreground/--replace cannot be used together') + if kill_daemon(address): + if not args.quiet: + print ('Kill command sent to daemon, if it does not die in a couple of seconds use kill to kill it') + raise SystemExit(0) + else: + if not args.quiet: + print ('No running daemon found') + raise SystemExit(1) + + if args.replace: + while kill_daemon(address): + if not args.quiet: + print ('Kill command sent to daemon, waiting for daemon to exit, press Ctrl-C to terminate wait and exit') + sleep(2) + + if USE_FILESYSTEM and not args.foreground: + # We must daemonize before creating the locked pidfile, unfortunately, + # this means further print statements are discarded + is_daemon = daemonize() + + if USE_FILESYSTEM: + # Create a locked pid file containing the daemon’s PID + if lockpidfile(pidfile) is None: + if not args.quiet: + sys.stderr.write( + 'The daemon is already running. Use %s -k to kill it.\n' % ( + os.path.basename(sys.argv[0]))) + raise SystemExit(1) + + # Bind to address or bail if we cannot bind + sock = check_existing(address) + if sock is None: + if not args.quiet: + sys.stderr.write( + 'The daemon is already running. Use %s -k to kill it.\n' % ( + os.path.basename(sys.argv[0]))) + raise SystemExit(1) + + if not USE_FILESYSTEM and not args.foreground: + # We daemonize on linux + is_daemon = daemonize() + + return main_loop(sock, is_daemon) + + +if __name__ == '__main__': + main() diff --git a/powerline-bin/bin/powerline-lint b/powerline-bin/bin/powerline-lint new file mode 100644 index 0000000..b41bf08 --- /dev/null +++ b/powerline-bin/bin/powerline-lint @@ -0,0 +1,13 @@ +#!/opt/python/bin/python3 +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from powerline.lint import check +from powerline.commands.lint import get_argparser + + +if __name__ == '__main__': + args = get_argparser().parse_args() + sys.exit(check(args.config_path, args.debug)) diff --git a/powerline-bin/bin/powerline-render b/powerline-bin/bin/powerline-render new file mode 100644 index 0000000..d154c47 --- /dev/null +++ b/powerline-bin/bin/powerline-render @@ -0,0 +1,31 @@ +#!/opt/python/bin/python3 +# vim:fileencoding=utf-8:noet + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import os + +try: + from powerline.shell import ShellPowerline +except ImportError: + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__))))) + from powerline.shell import ShellPowerline + +from powerline.commands.main import get_argparser, finish_args, write_output +from powerline.lib.encoding import get_unicode_writer + + +if sys.version_info < (3,): + write = sys.stdout.write +else: + write = sys.stdout.buffer.write + + +if __name__ == '__main__': + parser = get_argparser() + args = parser.parse_args() + finish_args(parser, os.environ, args) + powerline = ShellPowerline(args, run_once=True) + segment_info = {'args': args, 'environ': os.environ} + write_output(args, powerline, segment_info, get_unicode_writer()) diff --git a/powerline-bin/powerline/__init__.py b/powerline-bin/powerline/__init__.py new file mode 100644 index 0000000..7c781d9 --- /dev/null +++ b/powerline-bin/powerline/__init__.py @@ -0,0 +1,991 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import sys +import logging + +from threading import Lock, Event + +from powerline.colorscheme import Colorscheme +from powerline.lib.config import ConfigLoader +from powerline.lib.unicode import unicode, safe_unicode, FailedUnicode +from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR +from powerline.lib.dict import mergedicts +from powerline.lib.encoding import get_preferred_output_encoding +from powerline.lib.path import join + + +class NotInterceptedError(BaseException): + pass + + +def _config_loader_condition(path): + if path and os.path.isfile(path): + return path + return None + + +def _find_config_files(search_paths, config_file, config_loader=None, loader_callback=None): + config_file += '.json' + found = False + for path in search_paths: + config_file_path = join(path, config_file) + if os.path.isfile(config_file_path): + yield config_file_path + found = True + elif config_loader: + config_loader.register_missing(_config_loader_condition, loader_callback, config_file_path) + if not found: + raise IOError('Config file not found in search paths ({0}): {1}'.format( + ', '.join(search_paths), + config_file + )) + + +class PowerlineLogger(object): + '''Proxy class for logging.Logger instance + + It emits messages in format ``{ext}:{prefix}:{message}`` where + + ``{ext}`` + is a used powerline extension (e.g. “vim”, “shell”, “ipython”). + ``{prefix}`` + is a local prefix, usually a segment name. + ``{message}`` + is the original message passed to one of the logging methods. + + Each of the methods (``critical``, ``exception``, ``info``, ``error``, + ``warn``, ``debug``) expects to receive message in an ``str.format`` format, + not in printf-like format. + + Log is saved to the location :ref:`specified by user `. + ''' + + def __init__(self, use_daemon_threads, logger, ext): + self.logger = logger + self.ext = ext + self.use_daemon_threads = use_daemon_threads + self.prefix = '' + self.last_msgs = {} + + def _log(self, attr, msg, *args, **kwargs): + prefix = kwargs.get('prefix') or self.prefix + prefix = self.ext + ((':' + prefix) if prefix else '') + msg = safe_unicode(msg) + if args or kwargs: + args = [safe_unicode(s) if isinstance(s, bytes) else s for s in args] + kwargs = dict(( + (k, safe_unicode(v) if isinstance(v, bytes) else v) + for k, v in kwargs.items() + )) + msg = msg.format(*args, **kwargs) + msg = prefix + ':' + msg + key = attr + ':' + prefix + if msg != self.last_msgs.get(key): + getattr(self.logger, attr)(msg) + self.last_msgs[key] = msg + + def critical(self, msg, *args, **kwargs): + self._log('critical', msg, *args, **kwargs) + + def exception(self, msg, *args, **kwargs): + self._log('exception', msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + self._log('info', msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + self._log('error', msg, *args, **kwargs) + + def warn(self, msg, *args, **kwargs): + self._log('warning', msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + self._log('debug', msg, *args, **kwargs) + + +_fallback_logger = None + + +def get_fallback_logger(stream=None): + global _fallback_logger + if _fallback_logger: + return _fallback_logger + + log_format = '%(asctime)s:%(levelname)s:%(message)s' + formatter = logging.Formatter(log_format) + + level = logging.WARNING + handler = logging.StreamHandler(stream) + handler.setLevel(level) + handler.setFormatter(formatter) + + logger = logging.Logger('powerline') + logger.setLevel(level) + logger.addHandler(handler) + _fallback_logger = PowerlineLogger(None, logger, '_fallback_') + return _fallback_logger + + +def _generate_change_callback(lock, key, dictionary): + def on_file_change(path): + with lock: + dictionary[key] = True + return on_file_change + + +def get_config_paths(): + '''Get configuration paths from environment variables. + + Uses $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS according to the XDG specification. + + :return: list of paths + ''' + config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) + config_path = join(config_home, 'powerline') + config_paths = [config_path] + config_dirs = os.environ.get('XDG_CONFIG_DIRS', DEFAULT_SYSTEM_CONFIG_DIR) + if config_dirs is not None: + config_paths[:0] = reversed([join(d, 'powerline') for d in config_dirs.split(':')]) + plugin_path = join(os.path.realpath(os.path.dirname(__file__)), 'config_files') + config_paths.insert(0, plugin_path) + return config_paths + + +def generate_config_finder(get_config_paths=get_config_paths): + '''Generate find_config_files function + + This function will find .json file given its path. + + :param function get_config_paths: + Function that being called with no arguments will return a list of paths + that should be searched for configuration files. + + :return: + Function that being given configuration file name will return full path + to it or raise IOError if it failed to find the file. + ''' + config_paths = get_config_paths() + return lambda *args: _find_config_files(config_paths, *args) + + +def load_config(cfg_path, find_config_files, config_loader, loader_callback=None): + '''Load configuration file and setup watches + + Watches are only set up if loader_callback is not None. + + :param str cfg_path: + Path for configuration file that should be loaded. + :param function find_config_files: + Function that finds configuration file. Check out the description of + the return value of ``generate_config_finder`` function. + :param ConfigLoader config_loader: + Configuration file loader class instance. + :param function loader_callback: + Function that will be called by config_loader when change to + configuration file is detected. + + :return: Configuration file contents. + ''' + found_files = find_config_files(cfg_path, config_loader, loader_callback) + ret = None + for path in found_files: + if loader_callback: + config_loader.register(loader_callback, path) + if ret is None: + ret = config_loader.load(path) + else: + mergedicts(ret, config_loader.load(path)) + return ret + + +def _set_log_handlers(common_config, logger, get_module_attr, stream=None): + '''Set log handlers + + :param dict common_config: + Configuration dictionary used to create handler. + :param logging.Logger logger: + Logger to which handlers will be attached. + :param func get_module_attr: + :py:func:`gen_module_attr_getter` output. + :param file stream: + Stream to use by default for :py:class:`logging.StreamHandler` in place + of :py:attr:`sys.stderr`. May be ``None``. + ''' + log_targets = common_config['log_file'] + num_handlers = 0 + for log_target in log_targets: + if log_target is None: + log_target = ['logging.StreamHandler', []] + elif isinstance(log_target, unicode): + log_target = os.path.expanduser(log_target) + log_dir = os.path.dirname(log_target) + if log_dir and not os.path.isdir(log_dir): + os.mkdir(log_dir) + log_target = ['logging.FileHandler', [[log_target]]] + module, handler_class_name = log_target[0].rpartition('.')[::2] + module = module or 'logging.handlers' + try: + handler_class_args = log_target[1][0] + except IndexError: + if module == 'logging' and handler_class_name == 'StreamHandler': + handler_class_args = [stream] + else: + handler_class_args = () + try: + handler_class_kwargs = log_target[1][1] + except IndexError: + handler_class_kwargs = {} + module = str(module) + handler_class_name = str(handler_class_name) + handler_class = get_module_attr(module, handler_class_name) + if not handler_class: + continue + handler = handler_class(*handler_class_args, **handler_class_kwargs) + try: + handler_level_name = log_target[2] + except IndexError: + handler_level_name = common_config['log_level'] + try: + handler_format = log_target[3] + except IndexError: + handler_format = common_config['log_format'] + handler.setLevel(getattr(logging, handler_level_name)) + handler.setFormatter(logging.Formatter(handler_format)) + logger.addHandler(handler) + num_handlers += 1 + if num_handlers == 0 and log_targets: + raise ValueError('Failed to set up any handlers') + + +def create_logger(common_config, use_daemon_threads=True, ext='__unknown__', + import_paths=None, imported_modules=None, stream=None): + '''Create logger according to provided configuration + + :param dict common_config: + Common configuration, from :py:func:`finish_common_config`. + :param bool use_daemon_threads: + Whether daemon threads should be used. Argument to + :py:class:`PowerlineLogger` constructor. + :param str ext: + Used extension. Argument to :py:class:`PowerlineLogger` constructor. + :param set imported_modules: + Set where imported modules are saved. Argument to + :py:func:`gen_module_attr_getter`. May be ``None``, in this case new + empty set is used. + :param file stream: + Stream to use by default for :py:class:`logging.StreamHandler` in place + of :py:attr:`sys.stderr`. May be ``None``. + + :return: Three objects: + + #. :py:class:`logging.Logger` instance. + #. :py:class:`PowerlineLogger` instance. + #. Function, output of :py:func:`gen_module_attr_getter`. + ''' + logger = logging.Logger('powerline') + level = getattr(logging, common_config['log_level']) + logger.setLevel(level) + + pl = PowerlineLogger(use_daemon_threads, logger, ext) + get_module_attr = gen_module_attr_getter( + pl, common_config['paths'], + set() if imported_modules is None else imported_modules) + + _set_log_handlers(common_config, logger, get_module_attr, stream) + + return logger, pl, get_module_attr + + +def get_default_theme(is_unicode=True): + '''Get default theme used by powerline + + :param bool is_unicode: + If true, return theme for unicode environments, otherwise return theme + that is supposed to be ASCII-only. + + :return: theme name. + ''' + return 'powerline_terminus' if is_unicode else 'ascii' + + +def finish_common_config(encoding, common_config): + '''Add default values to common config and expand ~ in paths + + :param dict common_config: + Common configuration, as it was just loaded. + + :return: + Copy of common configuration with all configuration keys and expanded + paths. + ''' + encoding = encoding.lower() + default_top_theme = get_default_theme( + encoding.startswith('utf') or encoding.startswith('ucs')) + + common_config = common_config.copy() + common_config.setdefault('default_top_theme', default_top_theme) + common_config.setdefault('paths', []) + common_config.setdefault('watcher', 'auto') + common_config.setdefault('log_level', 'WARNING') + common_config.setdefault('log_format', '%(asctime)s:%(levelname)s:%(message)s') + common_config.setdefault('term_truecolor', False) + common_config.setdefault('term_escape_style', 'auto') + common_config.setdefault('ambiwidth', 1) + common_config.setdefault('additional_escapes', None) + common_config.setdefault('reload_config', True) + common_config.setdefault('interval', None) + common_config.setdefault('log_file', [None]) + + if not isinstance(common_config['log_file'], list): + common_config['log_file'] = [common_config['log_file']] + + common_config['paths'] = [ + os.path.expanduser(path) for path in common_config['paths'] + ] + + return common_config + + +if sys.version_info < (3,): + # `raise exception[0], None, exception[1]` is a SyntaxError in python-3* + # Not using ('''…''') because this syntax does not work in python-2.6 + exec(( + 'def reraise(exception):\n' + ' if type(exception) is tuple:\n' + ' raise exception[0], None, exception[1]\n' + ' else:\n' + ' raise exception\n' + )) +else: + def reraise(exception): + if type(exception) is tuple: + raise exception[0].with_traceback(exception[1]) + else: + raise exception + + +def gen_module_attr_getter(pl, import_paths, imported_modules): + def get_module_attr(module, attr, prefix='powerline'): + '''Import module and get its attribute. + + Replaces ``from {module} import {attr}``. + + :param str module: + Module name, will be passed as first argument to ``__import__``. + :param str attr: + Module attribute, will be passed to ``__import__`` as the only value + in ``fromlist`` tuple. + + :return: + Attribute value or ``None``. Note: there is no way to distinguish + between successfull import of attribute equal to ``None`` and + unsuccessfull import. + ''' + oldpath = sys.path + sys.path = import_paths + sys.path + module = str(module) + attr = str(attr) + try: + imported_modules.add(module) + return getattr(__import__(module, fromlist=(attr,)), attr) + except Exception as e: + pl.exception('Failed to import attr {0} from module {1}: {2}', attr, module, str(e), prefix=prefix) + return None + finally: + sys.path = oldpath + + return get_module_attr + + +LOG_KEYS = set(('log_format', 'log_level', 'log_file', 'paths')) +'''List of keys related to logging +''' + + +def _get_log_keys(common_config): + '''Return a common configuration copy with only log-related config left + + :param dict common_config: + Common configuration. + + :return: + :py:class:`dict` instance which has only keys from + :py:attr:`powerline.LOG_KEYS` left. + ''' + return dict(( + (k, v) for k, v in common_config.items() if k in LOG_KEYS + )) + + +DEFAULT_UPDATE_INTERVAL = 2 +'''Default value for :ref:`update_interval ` +''' + + +class Powerline(object): + '''Main powerline class, entrance point for all powerline uses. Sets + powerline up and loads the configuration. + + :param str ext: + extension used. Determines where configuration files will + searched and what renderer module will be used. Affected: used ``ext`` + dictionary from :file:`powerline/config.json`, location of themes and + colorschemes, render module (``powerline.renders.{ext}``). + :param str renderer_module: + Overrides renderer module (defaults to ``ext``). Should be the name of + the package imported like this: ``powerline.renderers.{render_module}``. + If this parameter contains a dot ``powerline.renderers.`` is not + prepended. There is also a special case for renderers defined in + toplevel modules: ``foo.`` (note: dot at the end) tries to get renderer + from module ``foo`` (because ``foo`` (without dot) tries to get renderer + from module ``powerline.renderers.foo``). When ``.foo`` (with leading + dot) variant is used ``renderer_module`` will be + ``powerline.renderers.{ext}{renderer_module}``. + :param bool run_once: + Determines whether :py:meth:`render` method will be run only once + during python session. + :param Logger logger: + If present no new logger will be created and the provided logger will be + used. + :param bool use_daemon_threads: + When creating threads make them daemon ones. + :param Event shutdown_event: + Use this Event as shutdown_event instead of creating new event. + :param ConfigLoader config_loader: + Instance of the class that manages (re)loading of the configuration. + ''' + + def __init__(self, *args, **kwargs): + self.init_args = (args, kwargs) + self.init(*args, **kwargs) + + def init(self, + ext, + renderer_module=None, + run_once=False, + logger=None, + use_daemon_threads=True, + shutdown_event=None, + config_loader=None): + '''Do actual initialization. + + __init__ function only stores the arguments and runs this function. This + function exists for powerline to be able to reload itself: it is easier + to make ``__init__`` store arguments and call overriddable ``init`` than + tell developers that each time they override Powerline.__init__ in + subclasses they must store actual arguments. + ''' + self.ext = ext + self.run_once = run_once + self.logger = logger + self.had_logger = bool(self.logger) + self.use_daemon_threads = use_daemon_threads + + if not renderer_module: + self.renderer_module = 'powerline.renderers.' + ext + elif '.' not in renderer_module: + self.renderer_module = 'powerline.renderers.' + renderer_module + elif renderer_module.startswith('.'): + self.renderer_module = 'powerline.renderers.' + ext + renderer_module + elif renderer_module.endswith('.'): + self.renderer_module = renderer_module[:-1] + else: + self.renderer_module = renderer_module + + self.find_config_files = generate_config_finder(self.get_config_paths) + + self.cr_kwargs_lock = Lock() + self.cr_kwargs = {} + self.cr_callbacks = {} + for key in ('main', 'colors', 'colorscheme', 'theme'): + self.cr_kwargs['load_' + key] = True + self.cr_callbacks[key] = _generate_change_callback( + self.cr_kwargs_lock, + 'load_' + key, + self.cr_kwargs + ) + + self.shutdown_event = shutdown_event or Event() + self.config_loader = config_loader or ConfigLoader(shutdown_event=self.shutdown_event, run_once=run_once) + self.run_loader_update = False + + self.renderer_options = {} + + self.prev_common_config = None + self.prev_ext_config = None + self.pl = None + self.setup_args = () + self.setup_kwargs = {} + self.imported_modules = set() + self.update_interval = DEFAULT_UPDATE_INTERVAL + + get_encoding = staticmethod(get_preferred_output_encoding) + '''Get encoding used by the current application + + Usually returns encoding of the current locale. + ''' + + def create_logger(self): + '''Create logger + + This function is used to create logger unless it was already specified + at initialization. + + :return: Three objects: + + #. :py:class:`logging.Logger` instance. + #. :py:class:`PowerlineLogger` instance. + #. Function, output of :py:func:`gen_module_attr_getter`. + ''' + return create_logger( + common_config=self.common_config, + use_daemon_threads=self.use_daemon_threads, + ext=self.ext, + imported_modules=self.imported_modules, + stream=self.default_log_stream, + ) + + def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False): + '''(Re)create renderer object. Can be used after Powerline object was + successfully initialized. If any of the below parameters except + ``load_main`` is True renderer object will be recreated. + + :param bool load_main: + Determines whether main configuration file (:file:`config.json`) + should be loaded. If appropriate configuration changes implies + ``load_colorscheme`` and ``load_theme`` and recreation of renderer + object. Won’t trigger recreation if only unrelated configuration + changed. + :param bool load_colors: + Determines whether colors configuration from :file:`colors.json` + should be (re)loaded. + :param bool load_colorscheme: + Determines whether colorscheme configuration should be (re)loaded. + :param bool load_theme: + Determines whether theme configuration should be reloaded. + ''' + common_config_differs = False + ext_config_differs = False + if load_main: + self._purge_configs('main') + config = self.load_main_config() + self.common_config = finish_common_config(self.get_encoding(), config['common']) + if self.common_config != self.prev_common_config: + common_config_differs = True + + load_theme = (load_theme + or not self.prev_common_config + or self.prev_common_config['default_top_theme'] != self.common_config['default_top_theme']) + + log_keys_differ = (not self.prev_common_config or ( + _get_log_keys(self.prev_common_config) != _get_log_keys(self.common_config) + )) + + self.prev_common_config = self.common_config + + if log_keys_differ: + if self.had_logger: + self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext) + self.get_module_attr = gen_module_attr_getter( + self.pl, self.common_config['paths'], self.imported_modules) + else: + self.logger, self.pl, self.get_module_attr = self.create_logger() + self.config_loader.pl = self.pl + + if not self.run_once: + self.config_loader.set_watcher(self.common_config['watcher']) + + mergedicts(self.renderer_options, dict( + pl=self.pl, + term_truecolor=self.common_config['term_truecolor'], + term_escape_style=self.common_config['term_escape_style'], + ambiwidth=self.common_config['ambiwidth'], + tmux_escape=self.common_config['additional_escapes'] == 'tmux', + screen_escape=self.common_config['additional_escapes'] == 'screen', + theme_kwargs={ + 'ext': self.ext, + 'common_config': self.common_config, + 'run_once': self.run_once, + 'shutdown_event': self.shutdown_event, + 'get_module_attr': self.get_module_attr, + }, + )) + + if not self.run_once and self.common_config['reload_config']: + interval = self.common_config['interval'] + self.config_loader.set_interval(interval) + self.run_loader_update = (interval is None) + if interval is not None and not self.config_loader.is_alive(): + self.config_loader.start() + + self.ext_config = config['ext'][self.ext] + + top_theme = ( + self.ext_config.get('top_theme') + or self.common_config['default_top_theme'] + ) + self.theme_levels = ( + os.path.join('themes', top_theme), + os.path.join('themes', self.ext, '__main__'), + ) + self.renderer_options['theme_kwargs']['top_theme'] = top_theme + + if self.ext_config != self.prev_ext_config: + ext_config_differs = True + if ( + not self.prev_ext_config + or self.ext_config.get('components') != self.prev_ext_config.get('components') + ): + self.setup_components(self.ext_config.get('components')) + if ( + not self.prev_ext_config + or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes') + ): + self.renderer_options['local_themes'] = self.get_local_themes(self.ext_config.get('local_themes')) + self.update_interval = self.ext_config.get('update_interval', 2) + load_colorscheme = ( + load_colorscheme + or not self.prev_ext_config + or self.prev_ext_config['colorscheme'] != self.ext_config['colorscheme'] + ) + load_theme = ( + load_theme + or not self.prev_ext_config + or self.prev_ext_config['theme'] != self.ext_config['theme'] + ) + self.prev_ext_config = self.ext_config + + create_renderer = load_colors or load_colorscheme or load_theme or common_config_differs or ext_config_differs + + if load_colors: + self._purge_configs('colors') + self.colors_config = self.load_colors_config() + + if load_colorscheme or load_colors: + self._purge_configs('colorscheme') + if load_colorscheme: + self.colorscheme_config = self.load_colorscheme_config(self.ext_config['colorscheme']) + self.renderer_options['theme_kwargs']['colorscheme'] = ( + Colorscheme(self.colorscheme_config, self.colors_config)) + + if load_theme: + self._purge_configs('theme') + self.renderer_options['theme_config'] = self.load_theme_config(self.ext_config.get('theme', 'default')) + + if create_renderer: + Renderer = self.get_module_attr(self.renderer_module, 'renderer') + if not Renderer: + if hasattr(self, 'renderer'): + return + else: + raise ImportError('Failed to obtain renderer') + + # Renderer updates configuration file via segments’ .startup thus it + # should be locked to prevent state when configuration was updated, + # but .render still uses old renderer. + try: + renderer = Renderer(**self.renderer_options) + except Exception as e: + self.exception('Failed to construct renderer object: {0}', str(e)) + if not hasattr(self, 'renderer'): + raise + else: + self.renderer = renderer + + default_log_stream = sys.stdout + '''Default stream for default log handler + + Usually it is ``sys.stderr``, but there is sometimes a reason to prefer + ``sys.stdout`` or a custom file-like object. It is not supposed to be used + to write to some file. + ''' + + def setup_components(self, components): + '''Run component-specific setup + + :param set components: + Set of the enabled componets or None. + + Should be overridden by subclasses. + ''' + pass + + @staticmethod + def get_config_paths(): + '''Get configuration paths. + + Should be overridden in subclasses in order to provide a way to override + used paths. + + :return: list of paths + ''' + return get_config_paths() + + def load_config(self, cfg_path, cfg_type): + '''Load configuration and setup watches + + :param str cfg_path: + Path to the configuration file without any powerline configuration + directory or ``.json`` suffix. + :param str cfg_type: + Configuration type. May be one of ``main`` (for ``config.json`` + file), ``colors``, ``colorscheme``, ``theme``. + + :return: dictionary with loaded configuration. + ''' + return load_config( + cfg_path, + self.find_config_files, + self.config_loader, + self.cr_callbacks[cfg_type] + ) + + def _purge_configs(self, cfg_type): + function = self.cr_callbacks[cfg_type] + self.config_loader.unregister_functions(set((function,))) + self.config_loader.unregister_missing(set(((self.find_config_files, function),))) + + def load_main_config(self): + '''Get top-level configuration. + + :return: dictionary with :ref:`top-level configuration `. + ''' + return self.load_config('config', 'main') + + def _load_hierarhical_config(self, cfg_type, levels, ignore_levels): + '''Load and merge multiple configuration files + + :param str cfg_type: + Type of the loaded configuration files (e.g. ``colorscheme``, + ``theme``). + :param list levels: + Configuration names resembling levels in hierarchy, sorted by + priority. Configuration file names with higher priority should go + last. + :param set ignore_levels: + If only files listed in this variable are present then configuration + file is considered not loaded: at least one file on the level not + listed in this variable must be present. + ''' + config = {} + loaded = 0 + exceptions = [] + for i, cfg_path in enumerate(levels): + try: + lvl_config = self.load_config(cfg_path, cfg_type) + except IOError as e: + if sys.version_info < (3,): + tb = sys.exc_info()[2] + exceptions.append((e, tb)) + else: + exceptions.append(e) + else: + if i not in ignore_levels: + loaded += 1 + mergedicts(config, lvl_config) + if not loaded: + for exception in exceptions: + if type(exception) is tuple: + e = exception[0] + else: + e = exception + self.exception('Failed to load %s: {0}' % cfg_type, e, exception=exception) + raise e + return config + + def load_colorscheme_config(self, name): + '''Get colorscheme. + + :param str name: + Name of the colorscheme to load. + + :return: dictionary with :ref:`colorscheme configuration `. + ''' + levels = ( + os.path.join('colorschemes', name), + os.path.join('colorschemes', self.ext, '__main__'), + os.path.join('colorschemes', self.ext, name), + ) + return self._load_hierarhical_config('colorscheme', levels, (1,)) + + def load_theme_config(self, name): + '''Get theme configuration. + + :param str name: + Name of the theme to load. + + :return: dictionary with :ref:`theme configuration ` + ''' + levels = self.theme_levels + ( + os.path.join('themes', self.ext, name), + ) + return self._load_hierarhical_config('theme', levels, (0, 1,)) + + def load_colors_config(self): + '''Get colorscheme. + + :return: dictionary with :ref:`colors configuration `. + ''' + return self.load_config('colors', 'colors') + + @staticmethod + def get_local_themes(local_themes): + '''Get local themes. No-op here, to be overridden in subclasses if + required. + + :param dict local_themes: + Usually accepts ``{matcher_name : theme_name}``. May also receive + None in case there is no local_themes configuration. + + :return: + anything accepted by ``self.renderer.get_theme`` and processable by + ``self.renderer.add_local_theme``. Renderer module is determined by + ``__init__`` arguments, refer to its documentation. + ''' + return None + + def update_renderer(self): + '''Updates/creates a renderer if needed.''' + if self.run_loader_update: + self.config_loader.update() + cr_kwargs = None + with self.cr_kwargs_lock: + if self.cr_kwargs: + cr_kwargs = self.cr_kwargs.copy() + if cr_kwargs: + try: + self.create_renderer(**cr_kwargs) + except Exception as e: + self.exception('Failed to create renderer: {0}', str(e)) + if hasattr(self, 'renderer'): + with self.cr_kwargs_lock: + self.cr_kwargs.clear() + else: + raise + else: + with self.cr_kwargs_lock: + self.cr_kwargs.clear() + + def render(self, *args, **kwargs): + '''Update/create renderer if needed and pass all arguments further to + ``self.renderer.render()``. + ''' + try: + self.update_renderer() + return self.renderer.render(*args, **kwargs) + except Exception as e: + exc = e + try: + self.exception('Failed to render: {0}', str(e)) + except Exception as e: + exc = e + ret = FailedUnicode(safe_unicode(exc)) + if kwargs.get('output_width', False): + ret = ret, len(ret) + return ret + + def render_above_lines(self, *args, **kwargs): + '''Like .render(), but for ``self.renderer.render_above_lines()`` + ''' + try: + self.update_renderer() + for line in self.renderer.render_above_lines(*args, **kwargs): + yield line + except Exception as e: + exc = e + try: + self.exception('Failed to render: {0}', str(e)) + except Exception as e: + exc = e + yield FailedUnicode(safe_unicode(exc)) + + def setup(self, *args, **kwargs): + '''Setup the environment to use powerline. + + Must not be overridden by subclasses. This one only saves setup + arguments for :py:meth:`reload` method and calls :py:meth:`do_setup`. + ''' + self.shutdown_event.clear() + self.setup_args = args + self.setup_kwargs.update(kwargs) + self.do_setup(*args, **kwargs) + + @staticmethod + def do_setup(): + '''Function that does initialization + + Should be overridden by subclasses. May accept any number of regular or + keyword arguments. + ''' + pass + + def reload(self): + '''Reload powerline after update. + + Should handle most (but not all) powerline updates. + + Purges out all powerline modules and modules imported by powerline for + segment and matcher functions. Requires defining ``setup`` function that + updates reference to main powerline object. + + .. warning:: + Not guaranteed to work properly, use it at your own risk. It + may break your python code. + ''' + import sys + modules = self.imported_modules | set((module for module in sys.modules if module.startswith('powerline'))) + modules_holder = [] + for module in modules: + try: + # Needs to hold module to prevent garbage collecting until they + # are all reloaded. + modules_holder.append(sys.modules.pop(module)) + except KeyError: + pass + PowerlineClass = getattr(__import__(self.__module__, fromlist=(self.__class__.__name__,)), self.__class__.__name__) + self.shutdown(set_event=True) + init_args, init_kwargs = self.init_args + powerline = PowerlineClass(*init_args, **init_kwargs) + powerline.setup(*self.setup_args, **self.setup_kwargs) + + def shutdown(self, set_event=True): + '''Shut down all background threads. + + :param bool set_event: + Set ``shutdown_event`` and call ``renderer.shutdown`` which should + shut down all threads. Set it to False unless you are exiting an + application. + + If set to False this does nothing more then resolving reference + cycle ``powerline → config_loader → bound methods → powerline`` by + unsubscribing from config_loader events. + ''' + if set_event: + self.shutdown_event.set() + try: + self.renderer.shutdown() + except AttributeError: + pass + functions = tuple(self.cr_callbacks.values()) + self.config_loader.unregister_functions(set(functions)) + self.config_loader.unregister_missing(set(((self.find_config_files, function) for function in functions))) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.shutdown() + + def exception(self, msg, *args, **kwargs): + if 'prefix' not in kwargs: + kwargs['prefix'] = 'powerline' + exception = kwargs.pop('exception', None) + pl = getattr(self, 'pl', None) or get_fallback_logger(self.default_log_stream) + if exception: + try: + reraise(exception) + except Exception: + return pl.exception(msg, *args, **kwargs) + return pl.exception(msg, *args, **kwargs) diff --git a/powerline-bin/powerline/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..bb3b274 Binary files /dev/null and b/powerline-bin/powerline/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/colorscheme.cpython-312.pyc b/powerline-bin/powerline/__pycache__/colorscheme.cpython-312.pyc new file mode 100644 index 0000000..0509c37 Binary files /dev/null and b/powerline-bin/powerline/__pycache__/colorscheme.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/config.cpython-312.pyc b/powerline-bin/powerline/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..cc33b55 Binary files /dev/null and b/powerline-bin/powerline/__pycache__/config.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/ipython.cpython-312.pyc b/powerline-bin/powerline/__pycache__/ipython.cpython-312.pyc new file mode 100644 index 0000000..02994dd Binary files /dev/null and b/powerline-bin/powerline/__pycache__/ipython.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/lemonbar.cpython-312.pyc b/powerline-bin/powerline/__pycache__/lemonbar.cpython-312.pyc new file mode 100644 index 0000000..78782af Binary files /dev/null and b/powerline-bin/powerline/__pycache__/lemonbar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/pdb.cpython-312.pyc b/powerline-bin/powerline/__pycache__/pdb.cpython-312.pyc new file mode 100644 index 0000000..58971d1 Binary files /dev/null and b/powerline-bin/powerline/__pycache__/pdb.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/renderer.cpython-312.pyc b/powerline-bin/powerline/__pycache__/renderer.cpython-312.pyc new file mode 100644 index 0000000..f53a04b Binary files /dev/null and b/powerline-bin/powerline/__pycache__/renderer.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/segment.cpython-312.pyc b/powerline-bin/powerline/__pycache__/segment.cpython-312.pyc new file mode 100644 index 0000000..237b166 Binary files /dev/null and b/powerline-bin/powerline/__pycache__/segment.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/shell.cpython-312.pyc b/powerline-bin/powerline/__pycache__/shell.cpython-312.pyc new file mode 100644 index 0000000..90ec20e Binary files /dev/null and b/powerline-bin/powerline/__pycache__/shell.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/theme.cpython-312.pyc b/powerline-bin/powerline/__pycache__/theme.cpython-312.pyc new file mode 100644 index 0000000..605379b Binary files /dev/null and b/powerline-bin/powerline/__pycache__/theme.cpython-312.pyc differ diff --git a/powerline-bin/powerline/__pycache__/vim.cpython-312.pyc b/powerline-bin/powerline/__pycache__/vim.cpython-312.pyc new file mode 100644 index 0000000..070fff2 Binary files /dev/null and b/powerline-bin/powerline/__pycache__/vim.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/__init__.py b/powerline-bin/powerline/bindings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/bindings/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..925c44c Binary files /dev/null and b/powerline-bin/powerline/bindings/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/__pycache__/config.cpython-312.pyc b/powerline-bin/powerline/bindings/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..b01f32d Binary files /dev/null and b/powerline-bin/powerline/bindings/__pycache__/config.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/awesome/__pycache__/powerline-awesome.cpython-312.pyc b/powerline-bin/powerline/bindings/awesome/__pycache__/powerline-awesome.cpython-312.pyc new file mode 100644 index 0000000..b1a62dc Binary files /dev/null and b/powerline-bin/powerline/bindings/awesome/__pycache__/powerline-awesome.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/awesome/powerline-awesome.py b/powerline-bin/powerline/bindings/awesome/powerline-awesome.py new file mode 100644 index 0000000..500d47d --- /dev/null +++ b/powerline-bin/powerline/bindings/awesome/powerline-awesome.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from powerline.bindings.wm import DEFAULT_UPDATE_INTERVAL +from powerline.bindings.wm.awesome import run + + +def main(): + try: + interval = float(sys.argv[1]) + except IndexError: + interval = DEFAULT_UPDATE_INTERVAL + run(interval=interval) + + +if __name__ == '__main__': + main() diff --git a/powerline-bin/powerline/bindings/awesome/powerline.lua b/powerline-bin/powerline/bindings/awesome/powerline.lua new file mode 100644 index 0000000..470901f --- /dev/null +++ b/powerline-bin/powerline/bindings/awesome/powerline.lua @@ -0,0 +1,15 @@ +local wibox = require('wibox') +local awful = require('awful') + +powerline_widget = wibox.widget.textbox() +powerline_widget:set_align('right') + +function powerline(mode, widget) end + +if string.find(awesome.version, 'v4') then + awful.spawn.with_shell('powerline-daemon -q') + awful.spawn.with_shell('powerline wm.awesome') +else + awful.util.spawn_with_shell('powerline-daemon -q') + awful.util.spawn_with_shell('powerline wm.awesome') +end diff --git a/powerline-bin/powerline/bindings/bar/__pycache__/powerline-bar.cpython-312.pyc b/powerline-bin/powerline/bindings/bar/__pycache__/powerline-bar.cpython-312.pyc new file mode 100644 index 0000000..26b7632 Binary files /dev/null and b/powerline-bin/powerline/bindings/bar/__pycache__/powerline-bar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/bar/powerline-bar.py b/powerline-bin/powerline/bindings/bar/powerline-bar.py new file mode 100644 index 0000000..71e8ae3 --- /dev/null +++ b/powerline-bin/powerline/bindings/bar/powerline-bar.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import sys +import time + +from threading import Lock, Timer +from argparse import ArgumentParser + +from powerline.lemonbar import LemonbarPowerline +from powerline.lib.encoding import get_unicode_writer +from powerline.bindings.wm import DEFAULT_UPDATE_INTERVAL + + +if __name__ == '__main__': + parser = ArgumentParser(description='Powerline lemonbar bindings.') + parser.add_argument( + '--i3', action='store_true', + help='Subscribe for i3 events.' + ) + args = parser.parse_args() + powerline = LemonbarPowerline() + powerline.update_renderer() + powerline.pl.warn("The 'bar' bindings are deprecated, please switch to 'lemonbar'") + lock = Lock() + modes = ['default'] + write = get_unicode_writer(encoding='utf-8') + + def render(reschedule=False): + if reschedule: + Timer(DEFAULT_UPDATE_INTERVAL, render, kwargs={'reschedule': True}).start() + + global lock + with lock: + write(powerline.render(mode=modes[0])) + write('\n') + sys.stdout.flush() + + def update(evt): + modes[0] = evt.change + render() + + render(reschedule=True) + + if args.i3: + try: + import i3ipc + except ImportError: + import i3 + i3.Subscription(lambda evt, data, sub: print(render()), 'workspace') + else: + conn = i3ipc.Connection() + conn.on('workspace::focus', lambda conn, evt: render()) + conn.on('mode', lambda conn, evt: update(evt)) + conn.main() + + while True: + time.sleep(1e8) diff --git a/powerline-bin/powerline/bindings/bash/powerline.sh b/powerline-bin/powerline/bindings/bash/powerline.sh new file mode 100644 index 0000000..f4e933e --- /dev/null +++ b/powerline-bin/powerline/bindings/bash/powerline.sh @@ -0,0 +1,153 @@ +_powerline_columns_fallback() { + if which stty &>/dev/null ; then + local cols="$(stty size 2>/dev/null)" + if ! test -z "$cols" ; then + echo "${cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + +_powerline_tmux_pane() { + echo "${TMUX_PANE:-`TMUX="$_POWERLINE_TMUX" tmux display -p "#D"`}" | \ + tr -d ' %' +} + +_powerline_tmux_setenv() { + TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`_powerline_tmux_pane` "$2" + TMUX="$_POWERLINE_TMUX" tmux refresh -S +} + +_powerline_tmux_set_pwd() { + if test "$_POWERLINE_SAVED_PWD" != "$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi +} + +_powerline_return() { + return $1 +} + +_POWERLINE_HAS_PIPESTATUS="$( + _powerline_return 0 | _powerline_return 43 + test "${PIPESTATUS[*]}" = "0 43" + echo "$?" +)" + +_powerline_has_pipestatus() { + return $_POWERLINE_HAS_PIPESTATUS +} + +_powerline_status_wrapper() { + local last_exit_code=$? last_pipe_status=( "${PIPESTATUS[@]}" ) + + if ! _powerline_has_pipestatus \ + || test "${#last_pipe_status[@]}" -eq "0" \ + || test "$last_exit_code" != "${last_pipe_status[$(( ${#last_pipe_status[@]} - 1 ))]}" ; then + last_pipe_status=() + fi + "$@" $last_exit_code "${last_pipe_status[*]}" + return $last_exit_code +} + +_powerline_add_status_wrapped_command() { + local action="$1" ; shift + local cmd="$1" ; shift + full_cmd="_powerline_status_wrapper $cmd" + if test "$action" = "append" ; then + PROMPT_COMMAND="$PROMPT_COMMAND"$'\n'"$full_cmd" + else + PROMPT_COMMAND="$full_cmd"$'\n'"$PROMPT_COMMAND" + fi +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "${COLUMNS:-`_powerline_columns_fallback`}" +} + +_powerline_init_tmux_support() { + if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then + # TMUX variable may be unset to create new tmux session inside this one + _POWERLINE_TMUX="$TMUX" + + trap '_powerline_tmux_set_columns' WINCH + _powerline_tmux_set_columns + + test "$PROMPT_COMMAND" != "${PROMPT_COMMAND/_powerline_tmux_set_pwd}" \ + || _powerline_add_status_wrapped_command append _powerline_tmux_set_pwd + fi +} + +_powerline_local_prompt() { + # Arguments: + # 1: side + # 2: renderer_module arg + # 3: last_exit_code + # 4: last_pipe_status + # 5: jobnum + # 6: local theme + "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS shell $1 \ + $2 \ + --last-exit-code=$3 \ + --last-pipe-status="$4" \ + --jobnum=$5 \ + --renderer-arg="client_id=$$" \ + --renderer-arg="local_theme=$6" +} + +_powerline_prompt() { + # Arguments: + # 1: side + # 2: last_exit_code + # 3: last_pipe_status + # 4: jobnum + "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS shell $1 \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ + -r.bash \ + --last-exit-code=$2 \ + --last-pipe-status="$3" \ + --jobnum=$4 \ + --renderer-arg="client_id=$$" +} + +_powerline_set_prompt() { + local last_exit_code=$1 ; shift + local last_pipe_status=$1 ; shift + local jobnum="$(jobs -p|wc -l)" + PS1="$(_powerline_prompt aboveleft $last_exit_code "$last_pipe_status" $jobnum)" + if test -n "$POWERLINE_SHELL_CONTINUATION$POWERLINE_BASH_CONTINUATION" ; then + PS2="$(_powerline_local_prompt left -r.bash $last_exit_code "$last_pipe_status" $jobnum continuation)" + fi + if test -n "$POWERLINE_SHELL_SELECT$POWERLINE_BASH_SELECT" ; then + PS3="$(_powerline_local_prompt left '' $last_exit_code "$last_pipe_status" $jobnum select)" + fi +} + +_powerline_setup_prompt() { + VIRTUAL_ENV_DISABLE_PROMPT=1 + if test -z "${POWERLINE_COMMAND}" ; then + POWERLINE_COMMAND="$("$POWERLINE_CONFIG_COMMAND" shell command)" + fi + test "$PROMPT_COMMAND" != "${PROMPT_COMMAND%_powerline_set_prompt*}" \ + || _powerline_add_status_wrapped_command prepend _powerline_set_prompt + PS2="$(_powerline_local_prompt left -r.bash 0 0 0 continuation)" + PS3="$(_powerline_local_prompt left '' 0 0 0 select)" +} + +if test -z "${POWERLINE_CONFIG_COMMAND}" ; then + if which powerline-config >/dev/null ; then + POWERLINE_CONFIG_COMMAND=powerline-config + else + POWERLINE_CONFIG_COMMAND="$(dirname "$BASH_SOURCE")/../../../scripts/powerline-config" + fi +fi + +if "${POWERLINE_CONFIG_COMMAND}" shell --shell=bash uses prompt ; then + _powerline_setup_prompt +fi +if "${POWERLINE_CONFIG_COMMAND}" shell --shell=bash uses tmux ; then + _powerline_init_tmux_support +fi diff --git a/powerline-bin/powerline/bindings/config.py b/powerline-bin/powerline/bindings/config.py new file mode 100644 index 0000000..3100633 --- /dev/null +++ b/powerline-bin/powerline/bindings/config.py @@ -0,0 +1,286 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import re +import sys +import subprocess +import shlex + +from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY +from powerline.lib.config import ConfigLoader +from powerline import generate_config_finder, load_config, create_logger, finish_common_config +from powerline.shell import ShellPowerline +from powerline.lib.shell import which +from powerline.bindings.tmux import (TmuxVersionInfo, run_tmux_command, set_tmux_environment, get_tmux_version, + source_tmux_file) +from powerline.lib.encoding import get_preferred_output_encoding +from powerline.renderers.tmux import attrs_to_tmux_attrs +from powerline.commands.main import finish_args + + +CONFIG_FILE_NAME = re.compile(r'powerline_tmux_(?P\d+)\.(?P\d+)(?P[a-z]+)?(?:_(?Pplus|minus))?\.conf') +CONFIG_MATCHERS = { + None: (lambda a, b: a.major == b.major and a.minor == b.minor), + 'plus': (lambda a, b: a[:2] <= b[:2]), + 'minus': (lambda a, b: a[:2] >= b[:2]), +} +CONFIG_PRIORITY = { + None: 3, + 'plus': 2, + 'minus': 1, +} + + +def list_all_tmux_configs(): + '''List all version-specific tmux configuration files''' + for root, dirs, files in os.walk(TMUX_CONFIG_DIRECTORY): + dirs[:] = () + for fname in files: + match = CONFIG_FILE_NAME.match(fname) + if match: + assert match.group('suffix') is None + yield ( + os.path.join(root, fname), + CONFIG_MATCHERS[match.group('mod')], + CONFIG_PRIORITY[match.group('mod')], + TmuxVersionInfo( + int(match.group('major')), + int(match.group('minor')), + match.group('suffix'), + ), + ) + + +def get_tmux_configs(version): + '''Get tmux configuration suffix given parsed tmux version + + :param TmuxVersionInfo version: Parsed tmux version. + ''' + for fname, matcher, priority, file_version in list_all_tmux_configs(): + if matcher(file_version, version): + yield (fname, priority + file_version.minor * 10 + file_version.major * 10000) + + +def source_tmux_files(pl, args, tmux_version=None, source_tmux_file=source_tmux_file): + '''Source relevant version-specific tmux configuration files + + Files are sourced in the following order: + * First relevant files with older versions are sourced. + * If files for same versions are to be sourced then first _minus files are + sourced, then _plus files and then files without _minus or _plus suffixes. + ''' + tmux_version = tmux_version or get_tmux_version(pl) + source_tmux_file(os.path.join(TMUX_CONFIG_DIRECTORY, 'powerline-base.conf')) + for fname, priority in sorted(get_tmux_configs(tmux_version), key=(lambda v: v[1])): + source_tmux_file(fname) + if not os.environ.get('POWERLINE_COMMAND'): + cmd = deduce_command() + if cmd: + set_tmux_environment('POWERLINE_COMMAND', deduce_command(), remove=False) + try: + run_tmux_command('refresh-client') + except subprocess.CalledProcessError: + # On tmux-2.0 this command may fail for whatever reason. Since it is + # critical just ignore the failure. + pass + + +class EmptyArgs(object): + def __init__(self, ext, config_path): + self.ext = [ext] + self.side = 'left' + self.config_path = None + + def __getattr__(self, attr): + return None + + +def init_tmux_environment(pl, args, set_tmux_environment=set_tmux_environment): + '''Initialize tmux environment from tmux configuration + ''' + powerline = ShellPowerline(finish_args(None, os.environ, EmptyArgs('tmux', args.config_path))) + # TODO Move configuration files loading out of Powerline object and use it + # directly + powerline.update_renderer() + # FIXME Use something more stable then `theme_kwargs` + colorscheme = powerline.renderer_options['theme_kwargs']['colorscheme'] + + def get_highlighting(group): + return colorscheme.get_highlighting([group], None) + + for varname, highlight_group in ( + ('_POWERLINE_BACKGROUND_COLOR', 'background'), + ('_POWERLINE_ACTIVE_WINDOW_STATUS_COLOR', 'active_window_status'), + ('_POWERLINE_WINDOW_STATUS_COLOR', 'window_status'), + ('_POWERLINE_ACTIVITY_STATUS_COLOR', 'activity_status'), + ('_POWERLINE_BELL_STATUS_COLOR', 'bell_status'), + ('_POWERLINE_WINDOW_COLOR', 'window'), + ('_POWERLINE_WINDOW_DIVIDER_COLOR', 'window:divider'), + ('_POWERLINE_WINDOW_CURRENT_COLOR', 'window:current'), + ('_POWERLINE_WINDOW_NAME_COLOR', 'window_name'), + ('_POWERLINE_SESSION_COLOR', 'session'), + ): + highlight = get_highlighting(highlight_group) + set_tmux_environment(varname, powerline.renderer.hlstyle(**highlight)[2:-1]) + for varname, prev_group, next_group in ( + ('_POWERLINE_WINDOW_CURRENT_HARD_DIVIDER_COLOR', 'window', 'window:current'), + ('_POWERLINE_WINDOW_CURRENT_HARD_DIVIDER_NEXT_COLOR', 'window:current', 'window'), + ('_POWERLINE_SESSION_HARD_DIVIDER_NEXT_COLOR', 'session', 'background'), + ): + prev_highlight = get_highlighting(prev_group) + next_highlight = get_highlighting(next_group) + set_tmux_environment( + varname, + powerline.renderer.hlstyle( + fg=prev_highlight['bg'], + bg=next_highlight['bg'], + attrs=0, + )[2:-1] + ) + for varname, attr, group in ( + ('_POWERLINE_ACTIVE_WINDOW_FG', 'fg', 'active_window_status'), + ('_POWERLINE_WINDOW_STATUS_FG', 'fg', 'window_status'), + ('_POWERLINE_ACTIVITY_STATUS_FG', 'fg', 'activity_status'), + ('_POWERLINE_ACTIVITY_STATUS_ATTR', 'attrs', 'activity_status'), + ('_POWERLINE_BELL_STATUS_FG', 'fg', 'bell_status'), + ('_POWERLINE_BELL_STATUS_ATTR', 'attrs', 'bell_status'), + ('_POWERLINE_BACKGROUND_FG', 'fg', 'background'), + ('_POWERLINE_BACKGROUND_BG', 'bg', 'background'), + ('_POWERLINE_SESSION_FG', 'fg', 'session'), + ('_POWERLINE_SESSION_BG', 'bg', 'session'), + ('_POWERLINE_SESSION_ATTR', 'attrs', 'session'), + ('_POWERLINE_SESSION_PREFIX_FG', 'fg', 'session:prefix'), + ('_POWERLINE_SESSION_PREFIX_BG', 'bg', 'session:prefix'), + ('_POWERLINE_SESSION_PREFIX_ATTR', 'attrs', 'session:prefix'), + ): + if attr == 'attrs': + attrs = attrs_to_tmux_attrs(get_highlighting(group)[attr]) + set_tmux_environment(varname, ']#['.join(attrs)) + set_tmux_environment(varname + '_LEGACY', (','.join( + # Tmux-1.6 does not accept no… attributes in + # window-status-…-attr options. + (attr for attr in attrs if not attr.startswith('no'))) + # But it does not support empty attributes as well. + or 'none')) + else: + if powerline.common_config['term_truecolor']: + set_tmux_environment(varname, '#{0:06x}'.format(get_highlighting(group)[attr][1])) + else: + set_tmux_environment(varname, 'colour' + str(get_highlighting(group)[attr][0])) + + left_dividers = powerline.renderer.theme.dividers['left'] + set_tmux_environment('_POWERLINE_LEFT_HARD_DIVIDER', left_dividers['hard']) + set_tmux_environment('_POWERLINE_LEFT_SOFT_DIVIDER', left_dividers['soft']) + set_tmux_environment('_POWERLINE_LEFT_HARD_DIVIDER_SPACES', ( + ' ' * powerline.renderer.strwidth(left_dividers['hard']))) + + +TMUX_VAR_RE = re.compile('\$(_POWERLINE_\w+)') + + +def tmux_setup(pl, args): + tmux_environ = {} + tmux_version = get_tmux_version(pl) + + def set_tmux_environment_nosource(varname, value, remove=True): + tmux_environ[varname] = value + + def replace_cb(match): + return tmux_environ[match.group(1)] + + def replace_env(s): + return TMUX_VAR_RE.subn(replace_cb, s)[0] + + def source_tmux_file_nosource(fname): + with open(fname) as fd: + for line in fd: + if line.startswith('#') or line == '\n': + continue + args = shlex.split(line) + args = [args[0]] + [replace_env(arg) for arg in args[1:]] + run_tmux_command(*args) + + if args.source is None: + args.source = tmux_version < (1, 9) + + if args.source: + ste = set_tmux_environment + stf = source_tmux_file + else: + ste = set_tmux_environment_nosource + stf = source_tmux_file_nosource + + init_tmux_environment(pl, args, set_tmux_environment=ste) + source_tmux_files(pl, args, tmux_version=tmux_version, source_tmux_file=stf) + + +def get_main_config(args): + find_config_files = generate_config_finder() + config_loader = ConfigLoader(run_once=True) + return load_config('config', find_config_files, config_loader) + + +def create_powerline_logger(args): + config = get_main_config(args) + common_config = finish_common_config(get_preferred_output_encoding(), config['common']) + logger, pl, get_module_attr = create_logger(common_config) + return pl + + +def check_command(cmd): + if which(cmd): + return cmd + + +def deduce_command(): + '''Deduce which command to use for ``powerline`` + + Candidates: + + * ``powerline``. Present only when installed system-wide. + * ``{powerline_root}/scripts/powerline``. Present after ``pip install -e`` + was run and C client was compiled (in this case ``pip`` does not install + binary file). + * ``{powerline_root}/client/powerline.sh``. Useful when ``sh``, ``sed`` and + ``socat`` are present, but ``pip`` or ``setup.py`` was not run. + * ``{powerline_root}/client/powerline.py``. Like above, but when one of + ``sh``, ``sed`` and ``socat`` was not present. + * ``powerline-render``. Should not really ever be used. + * ``{powerline_root}/scripts/powerline-render``. Same. + ''' + return ( + None + or check_command('powerline') + or check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline')) + or ((which('sh') and which('sed') and which('socat')) + and check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.sh'))) + or check_command(os.path.join(POWERLINE_ROOT, 'client', 'powerline.py')) + or check_command('powerline-render') + or check_command(os.path.join(POWERLINE_ROOT, 'scripts', 'powerline-render')) + ) + + +def shell_command(pl, args): + cmd = deduce_command() + if cmd: + print(cmd) + else: + sys.exit(1) + + +def uses(pl, args): + component = args.component + if not component: + raise ValueError('Must specify component') + shell = args.shell + template = 'POWERLINE_NO_{shell}_{component}' + for sh in (shell, 'shell') if shell else ('shell'): + varname = template.format(shell=sh.upper(), component=component.upper()) + if os.environ.get(varname): + sys.exit(1) + config = get_main_config(args) + if component in config.get('ext', {}).get('shell', {}).get('components', ('tmux', 'prompt')): + sys.exit(0) + else: + sys.exit(1) diff --git a/powerline-bin/powerline/bindings/fish/powerline-setup.fish b/powerline-bin/powerline/bindings/fish/powerline-setup.fish new file mode 100644 index 0000000..3887138 --- /dev/null +++ b/powerline-bin/powerline/bindings/fish/powerline-setup.fish @@ -0,0 +1,109 @@ +function powerline-setup + function _powerline_columns_fallback + if which stty >/dev/null + if stty size >/dev/null + stty size | cut -d' ' -f2 + return 0 + end + end + echo 0 + return 0 + end + + function _powerline_columns + # Hack: `test "" -eq 0` is true, as well as `test 0 -eq 0` + # Note: at fish startup `$COLUMNS` is equal to zero, meaning that it may + # not be used. + if test "$COLUMNS" -eq 0 + _powerline_columns_fallback + else + echo "$COLUMNS" + end + end + + if test -z "$POWERLINE_CONFIG_COMMAND" + if which powerline-config >/dev/null + set -g POWERLINE_CONFIG_COMMAND powerline-config + else + set -g POWERLINE_CONFIG_COMMAND (dirname (status -f))/../../../scripts/powerline-config + end + end + + if env $POWERLINE_CONFIG_COMMAND shell --shell=fish uses prompt + if test -z "$POWERLINE_COMMAND" + set -g POWERLINE_COMMAND (env $POWERLINE_CONFIG_COMMAND shell command) + end + function _powerline_set_default_mode --on-variable fish_key_bindings + if test $fish_key_bindings != fish_vi_key_bindings + set -g _POWERLINE_DEFAULT_MODE default + else + set -g -e _POWERLINE_DEFAULT_MODE + end + end + function _powerline_update --on-variable POWERLINE_COMMAND + set -l addargs "--last-exit-code=\$status" + set -l addargs "$addargs --last-pipe-status=\$status" + set -l addargs "$addargs --jobnum=(jobs -p | wc -l)" + # One random value has an 1/32767 = 0.0031% probability of having + # the same value in two shells + set -l addargs "$addargs --renderer-arg=client_id="(random) + set -l addargs "$addargs --width=\$_POWERLINE_COLUMNS" + set -l addargs "$addargs --renderer-arg=mode=\$fish_bind_mode" + set -l addargs "$addargs --renderer-arg=default_mode=\$_POWERLINE_DEFAULT_MODE" + set -l promptside + set -l rpromptpast + set -l columnsexpr + if test -z "$POWERLINE_NO_FISH_ABOVE$POWERLINE_NO_SHELL_ABOVE" + set promptside aboveleft + set rpromptpast 'echo -n " "' + set columnsexpr '(math (_powerline_columns) - 1)' + else + set promptside left + set rpromptpast + set columnsexpr '(_powerline_columns)' + end + echo " + function fish_prompt + env \$POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS shell $promptside $addargs + end + function fish_right_prompt + env \$POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS shell right $addargs + $rpromptpast + end + function _powerline_set_columns --on-signal WINCH + set -g _POWERLINE_COLUMNS $columnsexpr + end + " | source + _powerline_set_columns + end + _powerline_set_default_mode + _powerline_update + end + if env $POWERLINE_CONFIG_COMMAND shell --shell=fish uses tmux + if test -n "$TMUX" + if tmux refresh -S ^/dev/null + set -g _POWERLINE_TMUX "$TMUX" + function _powerline_tmux_pane + if test -z "$TMUX_PANE" + env TMUX="$_POWERLINE_TMUX" tmux display -p "#D" | tr -d ' %' + else + echo "$TMUX_PANE" | tr -d ' %' + end + end + function _powerline_tmux_setenv + env TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_$argv[1]_(_powerline_tmux_pane) "$argv[2]" + env TMUX="$_POWERLINE_TMUX" tmux refresh -S + end + function _powerline_tmux_set_pwd --on-variable PWD + _powerline_tmux_setenv PWD "$PWD" + end + function _powerline_tmux_set_columns --on-signal WINCH + _powerline_tmux_setenv COLUMNS (_powerline_columns) + end + _powerline_tmux_set_columns + _powerline_tmux_set_pwd + end + end + end +end +# vim: ft=fish diff --git a/powerline-bin/powerline/bindings/i3/__pycache__/powerline-i3.cpython-312.pyc b/powerline-bin/powerline/bindings/i3/__pycache__/powerline-i3.cpython-312.pyc new file mode 100644 index 0000000..4d41e94 Binary files /dev/null and b/powerline-bin/powerline/bindings/i3/__pycache__/powerline-i3.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/i3/powerline-i3.py b/powerline-bin/powerline/bindings/i3/powerline-i3.py new file mode 100644 index 0000000..f44e928 --- /dev/null +++ b/powerline-bin/powerline/bindings/i3/powerline-i3.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import time + +from threading import Lock + +from powerline.bindings.wm import get_i3_connection, i3_subscribe + +from powerline import Powerline +from powerline.lib.monotonic import monotonic + + +class I3Powerline(Powerline): + '''Powerline child for i3bar + + Currently only changes the default log target. + ''' + default_log_stream = sys.stderr + + +if __name__ == '__main__': + name = 'wm' + if len(sys.argv) > 1: + name = sys.argv[1] + + powerline = I3Powerline(name, renderer_module='i3bar') + powerline.update_renderer() + + interval = 0.5 + + print ('{"version": 1}') + print ('[') + print ('[]') + + lock = Lock() + + def render(event=None, data=None, sub=None): + global lock + with lock: + print (',[' + powerline.render()[:-1] + ']') + sys.stdout.flush() + + i3 = get_i3_connection() + i3_subscribe(i3, 'workspace', render) + + while True: + start_time = monotonic() + render() + time.sleep(max(interval - (monotonic() - start_time), 0.1)) diff --git a/powerline-bin/powerline/bindings/ipython/__init__.py b/powerline-bin/powerline/bindings/ipython/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/bindings/ipython/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/ipython/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8741ddb Binary files /dev/null and b/powerline-bin/powerline/bindings/ipython/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/ipython/__pycache__/post_0_11.cpython-312.pyc b/powerline-bin/powerline/bindings/ipython/__pycache__/post_0_11.cpython-312.pyc new file mode 100644 index 0000000..83ac444 Binary files /dev/null and b/powerline-bin/powerline/bindings/ipython/__pycache__/post_0_11.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/ipython/__pycache__/pre_0_11.cpython-312.pyc b/powerline-bin/powerline/bindings/ipython/__pycache__/pre_0_11.cpython-312.pyc new file mode 100644 index 0000000..3680e56 Binary files /dev/null and b/powerline-bin/powerline/bindings/ipython/__pycache__/pre_0_11.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/ipython/__pycache__/since_5.cpython-312.pyc b/powerline-bin/powerline/bindings/ipython/__pycache__/since_5.cpython-312.pyc new file mode 100644 index 0000000..f18889a Binary files /dev/null and b/powerline-bin/powerline/bindings/ipython/__pycache__/since_5.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/ipython/post_0_11.py b/powerline-bin/powerline/bindings/ipython/post_0_11.py new file mode 100644 index 0000000..6c1efb1 --- /dev/null +++ b/powerline-bin/powerline/bindings/ipython/post_0_11.py @@ -0,0 +1,123 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from weakref import ref +from warnings import warn + +try: + from IPython.core.prompts import PromptManager + has_prompt_manager = True +except ImportError: + has_prompt_manager = False +from IPython.core.magic import Magics, magics_class, line_magic + +from powerline.ipython import IPythonPowerline, IPythonInfo + +if has_prompt_manager: + from powerline.ipython import RewriteResult + + +@magics_class +class PowerlineMagics(Magics): + def __init__(self, ip, powerline): + super(PowerlineMagics, self).__init__(ip) + self._powerline = powerline + + @line_magic + def powerline(self, line): + if line == 'reload': + self._powerline.reload() + else: + raise ValueError('Expected `reload`, but got {0}'.format(line)) + + +old_prompt_manager = None + + +class ShutdownHook(object): + def __init__(self, ip): + self.powerline = lambda: None + ip.hooks.shutdown_hook.add(self) + + def __call__(self): + from IPython.core.hooks import TryNext + powerline = self.powerline() + if powerline is not None: + powerline.shutdown() + raise TryNext() + + +if has_prompt_manager: + class PowerlinePromptManager(PromptManager): + def __init__(self, powerline, shell): + self.powerline = powerline + self.powerline_segment_info = IPythonInfo(shell) + self.shell = shell + + def render(self, name, color=True, *args, **kwargs): + res = self.powerline.render( + is_prompt=name.startswith('in'), + side='left', + output_width=True, + output_raw=not color, + matcher_info=name, + segment_info=self.powerline_segment_info, + ) + self.txtwidth = res[-1] + self.width = res[-1] + ret = res[0] if color else res[1] + if name == 'rewrite': + return RewriteResult(ret) + else: + return ret + + class ConfigurableIPythonPowerline(IPythonPowerline): + def init(self, ip): + config = ip.config.Powerline + self.config_overrides = config.get('config_overrides') + self.theme_overrides = config.get('theme_overrides', {}) + self.config_paths = config.get('config_paths') + if has_prompt_manager: + renderer_module = '.pre_5' + else: + renderer_module = '.since_5' + super(ConfigurableIPythonPowerline, self).init( + renderer_module=renderer_module) + + def do_setup(self, ip, shutdown_hook): + global old_prompt_manager + + if old_prompt_manager is None: + old_prompt_manager = ip.prompt_manager + prompt_manager = PowerlinePromptManager( + powerline=self, + shell=ip.prompt_manager.shell, + ) + ip.prompt_manager = prompt_manager + + magics = PowerlineMagics(ip, self) + shutdown_hook.powerline = ref(self) + ip.register_magics(magics) + + +def load_ipython_extension(ip): + if has_prompt_manager: + shutdown_hook = ShutdownHook(ip) + powerline = ConfigurableIPythonPowerline(ip) + powerline.setup(ip, shutdown_hook) + else: + from powerline.bindings.ipython.since_5 import PowerlinePrompts + ip.prompts_class = PowerlinePrompts + ip.prompts = PowerlinePrompts(ip) + warn(DeprecationWarning( + 'post_0_11 extension is deprecated since IPython 5, use\n' + ' from powerline.bindings.ipython.since_5 import PowerlinePrompts\n' + ' c.TerminalInteractiveShell.prompts_class = PowerlinePrompts\n' + )) + + +def unload_ipython_extension(ip): + global old_prompt_manager + if old_prompt_manager is not None: + ip.prompt_manager = old_prompt_manager + old_prompt_manager = None diff --git a/powerline-bin/powerline/bindings/ipython/pre_0_11.py b/powerline-bin/powerline/bindings/ipython/pre_0_11.py new file mode 100644 index 0000000..2bd8095 --- /dev/null +++ b/powerline-bin/powerline/bindings/ipython/pre_0_11.py @@ -0,0 +1,146 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re + +from weakref import ref + +from IPython.Prompts import BasePrompt +from IPython.ipapi import get as get_ipython +from IPython.ipapi import TryNext + +from powerline.ipython import IPythonPowerline, RewriteResult +from powerline.lib.unicode import string + + +class IPythonInfo(object): + def __init__(self, cache): + self._cache = cache + + @property + def prompt_count(self): + return self._cache.prompt_count + + +class PowerlinePrompt(BasePrompt): + def __init__(self, powerline, powerline_last_in, old_prompt): + self.powerline = powerline + self.powerline_last_in = powerline_last_in + self.powerline_segment_info = IPythonInfo(old_prompt.cache) + self.cache = old_prompt.cache + if hasattr(old_prompt, 'sep'): + self.sep = old_prompt.sep + self.pad_left = False + + def __str__(self): + self.set_p_str() + return string(self.p_str) + + def set_p_str(self): + self.p_str, self.p_str_nocolor, self.powerline_prompt_width = ( + self.powerline.render( + is_prompt=self.powerline_is_prompt, + side='left', + output_raw=True, + output_width=True, + segment_info=self.powerline_segment_info, + matcher_info=self.powerline_prompt_type, + ) + ) + + @staticmethod + def set_colors(): + pass + + +class PowerlinePrompt1(PowerlinePrompt): + powerline_prompt_type = 'in' + powerline_is_prompt = True + rspace = re.compile(r'(\s*)$') + + def __str__(self): + self.cache.prompt_count += 1 + self.set_p_str() + self.cache.last_prompt = self.p_str_nocolor.split('\n')[-1] + return string(self.p_str) + + def set_p_str(self): + super(PowerlinePrompt1, self).set_p_str() + self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group()) + self.powerline_last_in['nrspaces'] = self.nrspaces + + def auto_rewrite(self): + return RewriteResult(self.powerline.render( + is_prompt=False, + side='left', + matcher_info='rewrite', + segment_info=self.powerline_segment_info) + (' ' * self.nrspaces) + ) + + +class PowerlinePromptOut(PowerlinePrompt): + powerline_prompt_type = 'out' + powerline_is_prompt = False + + def set_p_str(self): + super(PowerlinePromptOut, self).set_p_str() + spaces = ' ' * self.powerline_last_in['nrspaces'] + self.p_str += spaces + self.p_str_nocolor += spaces + + +class PowerlinePrompt2(PowerlinePromptOut): + powerline_prompt_type = 'in2' + powerline_is_prompt = True + + +class ConfigurableIPythonPowerline(IPythonPowerline): + def init(self, config_overrides=None, theme_overrides={}, config_paths=None): + self.config_overrides = config_overrides + self.theme_overrides = theme_overrides + self.config_paths = config_paths + super(ConfigurableIPythonPowerline, self).init(renderer_module='.pre_5') + + def ipython_magic(self, ip, parameter_s=''): + if parameter_s == 'reload': + self.reload() + else: + raise ValueError('Expected `reload`, but got {0}'.format(parameter_s)) + + def do_setup(self, ip, shutdown_hook): + last_in = {'nrspaces': 0} + for attr, prompt_class in ( + ('prompt1', PowerlinePrompt1), + ('prompt2', PowerlinePrompt2), + ('prompt_out', PowerlinePromptOut) + ): + old_prompt = getattr(ip.IP.outputcache, attr) + prompt = prompt_class(self, last_in, old_prompt) + setattr(ip.IP.outputcache, attr, prompt) + ip.expose_magic('powerline', self.ipython_magic) + shutdown_hook.powerline = ref(self) + + +class ShutdownHook(object): + powerline = lambda: None + + def __call__(self): + from IPython.ipapi import TryNext + powerline = self.powerline() + if powerline is not None: + powerline.shutdown() + raise TryNext() + + +def setup(**kwargs): + ip = get_ipython() + + powerline = ConfigurableIPythonPowerline(**kwargs) + shutdown_hook = ShutdownHook() + + def late_startup_hook(): + powerline.setup(ip, shutdown_hook) + raise TryNext() + + ip.IP.hooks.late_startup_hook.add(late_startup_hook) + ip.IP.hooks.shutdown_hook.add(shutdown_hook) diff --git a/powerline-bin/powerline/bindings/ipython/since_5.py b/powerline-bin/powerline/bindings/ipython/since_5.py new file mode 100644 index 0000000..5a899ae --- /dev/null +++ b/powerline-bin/powerline/bindings/ipython/since_5.py @@ -0,0 +1,81 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from weakref import ref + +from IPython.terminal.prompts import Prompts +from pygments.token import Token # NOQA + +from powerline.ipython import IPythonPowerline +from powerline.renderers.ipython.since_5 import PowerlinePromptStyle +from powerline.bindings.ipython.post_0_11 import PowerlineMagics, ShutdownHook + + +class ConfigurableIPythonPowerline(IPythonPowerline): + def init(self, ip): + config = ip.config.Powerline + self.config_overrides = config.get('config_overrides') + self.theme_overrides = config.get('theme_overrides', {}) + self.config_paths = config.get('config_paths') + super(ConfigurableIPythonPowerline, self).init( + renderer_module='.since_5') + + def do_setup(self, ip, prompts, shutdown_hook): + prompts.powerline = self + + msfn_missing = () + saved_msfn = getattr(ip, '_make_style_from_name', msfn_missing) + + if hasattr(saved_msfn, 'powerline_original'): + saved_msfn = saved_msfn.powerline_original + + def _make_style_from_name(ip, name): + prev_style = saved_msfn(name) + new_style = PowerlinePromptStyle(lambda: prev_style) + return new_style + + _make_style_from_name.powerline_original = saved_msfn + + if not isinstance(ip._style, PowerlinePromptStyle): + prev_style = ip._style + ip._style = PowerlinePromptStyle(lambda: prev_style) + + if not isinstance(saved_msfn, type(self.init)): + _saved_msfn = saved_msfn + saved_msfn = lambda: _saved_msfn(ip) + + if saved_msfn is not msfn_missing: + ip._make_style_from_name = _make_style_from_name + + magics = PowerlineMagics(ip, self) + ip.register_magics(magics) + + if shutdown_hook: + shutdown_hook.powerline = ref(self) + + +class PowerlinePrompts(Prompts): + '''Class that returns powerline prompts + ''' + def __init__(self, shell): + shutdown_hook = ShutdownHook(shell) + powerline = ConfigurableIPythonPowerline(shell) + self.shell = shell + powerline.do_setup(shell, self, shutdown_hook) + self.last_output_count = None + self.last_output = {} + + for prompt in ('in', 'continuation', 'rewrite', 'out'): + exec(( + 'def {0}_prompt_tokens(self, *args, **kwargs):\n' + ' if self.last_output_count != self.shell.execution_count:\n' + ' self.last_output.clear()\n' + ' self.last_output_count = self.shell.execution_count\n' + ' if "{0}" not in self.last_output:\n' + ' self.last_output["{0}"] = self.powerline.render(' + ' side="left",' + ' matcher_info="{1}",' + ' segment_info=self.shell,' + ' ) + [(Token.Generic.Prompt, " ")]\n' + ' return self.last_output["{0}"]' + ).format(prompt, 'in2' if prompt == 'continuation' else prompt)) diff --git a/powerline-bin/powerline/bindings/lemonbar/__pycache__/powerline-lemonbar.cpython-312.pyc b/powerline-bin/powerline/bindings/lemonbar/__pycache__/powerline-lemonbar.cpython-312.pyc new file mode 100644 index 0000000..df4cca8 Binary files /dev/null and b/powerline-bin/powerline/bindings/lemonbar/__pycache__/powerline-lemonbar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/lemonbar/powerline-lemonbar.py b/powerline-bin/powerline/bindings/lemonbar/powerline-lemonbar.py new file mode 100644 index 0000000..7637254 --- /dev/null +++ b/powerline-bin/powerline/bindings/lemonbar/powerline-lemonbar.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import time +import re +import subprocess + +from threading import Lock, Timer + +from powerline.lemonbar import LemonbarPowerline +from powerline.commands.lemonbar import get_argparser +from powerline.bindings.wm import get_connected_xrandr_outputs + + +if __name__ == '__main__': + parser = get_argparser() + args = parser.parse_args() + + powerline = LemonbarPowerline() + powerline.update_renderer() + bars = [] + + for screen in get_connected_xrandr_outputs(powerline.pl): + command = [args.bar_command, '-g', '{0}x{1}+{2}'.format(screen['width'], args.height, screen['x'])] + args.args[1:] + process = subprocess.Popen(command, stdin=subprocess.PIPE) + bars.append((screen['name'], process, int(screen['width']) / 5)) + + lock = Lock() + modes = ['default'] + + def render(reschedule=False): + if reschedule: + Timer(args.interval, render, kwargs={'reschedule': True}).start() + + global lock + with lock: + for output, process, width in bars: + process.stdin.write(powerline.render(mode=modes[0], width=width, matcher_info=output).encode('utf-8') + b'\n') + process.stdin.flush() + + def update(evt): + modes[0] = evt.change + render() + + render(reschedule=True) + + if args.i3: + try: + import i3ipc + except ImportError: + import i3 + i3.Subscription(lambda evt, data, sub: render(), 'workspace') + else: + conn = i3ipc.Connection() + conn.on('workspace::focus', lambda conn, evt: render()) + conn.on('mode', lambda conn, evt: update(evt)) + conn.main() + + while True: + time.sleep(1e8) diff --git a/powerline-bin/powerline/bindings/pdb/__init__.py b/powerline-bin/powerline/bindings/pdb/__init__.py new file mode 100644 index 0000000..4033e61 --- /dev/null +++ b/powerline-bin/powerline/bindings/pdb/__init__.py @@ -0,0 +1,183 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import pdb + +from powerline.pdb import PDBPowerline +from powerline.lib.encoding import get_preferred_output_encoding +from powerline.lib.unicode import unicode + + +if sys.version_info < (3,): + # XXX The below classes make code compatible with PDBpp which uses pyrepl + # which does not expect unicode or something above ASCII. They are + # completely not needed if pdbpp is not used, but that’s not always the + # case. + class PowerlineRenderBytesResult(bytes): + def __new__(cls, s, encoding=None): + encoding = encoding or s.encoding + if isinstance(s, PowerlineRenderResult): + return s.encode(encoding) + self = bytes.__new__(cls, s.encode(encoding) if isinstance(s, unicode) else s) + self.encoding = encoding + return self + + for meth in ( + '__contains__', + 'partition', 'rpartition', + 'split', 'rsplit', + 'count', 'join', + ): + exec(( + 'def {0}(self, *args):\n' + ' if any((isinstance(arg, unicode) for arg in args)):\n' + ' return self.__unicode__().{0}(*args)\n' + ' else:\n' + ' return bytes.{0}(self, *args)' + ).format(meth)) + + for meth in ( + 'find', 'rfind', + 'index', 'rindex', + ): + exec(( + 'def {0}(self, *args):\n' + ' if any((isinstance(arg, unicode) for arg in args)):\n' + ' args = [arg.encode(self.encoding) if isinstance(arg, unicode) else arg for arg in args]\n' + ' return bytes.{0}(self, *args)' + ).format(meth)) + + def __len__(self): + return len(self.decode(self.encoding)) + + def __getitem__(self, *args): + return PowerlineRenderBytesResult(bytes.__getitem__(self, *args), encoding=self.encoding) + + def __getslice__(self, *args): + return PowerlineRenderBytesResult(bytes.__getslice__(self, *args), encoding=self.encoding) + + @staticmethod + def add(encoding, *args): + if any((isinstance(arg, unicode) for arg in args)): + return PowerlineRenderResult(''.join(( + arg + if isinstance(arg, unicode) + else arg.decode(encoding) + for arg in args + )), encoding) + else: + return PowerlineRenderBytesResult(b''.join(args), encoding=encoding) + + def __add__(self, other): + return self.add(self.encoding, self, other) + + def __radd__(self, other): + return self.add(self.encoding, other, self) + + def __unicode__(self): + return PowerlineRenderResult(self) + + class PowerlineRenderResult(unicode): + def __new__(cls, s, encoding=None): + encoding = ( + encoding + or getattr(s, 'encoding', None) + or get_preferred_output_encoding() + ) + if isinstance(s, unicode): + self = unicode.__new__(cls, s) + else: + self = unicode.__new__(cls, s, encoding, 'replace') + self.encoding = encoding + return self + + def __str__(self): + return PowerlineRenderBytesResult(self) + + def __getitem__(self, *args): + return PowerlineRenderResult(unicode.__getitem__(self, *args)) + + def __getslice__(self, *args): + return PowerlineRenderResult(unicode.__getslice__(self, *args)) + + @staticmethod + def add(encoding, *args): + return PowerlineRenderResult(''.join(( + arg + if isinstance(arg, unicode) + else arg.decode(encoding) + for arg in args + )), encoding) + + def __add__(self, other): + return self.add(self.encoding, self, other) + + def __radd__(self, other): + return self.add(self.encoding, other, self) + + def encode(self, *args, **kwargs): + return PowerlineRenderBytesResult(unicode.encode(self, *args, **kwargs), args[0]) +else: + PowerlineRenderResult = str + + +def use_powerline_prompt(cls): + '''Decorator that installs powerline prompt to the class + + :param pdb.Pdb cls: + Class that should be decorated. + + :return: + ``cls`` argument or a class derived from it. Latter is used to turn + old-style classes into new-style classes. + ''' + @property + def prompt(self): + try: + powerline = self.powerline + except AttributeError: + powerline = PDBPowerline() + powerline.setup(self) + self.powerline = powerline + return PowerlineRenderResult(powerline.render(side='left')) + + @prompt.setter + def prompt(self, _): + pass + + @prompt.deleter + def prompt(self): + pass + + if not hasattr(cls, '__class__'): + # Old-style class: make it new-style or @property will not work. + old_cls = cls + + class cls(cls, object): + __module__ = cls.__module__ + __doc__ = cls.__doc__ + + cls.__name__ = old_cls.__name__ + + cls.prompt = prompt + + return cls + + +def main(): + '''Run module as a script + + Uses :py:func:`pdb.main` function directly, but prior to that it mocks + :py:class:`pdb.Pdb` class with powerline-specific class instance. + ''' + orig_pdb = pdb.Pdb + + @use_powerline_prompt + class Pdb(pdb.Pdb, object): + def __init__(self): + orig_pdb.__init__(self) + + pdb.Pdb = Pdb + + return pdb.main() diff --git a/powerline-bin/powerline/bindings/pdb/__main__.py b/powerline-bin/powerline/bindings/pdb/__main__.py new file mode 100644 index 0000000..768b2f2 --- /dev/null +++ b/powerline-bin/powerline/bindings/pdb/__main__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.bindings.pdb import main + + +if __name__ == '__main__': + main() diff --git a/powerline-bin/powerline/bindings/pdb/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/pdb/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..6b683ce Binary files /dev/null and b/powerline-bin/powerline/bindings/pdb/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/pdb/__pycache__/__main__.cpython-312.pyc b/powerline-bin/powerline/bindings/pdb/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000..570f51b Binary files /dev/null and b/powerline-bin/powerline/bindings/pdb/__pycache__/__main__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/qtile/__init__.py b/powerline-bin/powerline/bindings/qtile/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/bindings/qtile/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/qtile/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..aff090f Binary files /dev/null and b/powerline-bin/powerline/bindings/qtile/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/qtile/__pycache__/widget.cpython-312.pyc b/powerline-bin/powerline/bindings/qtile/__pycache__/widget.cpython-312.pyc new file mode 100644 index 0000000..334316d Binary files /dev/null and b/powerline-bin/powerline/bindings/qtile/__pycache__/widget.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/qtile/widget.py b/powerline-bin/powerline/bindings/qtile/widget.py new file mode 100644 index 0000000..92e3a27 --- /dev/null +++ b/powerline-bin/powerline/bindings/qtile/widget.py @@ -0,0 +1,61 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from libqtile.bar import CALCULATED +from libqtile.widget import TextBox + +from powerline import Powerline + + +class QTilePowerline(Powerline): + def do_setup(self, obj): + obj.powerline = self + + +class PowerlineTextBox(TextBox): + # TODO Replace timeout argument with update_interval argument in next major + # release. + def __init__(self, timeout=2, text=b' ', width=CALCULATED, side='right', update_interval=None, **config): + super(PowerlineTextBox, self).__init__(text, width, **config) + self.side = side + self.update_interval = update_interval or timeout + self.did_run_timer_setup = False + powerline = QTilePowerline(ext='wm', renderer_module='pango_markup') + powerline.setup(self) + + def update(self): + if not self.configured: + return True + self.text = self.powerline.render(side=self.side).encode('utf-8') + self.bar.draw() + return True + + def cmd_update(self, text): + self.update(text) + + def cmd_get(self): + return self.text + + def timer_setup(self): + if not self.did_run_timer_setup: + self.did_run_timer_setup = True + self.timeout_add(self.update_interval, self.update) + + def _configure(self, qtile, bar): + super(PowerlineTextBox, self)._configure(qtile, bar) + if self.layout.markup: + # QTile-0.9.1: no need to recreate layout or run timer_setup + return + self.layout = self.drawer.textlayout( + self.text, + self.foreground, + self.font, + self.fontsize, + self.fontshadow, + markup=True, + ) + self.timer_setup() + + +# TODO: Remove this at next major release +Powerline = PowerlineTextBox diff --git a/powerline-bin/powerline/bindings/rc/powerline.rc b/powerline-bin/powerline/bindings/rc/powerline.rc new file mode 100644 index 0000000..b2d6538 --- /dev/null +++ b/powerline-bin/powerline/bindings/rc/powerline.rc @@ -0,0 +1,92 @@ +fn _powerline_sigwinch { + _POWERLINE_COLUMNS = `{ + stty size | cut -d' ' -f2 + } + _powerline_tmux_setenv COLUMNS $_POWERLINE_COLUMNS +} +fn _powerline_update_pwd { + _POWERLINE_NEW_PWD = `{pwd} + if (test $^_POWERLINE_NEW_PWD '=' $^_POWERLINE_SAVED_PWD) { + _POWERLINE_SAVED_PWD = $_POWERLINE_NEW_PWD + _powerline_tmux_setenv PWD $_POWERLINE_SAVED_PWD + } +} +fn _powerline_continuation_prompt { + _powerline_prompt --renderer-arg 'local_theme=continuation' $* +} +fn _powerline_prompt { + $POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS shell aboveleft -r.readline --last-pipe-status $^_POWERLINE_STATUS --last-exit-code $_POWERLINE_STATUS($#_POWERLINE_STATUS) --jobnum $_POWERLINE_JOBNUM --renderer-arg 'client_id='$pid $* +} +fn _powerline_set_prompt { + _POWERLINE_STATUS = ( $status ) + _POWERLINE_JOBNUM = $#apids + prompt = (``() { + _powerline_prompt + } ``() { + _powerline_continuation_prompt + }) + _powerline_update_pwd +} + +fn _powerline_common_setup { + fn sigwinch { + _powerline_sigwinch + } + _powerline_sigwinch + _POWERLINE_SAVED_PWD = '' +} + +fn _powerline_tmux_pane { + if (test -n $TMUX_PANE) { + echo $TMUX_PANE | tr -d ' %' + } else { + TMUX=$_POWERLINE_TMUX tmux display -p '#D' | tr -d ' %' + } +} + +fn _powerline_tmux_setenv { +} + +if (test -z $POWERLINE_CONFIG_COMMAND) { + if (which powerline-config >/dev/null) { + POWERLINE_CONFIG_COMMAND = powerline-config + } else { + echo powerline-config executable not found, unable to proceed >[2=1] + } +} +if (test -n $POWERLINE_CONFIG_COMMAND) { + if ($POWERLINE_CONFIG_COMMAND shell --shell rcsh uses prompt) { + if (test -n $POWERLINE_COMMAND_ARGS) { + # Perform splitting + POWERLINE_COMMAND_ARGS=( `{echo $POWERLINE_COMMAND_ARGS} ) + } + fn prompt { + _powerline_set_prompt + } + if (test -z $POWERLINE_SHELL_CONTINUATION$POWERLINE_RCSH_CONTINUATION) { + _POWERLINE_STATUS = 0 + _POWERLINE_JOBNUM = 0 + _POWERLINE_CONTINUATION = `{ + _powerline_continuation_prompt + } + fn _powerline_continuation_prompt { + echo -n $_POWERLINE_CONTINUATION + } + } + _powerline_common_setup + } + if (test -n $TMUX) { + if ($POWERLINE_CONFIG_COMMAND shell --shell rcsh uses tmux) { + _POWERLINE_TMUX=$TMUX + fn _powerline_tmux_setenv { + if (test -n $2) { + TMUX=$_POWERLINE_TMUX tmux setenv -g TMUX_$1^_`{ + _powerline_tmux_pane + } $2 + } + } + _powerline_common_setup + } + } +} +# vim: ft=rcshell diff --git a/powerline-bin/powerline/bindings/shell/powerline.sh b/powerline-bin/powerline/bindings/shell/powerline.sh new file mode 100644 index 0000000..15e13f2 --- /dev/null +++ b/powerline-bin/powerline/bindings/shell/powerline.sh @@ -0,0 +1,239 @@ +_POWERLINE_SOURCED="$_" +_powerline_columns_fallback() { + if which stty >/dev/null ; then + # Ksh does not have “local” built-in + _powerline_cols="$(stty size 2>/dev/null)" + if ! test -z "$_powerline_cols" ; then + echo "${_powerline_cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + +_powerline_has_jobs_in_subshell() { + if test -n "$_POWERLINE_HAS_JOBS_IN_SUBSHELL" ; then + return $_POWERLINE_HAS_JOBS_IN_SUBSHELL + elif test -z "$1" ; then + sleep 1 & + # Check whether shell outputs anything in a subshell when using jobs + # built-in. Shells like dash will not output anything meaning that + # I have to bother with temporary files. + test "$(jobs -p|wc -l)" -gt 0 + else + case "$1" in + dash|bb|ash) return 1 ;; + mksh|ksh|bash) return 0 ;; + *) _powerline_has_jobs_in_subshell ;; + esac + fi + _POWERLINE_HAS_JOBS_IN_SUBSHELL=$? + return $_POWERLINE_HAS_JOBS_IN_SUBSHELL +} + +_powerline_set_append_trap() { + if _powerline_has_jobs_in_subshell "$@" ; then + _powerline_append_trap() { + # Arguments: command, signal + # Ksh does not have “local” built-in + _powerline_traps="$(trap)" + if echo "$_powerline_traps" | grep -cm1 $2'$' >/dev/null ; then + _powerline_traps="$(echo "$_powerline_traps" | sed "s/ $2/'\\n$1' $2/")" + eval "$_powerline_traps" + else + trap "$1" $2 + fi + } + else + _powerline_append_trap() { + # Arguments: command, signal + _powerline_create_temp + trap > $_POWERLINE_TEMP + if grep -cm1 $2'$' $_POWERLINE_TEMP >/dev/null ; then + sed -i -e "s/ $2/'\\n$1' $2/" + . $_POWERLINE_TEMP + else + trap "$1" $2 + fi + echo -n > $_POWERLINE_TEMP + } + fi + _powerline_set_append_trap() { + return 0 + } +} + +_powerline_create_temp() { + if test -z "$_POWERLINE_TEMP" || ! test -e "$_POWERLINE_TEMP" ; then + _POWERLINE_TEMP="$(mktemp "${TMPDIR:-/tmp}/powerline.XXXXXXXX")" + _powerline_append_trap 'rm $_POWERLINE_TEMP' EXIT + fi +} + +_powerline_set_set_jobs() { + if _powerline_has_jobs_in_subshell "$@" ; then + _powerline_set_jobs() { + _POWERLINE_JOBS="$(jobs -p|wc -l|tr -d ' ')" + } + else + _powerline_set_append_trap "$@" + _POWERLINE_PID=$$ + _powerline_append_trap '_powerline_do_set_jobs' USR1 + _powerline_do_set_jobs() { + _powerline_create_temp + jobs -p > $_POWERLINE_TEMP + } + # This command will always be launched from a subshell, thus a hack is + # needed to run `jobs -p` outside of the subshell. + _powerline_set_jobs() { + kill -USR1 $_POWERLINE_PID + # Note: most likely this will read data from the previous run. Tests + # show that it is OK for some reasons. + _POWERLINE_JOBS="$(wc -l < $_POWERLINE_TEMP | tr -d ' ')" + } + fi + _powerline_set_set_jobs() { + return 0 + } +} + +_powerline_set_command() { + if test -z "${POWERLINE_COMMAND}" ; then + POWERLINE_COMMAND="$("$POWERLINE_CONFIG_COMMAND" shell command)" + fi +} + +_powerline_tmux_pane() { + echo "${TMUX_PANE:-`TMUX="$_POWERLINE_TMUX" tmux display -p "#D"`}" | \ + tr -d ' %' +} + +_powerline_tmux_setenv() { + TMUX="$_POWERLINE_TMUX" tmux setenv -g TMUX_"$1"_`_powerline_tmux_pane` "$2" + TMUX="$_POWERLINE_TMUX" tmux refresh -S +} + +_powerline_tmux_set_pwd() { + if test "$_POWERLINE_SAVED_PWD" != "$PWD" ; then + _POWERLINE_SAVED_PWD="$PWD" + _powerline_tmux_setenv PWD "$PWD" + fi +} + +_powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" +} + +_powerline_set_renderer_arg() { + case "$1" in + bb|ash) _POWERLINE_RENDERER_ARG="-r .bash" ;; + mksh|ksh) _POWERLINE_RENDERER_ARG="-r .ksh" ;; + bash|dash) _POWERLINE_RENDERER_ARG= ;; + esac +} + +_powerline_set_jobs() { + _powerline_set_set_jobs + _powerline_set_jobs +} + +_powerline_local_prompt() { + # Arguments: side, exit_code, local theme + _powerline_set_jobs + "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS shell $1 \ + $_POWERLINE_RENDERER_ARG \ + --renderer-arg="client_id=$$" \ + --last-exit-code=$2 \ + --jobnum=$_POWERLINE_JOBS \ + --renderer-arg="local_theme=$3" +} + +_powerline_prompt() { + # Arguments: side, exit_code + _powerline_set_jobs + "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS shell $1 \ + --width="${COLUMNS:-$(_powerline_columns_fallback)}" \ + $_POWERLINE_RENDERER_ARG \ + --renderer-arg="client_id=$$" \ + --last-exit-code=$2 \ + --jobnum=$_POWERLINE_JOBS + _powerline_update_psN +} + +_powerline_setup_psN() { + case "$1" in + mksh|ksh|bash) + _POWERLINE_PID=$$ + _powerline_update_psN() { + kill -USR1 $_POWERLINE_PID + } + # No command substitution in PS2 and PS3 + _powerline_set_psN() { + if test -n "$POWERLINE_SHELL_CONTINUATION" ; then + PS2="$(_powerline_local_prompt left $? continuation)" + fi + if test -n "$POWERLINE_SHELL_SELECT" ; then + PS3="$(_powerline_local_prompt left $? select)" + fi + } + _powerline_append_trap '_powerline_set_psN' USR1 + _powerline_set_psN + ;; + bb|ash|dash) + _powerline_update_psN() { + # Do nothing + return + } + PS2='$(_powerline_local_prompt left $? continuation)' + # No select support + ;; + esac +} + +_powerline_setup_prompt() { + VIRTUAL_ENV_DISABLE_PROMPT=1 + _powerline_set_append_trap "$@" + _powerline_set_set_jobs "$@" + _powerline_set_command "$@" + _powerline_set_renderer_arg "$@" + PS1='$(_powerline_prompt aboveleft $?)' + PS2="$(_powerline_local_prompt left 0 continuation)" + PS3="$(_powerline_local_prompt left 0 select)" + _powerline_setup_psN "$@" +} + +_powerline_init_tmux_support() { + # Dash does not have &>/dev/null + if test -n "$TMUX" && tmux refresh -S >/dev/null 2>/dev/null ; then + # TMUX variable may be unset to create new tmux session inside this one + _POWERLINE_TMUX="$TMUX" + + _powerline_set_append_trap "$@" + + # If _powerline_tmux_set_pwd is used before _powerline_prompt it sets $? + # to zero in ksh. + PS1="$PS1"'$(_powerline_tmux_set_pwd)' + _powerline_append_trap '_powerline_tmux_set_columns' WINCH + _powerline_tmux_set_columns + fi +} + +if test -z "${POWERLINE_CONFIG_COMMAND}" ; then + if which powerline-config >/dev/null ; then + POWERLINE_CONFIG_COMMAND=powerline-config + else + POWERLINE_CONFIG_COMMAND="$(dirname "$_POWERLINE_SOURCED")/../../../scripts/powerline-config" + fi +fi + +# Strips the leading `-`: it may be present when shell is a login shell +_POWERLINE_USED_SHELL=${0#-} +_POWERLINE_USED_SHELL=${_POWERLINE_USED_SHELL##*/} + +if "${POWERLINE_CONFIG_COMMAND}" shell uses tmux ; then + _powerline_init_tmux_support $_POWERLINE_USED_SHELL +fi +if "${POWERLINE_CONFIG_COMMAND}" shell --shell=bash uses prompt ; then + _powerline_setup_prompt $_POWERLINE_USED_SHELL +fi diff --git a/powerline-bin/powerline/bindings/tcsh/powerline.tcsh b/powerline-bin/powerline/bindings/tcsh/powerline.tcsh new file mode 100644 index 0000000..4897b4c --- /dev/null +++ b/powerline-bin/powerline/bindings/tcsh/powerline.tcsh @@ -0,0 +1,60 @@ +# http://unix.stackexchange.com/questions/4650/determining-path-to-sourced-shell-script: +# > In tcsh, $_ at the beginning of the script will contain the location if the +# > file was sourced and $0 contains it if it was run. +# +# Guess this relies on `$_` being set as to last argument to previous command +# which must be `.` or `source` in this case +set POWERLINE_SOURCED=($_) +if ! $?POWERLINE_CONFIG_COMMAND then + if ( { which powerline-config > /dev/null } ) then + set POWERLINE_CONFIG_COMMAND="powerline-config" + else + set POWERLINE_CONFIG_COMMAND="$POWERLINE_SOURCED[2]:h:h:h:h/scripts/powerline-config" + endif +else + if "$POWERLINE_CONFIG_COMMAND" == "" then + if ( { which powerline-config > /dev/null } ) then + set POWERLINE_CONFIG_COMMAND="powerline-config" + else + set POWERLINE_CONFIG_COMMAND="$POWERLINE_SOURCED[2]:h:h:h:h/scripts/powerline-config" + endif + endif +endif +if ( { $POWERLINE_CONFIG_COMMAND shell --shell=tcsh uses tmux } ) then + if ( $?TMUX_PANE ) then + if ( "$TMUX_PANE" == "" ) then + set _POWERLINE_TMUX_PANE="`tmux display -p '#D'`" + else + set _POWERLINE_TMUX_PANE="$TMUX_PANE" + endif + else + set _POWERLINE_TMUX_PANE="`tmux display -p '#D'`" + endif + set _POWERLINE_TMUX_PANE="`echo $_POWERLINE_TMUX_PANE:q | tr -d '% '`" + alias _powerline_tmux_set_pwd 'if ( $?TMUX && { tmux refresh -S >&/dev/null } ) tmux setenv -g TMUX_PWD_$_POWERLINE_TMUX_PANE $PWD:q ; if ( $?TMUX ) tmux refresh -S >&/dev/null' + alias cwdcmd "`alias cwdcmd` ; _powerline_tmux_set_pwd" +endif +if ( { $POWERLINE_CONFIG_COMMAND shell --shell=tcsh uses prompt } ) then + if ! $?POWERLINE_COMMAND then + set POWERLINE_COMMAND="`$POWERLINE_CONFIG_COMMAND:q shell command`" + else + if "$POWERLINE_COMMAND" == "" then + set POWERLINE_COMMAND="`$POWERLINE_CONFIG_COMMAND:q shell command`" + endif + endif + if ! $?POWERLINE_COMMAND_ARGS then + set POWERLINE_COMMAND_ARGS="" + endif + + if ( $?POWERLINE_NO_TCSH_ABOVE || $?POWERLINE_NO_SHELL_ABOVE ) then + alias _powerline_above true + else + alias _powerline_above '$POWERLINE_COMMAND:q $POWERLINE_COMMAND_ARGS shell above --renderer-arg=client_id=$$ --last-exit-code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS' + endif + + alias _powerline_set_prompt 'set prompt="`$POWERLINE_COMMAND:q $POWERLINE_COMMAND_ARGS shell left -r .tcsh --renderer-arg=client_id=$$ --last-exit-code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' + alias _powerline_set_rprompt 'set rprompt="`$POWERLINE_COMMAND:q $POWERLINE_COMMAND_ARGS shell right -r .tcsh --renderer-arg=client_id=$$ --last-exit-code=$POWERLINE_STATUS --width=$POWERLINE_COLUMNS`"' + alias _powerline_set_columns 'set POWERLINE_COLUMNS=`stty size|cut -d" " -f2` ; set POWERLINE_COLUMNS=`expr $POWERLINE_COLUMNS - 2`' + + alias precmd 'set POWERLINE_STATUS=$? ; '"`alias precmd`"' ; _powerline_set_columns ; _powerline_above ; _powerline_set_prompt ; _powerline_set_rprompt' +endif diff --git a/powerline-bin/powerline/bindings/tmux/__init__.py b/powerline-bin/powerline/bindings/tmux/__init__.py new file mode 100644 index 0000000..011cd68 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/__init__.py @@ -0,0 +1,84 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re +import os +import subprocess + +from collections import namedtuple + +from powerline.lib.shell import run_cmd + + +TmuxVersionInfo = namedtuple('TmuxVersionInfo', ('major', 'minor', 'suffix')) + + +def get_tmux_executable_name(): + '''Returns tmux executable name + + It should be defined in POWERLINE_TMUX_EXE environment variable, otherwise + it is simply “tmux”. + ''' + + return os.environ.get('POWERLINE_TMUX_EXE', 'tmux') + + +def _run_tmux(runner, args): + return runner([get_tmux_executable_name()] + list(args)) + + +def run_tmux_command(*args): + '''Run tmux command, ignoring the output''' + _run_tmux(subprocess.check_call, args) + + +def get_tmux_output(pl, *args): + '''Run tmux command and return its output''' + return _run_tmux(lambda cmd: run_cmd(pl, cmd), args) + + +def set_tmux_environment(varname, value, remove=True): + '''Set tmux global environment variable + + :param str varname: + Name of the variable to set. + :param str value: + Variable value. + :param bool remove: + True if variable should be removed from the environment prior to + attaching any client (runs ``tmux set-environment -r {varname}``). + ''' + run_tmux_command('set-environment', '-g', varname, value) + if remove: + try: + run_tmux_command('set-environment', '-r', varname) + except subprocess.CalledProcessError: + # On tmux-2.0 this command may fail for whatever reason. Since it is + # critical just ignore the failure. + pass + + +def source_tmux_file(fname): + '''Source tmux configuration file + + :param str fname: + Full path to the sourced file. + ''' + run_tmux_command('source', fname) + + +NON_DIGITS = re.compile('[^0-9]+') +DIGITS = re.compile('[0-9]+') +NON_LETTERS = re.compile('[^a-z]+') + + +def get_tmux_version(pl): + version_string = get_tmux_output(pl, '-V') + _, version_string = version_string.split(' ') + version_string = version_string.strip() + if version_string == 'master': + return TmuxVersionInfo(float('inf'), 0, version_string) + major, minor = version_string.split('.') + suffix = DIGITS.subn('', minor)[0] or None + minor = NON_DIGITS.subn('', minor)[0] + return TmuxVersionInfo(int(major), int(minor), suffix) diff --git a/powerline-bin/powerline/bindings/tmux/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/tmux/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a26c75e Binary files /dev/null and b/powerline-bin/powerline/bindings/tmux/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/tmux/powerline-base.conf b/powerline-bin/powerline/bindings/tmux/powerline-base.conf new file mode 100644 index 0000000..ca0bbf5 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline-base.conf @@ -0,0 +1,11 @@ +set -g status on +set -g status-interval 2 +set -g status-left-length 20 +set -g status-right '#(env "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS tmux right -R pane_id=\"`tmux display -p "#""D"`\")' +set -g status-right-length 150 +set -g window-status-format "#[$_POWERLINE_WINDOW_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER_SPACES#I#F #[$_POWERLINE_WINDOW_DIVIDER_COLOR]$_POWERLINE_LEFT_SOFT_DIVIDER#[default]#W $_POWERLINE_LEFT_HARD_DIVIDER_SPACES" +set -g window-status-current-format "#[$_POWERLINE_WINDOW_CURRENT_HARD_DIVIDER_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#[$_POWERLINE_WINDOW_CURRENT_COLOR]#I#F $_POWERLINE_LEFT_SOFT_DIVIDER#[$_POWERLINE_WINDOW_NAME_COLOR]#W #[$_POWERLINE_WINDOW_CURRENT_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER" + +# Legacy status-left definition to be overwritten for tmux Versions 1.8+ +set -g status-left "#[$_POWERLINE_SESSION_COLOR] #S #[$_POWERLINE_SESSION_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#(env \"\$POWERLINE_COMMAND\" tmux left -R pane_id=\"`tmux display -p '#''D'`\")" +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline.conf b/powerline-bin/powerline/bindings/tmux/powerline.conf new file mode 100644 index 0000000..29ec6a4 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline.conf @@ -0,0 +1,2 @@ +if-shell 'env "$POWERLINE_CONFIG_COMMAND" tmux setup' '' 'run-shell "powerline-config tmux setup"' +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.7_plus.conf b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.7_plus.conf new file mode 100644 index 0000000..ec225c0 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.7_plus.conf @@ -0,0 +1,3 @@ +set -g status-right '#(env "$POWERLINE_COMMAND" $POWERLINE_COMMAND_ARGS tmux right -R pane_id="`tmux display -p "#""D"`" --width=`tmux display -p "#""{client_width}"` -R width_adjust=`tmux show-options -g status-left-length | cut -d" " -f 2`)' +set -g status-left "#[$_POWERLINE_SESSION_COLOR] #S #[$_POWERLINE_SESSION_HARD_DIVIDER_NEXT_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER#(env \"\$POWERLINE_COMMAND\" tmux left --width=`tmux display -p '#''{client_width}'` -R width_adjust=`tmux show-options -g status-right-length | cut -d' ' -f2` -R pane_id=\"`tmux display -p '#''D'`\")" +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8.conf b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8.conf new file mode 100644 index 0000000..fbcd2a5 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8.conf @@ -0,0 +1,5 @@ +# powerline_tmux_1.8.conf +# tmux Version 1.8 introduces window-status-last-{attr,bg,fg}, which is +# deprecated for versions 1.9+, thus only applicable to version 1.8. +set -qg window-status-last-fg "$_POWERLINE_ACTIVE_WINDOW_FG" +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8_minus.conf b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8_minus.conf new file mode 100644 index 0000000..284eee0 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8_minus.conf @@ -0,0 +1,11 @@ +# powerline_tmux_legacy_common.conf +# tmux Version 1.8 and earlier (legacy) common options. The foo-{attr,bg,fg} +# options are deprecated starting with tmux Version 1.9. +set -g status-fg "$_POWERLINE_BACKGROUND_FG" +set -g status-bg "$_POWERLINE_BACKGROUND_BG" +set-window-option -g window-status-fg "$_POWERLINE_WINDOW_STATUS_FG" +set-window-option -g window-status-activity-attr "$_POWERLINE_ACTIVITY_STATUS_ATTR_LEGACY" +set-window-option -g window-status-bell-attr "$_POWERLINE_BELL_STATUS_ATTR_LEGACY" +set-window-option -g window-status-activity-fg "$_POWERLINE_ACTIVITY_STATUS_FG" +set-window-option -g window-status-bell-fg "$_POWERLINE_BELL_STATUS_FG" +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf new file mode 100644 index 0000000..4edcf21 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.8_plus.conf @@ -0,0 +1,5 @@ +# powerline_tmux_1.8_plus.conf +# tmux Version 1.8 introduces the 'client_prefix' format variable, applicable +# for versions 1.8+ +set -qg status-left "#{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_FG]#[bg=$_POWERLINE_SESSION_PREFIX_BG]#[$_POWERLINE_SESSION_PREFIX_ATTR],#[fg=$_POWERLINE_SESSION_FG]#[bg=$_POWERLINE_SESSION_BG]#[$_POWERLINE_SESSION_ATTR]} #S #{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_BG],#[fg=$_POWERLINE_SESSION_BG]}#[bg=$_POWERLINE_BACKGROUND_BG]#[nobold]$_POWERLINE_LEFT_HARD_DIVIDER#(env \$POWERLINE_COMMAND \$POWERLINE_COMMAND_ARGS tmux left --width=`tmux display -p '#''{client_width}'` -R width_adjust=`tmux show-options -g status-right-length | cut -d' ' -f2` -R pane_id=\"`tmux display -p '#''D'`\")" +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf new file mode 100644 index 0000000..7ab9a8b --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline_tmux_1.9_plus.conf @@ -0,0 +1,8 @@ +# powerline_tmux_1.9_plus.conf +# Version 1.9 introduces the foo-style options, applicable to version 1.9+ +set-option -qg status-style "$_POWERLINE_BACKGROUND_COLOR" +set-option -qg window-status-last-style "$_POWERLINE_ACTIVE_WINDOW_STATUS_COLOR" +set-window-option -qg window-status-style "$_POWERLINE_WINDOW_STATUS_COLOR" +set-window-option -qg window-status-activity-style "$_POWERLINE_ACTIVITY_STATUS_COLOR" +set-window-option -qg window-status-bell-style "$_POWERLINE_BELL_STATUS_COLOR" +# vim: ft=tmux diff --git a/powerline-bin/powerline/bindings/tmux/powerline_tmux_2.1_plus.conf b/powerline-bin/powerline/bindings/tmux/powerline_tmux_2.1_plus.conf new file mode 100644 index 0000000..8bf8872 --- /dev/null +++ b/powerline-bin/powerline/bindings/tmux/powerline_tmux_2.1_plus.conf @@ -0,0 +1,3 @@ +# Starting from tmux-2.1 escaping of dollar signs inside #() is harmful +set -qg status-left "#{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_FG]#[bg=$_POWERLINE_SESSION_PREFIX_BG]#[$_POWERLINE_SESSION_PREFIX_ATTR],#[fg=$_POWERLINE_SESSION_FG]#[bg=$_POWERLINE_SESSION_BG]#[$_POWERLINE_SESSION_ATTR]} #S #{?client_prefix,#[fg=$_POWERLINE_SESSION_PREFIX_BG],#[fg=$_POWERLINE_SESSION_BG]}#[bg=$_POWERLINE_BACKGROUND_BG]#[nobold]$_POWERLINE_LEFT_HARD_DIVIDER#(env $POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS tmux left --width=`tmux display -p '#''{client_width}'` -R width_adjust=`tmux show-options -g status-right-length | cut -d' ' -f2` -R pane_id=\"`tmux display -p '#''D'`\")" +set -g window-status-format "#[$_POWERLINE_WINDOW_COLOR]$_POWERLINE_LEFT_HARD_DIVIDER_SPACES#I#{?window_flags,#F, } #[$_POWERLINE_WINDOW_DIVIDER_COLOR]$_POWERLINE_LEFT_SOFT_DIVIDER#[default]#W $_POWERLINE_LEFT_HARD_DIVIDER_SPACES" diff --git a/powerline-bin/powerline/bindings/vim/__init__.py b/powerline-bin/powerline/bindings/vim/__init__.py new file mode 100644 index 0000000..b754c1f --- /dev/null +++ b/powerline-bin/powerline/bindings/vim/__init__.py @@ -0,0 +1,482 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import codecs + +try: + import vim +except ImportError: + vim = object() + +from powerline.lib.unicode import unicode + + +if ( + hasattr(vim, 'options') + and hasattr(vim, 'vvars') + and vim.vvars['version'] > 703 +): + if sys.version_info < (3,): + def get_vim_encoding(): + return vim.options['encoding'] or 'ascii' + else: + def get_vim_encoding(): + return vim.options['encoding'].decode('ascii') or 'ascii' +elif hasattr(vim, 'eval'): + def get_vim_encoding(): + return vim.eval('&encoding') or 'ascii' +else: + def get_vim_encoding(): + return 'utf-8' + +get_vim_encoding.__doc__ = ( + '''Get encoding used for Vim strings + + :return: + Value of ``&encoding``. If it is empty (i.e. Vim is compiled + without +multibyte) returns ``'ascii'``. When building documentation + outputs ``'utf-8'`` unconditionally. + ''' +) + + +vim_encoding = get_vim_encoding() + + +python_to_vim_types = { + unicode: ( + lambda o: b'\'' + (o.translate({ + ord('\''): '\'\'', + }).encode(vim_encoding)) + b'\'' + ), + list: ( + lambda o: b'[' + ( + b','.join((python_to_vim(i) for i in o)) + ) + b']' + ), + bytes: (lambda o: b'\'' + o.replace(b'\'', b'\'\'') + b'\''), + int: (str if str is bytes else (lambda o: unicode(o).encode('ascii'))), +} +python_to_vim_types[float] = python_to_vim_types[int] + + +def python_to_vim(o): + return python_to_vim_types[type(o)](o) + + +if sys.version_info < (3,): + def str_to_bytes(s): + return s + + def unicode_eval(expr): + ret = vim.eval(expr) + return ret.decode(vim_encoding, 'powerline_vim_strtrans_error') +else: + def str_to_bytes(s): + return s.encode(vim_encoding) + + def unicode_eval(expr): + return vim.eval(expr) + + +def safe_bytes_eval(expr): + return bytes(bytearray(( + int(chunk) for chunk in ( + vim.eval( + b'substitute(' + expr + b', ' + + b'\'^.*$\', \'\\=join(map(range(len(submatch(0))), ' + + b'"char2nr(submatch(0)[v:val])"))\', "")' + ).split() + ) + ))) + + +def eval_bytes(expr): + try: + return str_to_bytes(vim.eval(expr)) + except UnicodeDecodeError: + return safe_bytes_eval(expr) + + +def eval_unicode(expr): + try: + return unicode_eval(expr) + except UnicodeDecodeError: + return safe_bytes_eval(expr).decode(vim_encoding, 'powerline_vim_strtrans_error') + + +if hasattr(vim, 'bindeval'): + rettype_func = { + None: lambda f: f, + 'unicode': ( + lambda f: ( + lambda *args, **kwargs: ( + f(*args, **kwargs).decode( + vim_encoding, 'powerline_vim_strtrans_error' + )))) + } + rettype_func['int'] = rettype_func['bytes'] = rettype_func[None] + rettype_func['str'] = rettype_func['bytes'] if str is bytes else rettype_func['unicode'] + + def vim_get_func(f, rettype=None): + '''Return a vim function binding.''' + try: + func = vim.bindeval('function("' + f + '")') + except vim.error: + return None + else: + return rettype_func[rettype](func) +else: + rettype_eval = { + None: getattr(vim, 'eval', None), + 'int': lambda expr: int(vim.eval(expr)), + 'bytes': eval_bytes, + 'unicode': eval_unicode, + } + rettype_eval['str'] = rettype_eval[None] + + class VimFunc(object): + '''Evaluate a vim function using vim.eval(). + + This is a fallback class for older vim versions. + ''' + __slots__ = ('f', 'eval') + + def __init__(self, f, rettype=None): + self.f = f.encode('utf-8') + self.eval = rettype_eval[rettype] + + def __call__(self, *args): + return self.eval(self.f + b'(' + (b','.join(( + python_to_vim(o) for o in args + ))) + b')') + + vim_get_func = VimFunc + + +def vim_get_autoload_func(f, rettype=None): + func = vim_get_func(f) + if not func: + vim.command('runtime! ' + f.replace('#', '/')[:f.rindex('#')] + '.vim') + func = vim_get_func(f) + return func + + +if hasattr(vim, 'Function'): + def vim_func_exists(f): + try: + vim.Function(f) + except ValueError: + return False + else: + return True +else: + def vim_func_exists(f): + try: + return bool(int(vim.eval('exists("*{0}")'.format(f)))) + except vim.error: + return False + + +if type(vim) is object: + vim_get_func = lambda *args, **kwargs: None + + +_getbufvar = vim_get_func('getbufvar') +_vim_exists = vim_get_func('exists', rettype='int') + + +# It may crash on some old vim versions and I do not remember in which patch +# I fixed this crash. +if hasattr(vim, 'vvars') and vim.vvars[str('version')] > 703: + _vim_to_python_types = { + getattr(vim, 'Dictionary', None) or type(vim.bindeval('{}')): + lambda value: dict(( + (_vim_to_python(k), _vim_to_python(v)) + for k, v in value.items() + )), + getattr(vim, 'List', None) or type(vim.bindeval('[]')): + lambda value: [_vim_to_python(item) for item in value], + getattr(vim, 'Function', None) or type(vim.bindeval('function("mode")')): + lambda _: None, + } + + def vim_getvar(varname): + return _vim_to_python(vim.vars[str(varname)]) + + def bufvar_exists(buffer, varname): + buffer = buffer or vim.current.buffer + return varname in buffer.vars + + def vim_getwinvar(segment_info, varname): + return _vim_to_python(segment_info['window'].vars[str(varname)]) + + def vim_global_exists(name): + try: + vim.vars[name] + except KeyError: + return False + else: + return True +else: + _vim_to_python_types = { + dict: (lambda value: dict(((k, _vim_to_python(v)) for k, v in value.items()))), + list: (lambda value: [_vim_to_python(i) for i in value]), + } + + def vim_getvar(varname): + varname = 'g:' + varname + if _vim_exists(varname): + return vim.eval(varname) + else: + raise KeyError(varname) + + def bufvar_exists(buffer, varname): + if not buffer or buffer.number == vim.current.buffer.number: + return int(vim.eval('exists("b:{0}")'.format(varname))) + else: + return int(vim.eval( + 'has_key(getbufvar({0}, ""), {1})'.format(buffer.number, varname) + )) + + def vim_getwinvar(segment_info, varname): + result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname)) + if result == '': + if not int(vim.eval('has_key(getwinvar({0}, ""), "{1}")'.format(segment_info['winnr'], varname))): + raise KeyError(varname) + return result + + def vim_global_exists(name): + return int(vim.eval('exists("g:' + name + '")')) + + +def vim_command_exists(name): + return _vim_exists(':' + name) + + +if sys.version_info < (3,): + getbufvar = _getbufvar +else: + _vim_to_python_types[bytes] = lambda value: value.decode(vim_encoding) + + def getbufvar(*args): + return _vim_to_python(_getbufvar(*args)) + + +_id = lambda value: value + + +def _vim_to_python(value): + return _vim_to_python_types.get(type(value), _id)(value) + + +if hasattr(vim, 'options'): + def vim_getbufoption(info, option): + return _vim_to_python(info['buffer'].options[str(option)]) + + def vim_getoption(option): + return vim.options[str(option)] + + def vim_setoption(option, value): + vim.options[str(option)] = value +else: + def vim_getbufoption(info, option): + return getbufvar(info['bufnr'], '&' + option) + + def vim_getoption(option): + return vim.eval('&g:' + option) + + def vim_setoption(option, value): + vim.command('let &g:{option} = {value}'.format( + option=option, value=python_to_vim(value))) + + +if hasattr(vim, 'tabpages'): + current_tabpage = lambda: vim.current.tabpage + list_tabpages = lambda: vim.tabpages + + def list_tabpage_buffers_segment_info(segment_info): + return ( + {'buffer': window.buffer, 'bufnr': window.buffer.number} + for window in segment_info['tabpage'].windows + ) +else: + class FalseObject(object): + @staticmethod + def __nonzero__(): + return False + + __bool__ = __nonzero__ + + def get_buffer(number): + for buffer in vim.buffers: + if buffer.number == number: + return buffer + raise KeyError(number) + + class WindowVars(object): + __slots__ = ('tabnr', 'winnr') + + def __init__(self, window): + self.tabnr = window.tabnr + self.winnr = window.number + + def __getitem__(self, key): + has_key = vim.eval('has_key(gettabwinvar({0}, {1}, ""), "{2}")'.format(self.tabnr, self.winnr, key)) + if has_key == '0': + raise KeyError + return vim.eval('gettabwinvar({0}, {1}, "{2}")'.format(self.tabnr, self.winnr, key)) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + class Window(FalseObject): + __slots__ = ('tabnr', 'number', '_vars') + + def __init__(self, tabnr, number): + self.tabnr = tabnr + self.number = number + self.vars = WindowVars(self) + + @property + def buffer(self): + return get_buffer(int(vim.eval('tabpagebuflist({0})[{1}]'.format(self.tabnr, self.number - 1)))) + + class Tabpage(FalseObject): + __slots__ = ('number',) + + def __init__(self, number): + self.number = number + + def __eq__(self, tabpage): + if not isinstance(tabpage, Tabpage): + raise NotImplementedError + return self.number == tabpage.number + + @property + def window(self): + return Window(self.number, int(vim.eval('tabpagewinnr({0})'.format(self.number)))) + + def _last_tab_nr(): + return int(vim.eval('tabpagenr("$")')) + + def current_tabpage(): + return Tabpage(int(vim.eval('tabpagenr()'))) + + def list_tabpages(): + return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)] + + class TabBufSegmentInfo(dict): + def __getitem__(self, key): + try: + return super(TabBufSegmentInfo, self).__getitem__(key) + except KeyError: + if key != 'buffer': + raise + else: + buffer = get_buffer(super(TabBufSegmentInfo, self).__getitem__('bufnr')) + self['buffer'] = buffer + return buffer + + def list_tabpage_buffers_segment_info(segment_info): + return ( + TabBufSegmentInfo(bufnr=int(bufnrstr)) + for bufnrstr in vim.eval('tabpagebuflist({0})'.format(segment_info['tabnr'])) + ) + + +class VimEnviron(object): + @staticmethod + def __getitem__(key): + return vim.eval('$' + key) + + @staticmethod + def get(key, default=None): + return vim.eval('$' + key) or default + + @staticmethod + def __setitem__(key, value): + return vim.command( + 'let ${0}="{1}"'.format( + key, + value.replace('"', '\\"') + .replace('\\', '\\\\') + .replace('\n', '\\n') + .replace('\0', '') + ) + ) + + +if sys.version_info < (3,): + def buffer_name(segment_info): + return segment_info['buffer'].name +else: + vim_bufname = vim_get_func('bufname', rettype='bytes') + + def buffer_name(segment_info): + try: + name = segment_info['buffer'].name + except UnicodeDecodeError: + return vim_bufname(segment_info['bufnr']) + else: + return name.encode(segment_info['encoding']) if name else None + + +vim_strtrans = vim_get_func('strtrans', rettype='unicode') + + +def powerline_vim_strtrans_error(e): + if not isinstance(e, UnicodeDecodeError): + raise NotImplementedError + text = vim_strtrans(e.object[e.start:e.end]) + return (text, e.end) + + +codecs.register_error('powerline_vim_strtrans_error', powerline_vim_strtrans_error) + + +did_autocmd = False +buffer_caches = [] + + +def register_buffer_cache(cachedict): + global did_autocmd + global buffer_caches + from powerline.vim import get_default_pycmd, pycmd + if not did_autocmd: + import __main__ + __main__.powerline_on_bwipe = on_bwipe + vim.command('augroup Powerline') + vim.command(' autocmd! BufWipeout * :{pycmd} powerline_on_bwipe()'.format( + pycmd=(pycmd or get_default_pycmd()))) + vim.command('augroup END') + did_autocmd = True + buffer_caches.append(cachedict) + return cachedict + + +def on_bwipe(): + global buffer_caches + bufnr = int(vim.eval('expand("")')) + for cachedict in buffer_caches: + cachedict.pop(bufnr, None) + + +environ = VimEnviron() + + +def create_ruby_dpowerline(): + vim.command(( + ''' + ruby + if $powerline == nil + class Powerline + end + $powerline = Powerline.new + end + ''' + )) diff --git a/powerline-bin/powerline/bindings/vim/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/vim/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..697841e Binary files /dev/null and b/powerline-bin/powerline/bindings/vim/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/vim/autoload/powerline/debug.vim b/powerline-bin/powerline/bindings/vim/autoload/powerline/debug.vim new file mode 100644 index 0000000..244319a --- /dev/null +++ b/powerline-bin/powerline/bindings/vim/autoload/powerline/debug.vim @@ -0,0 +1,20 @@ +python import cProfile +python powerline_pr = cProfile.Profile() + +function powerline#debug#profile_pyeval(s) + python powerline_pr.enable() + try + let ret = pyeval(a:s) + finally + python powerline_pr.disable() + endtry + return ret +endfunction + +function powerline#debug#write_profile(fname) + python import vim + python powerline_pr.dump_stats(vim.eval('a:fname')) + python powerline_pr = cProfile.Profile() +endfunction + +command -nargs=1 -complete=file WriteProfiling :call powerline#debug#write_profile() diff --git a/powerline-bin/powerline/bindings/vim/plugin/powerline.vim b/powerline-bin/powerline/bindings/vim/plugin/powerline.vim new file mode 100644 index 0000000..11ec05e --- /dev/null +++ b/powerline-bin/powerline/bindings/vim/plugin/powerline.vim @@ -0,0 +1,169 @@ +if exists('g:powerline_loaded') + finish +endif +let g:powerline_loaded = 1 + +if exists('g:powerline_pycmd') + let s:pycmd = substitute(g:powerline_pycmd, '\v\C^(py)%[thon](3?)$', '\1\2', '') + if s:pycmd is# 'py' + let s:has_python = has('python') + let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval') + elseif s:pycmd is# 'py3' + let s:has_python = has('python3') + let s:pyeval = 'py3eval' + let s:pyeval = get(g:, 'powerline_pyeval', 'py3eval') + else + if !exists('g:powerline_pyeval') + echohl ErrorMsg + echomsg 'g:powerline_pycmd was set to an unknown values, but g:powerline_pyeval' + echomsg 'was not set. You should either set g:powerline_pycmd to "py3" or "py",' + echomsg 'specify g:powerline_pyeval explicitly or unset both and let powerline' + echomsg 'figure them out.' + echohl None + unlet s:pycmd + finish + endif + let s:pyeval = g:powerline_pyeval + let s:has_python = 1 + endif +elseif has('python') + let s:has_python = 1 + let s:pycmd = 'py' + let s:pyeval = get(g:, 'powerline_pyeval', 'pyeval') +elseif has('python3') + let s:has_python = 1 + let s:pycmd = 'py3' + let s:pyeval = get(g:, 'powerline_pyeval', 'py3eval') +else + let s:has_python = 0 +endif + +if !s:has_python + if !exists('g:powerline_no_python_error') + echohl ErrorMsg + echomsg 'You need vim compiled with Python 2.6, 2.7 or 3.2 and later support' + echomsg 'for Powerline to work. Please consult the documentation for more' + echomsg 'details.' + echohl None + endif + unlet s:has_python + finish +endif +unlet s:has_python + +let s:import_cmd = 'from powerline.vim import VimPowerline' +function s:rcmd(s) + if !exists('s:pystr') + let s:pystr = a:s . "\n" + else + let s:pystr = s:pystr . a:s . "\n" + endif +endfunction +try + let s:can_replace_pyeval = !exists('g:powerline_pyeval') + call s:rcmd('try:') + call s:rcmd(' powerline_appended_path = None') + call s:rcmd(' try:') + call s:rcmd(' '.s:import_cmd.'') + call s:rcmd(' except ImportError:') + call s:rcmd(' import sys, vim') + call s:rcmd(' powerline_appended_path = vim.eval("expand(\":h:h:h:h:h\")")') + call s:rcmd(' sys.path.append(powerline_appended_path)') + call s:rcmd(' '.s:import_cmd.'') + call s:rcmd(' import vim') + call s:rcmd(' powerline_instance = VimPowerline()') + call s:rcmd(' powerline_instance.setup(pyeval=vim.eval("s:pyeval"), pycmd=vim.eval("s:pycmd"), can_replace_pyeval=int(vim.eval("s:can_replace_pyeval")))') + call s:rcmd(' del VimPowerline') + call s:rcmd(' del powerline_instance') + call s:rcmd('except Exception:') + call s:rcmd(' import traceback, sys') + call s:rcmd(' traceback.print_exc(file=sys.stdout)') + call s:rcmd(' raise') + execute s:pycmd s:pystr + unlet s:pystr + let s:launched = 1 +finally + unlet s:can_replace_pyeval + unlet s:import_cmd + if !exists('s:launched') + unlet s:pystr + echohl ErrorMsg + echomsg 'An error occurred while importing powerline module.' + echomsg 'This could be caused by invalid sys.path setting,' + echomsg 'or by an incompatible Python version (powerline requires' + echomsg 'Python 2.6, 2.7 or 3.2 and later to work). Please consult' + echomsg 'the troubleshooting section in the documentation for' + echomsg 'possible solutions.' + if s:pycmd is# 'py' && has('python3') + echomsg 'If powerline on your system is installed for python 3 only you' + echomsg 'should set g:powerline_pycmd to "py3" to make it load correctly.' + endif + echohl None + call s:rcmd('def powerline_troubleshoot():') + call s:rcmd(' import sys') + call s:rcmd(' import vim') + call s:rcmd(' if sys.version_info < (2, 6):') + call s:rcmd(' print("Too old python version: " + sys.version + " (first supported is 2.6)")') + call s:rcmd(' elif sys.version_info[0] == 3 and sys.version_info[1] < 2:') + call s:rcmd(' print("Too old python 3 version: " + sys.version + " (first supported is 3.2)")') + call s:rcmd(' try:') + call s:rcmd(' import powerline') + call s:rcmd(' except ImportError:') + call s:rcmd(' print("Unable to import powerline, is it installed?")') + call s:rcmd(' else:') + call s:rcmd(' if not vim.eval(''expand("")'').startswith("/usr/"):') + call s:rcmd(' import os') + call s:rcmd(' powerline_dir = os.path.realpath(os.path.normpath(powerline.__file__))') + call s:rcmd(' powerline_dir = os.path.dirname(powerline.__file__)') + call s:rcmd(' this_dir = os.path.realpath(os.path.normpath(vim.eval(''expand(":p")'')))') + call s:rcmd(' this_dir = os.path.dirname(this_dir)') " powerline/bindings/vim/plugin + call s:rcmd(' this_dir = os.path.dirname(this_dir)') " powerline/bindings/vim + call s:rcmd(' this_dir = os.path.dirname(this_dir)') " powerline/bindings + call s:rcmd(' this_dir = os.path.dirname(this_dir)') " powerline + call s:rcmd(' if os.path.basename(this_dir) != "powerline":') + call s:rcmd(' print("Check your installation:")') + call s:rcmd(' print("this script is not in powerline[/bindings/vim/plugin] directory,")') + call s:rcmd(' print("neither it is installed system-wide")') + call s:rcmd(' real_powerline_dir = os.path.realpath(powerline_dir)') + call s:rcmd(' real_this_dir = os.path.realpath(this_dir)') + call s:rcmd(' this_dir_par = os.path.dirname(real_this_dir)') + call s:rcmd(' powerline_appended_path = globals().get("powerline_appended_path")') + call s:rcmd(' if powerline_appended_path is not None and this_dir_par != powerline_appended_path:') + call s:rcmd(' print("Check your installation: this script is symlinked somewhere")') + call s:rcmd(' print("where powerline is not present: {0!r} != {1!r}.".format(') + call s:rcmd(' real_this_dir, powerline_appended_path))') + call s:rcmd(' elif real_powerline_dir != real_this_dir:') + call s:rcmd(' print("It appears that you have two powerline versions installed:")') + call s:rcmd(' print("one in " + real_powerline_dir + ", other in " + real_this_dir + ".")') + call s:rcmd(' print("You should remove one of this. Check out troubleshooting section,")') + call s:rcmd(' print("it contains some information about the alternatives.")') + call s:rcmd(' try:') + call s:rcmd(' from powerline.lint import check') + call s:rcmd(' except ImportError:') + call s:rcmd(' print("Failed to import powerline.lint.check, cannot run powerline-lint")') + call s:rcmd(' else:') + call s:rcmd(' try:') + call s:rcmd(' paths = powerline_instance.get_config_paths()') + call s:rcmd(' except NameError:') + call s:rcmd(' pass') + call s:rcmd(' else:') + call s:rcmd(' from powerline.lint.markedjson.error import echoerr') + call s:rcmd(' ee = lambda *args, **kwargs: echoerr(*args, stream=sys.stdout, **kwargs)') + call s:rcmd(' check(paths=paths, echoerr=ee, require_ext="vim")') + call s:rcmd('try:') + call s:rcmd(' powerline_troubleshoot()') + call s:rcmd('finally:') + call s:rcmd(' del powerline_troubleshoot') + execute s:pycmd s:pystr + unlet s:pystr + unlet s:pycmd + unlet s:pyeval + delfunction s:rcmd + finish + else + unlet s:launched + endif + unlet s:pycmd + unlet s:pyeval + delfunction s:rcmd +endtry diff --git a/powerline-bin/powerline/bindings/wm/__init__.py b/powerline-bin/powerline/bindings/wm/__init__.py new file mode 100644 index 0000000..646b701 --- /dev/null +++ b/powerline-bin/powerline/bindings/wm/__init__.py @@ -0,0 +1,85 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re + +from powerline.theme import requires_segment_info +from powerline.lib.shell import run_cmd +from powerline.bindings.wm.awesome import AwesomeThread + + +DEFAULT_UPDATE_INTERVAL = 0.5 + + +conn = None + + +def i3_subscribe(conn, event, callback): + '''Subscribe to i3 workspace event + + :param conn: + Connection returned by :py:func:`get_i3_connection`. + :param str event: + Event to subscribe to, e.g. ``'workspace'``. + :param func callback: + Function to run on event. + ''' + try: + import i3ipc + except ImportError: + import i3 + conn.Subscription(callback, event) + return + else: + pass + + conn.on(event, callback) + + from threading import Thread + + class I3Thread(Thread): + daemon = True + + def __init__(self, conn): + super(I3Thread, self).__init__() + self.__conn = conn + + def run(self): + self.__conn.main() + + thread = I3Thread(conn=conn) + + thread.start() + + +def get_i3_connection(): + '''Return a valid, cached i3 Connection instance + ''' + global conn + if not conn: + try: + import i3ipc + except ImportError: + import i3 as conn + else: + conn = i3ipc.Connection() + return conn + + +XRANDR_OUTPUT_RE = re.compile(r'^(?P[0-9A-Za-z-]+) connected(?P primary)? (?P\d+)x(?P\d+)\+(?P\d+)\+(?P\d+)', re.MULTILINE) + + +def get_connected_xrandr_outputs(pl): + '''Iterate over xrandr outputs + + Outputs are represented by a dictionary with ``name``, ``width``, + ``height``, ``primary``, ``x`` and ``y`` keys. + ''' + return (match.groupdict() for match in XRANDR_OUTPUT_RE.finditer( + run_cmd(pl, ['xrandr', '-q']) + )) + + +wm_threads = { + 'awesome': AwesomeThread, +} diff --git a/powerline-bin/powerline/bindings/wm/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/wm/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5de62c9 Binary files /dev/null and b/powerline-bin/powerline/bindings/wm/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/wm/__pycache__/awesome.cpython-312.pyc b/powerline-bin/powerline/bindings/wm/__pycache__/awesome.cpython-312.pyc new file mode 100644 index 0000000..2f8bc58 Binary files /dev/null and b/powerline-bin/powerline/bindings/wm/__pycache__/awesome.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/wm/awesome.py b/powerline-bin/powerline/bindings/wm/awesome.py new file mode 100644 index 0000000..b6e07f2 --- /dev/null +++ b/powerline-bin/powerline/bindings/wm/awesome.py @@ -0,0 +1,59 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from threading import Thread, Event +from time import sleep +from subprocess import Popen, PIPE + +from powerline import Powerline +from powerline.lib.monotonic import monotonic + + +def read_to_log(pl, client): + for line in client.stdout: + if line: + pl.info(line, prefix='awesome-client') + for line in client.stderr: + if line: + pl.error(line, prefix='awesome-client') + if client.wait(): + pl.error('Client exited with {0}', client.returncode, prefix='awesome') + + +def run(thread_shutdown_event=None, pl_shutdown_event=None, pl_config_loader=None, + interval=None): + powerline = Powerline( + 'wm', + renderer_module='pango_markup', + shutdown_event=pl_shutdown_event, + config_loader=pl_config_loader, + ) + powerline.update_renderer() + + if not thread_shutdown_event: + thread_shutdown_event = powerline.shutdown_event + + while not thread_shutdown_event.is_set(): + # powerline.update_interval may change over time + used_interval = interval or powerline.update_interval + start_time = monotonic() + s = powerline.render(side='right') + request = 'powerline_widget:set_markup(\'' + s.translate({'\'': '\\\'', '\\': '\\\\'}) + '\')\n' + client = Popen(['awesome-client'], shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE) + client.stdin.write(request.encode('utf-8')) + client.stdin.close() + read_to_log(powerline.pl, client) + thread_shutdown_event.wait(max(used_interval - (monotonic() - start_time), 0.1)) + + +class AwesomeThread(Thread): + __slots__ = ('powerline_shutdown_event',) + + def __init__(self, **kwargs): + super(AwesomeThread, self).__init__() + self.powerline_run_kwargs = kwargs + + def run(self): + run(**self.powerline_run_kwargs) diff --git a/powerline-bin/powerline/bindings/zsh/__init__.py b/powerline-bin/powerline/bindings/zsh/__init__.py new file mode 100644 index 0000000..1037bba --- /dev/null +++ b/powerline-bin/powerline/bindings/zsh/__init__.py @@ -0,0 +1,228 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import atexit + +from weakref import WeakValueDictionary, ref + +import zsh + +from powerline.shell import ShellPowerline +from powerline.lib.overrides import parsedotval, parse_override_var +from powerline.lib.unicode import unicode, u +from powerline.lib.encoding import (get_preferred_output_encoding, + get_preferred_environment_encoding) +from powerline.lib.dict import mergeargs + + +used_powerlines = WeakValueDictionary() + + +def shutdown(): + for powerline in tuple(used_powerlines.values()): + powerline.shutdown() + + +def get_var_config(var): + try: + val = zsh.getvalue(var) + if isinstance(val, dict): + return mergeargs([parsedotval((u(k), u(v))) for k, v in val.items()]) + elif isinstance(val, (unicode, str, bytes)): + return mergeargs(parse_override_var(u(val))) + else: + return None + except: + return None + + +class Args(object): + __slots__ = ('last_pipe_status', 'last_exit_code') + ext = ['shell'] + renderer_module = '.zsh' + + @property + def config_override(self): + return get_var_config('POWERLINE_CONFIG_OVERRIDES') + + @property + def theme_override(self): + return get_var_config('POWERLINE_THEME_OVERRIDES') + + @property + def config_path(self): + try: + ret = zsh.getvalue('POWERLINE_CONFIG_PATHS') + except IndexError: + return None + else: + if isinstance(ret, (unicode, str, bytes)): + return [ + path + for path in ret.split((b':' if isinstance(ret, bytes) else ':')) + if path + ] + else: + return ret + + @property + def jobnum(self): + return zsh.getvalue('_POWERLINE_JOBNUM') + + +def string(s): + if type(s) is bytes: + return s.decode(get_preferred_environment_encoding(), 'replace') + else: + return str(s) + + +class Environment(object): + @staticmethod + def __getitem__(key): + try: + return string(zsh.getvalue(key)) + except IndexError as e: + raise KeyError(*e.args) + + @staticmethod + def get(key, default=None): + try: + return string(zsh.getvalue(key)) + except IndexError: + return default + + @staticmethod + def __contains__(key): + try: + zsh.getvalue(key) + return True + except IndexError: + return False + + +if hasattr(getattr(zsh, 'environ', None), '__contains__'): + environ = zsh.environ +else: + environ = Environment() + + +if hasattr(zsh, 'expand') and zsh.expand('${:-}') == '': + zsh_expand = zsh.expand +else: + def zsh_expand(s): + zsh.eval('local _POWERLINE_REPLY="' + s + '"') + ret = zsh.getvalue('_POWERLINE_REPLY') + zsh.setvalue('_POWERLINE_REPLY', None) + return ret + + +class ZshPowerline(ShellPowerline): + def init(self, **kwargs): + super(ZshPowerline, self).init(Args(), **kwargs) + + def precmd(self): + self.args.last_pipe_status = zsh.pipestatus() + self.args.last_exit_code = zsh.last_exit_code() + + def do_setup(self, zsh_globals): + set_prompt(self, 'PS1', 'left', None, above=True) + set_prompt(self, 'RPS1', 'right', None) + set_prompt(self, 'PS2', 'left', 'continuation') + set_prompt(self, 'RPS2', 'right', 'continuation') + set_prompt(self, 'PS3', 'left', 'select') + used_powerlines[id(self)] = self + zsh_globals['_powerline'] = self + + +class Prompt(object): + __slots__ = ('powerline', 'side', 'savedpsvar', 'savedps', 'args', 'theme', 'above', '__weakref__') + + def __init__(self, powerline, side, theme, savedpsvar=None, savedps=None, above=False): + self.powerline = powerline + self.side = side + self.above = above + self.savedpsvar = savedpsvar + self.savedps = savedps + self.args = powerline.args + self.theme = theme + + def __str__(self): + parser_state = u(zsh_expand('${(%):-%_}')) + shortened_path = u(zsh_expand('${(%):-%~}')) + try: + mode = u(zsh.getvalue('_POWERLINE_MODE')) + except IndexError: + mode = None + try: + default_mode = u(zsh.getvalue('_POWERLINE_DEFAULT_MODE')) + except IndexError: + default_mode = None + segment_info = { + 'args': self.args, + 'environ': environ, + 'client_id': 1, + 'local_theme': self.theme, + 'parser_state': parser_state, + 'shortened_path': shortened_path, + 'mode': mode, + 'default_mode': default_mode, + } + try: + zle_rprompt_indent = zsh.getvalue('ZLE_RPROMPT_INDENT') + except IndexError: + zle_rprompt_indent = 1 + r = '' + if self.above: + for line in self.powerline.render_above_lines( + width=zsh.columns() - zle_rprompt_indent, + segment_info=segment_info, + ): + if line: + r += line + '\n' + r += self.powerline.render( + width=zsh.columns(), + side=self.side, + segment_info=segment_info, + mode=mode, + ) + if type(r) is not str: + if type(r) is bytes: + return r.decode(get_preferred_output_encoding(), 'replace') + else: + return r.encode(get_preferred_output_encoding(), 'replace') + return r + + def __del__(self): + if self.savedps: + zsh.setvalue(self.savedpsvar, self.savedps) + self.powerline.shutdown() + + +def set_prompt(powerline, psvar, side, theme, above=False): + try: + savedps = zsh.getvalue(psvar) + except IndexError: + savedps = None + zpyvar = 'ZPYTHON_POWERLINE_' + psvar + prompt = Prompt(powerline, side, theme, psvar, savedps, above) + zsh.setvalue(zpyvar, None) + zsh.set_special_string(zpyvar, prompt) + zsh.setvalue(psvar, '${' + zpyvar + '}') + return ref(prompt) + + +def reload(): + for powerline in tuple(used_powerlines.values()): + powerline.reload() + + +def reload_config(): + for powerline in used_powerlines.values(): + powerline.create_renderer(load_main=True, load_colors=True, load_colorscheme=True, load_theme=True) + + +def setup(zsh_globals): + powerline = ZshPowerline() + powerline.setup(zsh_globals) + atexit.register(shutdown) diff --git a/powerline-bin/powerline/bindings/zsh/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/bindings/zsh/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1d60b45 Binary files /dev/null and b/powerline-bin/powerline/bindings/zsh/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/bindings/zsh/powerline.zsh b/powerline-bin/powerline/bindings/zsh/powerline.zsh new file mode 100644 index 0000000..5d7cb28 --- /dev/null +++ b/powerline-bin/powerline/bindings/zsh/powerline.zsh @@ -0,0 +1,216 @@ +local _POWERLINE_SOURCED="$0:A" + +_powerline_columns_fallback() { + if which stty &>/dev/null ; then + local cols="$(stty size 2>/dev/null)" + if ! test -z "$cols" ; then + echo "${cols#* }" + return 0 + fi + fi + echo 0 + return 0 +} + +_powerline_append_precmd_function() { + if test -z "${precmd_functions[(re)$1]}" ; then + precmd_functions+=( $1 ) + fi +} + +integer -g _POWERLINE_JOBNUM=0 + +_powerline_tmux_pane() { + local -x TMUX="$_POWERLINE_TMUX" + echo "${TMUX_PANE:-`tmux display -p "#D"`}" | tr -d ' %' +} + +_powerline_tmux_pane() { + local -x TMUX="$_POWERLINE_TMUX" + echo "${TMUX_PANE:-`tmux display -p "#D"`}" | tr -d ' %' +} + +_powerline_init_tmux_support() { + emulate -L zsh + if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then + # TMUX variable may be unset to create new tmux session inside this one + typeset -g _POWERLINE_TMUX="$TMUX" + + function -g _powerline_tmux_setenv() { + emulate -L zsh + local -x TMUX="$_POWERLINE_TMUX" + tmux setenv -g TMUX_"$1"_$(_powerline_tmux_pane) "$2" + tmux refresh -S + } + + function -g _powerline_tmux_set_pwd() { + _powerline_tmux_setenv PWD "$PWD" + } + + function -g _powerline_tmux_set_columns() { + _powerline_tmux_setenv COLUMNS "${COLUMNS:-$(_powerline_columns_fallback)}" + } + + chpwd_functions+=( _powerline_tmux_set_pwd ) + trap '_powerline_tmux_set_columns' SIGWINCH + _powerline_tmux_set_columns + _powerline_tmux_set_pwd + fi +} + +_powerline_init_modes_support() { + emulate -L zsh + + test -z "$ZSH_VERSION" && return 0 + + local -a vs + vs=( ${(s:.:)ZSH_VERSION} ) + + # Mode support requires >=zsh-4.3.11 + if (( vs[1] < 4 || (vs[1] == 4 && (vs[2] < 3 || (vs[2] == 3 && vs[3] < 11))) )) ; then + return 0 + fi + + function -g _powerline_get_main_keymap_name() { + REPLY="${${(Q)${${(z)${"$(bindkey -lL main)"}}[3]}}:-.safe}" + } + + function -g _powerline_set_true_keymap_name() { + typeset -g _POWERLINE_MODE="${1}" + local plm_bk="$(bindkey -lL ${_POWERLINE_MODE})" + if [[ $plm_bk = 'bindkey -A'* ]] ; then + _powerline_set_true_keymap_name ${(Q)${${(z)plm_bk}[3]}} + fi + } + + function -g _powerline_zle_keymap_select() { + _powerline_set_true_keymap_name $KEYMAP + zle reset-prompt + test -z "$_POWERLINE_SAVE_WIDGET" || zle $_POWERLINE_SAVE_WIDGET + } + + function -g _powerline_set_main_keymap_name() { + local REPLY + _powerline_get_main_keymap_name + _powerline_set_true_keymap_name "$REPLY" + } + + _powerline_add_widget zle-keymap-select _powerline_zle_keymap_select + _powerline_set_main_keymap_name + + if [[ "$_POWERLINE_MODE" != vi* ]] ; then + typeset -g _POWERLINE_DEFAULT_MODE="$_POWERLINE_MODE" + fi + + _powerline_append_precmd_function _powerline_set_main_keymap_name +} + +_powerline_set_jobnum() { + # If you are wondering why I am not using the same code as I use for bash + # ($(jobs|wc -l)): consider the following test: + # echo abc | less + # + # . This way jobs will print + # [1] + done echo abc | + # suspended less -M + # ([ is in first column). You see: any line counting thingie will return + # wrong number of jobs. You need to filter the lines first. Or not use + # jobs built-in at all. + integer -g _POWERLINE_JOBNUM=${(%):-%j} +} + +_powerline_update_counter() { + zpython '_powerline.precmd()' +} + +_powerline_setup_prompt() { + emulate -L zsh + + _powerline_append_precmd_function _powerline_set_jobnum + + typeset -g VIRTUAL_ENV_DISABLE_PROMPT=1 + + if test -z "${POWERLINE_NO_ZSH_ZPYTHON}" && { zmodload libzpython || zmodload zsh/zpython } &>/dev/null ; then + _powerline_append_precmd_function _powerline_update_counter + zpython 'from powerline.bindings.zsh import setup as _powerline_setup' + zpython '_powerline_setup(globals())' + zpython 'del _powerline_setup' + powerline-reload() { + zpython 'from powerline.bindings.zsh import reload as _powerline_reload' + zpython '_powerline_reload()' + zpython 'del _powerline_reload' + } + powerline-reload-config() { + zpython 'from powerline.bindings.zsh import reload_config as _powerline_reload_config' + zpython '_powerline_reload_config()' + zpython 'del _powerline_reload_config' + } + else + if test -z "${POWERLINE_COMMAND}" ; then + typeset -g POWERLINE_COMMAND="$($POWERLINE_CONFIG_COMMAND shell command)" + fi + + local add_args='-r .zsh' + add_args+=' --last-exit-code=$?' + add_args+=' --last-pipe-status="$pipestatus"' + add_args+=' --renderer-arg="client_id=$$"' + add_args+=' --renderer-arg="shortened_path=${(%):-%~}"' + add_args+=' --jobnum=$_POWERLINE_JOBNUM' + add_args+=' --renderer-arg="mode=$_POWERLINE_MODE"' + add_args+=' --renderer-arg="default_mode=$_POWERLINE_DEFAULT_MODE"' + local new_args_2=' --renderer-arg="parser_state=${(%%):-%_}"' + new_args_2+=' --renderer-arg="local_theme=continuation"' + local add_args_3=$add_args' --renderer-arg="local_theme=select"' + local add_args_2=$add_args$new_args_2 + add_args+=' --width=$(( ${COLUMNS:-$(_powerline_columns_fallback)} - ${ZLE_RPROMPT_INDENT:-1} ))' + local add_args_r2=$add_args$new_args_2 + typeset -g PS1='$("$POWERLINE_COMMAND" $=POWERLINE_COMMAND_ARGS shell aboveleft '$add_args')' + typeset -g RPS1='$("$POWERLINE_COMMAND" $=POWERLINE_COMMAND_ARGS shell right '$add_args')' + typeset -g PS2='$("$POWERLINE_COMMAND" $=POWERLINE_COMMAND_ARGS shell left '$add_args_2')' + typeset -g RPS2='$("$POWERLINE_COMMAND" $=POWERLINE_COMMAND_ARGS shell right '$add_args_r2')' + typeset -g PS3='$("$POWERLINE_COMMAND" $=POWERLINE_COMMAND_ARGS shell left '$add_args_3')' + fi +} + +_powerline_add_widget() { + local widget="$1" + local function="$2" + local old_widget_command="$(zle -l -L $widget)" + if [[ "$old_widget_command" = "zle -N $widget $function" ]] ; then + return 0 + elif [[ -z "$old_widget_command" ]] ; then + zle -N $widget $function + else + local save_widget="_powerline_save_$widget" + local -i i=0 + while ! test -z "$(zle -l -L $save_widget)" ; do + save_widget="${save_widget}_$i" + (( i++ )) + done + # If widget was defined with `zle -N widget` (without `function` + # argument) then this function will be handy. + eval "function $save_widget() { emulate -L zsh; $widget \$@ }" + eval "${old_widget_command/$widget/$save_widget}" + zle -N $widget $function + typeset -g _POWERLINE_SAVE_WIDGET="$save_widget" + fi +} + +if test -z "${POWERLINE_CONFIG_COMMAND}" ; then + if which powerline-config >/dev/null ; then + typeset -g POWERLINE_CONFIG_COMMAND=powerline-config + else + typeset -g POWERLINE_CONFIG_COMMAND="${_POWERLINE_SOURCED:h:h:h:h}/scripts/powerline-config" + fi +fi + +setopt promptpercent +setopt promptsubst + +if "${POWERLINE_CONFIG_COMMAND}" shell --shell=zsh uses prompt ; then + _powerline_setup_prompt + _powerline_init_modes_support +fi +if "${POWERLINE_CONFIG_COMMAND}" shell --shell=zsh uses tmux ; then + _powerline_init_tmux_support +fi diff --git a/powerline-bin/powerline/colorscheme.py b/powerline-bin/powerline/colorscheme.py new file mode 100644 index 0000000..66416b5 --- /dev/null +++ b/powerline-bin/powerline/colorscheme.py @@ -0,0 +1,147 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from copy import copy + +from powerline.lib.unicode import unicode + + +DEFAULT_MODE_KEY = None +ATTR_BOLD = 1 +ATTR_ITALIC = 2 +ATTR_UNDERLINE = 4 + + +def get_attrs_flag(attrs): + '''Convert an attribute array to a renderer flag.''' + attrs_flag = 0 + if 'bold' in attrs: + attrs_flag |= ATTR_BOLD + if 'italic' in attrs: + attrs_flag |= ATTR_ITALIC + if 'underline' in attrs: + attrs_flag |= ATTR_UNDERLINE + return attrs_flag + + +def pick_gradient_value(grad_list, gradient_level): + '''Given a list of colors and gradient percent, return a color that should be used. + + Note: gradient level is not checked for being inside [0, 100] interval. + ''' + return grad_list[int(round(gradient_level * (len(grad_list) - 1) / 100))] + + +class Colorscheme(object): + def __init__(self, colorscheme_config, colors_config): + '''Initialize a colorscheme.''' + self.colors = {} + self.gradients = {} + + self.groups = colorscheme_config['groups'] + self.translations = colorscheme_config.get('mode_translations', {}) + + # Create a dict of color tuples with both a cterm and hex value + for color_name, color in colors_config['colors'].items(): + try: + self.colors[color_name] = (color[0], int(color[1], 16)) + except TypeError: + self.colors[color_name] = (color, cterm_to_hex[color]) + + # Create a dict of gradient names with two lists: for cterm and hex + # values. Two lists in place of one list of pairs were chosen because + # true colors allow more precise gradients. + for gradient_name, gradient in colors_config['gradients'].items(): + if len(gradient) == 2: + self.gradients[gradient_name] = ( + (gradient[0], [int(color, 16) for color in gradient[1]])) + else: + self.gradients[gradient_name] = ( + (gradient[0], [cterm_to_hex[color] for color in gradient[0]])) + + def get_gradient(self, gradient, gradient_level): + if gradient in self.gradients: + return tuple((pick_gradient_value(grad_list, gradient_level) for grad_list in self.gradients[gradient])) + else: + return self.colors[gradient] + + def get_group_props(self, mode, trans, group, translate_colors=True): + if isinstance(group, (str, unicode)): + try: + group_props = trans['groups'][group] + except KeyError: + try: + group_props = self.groups[group] + except KeyError: + return None + else: + return self.get_group_props(mode, trans, group_props, True) + else: + return self.get_group_props(mode, trans, group_props, False) + else: + if translate_colors: + group_props = copy(group) + try: + ctrans = trans['colors'] + except KeyError: + pass + else: + for key in ('fg', 'bg'): + try: + group_props[key] = ctrans[group_props[key]] + except KeyError: + pass + return group_props + else: + return group + + def get_highlighting(self, groups, mode, gradient_level=None): + trans = self.translations.get(mode, {}) + for group in groups: + group_props = self.get_group_props(mode, trans, group) + if group_props: + break + else: + raise KeyError('Highlighting groups not found in colorscheme: ' + ', '.join(groups)) + + if gradient_level is None: + pick_color = self.colors.__getitem__ + else: + pick_color = lambda gradient: self.get_gradient(gradient, gradient_level) + + return { + 'fg': pick_color(group_props['fg']), + 'bg': pick_color(group_props['bg']), + 'attrs': get_attrs_flag(group_props.get('attrs', [])), + } + + +# 0 1 2 3 4 5 6 7 8 9 +cterm_to_hex = ( + 0x000000, 0xc00000, 0x008000, 0x804000, 0x0000c0, 0xc000c0, 0x008080, 0xc0c0c0, 0x808080, 0xff6060, # 0 + 0x00ff00, 0xffff00, 0x8080ff, 0xff40ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, 0x000087, 0x0000af, # 1 + 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, # 2 + 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, # 3 + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, # 4 + 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, # 5 + 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, # 6 + 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, # 7 + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, # 8 + 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, # 9 + 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, # 10 + 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, # 11 + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, # 12 + 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, # 13 + 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, # 14 + 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, # 15 + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, # 16 + 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, # 17 + 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, # 18 + 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, # 19 + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, # 20 + 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, # 21 + 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, # 22 + 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, # 23 + 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, # 24 + 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee # 25 +) diff --git a/powerline-bin/powerline/commands/__init__.py b/powerline-bin/powerline/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/commands/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/commands/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e49d962 Binary files /dev/null and b/powerline-bin/powerline/commands/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/commands/__pycache__/config.cpython-312.pyc b/powerline-bin/powerline/commands/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..b772883 Binary files /dev/null and b/powerline-bin/powerline/commands/__pycache__/config.cpython-312.pyc differ diff --git a/powerline-bin/powerline/commands/__pycache__/daemon.cpython-312.pyc b/powerline-bin/powerline/commands/__pycache__/daemon.cpython-312.pyc new file mode 100644 index 0000000..5cef3f1 Binary files /dev/null and b/powerline-bin/powerline/commands/__pycache__/daemon.cpython-312.pyc differ diff --git a/powerline-bin/powerline/commands/__pycache__/lemonbar.cpython-312.pyc b/powerline-bin/powerline/commands/__pycache__/lemonbar.cpython-312.pyc new file mode 100644 index 0000000..a5f8989 Binary files /dev/null and b/powerline-bin/powerline/commands/__pycache__/lemonbar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/commands/__pycache__/lint.cpython-312.pyc b/powerline-bin/powerline/commands/__pycache__/lint.cpython-312.pyc new file mode 100644 index 0000000..08211fd Binary files /dev/null and b/powerline-bin/powerline/commands/__pycache__/lint.cpython-312.pyc differ diff --git a/powerline-bin/powerline/commands/__pycache__/main.cpython-312.pyc b/powerline-bin/powerline/commands/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..e4132f3 Binary files /dev/null and b/powerline-bin/powerline/commands/__pycache__/main.cpython-312.pyc differ diff --git a/powerline-bin/powerline/commands/config.py b/powerline-bin/powerline/commands/config.py new file mode 100644 index 0000000..06c6436 --- /dev/null +++ b/powerline-bin/powerline/commands/config.py @@ -0,0 +1,109 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (division, absolute_import, print_function) + +import argparse + +import powerline.bindings.config as config + + +class StrFunction(object): + def __init__(self, function, name=None): + self.name = name or function.__name__ + self.function = function + + def __call__(self, *args, **kwargs): + self.function(*args, **kwargs) + + def __str__(self): + return self.name + + +TMUX_ACTIONS = { + 'source': StrFunction(config.source_tmux_files, 'source'), + 'setenv': StrFunction(config.init_tmux_environment, 'setenv'), + 'setup': StrFunction(config.tmux_setup, 'setup'), +} + + +SHELL_ACTIONS = { + 'command': StrFunction(config.shell_command, 'command'), + 'uses': StrFunction(config.uses), +} + + +class ConfigArgParser(argparse.ArgumentParser): + def parse_args(self, *args, **kwargs): + ret = super(ConfigArgParser, self).parse_args(*args, **kwargs) + if not hasattr(ret, 'function'): + # In Python-3* `powerline-config` (without arguments) raises + # AttributeError. I have not found any standard way to display same + # error message as in Python-2*. + self.error('too few arguments') + return ret + + +def get_argparser(ArgumentParser=ConfigArgParser): + parser = ArgumentParser(description='Script used to obtain powerline configuration.') + parser.add_argument( + '-p', '--config-path', action='append', metavar='PATH', + help='Path to configuration directory. If it is present ' + 'then configuration files will only be seeked in the provided path. ' + 'May be provided multiple times to search in a list of directories.' + ) + subparsers = parser.add_subparsers() + tmux_parser = subparsers.add_parser('tmux', help='Tmux-specific commands') + tmux_parser.add_argument( + 'function', + choices=tuple(TMUX_ACTIONS.values()), + metavar='ACTION', + type=(lambda v: TMUX_ACTIONS.get(v)), + help='If action is `source\' then version-specific tmux configuration ' + 'files are sourced, if it is `setenv\' then special ' + '(prefixed with `_POWERLINE\') tmux global environment variables ' + 'are filled with data from powerline configuration. ' + 'Action `setup\' is just doing `setenv\' then `source\'.' + ) + tpg = tmux_parser.add_mutually_exclusive_group() + tpg.add_argument( + '-s', '--source', action='store_true', default=None, + help='When using `setup\': always use configuration file sourcing. ' + 'By default this is determined automatically based on tmux ' + 'version: this is the default for tmux 1.8 and below.', + ) + tpg.add_argument( + '-n', '--no-source', action='store_false', dest='source', default=None, + help='When using `setup\': in place of sourcing directly execute ' + 'configuration files. That is, read each needed ' + 'powerline-specific configuration file, substitute ' + '`$_POWERLINE_…\' variables with appropriate values and run ' + '`tmux config line\'. This is the default behaviour for ' + 'tmux 1.9 and above.' + ) + + shell_parser = subparsers.add_parser('shell', help='Shell-specific commands') + shell_parser.add_argument( + 'function', + choices=tuple(SHELL_ACTIONS.values()), + type=(lambda v: SHELL_ACTIONS.get(v)), + metavar='ACTION', + help='If action is `command\' then preferred powerline command is ' + 'output, if it is `uses\' then powerline-config script will exit ' + 'with 1 if specified component is disabled and 0 otherwise.', + ) + shell_parser.add_argument( + 'component', + nargs='?', + choices=('tmux', 'prompt'), + metavar='COMPONENT', + help='Only applicable for `uses\' subcommand: makes `powerline-config\' ' + 'exit with 0 if specific component is enabled and with 1 otherwise. ' + '`tmux\' component stands for tmux bindings ' + '(e.g. those that notify tmux about current directory changes), ' + '`prompt\' component stands for shell prompt.' + ) + shell_parser.add_argument( + '-s', '--shell', + metavar='SHELL', + help='Shell for which query is run', + ) + return parser diff --git a/powerline-bin/powerline/commands/daemon.py b/powerline-bin/powerline/commands/daemon.py new file mode 100644 index 0000000..7e8c8ab --- /dev/null +++ b/powerline-bin/powerline/commands/daemon.py @@ -0,0 +1,24 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (division, absolute_import, print_function) + +import argparse + + +def get_argparser(ArgumentParser=argparse.ArgumentParser): + parser = ArgumentParser(description='Daemon that improves powerline performance.') + parser.add_argument( + '--quiet', '-q', action='store_true', + help='Without other options: do not complain about already running ' + 'powerline-daemon instance. ' + 'Will still exit with 1. ' + 'With `--kill\' and `--replace\': do not show any messages. ' + 'With `--foreground\': ignored. ' + 'Does not silence exceptions in any case.' + ) + parser.add_argument('--socket', '-s', help='Specify socket which will be used for connecting to daemon.') + exclusive_group = parser.add_mutually_exclusive_group() + exclusive_group.add_argument('--kill', '-k', action='store_true', help='Kill an already running instance.') + replace_group = exclusive_group.add_argument_group() + replace_group.add_argument('--foreground', '-f', action='store_true', help='Run in the foreground (don’t daemonize).') + replace_group.add_argument('--replace', '-r', action='store_true', help='Replace an already running instance.') + return parser diff --git a/powerline-bin/powerline/commands/lemonbar.py b/powerline-bin/powerline/commands/lemonbar.py new file mode 100644 index 0000000..547c52c --- /dev/null +++ b/powerline-bin/powerline/commands/lemonbar.py @@ -0,0 +1,35 @@ +# vim:fileencoding=utf-8:noet +# WARNING: using unicode_literals causes errors in argparse +from __future__ import (division, absolute_import, print_function) + +import argparse + + +def get_argparser(ArgumentParser=argparse.ArgumentParser): + parser = ArgumentParser( + description='Powerline BAR bindings.' + ) + parser.add_argument( + '--i3', action='store_true', + help='Subscribe for i3 events.' + ) + parser.add_argument( + '--height', default='', + metavar='PIXELS', help='Bar height.' + ) + parser.add_argument( + '--interval', '-i', + type=float, default=0.5, + metavar='SECONDS', help='Refresh interval.' + ) + parser.add_argument( + '--bar-command', '-C', + default='lemonbar', + metavar='CMD', help='Name of the lemonbar executable to use.' + ) + parser.add_argument( + 'args', nargs=argparse.REMAINDER, + help='Extra arguments for lemonbar. Should be preceded with ``--`` ' + 'argument in order not to be confused with script own arguments.' + ) + return parser diff --git a/powerline-bin/powerline/commands/lint.py b/powerline-bin/powerline/commands/lint.py new file mode 100644 index 0000000..8961a65 --- /dev/null +++ b/powerline-bin/powerline/commands/lint.py @@ -0,0 +1,21 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (division, absolute_import, print_function) + +import argparse + + +def get_argparser(ArgumentParser=argparse.ArgumentParser): + parser = ArgumentParser(description='Powerline configuration checker.') + parser.add_argument( + '-p', '--config-path', action='append', metavar='PATH', + help='Paths where configuration should be checked, in order. You must ' + 'supply all paths necessary for powerline to work, ' + 'checking partial (e.g. only user overrides) configuration ' + 'is not supported.' + ) + parser.add_argument( + '-d', '--debug', action='store_const', const=True, + help='Display additional information. Used for debugging ' + '`powerline-lint\' itself, not for debugging configuration.' + ) + return parser diff --git a/powerline-bin/powerline/commands/main.py b/powerline-bin/powerline/commands/main.py new file mode 100644 index 0000000..366eba2 --- /dev/null +++ b/powerline-bin/powerline/commands/main.py @@ -0,0 +1,190 @@ +# vim:fileencoding=utf-8:noet +# WARNING: using unicode_literals causes errors in argparse +from __future__ import (division, absolute_import, print_function) + +import argparse +import sys + +from itertools import chain + +from powerline.lib.overrides import parsedotval, parse_override_var +from powerline.lib.dict import mergeargs +from powerline.lib.encoding import get_preferred_arguments_encoding +from powerline.lib.unicode import u, unicode +from powerline.bindings.wm import wm_threads + + +if sys.version_info < (3,): + encoding = get_preferred_arguments_encoding() + + def arg_to_unicode(s): + return unicode(s, encoding, 'replace') if not isinstance(s, unicode) else s # NOQA +else: + def arg_to_unicode(s): + return s + + +def finish_args(parser, environ, args, is_daemon=False): + '''Do some final transformations + + Transforms ``*_override`` arguments into dictionaries, adding overrides from + environment variables. Transforms ``renderer_arg`` argument into dictionary + as well, but only if it is true. + + :param dict environ: + Environment from which additional overrides should be taken from. + :param args: + Arguments object returned by + :py:meth:`argparse.ArgumentParser.parse_args`. Will be modified + in-place. + + :return: Object received as second (``args``) argument. + ''' + args.config_override = mergeargs(chain( + parse_override_var(environ.get('POWERLINE_CONFIG_OVERRIDES', '')), + (parsedotval(v) for v in args.config_override or ()), + )) + args.theme_override = mergeargs(chain( + parse_override_var(environ.get('POWERLINE_THEME_OVERRIDES', '')), + (parsedotval(v) for v in args.theme_override or ()), + )) + if args.renderer_arg: + args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg), remove=True) + if 'pane_id' in args.renderer_arg: + if isinstance(args.renderer_arg['pane_id'], (bytes, unicode)): + try: + args.renderer_arg['pane_id'] = int(args.renderer_arg['pane_id'].lstrip(' %')) + except ValueError: + pass + if 'client_id' not in args.renderer_arg: + args.renderer_arg['client_id'] = args.renderer_arg['pane_id'] + args.config_path = ( + [path for path in environ.get('POWERLINE_CONFIG_PATHS', '').split(':') if path] + + (args.config_path or []) + ) + if args.ext[0].startswith('wm.'): + if not is_daemon: + parser.error('WM bindings must be used with daemon only') + elif args.ext[0][3:] not in wm_threads: + parser.error('WM binding not found') + elif not args.side: + parser.error('expected one argument') + return args + + +def int_or_sig(s): + if s.startswith('sig'): + return u(s) + else: + return int(s) + + +def get_argparser(ArgumentParser=argparse.ArgumentParser): + parser = ArgumentParser(description='Powerline prompt and statusline script.') + parser.add_argument( + 'ext', nargs=1, + help='Extension: application for which powerline command is launched ' + '(usually `shell\' or `tmux\'). Also supports `wm.\' extensions: ' + + ', '.join(('`wm.' + key + '\'' for key in wm_threads.keys())) + '.' + ) + parser.add_argument( + 'side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'), + help='Side: `left\' and `right\' represent left and right side ' + 'respectively, `above\' emits lines that are supposed to be printed ' + 'just above the prompt and `aboveleft\' is like concatenating ' + '`above\' with `left\' with the exception that only one Python ' + 'instance is used in this case. May be omitted for `wm.*\' extensions.' + ) + parser.add_argument( + '-r', '--renderer-module', metavar='MODULE', type=str, + help='Renderer module. Usually something like `.bash\' or `.zsh\' ' + '(with leading dot) which is `powerline.renderers.{ext}{MODULE}\', ' + 'may also be full module name (must contain at least one dot or ' + 'end with a dot in case it is top-level module) or ' + '`powerline.renderers\' submodule (in case there are no dots).' + ) + parser.add_argument( + '-w', '--width', type=int, + help='Maximum prompt with. Triggers truncation of some segments.' + ) + parser.add_argument( + '--last-exit-code', metavar='INT', type=int_or_sig, + help='Last exit code.' + ) + parser.add_argument( + '--last-pipe-status', metavar='LIST', default='', + type=lambda s: [int_or_sig(status) for status in s.split()], + help='Like above, but is supposed to contain space-separated array ' + 'of statuses, representing exit statuses of commands in one pipe.' + ) + parser.add_argument( + '--jobnum', metavar='INT', type=int, + help='Number of jobs.' + ) + parser.add_argument( + '-c', '--config-override', metavar='KEY.KEY=VALUE', type=arg_to_unicode, + action='append', + help='Configuration overrides for `config.json\'. Is translated to a ' + 'dictionary and merged with the dictionary obtained from actual ' + 'JSON configuration: KEY.KEY=VALUE is translated to ' + '`{"KEY": {"KEY": VALUE}}\' and then merged recursively. ' + 'VALUE may be any JSON value, values that are not ' + '`null\', `true\', `false\', start with digit, `{\', `[\' ' + 'are treated like strings. If VALUE is omitted ' + 'then corresponding key is removed.' + ) + parser.add_argument( + '-t', '--theme-override', metavar='THEME.KEY.KEY=VALUE', type=arg_to_unicode, + action='append', + help='Like above, but theme-specific. THEME should point to ' + 'an existing and used theme to have any effect, but it is fine ' + 'to use any theme here.' + ) + parser.add_argument( + '-R', '--renderer-arg', + metavar='KEY=VAL', type=arg_to_unicode, action='append', + help='Like above, but provides argument for renderer. Is supposed ' + 'to be used only by shell bindings to provide various data like ' + 'last-exit-code or last-pipe-status (they are not using ' + '`--renderer-arg\' for historical resons: `--renderer-arg\' ' + 'was added later).' + ) + parser.add_argument( + '-p', '--config-path', action='append', metavar='PATH', + help='Path to configuration directory. If it is present then ' + 'configuration files will only be seeked in the provided path. ' + 'May be provided multiple times to search in a list of directories.' + ) + parser.add_argument( + '--socket', metavar='ADDRESS', type=str, + help='Socket address to use in daemon clients. Is always UNIX domain ' + 'socket on linux and file socket on Mac OS X. Not used here, ' + 'present only for compatibility with other powerline clients. ' + 'This argument must always be the first one and be in a form ' + '`--socket ADDRESS\': no `=\' or short form allowed ' + '(in other powerline clients, not here).' + ) + return parser + + +def write_output(args, powerline, segment_info, write): + if args.renderer_arg: + segment_info.update(args.renderer_arg) + if args.side.startswith('above'): + for line in powerline.render_above_lines( + width=args.width, + segment_info=segment_info, + mode=segment_info.get('mode', None), + ): + if line: + write(line + '\n') + args.side = args.side[len('above'):] + + if args.side: + rendered = powerline.render( + width=args.width, + side=args.side, + segment_info=segment_info, + mode=segment_info.get('mode', None), + ) + write(rendered) diff --git a/powerline-bin/powerline/config.py b/powerline-bin/powerline/config.py new file mode 100644 index 0000000..edcf921 --- /dev/null +++ b/powerline-bin/powerline/config.py @@ -0,0 +1,10 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + + +POWERLINE_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BINDINGS_DIRECTORY = os.path.join(POWERLINE_ROOT, 'powerline', 'bindings') +TMUX_CONFIG_DIRECTORY = os.path.join(BINDINGS_DIRECTORY, 'tmux') +DEFAULT_SYSTEM_CONFIG_DIR = None diff --git a/powerline-bin/powerline/config_files/colors.json b/powerline-bin/powerline/config_files/colors.json new file mode 100644 index 0000000..1564f18 --- /dev/null +++ b/powerline-bin/powerline/config_files/colors.json @@ -0,0 +1,124 @@ +{ + "colors": { + "black": 16, + "white": 231, + + "green": 2, + "darkestgreen": 22, + "darkgreen": 28, + "mediumgreen": 70, + "brightgreen": 148, + + "darkestcyan": 23, + "darkcyan": 74, + "mediumcyan": 117, + "brightcyan": 159, + + "darkestblue": 24, + "darkblue": 31, + + "red": 1, + "darkestred": 52, + "darkred": 88, + "mediumred": 124, + "brightred": 160, + "brightestred": 196, + + "darkestpurple": 55, + "mediumpurple": 98, + "brightpurple": 189, + + "darkorange": 94, + "mediumorange": 166, + "brightorange": 208, + "brightestorange": 214, + + "yellow": 11, + "brightyellow": 220, + + "gray0": 233, + "gray1": 235, + "gray2": 236, + "gray3": 239, + "gray4": 240, + "gray5": 241, + "gray6": 244, + "gray7": 245, + "gray8": 247, + "gray9": 250, + "gray10": 252, + + "gray11": 234, + "gray90": 254, + + "gray70": [249, "b3b3b3"], + + "lightyellowgreen": 106, + "gold3": 178, + "orangered": 202, + + "steelblue": 67, + "darkorange3": 166, + "skyblue1": 117, + "khaki1": 228, + + "solarized:base03": [8, "002b36"], + "solarized:base02": [0, "073642"], + "solarized:base01": [10, "586e75"], + "solarized:base00": [11, "657b83"], + "solarized:base0": [12, "839496"], + "solarized:base1": [14, "93a1a1"], + "solarized:base2": [7, "eee8d5"], + "solarized:base3": [15, "fdf6e3"], + "solarized:yellow": [3, "b58900"], + "solarized:orange": [9, "cb4b16"], + "solarized:red": [1, "dc322f"], + "solarized:magenta": [5, "d33682"], + "solarized:violet": [13, "6c71c4"], + "solarized:blue": [4, "268bd2"], + "solarized:cyan": [6, "2aa198"], + "solarized:green": [2, "859900"] + }, + "gradients": { + "dark_GREEN_Orange_red": [ + [22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 94, 94, 94, 94, 94, 94, 94, 88, 52], + ["006000", "006000", "006000", "006000", "006000", "006000", "006000", "006000", "006000", "036000", "076000", "0a6000", "0d6000", "106000", "126000", "146000", "166000", "186000", "1a6000", "1b6000", "1d6000", "1e6000", "206000", "216000", "236000", "246000", "256000", "266000", "286000", "296000", "2a6000", "2b6000", "2c6100", "2d6100", "2f6100", "306100", "316100", "326100", "336100", "346100", "356100", "366100", "376100", "386100", "386100", "396100", "3a6100", "3b6100", "3c6100", "3d6100", "3e6100", "3f6100", "406100", "406100", "416100", "426000", "436000", "446000", "456000", "456000", "466000", "476000", "486000", "496000", "496000", "4a6000", "4b6000", "4c6000", "4d6000", "4d6000", "4e6000", "4f6000", "506000", "506000", "516000", "526000", "536000", "536000", "546000", "556000", "566000", "566000", "576000", "586000", "596000", "596000", "5a6000", "5d6000", "616000", "646000", "686000", "6b6000", "6f6000", "726000", "766000", "796000", "7d6000", "806000", "7e5500", "6f3105", "5d0001"] + ], + "GREEN_Orange_red": [ + [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1], + ["005f00", "015f00", "025f00", "035f00", "045f00", "055f00", "065f00", "075f00", "085f00", "095f00", "0b5f00", "0c5f00", "0d5f00", "0e5f00", "0f5f00", "105f00", "115f00", "125f00", "135f00", "145f00", "165f00", "175f00", "185f00", "195f00", "1a5f00", "1b5f00", "1c5f00", "1d5f00", "1e5f00", "1f5f00", "215f00", "225f00", "235f00", "245f00", "255f00", "265f00", "275f00", "285f00", "295f00", "2a5f00", "2c5f00", "2d5f00", "2e5f00", "2f5f00", "305f00", "315f00", "325f00", "335f00", "345f00", "355f00", "375f00", "385f00", "395f00", "3a5f00", "3b5f00", "3c5f00", "3d5f00", "3e5f00", "3f5f00", "415f00", "425f00", "435f00", "445f00", "455f00", "465f00", "475f00", "485f00", "495f00", "4a5f00", "4c5f00", "4d5f00", "4e5f00", "4f5f00", "505f00", "515f00", "525f00", "535f00", "545f00", "555f00", "575f00", "585f00", "595f00", "5a5f00", "5b5f00", "5c5f00", "5d5f00", "5e5f00", "615f00", "655f00", "685f00", "6c5f00", "6f5f00", "735f00", "765f00", "7a5f00", "7d5f00", "815f00", "845f00", "815200", "702900"] + ], + "green_yellow_red": [ + [190, 184, 178, 172, 166, 160], + ["8ae71c", "8ce71c", "8fe71c", "92e71c", "95e71d", "98e71d", "9ae71d", "9de71d", "a0e71e", "a3e71e", "a6e71e", "a8e71e", "abe71f", "aee71f", "b1e71f", "b4e71f", "b6e720", "b9e720", "bce720", "bfe720", "c2e821", "c3e721", "c5e621", "c7e521", "c9e522", "cbe422", "cde322", "cfe222", "d1e223", "d3e123", "d5e023", "d7df23", "d9df24", "dbde24", "dddd24", "dfdc24", "e1dc25", "e3db25", "e5da25", "e7d925", "e9d926", "e9d626", "e9d426", "e9d126", "e9cf27", "e9cc27", "e9ca27", "e9c727", "e9c528", "e9c228", "e9c028", "e9bd28", "e9bb29", "e9b829", "e9b629", "e9b329", "e9b12a", "e9ae2a", "e9ac2a", "e9a92a", "eaa72b", "eaa42b", "eaa22b", "ea9f2b", "ea9d2c", "ea9b2c", "ea982c", "ea962c", "ea942d", "ea912d", "ea8f2d", "ea8d2d", "ea8a2e", "ea882e", "ea862e", "ea832e", "ea812f", "ea7f2f", "ea7c2f", "ea7a2f", "eb7830", "eb7530", "eb7330", "eb7130", "eb6f31", "eb6c31", "eb6a31", "eb6831", "eb6632", "eb6332", "eb6132", "eb5f32", "eb5d33", "eb5a33", "eb5833", "eb5633", "eb5434", "eb5134", "eb4f34", "eb4d34", "ec4b35"] + ], + "green_yellow_orange_red": [ + [2, 3, 9, 1], + ["719e07", "739d06", "759c06", "779c06", "799b06", "7b9a05", "7d9a05", "7f9905", "819805", "839805", "859704", "879704", "899604", "8b9504", "8d9504", "8f9403", "919303", "949303", "969203", "989102", "9a9102", "9c9002", "9e9002", "a08f02", "a28e01", "a48e01", "a68d01", "a88c01", "aa8c01", "ac8b00", "ae8a00", "b08a00", "b28900", "b58900", "b58700", "b68501", "b78302", "b78102", "b87f03", "b97d04", "b97b04", "ba7905", "bb7806", "bb7606", "bc7407", "bd7208", "bd7008", "be6e09", "bf6c0a", "bf6a0a", "c0690b", "c1670c", "c1650c", "c2630d", "c3610e", "c35f0e", "c45d0f", "c55b10", "c55a10", "c65811", "c75612", "c75412", "c85213", "c95014", "c94e14", "ca4c15", "cb4b16", "cb4a16", "cc4917", "cc4818", "cd4719", "cd4719", "ce461a", "ce451b", "cf441c", "cf441c", "d0431d", "d0421e", "d1411f", "d1411f", "d24020", "d23f21", "d33e22", "d33e22", "d43d23", "d43c24", "d53b25", "d53b25", "d63a26", "d63927", "d73828", "d73828", "d83729", "d8362a", "d9352b", "d9352b", "da342c", "da332d", "db322e", "dc322f"] + ], + "yellow_red": [ + [220, 178, 172, 166, 160], + ["ffd700", "fdd500", "fbd300", "fad200", "f8d000", "f7cf00", "f5cd00", "f3cb00", "f2ca00", "f0c800", "efc700", "edc500", "ebc300", "eac200", "e8c000", "e7bf00", "e5bd00", "e3bb00", "e2ba00", "e0b800", "dfb700", "ddb500", "dbb300", "dab200", "d8b000", "d7af00", "d7ad00", "d7ab00", "d7aa00", "d7a800", "d7a700", "d7a500", "d7a300", "d7a200", "d7a000", "d79f00", "d79d00", "d79b00", "d79a00", "d79800", "d79700", "d79500", "d79300", "d79200", "d79000", "d78f00", "d78d00", "d78b00", "d78a00", "d78800", "d78700", "d78500", "d78300", "d78200", "d78000", "d77f00", "d77d00", "d77b00", "d77a00", "d77800", "d77700", "d77500", "d77300", "d77200", "d77000", "d76f00", "d76d00", "d76b00", "d76a00", "d76800", "d76700", "d76500", "d76300", "d76200", "d76000", "d75f00", "d75b00", "d75700", "d75300", "d74f00", "d74c00", "d74800", "d74400", "d74000", "d73c00", "d73900", "d73500", "d73100", "d72d00", "d72900", "d72600", "d72200", "d71e00", "d71a00", "d71600", "d71300", "d70f00", "d70b00", "d70700"] + ], + "yellow_orange_red": [ + [3, 9, 1], + ["b58900", "b58700", "b58600", "b68501", "b68401", "b78202", "b78102", "b88003", "b87f03", "b87d03", "b97c04", "b97b04", "ba7a05", "ba7805", "bb7706", "bb7606", "bc7507", "bc7307", "bc7207", "bd7108", "bd7008", "be6e09", "be6d09", "bf6c0a", "bf6b0a", "c06a0b", "c0680b", "c0670b", "c1660c", "c1650c", "c2630d", "c2620d", "c3610e", "c3600e", "c35e0e", "c45d0f", "c45c0f", "c55b10", "c55910", "c65811", "c65711", "c75612", "c75412", "c75312", "c85213", "c85113", "c94f14", "c94e14", "ca4d15", "ca4c15", "cb4b16", "cb4a16", "cb4a17", "cc4917", "cc4918", "cc4818", "cd4819", "cd4719", "cd471a", "ce461a", "ce461b", "ce451b", "cf451c", "cf441c", "cf441d", "d0431d", "d0431e", "d0421e", "d1421f", "d1411f", "d14120", "d24020", "d24021", "d23f21", "d33f22", "d33e22", "d33e23", "d43d23", "d43d24", "d43c24", "d53c25", "d53b25", "d53b26", "d63a26", "d63a27", "d63927", "d73928", "d73828", "d73829", "d83729", "d8372a", "d8362a", "d9362b", "d9352b", "d9352c", "da342c", "da342d", "da332d", "db332e"] + ], + "blue_red": [ + [39, 74, 68, 67, 103, 97, 96, 132, 131, 167, 203, 197], + ["19b4fe", "1bb2fc", "1db1fa", "1faff8", "22aef6", "24adf4", "26abf2", "29aaf0", "2ba9ee", "2da7ec", "30a6ea", "32a5e8", "34a3e6", "36a2e4", "39a0e2", "3b9fe1", "3d9edf", "409cdd", "429bdb", "449ad9", "4798d7", "4997d5", "4b96d3", "4d94d1", "5093cf", "5292cd", "5490cb", "578fc9", "598dc7", "5b8cc6", "5e8bc4", "6089c2", "6288c0", "6487be", "6785bc", "6984ba", "6b83b8", "6e81b6", "7080b4", "727eb2", "757db0", "777cae", "797aac", "7b79ab", "7e78a9", "8076a7", "8275a5", "8574a3", "8772a1", "89719f", "8c709d", "8e6e9b", "906d99", "926b97", "956a95", "976993", "996791", "9c668f", "9e658e", "a0638c", "a3628a", "a56188", "a75f86", "a95e84", "ac5c82", "ae5b80", "b05a7e", "b3587c", "b5577a", "b75678", "ba5476", "bc5374", "be5273", "c05071", "c34f6f", "c54e6d", "c74c6b", "ca4b69", "cc4967", "ce4865", "d14763", "d34561", "d5445f", "d7435d", "da415b", "dc4059", "de3f58", "e13d56", "e33c54", "e53a52", "e83950", "ea384e", "ec364c", "ee354a", "f13448", "f33246", "f53144", "f83042", "fa2e40"] + ], + "white_red": [ + [231, 255, 223, 216, 209, 202, 196], + ["ffffff", "fefefe", "fdfdfd", "fdfdfd", "fcfcfc", "fbfbfb", "fafafa", "fafafa", "f9f9f9", "f8f8f8", "f7f7f7", "f7f7f7", "f6f6f6", "f5f5f5", "f4f4f4", "f4f3f4", "f3f3f3", "f2f2f2", "f1f1f1", "f0f0f0", "f0f0f0", "efefef", "eeeeee", "efecea", "f1eae4", "f2e8de", "f3e6d8", "f5e4d3", "f6e2cd", "f7e0c7", "f8dec2", "f9dcbc", "fadab6", "fad8b1", "fbd5ac", "fbd2a9", "fbcea5", "fbcaa1", "fbc79e", "fbc39a", "fbc097", "fbbc93", "fbb88f", "fbb58c", "fab188", "faad85", "faaa81", "fba67e", "fba37a", "fb9f76", "fb9c73", "fb986f", "fb946c", "fb9168", "fa8d65", "fa8961", "fa865c", "fa8256", "fb7f4f", "fb7b48", "fb7841", "fb743a", "fb7133", "fb6d2c", "fa6a23", "fa661a", "fa620e", "fa5f03", "fa5d03", "fa5b03", "fa5a03", "fa5803", "fa5703", "fa5503", "fa5303", "fa5103", "fa4f03", "fa4e03", "fa4c03", "fa4a04", "fa4804", "fa4604", "fa4404", "fa4204", "fa3f04", "fa3d04", "fa3b04", "fa3805", "fa3605", "fa3305", "fb3105", "fb2e05", "fb2a05", "fb2705", "fb2306", "fb1f06", "fb1b06", "fb1506", "fb0e06", "fa0506", "fa0007"] + ], + "dark_green_gray": [ + [70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247], + ["51b000", "52b000", "54b000", "55b002", "56b007", "57b00d", "58b011", "59af15", "5aaf18", "5caf1b", "5daf1e", "5eaf21", "5faf23", "60ae25", "61ae27", "62ae2a", "63ae2c", "64ae2e", "65ae30", "66ae31", "67ad33", "68ad35", "69ad37", "69ad38", "6aad3a", "6bad3c", "6cac3d", "6dac3f", "6eac40", "6fac42", "70ac44", "70ac45", "71ab47", "72ab48", "73ab49", "74ab4b", "75ab4c", "75ab4e", "76aa4f", "77aa51", "78aa52", "79aa53", "79aa55", "7aaa56", "7ba957", "7ca959", "7ca95a", "7da95b", "7ea95d", "7fa95e", "7fa85f", "80a861", "81a862", "81a863", "82a865", "83a766", "83a767", "84a768", "85a76a", "85a76b", "86a66c", "87a66d", "87a66f", "88a670", "89a671", "89a672", "8aa574", "8ba575", "8ba576", "8ca577", "8da579", "8da47a", "8ea47b", "8ea47c", "8fa47d", "90a47f", "90a380", "91a381", "91a382", "92a384", "93a385", "93a286", "94a287", "94a288", "95a28a", "95a18b", "96a18c", "97a18d", "97a18e", "98a190", "98a091", "99a092", "99a093", "9aa094", "9aa096", "9b9f97", "9b9f98", "9c9f99", "9c9f9a", "9d9e9c", "9d9e9d"] + ], + "light_green_gray": [ + [148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 187, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250], + ["a3d900", "a4d800", "a4d800", "a5d805", "a5d80d", "a6d714", "a6d719", "a6d71d", "a7d621", "a7d625", "a8d628", "a8d62b", "a8d52e", "a9d531", "a9d533", "aad536", "aad438", "aad43a", "abd43d", "abd33f", "abd341", "acd343", "acd345", "acd247", "add249", "add24b", "add14d", "aed14f", "aed151", "aed152", "afd054", "afd056", "afd058", "b0d059", "b0cf5b", "b0cf5d", "b1cf5e", "b1ce60", "b1ce62", "b1ce63", "b2ce65", "b2cd67", "b2cd68", "b3cd6a", "b3cc6b", "b3cc6d", "b3cc6e", "b4cc70", "b4cb71", "b4cb73", "b4cb75", "b5ca76", "b5ca78", "b5ca79", "b5ca7a", "b6c97c", "b6c97d", "b6c97f", "b6c880", "b6c882", "b7c883", "b7c885", "b7c786", "b7c788", "b7c789", "b8c68a", "b8c68c", "b8c68d", "b8c68f", "b8c590", "b9c591", "b9c593", "b9c494", "b9c496", "b9c497", "b9c498", "bac39a", "bac39b", "bac39d", "bac29e", "bac29f", "bac2a1", "bac2a2", "bac1a4", "bbc1a5", "bbc1a6", "bbc0a8", "bbc0a9", "bbc0aa", "bbc0ac", "bbbfad", "bbbfae", "bbbfb0", "bbbeb1", "bcbeb3", "bcbeb4", "bcbdb5", "bcbdb7", "bcbdb8", "bcbdb9", "bcbcbb"] + ] + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/default.json b/powerline-bin/powerline/config_files/colorschemes/default.json new file mode 100644 index 0000000..7e271ef --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/default.json @@ -0,0 +1,56 @@ +{ + "name": "Default", + "groups": { + "information:additional": { "fg": "gray9", "bg": "gray4", "attrs": [] }, + "information:regular": { "fg": "gray10", "bg": "gray4", "attrs": ["bold"] }, + "information:highlighted": { "fg": "white", "bg": "gray4", "attrs": [] }, + "information:priority": { "fg": "brightyellow", "bg": "mediumorange", "attrs": [] }, + "warning:regular": { "fg": "white", "bg": "brightred", "attrs": ["bold"] }, + "critical:failure": { "fg": "white", "bg": "darkestred", "attrs": [] }, + "critical:success": { "fg": "white", "bg": "darkestgreen", "attrs": [] }, + "background": { "fg": "white", "bg": "gray0", "attrs": [] }, + "background:divider": { "fg": "gray5", "bg": "gray0", "attrs": [] }, + "session": { "fg": "black", "bg": "gray10", "attrs": ["bold"] }, + "date": { "fg": "gray8", "bg": "gray2", "attrs": [] }, + "time": { "fg": "gray10", "bg": "gray2", "attrs": ["bold"] }, + "time:divider": { "fg": "gray5", "bg": "gray2", "attrs": [] }, + "email_alert": "warning:regular", + "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attrs": ["bold"] }, + "hostname": { "fg": "black", "bg": "gray10", "attrs": ["bold"] }, + "weather": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "gray0", "attrs": [] }, + "weather_condition_hot": { "fg": "khaki1", "bg": "gray0", "attrs": [] }, + "weather_condition_snowy": { "fg": "skyblue1", "bg": "gray0", "attrs": [] }, + "weather_condition_rainy": { "fg": "skyblue1", "bg": "gray0", "attrs": [] }, + "uptime": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "external_ip": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "internal_ip": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "network_load": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attrs": [] }, + "network_load_sent_gradient": "network_load_gradient", + "network_load_recv_gradient": "network_load_gradient", + "network_load:divider": "background:divider", + "system_load": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "system_load_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attrs": [] }, + "environment": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "cpu_load_percent": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "cpu_load_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "gray0", "attrs": [] }, + "battery": { "fg": "gray8", "bg": "gray0", "attrs": [] }, + "battery_gradient": { "fg": "white_red", "bg": "gray0", "attrs": [] }, + "battery_full": { "fg": "red", "bg": "gray0", "attrs": [] }, + "battery_empty": { "fg": "white", "bg": "gray0", "attrs": [] }, + "player": { "fg": "gray10", "bg": "black", "attrs": [] }, + "user": { "fg": "white", "bg": "darkblue", "attrs": ["bold"] }, + "branch": { "fg": "gray9", "bg": "gray2", "attrs": [] }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray2", "attrs": [] }, + "branch_clean": { "fg": "gray9", "bg": "gray2", "attrs": [] }, + "branch:divider": { "fg": "gray7", "bg": "gray2", "attrs": [] }, + "stash": "branch_dirty", + "stash:divider": "branch:divider", + "cwd": "information:additional", + "cwd:current_folder": "information:regular", + "cwd:divider": { "fg": "gray7", "bg": "gray4", "attrs": [] }, + "virtualenv": { "fg": "white", "bg": "darkcyan", "attrs": [] }, + "attached_clients": { "fg": "gray8", "bg": "gray0", "attrs": [] } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/ipython/__main__.json b/powerline-bin/powerline/config_files/colorschemes/ipython/__main__.json new file mode 100644 index 0000000..982ea35 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/ipython/__main__.json @@ -0,0 +1,6 @@ +{ + "groups": { + "prompt": "information:additional", + "prompt_count": "information:highlighted" + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/pdb/__main__.json b/powerline-bin/powerline/config_files/colorschemes/pdb/__main__.json new file mode 100644 index 0000000..01a51fe --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/pdb/__main__.json @@ -0,0 +1,8 @@ +{ + "groups": { + "current_code_name": "information:additional", + "current_context": "current_code_name", + "current_line": "information:regular", + "current_file": "information:regular" + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/pdb/default.json b/powerline-bin/powerline/config_files/colorschemes/pdb/default.json new file mode 100644 index 0000000..b97acf7 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/pdb/default.json @@ -0,0 +1,5 @@ +{ + "groups": { + "stack_depth": { "fg": "gray1", "bg": "gray10", "attrs": ["bold"] } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/pdb/solarized.json b/powerline-bin/powerline/config_files/colorschemes/pdb/solarized.json new file mode 100644 index 0000000..2e1c787 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/pdb/solarized.json @@ -0,0 +1,5 @@ +{ + "groups": { + "stack_depth": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": ["bold"] } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/shell/__main__.json b/powerline-bin/powerline/config_files/colorschemes/shell/__main__.json new file mode 100644 index 0000000..6e3856f --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/shell/__main__.json @@ -0,0 +1,10 @@ +{ + "groups": { + "continuation": "cwd", + "continuation:current": "cwd:current_folder", + "exit_fail": "critical:failure", + "exit_success": "critical:success", + "jobnum": "information:priority", + "superuser": "warning:regular" + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/shell/default.json b/powerline-bin/powerline/config_files/colorschemes/shell/default.json new file mode 100644 index 0000000..1126feb --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/shell/default.json @@ -0,0 +1,16 @@ +{ + "name": "Default color scheme for shell prompts", + "groups": { + "hostname": { "fg": "brightyellow", "bg": "mediumorange", "attrs": [] }, + "environment": { "fg": "white", "bg": "darkestgreen", "attrs": [] }, + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attrs": ["bold"] }, + "attached_clients": { "fg": "white", "bg": "darkestgreen", "attrs": [] } + }, + "mode_translations": { + "vicmd": { + "groups": { + "mode": {"fg": "darkestcyan", "bg": "white", "attrs": ["bold"]} + } + } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/shell/solarized.json b/powerline-bin/powerline/config_files/colorschemes/shell/solarized.json new file mode 100644 index 0000000..69dcab1 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/shell/solarized.json @@ -0,0 +1,13 @@ +{ + "name": "Solarized dark for shell", + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": ["bold"] } + }, + "mode_translations": { + "vicmd": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/solarized.json b/powerline-bin/powerline/config_files/colorschemes/solarized.json new file mode 100644 index 0000000..c0eba5a --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/solarized.json @@ -0,0 +1,40 @@ +{ + "name": "Solarized dark", + "groups": { + "information:additional": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "information:regular": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, + "information:highlighted": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"]}, + "information:priority": { "fg": "solarized:base3", "bg": "solarized:yellow", "attrs": [] }, + "warning:regular": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": [] }, + "critical:failure": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": [] }, + "critical:success": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "background": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": [] }, + "background:divider": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "user": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] }, + "virtualenv": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "branch": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": [] }, + "branch_clean": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "stash": "branch_dirty", + "email_alert_gradient": { "fg": "solarized:base3", "bg": "yellow_orange_red", "attrs": [] }, + "email_alert": "warning:regular", + "cwd": "information:additional", + "cwd:current_folder": "information:regular", + "cwd:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] }, + "network_load": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] }, + "network_load:divider": "network_load", + "network_load_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base03", "attrs": [] }, + "network_load_sent_gradient": "network_load_gradient", + "network_load_recv_gradient": "network_load_gradient", + "hostname": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "environment": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "attached_clients": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "date": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "time": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": ["bold"] }, + "time:divider": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "system_load": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "solarized:base03", "attrs": [] }, + "weather": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] }, + "uptime": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/tmux/default.json b/powerline-bin/powerline/config_files/colorschemes/tmux/default.json new file mode 100644 index 0000000..8525fb0 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/tmux/default.json @@ -0,0 +1,14 @@ +{ + "groups": { + "active_window_status": {"fg": "darkblue", "bg": "gray0", "attrs": []}, + "window_status": {"fg": "gray70", "bg": "gray0", "attrs": []}, + "activity_status": {"fg": "yellow", "bg": "gray0", "attrs": []}, + "bell_status": {"fg": "red", "bg": "gray0", "attrs": []}, + "window": {"fg": "gray6", "bg": "gray0", "attrs": []}, + "window:divider": {"fg": "gray4", "bg": "gray0", "attrs": []}, + "window:current": {"fg": "mediumcyan", "bg": "darkblue", "attrs": []}, + "window_name": {"fg": "white", "bg": "darkblue", "attrs": ["bold"]}, + "session": {"fg": "black", "bg": "gray90", "attrs": ["bold"]}, + "session:prefix": {"fg": "gray90", "bg": "darkblue", "attrs": ["bold"]} + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/tmux/solarized.json b/powerline-bin/powerline/config_files/colorschemes/tmux/solarized.json new file mode 100644 index 0000000..20c42d3 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/tmux/solarized.json @@ -0,0 +1,14 @@ +{ + "groups": { + "active_window_status": { "fg": "solarized:blue", "bg": "solarized:base02", "attrs": [] }, + "window_status": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "activity_status": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": [] }, + "bell_status": { "fg": "solarized:red", "bg": "solarized:base02", "attrs": [] }, + "window": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "window:divider": { "fg": "solarized:base01", "bg": "solarized:base02", "attrs": [] }, + "window:current": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "window_name": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, + "session": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "session:prefix": { "fg": "solarized:base01", "bg": "solarized:base3", "attrs": [] } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/vim/__main__.json b/powerline-bin/powerline/config_files/colorschemes/vim/__main__.json new file mode 100644 index 0000000..1ce2e7b --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/vim/__main__.json @@ -0,0 +1,50 @@ +{ + "groups": { + "branch_clean": "branch", + "environment": "information:unimportant", + "file_size": "information:unimportant", + "file_format": "information:unimportant", + "file_encoding": "file_format", + "file_type": "file_format", + "branch": "information:additional", + "file_scheme": "file_name", + "file_directory": "information:additional", + "file_name_empty": "file_directory", + "line_percent": "information:additional", + "line_count": "line_current", + "position": "information:additional", + "single_tab": "line_current", + "many_tabs": "line_current", + "bufnr": "file_directory", + "winnr": "information:unimportant", + "tabnr": "file_directory", + "capslock_indicator": "paste_indicator", + + "csv:column_number": "line_current", + "csv:column_name": "line_current_symbol", + + "tab:background": "background", + "tab:divider": "background:divider", + + "tab_nc:modified_indicator": "modified_indicator", + "tab_nc:file_directory": "information:unimportant", + "tab_nc:file_name": "tab_nc:file_directory", + "tab_nc:tabnr": "tab_nc:file_directory", + + "buf_nc:file_directory": "tab_nc:file_directory", + "buf_nc:file_name": "buf_nc:file_directory", + "buf_nc:bufnr": "buf_nc:file_directory", + "buf_nc:modified_indicator": "tab_nc:modified_indicator", + + "buf_nc_mod:file_directory": "tab_nc:file_directory", + "buf_nc_mod:file_name": "buf_nc_mod:file_directory", + "buf_nc_mod:bufnr": "buf_nc_mod:file_directory", + "buf_nc_mod:modified_indicator": "tab_nc:modified_indicator", + + + "commandt:label": "file_name", + "commandt:background": "background", + "commandt:finder": "file_name", + "commandt:path": "file_directory" + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/vim/default.json b/powerline-bin/powerline/config_files/colorschemes/vim/default.json new file mode 100644 index 0000000..e02a160 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/vim/default.json @@ -0,0 +1,154 @@ +{ + "name": "Default color scheme", + "groups": { + "information:unimportant": { "fg": "gray8", "bg": "gray2", "attrs": [] }, + "information:additional": { "fg": "gray9", "bg": "gray4", "attrs": [] }, + "background": { "fg": "white", "bg": "gray2", "attrs": [] }, + "background:divider": { "fg": "gray6", "bg": "gray2", "attrs": [] }, + "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attrs": ["bold"] }, + "visual_range": { "fg": "brightestorange", "bg": "darkorange", "attrs": ["bold"] }, + "modified_indicator": { "fg": "brightyellow", "bg": "gray4", "attrs": ["bold"] }, + "paste_indicator": { "fg": "white", "bg": "mediumorange", "attrs": ["bold"] }, + "readonly_indicator": { "fg": "brightestred", "bg": "gray4", "attrs": [] }, + "branch_dirty": { "fg": "brightyellow", "bg": "gray4", "attrs": [] }, + "branch:divider": { "fg": "gray7", "bg": "gray4", "attrs": [] }, + "file_name": { "fg": "white", "bg": "gray4", "attrs": ["bold"] }, + "window_title": { "fg": "white", "bg": "gray4", "attrs": [] }, + "file_name_no_file": { "fg": "gray9", "bg": "gray4", "attrs": ["bold"] }, + "file_vcs_status": { "fg": "brightestred", "bg": "gray4", "attrs": [] }, + "file_vcs_status_M": { "fg": "brightyellow", "bg": "gray4", "attrs": [] }, + "file_vcs_status_A": { "fg": "brightgreen", "bg": "gray4", "attrs": [] }, + "line_percent": { "fg": "gray9", "bg": "gray4", "attrs": [] }, + "line_percent_gradient": { "fg": "dark_green_gray", "bg": "gray4", "attrs": [] }, + "position": { "fg": "gray9", "bg": "gray4", "attrs": [] }, + "position_gradient": { "fg": "green_yellow_red", "bg": "gray4", "attrs": [] }, + "line_current": { "fg": "gray1", "bg": "gray10", "attrs": ["bold"] }, + "line_current_symbol": { "fg": "gray1", "bg": "gray10", "attrs": [] }, + "virtcol_current_gradient": { "fg": "dark_GREEN_Orange_red", "bg": "gray10", "attrs": [] }, + "col_current": { "fg": "gray6", "bg": "gray10", "attrs": [] }, + "modified_buffers": { "fg": "brightyellow", "bg": "gray2", "attrs": [] }, + "attached_clients": { "fg": "gray8", "bg": "gray2", "attrs": [] }, + "error": { "fg": "brightestred", "bg": "darkred", "attrs": ["bold"] }, + "warning": { "fg": "brightyellow", "bg": "darkorange", "attrs": ["bold"] }, + "current_tag": { "fg": "gray9", "bg": "gray2", "attrs": [] }, + + "tab_nc:modified_indicator": { "fg": "brightyellow", "bg": "gray2", "attrs": ["bold"] } + }, + "mode_translations": { + "nc": { + "colors": { + "brightyellow": "darkorange", + "brightestred": "darkred", + "gray0": "gray0", + "gray1": "gray0", + "gray2": "gray0", + "gray3": "gray1", + "gray4": "gray1", + "gray5": "gray1", + "gray6": "gray1", + "gray7": "gray4", + "gray8": "gray4", + "gray9": "gray4", + "gray10": "gray5", + "white": "gray6", + "dark_green_gray": "gray5" + } + }, + "i": { + "colors": { + "gray0": "darkestblue", + "gray1": "darkestblue", + "gray2": "darkestblue", + "gray3": "darkblue", + "gray4": "darkblue", + "gray5": "darkestcyan", + "gray6": "darkestcyan", + "gray7": "darkestcyan", + "gray8": "mediumcyan", + "gray9": "mediumcyan", + "gray10": "mediumcyan", + "green_yellow_red": "gray5", + "dark_green_gray": "light_green_gray" + }, + "groups": { + "mode": { "fg": "darkestcyan", "bg": "white", "attrs": ["bold"] }, + "background:divider": { "fg": "darkcyan", "bg": "darkestblue", "attrs": [] }, + "branch:divider": { "fg": "darkcyan", "bg": "darkblue", "attrs": [] } + } + }, + "ic": { + "colors": { + "gray0": "darkestblue", + "gray1": "darkestblue", + "gray2": "darkestblue", + "gray3": "darkblue", + "gray4": "darkblue", + "gray5": "darkestcyan", + "gray6": "darkestcyan", + "gray7": "darkestcyan", + "gray8": "mediumcyan", + "gray9": "mediumcyan", + "gray10": "mediumcyan", + "green_yellow_red": "gray5", + "dark_green_gray": "light_green_gray" + }, + "groups": { + "mode": { "fg": "darkestcyan", "bg": "white", "attrs": ["bold"] }, + "background:divider": { "fg": "darkcyan", "bg": "darkestblue", "attrs": [] }, + "branch:divider": { "fg": "darkcyan", "bg": "darkblue", "attrs": [] } + } + }, + "ix": { + "colors": { + "gray0": "darkestblue", + "gray1": "darkestblue", + "gray2": "darkestblue", + "gray3": "darkblue", + "gray4": "darkblue", + "gray5": "darkestcyan", + "gray6": "darkestcyan", + "gray7": "darkestcyan", + "gray8": "mediumcyan", + "gray9": "mediumcyan", + "gray10": "mediumcyan", + "green_yellow_red": "gray5", + "dark_green_gray": "light_green_gray" + }, + "groups": { + "mode": { "fg": "darkestcyan", "bg": "white", "attrs": ["bold"] }, + "background:divider": { "fg": "darkcyan", "bg": "darkestblue", "attrs": [] }, + "branch:divider": { "fg": "darkcyan", "bg": "darkblue", "attrs": [] } + } + }, + "v": { + "groups": { + "mode": { "fg": "darkorange", "bg": "brightestorange", "attrs": ["bold"] } + } + }, + "V": { + "groups": { + "mode": { "fg": "darkorange", "bg": "brightestorange", "attrs": ["bold"] } + } + }, + "^V": { + "groups": { + "mode": { "fg": "darkorange", "bg": "brightestorange", "attrs": ["bold"] } + } + }, + "R": { + "groups": { + "mode": { "fg": "white", "bg": "brightred", "attrs": ["bold"] } + } + }, + "Rc": { + "groups": { + "mode": { "fg": "white", "bg": "brightred", "attrs": ["bold"] } + } + }, + "Rx": { + "groups": { + "mode": { "fg": "white", "bg": "brightred", "attrs": ["bold"] } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/vim/solarized.json b/powerline-bin/powerline/config_files/colorschemes/vim/solarized.json new file mode 100644 index 0000000..55cfaa7 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/vim/solarized.json @@ -0,0 +1,121 @@ +{ + "name": "Solarized dark for vim", + "groups": { + "information:additional": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "information:unimportant": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "background": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": [] }, + "background:divider": { "fg": "solarized:base00", "bg": "solarized:base02", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": ["bold"] }, + "visual_range": { "fg": "solarized:green", "bg": "solarized:base3", "attrs": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base01", "attrs": ["bold"] }, + "paste_indicator": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base01", "attrs": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base01", "attrs": [] }, + "branch:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] }, + "stash:divider": "branch:divider", + "file_name": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, + "window_title": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "file_name_no_file": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, + "file_format": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base01", "attrs": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base01", "attrs": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base01", "attrs": [] }, + "line_percent": { "fg": "solarized:base3", "bg": "solarized:base00", "attrs": [] }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base00", "attrs": [] }, + "position": { "fg": "solarized:base3", "bg": "solarized:base00", "attrs": [] }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base00", "attrs": [] }, + "line_current": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "virtcol_current_gradient": { "fg": "GREEN_Orange_red", "bg": "solarized:base2", "attrs": [] }, + "col_current": { "fg": "solarized:base0", "bg": "solarized:base2", "attrs": [] }, + "environment": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "attached_clients": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "error": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] }, + "warning": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] }, + "current_tag": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": ["bold"] } + }, + "mode_translations": { + "nc": { + "colors": { + "solarized:base01": "solarized:base02", + "solarized:base00": "solarized:base02", + "solarized:base0": "solarized:base01", + "solarized:base1": "solarized:base00", + "solarized:base2": "solarized:base0", + "solarized:base3": "solarized:base1" + } + }, + "i": { + "groups": { + "background": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "background:divider": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base2", "attrs": ["bold"] }, + "paste_indicator": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base2", "attrs": [] }, + "branch": { "fg": "solarized:base01", "bg": "solarized:base2", "attrs": [] }, + "branch:divider": { "fg": "solarized:base00", "bg": "solarized:base2", "attrs": [] }, + "file_directory": { "fg": "solarized:base01", "bg": "solarized:base2", "attrs": [] }, + "file_name": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": ["bold"] }, + "file_size": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "file_name_no_file": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": ["bold"] }, + "file_name_empty": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "file_format": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base2", "attrs": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base2", "attrs": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base2", "attrs": [] }, + "line_percent": { "fg": "solarized:base3", "bg": "solarized:base1", "attrs": [] }, + "line_percent_gradient": { "fg": "solarized:base3", "bg": "solarized:base1", "attrs": [] }, + "position": { "fg": "solarized:base3", "bg": "solarized:base1", "attrs": [] }, + "position_gradient": { "fg": "solarized:base3", "bg": "solarized:base1", "attrs": [] }, + "line_current": { "fg": "solarized:base03", "bg": "solarized:base3", "attrs": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base03", "bg": "solarized:base3", "attrs": [] }, + "col_current": { "fg": "solarized:base0", "bg": "solarized:base3", "attrs": [] } + } + }, + "ic": { + "groups": { + "background": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "background:divider": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] } + } + }, + "ix": { + "groups": { + "background": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "background:divider": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] } + } + }, + "v": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] } + } + }, + "V": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] } + } + }, + "^V": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] } + } + }, + "R": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] } + } + }, + "Rc": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] } + } + }, + "Rx": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/colorschemes/vim/solarizedlight.json b/powerline-bin/powerline/config_files/colorschemes/vim/solarizedlight.json new file mode 100644 index 0000000..f862d39 --- /dev/null +++ b/powerline-bin/powerline/config_files/colorschemes/vim/solarizedlight.json @@ -0,0 +1,122 @@ +{ + "name": "Solarized light for vim", + "groups": { + "information:additional": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "information:unimportant": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] }, + "background": { "fg": "solarized:base03", "bg": "solarized:base01", "attrs": [] }, + "background:divider": { "fg": "solarized:base0", "bg": "solarized:base01", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": ["bold"] }, + "visual_range": { "fg": "solarized:green", "bg": "solarized:base3", "attrs": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base2", "attrs": ["bold"] }, + "paste_indicator": { "fg": "solarized:red", "bg": "solarized:base2", "attrs": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base2", "attrs": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base2", "attrs": [] }, + "branch:divider": { "fg": "solarized:base1", "bg": "solarized:base2", "attrs": [] }, + "stash": "branch_dirty", + "stash:divider": "branch:divider", + "file_name": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": ["bold"] }, + "window_title": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "file_size": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "file_name_no_file": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": ["bold"] }, + "file_name_empty": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base2", "attrs": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base2", "attrs": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base2", "attrs": [] }, + "line_percent": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "line_percent_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base2", "attrs": [] }, + "position": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "position_gradient": { "fg": "green_yellow_orange_red", "bg": "solarized:base2", "attrs": [] }, + "line_current": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": [] }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "solarized:base02", "attrs": [] }, + "col_current": { "fg": "solarized:base00", "bg": "solarized:base02", "attrs": [] }, + "error": { "fg": "solarized:base03", "bg": "solarized:red", "attrs": ["bold"] }, + "warning": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": ["bold"] }, + "current_tag": { "fg": "solarized:base03", "bg": "solarized:base01", "attrs": ["bold"] } + }, + "mode_translations": { + "nc": { + "colors": { + "solarized:base2": "solarized:base01", + "solarized:base0": "solarized:base01", + "solarized:base00": "solarized:base2", + "solarized:base1": "solarized:base0", + "solarized:base02": "solarized:base00", + "solarized:base03": "solarized:base1" + } + }, + "i": { + "groups": { + "background": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "background:divider": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] }, + "modified_indicator": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": ["bold"] }, + "paste_indicator": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] }, + "readonly_indicator": { "fg": "solarized:red", "bg": "solarized:base02", "attrs": [] }, + "branch": { "fg": "solarized:base2", "bg": "solarized:base02", "attrs": [] }, + "branch:divider": { "fg": "solarized:base0", "bg": "solarized:base02", "attrs": [] }, + "file_directory": { "fg": "solarized:base2", "bg": "solarized:base02", "attrs": [] }, + "file_name": { "fg": "solarized:base01", "bg": "solarized:base02", "attrs": ["bold"] }, + "file_size": { "fg": "solarized:base01", "bg": "solarized:base02", "attrs": [] }, + "file_name_no_file": { "fg": "solarized:base01", "bg": "solarized:base02", "attrs": ["bold"] }, + "file_name_empty": { "fg": "solarized:base01", "bg": "solarized:base02", "attrs": [] }, + "file_format": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "file_vcs_status": { "fg": "solarized:red", "bg": "solarized:base02", "attrs": [] }, + "file_vcs_status_M": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": [] }, + "file_vcs_status_A": { "fg": "solarized:green", "bg": "solarized:base02", "attrs": [] }, + "line_percent": { "fg": "solarized:base03", "bg": "solarized:base1", "attrs": [] }, + "line_percent_gradient": { "fg": "solarized:base03", "bg": "solarized:base1", "attrs": [] }, + "position": { "fg": "solarized:base03", "bg": "solarized:base1", "attrs": [] }, + "position_gradient": { "fg": "solarized:base03", "bg": "solarized:base1", "attrs": [] }, + "line_current": { "fg": "solarized:base3", "bg": "solarized:base03", "attrs": ["bold"] }, + "line_current_symbol": { "fg": "solarized:base3", "bg": "solarized:base03", "attrs": [] }, + "virtcol_current_gradient": { "fg": "yellow_orange_red", "bg": "solarized:base03", "attrs": [] }, + "col_current": { "fg": "solarized:base00", "bg": "solarized:base03", "attrs": [] } + } + }, + "ic": { + "groups": { + "background": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "background:divider": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] } + } + }, + "ix": { + "groups": { + "background": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": [] }, + "background:divider": { "fg": "solarized:base02", "bg": "solarized:base2", "attrs": [] }, + "mode": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] } + } + }, + "v": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] } + } + }, + "V": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] } + } + }, + "^V": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:orange", "attrs": ["bold"] } + } + }, + "R": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] } + } + }, + "Rc": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] } + } + }, + "Rx": { + "groups": { + "mode": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/config.json b/powerline-bin/powerline/config_files/config.json new file mode 100644 index 0000000..4491885 --- /dev/null +++ b/powerline-bin/powerline/config_files/config.json @@ -0,0 +1,53 @@ +{ + "common": { + "term_truecolor": false + }, + "ext": { + "ipython": { + "colorscheme": "default", + "theme": "in", + "local_themes": { + "rewrite": "rewrite", + "out": "out", + "in2": "in2" + } + }, + "pdb": { + "colorscheme": "default", + "theme": "default" + }, + "shell": { + "colorscheme": "default", + "theme": "default", + "local_themes": { + "continuation": "continuation", + "select": "select" + } + }, + "tmux": { + "colorscheme": "default", + "theme": "default" + }, + "vim": { + "colorscheme": "default", + "theme": "default", + "local_themes": { + "__tabline__": "tabline", + + "cmdwin": "cmdwin", + "help": "help", + "quickfix": "quickfix", + + "powerline.matchers.vim.plugin.nerdtree.nerdtree": "plugin_nerdtree", + "powerline.matchers.vim.plugin.commandt.commandt": "plugin_commandt", + "powerline.matchers.vim.plugin.gundo.gundo": "plugin_gundo", + "powerline.matchers.vim.plugin.gundo.gundo_preview": "plugin_gundo-preview" + } + }, + "wm": { + "colorscheme": "default", + "theme": "default", + "update_interval": 2 + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/ascii.json b/powerline-bin/powerline/config_files/themes/ascii.json new file mode 100644 index 0000000..0ea05e7 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/ascii.json @@ -0,0 +1,153 @@ +{ + "use_non_breaking_spaces": false, + "dividers": { + "left": { + "hard": " ", + "soft": "| " + }, + "right": { + "hard": " ", + "soft": " |" + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "BR " + }, + "stash": { + "before": "ST " + }, + "cwd": { + "args": { + "ellipsis": "..." + } + }, + "player": { + "args": { + "state_symbols": { + "fallback": "", + "play": ">", + "pause": "~", + "stop": "X" + } + } + }, + + "line_current_symbol": { + "contents": "LN " + }, + + "time": { + "before": "" + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "DL {value:>8}", + "sent_format": "UL {value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": "H " + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "O", + "empty_heart": "O", + "online": "C", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "UP " + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "MAIL " + }, + "powerline.segments.common.env.virtualenv": { + "before": "(e) " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "day": "DAY", + "blustery": "WIND", + "rainy": "RAIN", + "cloudy": "CLOUDS", + "snowy": "SNOW", + "stormy": "STORM", + "foggy": "FOG", + "sunny": "SUN", + "night": "NIGHT", + "windy": "WINDY", + "not_available": "NA", + "unknown": "UKN" + }, + "temp_format": "{temp:.0f} C" + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": false + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N-OPER", + "v": "VISUAL", + "V": "V-LINE", + "^V": "V-BLCK", + "s": "SELECT", + "S": "S-LINE", + "^S": "S-BLCK", + "i": "INSERT", + "ic": "I-COMP", + "ix": "I-C_X ", + "R": "RPLACE", + "Rv": "V-RPLC", + "Rc": "R-COMP", + "Rx": "R-C_X ", + "c": "COMMND", + "cv": "VIM-EX", + "ce": "NRM-EX", + "r": "PROMPT", + "rm": "-MORE-", + "r?": "CNFIRM", + "!": "!SHELL", + "t": "TERM " + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows} x {vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "RO" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "O", + "changed": "X" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/ipython/in.json b/powerline-bin/powerline/config_files/themes/ipython/in.json new file mode 100644 index 0000000..edd4d29 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/ipython/in.json @@ -0,0 +1,25 @@ +{ + "segments": { + "left": [ + { + "function": "powerline.segments.common.env.virtualenv", + "priority": 10 + }, + { + "type": "string", + "contents": "In [", + "draw_soft_divider": false, + "highlight_groups": ["prompt"] + }, + { + "function": "powerline.segments.ipython.prompt_count", + "draw_soft_divider": false + }, + { + "type": "string", + "contents": "]", + "highlight_groups": ["prompt"] + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/ipython/in2.json b/powerline-bin/powerline/config_files/themes/ipython/in2.json new file mode 100644 index 0000000..422c44b --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/ipython/in2.json @@ -0,0 +1,12 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "", + "width": "auto", + "highlight_groups": ["prompt"] + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/ipython/out.json b/powerline-bin/powerline/config_files/themes/ipython/out.json new file mode 100644 index 0000000..2425d0b --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/ipython/out.json @@ -0,0 +1,24 @@ +{ + "default_module": "powerline.segments.ipython", + "segments": { + "left": [ + { + "type": "string", + "contents": "Out[", + "draw_soft_divider": false, + "width": "auto", + "align": "r", + "highlight_groups": ["prompt"] + }, + { + "function": "prompt_count", + "draw_soft_divider": false + }, + { + "type": "string", + "contents": "]", + "highlight_groups": ["prompt"] + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/ipython/rewrite.json b/powerline-bin/powerline/config_files/themes/ipython/rewrite.json new file mode 100644 index 0000000..8192fe4 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/ipython/rewrite.json @@ -0,0 +1,23 @@ +{ + "default_module": "powerline.segments.ipython", + "segments": { + "left": [ + { + "type": "string", + "contents": "", + "draw_soft_divider": false, + "width": "auto", + "highlight_groups": ["prompt"] + }, + { + "function": "prompt_count", + "draw_soft_divider": false + }, + { + "type": "string", + "contents": ">", + "highlight_groups": ["prompt"] + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/pdb/default.json b/powerline-bin/powerline/config_files/themes/pdb/default.json new file mode 100644 index 0000000..dcae108 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/pdb/default.json @@ -0,0 +1,27 @@ +{ + "default_module": "powerline.segments.pdb", + "segments": { + "left": [ + { + "function": "stack_depth" + }, + { + "type": "segment_list", + "function": "powerline.listers.pdb.frame_lister", + "segments": [ + { + "function": "current_file", + "after": ":" + }, + { + "function": "current_line", + "after": " " + }, + { + "function": "current_code_name" + } + ] + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/powerline.json b/powerline-bin/powerline/config_files/themes/powerline.json new file mode 100644 index 0000000..366a7ea --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/powerline.json @@ -0,0 +1,151 @@ +{ + "dividers": { + "left": { + "hard": " ", + "soft": " " + }, + "right": { + "hard": " ", + "soft": " " + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": " " + }, + "stash": { + "before": "⌆ " + }, + "cwd": { + "args": { + "ellipsis": "⋯" + } + }, + + "line_current_symbol": { + "contents": " " + }, + "player": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + + "time": { + "before": "⌚ " + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "⬇ {value:>8}", + "sent_format": "⬆ {value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": " " + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "⇑ " + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "✉ " + }, + "powerline.segments.common.env.virtualenv": { + "before": "ⓔ " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "day": "〇", + "blustery": "⚑", + "rainy": "☔", + "cloudy": "☁", + "snowy": "❅", + "stormy": "☈", + "foggy": "≡", + "sunny": "☼", + "night": "☾", + "windy": "☴", + "not_available": "�", + "unknown": "⚠" + } + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": true + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "ic": "I·COMP", + "ix": "I·C-X ", + "R": "RPLACE", + "Rv": "V·RPLC", + "Rc": "R·COMP", + "Rx": "R·C-X ", + "c": "COMMND", + "cv": "VIM·EX", + "ce": "NRM·EX", + "r": "PROMPT", + "rm": "-MORE-", + "r?": "CNFIRM", + "!": "!SHELL", + "t": "TERM " + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "↕{rows} ↔{vcols}", + "v_text_oneline": "↔{vcols}", + "v_text_multiline": "↕{rows}", + "V_text": "⇕{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "●", + "changed": "○" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/powerline_terminus.json b/powerline-bin/powerline/config_files/themes/powerline_terminus.json new file mode 100644 index 0000000..e5fb1c8 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/powerline_terminus.json @@ -0,0 +1,151 @@ +{ + "dividers": { + "left": { + "hard": " ", + "soft": " " + }, + "right": { + "hard": " ", + "soft": " " + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": " " + }, + "stash": { + "before": "ST " + }, + "cwd": { + "args": { + "ellipsis": "…" + } + }, + + "line_current_symbol": { + "contents": " " + }, + "player": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + + "time": { + "before": "" + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "⇓ {value:>8}", + "sent_format": "⇑ {value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": " " + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "↑ " + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "MAIL " + }, + "powerline.segments.common.env.virtualenv": { + "before": "(e) " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "day": "DAY", + "blustery": "WIND", + "rainy": "RAIN", + "cloudy": "CLOUDS", + "snowy": "SNOW", + "stormy": "STORM", + "foggy": "FOG", + "sunny": "SUN", + "night": "NIGHT", + "windy": "WINDY", + "not_available": "NA", + "unknown": "UKN" + } + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": true + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "ic": "I·COMP", + "ix": "I·C-X ", + "R": "RPLACE", + "Rv": "V·RPLC", + "Rc": "R·COMP", + "Rx": "R·C-X ", + "c": "COMMND", + "cv": "VIM·EX", + "ce": "NRM·EX", + "r": "PROMPT", + "rm": "-MORE-", + "r?": "CNFIRM", + "!": "!SHELL", + "t": "TERM " + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "↕{rows} ↔{vcols}", + "v_text_oneline": "↔{vcols}", + "v_text_multiline": "↕{rows}", + "V_text": "⇕{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "●", + "changed": "○" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/powerline_unicode7.json b/powerline-bin/powerline/config_files/themes/powerline_unicode7.json new file mode 100644 index 0000000..bd62826 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/powerline_unicode7.json @@ -0,0 +1,165 @@ +{ + "dividers": { + "left": { + "hard": " ", + "soft": " " + }, + "right": { + "hard": " ", + "soft": " " + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "🔀 " + }, + "stash": { + "before": "📝" + }, + "cwd": { + "args": { + "ellipsis": "⋯" + } + }, + + "line_current_symbol": { + "contents": " " + }, + "player": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "⏵", + "pause": "⏸", + "stop": "⏹" + } + } + }, + + "time": { + "before": "🕐 " + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "⬇ {value:>8}", + "sent_format": "⬆ {value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": "🏠 " + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "💙", + "empty_heart": "💛", + "online": "⚡️", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "⇑ " + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "✉ " + }, + "powerline.segments.common.env.virtualenv": { + "before": "🐍 " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "tornado": "🌪", + "hurricane": "🌀", + "showers": "☔", + "scattered_showers": "☔", + "thunderstorms": "🌩", + "isolated_thunderstorms": "🌩", + "scattered_thunderstorms": "🌩", + "dust": "🌫", + "fog": "🌫", + "cold": "❄", + "partly_cloudy_day": "🌤", + "mostly_cloudy_day": "🌥", + "sun": "🌣", + "hot": "♨", + "day": "☀", + "blustery": "⚑", + "rainy": "☂", + "cloudy": "☁", + "snowy": "☃", + "stormy": "☈", + "foggy": "🌁", + "sunny": "🌣", + "night": "☾", + "windy": "☴", + "not_available": "�", + "unknown": "⚠" + } + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": true + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "ic": "I·COMP", + "ix": "I·C-X ", + "R": "RPLACE", + "Rv": "V·RPLC", + "Rc": "R·COMP", + "Rx": "R·C-X ", + "c": "COMMND", + "cv": "VIM·EX", + "ce": "NRM·EX", + "r": "PROMPT", + "rm": "-MORE-", + "r?": "CNFIRM", + "!": "!SHELL", + "t": "TERM " + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "↕{rows} ↔{vcols}", + "v_text_oneline": "↔{vcols}", + "v_text_multiline": "↕{rows}", + "V_text": "⇕{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "🔏" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "🖫⃥" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "●", + "changed": "○" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/shell/__main__.json b/powerline-bin/powerline/config_files/themes/shell/__main__.json new file mode 100644 index 0000000..13ae942 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/shell/__main__.json @@ -0,0 +1,14 @@ +{ + "segment_data": { + "hostname": { + "args": { + "only_if_ssh": true + } + }, + "cwd": { + "args": { + "dir_limit_depth": 3 + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/shell/continuation.json b/powerline-bin/powerline/config_files/themes/shell/continuation.json new file mode 100644 index 0000000..9307fc0 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/shell/continuation.json @@ -0,0 +1,12 @@ +{ + "default_module": "powerline.segments.shell", + "segments": { + "left": [ + { + "function": "continuation" + } + ], + "right": [ + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/shell/default.json b/powerline-bin/powerline/config_files/themes/shell/default.json new file mode 100644 index 0000000..38039d8 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/shell/default.json @@ -0,0 +1,43 @@ +{ + "segments": { + "left": [ + { + "function": "powerline.segments.shell.mode" + }, + { + "function": "powerline.segments.common.net.hostname", + "priority": 10 + }, + { + "function": "powerline.segments.common.env.user", + "priority": 30 + }, + { + "function": "powerline.segments.common.env.virtualenv", + "priority": 50 + }, + { + "function": "powerline.segments.shell.cwd", + "priority": 10 + }, + { + "function": "powerline.segments.shell.jobnum", + "priority": 20 + } + ], + "right": [ + { + "function": "powerline.segments.shell.last_pipe_status", + "priority": 10 + }, + { + "function": "powerline.segments.common.vcs.stash", + "priority": 50 + }, + { + "function": "powerline.segments.common.vcs.branch", + "priority": 40 + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/shell/default_leftonly.json b/powerline-bin/powerline/config_files/themes/shell/default_leftonly.json new file mode 100644 index 0000000..b576273 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/shell/default_leftonly.json @@ -0,0 +1,34 @@ +{ + "segments": { + "left": [ + { + "function": "powerline.segments.common.net.hostname", + "priority": 10 + }, + { + "function": "powerline.segments.common.env.user", + "priority": 30 + }, + { + "function": "powerline.segments.common.env.virtualenv", + "priority": 50 + }, + { + "function": "powerline.segments.common.vcs.branch", + "priority": 40 + }, + { + "function": "powerline.segments.shell.cwd", + "priority": 10 + }, + { + "function": "powerline.segments.shell.jobnum", + "priority": 20 + }, + { + "function": "powerline.segments.shell.last_pipe_status", + "priority": 10 + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/shell/select.json b/powerline-bin/powerline/config_files/themes/shell/select.json new file mode 100644 index 0000000..3d81408 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/shell/select.json @@ -0,0 +1,13 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "Select variant", + "width": "auto", + "align": "r", + "highlight_groups": ["continuation:current"] + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/tmux/default.json b/powerline-bin/powerline/config_files/themes/tmux/default.json new file mode 100644 index 0000000..4532ced --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/tmux/default.json @@ -0,0 +1,28 @@ +{ + "segments": { + "right": [ + { + "function": "powerline.segments.common.sys.uptime", + "priority": 50 + }, + { + "function": "powerline.segments.common.sys.system_load", + "priority": 50 + }, + { + "function": "powerline.segments.common.time.date" + }, + { + "function": "powerline.segments.common.time.date", + "name": "time", + "args": { + "format": "%H:%M", + "istime": true + } + }, + { + "function": "powerline.segments.common.net.hostname" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/unicode.json b/powerline-bin/powerline/config_files/themes/unicode.json new file mode 100644 index 0000000..0802852 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/unicode.json @@ -0,0 +1,151 @@ +{ + "dividers": { + "left": { + "hard": "▌ ", + "soft": "│ " + }, + "right": { + "hard": " ▐", + "soft": " │" + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "⎇ " + }, + "stash": { + "before": "⌆" + }, + "cwd": { + "args": { + "ellipsis": "⋯" + } + }, + "player": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + + "line_current_symbol": { + "contents": "␤ " + }, + + "time": { + "before": "⌚ " + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "⬇ {value:>8}", + "sent_format": "⬆ {value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": "⌂ " + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "⇑ " + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "✉ " + }, + "powerline.segments.common.env.virtualenv": { + "before": "ⓔ " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "day": "〇", + "blustery": "⚑", + "rainy": "☔", + "cloudy": "☁", + "snowy": "❅", + "stormy": "☈", + "foggy": "≡", + "sunny": "☼", + "night": "☾", + "windy": "☴", + "not_available": "�", + "unknown": "⚠" + } + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": true + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "ic": "I·COMP", + "ix": "I·C-X ", + "R": "RPLACE", + "Rv": "V·RPLC", + "Rc": "R·COMP", + "Rx": "R·C-X ", + "c": "COMMND", + "cv": "VIM·EX", + "ce": "NRM·EX", + "r": "PROMPT", + "rm": "-MORE-", + "r?": "CNFIRM", + "!": "!SHELL", + "t": "TERM " + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "↕{rows} ↔{vcols}", + "v_text_oneline": "↔{vcols}", + "v_text_multiline": "↕{rows}", + "V_text": "⇕{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "⊗" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "●", + "changed": "○" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/unicode_terminus.json b/powerline-bin/powerline/config_files/themes/unicode_terminus.json new file mode 100644 index 0000000..9c76985 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/unicode_terminus.json @@ -0,0 +1,151 @@ +{ + "dividers": { + "left": { + "hard": "▌ ", + "soft": "│ " + }, + "right": { + "hard": " ▐", + "soft": " │" + } + }, + "spaces": 1, + "segment_data": { + "branch": { + "before": "BR " + }, + "stash": { + "before": "ST " + }, + "cwd": { + "args": { + "ellipsis": "…" + } + }, + + "line_current_symbol": { + "contents": "␤ " + }, + "player": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + + "time": { + "before": "" + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "⇓ {value:>8}", + "sent_format": "⇑ {value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": "⌂ " + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "↑ " + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "MAIL " + }, + "powerline.segments.common.env.virtualenv": { + "before": "(e) " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "day": "DAY", + "blustery": "WIND", + "rainy": "RAIN", + "cloudy": "CLOUDS", + "snowy": "SNOW", + "stormy": "STORM", + "foggy": "FOG", + "sunny": "SUN", + "night": "NIGHT", + "windy": "WINDY", + "not_available": "NA", + "unknown": "UKN" + } + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": true + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NORMAL", + "no": "N·OPER", + "v": "VISUAL", + "V": "V·LINE", + "^V": "V·BLCK", + "s": "SELECT", + "S": "S·LINE", + "^S": "S·BLCK", + "i": "INSERT", + "ic": "I·COMP", + "ix": "I·C-X ", + "R": "RPLACE", + "Rv": "V·RPLC", + "Rc": "R·COMP", + "Rx": "R·C-X ", + "c": "COMMND", + "cv": "VIM·EX", + "ce": "NRM·EX", + "r": "PROMPT", + "rm": "-MORE-", + "r?": "CNFIRM", + "!": "!SHELL", + "t": "TERM " + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows} × {vcols}", + "v_text_oneline": "C:{vcols}", + "v_text_multiline": "L:{rows}", + "V_text": "L:{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "RO" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "●", + "changed": "○" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/unicode_terminus_condensed.json b/powerline-bin/powerline/config_files/themes/unicode_terminus_condensed.json new file mode 100644 index 0000000..421f5c8 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/unicode_terminus_condensed.json @@ -0,0 +1,151 @@ +{ + "dividers": { + "left": { + "hard": "▌", + "soft": "│" + }, + "right": { + "hard": "▐", + "soft": "│" + } + }, + "spaces": 0, + "segment_data": { + "branch": { + "before": "B " + }, + "stash": { + "before": "S " + }, + "cwd": { + "args": { + "use_path_separator": true, + "ellipsis": "…" + } + }, + + "line_current_symbol": { + "contents": "␤" + }, + "player": { + "args": { + "state_symbols": { + "fallback": "♫", + "play": "▶", + "pause": "▮▮", + "stop": "■" + } + } + }, + + "time": { + "before": "" + }, + + "powerline.segments.common.net.network_load": { + "args": { + "recv_format": "⇓{value:>8}", + "sent_format": "⇑{value:>8}" + } + }, + "powerline.segments.common.net.hostname": { + "before": "⌂" + }, + "powerline.segments.common.bat.battery": { + "args": { + "full_heart": "♥", + "empty_heart": "♥", + "online": "⚡︎", + "offline": " " + } + }, + "powerline.segments.common.sys.uptime": { + "before": "↑" + }, + "powerline.segments.common.mail.email_imap_alert": { + "before": "M " + }, + "powerline.segments.common.env.virtualenv": { + "before": "E " + }, + "powerline.segments.common.wthr.weather": { + "args": { + "icons": { + "day": "D", + "blustery": "W", + "rainy": "R", + "cloudy": "c", + "snowy": "*", + "stormy": "S", + "foggy": "f", + "sunny": "s", + "night": "N", + "windy": "w", + "not_available": "-", + "unknown": "!" + } + } + }, + "powerline.segments.common.time.fuzzy_time": { + "args": { + "unicode_text": true + } + }, + + "powerline.segments.vim.mode": { + "args": { + "override": { + "n": "NML", + "no": "NOP", + "v": "VIS", + "V": "VLN", + "^V": "VBL", + "s": "SEL", + "S": "SLN", + "^S": "SBL", + "i": "INS", + "ic": "I-C", + "ix": "I^X", + "R": "REP", + "Rv": "VRP", + "Rc": "R-C", + "Rx": "R^X", + "c": "CMD", + "cv": "VEX", + "ce": " EX", + "r": "PRT", + "rm": "MOR", + "r?": "CON", + "!": " SH" + } + } + }, + "powerline.segments.vim.visual_range": { + "args": { + "CTRL_V_text": "{rows}×{vcols}", + "v_text_oneline": "↔{vcols}", + "v_text_multiline": "↕{rows}", + "V_text": "⇕{rows}" + } + }, + "powerline.segments.vim.readonly_indicator": { + "args": { + "text": "RO" + } + }, + "powerline.segments.vim.modified_indicator": { + "args": { + "text": "+" + } + }, + + "powerline.segments.i3wm.scratchpad": { + "args": { + "icons": { + "fresh": "●", + "changed": "○" + } + } + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/__main__.json b/powerline-bin/powerline/config_files/themes/vim/__main__.json new file mode 100644 index 0000000..7cd3305 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/__main__.json @@ -0,0 +1,10 @@ +{ + "segment_data": { + "line_percent": { + "args": { + "gradient": true + }, + "after": "%" + } + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/cmdwin.json b/powerline-bin/powerline/config_files/themes/vim/cmdwin.json new file mode 100644 index 0000000..e6a05b0 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/cmdwin.json @@ -0,0 +1,18 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "Command Line", + "highlight_groups": ["file_name"] + }, + { + "type": "string", + "highlight_groups": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/default.json b/powerline-bin/powerline/config_files/themes/vim/default.json new file mode 100644 index 0000000..9b0c744 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/default.json @@ -0,0 +1,128 @@ +{ + "segments": { + "left": [ + { + "function": "mode", + "exclude_modes": ["nc"] + }, + { + "function": "visual_range", + "include_modes": ["v", "V", "^V", "s", "S", "^S"], + "priority": 10 + }, + { + "function": "paste_indicator", + "exclude_modes": ["nc"], + "priority": 10 + }, + { + "function": "powerline.segments.vim.plugin.capslock.capslock_indicator", + "include_modes": ["i", "R", "Rv"], + "priority": 10 + }, + { + "function": "branch", + "exclude_modes": ["nc"], + "priority": 30 + }, + { + "function": "readonly_indicator", + "draw_soft_divider": false, + "after": " " + }, + { + "function": "file_scheme", + "priority": 20 + }, + { + "function": "file_directory", + "priority": 40, + "draw_soft_divider": false + }, + { + "function": "file_name", + "draw_soft_divider": false + }, + { + "function": "file_vcs_status", + "before": " ", + "draw_soft_divider": false + }, + { + "function": "modified_indicator", + "before": " " + }, + { + "exclude_modes": ["i", "R", "Rv"], + "function": "trailing_whitespace", + "display": false, + "priority": 60 + }, + { + "exclude_modes": ["nc"], + "function": "powerline.segments.vim.plugin.syntastic.syntastic", + "priority": 50 + }, + { + "exclude_modes": ["nc"], + "function": "powerline.segments.vim.plugin.tagbar.current_tag", + "draw_soft_divider": false, + "priority": 50 + }, + { + "type": "string", + "highlight_groups": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "function": "file_format", + "draw_soft_divider": false, + "exclude_modes": ["nc"], + "priority": 60 + }, + { + "function": "file_encoding", + "exclude_modes": ["nc"], + "priority": 60 + }, + { + "function": "file_type", + "exclude_modes": ["nc"], + "priority": 60 + }, + { + "function": "line_percent", + "priority": 50, + "width": 4, + "align": "r" + }, + { + "function": "csv_col_current", + "priority": 30 + }, + { + "type": "string", + "name": "line_current_symbol", + "highlight_groups": ["line_current_symbol", "line_current"] + }, + { + "function": "line_current", + "draw_soft_divider": false, + "width": 3, + "align": "r" + }, + { + "function": "virtcol_current", + "draw_soft_divider": false, + "priority": 20, + "before": ":", + "width": 3, + "align": "l" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/help.json b/powerline-bin/powerline/config_files/themes/vim/help.json new file mode 100644 index 0000000..45c9458 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/help.json @@ -0,0 +1,36 @@ +{ + "segments": { + "left": [ + { + "function": "file_name", + "draw_soft_divider": false + }, + { + "type": "string", + "highlight_groups": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "function": "line_percent", + "priority": 30, + "width": 4, + "align": "r" + }, + { + "type": "string", + "name": "line_current_symbol", + "highlight_groups": ["line_current_symbol", "line_current"] + }, + { + "function": "line_current", + "draw_soft_divider": false, + "width": 3, + "align": "r" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/plugin_commandt.json b/powerline-bin/powerline/config_files/themes/vim/plugin_commandt.json new file mode 100644 index 0000000..dd6748f --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/plugin_commandt.json @@ -0,0 +1,26 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "contents": "Command-T", + "highlight_groups": ["commandt:label"] + }, + { + "function": "powerline.segments.vim.plugin.commandt.finder" + }, + { + "function": "powerline.segments.vim.plugin.commandt.path" + }, + { + "type": "string", + "highlight_groups": ["commandt:background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/plugin_gundo-preview.json b/powerline-bin/powerline/config_files/themes/vim/plugin_gundo-preview.json new file mode 100644 index 0000000..ad8432c --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/plugin_gundo-preview.json @@ -0,0 +1,18 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "highlight_groups": ["gundo:name", "file_name"], + "contents": "Undo diff" + }, + { + "type": "string", + "highlight_groups": ["gundo:background", "background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/plugin_gundo.json b/powerline-bin/powerline/config_files/themes/vim/plugin_gundo.json new file mode 100644 index 0000000..a03b256 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/plugin_gundo.json @@ -0,0 +1,18 @@ +{ + "segments": { + "left": [ + { + "type": "string", + "highlight_groups": ["gundo:name", "file_name"], + "contents": "Undo tree" + }, + { + "type": "string", + "highlight_groups": ["gundo:background", "background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/plugin_nerdtree.json b/powerline-bin/powerline/config_files/themes/vim/plugin_nerdtree.json new file mode 100644 index 0000000..896d393 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/plugin_nerdtree.json @@ -0,0 +1,17 @@ +{ + "default_module": "powerline.segments.vim.plugin.nerdtree", + "segments": { + "left": [ + { + "function": "nerdtree" + }, + { + "type": "string", + "highlight_groups": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/quickfix.json b/powerline-bin/powerline/config_files/themes/vim/quickfix.json new file mode 100644 index 0000000..ae4d5a5 --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/quickfix.json @@ -0,0 +1,40 @@ +{ + "segment_data": { + "buffer_name": { + "contents": "Location List" + } + }, + "segments": { + "left": [ + { + "type": "string", + "name": "buffer_name", + "highlight_groups": ["file_name"] + }, + { + "function": "window_title", + "draw_soft_divider": false + }, + { + "type": "string", + "highlight_groups": ["background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "type": "string", + "name": "line_current_symbol", + "highlight_groups": ["line_current_symbol", "line_current"] + }, + { + "function": "line_current", + "draw_soft_divider": false, + "width": 3, + "align": "r" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/vim/tabline.json b/powerline-bin/powerline/config_files/themes/vim/tabline.json new file mode 100644 index 0000000..1e3130e --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/vim/tabline.json @@ -0,0 +1,93 @@ +{ + "default_module": "powerline.segments.vim", + "segments": { + "left": [ + { + "type": "segment_list", + "function": "powerline.listers.vim.tablister", + "exclude_function": "single_tab", + "segments": [ + { + "function": "tab" + }, + { + "function": "tabnr", + "after": " ", + "priority": 5 + }, + { + "function": "file_directory", + "priority": 40 + }, + { + "function": "file_name", + "args": { + "display_no_file": true + }, + "priority": 10 + }, + { + "function": "tab_modified_indicator", + "priority": 5 + } + ] + }, + { + "function": "tab", + "args": { + "end": true + } + }, + { + "type": "segment_list", + "function": "powerline.listers.vim.bufferlister", + "include_function": "single_tab", + "segments": [ + { + "function": "bufnr", + "after": " ", + "priority": 5 + }, + { + "function": "file_directory", + "priority": 40 + }, + { + "function": "file_name", + "args": { + "display_no_file": true + }, + "priority": 10 + }, + { + "function": "modified_indicator", + "priority": 5 + } + ] + }, + { + "type": "string", + "highlight_groups": ["tab:background"], + "draw_soft_divider": false, + "draw_hard_divider": false, + "width": "auto" + } + ], + "right": [ + { + "type": "string", + "contents": "Bufs", + "name": "single_tab", + "highlight_groups": ["single_tab"], + "include_function": "single_tab" + }, + { + "type": "string", + "contents": "Tabs", + "name": "many_tabs", + "highlight_groups": ["many_tabs"], + "exclude_function": "single_tab" + } + ] + } +} diff --git a/powerline-bin/powerline/config_files/themes/wm/default.json b/powerline-bin/powerline/config_files/themes/wm/default.json new file mode 100644 index 0000000..579080d --- /dev/null +++ b/powerline-bin/powerline/config_files/themes/wm/default.json @@ -0,0 +1,29 @@ +{ + "segments": { + "right": [ + { + "function": "powerline.segments.common.wthr.weather", + "priority": 50 + }, + { + "function": "powerline.segments.common.time.date" + }, + { + "function": "powerline.segments.common.time.date", + "name": "time", + "args": { + "format": "%H:%M", + "istime": true + } + }, + { + "function": "powerline.segments.common.mail.email_imap_alert", + "priority": 10, + "args": { + "username": "", + "password": "" + } + } + ] + } +} diff --git a/powerline-bin/powerline/dist/systemd/powerline-daemon.service b/powerline-bin/powerline/dist/systemd/powerline-daemon.service new file mode 100644 index 0000000..96b685d --- /dev/null +++ b/powerline-bin/powerline/dist/systemd/powerline-daemon.service @@ -0,0 +1,10 @@ +[Unit] +Description=powerline-daemon - Daemon that improves powerline performance +Documentation=man:powerline-daemon(1) +Documentation=https://powerline.readthedocs.org/en/latest/ + +[Service] +ExecStart=/usr/bin/powerline-daemon --foreground + +[Install] +WantedBy=default.target diff --git a/powerline-bin/powerline/ipython.py b/powerline-bin/powerline/ipython.py new file mode 100644 index 0000000..cb84fc7 --- /dev/null +++ b/powerline-bin/powerline/ipython.py @@ -0,0 +1,68 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline import Powerline +from powerline.lib.dict import mergedicts +from powerline.lib.unicode import string + + +class IPythonInfo(object): + def __init__(self, shell): + self._shell = shell + + @property + def prompt_count(self): + return self._shell.execution_count + + +# HACK: ipython tries to only leave us with plain ASCII +class RewriteResult(object): + def __init__(self, prompt): + self.prompt = string(prompt) + + def __str__(self): + return self.prompt + + def __add__(self, s): + if type(s) is not str: + try: + s = s.encode('utf-8') + except AttributeError: + raise NotImplementedError + return RewriteResult(self.prompt + s) + + +class IPythonPowerline(Powerline): + def init(self, **kwargs): + super(IPythonPowerline, self).init( + 'ipython', + use_daemon_threads=True, + **kwargs + ) + + def get_config_paths(self): + if self.config_paths: + return self.config_paths + else: + return super(IPythonPowerline, self).get_config_paths() + + def get_local_themes(self, local_themes): + return dict(((type, {'config': self.load_theme_config(name)}) for type, name in local_themes.items())) + + def load_main_config(self): + r = super(IPythonPowerline, self).load_main_config() + if self.config_overrides: + mergedicts(r, self.config_overrides) + return r + + def load_theme_config(self, name): + r = super(IPythonPowerline, self).load_theme_config(name) + if name in self.theme_overrides: + mergedicts(r, self.theme_overrides[name]) + return r + + def do_setup(self, wrefs): + for wref in wrefs: + obj = wref() + if obj is not None: + setattr(obj, 'powerline', self) diff --git a/powerline-bin/powerline/lemonbar.py b/powerline-bin/powerline/lemonbar.py new file mode 100644 index 0000000..b49f86b --- /dev/null +++ b/powerline-bin/powerline/lemonbar.py @@ -0,0 +1,21 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline import Powerline +from powerline.lib.dict import mergedicts + + +class LemonbarPowerline(Powerline): + def init(self): + super(LemonbarPowerline, self).init(ext='wm', renderer_module='lemonbar') + + get_encoding = staticmethod(lambda: 'utf-8') + + def get_local_themes(self, local_themes): + if not local_themes: + return {} + + return dict(( + (key, {'config': self.load_theme_config(val)}) + for key, val in local_themes.items() + )) diff --git a/powerline-bin/powerline/lib/__init__.py b/powerline-bin/powerline/lib/__init__.py new file mode 100644 index 0000000..2a5fbd0 --- /dev/null +++ b/powerline-bin/powerline/lib/__init__.py @@ -0,0 +1,28 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from functools import wraps + + +def wraps_saveargs(wrapped): + def dec(wrapper): + r = wraps(wrapped)(wrapper) + r.powerline_origin = getattr(wrapped, 'powerline_origin', wrapped) + return r + return dec + + +def add_divider_highlight_group(highlight_group): + def dec(func): + @wraps_saveargs(func) + def f(**kwargs): + r = func(**kwargs) + if r: + return [{ + 'contents': r, + 'divider_highlight_group': highlight_group, + }] + else: + return None + return f + return dec diff --git a/powerline-bin/powerline/lib/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1e01f87 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/config.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..3090912 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/config.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/debug.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/debug.cpython-312.pyc new file mode 100644 index 0000000..ea2ddee Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/debug.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/dict.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/dict.cpython-312.pyc new file mode 100644 index 0000000..6f25e07 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/dict.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/encoding.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/encoding.cpython-312.pyc new file mode 100644 index 0000000..46396bc Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/encoding.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/humanize_bytes.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/humanize_bytes.cpython-312.pyc new file mode 100644 index 0000000..9251f9c Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/humanize_bytes.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/inotify.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/inotify.cpython-312.pyc new file mode 100644 index 0000000..d469997 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/inotify.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/memoize.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/memoize.cpython-312.pyc new file mode 100644 index 0000000..e3168ea Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/memoize.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/monotonic.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/monotonic.cpython-312.pyc new file mode 100644 index 0000000..906fa02 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/monotonic.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/overrides.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/overrides.cpython-312.pyc new file mode 100644 index 0000000..f79b11e Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/overrides.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/path.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/path.cpython-312.pyc new file mode 100644 index 0000000..5210445 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/path.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/shell.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/shell.cpython-312.pyc new file mode 100644 index 0000000..7fdffdb Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/shell.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/threaded.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/threaded.cpython-312.pyc new file mode 100644 index 0000000..f1b0145 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/threaded.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/unicode.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/unicode.cpython-312.pyc new file mode 100644 index 0000000..6e9b783 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/unicode.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/__pycache__/url.cpython-312.pyc b/powerline-bin/powerline/lib/__pycache__/url.cpython-312.pyc new file mode 100644 index 0000000..4773173 Binary files /dev/null and b/powerline-bin/powerline/lib/__pycache__/url.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/config.py b/powerline-bin/powerline/lib/config.py new file mode 100644 index 0000000..0c95e47 --- /dev/null +++ b/powerline-bin/powerline/lib/config.py @@ -0,0 +1,218 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import json +import codecs + +from copy import deepcopy +from threading import Event, Lock +from collections import defaultdict + +from powerline.lib.threaded import MultiRunnedThread +from powerline.lib.watcher import create_file_watcher + + +def open_file(path): + return codecs.open(path, encoding='utf-8') + + +def load_json_config(config_file_path, load=json.load, open_file=open_file): + with open_file(config_file_path) as config_file_fp: + return load(config_file_fp) + + +class DummyWatcher(object): + def __call__(self, *args, **kwargs): + return False + + def watch(self, *args, **kwargs): + pass + + +class DeferredWatcher(object): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + self.calls = [] + + def __call__(self, *args, **kwargs): + self.calls.append(('__call__', args, kwargs)) + + def watch(self, *args, **kwargs): + self.calls.append(('watch', args, kwargs)) + + def unwatch(self, *args, **kwargs): + self.calls.append(('unwatch', args, kwargs)) + + def transfer_calls(self, watcher): + for attr, args, kwargs in self.calls: + getattr(watcher, attr)(*args, **kwargs) + + +class ConfigLoader(MultiRunnedThread): + def __init__(self, shutdown_event=None, watcher=None, watcher_type=None, load=load_json_config, run_once=False): + super(ConfigLoader, self).__init__() + self.shutdown_event = shutdown_event or Event() + if run_once: + self.watcher = DummyWatcher() + self.watcher_type = 'dummy' + else: + self.watcher = watcher or DeferredWatcher() + if watcher: + if not watcher_type: + raise ValueError('When specifying watcher you must also specify watcher type') + self.watcher_type = watcher_type + else: + self.watcher_type = 'deferred' + self._load = load + + self.pl = None + self.interval = None + + self.lock = Lock() + + self.watched = defaultdict(set) + self.missing = defaultdict(set) + self.loaded = {} + + def set_watcher(self, watcher_type, force=False): + if watcher_type == self.watcher_type: + return + watcher = create_file_watcher(self.pl, watcher_type) + with self.lock: + if self.watcher_type == 'deferred': + self.watcher.transfer_calls(watcher) + self.watcher = watcher + self.watcher_type = watcher_type + + def set_pl(self, pl): + self.pl = pl + + def set_interval(self, interval): + self.interval = interval + + def register(self, function, path): + '''Register function that will be run when file changes. + + :param function function: + Function that will be called when file at the given path changes. + :param str path: + Path that will be watched for. + ''' + with self.lock: + self.watched[path].add(function) + self.watcher.watch(path) + + def register_missing(self, condition_function, function, key): + '''Register any function that will be called with given key each + interval seconds (interval is defined at __init__). Its result is then + passed to ``function``, but only if the result is true. + + :param function condition_function: + Function which will be called each ``interval`` seconds. All + exceptions from it will be logged and ignored. IOError exception + will be ignored without logging. + :param function function: + Function which will be called if condition_function returns + something that is true. Accepts result of condition_function as an + argument. + :param str key: + Any value, it will be passed to condition_function on each call. + + Note: registered functions will be automatically removed if + condition_function results in something true. + ''' + with self.lock: + self.missing[key].add((condition_function, function)) + + def unregister_functions(self, removed_functions): + '''Unregister files handled by these functions. + + :param set removed_functions: + Set of functions previously passed to ``.register()`` method. + ''' + with self.lock: + for path, functions in list(self.watched.items()): + functions -= removed_functions + if not functions: + self.watched.pop(path) + self.loaded.pop(path, None) + + def unregister_missing(self, removed_functions): + '''Unregister files handled by these functions. + + :param set removed_functions: + Set of pairs (2-tuples) representing ``(condition_function, + function)`` function pairs previously passed as an arguments to + ``.register_missing()`` method. + ''' + with self.lock: + for key, functions in list(self.missing.items()): + functions -= removed_functions + if not functions: + self.missing.pop(key) + + def load(self, path): + try: + # No locks: GIL does what we need + return deepcopy(self.loaded[path]) + except KeyError: + r = self._load(path) + self.loaded[path] = deepcopy(r) + return r + + def update(self): + toload = [] + with self.lock: + for path, functions in self.watched.items(): + for function in functions: + try: + modified = self.watcher(path) + except OSError as e: + modified = True + self.exception('Error while running watcher for path {0}: {1}', path, str(e)) + else: + if modified: + toload.append(path) + if modified: + function(path) + with self.lock: + for key, functions in list(self.missing.items()): + for condition_function, function in list(functions): + try: + path = condition_function(key) + except IOError: + pass + except Exception as e: + self.exception('Error while running condition function for key {0}: {1}', key, str(e)) + else: + if path: + toload.append(path) + function(path) + functions.remove((condition_function, function)) + if not functions: + self.missing.pop(key) + for path in toload: + try: + self.loaded[path] = deepcopy(self._load(path)) + except Exception as e: + self.exception('Error while loading {0}: {1}', path, str(e)) + try: + self.loaded.pop(path) + except KeyError: + pass + try: + self.loaded.pop(path) + except KeyError: + pass + + def run(self): + while self.interval is not None and not self.shutdown_event.is_set(): + self.update() + self.shutdown_event.wait(self.interval) + + def exception(self, msg, *args, **kwargs): + if self.pl: + self.pl.exception(msg, prefix='config_loader', *args, **kwargs) + else: + raise diff --git a/powerline-bin/powerline/lib/debug.py b/powerline-bin/powerline/lib/debug.py new file mode 100644 index 0000000..515e8c4 --- /dev/null +++ b/powerline-bin/powerline/lib/debug.py @@ -0,0 +1,97 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import gc +import sys + +from types import FrameType +from itertools import chain + + +# From http://code.activestate.com/recipes/523004-find-cyclical-references/ +def print_cycles(objects, outstream=sys.stdout, show_progress=False): + '''Find reference cycles + + :param list objects: + A list of objects to find cycles in. It is often useful to pass in + gc.garbage to find the cycles that are preventing some objects from + being garbage collected. + :param file outstream: + The stream for output. + :param bool show_progress: + If True, print the number of objects reached as they are found. + ''' + def print_path(path): + for i, step in enumerate(path): + # next “wraps around” + next = path[(i + 1) % len(path)] + + outstream.write(' %s -- ' % str(type(step))) + written = False + if isinstance(step, dict): + for key, val in step.items(): + if val is next: + outstream.write('[%s]' % repr(key)) + written = True + break + if key is next: + outstream.write('[key] = %s' % repr(val)) + written = True + break + elif isinstance(step, (list, tuple)): + for i, item in enumerate(step): + if item is next: + outstream.write('[%d]' % i) + written = True + elif getattr(type(step), '__getattribute__', None) in (object.__getattribute__, type.__getattribute__): + for attr in chain(dir(step), getattr(step, '__dict__', ())): + if getattr(step, attr, None) is next: + try: + outstream.write('%r.%s' % (step, attr)) + except TypeError: + outstream.write('.%s' % (step, attr)) + written = True + break + if not written: + outstream.write(repr(step)) + outstream.write(' ->\n') + outstream.write('\n') + + def recurse(obj, start, all, current_path): + if show_progress: + outstream.write('%d\r' % len(all)) + + all[id(obj)] = None + + referents = gc.get_referents(obj) + for referent in referents: + # If we’ve found our way back to the start, this is + # a cycle, so print it out + if referent is start: + try: + outstream.write('Cyclic reference: %r\n' % referent) + except TypeError: + try: + outstream.write('Cyclic reference: %i (%r)\n' % (id(referent), type(referent))) + except TypeError: + outstream.write('Cyclic reference: %i\n' % id(referent)) + print_path(current_path) + + # Don’t go back through the original list of objects, or + # through temporary references to the object, since those + # are just an artifact of the cycle detector itself. + elif referent is objects or isinstance(referent, FrameType): + continue + + # We haven’t seen this object before, so recurse + elif id(referent) not in all: + recurse(referent, start, all, current_path + (obj,)) + + for obj in objects: + # We are not interested in non-powerline cyclic references + try: + if not type(obj).__module__.startswith('powerline'): + continue + except AttributeError: + continue + recurse(obj, obj, {}, ()) diff --git a/powerline-bin/powerline/lib/dict.py b/powerline-bin/powerline/lib/dict.py new file mode 100644 index 0000000..c06ab30 --- /dev/null +++ b/powerline-bin/powerline/lib/dict.py @@ -0,0 +1,88 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + +REMOVE_THIS_KEY = object() + + +def mergeargs(argvalue, remove=False): + if not argvalue: + return None + r = {} + for subval in argvalue: + mergedicts(r, dict([subval]), remove=remove) + return r + + +def _clear_special_values(d): + '''Remove REMOVE_THIS_KEY values from dictionary + ''' + l = [d] + while l: + i = l.pop() + pops = [] + for k, v in i.items(): + if v is REMOVE_THIS_KEY: + pops.append(k) + elif isinstance(v, dict): + l.append(v) + for k in pops: + i.pop(k) + + +def mergedicts(d1, d2, remove=True): + '''Recursively merge two dictionaries + + First dictionary is modified in-place. + ''' + _setmerged(d1, d2) + for k in d2: + if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): + mergedicts(d1[k], d2[k], remove) + elif remove and d2[k] is REMOVE_THIS_KEY: + d1.pop(k, None) + else: + if remove and isinstance(d2[k], dict): + _clear_special_values(d2[k]) + d1[k] = d2[k] + + +def mergedefaults(d1, d2): + '''Recursively merge two dictionaries, keeping existing values + + First dictionary is modified in-place. + ''' + for k in d2: + if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): + mergedefaults(d1[k], d2[k]) + else: + d1.setdefault(k, d2[k]) + + +def _setmerged(d1, d2): + if hasattr(d1, 'setmerged'): + d1.setmerged(d2) + + +def mergedicts_copy(d1, d2): + '''Recursively merge two dictionaries. + + Dictionaries are not modified. Copying happens only if necessary. Assumes + that first dictionary supports .copy() method. + ''' + ret = d1.copy() + _setmerged(ret, d2) + for k in d2: + if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict): + ret[k] = mergedicts_copy(d1[k], d2[k]) + else: + ret[k] = d2[k] + return ret + + +def updated(d, *args, **kwargs): + '''Copy dictionary and update it with provided arguments + ''' + d = d.copy() + d.update(*args, **kwargs) + return d diff --git a/powerline-bin/powerline/lib/encoding.py b/powerline-bin/powerline/lib/encoding.py new file mode 100644 index 0000000..76a51d8 --- /dev/null +++ b/powerline-bin/powerline/lib/encoding.py @@ -0,0 +1,125 @@ +# vim:fileencoding=utf-8:noet + +'''Encodings support + +This is the only module from which functions obtaining encoding should be +exported. Note: you should always care about errors= argument since it is not +guaranteed that encoding returned by some function can encode/decode given +string. + +All functions in this module must always return a valid encoding. Most of them +are not thread-safe. +''' + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import locale + + +def get_preferred_file_name_encoding(): + '''Get preferred file name encoding + ''' + return ( + sys.getfilesystemencoding() + or locale.getpreferredencoding() + or 'utf-8' + ) + + +def get_preferred_file_contents_encoding(): + '''Get encoding preferred for file contents + ''' + return ( + locale.getpreferredencoding() + or 'utf-8' + ) + + +def get_preferred_output_encoding(): + '''Get encoding that should be used for printing strings + + .. warning:: + Falls back to ASCII, so that output is most likely to be displayed + correctly. + ''' + if hasattr(locale, 'LC_MESSAGES'): + return ( + locale.getlocale(locale.LC_MESSAGES)[1] + or locale.getdefaultlocale()[1] + or 'ascii' + ) + + return ( + locale.getdefaultlocale()[1] + or 'ascii' + ) + + +def get_preferred_input_encoding(): + '''Get encoding that should be used for reading shell command output + + .. warning:: + Falls back to latin1 so that function is less likely to throw as decoded + output is primary searched for ASCII values. + ''' + if hasattr(locale, 'LC_MESSAGES'): + return ( + locale.getlocale(locale.LC_MESSAGES)[1] + or locale.getdefaultlocale()[1] + or 'latin1' + ) + + return ( + locale.getdefaultlocale()[1] + or 'latin1' + ) + + +def get_preferred_arguments_encoding(): + '''Get encoding that should be used for command-line arguments + + .. warning:: + Falls back to latin1 so that function is less likely to throw as + non-ASCII command-line arguments most likely contain non-ASCII + filenames and screwing them up due to unidentified locale is not much of + a problem. + ''' + return ( + locale.getdefaultlocale()[1] + or 'latin1' + ) + + +def get_preferred_environment_encoding(): + '''Get encoding that should be used for decoding environment variables + ''' + return ( + locale.getpreferredencoding() + or 'utf-8' + ) + + +def get_unicode_writer(stream=sys.stdout, encoding=None, errors='replace'): + '''Get function which will write unicode string to the given stream + + Writing is done using encoding returned by + :py:func:`get_preferred_output_encoding`. + + :param file stream: + Stream to write to. Default value is :py:attr:`sys.stdout`. + :param str encoding: + Determines which encoding to use. If this argument is specified then + :py:func:`get_preferred_output_encoding` is not used. + :param str errors: + Determines what to do with characters which cannot be encoded. See + ``errors`` argument of :py:func:`codecs.encode`. + + :return: Callable which writes unicode string to the given stream using + the preferred output encoding. + ''' + encoding = encoding or get_preferred_output_encoding() + if sys.version_info < (3,) or not hasattr(stream, 'buffer'): + return lambda s: stream.write(s.encode(encoding, errors)) + else: + return lambda s: stream.buffer.write(s.encode(encoding, errors)) diff --git a/powerline-bin/powerline/lib/humanize_bytes.py b/powerline-bin/powerline/lib/humanize_bytes.py new file mode 100644 index 0000000..c98a117 --- /dev/null +++ b/powerline-bin/powerline/lib/humanize_bytes.py @@ -0,0 +1,25 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from math import log + + +unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2])) + + +def humanize_bytes(num, suffix='B', si_prefix=False): + '''Return a human friendly byte representation. + + Modified version from http://stackoverflow.com/questions/1094841 + ''' + if num == 0: + return '0 ' + suffix + div = 1000 if si_prefix else 1024 + exponent = min(int(log(num, div)) if num else 0, len(unit_list) - 1) + quotient = float(num) / div ** exponent + unit, decimals = unit_list[exponent] + if unit and not si_prefix: + unit = unit.upper() + 'i' + return ('{{quotient:.{decimals}f}} {{unit}}{{suffix}}' + .format(decimals=decimals) + .format(quotient=quotient, unit=unit, suffix=suffix)) diff --git a/powerline-bin/powerline/lib/inotify.py b/powerline-bin/powerline/lib/inotify.py new file mode 100644 index 0000000..8b74a7f --- /dev/null +++ b/powerline-bin/powerline/lib/inotify.py @@ -0,0 +1,184 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import os +import errno +import ctypes +import struct + +from ctypes.util import find_library + +from powerline.lib.encoding import get_preferred_file_name_encoding + + +__copyright__ = '2013, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +class INotifyError(Exception): + pass + + +_inotify = None + + +def load_inotify(): + ''' Initialize the inotify library ''' + global _inotify + if _inotify is None: + if hasattr(sys, 'getwindowsversion'): + # On windows abort before loading the C library. Windows has + # multiple, incompatible C runtimes, and we have no way of knowing + # if the one chosen by ctypes is compatible with the currently + # loaded one. + raise INotifyError('INotify not available on windows') + if sys.platform == 'darwin': + raise INotifyError('INotify not available on OS X') + if not hasattr(ctypes, 'c_ssize_t'): + raise INotifyError('You need python >= 2.7 to use inotify') + name = find_library('c') + if not name: + raise INotifyError('Cannot find C library') + libc = ctypes.CDLL(name, use_errno=True) + for function in ('inotify_add_watch', 'inotify_init1', 'inotify_rm_watch'): + if not hasattr(libc, function): + raise INotifyError('libc is too old') + # inotify_init1() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) + init1 = prototype(('inotify_init1', libc), ((1, 'flags', 0),)) + + # inotify_add_watch() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32, use_errno=True) + add_watch = prototype(('inotify_add_watch', libc), ( + (1, 'fd'), (1, 'pathname'), (1, 'mask'))) + + # inotify_rm_watch() + prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, use_errno=True) + rm_watch = prototype(('inotify_rm_watch', libc), ( + (1, 'fd'), (1, 'wd'))) + + # read() + prototype = ctypes.CFUNCTYPE(ctypes.c_ssize_t, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, use_errno=True) + read = prototype(('read', libc), ( + (1, 'fd'), (1, 'buf'), (1, 'count'))) + _inotify = (init1, add_watch, rm_watch, read) + return _inotify + + +class INotify(object): + + # See for the flags defined below + + # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. + ACCESS = 0x00000001 # File was accessed. + MODIFY = 0x00000002 # File was modified. + ATTRIB = 0x00000004 # Metadata changed. + CLOSE_WRITE = 0x00000008 # Writtable file was closed. + CLOSE_NOWRITE = 0x00000010 # Unwrittable file closed. + OPEN = 0x00000020 # File was opened. + MOVED_FROM = 0x00000040 # File was moved from X. + MOVED_TO = 0x00000080 # File was moved to Y. + CREATE = 0x00000100 # Subfile was created. + DELETE = 0x00000200 # Subfile was deleted. + DELETE_SELF = 0x00000400 # Self was deleted. + MOVE_SELF = 0x00000800 # Self was moved. + + # Events sent by the kernel. + UNMOUNT = 0x00002000 # Backing fs was unmounted. + Q_OVERFLOW = 0x00004000 # Event queued overflowed. + IGNORED = 0x00008000 # File was ignored. + + # Helper events. + CLOSE = (CLOSE_WRITE | CLOSE_NOWRITE) # Close. + MOVE = (MOVED_FROM | MOVED_TO) # Moves. + + # Special flags. + ONLYDIR = 0x01000000 # Only watch the path if it is a directory. + DONT_FOLLOW = 0x02000000 # Do not follow a sym link. + EXCL_UNLINK = 0x04000000 # Exclude events on unlinked objects. + MASK_ADD = 0x20000000 # Add to the mask of an already existing watch. + ISDIR = 0x40000000 # Event occurred against dir. + ONESHOT = 0x80000000 # Only send event once. + + # All events which a program can wait on. + ALL_EVENTS = ( + ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | OPEN | + MOVED_FROM | MOVED_TO | CREATE | DELETE | DELETE_SELF | MOVE_SELF + ) + + # See + CLOEXEC = 0x80000 + NONBLOCK = 0x800 + + def __init__(self, cloexec=True, nonblock=True): + self._init1, self._add_watch, self._rm_watch, self._read = load_inotify() + flags = 0 + if cloexec: + flags |= self.CLOEXEC + if nonblock: + flags |= self.NONBLOCK + self._inotify_fd = self._init1(flags) + if self._inotify_fd == -1: + raise INotifyError(os.strerror(ctypes.get_errno())) + + self._buf = ctypes.create_string_buffer(5000) + self.fenc = get_preferred_file_name_encoding() + self.hdr = struct.Struct(b'iIII') + # We keep a reference to os to prevent it from being deleted + # during interpreter shutdown, which would lead to errors in the + # __del__ method + self.os = os + + def handle_error(self): + eno = ctypes.get_errno() + extra = '' + if eno == errno.ENOSPC: + extra = 'You may need to increase the inotify limits on your system, via /proc/sys/fs/inotify/max_user_*' + raise OSError(eno, self.os.strerror(eno) + str(extra)) + + def __del__(self): + # This method can be called during interpreter shutdown, which means we + # must do the absolute minimum here. Note that there could be running + # daemon threads that are trying to call other methods on this object. + try: + self.os.close(self._inotify_fd) + except (AttributeError, TypeError): + pass + + def close(self): + if hasattr(self, '_inotify_fd'): + self.os.close(self._inotify_fd) + del self.os + del self._add_watch + del self._rm_watch + del self._inotify_fd + + def read(self, get_name=True): + buf = [] + while True: + num = self._read(self._inotify_fd, self._buf, len(self._buf)) + if num == 0: + break + if num < 0: + en = ctypes.get_errno() + if en == errno.EAGAIN: + break # No more data + if en == errno.EINTR: + continue # Interrupted, try again + raise OSError(en, self.os.strerror(en)) + buf.append(self._buf.raw[:num]) + raw = b''.join(buf) + pos = 0 + lraw = len(raw) + while lraw - pos >= self.hdr.size: + wd, mask, cookie, name_len = self.hdr.unpack_from(raw, pos) + pos += self.hdr.size + name = None + if get_name: + name = raw[pos:pos + name_len].rstrip(b'\0') + pos += name_len + self.process_event(wd, mask, cookie, name) + + def process_event(self, *args): + raise NotImplementedError() diff --git a/powerline-bin/powerline/lib/memoize.py b/powerline-bin/powerline/lib/memoize.py new file mode 100644 index 0000000..cedbe45 --- /dev/null +++ b/powerline-bin/powerline/lib/memoize.py @@ -0,0 +1,42 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from functools import wraps + +from powerline.lib.monotonic import monotonic + + +def default_cache_key(**kwargs): + return frozenset(kwargs.items()) + + +class memoize(object): + '''Memoization decorator with timeout.''' + def __init__(self, timeout, cache_key=default_cache_key, cache_reg_func=None): + self.timeout = timeout + self.cache_key = cache_key + self.cache = {} + self.cache_reg_func = cache_reg_func + + def __call__(self, func): + @wraps(func) + def decorated_function(**kwargs): + if self.cache_reg_func: + self.cache_reg_func(self.cache) + self.cache_reg_func = None + + key = self.cache_key(**kwargs) + try: + cached = self.cache.get(key, None) + except TypeError: + return func(**kwargs) + # Handle case when time() appears to be less then cached['time'] due + # to clock updates. Not applicable for monotonic clock, but this + # case is currently rare. + if cached is None or not (cached['time'] < monotonic() < cached['time'] + self.timeout): + cached = self.cache[key] = { + 'result': func(**kwargs), + 'time': monotonic(), + } + return cached['result'] + return decorated_function diff --git a/powerline-bin/powerline/lib/monotonic.py b/powerline-bin/powerline/lib/monotonic.py new file mode 100644 index 0000000..cd7c414 --- /dev/null +++ b/powerline-bin/powerline/lib/monotonic.py @@ -0,0 +1,100 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + try: + # >=python-3.3, Unix + from time import clock_gettime + try: + # >={kernel}-sources-2.6.28 + from time import CLOCK_MONOTONIC_RAW as CLOCK_ID + except ImportError: + from time import CLOCK_MONOTONIC as CLOCK_ID + + monotonic = lambda: clock_gettime(CLOCK_ID) + except ImportError: + # >=python-3.3 + from time import monotonic +except ImportError: + import ctypes + import sys + + try: + if sys.platform == 'win32': + # Windows only + GetTickCount64 = ctypes.windll.kernel32.GetTickCount64 + GetTickCount64.restype = ctypes.c_ulonglong + + def monotonic(): + return GetTickCount64() / 1000 + + elif sys.platform == 'darwin': + # Mac OS X + from ctypes.util import find_library + + libc_name = find_library('c') + if not libc_name: + raise OSError + + libc = ctypes.CDLL(libc_name, use_errno=True) + + mach_absolute_time = libc.mach_absolute_time + mach_absolute_time.argtypes = () + mach_absolute_time.restype = ctypes.c_uint64 + + class mach_timebase_info_data_t(ctypes.Structure): + _fields_ = ( + ('numer', ctypes.c_uint32), + ('denom', ctypes.c_uint32), + ) + mach_timebase_info_data_p = ctypes.POINTER(mach_timebase_info_data_t) + + _mach_timebase_info = libc.mach_timebase_info + _mach_timebase_info.argtypes = (mach_timebase_info_data_p,) + _mach_timebase_info.restype = ctypes.c_int + + def mach_timebase_info(): + timebase = mach_timebase_info_data_t() + _mach_timebase_info(ctypes.byref(timebase)) + return (timebase.numer, timebase.denom) + + timebase = mach_timebase_info() + factor = timebase[0] / timebase[1] * 1e-9 + + def monotonic(): + return mach_absolute_time() * factor + else: + # linux only (no librt on OS X) + import os + + # See + CLOCK_MONOTONIC = 1 + CLOCK_MONOTONIC_RAW = 4 + + class timespec(ctypes.Structure): + _fields_ = ( + ('tv_sec', ctypes.c_long), + ('tv_nsec', ctypes.c_long) + ) + tspec = timespec() + + librt = ctypes.CDLL('librt.so.1', use_errno=True) + clock_gettime = librt.clock_gettime + clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] + + if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(tspec)) == 0: + # >={kernel}-sources-2.6.28 + clock_id = CLOCK_MONOTONIC_RAW + elif clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) == 0: + clock_id = CLOCK_MONOTONIC + else: + raise OSError + + def monotonic(): + if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) != 0: + errno_ = ctypes.get_errno() + raise OSError(errno_, os.strerror(errno_)) + return tspec.tv_sec + tspec.tv_nsec / 1e9 + + except: + from time import time as monotonic # NOQA diff --git a/powerline-bin/powerline/lib/overrides.py b/powerline-bin/powerline/lib/overrides.py new file mode 100644 index 0000000..3257d98 --- /dev/null +++ b/powerline-bin/powerline/lib/overrides.py @@ -0,0 +1,80 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import json + +from powerline.lib.dict import REMOVE_THIS_KEY + + +def parse_value(s): + '''Convert string to Python object + + Rules: + + * Empty string means that corresponding key should be removed from the + dictionary. + * Strings that start with a minus, digit or with some character that starts + JSON collection or string object are parsed as JSON. + * JSON special values ``null``, ``true``, ``false`` (case matters) are + parsed as JSON. + * All other values are considered to be raw strings. + + :param str s: Parsed string. + + :return: Python object. + ''' + if not s: + return REMOVE_THIS_KEY + elif s[0] in '"{[0123456789-' or s in ('null', 'true', 'false'): + return json.loads(s) + else: + return s + + +def keyvaluesplit(s): + '''Split K1.K2=VAL into K1.K2 and parsed VAL + ''' + if '=' not in s: + raise TypeError('Option must look like option=json_value') + if s[0] == '_': + raise ValueError('Option names must not start with `_\'') + idx = s.index('=') + o = s[:idx] + val = parse_value(s[idx + 1:]) + return (o, val) + + +def parsedotval(s): + '''Parse K1.K2=VAL into {"K1":{"K2":VAL}} + + ``VAL`` is processed according to rules defined in :py:func:`parse_value`. + ''' + if type(s) is tuple: + o, val = s + val = parse_value(val) + else: + o, val = keyvaluesplit(s) + + keys = o.split('.') + if len(keys) > 1: + r = (keys[0], {}) + rcur = r[1] + for key in keys[1:-1]: + rcur[key] = {} + rcur = rcur[key] + rcur[keys[-1]] = val + return r + else: + return (o, val) + + +def parse_override_var(s): + '''Parse a semicolon-separated list of strings into a sequence of values + + Emits the same items in sequence as :py:func:`parsedotval` does. + ''' + return ( + parsedotval(item) + for item in s.split(';') + if item + ) diff --git a/powerline-bin/powerline/lib/path.py b/powerline-bin/powerline/lib/path.py new file mode 100644 index 0000000..49ff433 --- /dev/null +++ b/powerline-bin/powerline/lib/path.py @@ -0,0 +1,18 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + + +def realpath(path): + return os.path.abspath(os.path.realpath(path)) + + +def join(*components): + if any((isinstance(p, bytes) for p in components)): + return os.path.join(*[ + p if isinstance(p, bytes) else p.encode('ascii') + for p in components + ]) + else: + return os.path.join(*components) diff --git a/powerline-bin/powerline/lib/shell.py b/powerline-bin/powerline/lib/shell.py new file mode 100644 index 0000000..2082e82 --- /dev/null +++ b/powerline-bin/powerline/lib/shell.py @@ -0,0 +1,133 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import os + +from subprocess import Popen, PIPE +from functools import partial + +from powerline.lib.encoding import get_preferred_input_encoding, get_preferred_output_encoding + + +if sys.platform.startswith('win32'): + # Prevent windows from launching consoles when calling commands + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx + Popen = partial(Popen, creationflags=0x08000000) + + +def run_cmd(pl, cmd, stdin=None, strip=True): + '''Run command and return its stdout, stripped + + If running command fails returns None and logs failure to ``pl`` argument. + + :param PowerlineLogger pl: + Logger used to log failures. + :param list cmd: + Command which will be run. + :param str stdin: + String passed to command. May be None. + :param bool strip: + True if the result should be stripped. + ''' + try: + p = Popen(cmd, shell=False, stdout=PIPE, stdin=PIPE) + except OSError as e: + pl.exception('Could not execute command ({0}): {1}', e, cmd) + return None + else: + stdout, err = p.communicate( + stdin if stdin is None else stdin.encode(get_preferred_output_encoding())) + stdout = stdout.decode(get_preferred_input_encoding()) + return stdout.strip() if strip else stdout + + +def asrun(pl, ascript): + '''Run the given AppleScript and return the standard output and error.''' + return run_cmd(pl, ['osascript', '-'], ascript) + + +def readlines(cmd, cwd): + '''Run command and read its output, line by line + + :param list cmd: + Command which will be run. + :param str cwd: + Working directory of the command which will be run. + ''' + p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) + encoding = get_preferred_input_encoding() + p.stderr.close() + with p.stdout: + for line in p.stdout: + yield line[:-1].decode(encoding) + + +try: + from shutil import which +except ImportError: + # shutil.which was added in python-3.3. Here is what was added: + # Lib/shutil.py, commit 5abe28a9c8fe701ba19b1db5190863384e96c798 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + '''Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + ``mode`` defaults to os.F_OK | os.X_OK. ``path`` defaults to the result + of ``os.environ.get('PATH')``, or can be overridden with a custom search + path. + ''' + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return ( + os.path.exists(fn) + and os.access(fn, mode) + and not os.path.isdir(fn) + ) + + # If we’re given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get('PATH', os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == 'win32': + # The current directory takes precedence on Windows. + if os.curdir not in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get('PATHEXT', '').split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given 'python.exe'. + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don’t have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if normdir not in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None diff --git a/powerline-bin/powerline/lib/threaded.py b/powerline-bin/powerline/lib/threaded.py new file mode 100644 index 0000000..e5a6b3e --- /dev/null +++ b/powerline-bin/powerline/lib/threaded.py @@ -0,0 +1,262 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from threading import Thread, Lock, Event +from types import MethodType + +from powerline.lib.monotonic import monotonic +from powerline.segments import Segment + + +class MultiRunnedThread(object): + daemon = True + + def __init__(self): + self.thread = None + + def is_alive(self): + return self.thread and self.thread.is_alive() + + def start(self): + self.shutdown_event.clear() + self.thread = Thread(target=self.run) + self.thread.daemon = self.daemon + self.thread.start() + + def join(self, *args, **kwargs): + if self.thread: + return self.thread.join(*args, **kwargs) + return None + + +class ThreadedSegment(Segment, MultiRunnedThread): + min_sleep_time = 0.1 + update_first = True + interval = 1 + daemon = False + + argmethods = ('render', 'set_state') + + def __init__(self): + super(ThreadedSegment, self).__init__() + self.run_once = True + self.crashed = False + self.crashed_value = None + self.update_value = None + self.updated = False + + def __call__(self, pl, update_first=True, **kwargs): + if self.run_once: + self.pl = pl + self.set_state(**kwargs) + update_value = self.get_update_value(True) + elif not self.is_alive(): + # Without this we will not have to wait long until receiving bug “I + # opened vim, but branch information is only shown after I move + # cursor”. + # + # If running once .update() is called in __call__. + self.start() + update_value = self.get_update_value(self.do_update_first) + else: + update_value = self.get_update_value(not self.updated) + + if self.crashed: + return self.crashed_value + + return self.render(update_value, update_first=update_first, pl=pl, **kwargs) + + def set_update_value(self): + try: + self.update_value = self.update(self.update_value) + except Exception as e: + self.exception('Exception while updating: {0}', str(e)) + self.crashed = True + except KeyboardInterrupt: + self.warn('Caught keyboard interrupt while updating') + self.crashed = True + else: + self.crashed = False + self.updated = True + + def get_update_value(self, update=False): + if update: + self.set_update_value() + return self.update_value + + def run(self): + if self.do_update_first: + start_time = monotonic() + while True: + self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) + if self.shutdown_event.is_set(): + break + start_time = monotonic() + self.set_update_value() + else: + while not self.shutdown_event.is_set(): + start_time = monotonic() + self.set_update_value() + self.shutdown_event.wait(max(self.interval - (monotonic() - start_time), self.min_sleep_time)) + + def shutdown(self): + self.shutdown_event.set() + if self.daemon and self.is_alive(): + # Give the worker thread a chance to shutdown, but don’t block for + # too long + self.join(0.01) + + def set_interval(self, interval=None): + # Allowing “interval” keyword in configuration. + # Note: Here **kwargs is needed to support foreign data, in subclasses + # it can be seen in a number of places in order to support + # .set_interval(). + interval = interval or getattr(self, 'interval') + self.interval = interval + + def set_state(self, interval=None, update_first=True, shutdown_event=None, **kwargs): + self.set_interval(interval) + self.shutdown_event = shutdown_event or Event() + self.do_update_first = update_first and self.update_first + self.updated = self.updated or (not self.do_update_first) + + def startup(self, pl, **kwargs): + self.run_once = False + self.pl = pl + self.daemon = pl.use_daemon_threads + + self.set_state(**kwargs) + + if not self.is_alive(): + self.start() + + def critical(self, *args, **kwargs): + self.pl.critical(prefix=self.__class__.__name__, *args, **kwargs) + + def exception(self, *args, **kwargs): + self.pl.exception(prefix=self.__class__.__name__, *args, **kwargs) + + def info(self, *args, **kwargs): + self.pl.info(prefix=self.__class__.__name__, *args, **kwargs) + + def error(self, *args, **kwargs): + self.pl.error(prefix=self.__class__.__name__, *args, **kwargs) + + def warn(self, *args, **kwargs): + self.pl.warn(prefix=self.__class__.__name__, *args, **kwargs) + + def debug(self, *args, **kwargs): + self.pl.debug(prefix=self.__class__.__name__, *args, **kwargs) + + def argspecobjs(self): + for name in self.argmethods: + try: + yield name, getattr(self, name) + except AttributeError: + pass + + def additional_args(self): + return (('interval', self.interval),) + + _omitted_args = { + 'render': (0,), + 'set_state': ('shutdown_event',), + } + + def omitted_args(self, name, method): + ret = self._omitted_args.get(name, ()) + if isinstance(getattr(self, name, None), MethodType): + ret = tuple((i + 1 if isinstance(i, int) else i for i in ret)) + return ret + + +class KwThreadedSegment(ThreadedSegment): + update_first = True + + argmethods = ('render', 'set_state', 'key', 'render_one') + + def __init__(self): + super(KwThreadedSegment, self).__init__() + self.updated = True + self.update_value = ({}, set()) + self.write_lock = Lock() + self.new_queries = [] + + @staticmethod + def key(**kwargs): + return frozenset(kwargs.items()) + + def render(self, update_value, update_first, key=None, after_update=False, **kwargs): + queries, crashed = update_value + if key is None: + key = self.key(**kwargs) + if key in crashed: + return self.crashed_value + + try: + update_state = queries[key][1] + except KeyError: + with self.write_lock: + self.new_queries.append(key) + if self.do_update_first or self.run_once: + if after_update: + self.error('internal error: value was not computed even though update_first was set') + update_state = None + else: + return self.render( + update_value=self.get_update_value(True), + update_first=False, + key=key, + after_update=True, + **kwargs + ) + else: + update_state = None + + return self.render_one(update_state, **kwargs) + + def update_one(self, crashed, updates, key): + try: + updates[key] = (monotonic(), self.compute_state(key)) + except Exception as e: + self.exception('Exception while computing state for {0!r}: {1}', key, str(e)) + crashed.add(key) + except KeyboardInterrupt: + self.warn('Interrupt while computing state for {0!r}', key) + crashed.add(key) + + def update(self, old_update_value): + updates = {} + crashed = set() + update_value = (updates, crashed) + queries = old_update_value[0] + + new_queries = self.new_queries + with self.write_lock: + self.new_queries = [] + + for key, (last_query_time, state) in queries.items(): + if last_query_time < monotonic() < last_query_time + self.interval: + updates[key] = (last_query_time, state) + else: + self.update_one(crashed, updates, key) + + for key in new_queries: + self.update_one(crashed, updates, key) + + return update_value + + def set_state(self, interval=None, update_first=True, shutdown_event=None, **kwargs): + self.set_interval(interval) + self.do_update_first = update_first and self.update_first + self.shutdown_event = shutdown_event or Event() + + @staticmethod + def render_one(update_state, **kwargs): + return update_state + + _omitted_args = { + 'render': ('update_value', 'key', 'after_update'), + 'set_state': ('shutdown_event',), + 'render_one': (0,), + } diff --git a/powerline-bin/powerline/lib/unicode.py b/powerline-bin/powerline/lib/unicode.py new file mode 100644 index 0000000..152bacd --- /dev/null +++ b/powerline-bin/powerline/lib/unicode.py @@ -0,0 +1,283 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import codecs + +from unicodedata import east_asian_width, combining + +from powerline.lib.encoding import get_preferred_output_encoding + + +try: + from __builtin__ import unicode +except ImportError: + unicode = str + + +try: + from __builtin__ import unichr +except ImportError: + unichr = chr + + +if sys.maxunicode < 0x10FFFF: + _unichr = unichr + + def unichr(ch): + if ch <= sys.maxunicode: + return _unichr(ch) + else: + ch -= 0x10000 + return _unichr((ch >> 10) + 0xD800) + _unichr((ch & ((1 << 10) - 1)) + 0xDC00) + + +def u(s): + '''Return unicode instance assuming UTF-8 encoded string. + ''' + if type(s) is unicode: + return s + else: + return unicode(s, 'utf-8') + + +if sys.version_info < (3,): + def tointiter(s): + '''Convert a byte string to the sequence of integers + ''' + return (ord(c) for c in s) +else: + def tointiter(s): + '''Convert a byte string to the sequence of integers + ''' + return iter(s) + + +def powerline_decode_error(e): + if not isinstance(e, UnicodeDecodeError): + raise NotImplementedError + return (''.join(( + '<{0:02X}>'.format(c) + for c in tointiter(e.object[e.start:e.end]) + )), e.end) + + +codecs.register_error('powerline_decode_error', powerline_decode_error) + + +last_swe_idx = 0 + + +def register_strwidth_error(strwidth): + '''Create new encode errors handling method similar to ``replace`` + + Like ``replace`` this method uses question marks in place of the characters + that cannot be represented in the requested encoding. Unlike ``replace`` the + amount of question marks is identical to the amount of display cells + offending character occupies. Thus encoding ``…`` (U+2026, HORIZONTAL + ELLIPSIS) to ``latin1`` will emit one question mark, but encoding ``A`` + (U+FF21, FULLWIDTH LATIN CAPITAL LETTER A) will emit two question marks. + + Since width of some characters depends on the terminal settings and + powerline knows how to respect them a single error handling method cannot be + used. Instead of it the generator function is used which takes ``strwidth`` + function (function that knows how to compute string width respecting all + needed settings) and emits new error handling method name. + + :param function strwidth: + Function that computs string width measured in display cells the string + occupies when displayed. + + :return: New error handling method name. + ''' + global last_swe_idx + last_swe_idx += 1 + + def powerline_encode_strwidth_error(e): + if not isinstance(e, UnicodeEncodeError): + raise NotImplementedError + return ('?' * strwidth(e.object[e.start:e.end]), e.end) + + ename = 'powerline_encode_strwidth_error_{0}'.format(last_swe_idx) + codecs.register_error(ename, powerline_encode_strwidth_error) + return ename + + +def out_u(s): + '''Return unicode string suitable for displaying + + Unlike other functions assumes get_preferred_output_encoding() first. Unlike + u() does not throw exceptions for invalid unicode strings. Unlike + safe_unicode() does throw an exception if object is not a string. + ''' + if isinstance(s, unicode): + return s + elif isinstance(s, bytes): + return unicode(s, get_preferred_output_encoding(), 'powerline_decode_error') + else: + raise TypeError('Expected unicode or bytes instance, got {0}'.format(repr(type(s)))) + + +def safe_unicode(s): + '''Return unicode instance without raising an exception. + + Order of assumptions: + * ASCII string or unicode object + * UTF-8 string + * Object with __str__() or __repr__() method that returns UTF-8 string or + unicode object (depending on python version) + * String in powerline.lib.encoding.get_preferred_output_encoding() encoding + * If everything failed use safe_unicode on last exception with which + everything failed + ''' + try: + try: + if type(s) is bytes: + return unicode(s, 'ascii') + else: + return unicode(s) + except UnicodeDecodeError: + try: + return unicode(s, 'utf-8') + except TypeError: + return unicode(str(s), 'utf-8') + except UnicodeDecodeError: + return unicode(s, get_preferred_output_encoding()) + except Exception as e: + return safe_unicode(e) + + +class FailedUnicode(unicode): + '''Builtin ``unicode`` subclass indicating fatal error + + If your code for some reason wants to determine whether `.render()` method + failed it should check returned string for being a FailedUnicode instance. + Alternatively you could subclass Powerline and override `.render()` method + to do what you like in place of catching the exception and returning + FailedUnicode. + ''' + pass + + +if sys.version_info < (3,): + def string(s): + if type(s) is not str: + return s.encode('utf-8') + else: + return s +else: + def string(s): + if type(s) is not str: + return s.decode('utf-8') + else: + return s + + +string.__doc__ = ( + '''Transform ``unicode`` or ``bytes`` object into ``str`` object + + On Python-2 this encodes ``unicode`` to ``bytes`` (which is ``str``) using + UTF-8 encoding; on Python-3 this decodes ``bytes`` to ``unicode`` (which is + ``str``) using UTF-8 encoding. + + Useful for functions that expect an ``str`` object in both unicode versions, + not caring about the semantic differences between them in Python-2 and + Python-3. + ''' +) + + +def surrogate_pair_to_character(high, low): + '''Transform a pair of surrogate codepoints to one codepoint + ''' + return 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00) + + +_strwidth_documentation = ( + '''Compute string width in display cells + + {0} + + :param dict width_data: + Dictionary which maps east_asian_width property values to strings + lengths. It is expected to contain the following keys and values (from + `East Asian Width annex `_): + + === ====== =========================================================== + Key Value Description + === ====== =========================================================== + F 2 Fullwidth: all characters that are defined as Fullwidth in + the Unicode Standard [Unicode] by having a compatibility + decomposition of type to characters elsewhere in the + Unicode Standard that are implicitly narrow but unmarked. + H 1 Halfwidth: all characters that are explicitly defined as + Halfwidth in the Unicode Standard by having a compatibility + decomposition of type to characters elsewhere in + the Unicode Standard that are implicitly wide but unmarked, + plus U+20A9 ₩ WON SIGN. + W 2 Wide: all other characters that are always wide. These + characters occur only in the context of East Asian + typography where they are wide characters (such as the + Unified Han Ideographs or Squared Katakana Symbols). This + category includes characters that have explicit halfwidth + counterparts. + Na 1 Narrow: characters that are always narrow and have explicit + fullwidth or wide counterparts. These characters are + implicitly narrow in East Asian typography and legacy + character sets because they have explicit fullwidth or wide + counterparts. All of ASCII is an example of East Asian + Narrow characters. + A 1 or 2 Ambigious: characters that may sometimes be wide and + sometimes narrow. Ambiguous characters require additional + information not contained in the character code to further + resolve their width. This information is usually defined in + terminal setting that should in turn respect glyphs widths + in used fonts. Also see :ref:`ambiwidth configuration + option `. + N 1 Neutral characters: character that does not occur in legacy + East Asian character sets. + === ====== =========================================================== + + :param unicode string: + String whose width will be calculated. + + :return: unsigned integer.''') + + +def strwidth_ucs_4(width_data, string): + return sum((( + ( + 0 + ) if combining(symbol) else ( + width_data[east_asian_width(symbol)] + ) + ) for symbol in string)) + + +strwidth_ucs_4.__doc__ = _strwidth_documentation.format( + '''This version of function expects that characters above 0xFFFF are + represented using one symbol. This is only the case in UCS-4 Python builds. + + .. note: + Even in UCS-4 Python builds it is possible to represent characters above + 0xFFFF using surrogate pairs. Characters represented this way are not + supported.''') + + +def strwidth_ucs_2(width_data, string): + return sum((( + ( + width_data[east_asian_width(string[i - 1] + symbol)] + ) if 0xDC00 <= ord(symbol) <= 0xDFFF else ( + 0 + ) if combining(symbol) or 0xD800 <= ord(symbol) <= 0xDBFF else ( + width_data[east_asian_width(symbol)] + ) + ) for i, symbol in enumerate(string))) + + +strwidth_ucs_2.__doc__ = _strwidth_documentation.format( + '''This version of function expects that characters above 0xFFFF are + represented using two symbols forming a surrogate pair, which is the only + option in UCS-2 Python builds. It still works correctly in UCS-4 Python + builds, but is slower then its UCS-4 counterpart.''') diff --git a/powerline-bin/powerline/lib/url.py b/powerline-bin/powerline/lib/url.py new file mode 100644 index 0000000..f25919c --- /dev/null +++ b/powerline-bin/powerline/lib/url.py @@ -0,0 +1,17 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + from urllib.error import HTTPError # NOQA + from urllib.request import urlopen # NOQA + from urllib.parse import urlencode as urllib_urlencode # NOQA +except ImportError: + from urllib2 import urlopen, HTTPError # NOQA + from urllib import urlencode as urllib_urlencode # NOQA + + +def urllib_read(url): + try: + return urlopen(url, timeout=10).read().decode('utf-8') + except HTTPError: + return diff --git a/powerline-bin/powerline/lib/vcs/__init__.py b/powerline-bin/powerline/lib/vcs/__init__.py new file mode 100644 index 0000000..1b7f5e2 --- /dev/null +++ b/powerline-bin/powerline/lib/vcs/__init__.py @@ -0,0 +1,276 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import errno + +from threading import Lock +from collections import defaultdict + +from powerline.lib.watcher import create_tree_watcher +from powerline.lib.unicode import out_u +from powerline.lib.path import join + + +def generate_directories(path): + if os.path.isdir(path): + yield path + while True: + if os.path.ismount(path): + break + old_path = path + path = os.path.dirname(path) + if path == old_path or not path: + break + yield path + + +_file_watcher = None + + +def file_watcher(create_watcher): + global _file_watcher + if _file_watcher is None: + _file_watcher = create_watcher() + return _file_watcher + + +_branch_watcher = None + + +def branch_watcher(create_watcher): + global _branch_watcher + if _branch_watcher is None: + _branch_watcher = create_watcher() + return _branch_watcher + + +branch_name_cache = {} +branch_lock = Lock() +file_status_lock = Lock() + + +def get_branch_name(directory, config_file, get_func, create_watcher): + global branch_name_cache + with branch_lock: + # Check if the repo directory was moved/deleted + fw = branch_watcher(create_watcher) + is_watched = fw.is_watching(directory) + try: + changed = fw(directory) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + changed = True + if changed: + branch_name_cache.pop(config_file, None) + # Remove the watches for this repo + if is_watched: + fw.unwatch(directory) + fw.unwatch(config_file) + else: + # Check if the config file has changed + try: + changed = fw(config_file) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + # Config file does not exist (happens for mercurial) + if config_file not in branch_name_cache: + branch_name_cache[config_file] = out_u(get_func(directory, config_file)) + if changed: + # Config file has changed or was not tracked + branch_name_cache[config_file] = out_u(get_func(directory, config_file)) + return branch_name_cache[config_file] + + +class FileStatusCache(dict): + def __init__(self): + self.dirstate_map = defaultdict(set) + self.ignore_map = defaultdict(set) + self.keypath_ignore_map = {} + + def update_maps(self, keypath, directory, dirstate_file, ignore_file_name, extra_ignore_files): + parent = keypath + ignore_files = set() + while parent != directory: + nparent = os.path.dirname(keypath) + if nparent == parent: + break + parent = nparent + ignore_files.add(join(parent, ignore_file_name)) + for f in extra_ignore_files: + ignore_files.add(f) + self.keypath_ignore_map[keypath] = ignore_files + for ignf in ignore_files: + self.ignore_map[ignf].add(keypath) + self.dirstate_map[dirstate_file].add(keypath) + + def invalidate(self, dirstate_file=None, ignore_file=None): + for keypath in self.dirstate_map[dirstate_file]: + self.pop(keypath, None) + for keypath in self.ignore_map[ignore_file]: + self.pop(keypath, None) + + def ignore_files(self, keypath): + for ignf in self.keypath_ignore_map[keypath]: + yield ignf + + +file_status_cache = FileStatusCache() + + +def get_file_status(directory, dirstate_file, file_path, ignore_file_name, get_func, create_watcher, extra_ignore_files=()): + global file_status_cache + keypath = file_path if os.path.isabs(file_path) else join(directory, file_path) + file_status_cache.update_maps(keypath, directory, dirstate_file, ignore_file_name, extra_ignore_files) + + with file_status_lock: + # Optimize case of keypath not being cached + if keypath not in file_status_cache: + file_status_cache[keypath] = ans = get_func(directory, file_path) + return ans + + # Check if any relevant files have changed + file_changed = file_watcher(create_watcher) + changed = False + # Check if dirstate has changed + try: + changed = file_changed(dirstate_file) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + # The .git index file does not exist for a new git repo + return get_func(directory, file_path) + + if changed: + # Remove all cached values for files that depend on this + # dirstate_file + file_status_cache.invalidate(dirstate_file=dirstate_file) + else: + # Check if the file itself has changed + try: + changed ^= file_changed(keypath) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + # Do not call get_func again for a non-existant file + if keypath not in file_status_cache: + file_status_cache[keypath] = get_func(directory, file_path) + return file_status_cache[keypath] + + if changed: + file_status_cache.pop(keypath, None) + else: + # Check if one of the ignore files has changed + for ignf in file_status_cache.ignore_files(keypath): + try: + changed ^= file_changed(ignf) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + if changed: + # Invalidate cache for all files that might be affected + # by this ignore file + file_status_cache.invalidate(ignore_file=ignf) + break + + try: + return file_status_cache[keypath] + except KeyError: + file_status_cache[keypath] = ans = get_func(directory, file_path) + return ans + + +class TreeStatusCache(dict): + def __init__(self, pl): + self.tw = create_tree_watcher(pl) + self.pl = pl + + def cache_and_get(self, key, status): + ans = self.get(key, self) + if ans is self: + ans = self[key] = status() + return ans + + def __call__(self, repo): + key = repo.directory + try: + if self.tw(key, ignore_event=getattr(repo, 'ignore_event', None)): + self.pop(key, None) + except OSError as e: + self.pl.warn('Failed to check {0} for changes, with error: {1}', key, str(e)) + return self.cache_and_get(key, repo.status) + + +_tree_status_cache = None + + +def tree_status(repo, pl): + global _tree_status_cache + if _tree_status_cache is None: + _tree_status_cache = TreeStatusCache(pl) + return _tree_status_cache(repo) + + +vcs_props = ( + ('git', '.git', os.path.exists), + ('mercurial', '.hg', os.path.isdir), + ('bzr', '.bzr', os.path.isdir), +) + + +vcs_props_bytes = [ + (vcs, vcs_dir.encode('ascii'), check) + for vcs, vcs_dir, check in vcs_props +] + + +def guess(path, create_watcher): + for directory in generate_directories(path): + for vcs, vcs_dir, check in (vcs_props_bytes if isinstance(path, bytes) else vcs_props): + repo_dir = os.path.join(directory, vcs_dir) + if check(repo_dir): + if os.path.isdir(repo_dir) and not os.access(repo_dir, os.X_OK): + continue + try: + if vcs not in globals(): + globals()[vcs] = getattr(__import__(str('powerline.lib.vcs'), fromlist=[str(vcs)]), str(vcs)) + return globals()[vcs].Repository(directory, create_watcher) + except: + pass + return None + + +def get_fallback_create_watcher(): + from powerline.lib.watcher import create_file_watcher + from powerline import get_fallback_logger + from functools import partial + return partial(create_file_watcher, get_fallback_logger(), 'auto') + + +def debug(): + '''Test run guess(), repo.branch() and repo.status() + + To use:: + python -c 'from powerline.lib.vcs import debug; debug()' some_file_to_watch. + ''' + import sys + dest = sys.argv[-1] + repo = guess(os.path.abspath(dest), get_fallback_create_watcher) + if repo is None: + print ('%s is not a recognized vcs repo' % dest) + raise SystemExit(1) + print ('Watching %s' % dest) + print ('Press Ctrl-C to exit.') + try: + while True: + if os.path.isdir(dest): + print ('Branch name: %s Status: %s' % (repo.branch(), repo.status())) + else: + print ('File status: %s' % repo.status(dest)) + raw_input('Press Enter to check again: ') + except KeyboardInterrupt: + pass + except EOFError: + pass diff --git a/powerline-bin/powerline/lib/vcs/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/lib/vcs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1c2c7b0 Binary files /dev/null and b/powerline-bin/powerline/lib/vcs/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/vcs/__pycache__/bzr.cpython-312.pyc b/powerline-bin/powerline/lib/vcs/__pycache__/bzr.cpython-312.pyc new file mode 100644 index 0000000..4d8510a Binary files /dev/null and b/powerline-bin/powerline/lib/vcs/__pycache__/bzr.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/vcs/__pycache__/git.cpython-312.pyc b/powerline-bin/powerline/lib/vcs/__pycache__/git.cpython-312.pyc new file mode 100644 index 0000000..2fc437d Binary files /dev/null and b/powerline-bin/powerline/lib/vcs/__pycache__/git.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/vcs/__pycache__/mercurial.cpython-312.pyc b/powerline-bin/powerline/lib/vcs/__pycache__/mercurial.cpython-312.pyc new file mode 100644 index 0000000..d09c32e Binary files /dev/null and b/powerline-bin/powerline/lib/vcs/__pycache__/mercurial.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/vcs/bzr.py b/powerline-bin/powerline/lib/vcs/bzr.py new file mode 100644 index 0000000..e47d8b2 --- /dev/null +++ b/powerline-bin/powerline/lib/vcs/bzr.py @@ -0,0 +1,108 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import re + +from io import StringIO + +from bzrlib import (workingtree, status, library_state, trace, ui) + +from powerline.lib.vcs import get_branch_name, get_file_status +from powerline.lib.path import join +from powerline.lib.encoding import get_preferred_file_contents_encoding + + +class CoerceIO(StringIO): + def write(self, arg): + if isinstance(arg, bytes): + arg = arg.decode(get_preferred_file_contents_encoding(), 'replace') + return super(CoerceIO, self).write(arg) + + +nick_pat = re.compile(br'nickname\s*=\s*(.+)') + + +def branch_name_from_config_file(directory, config_file): + ans = None + try: + with open(config_file, 'rb') as f: + for line in f: + m = nick_pat.match(line) + if m is not None: + ans = m.group(1).strip().decode(get_preferred_file_contents_encoding(), 'replace') + break + except Exception: + pass + return ans or os.path.basename(directory) + + +state = None + + +class Repository(object): + def __init__(self, directory, create_watcher): + self.directory = os.path.abspath(directory) + self.create_watcher = create_watcher + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :'D?': dirty (tracked modified files: added, removed, deleted, modified), + :'?U': untracked-dirty (added, but not tracked files) + :None: clean (status is empty) + + With file argument: returns status of this file: The status codes are + those returned by bzr status -S + ''' + if path is not None: + return get_file_status( + directory=self.directory, + dirstate_file=join(self.directory, '.bzr', 'checkout', 'dirstate'), + file_path=path, + ignore_file_name='.bzrignore', + get_func=self.do_status, + create_watcher=self.create_watcher, + ) + return self.do_status(self.directory, path) + + def do_status(self, directory, path): + try: + return self._status(self.directory, path) + except Exception: + pass + + def _status(self, directory, path): + global state + if state is None: + state = library_state.BzrLibraryState(ui=ui.SilentUIFactory, trace=trace.DefaultConfig()) + buf = CoerceIO() + w = workingtree.WorkingTree.open(directory) + status.show_tree_status(w, specific_files=[path] if path else None, to_file=buf, short=True) + raw = buf.getvalue() + if not raw.strip(): + return + if path: + ans = raw[:2] + if ans == 'I ': # Ignored + ans = None + return ans + dirtied = untracked = ' ' + for line in raw.splitlines(): + if len(line) > 1 and line[1] in 'ACDMRIN': + dirtied = 'D' + elif line and line[0] == '?': + untracked = 'U' + ans = dirtied + untracked + return ans if ans.strip() else None + + def branch(self): + config_file = join(self.directory, '.bzr', 'branch', 'branch.conf') + return get_branch_name( + directory=self.directory, + config_file=config_file, + get_func=branch_name_from_config_file, + create_watcher=self.create_watcher, + ) diff --git a/powerline-bin/powerline/lib/vcs/git.py b/powerline-bin/powerline/lib/vcs/git.py new file mode 100644 index 0000000..9bdf56e --- /dev/null +++ b/powerline-bin/powerline/lib/vcs/git.py @@ -0,0 +1,208 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import re + +from powerline.lib.vcs import get_branch_name, get_file_status +from powerline.lib.shell import readlines +from powerline.lib.path import join +from powerline.lib.encoding import (get_preferred_file_name_encoding, + get_preferred_file_contents_encoding) +from powerline.lib.shell import which + + +_ref_pat = re.compile(br'ref:\s*refs/heads/(.+)') + + +def branch_name_from_config_file(directory, config_file): + try: + with open(config_file, 'rb') as f: + raw = f.read() + except EnvironmentError: + return os.path.basename(directory) + m = _ref_pat.match(raw) + if m is not None: + return m.group(1).decode(get_preferred_file_contents_encoding(), 'replace') + return raw[:7] + + +def git_directory(directory): + path = join(directory, '.git') + if os.path.isfile(path): + with open(path, 'rb') as f: + raw = f.read() + if not raw.startswith(b'gitdir: '): + raise IOError('invalid gitfile format') + raw = raw[8:] + if raw[-1:] == b'\n': + raw = raw[:-1] + if not isinstance(path, bytes): + raw = raw.decode(get_preferred_file_name_encoding()) + if not raw: + raise IOError('no path in gitfile') + return os.path.abspath(os.path.join(directory, raw)) + else: + return path + + +class GitRepository(object): + __slots__ = ('directory', 'create_watcher') + + def __init__(self, directory, create_watcher): + self.directory = os.path.abspath(directory) + self.create_watcher = create_watcher + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :First column: working directory status (D: dirty / space) + :Second column: index status (I: index dirty / space) + :Third column: presence of untracked files (U: untracked files / space) + :None: repository clean + + With file argument: returns status of this file. Output is + equivalent to the first two columns of ``git status --porcelain`` + (except for merge statuses as they are not supported by libgit2). + ''' + if path: + gitd = git_directory(self.directory) + # We need HEAD as without it using fugitive to commit causes the + # current file’s status (and only the current file) to not be updated + # for some reason I cannot be bothered to figure out. + return get_file_status( + directory=self.directory, + dirstate_file=join(gitd, 'index'), + file_path=path, + ignore_file_name='.gitignore', + get_func=self.do_status, + create_watcher=self.create_watcher, + extra_ignore_files=tuple(join(gitd, x) for x in ('logs/HEAD', 'info/exclude')), + ) + return self.do_status(self.directory, path) + + def branch(self): + directory = git_directory(self.directory) + head = join(directory, 'HEAD') + return get_branch_name( + directory=directory, + config_file=head, + get_func=branch_name_from_config_file, + create_watcher=self.create_watcher, + ) + + +try: + import pygit2 as git + + class Repository(GitRepository): + @staticmethod + def ignore_event(path, name): + return False + + def stash(self): + try: + stashref = git.Repository(git_directory(self.directory)).lookup_reference('refs/stash') + except KeyError: + return 0 + return sum(1 for _ in stashref.log()) + + def do_status(self, directory, path): + if path: + try: + status = git.Repository(directory).status_file(path) + except (KeyError, ValueError): + return None + + if status == git.GIT_STATUS_CURRENT: + return None + else: + if status & git.GIT_STATUS_WT_NEW: + return '??' + if status & git.GIT_STATUS_IGNORED: + return '!!' + + if status & git.GIT_STATUS_INDEX_NEW: + index_status = 'A' + elif status & git.GIT_STATUS_INDEX_DELETED: + index_status = 'D' + elif status & git.GIT_STATUS_INDEX_MODIFIED: + index_status = 'M' + else: + index_status = ' ' + + if status & git.GIT_STATUS_WT_DELETED: + wt_status = 'D' + elif status & git.GIT_STATUS_WT_MODIFIED: + wt_status = 'M' + else: + wt_status = ' ' + + return index_status + wt_status + else: + wt_column = ' ' + index_column = ' ' + untracked_column = ' ' + for status in git.Repository(directory).status().values(): + if status & git.GIT_STATUS_WT_NEW: + untracked_column = 'U' + continue + + if status & (git.GIT_STATUS_WT_DELETED | git.GIT_STATUS_WT_MODIFIED): + wt_column = 'D' + + if status & ( + git.GIT_STATUS_INDEX_NEW + | git.GIT_STATUS_INDEX_MODIFIED + | git.GIT_STATUS_INDEX_DELETED + ): + index_column = 'I' + r = wt_column + index_column + untracked_column + return r if r != ' ' else None +except ImportError: + class Repository(GitRepository): + def __init__(self, *args, **kwargs): + if not which('git'): + raise OSError('git executable is not available') + super(Repository, self).__init__(*args, **kwargs) + + @staticmethod + def ignore_event(path, name): + # Ignore changes to the index.lock file, since they happen + # frequently and dont indicate an actual change in the working tree + # status + return path.endswith('.git') and name == 'index.lock' + + def _gitcmd(self, directory, *args): + return readlines(('git',) + args, directory) + + def stash(self): + return sum(1 for _ in self._gitcmd(self.directory, 'stash', 'list')) + + def do_status(self, directory, path): + if path: + try: + return next(self._gitcmd(directory, 'status', '--porcelain', '--ignored', '--', path))[:2] + except StopIteration: + return None + else: + wt_column = ' ' + index_column = ' ' + untracked_column = ' ' + for line in self._gitcmd(directory, 'status', '--porcelain'): + if line[0] == '?': + untracked_column = 'U' + continue + elif line[0] == '!': + continue + + if line[0] != ' ': + index_column = 'I' + + if line[1] != ' ': + wt_column = 'D' + + r = wt_column + index_column + untracked_column + return r if r != ' ' else None diff --git a/powerline-bin/powerline/lib/vcs/mercurial.py b/powerline-bin/powerline/lib/vcs/mercurial.py new file mode 100644 index 0000000..09b6e0b --- /dev/null +++ b/powerline-bin/powerline/lib/vcs/mercurial.py @@ -0,0 +1,88 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +import hglib + +from powerline.lib.vcs import get_branch_name, get_file_status +from powerline.lib.path import join +from powerline.lib.encoding import get_preferred_file_contents_encoding + + +def branch_name_from_config_file(directory, config_file): + try: + with open(config_file, 'rb') as f: + raw = f.read() + return raw.decode(get_preferred_file_contents_encoding(), 'replace').strip() + except Exception: + return 'default' + + +class Repository(object): + __slots__ = ('directory', 'create_watcher') + + # hg status -> (powerline file status, repo status flag) + statuses = { + b'M': ('M', 1), b'A': ('A', 1), b'R': ('R', 1), b'!': ('D', 1), + b'?': ('U', 2), b'I': ('I', 0), b'C': ('', 0), + } + repo_statuses_str = (None, 'D ', ' U', 'DU') + + def __init__(self, directory, create_watcher): + self.directory = os.path.abspath(directory) + self.create_watcher = create_watcher + + def _repo(self, directory): + # Cannot create this object once and use always: when repository updates + # functions emit invalid results + return hglib.open(directory) + + def status(self, path=None): + '''Return status of repository or file. + + Without file argument: returns status of the repository: + + :'D?': dirty (tracked modified files: added, removed, deleted, modified), + :'?U': untracked-dirty (added, but not tracked files) + :None: clean (status is empty) + + With file argument: returns status of this file: `M`odified, `A`dded, + `R`emoved, `D`eleted (removed from filesystem, but still tracked), + `U`nknown, `I`gnored, (None)Clean. + ''' + if path: + return get_file_status( + directory=self.directory, + dirstate_file=join(self.directory, '.hg', 'dirstate'), + file_path=path, + ignore_file_name='.hgignore', + get_func=self.do_status, + create_watcher=self.create_watcher, + ) + return self.do_status(self.directory, path) + + def do_status(self, directory, path): + with self._repo(directory) as repo: + if path: + path = os.path.join(directory, path) + statuses = repo.status(include=path, all=True) + for status, paths in statuses: + if paths: + return self.statuses[status][0] + return None + else: + resulting_status = 0 + for status, paths in repo.status(all=True): + if paths: + resulting_status |= self.statuses[status][1] + return self.repo_statuses_str[resulting_status] + + def branch(self): + config_file = join(self.directory, '.hg', 'branch') + return get_branch_name( + directory=self.directory, + config_file=config_file, + get_func=branch_name_from_config_file, + create_watcher=self.create_watcher, + ) diff --git a/powerline-bin/powerline/lib/watcher/__init__.py b/powerline-bin/powerline/lib/watcher/__init__.py new file mode 100644 index 0000000..4fe9896 --- /dev/null +++ b/powerline-bin/powerline/lib/watcher/__init__.py @@ -0,0 +1,76 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from powerline.lib.watcher.stat import StatFileWatcher +from powerline.lib.watcher.inotify import INotifyFileWatcher +from powerline.lib.watcher.tree import TreeWatcher +from powerline.lib.watcher.uv import UvFileWatcher, UvNotFound +from powerline.lib.inotify import INotifyError + + +def create_file_watcher(pl, watcher_type='auto', expire_time=10): + '''Create an object that can watch for changes to specified files + + Use ``.__call__()`` method of the returned object to start watching the file + or check whether file has changed since last call. + + Use ``.unwatch()`` method of the returned object to stop watching the file. + + Uses inotify if available, then pyuv, otherwise tracks mtimes. expire_time + is the number of minutes after the last query for a given path for the + inotify watch for that path to be automatically removed. This conserves + kernel resources. + + :param PowerlineLogger pl: + Logger. + :param str watcher_type + One of ``inotify`` (linux only), ``uv``, ``stat``, ``auto``. Determines + what watcher will be used. ``auto`` will use ``inotify`` if available, + then ``libuv`` and then fall back to ``stat``. + :param int expire_time: + Number of minutes since last ``.__call__()`` before inotify watcher will + stop watching given file. + ''' + if watcher_type == 'stat': + pl.debug('Using requested stat-based watcher', prefix='watcher') + return StatFileWatcher() + if watcher_type == 'inotify': + # Explicitly selected inotify watcher: do not catch INotifyError then. + pl.debug('Using requested inotify watcher', prefix='watcher') + return INotifyFileWatcher(expire_time=expire_time) + elif watcher_type == 'uv': + pl.debug('Using requested uv watcher', prefix='watcher') + return UvFileWatcher() + + if sys.platform.startswith('linux'): + try: + pl.debug('Trying to use inotify watcher', prefix='watcher') + return INotifyFileWatcher(expire_time=expire_time) + except INotifyError: + pl.info('Failed to create inotify watcher', prefix='watcher') + + try: + pl.debug('Using libuv-based watcher') + return UvFileWatcher() + except UvNotFound: + pl.debug('Failed to import pyuv') + + pl.debug('Using stat-based watcher') + return StatFileWatcher() + + +def create_tree_watcher(pl, watcher_type='auto', expire_time=10): + '''Create an object that can watch for changes in specified directories + + :param PowerlineLogger pl: + Logger. + :param str watcher_type: + Watcher type. Currently the only supported types are ``inotify`` (linux + only), ``uv``, ``dummy`` and ``auto``. + :param int expire_time: + Number of minutes since last ``.__call__()`` before inotify watcher will + stop watching given file. + ''' + return TreeWatcher(pl, watcher_type, expire_time) diff --git a/powerline-bin/powerline/lib/watcher/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/lib/watcher/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9f4996c Binary files /dev/null and b/powerline-bin/powerline/lib/watcher/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/watcher/__pycache__/inotify.cpython-312.pyc b/powerline-bin/powerline/lib/watcher/__pycache__/inotify.cpython-312.pyc new file mode 100644 index 0000000..248988f Binary files /dev/null and b/powerline-bin/powerline/lib/watcher/__pycache__/inotify.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/watcher/__pycache__/stat.cpython-312.pyc b/powerline-bin/powerline/lib/watcher/__pycache__/stat.cpython-312.pyc new file mode 100644 index 0000000..1e5c96d Binary files /dev/null and b/powerline-bin/powerline/lib/watcher/__pycache__/stat.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/watcher/__pycache__/tree.cpython-312.pyc b/powerline-bin/powerline/lib/watcher/__pycache__/tree.cpython-312.pyc new file mode 100644 index 0000000..6a66443 Binary files /dev/null and b/powerline-bin/powerline/lib/watcher/__pycache__/tree.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/watcher/__pycache__/uv.cpython-312.pyc b/powerline-bin/powerline/lib/watcher/__pycache__/uv.cpython-312.pyc new file mode 100644 index 0000000..2d2c1df Binary files /dev/null and b/powerline-bin/powerline/lib/watcher/__pycache__/uv.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lib/watcher/inotify.py b/powerline-bin/powerline/lib/watcher/inotify.py new file mode 100644 index 0000000..4a134e8 --- /dev/null +++ b/powerline-bin/powerline/lib/watcher/inotify.py @@ -0,0 +1,268 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import errno +import os +import ctypes + +from threading import RLock + +from powerline.lib.inotify import INotify +from powerline.lib.monotonic import monotonic +from powerline.lib.path import realpath + + +class INotifyFileWatcher(INotify): + def __init__(self, expire_time=10): + super(INotifyFileWatcher, self).__init__() + self.watches = {} + self.modified = {} + self.last_query = {} + self.lock = RLock() + self.expire_time = expire_time * 60 + + def expire_watches(self): + now = monotonic() + for path, last_query in tuple(self.last_query.items()): + if last_query - now > self.expire_time: + self.unwatch(path) + + def process_event(self, wd, mask, cookie, name): + if wd == -1 and (mask & self.Q_OVERFLOW): + # We missed some INOTIFY events, so we dont + # know the state of any tracked files. + for path in tuple(self.modified): + if os.path.exists(path): + self.modified[path] = True + else: + self.watches.pop(path, None) + self.modified.pop(path, None) + self.last_query.pop(path, None) + return + + for path, num in tuple(self.watches.items()): + if num == wd: + if mask & self.IGNORED: + self.watches.pop(path, None) + self.modified.pop(path, None) + self.last_query.pop(path, None) + else: + if mask & self.ATTRIB: + # The watched file could have had its inode changed, in + # which case we will not get any more events for this + # file, so re-register the watch. For example by some + # other file being renamed as this file. + try: + self.unwatch(path) + except OSError: + pass + try: + self.watch(path) + except OSError as e: + if getattr(e, 'errno', None) != errno.ENOENT: + raise + else: + self.modified[path] = True + else: + self.modified[path] = True + + def unwatch(self, path): + ''' Remove the watch for path. Raises an OSError if removing the watch + fails for some reason. ''' + path = realpath(path) + with self.lock: + self.modified.pop(path, None) + self.last_query.pop(path, None) + wd = self.watches.pop(path, None) + if wd is not None: + if self._rm_watch(self._inotify_fd, wd) != 0: + self.handle_error() + + def watch(self, path): + ''' Register a watch for the file/directory named path. Raises an OSError if path + does not exist. ''' + path = realpath(path) + with self.lock: + if path not in self.watches: + bpath = path if isinstance(path, bytes) else path.encode(self.fenc) + flags = self.MOVE_SELF | self.DELETE_SELF + buf = ctypes.c_char_p(bpath) + # Try watching path as a directory + wd = self._add_watch(self._inotify_fd, buf, flags | self.ONLYDIR) + if wd == -1: + eno = ctypes.get_errno() + if eno != errno.ENOTDIR: + self.handle_error() + # Try watching path as a file + flags |= (self.MODIFY | self.ATTRIB) + wd = self._add_watch(self._inotify_fd, buf, flags) + if wd == -1: + self.handle_error() + self.watches[path] = wd + self.modified[path] = False + + def is_watching(self, path): + with self.lock: + return realpath(path) in self.watches + + def __call__(self, path): + ''' Return True if path has been modified since the last call. Can + raise OSError if the path does not exist. ''' + path = realpath(path) + with self.lock: + self.last_query[path] = monotonic() + self.expire_watches() + if path not in self.watches: + # Try to re-add the watch, it will fail if the file does not + # exist/you dont have permission + self.watch(path) + return True + self.read(get_name=False) + if path not in self.modified: + # An ignored event was received which means the path has been + # automatically unwatched + return True + ans = self.modified[path] + if ans: + self.modified[path] = False + return ans + + def close(self): + with self.lock: + for path in tuple(self.watches): + try: + self.unwatch(path) + except OSError: + pass + super(INotifyFileWatcher, self).close() + + +class NoSuchDir(ValueError): + pass + + +class BaseDirChanged(ValueError): + pass + + +class DirTooLarge(ValueError): + def __init__(self, bdir): + ValueError.__init__(self, 'The directory {0} is too large to monitor. Try increasing the value in /proc/sys/fs/inotify/max_user_watches'.format(bdir)) + + +class INotifyTreeWatcher(INotify): + is_dummy = False + + def __init__(self, basedir, ignore_event=None): + super(INotifyTreeWatcher, self).__init__() + self.basedir = realpath(basedir) + self.watch_tree() + self.modified = True + self.ignore_event = (lambda path, name: False) if ignore_event is None else ignore_event + + def watch_tree(self): + self.watched_dirs = {} + self.watched_rmap = {} + try: + self.add_watches(self.basedir) + except OSError as e: + if e.errno == errno.ENOSPC: + raise DirTooLarge(self.basedir) + + def add_watches(self, base, top_level=True): + ''' Add watches for this directory and all its descendant directories, + recursively. ''' + base = realpath(base) + # There may exist a link which leads to an endless + # add_watches loop or to maximum recursion depth exceeded + if not top_level and base in self.watched_dirs: + return + try: + is_dir = self.add_watch(base) + except OSError as e: + if e.errno == errno.ENOENT: + # The entry could have been deleted between listdir() and + # add_watch(). + if top_level: + raise NoSuchDir('The dir {0} does not exist'.format(base)) + return + if e.errno == errno.EACCES: + # We silently ignore entries for which we dont have permission, + # unless they are the top level dir + if top_level: + raise NoSuchDir('You do not have permission to monitor {0}'.format(base)) + return + raise + else: + if is_dir: + try: + files = os.listdir(base) + except OSError as e: + if e.errno in (errno.ENOTDIR, errno.ENOENT): + # The dir was deleted/replaced between the add_watch() + # and listdir() + if top_level: + raise NoSuchDir('The dir {0} does not exist'.format(base)) + return + raise + for x in files: + self.add_watches(os.path.join(base, x), top_level=False) + elif top_level: + # The top level dir is a file, not good. + raise NoSuchDir('The dir {0} does not exist'.format(base)) + + def add_watch(self, path): + bpath = path if isinstance(path, bytes) else path.encode(self.fenc) + wd = self._add_watch( + self._inotify_fd, + ctypes.c_char_p(bpath), + + # Ignore symlinks and watch only directories + self.DONT_FOLLOW | self.ONLYDIR | + + self.MODIFY | self.CREATE | self.DELETE | + self.MOVE_SELF | self.MOVED_FROM | self.MOVED_TO | + self.ATTRIB | self.DELETE_SELF + ) + if wd == -1: + eno = ctypes.get_errno() + if eno == errno.ENOTDIR: + return False + raise OSError(eno, 'Failed to add watch for: {0}: {1}'.format(path, self.os.strerror(eno))) + self.watched_dirs[path] = wd + self.watched_rmap[wd] = path + return True + + def process_event(self, wd, mask, cookie, name): + if wd == -1 and (mask & self.Q_OVERFLOW): + # We missed some INOTIFY events, so we dont + # know the state of any tracked dirs. + self.watch_tree() + self.modified = True + return + path = self.watched_rmap.get(wd, None) + if path is not None: + if not self.ignore_event(path, name): + self.modified = True + if mask & self.CREATE: + # A new sub-directory might have been created, monitor it. + try: + if not isinstance(path, bytes): + name = name.decode(self.fenc) + self.add_watch(os.path.join(path, name)) + except OSError as e: + if e.errno == errno.ENOENT: + # Deleted before add_watch() + pass + elif e.errno == errno.ENOSPC: + raise DirTooLarge(self.basedir) + else: + raise + if (mask & self.DELETE_SELF or mask & self.MOVE_SELF) and path == self.basedir: + raise BaseDirChanged('The directory %s was moved/deleted' % path) + + def __call__(self): + self.read() + ret = self.modified + self.modified = False + return ret diff --git a/powerline-bin/powerline/lib/watcher/stat.py b/powerline-bin/powerline/lib/watcher/stat.py new file mode 100644 index 0000000..0c08971 --- /dev/null +++ b/powerline-bin/powerline/lib/watcher/stat.py @@ -0,0 +1,44 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from threading import RLock + +from powerline.lib.path import realpath + + +class StatFileWatcher(object): + def __init__(self): + self.watches = {} + self.lock = RLock() + + def watch(self, path): + path = realpath(path) + with self.lock: + self.watches[path] = os.path.getmtime(path) + + def unwatch(self, path): + path = realpath(path) + with self.lock: + self.watches.pop(path, None) + + def is_watching(self, path): + with self.lock: + return realpath(path) in self.watches + + def __call__(self, path): + path = realpath(path) + with self.lock: + if path not in self.watches: + self.watches[path] = os.path.getmtime(path) + return True + mtime = os.path.getmtime(path) + if mtime != self.watches[path]: + self.watches[path] = mtime + return True + return False + + def close(self): + with self.lock: + self.watches.clear() diff --git a/powerline-bin/powerline/lib/watcher/tree.py b/powerline-bin/powerline/lib/watcher/tree.py new file mode 100644 index 0000000..7d2b83f --- /dev/null +++ b/powerline-bin/powerline/lib/watcher/tree.py @@ -0,0 +1,90 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from powerline.lib.monotonic import monotonic +from powerline.lib.inotify import INotifyError +from powerline.lib.path import realpath +from powerline.lib.watcher.inotify import INotifyTreeWatcher, DirTooLarge, NoSuchDir, BaseDirChanged +from powerline.lib.watcher.uv import UvTreeWatcher, UvNotFound + + +class DummyTreeWatcher(object): + is_dummy = True + + def __init__(self, basedir): + self.basedir = realpath(basedir) + + def __call__(self): + return False + + +class TreeWatcher(object): + def __init__(self, pl, watcher_type, expire_time): + self.watches = {} + self.last_query_times = {} + self.expire_time = expire_time * 60 + self.pl = pl + self.watcher_type = watcher_type + + def get_watcher(self, path, ignore_event): + if self.watcher_type == 'inotify': + return INotifyTreeWatcher(path, ignore_event=ignore_event) + if self.watcher_type == 'uv': + return UvTreeWatcher(path, ignore_event=ignore_event) + if self.watcher_type == 'dummy': + return DummyTreeWatcher(path) + # FIXME + if self.watcher_type == 'stat': + return DummyTreeWatcher(path) + if self.watcher_type == 'auto': + if sys.platform.startswith('linux'): + try: + return INotifyTreeWatcher(path, ignore_event=ignore_event) + except (INotifyError, DirTooLarge) as e: + if not isinstance(e, INotifyError): + self.pl.warn('Failed to watch path: {0} with error: {1}'.format(path, e)) + try: + return UvTreeWatcher(path, ignore_event=ignore_event) + except UvNotFound: + pass + return DummyTreeWatcher(path) + else: + raise ValueError('Unknown watcher type: {0}'.format(self.watcher_type)) + + def watch(self, path, ignore_event=None): + path = realpath(path) + w = self.get_watcher(path, ignore_event) + self.watches[path] = w + return w + + def expire_old_queries(self): + pop = [] + now = monotonic() + for path, lt in self.last_query_times.items(): + if now - lt > self.expire_time: + pop.append(path) + for path in pop: + del self.last_query_times[path] + + def __call__(self, path, ignore_event=None): + path = realpath(path) + self.expire_old_queries() + self.last_query_times[path] = monotonic() + w = self.watches.get(path, None) + if w is None: + try: + self.watch(path, ignore_event=ignore_event) + except NoSuchDir: + pass + return True + try: + return w() + except BaseDirChanged: + self.watches.pop(path, None) + return True + except DirTooLarge as e: + self.pl.warn(str(e)) + self.watches[path] = DummyTreeWatcher(path) + return False diff --git a/powerline-bin/powerline/lib/watcher/uv.py b/powerline-bin/powerline/lib/watcher/uv.py new file mode 100644 index 0000000..272db0f --- /dev/null +++ b/powerline-bin/powerline/lib/watcher/uv.py @@ -0,0 +1,207 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from collections import defaultdict +from threading import RLock +from functools import partial +from threading import Thread +from errno import ENOENT + +from powerline.lib.path import realpath +from powerline.lib.encoding import get_preferred_file_name_encoding + + +class UvNotFound(NotImplementedError): + pass + + +pyuv = None +pyuv_version_info = None + + +def import_pyuv(): + global pyuv + global pyuv_version_info + if not pyuv: + try: + pyuv = __import__('pyuv') + except ImportError: + raise UvNotFound + else: + pyuv_version_info = tuple((int(c) for c in pyuv.__version__.split('.'))) + + +class UvThread(Thread): + daemon = True + + def __init__(self, loop): + self.uv_loop = loop + self.async_handle = pyuv.Async(loop, self._async_cb) + super(UvThread, self).__init__() + + def _async_cb(self, handle): + self.uv_loop.stop() + self.async_handle.close() + + def run(self): + self.uv_loop.run() + + def join(self): + self.async_handle.send() + return super(UvThread, self).join() + + +_uv_thread = None + + +def start_uv_thread(): + global _uv_thread + if _uv_thread is None: + loop = pyuv.Loop() + _uv_thread = UvThread(loop) + _uv_thread.start() + return _uv_thread.uv_loop + + +def normpath(path, fenc): + path = realpath(path) + if isinstance(path, bytes): + return path.decode(fenc) + else: + return path + + +class UvWatcher(object): + def __init__(self): + import_pyuv() + self.watches = {} + self.lock = RLock() + self.loop = start_uv_thread() + self.fenc = get_preferred_file_name_encoding() + if pyuv_version_info >= (1, 0): + self._start_watch = self._start_watch_1_x + else: + self._start_watch = self._start_watch_0_x + + def _start_watch_1_x(self, path): + handle = pyuv.fs.FSEvent(self.loop) + handle.start(path, 0, partial(self._record_event, path)) + self.watches[path] = handle + + def _start_watch_0_x(self, path): + self.watches[path] = pyuv.fs.FSEvent( + self.loop, + path, + partial(self._record_event, path), + pyuv.fs.UV_CHANGE | pyuv.fs.UV_RENAME + ) + + def watch(self, path): + path = normpath(path, self.fenc) + with self.lock: + if path not in self.watches: + try: + self._start_watch(path) + except pyuv.error.FSEventError as e: + code = e.args[0] + if code == pyuv.errno.UV_ENOENT: + raise OSError(ENOENT, 'No such file or directory: ' + path) + else: + raise + + def unwatch(self, path): + path = normpath(path, self.fenc) + with self.lock: + try: + watch = self.watches.pop(path) + except KeyError: + return + watch.close(partial(self._stopped_watching, path)) + + def is_watching(self, path): + with self.lock: + return normpath(path, self.fenc) in self.watches + + def __del__(self): + try: + lock = self.lock + except AttributeError: + pass + else: + with lock: + while self.watches: + path, watch = self.watches.popitem() + watch.close(partial(self._stopped_watching, path)) + + +class UvFileWatcher(UvWatcher): + def __init__(self): + super(UvFileWatcher, self).__init__() + self.events = defaultdict(list) + + def _record_event(self, path, fsevent_handle, filename, events, error): + with self.lock: + self.events[path].append(events) + if events | pyuv.fs.UV_RENAME: + if not os.path.exists(path): + self.watches.pop(path).close() + + def _stopped_watching(self, path, *args): + self.events.pop(path, None) + + def __call__(self, path): + path = normpath(path, self.fenc) + with self.lock: + events = self.events.pop(path, None) + if events: + return True + if path not in self.watches: + self.watch(path) + return True + return False + + +class UvTreeWatcher(UvWatcher): + is_dummy = False + + def __init__(self, basedir, ignore_event=None): + super(UvTreeWatcher, self).__init__() + self.ignore_event = ignore_event or (lambda path, name: False) + self.basedir = normpath(basedir, self.fenc) + self.modified = True + self.watch_directory(self.basedir) + + def watch_directory(self, path): + for root, dirs, files in os.walk(normpath(path, self.fenc)): + self.watch_one_directory(root) + + def watch_one_directory(self, dirname): + try: + self.watch(dirname) + except OSError: + pass + + def _stopped_watching(self, path, *args): + pass + + def _record_event(self, path, fsevent_handle, filename, events, error): + if not self.ignore_event(path, filename): + self.modified = True + if events == pyuv.fs.UV_CHANGE | pyuv.fs.UV_RENAME: + # Stat changes to watched directory are UV_CHANGE|UV_RENAME. It + # is weird. + pass + elif events | pyuv.fs.UV_RENAME: + if not os.path.isdir(path): + self.unwatch(path) + else: + full_name = os.path.join(path, filename) + if os.path.isdir(full_name): + # For some reason mkdir and rmdir both fall into this + # category + self.watch_directory(full_name) + + def __call__(self): + return self.__dict__.pop('modified', False) diff --git a/powerline-bin/powerline/lint/__init__.py b/powerline-bin/powerline/lint/__init__.py new file mode 100644 index 0000000..8c68271 --- /dev/null +++ b/powerline-bin/powerline/lint/__init__.py @@ -0,0 +1,625 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import logging + +from collections import defaultdict +from itertools import chain +from functools import partial + +from powerline import generate_config_finder, get_config_paths, load_config +from powerline.segments.vim import vim_modes +from powerline.lib.dict import mergedicts_copy +from powerline.lib.config import ConfigLoader +from powerline.lib.unicode import unicode +from powerline.lib.path import join +from powerline.lint.markedjson import load +from powerline.lint.markedjson.error import echoerr, EchoErr, MarkedError +from powerline.lint.checks import (check_matcher_func, check_ext, check_config, check_top_theme, + check_color, check_translated_group_name, check_group, + check_segment_module, check_exinclude_function, type_keys, + check_segment_function, check_args, get_one_segment_function, + check_highlight_groups, check_highlight_group, check_full_segment_data, + get_all_possible_functions, check_segment_data_key, register_common_name, + highlight_group_spec, check_log_file_level, check_logging_handler) +from powerline.lint.spec import Spec +from powerline.lint.context import Context + + +def open_file(path): + return open(path, 'rb') + + +def generate_json_config_loader(lhadproblem): + def load_json_config(config_file_path, load=load, open_file=open_file): + with open_file(config_file_path) as config_file_fp: + r, hadproblem = load(config_file_fp) + if hadproblem: + lhadproblem[0] = True + return r + return load_json_config + + +function_name_re = '^(\w+\.)*[a-zA-Z_]\w*$' + + +divider_spec = Spec().printable().len( + 'le', 3, (lambda value: 'Divider {0!r} is too large!'.format(value))).copy +ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy +top_theme_spec = Spec().type(unicode).func(check_top_theme).copy +ext_spec = Spec( + colorscheme=Spec().type(unicode).func( + (lambda *args: check_config('colorschemes', *args)) + ), + theme=ext_theme_spec(), + top_theme=top_theme_spec().optional(), +).copy +gen_components_spec = (lambda *components: Spec().list(Spec().type(unicode).oneof(set(components)))) +log_level_spec = Spec().re('^[A-Z]+$').func( + (lambda value, *args: (True, True, not hasattr(logging, value))), + (lambda value: 'unknown debugging level {0}'.format(value)) +).copy +log_format_spec = Spec().type(unicode).copy +main_spec = (Spec( + common=Spec( + default_top_theme=top_theme_spec().optional(), + term_truecolor=Spec().type(bool).optional(), + term_escape_style=Spec().type(unicode).oneof(set(('auto', 'xterm', 'fbterm'))).optional(), + # Python is capable of loading from zip archives. Thus checking path + # only for existence of the path, not for it being a directory + paths=Spec().list( + (lambda value, *args: (True, True, not os.path.exists(os.path.expanduser(value.value)))), + (lambda value: 'path does not exist: {0}'.format(value)) + ).optional(), + log_file=Spec().either( + Spec().type(unicode).func( + ( + lambda value, *args: ( + True, + True, + not os.path.isdir(os.path.dirname(os.path.expanduser(value))) + ) + ), + (lambda value: 'directory does not exist: {0}'.format(os.path.dirname(value))) + ), + Spec().list(Spec().either( + Spec().type(unicode, type(None)), + Spec().tuple( + Spec().re(function_name_re).func(check_logging_handler), + Spec().tuple( + Spec().type(list).optional(), + Spec().type(dict).optional(), + ), + log_level_spec().func(check_log_file_level).optional(), + log_format_spec().optional(), + ), + )) + ).optional(), + log_level=log_level_spec().optional(), + log_format=log_format_spec().optional(), + interval=Spec().either(Spec().cmp('gt', 0.0), Spec().type(type(None))).optional(), + reload_config=Spec().type(bool).optional(), + watcher=Spec().type(unicode).oneof(set(('auto', 'inotify', 'stat'))).optional(), + ).context_message('Error while loading common configuration (key {key})'), + ext=Spec( + vim=ext_spec().update( + components=gen_components_spec('statusline', 'tabline').optional(), + local_themes=Spec( + __tabline__=ext_theme_spec(), + ).unknown_spec( + Spec().re(function_name_re).func(partial(check_matcher_func, 'vim')), + ext_theme_spec() + ), + ).optional(), + ipython=ext_spec().update( + local_themes=Spec( + in2=ext_theme_spec(), + out=ext_theme_spec(), + rewrite=ext_theme_spec(), + ), + ).optional(), + shell=ext_spec().update( + components=gen_components_spec('tmux', 'prompt').optional(), + local_themes=Spec( + continuation=ext_theme_spec(), + select=ext_theme_spec(), + ), + ).optional(), + wm=ext_spec().update( + local_themes=Spec().unknown_spec( + Spec().re('^[0-9A-Za-z-]+$'), + ext_theme_spec() + ).optional(), + update_interval=Spec().cmp('gt', 0.0).optional(), + ).optional(), + ).unknown_spec( + check_ext, + ext_spec(), + ).context_message('Error while loading extensions configuration (key {key})'), +).context_message('Error while loading main configuration')) + +term_color_spec = Spec().unsigned().cmp('le', 255).copy +true_color_spec = Spec().re( + '^[0-9a-fA-F]{6}$', + (lambda value: '"{0}" is not a six-digit hexadecimal unsigned integer written as a string'.format(value)) +).copy +colors_spec = (Spec( + colors=Spec().unknown_spec( + Spec().ident(), + Spec().either( + Spec().tuple(term_color_spec(), true_color_spec()), + term_color_spec() + ) + ).context_message('Error while checking colors (key {key})'), + gradients=Spec().unknown_spec( + Spec().ident(), + Spec().tuple( + Spec().len('gt', 1).list(term_color_spec()), + Spec().len('gt', 1).list(true_color_spec()).optional(), + ) + ).context_message('Error while checking gradients (key {key})'), +).context_message('Error while loading colors configuration')) + + +color_spec = Spec().type(unicode).func(check_color).copy +name_spec = Spec().type(unicode).len('gt', 0).optional().copy +group_name_spec = Spec().ident().copy +group_spec = Spec().either(Spec( + fg=color_spec(), + bg=color_spec(), + attrs=Spec().list(Spec().type(unicode).oneof(set(('bold', 'italic', 'underline')))), +), group_name_spec().func(check_group)).copy +groups_spec = Spec().unknown_spec( + group_name_spec(), + group_spec(), +).context_message('Error while loading groups (key {key})').copy +colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec(), +).context_message('Error while loading coloscheme')) +mode_translations_value_spec = Spec( + colors=Spec().unknown_spec( + color_spec(), + color_spec(), + ).optional(), + groups=Spec().unknown_spec( + group_name_spec().func(check_translated_group_name), + group_spec(), + ).optional(), +).copy +top_colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec(), + mode_translations=Spec().unknown_spec( + Spec().type(unicode), + mode_translations_value_spec(), + ).optional().context_message('Error while loading mode translations (key {key})').optional(), +).context_message('Error while loading top-level coloscheme')) +vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc', 'tab_nc', 'buf_nc'])).copy +vim_colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec(), + mode_translations=Spec().unknown_spec( + vim_mode_spec(), + mode_translations_value_spec(), + ).optional().context_message('Error while loading mode translations (key {key})'), +).context_message('Error while loading vim colorscheme')) +shell_mode_spec = Spec().re('^(?:[\w\-]+|\.safe)$').copy +shell_colorscheme_spec = (Spec( + name=name_spec(), + groups=groups_spec(), + mode_translations=Spec().unknown_spec( + shell_mode_spec(), + mode_translations_value_spec(), + ).optional().context_message('Error while loading mode translations (key {key})'), +).context_message('Error while loading shell colorscheme')) + + +args_spec = Spec( + pl=Spec().error('pl object must be set by powerline').optional(), + segment_info=Spec().error('Segment info dictionary must be set by powerline').optional(), +).unknown_spec(Spec(), Spec()).optional().copy +segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy +exinclude_spec = Spec().re(function_name_re).func(check_exinclude_function).copy +segment_spec_base = Spec( + name=Spec().re('^[a-zA-Z_]\w*$').optional(), + function=Spec().re(function_name_re).func(check_segment_function).optional(), + exclude_modes=Spec().list(vim_mode_spec()).optional(), + include_modes=Spec().list(vim_mode_spec()).optional(), + exclude_function=exinclude_spec().optional(), + include_function=exinclude_spec().optional(), + draw_hard_divider=Spec().type(bool).optional(), + draw_soft_divider=Spec().type(bool).optional(), + draw_inner_divider=Spec().type(bool).optional(), + display=Spec().type(bool).optional(), + module=segment_module_spec(), + priority=Spec().type(int, float, type(None)).optional(), + after=Spec().printable().optional(), + before=Spec().printable().optional(), + width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), + align=Spec().oneof(set('lr')).optional(), + args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_function, *args, **kwargs)), + contents=Spec().printable().optional(), + highlight_groups=Spec().list( + highlight_group_spec().re( + '^(?:(?!:divider$).)+$', + (lambda value: 'it is recommended that only divider highlight group names end with ":divider"') + ) + ).func(check_highlight_groups).optional(), + divider_highlight_group=highlight_group_spec().func(check_highlight_group).re( + ':divider$', + (lambda value: 'it is recommended that divider highlight group names end with ":divider"') + ).optional(), +).func(check_full_segment_data).copy +subsegment_spec = segment_spec_base().update( + type=Spec().oneof(set((key for key in type_keys if key != 'segment_list'))).optional(), +) +segment_spec = segment_spec_base().update( + type=Spec().oneof(type_keys).optional(), + segments=Spec().optional().list(subsegment_spec), +) +segments_spec = Spec().optional().list(segment_spec).copy +segdict_spec = Spec( + left=segments_spec().context_message('Error while loading segments from left side (key {key})'), + right=segments_spec().context_message('Error while loading segments from right side (key {key})'), +).func( + (lambda value, *args: (True, True, not (('left' in value) or ('right' in value)))), + (lambda value: 'segments dictionary must contain either left, right or both keys') +).context_message('Error while loading segments (key {key})').copy +divside_spec = Spec( + hard=divider_spec(), + soft=divider_spec(), +).copy +segment_data_value_spec = Spec( + after=Spec().printable().optional(), + before=Spec().printable().optional(), + display=Spec().type(bool).optional(), + args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_functions, *args, **kwargs)), + contents=Spec().printable().optional(), +).copy +dividers_spec = Spec( + left=divside_spec(), + right=divside_spec(), +).copy +spaces_spec = Spec().unsigned().cmp( + 'le', 2, (lambda value: 'Are you sure you need such a big ({0}) number of spaces?'.format(value)) +).copy +common_theme_spec = Spec( + default_module=segment_module_spec().optional(), + cursor_space=Spec().type(int, float).cmp('le', 100).cmp('gt', 0).optional(), + cursor_columns=Spec().type(int).cmp('gt', 0).optional(), +).context_message('Error while loading theme').copy +top_theme_spec = common_theme_spec().update( + dividers=dividers_spec(), + spaces=spaces_spec(), + use_non_breaking_spaces=Spec().type(bool).optional(), + segment_data=Spec().unknown_spec( + Spec().func(check_segment_data_key), + segment_data_value_spec(), + ).optional().context_message('Error while loading segment data (key {key})'), +) +main_theme_spec = common_theme_spec().update( + dividers=dividers_spec().optional(), + spaces=spaces_spec().optional(), + segment_data=Spec().unknown_spec( + Spec().func(check_segment_data_key), + segment_data_value_spec(), + ).optional().context_message('Error while loading segment data (key {key})'), +) +theme_spec = common_theme_spec().update( + dividers=dividers_spec().optional(), + spaces=spaces_spec().optional(), + segment_data=Spec().unknown_spec( + Spec().func(check_segment_data_key), + segment_data_value_spec(), + ).optional().context_message('Error while loading segment data (key {key})'), + segments=segdict_spec().update(above=Spec().list(segdict_spec()).optional()), +) + + +def register_common_names(): + register_common_name('player', 'powerline.segments.common.players', '_player') + + +def load_json_file(path): + with open_file(path) as F: + try: + config, hadproblem = load(F) + except MarkedError as e: + return True, None, str(e) + else: + return hadproblem, config, None + + +def updated_with_config(d): + hadproblem, config, error = load_json_file(d['path']) + d.update( + hadproblem=hadproblem, + config=config, + error=error, + ) + return d + + +def find_all_ext_config_files(search_paths, subdir): + for config_root in search_paths: + top_config_subpath = join(config_root, subdir) + if not os.path.isdir(top_config_subpath): + if os.path.exists(top_config_subpath): + yield { + 'error': 'Path {0} is not a directory'.format(top_config_subpath), + 'path': top_config_subpath, + } + continue + for ext_name in os.listdir(top_config_subpath): + ext_path = os.path.join(top_config_subpath, ext_name) + if not os.path.isdir(ext_path): + if ext_name.endswith('.json') and os.path.isfile(ext_path): + yield updated_with_config({ + 'error': False, + 'path': ext_path, + 'name': ext_name[:-5], + 'ext': None, + 'type': 'top_' + subdir, + }) + else: + yield { + 'error': 'Path {0} is not a directory or configuration file'.format(ext_path), + 'path': ext_path, + } + continue + for config_file_name in os.listdir(ext_path): + config_file_path = os.path.join(ext_path, config_file_name) + if config_file_name.endswith('.json') and os.path.isfile(config_file_path): + yield updated_with_config({ + 'error': False, + 'path': config_file_path, + 'name': config_file_name[:-5], + 'ext': ext_name, + 'type': subdir, + }) + else: + yield { + 'error': 'Path {0} is not a configuration file'.format(config_file_path), + 'path': config_file_path, + } + + +def dict2(d): + return defaultdict(dict, ((k, dict(v)) for k, v in d.items())) + + +def check(paths=None, debug=False, echoerr=echoerr, require_ext=None): + '''Check configuration sanity + + :param list paths: + Paths from which configuration should be loaded. + :param bool debug: + Determines whether some information useful for debugging linter should + be output. + :param function echoerr: + Function that will be used to echo the error(s). Should accept four + optional keyword parameters: ``problem`` and ``problem_mark``, and + ``context`` and ``context_mark``. + :param str require_ext: + Require configuration for some extension to be present. + + :return: + ``False`` if user configuration seems to be completely sane and ``True`` + if some problems were found. + ''' + hadproblem = False + + register_common_names() + search_paths = paths or get_config_paths() + find_config_files = generate_config_finder(lambda: search_paths) + + logger = logging.getLogger('powerline-lint') + logger.setLevel(logging.DEBUG if debug else logging.ERROR) + logger.addHandler(logging.StreamHandler()) + + ee = EchoErr(echoerr, logger) + + if require_ext: + used_main_spec = main_spec.copy() + try: + used_main_spec['ext'][require_ext].required() + except KeyError: + used_main_spec['ext'][require_ext] = ext_spec() + else: + used_main_spec = main_spec + + lhadproblem = [False] + load_json_config = generate_json_config_loader(lhadproblem) + + config_loader = ConfigLoader(run_once=True, load=load_json_config) + + lists = { + 'colorschemes': set(), + 'themes': set(), + 'exts': set(), + } + found_dir = { + 'themes': False, + 'colorschemes': False, + } + config_paths = defaultdict(lambda: defaultdict(dict)) + loaded_configs = defaultdict(lambda: defaultdict(dict)) + for d in chain( + find_all_ext_config_files(search_paths, 'colorschemes'), + find_all_ext_config_files(search_paths, 'themes'), + ): + if d['error']: + hadproblem = True + ee(problem=d['error']) + continue + if d['hadproblem']: + hadproblem = True + if d['ext']: + found_dir[d['type']] = True + lists['exts'].add(d['ext']) + if d['name'] == '__main__': + pass + elif d['name'].startswith('__') or d['name'].endswith('__'): + hadproblem = True + ee(problem='File name is not supposed to start or end with “__”: {0}'.format( + d['path'])) + else: + lists[d['type']].add(d['name']) + config_paths[d['type']][d['ext']][d['name']] = d['path'] + loaded_configs[d['type']][d['ext']][d['name']] = d['config'] + else: + config_paths[d['type']][d['name']] = d['path'] + loaded_configs[d['type']][d['name']] = d['config'] + + for typ in ('themes', 'colorschemes'): + if not found_dir[typ]: + hadproblem = True + ee(problem='Subdirectory {0} was not found in paths {1}'.format(typ, ', '.join(search_paths))) + + diff = set(config_paths['colorschemes']) - set(config_paths['themes']) + if diff: + hadproblem = True + for ext in diff: + typ = 'colorschemes' if ext in config_paths['themes'] else 'themes' + if not config_paths['top_' + typ] or typ == 'themes': + ee(problem='{0} extension {1} not present in {2}'.format( + ext, + 'configuration' if ( + ext in loaded_configs['themes'] and ext in loaded_configs['colorschemes'] + ) else 'directory', + typ, + )) + + try: + main_config = load_config('config', find_config_files, config_loader) + except IOError: + main_config = {} + ee(problem='Configuration file not found: config.json') + hadproblem = True + except MarkedError as e: + main_config = {} + ee(problem=str(e)) + hadproblem = True + else: + if used_main_spec.match( + main_config, + data={'configs': config_paths, 'lists': lists}, + context=Context(main_config), + echoerr=ee + )[1]: + hadproblem = True + + import_paths = [os.path.expanduser(path) for path in main_config.get('common', {}).get('paths', [])] + + try: + colors_config = load_config('colors', find_config_files, config_loader) + except IOError: + colors_config = {} + ee(problem='Configuration file not found: colors.json') + hadproblem = True + except MarkedError as e: + colors_config = {} + ee(problem=str(e)) + hadproblem = True + else: + if colors_spec.match(colors_config, context=Context(colors_config), echoerr=ee)[1]: + hadproblem = True + + if lhadproblem[0]: + hadproblem = True + + top_colorscheme_configs = dict(loaded_configs['top_colorschemes']) + data = { + 'ext': None, + 'top_colorscheme_configs': top_colorscheme_configs, + 'ext_colorscheme_configs': {}, + 'colors_config': colors_config + } + for colorscheme, config in loaded_configs['top_colorschemes'].items(): + data['colorscheme'] = colorscheme + if top_colorscheme_spec.match(config, context=Context(config), data=data, echoerr=ee)[1]: + hadproblem = True + + ext_colorscheme_configs = dict2(loaded_configs['colorschemes']) + for ext, econfigs in ext_colorscheme_configs.items(): + data = { + 'ext': ext, + 'top_colorscheme_configs': top_colorscheme_configs, + 'ext_colorscheme_configs': ext_colorscheme_configs, + 'colors_config': colors_config, + } + for colorscheme, config in econfigs.items(): + data['colorscheme'] = colorscheme + if ext == 'vim': + spec = vim_colorscheme_spec + elif ext == 'shell': + spec = shell_colorscheme_spec + else: + spec = colorscheme_spec + if spec.match(config, context=Context(config), data=data, echoerr=ee)[1]: + hadproblem = True + + colorscheme_configs = {} + for ext in lists['exts']: + colorscheme_configs[ext] = {} + for colorscheme in lists['colorschemes']: + econfigs = ext_colorscheme_configs[ext] + ecconfigs = econfigs.get(colorscheme) + mconfigs = ( + top_colorscheme_configs.get(colorscheme), + econfigs.get('__main__'), + ecconfigs, + ) + if not (mconfigs[0] or mconfigs[2]): + continue + config = None + for mconfig in mconfigs: + if not mconfig: + continue + if config: + config = mergedicts_copy(config, mconfig) + else: + config = mconfig + colorscheme_configs[ext][colorscheme] = config + + theme_configs = dict2(loaded_configs['themes']) + top_theme_configs = dict(loaded_configs['top_themes']) + for ext, configs in theme_configs.items(): + data = { + 'ext': ext, + 'colorscheme_configs': colorscheme_configs, + 'import_paths': import_paths, + 'main_config': main_config, + 'top_themes': top_theme_configs, + 'ext_theme_configs': configs, + 'colors_config': colors_config + } + for theme, config in configs.items(): + data['theme'] = theme + if theme == '__main__': + data['theme_type'] = 'main' + spec = main_theme_spec + else: + data['theme_type'] = 'regular' + spec = theme_spec + if spec.match(config, context=Context(config), data=data, echoerr=ee)[1]: + hadproblem = True + + for top_theme, config in top_theme_configs.items(): + data = { + 'ext': None, + 'colorscheme_configs': colorscheme_configs, + 'import_paths': import_paths, + 'main_config': main_config, + 'theme_configs': theme_configs, + 'ext_theme_configs': None, + 'colors_config': colors_config + } + data['theme_type'] = 'top' + data['theme'] = top_theme + if top_theme_spec.match(config, context=Context(config), data=data, echoerr=ee)[1]: + hadproblem = True + + return hadproblem diff --git a/powerline-bin/powerline/lint/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..b64c52d Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/__pycache__/checks.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/checks.cpython-312.pyc new file mode 100644 index 0000000..06575a4 Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/checks.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/__pycache__/context.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000..b0e6f80 Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/context.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/__pycache__/imp.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/imp.cpython-312.pyc new file mode 100644 index 0000000..a1da7a3 Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/imp.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/__pycache__/inspect.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/inspect.cpython-312.pyc new file mode 100644 index 0000000..b9667eb Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/inspect.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/__pycache__/selfcheck.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/selfcheck.cpython-312.pyc new file mode 100644 index 0000000..39147ba Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/selfcheck.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/__pycache__/spec.cpython-312.pyc b/powerline-bin/powerline/lint/__pycache__/spec.cpython-312.pyc new file mode 100644 index 0000000..d346f6b Binary files /dev/null and b/powerline-bin/powerline/lint/__pycache__/spec.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/checks.py b/powerline-bin/powerline/lint/checks.py new file mode 100644 index 0000000..c2fcfc4 --- /dev/null +++ b/powerline-bin/powerline/lint/checks.py @@ -0,0 +1,866 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import re +import logging + +from collections import defaultdict + +from powerline.lib.threaded import ThreadedSegment +from powerline.lib.unicode import unicode +from powerline.lint.markedjson.markedvalue import MarkedUnicode +from powerline.lint.markedjson.error import DelayedEchoErr, Mark +from powerline.lint.selfcheck import havemarks +from powerline.lint.context import JStr, list_themes +from powerline.lint.imp import WithPath, import_function, import_segment +from powerline.lint.spec import Spec +from powerline.lint.inspect import getconfigargspec + + +list_sep = JStr(', ') + + +generic_keys = set(( + 'exclude_modes', 'include_modes', + 'exclude_function', 'include_function', + 'width', 'align', + 'name', + 'draw_soft_divider', 'draw_hard_divider', + 'priority', + 'after', 'before', + 'display' +)) +type_keys = { + 'function': set(('function', 'args', 'draw_inner_divider')), + 'string': set(('contents', 'type', 'highlight_groups', 'divider_highlight_group')), + 'segment_list': set(('function', 'segments', 'args', 'type')), +} +required_keys = { + 'function': set(('function',)), + 'string': set(()), + 'segment_list': set(('function', 'segments',)), +} +highlight_keys = set(('highlight_groups', 'name')) + + +def get_function_strings(function_name, context, ext): + if '.' in function_name: + module, function_name = function_name.rpartition('.')[::2] + else: + module = context[0][1].get( + 'default_module', MarkedUnicode('powerline.segments.' + ext, None)) + return module, function_name + + +def check_matcher_func(ext, match_name, data, context, echoerr): + havemarks(match_name) + import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])] + + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.matchers.{0}'.format(ext) + match_function = match_name + with WithPath(import_paths): + try: + func = getattr(__import__(str(match_module), fromlist=[str(match_function)]), str(match_function)) + except ImportError: + echoerr(context='Error while loading matcher functions', + problem='failed to load module {0}'.format(match_module), + problem_mark=match_name.mark) + return True, False, True + except AttributeError: + echoerr(context='Error while loading matcher functions', + problem='failed to load matcher function {0}'.format(match_function), + problem_mark=match_name.mark) + return True, False, True + + if not callable(func): + echoerr(context='Error while loading matcher functions', + problem='loaded “function” {0} is not callable'.format(match_function), + problem_mark=match_name.mark) + return True, False, True + + if hasattr(func, 'func_code') and hasattr(func.func_code, 'co_argcount'): + if func.func_code.co_argcount != 1: + echoerr( + context='Error while loading matcher functions', + problem=( + 'function {0} accepts {1} arguments instead of 1. ' + 'Are you sure it is the proper function?' + ).format(match_function, func.func_code.co_argcount), + problem_mark=match_name.mark + ) + + return True, False, False + + +def check_ext(ext, data, context, echoerr): + havemarks(ext) + hadsomedirs = False + hadproblem = False + if ext not in data['lists']['exts']: + hadproblem = True + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='extension configuration does not exist') + else: + for typ in ('themes', 'colorschemes'): + if ext not in data['configs'][typ] and not data['configs']['top_' + typ]: + hadproblem = True + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='{0} configuration does not exist'.format(typ)) + else: + hadsomedirs = True + return hadsomedirs, hadproblem + + +def check_config(d, theme, data, context, echoerr): + if len(context) == 4: + ext = context[-2][0] + else: + # local_themes + ext = context[-3][0] + if ext not in data['lists']['exts']: + echoerr(context='Error while loading {0} extension configuration'.format(ext), + context_mark=ext.mark, + problem='extension configuration does not exist') + return True, False, True + if ( + (ext not in data['configs'][d] or theme not in data['configs'][d][ext]) + and theme not in data['configs']['top_' + d] + ): + echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext), + problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme), + problem_mark=theme.mark) + return True, False, True + return True, False, False + + +def check_top_theme(theme, data, context, echoerr): + havemarks(theme) + if theme not in data['configs']['top_themes']: + echoerr(context='Error while checking extension configuration (key {key})'.format(key=context.key), + context_mark=context[-2][0].mark, + problem='failed to find top theme {0}'.format(theme), + problem_mark=theme.mark) + return True, False, True + return True, False, False + + +def check_color(color, data, context, echoerr): + havemarks(color) + if (color not in data['colors_config'].get('colors', {}) + and color not in data['colors_config'].get('gradients', {})): + echoerr( + context='Error while checking highlight group in colorscheme (key {key})'.format( + key=context.key), + problem='found unexistent color or gradient {0}'.format(color), + problem_mark=color.mark + ) + return True, False, True + return True, False, False + + +def check_translated_group_name(group, data, context, echoerr): + return check_group(group, data, context, echoerr) + + +def check_group(group, data, context, echoerr): + havemarks(group) + if not isinstance(group, unicode): + return True, False, False + colorscheme = data['colorscheme'] + ext = data['ext'] + configs = None + if ext: + def listed_key(d, k): + try: + return [d[k]] + except KeyError: + return [] + + if colorscheme == '__main__': + colorscheme_names = set(data['ext_colorscheme_configs'][ext]) + colorscheme_names.update(data['top_colorscheme_configs']) + colorscheme_names.discard('__main__') + configs = [ + ( + name, + listed_key(data['ext_colorscheme_configs'][ext], name) + + listed_key(data['ext_colorscheme_configs'][ext], '__main__') + + listed_key(data['top_colorscheme_configs'], name) + ) + for name in colorscheme_names + ] + else: + configs = [ + ( + colorscheme, + listed_key(data['ext_colorscheme_configs'][ext], colorscheme) + + listed_key(data['ext_colorscheme_configs'][ext], '__main__') + + listed_key(data['top_colorscheme_configs'], colorscheme) + ) + ] + else: + try: + configs = [(colorscheme, [data['top_colorscheme_configs'][colorscheme]])] + except KeyError: + pass + hadproblem = False + for new_colorscheme, config_lst in configs: + not_found = [] + new_data = data.copy() + new_data['colorscheme'] = new_colorscheme + for config in config_lst: + havemarks(config) + try: + group_data = config['groups'][group] + except KeyError: + not_found.append(config.mark.name) + else: + proceed, echo, chadproblem = check_group( + group_data, + new_data, + context, + echoerr, + ) + if chadproblem: + hadproblem = True + if not proceed: + break + if not_found and len(not_found) == len(config_lst): + echoerr( + context='Error while checking group definition in colorscheme (key {key})'.format( + key=context.key), + problem='name {0} is not present anywhere in {1} {2} {3} colorschemes: {4}'.format( + group, len(not_found), ext, new_colorscheme, ', '.join(not_found)), + problem_mark=group.mark + ) + hadproblem = True + return True, False, hadproblem + + +def check_key_compatibility(segment, data, context, echoerr): + havemarks(segment) + segment_type = segment.get('type', MarkedUnicode('function', None)) + havemarks(segment_type) + + if segment_type not in type_keys: + echoerr(context='Error while checking segments (key {key})'.format(key=context.key), + problem='found segment with unknown type {0}'.format(segment_type), + problem_mark=segment_type.mark) + return False, False, True + + hadproblem = False + + keys = set(segment) + if not ((keys - generic_keys) < type_keys[segment_type]): + unknown_keys = keys - generic_keys - type_keys[segment_type] + echoerr( + context='Error while checking segments (key {key})'.format(key=context.key), + context_mark=context[-1][1].mark, + problem='found keys not used with the current segment type: {0}'.format( + list_sep.join(unknown_keys)), + problem_mark=list(unknown_keys)[0].mark + ) + hadproblem = True + + if not (keys >= required_keys[segment_type]): + missing_keys = required_keys[segment_type] - keys + echoerr( + context='Error while checking segments (key {key})'.format(key=context.key), + context_mark=context[-1][1].mark, + problem='found missing required keys: {0}'.format( + list_sep.join(missing_keys)) + ) + hadproblem = True + + if not (segment_type == 'function' or (keys & highlight_keys)): + echoerr( + context='Error while checking segments (key {key})'.format(key=context.key), + context_mark=context[-1][1].mark, + problem=( + 'found missing keys required to determine highlight group. ' + 'Either highlight_groups or name key must be present' + ) + ) + hadproblem = True + + return True, False, hadproblem + + +def check_segment_module(module, data, context, echoerr): + havemarks(module) + with WithPath(data['import_paths']): + try: + __import__(str(module)) + except ImportError as e: + if echoerr.logger.level >= logging.DEBUG: + echoerr.logger.exception(e) + echoerr(context='Error while checking segments (key {key})'.format(key=context.key), + problem='failed to import module {0}'.format(module), + problem_mark=module.mark) + return True, False, True + return True, False, False + + +def check_full_segment_data(segment, data, context, echoerr): + if 'name' not in segment and 'function' not in segment: + return True, False, False + + ext = data['ext'] + theme_segment_data = context[0][1].get('segment_data', {}) + main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + if not main_theme_name or data['theme'] == main_theme_name: + top_segment_data = {} + else: + top_segment_data = data['ext_theme_configs'].get(main_theme_name, {}).get('segment_data', {}) + + if segment.get('type', 'function') == 'function': + function_name = segment.get('function') + if function_name: + module, function_name = get_function_strings(function_name, context, ext) + names = [module + '.' + function_name, function_name] + else: + names = [] + elif segment.get('name'): + names = [segment['name']] + else: + return True, False, False + + segment_copy = segment.copy() + + for key in ('before', 'after', 'args', 'contents'): + if key not in segment_copy: + for segment_data in [theme_segment_data, top_segment_data]: + for name in names: + try: + val = segment_data[name][key] + k = segment_data[name].keydict[key] + segment_copy[k] = val + except KeyError: + pass + + return check_key_compatibility(segment_copy, data, context, echoerr) + + +highlight_group_spec = Spec().ident().copy +_highlight_group_spec = highlight_group_spec().context_message( + 'Error while checking function documentation while checking theme (key {key})') + + +def check_hl_group_name(hl_group, context_mark, context, echoerr): + '''Check highlight group name: it should match naming conventions + + :param str hl_group: + Checked group. + :param Mark context_mark: + Context mark. May be ``None``. + :param Context context: + Current context. + :param func echoerr: + Function used for error reporting. + + :return: ``False`` if check succeeded and ``True`` if it failed. + ''' + return _highlight_group_spec.match(hl_group, context_mark=context_mark, context=context, echoerr=echoerr)[1] + + +def check_segment_function(function_name, data, context, echoerr): + havemarks(function_name) + ext = data['ext'] + module, function_name = get_function_strings(function_name, context, ext) + if context[-2][1].get('type', 'function') == 'function': + func = import_segment(function_name, data, context, echoerr, module=module) + + if not func: + return True, False, True + + hl_groups = [] + divider_hl_group = None + + hadproblem = False + + if func.__doc__: + NO_H_G_USED_STR = 'No highlight groups are used (literal segment).' + H_G_USED_STR = 'Highlight groups used: ' + LHGUS = len(H_G_USED_STR) + D_H_G_USED_STR = 'Divider highlight group used: ' + LDHGUS = len(D_H_G_USED_STR) + pointer = 0 + mark_name = '<{0} docstring>'.format(function_name) + for i, line in enumerate(func.__doc__.split('\n')): + if H_G_USED_STR in line: + idx = line.index(H_G_USED_STR) + LHGUS + if hl_groups is None: + idx -= LHGUS + mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx) + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + context_mark=function_name.mark, + problem=( + 'found highlight group definition in addition to sentense stating that ' + 'no highlight groups are used' + ), + problem_mark=mark, + ) + hadproblem = True + continue + hl_groups.append(( + line[idx:], + (mark_name, i + 1, idx + 1, func.__doc__), + pointer + idx + )) + elif D_H_G_USED_STR in line: + idx = line.index(D_H_G_USED_STR) + LDHGUS + 2 + mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx) + divider_hl_group = MarkedUnicode(line[idx:-3], mark) + elif NO_H_G_USED_STR in line: + idx = line.index(NO_H_G_USED_STR) + if hl_groups: + mark = Mark(mark_name, i + 1, idx + 1, func.__doc__, pointer + idx) + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + context_mark=function_name.mark, + problem=( + 'found sentense stating that no highlight groups are used ' + 'in addition to highlight group definition' + ), + problem_mark=mark, + ) + hadproblem = True + continue + hl_groups = None + pointer += len(line) + len('\n') + + if divider_hl_group: + r = hl_exists(divider_hl_group, data, context, echoerr, allow_gradients=True) + if r: + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + context_mark=function_name.mark, + problem=( + 'found highlight group {0} not defined in the following colorschemes: {1}\n' + '(Group name was obtained from function documentation.)' + ).format(divider_hl_group, list_sep.join(r)), + problem_mark=divider_hl_group.mark, + ) + hadproblem = True + if check_hl_group_name(divider_hl_group, function_name.mark, context, echoerr): + hadproblem = True + + if hl_groups: + greg = re.compile(r'``([^`]+)``( \(gradient\))?') + parsed_hl_groups = [] + for line, mark_args, pointer in hl_groups: + for s in line.split(', '): + required_pack = [] + sub_pointer = pointer + for subs in s.split(' or '): + match = greg.match(subs) + try: + if not match: + continue + hl_group = MarkedUnicode( + match.group(1), + Mark(*mark_args, pointer=sub_pointer + match.start(1)) + ) + if check_hl_group_name(hl_group, function_name.mark, context, echoerr): + hadproblem = True + gradient = bool(match.group(2)) + required_pack.append((hl_group, gradient)) + finally: + sub_pointer += len(subs) + len(' or ') + parsed_hl_groups.append(required_pack) + pointer += len(s) + len(', ') + del hl_group, gradient + for required_pack in parsed_hl_groups: + rs = [ + hl_exists(hl_group, data, context, echoerr, allow_gradients=('force' if gradient else False)) + for hl_group, gradient in required_pack + ] + if all(rs): + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + problem=( + 'found highlight groups list ({0}) with all groups not defined in some colorschemes\n' + '(Group names were taken from function documentation.)' + ).format(list_sep.join((h[0] for h in required_pack))), + problem_mark=function_name.mark + ) + for r, h in zip(rs, required_pack): + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + h[0], list_sep.join(r)) + ) + hadproblem = True + elif hl_groups is not None: + r = hl_exists(function_name, data, context, echoerr, allow_gradients=True) + if r: + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + problem=( + 'found highlight group {0} not defined in the following colorschemes: {1}\n' + '(If not specified otherwise in documentation, ' + 'highlight group for function segments\n' + 'is the same as the function name.)' + ).format(function_name, list_sep.join(r)), + problem_mark=function_name.mark + ) + hadproblem = True + + return True, False, hadproblem + elif context[-2][1].get('type') != 'segment_list': + if function_name not in context[0][1].get('segment_data', {}): + main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + if data['theme'] == main_theme_name: + main_theme = {} + else: + main_theme = data['ext_theme_configs'].get(main_theme_name, {}) + if ( + function_name not in main_theme.get('segment_data', {}) + and function_name not in data['ext_theme_configs'].get('__main__', {}).get('segment_data', {}) + and not any(((function_name in theme.get('segment_data', {})) for theme in data['top_themes'].values())) + ): + echoerr(context='Error while checking segments (key {key})'.format(key=context.key), + problem='found useless use of name key (such name is not present in theme/segment_data)', + problem_mark=function_name.mark) + + return True, False, False + + +def hl_group_in_colorscheme(hl_group, cconfig, allow_gradients, data, context, echoerr): + havemarks(hl_group, cconfig) + if hl_group not in cconfig.get('groups', {}): + return False + elif not allow_gradients or allow_gradients == 'force': + group_config = cconfig['groups'][hl_group] + while isinstance(group_config, unicode): + try: + group_config = cconfig['groups'][group_config] + except KeyError: + # No such group. Error was already reported when checking + # colorschemes. + return True + havemarks(group_config) + hadgradient = False + for ckey in ('fg', 'bg'): + color = group_config.get(ckey) + if not color: + # No color. Error was already reported when checking + # colorschemes. + return True + havemarks(color) + # Gradients are only allowed for function segments. Note that + # whether *either* color or gradient exists should have been + # already checked + hascolor = color in data['colors_config'].get('colors', {}) + hasgradient = color in data['colors_config'].get('gradients', {}) + if hasgradient: + hadgradient = True + if allow_gradients is False and not hascolor and hasgradient: + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context.key), + context_mark=hl_group.mark, + problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), + problem_mark=color.mark + ) + return False + if allow_gradients == 'force' and not hadgradient: + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context.key), + context_mark=hl_group.mark, + problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), + problem_mark=group_config.mark + ) + return False + return True + + +def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): + havemarks(hl_group) + ext = data['ext'] + if ext not in data['colorscheme_configs']: + # No colorschemes. Error was already reported, no need to report it + # twice + return [] + r = [] + found = False + for colorscheme, cconfig in data['colorscheme_configs'][ext].items(): + if hl_group_in_colorscheme(hl_group, cconfig, allow_gradients, data, context, echoerr): + found = True + else: + r.append(colorscheme) + if not found: + pass + return r + + +def check_highlight_group(hl_group, data, context, echoerr): + havemarks(hl_group) + r = hl_exists(hl_group, data, context, echoerr) + if r: + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + hl_group, list_sep.join(r)), + problem_mark=hl_group.mark + ) + return True, False, True + return True, False, False + + +def check_highlight_groups(hl_groups, data, context, echoerr): + havemarks(hl_groups) + rs = [hl_exists(hl_group, data, context, echoerr) for hl_group in hl_groups] + if all(rs): + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + problem='found highlight groups list ({0}) with all groups not defined in some colorschemes'.format( + list_sep.join((unicode(h) for h in hl_groups))), + problem_mark=hl_groups.mark + ) + for r, hl_group in zip(rs, hl_groups): + echoerr( + context='Error while checking theme (key {key})'.format(key=context.key), + problem='found highlight group {0} not defined in the following colorschemes: {1}'.format( + hl_group, list_sep.join(r)), + problem_mark=hl_group.mark + ) + return True, False, True + return True, False, False + + +def check_segment_data_key(key, data, context, echoerr): + havemarks(key) + has_module_name = '.' in key + found = False + for ext, theme in list_themes(data, context): + for segments in theme.get('segments', {}).values(): + for segment in segments: + if 'name' in segment: + if key == segment['name']: + found = True + break + else: + function_name = segment.get('function') + if function_name: + module, function_name = get_function_strings(function_name, ((None, theme),), ext) + if has_module_name: + full_name = module + '.' + function_name + if key == full_name: + found = True + break + else: + if key == function_name: + found = True + break + if found: + break + if found: + break + else: + if data['theme_type'] != 'top': + echoerr(context='Error while checking segment data', + problem='found key {0} that cannot be associated with any segment'.format(key), + problem_mark=key.mark) + return True, False, True + + return True, False, False + + +threaded_args_specs = { + 'interval': Spec().cmp('gt', 0.0), + 'update_first': Spec().type(bool), + 'shutdown_event': Spec().error('Shutdown event must be set by powerline'), +} + + +def check_args_variant(func, args, data, context, echoerr): + havemarks(args) + argspec = getconfigargspec(func) + present_args = set(args) + all_args = set(argspec.args) + required_args = set(argspec.args[:-len(argspec.defaults)]) + + hadproblem = False + + if required_args - present_args: + echoerr( + context='Error while checking segment arguments (key {key})'.format(key=context.key), + context_mark=args.mark, + problem='some of the required keys are missing: {0}'.format(list_sep.join(required_args - present_args)) + ) + hadproblem = True + + if not all_args >= present_args: + echoerr(context='Error while checking segment arguments (key {key})'.format(key=context.key), + context_mark=args.mark, + problem='found unknown keys: {0}'.format(list_sep.join(present_args - all_args)), + problem_mark=next(iter(present_args - all_args)).mark) + hadproblem = True + + if isinstance(func, ThreadedSegment): + for key in set(threaded_args_specs) & present_args: + proceed, khadproblem = threaded_args_specs[key].match( + args[key], + args.mark, + data, + context.enter_key(args, key), + echoerr + ) + if khadproblem: + hadproblem = True + if not proceed: + return hadproblem + + return hadproblem + + +def check_args(get_functions, args, data, context, echoerr): + new_echoerr = DelayedEchoErr(echoerr) + count = 0 + hadproblem = False + for func in get_functions(data, context, new_echoerr): + count += 1 + shadproblem = check_args_variant(func, args, data, context, echoerr) + if shadproblem: + hadproblem = True + + if not count: + hadproblem = True + if new_echoerr: + new_echoerr.echo_all() + else: + echoerr(context='Error while checking segment arguments (key {key})'.format(key=context.key), + context_mark=context[-2][1].mark, + problem='no suitable segments found') + + return True, False, hadproblem + + +def get_one_segment_function(data, context, echoerr): + ext = data['ext'] + function_name = context[-2][1].get('function') + if function_name: + module, function_name = get_function_strings(function_name, context, ext) + func = import_segment(function_name, data, context, echoerr, module=module) + if func: + yield func + + +common_names = defaultdict(set) + + +def register_common_name(name, cmodule, cname): + s = cmodule + '.' + cname + cmodule_mark = Mark('', 1, 1, s, 1) + cname_mark = Mark('', 1, len(cmodule) + 1, s, len(cmodule) + 1) + common_names[name].add((MarkedUnicode(cmodule, cmodule_mark), MarkedUnicode(cname, cname_mark))) + + +def get_all_possible_functions(data, context, echoerr): + name = context[-2][0] + module, name = name.rpartition('.')[::2] + if module: + func = import_segment(name, data, context, echoerr, module=module) + if func: + yield func + else: + if name in common_names: + for cmodule, cname in common_names[name]: + cfunc = import_segment(cname, data, context, echoerr, module=MarkedUnicode(cmodule, None)) + if cfunc: + yield cfunc + for ext, theme_config in list_themes(data, context): + for segments in theme_config.get('segments', {}).values(): + for segment in segments: + if segment.get('type', 'function') == 'function': + function_name = segment.get('function') + current_name = segment.get('name') + if function_name: + module, function_name = get_function_strings(function_name, ((None, theme_config),), ext) + if current_name == name or function_name == name: + func = import_segment(function_name, data, context, echoerr, module=module) + if func: + yield func + + +def check_exinclude_function(name, data, context, echoerr): + ext = data['ext'] + module, name = name.rpartition('.')[::2] + if not module: + module = MarkedUnicode('powerline.selectors.' + ext, None) + func = import_function('selector', name, data, context, echoerr, module=module) + if not func: + return True, False, True + return True, False, False + + +def check_log_file_level(this_level, data, context, echoerr): + '''Check handler level specified in :ref:`log_file key ` + + This level must be greater or equal to the level in :ref:`log_level key + `. + ''' + havemarks(this_level) + hadproblem = False + top_level = context[0][1].get('common', {}).get('log_level', 'WARNING') + top_level_str = top_level + top_level_mark = getattr(top_level, 'mark', None) + if ( + not isinstance(top_level, unicode) or not hasattr(logging, top_level) + or not isinstance(this_level, unicode) or not hasattr(logging, this_level) + ): + return True, False, hadproblem + top_level = getattr(logging, top_level) + this_level_str = this_level + this_level_mark = this_level.mark + this_level = getattr(logging, this_level) + if this_level < top_level: + echoerr( + context='Error while checking log level index (key {key})'.format( + key=context.key), + context_mark=this_level_mark, + problem='found level that is less critical then top level ({0} < {0})'.format( + this_level_str, top_level_str), + problem_mark=top_level_mark, + ) + hadproblem = True + return True, False, hadproblem + + +def check_logging_handler(handler_name, data, context, echoerr): + havemarks(handler_name) + import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])] + + handler_module, separator, handler_class = handler_name.rpartition('.') + if not separator: + handler_module = 'logging.handlers' + handler_class = handler_name + with WithPath(import_paths): + try: + handler = getattr(__import__(str(handler_module), fromlist=[str(handler_class)]), str(handler_class)) + except ImportError: + echoerr(context='Error while loading logger class (key {key})'.format(key=context.key), + problem='failed to load module {0}'.format(handler_module), + problem_mark=handler_name.mark) + return True, False, True + except AttributeError: + echoerr(context='Error while loading logger class (key {key})'.format(key=context.key), + problem='failed to load handler class {0}'.format(handler_class), + problem_mark=handler_name.mark) + return True, False, True + + if not issubclass(handler, logging.Handler): + echoerr(context='Error while loading logger class (key {key})'.format(key=context.key), + problem='loaded class {0} is not a logging.Handler subclass'.format(handler_class), + problem_mark=handler_name.mark) + return True, False, True + + return True, False, False diff --git a/powerline-bin/powerline/lint/context.py b/powerline-bin/powerline/lint/context.py new file mode 100644 index 0000000..a48a283 --- /dev/null +++ b/powerline-bin/powerline/lint/context.py @@ -0,0 +1,68 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import itertools + +from powerline.lib.unicode import unicode +from powerline.lint.markedjson.markedvalue import MarkedUnicode +from powerline.lint.selfcheck import havemarks + + +class JStr(unicode): + def join(self, iterable): + return super(JStr, self).join((unicode(item) for item in iterable)) + + +key_sep = JStr('/') + + +def list_themes(data, context): + theme_type = data['theme_type'] + ext = data['ext'] + main_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None) + is_main_theme = (data['theme'] == main_theme_name) + if theme_type == 'top': + return list(itertools.chain(*[ + [(theme_ext, theme) for theme in theme_configs.values()] + for theme_ext, theme_configs in data['theme_configs'].items() + ])) + elif theme_type == 'main' or is_main_theme: + return [(ext, theme) for theme in data['ext_theme_configs'].values()] + else: + return [(ext, context[0][1])] + + +class Context(tuple): + for func in dir(tuple): + if func in ('__getitem__', '__init__', '__getattribute__', '__len__', '__iter__'): + continue + exec(( + 'def {0}(self, *args, **kwargs):\n' + ' raise TypeError("{0} is not allowed for Context")' + ).format(func)) + del func + + __slots__ = () + + def __new__(cls, base, context_key=None, context_value=None): + if context_key is not None: + assert(context_value is not None) + assert(type(base) is Context) + havemarks(context_key, context_value) + return tuple.__new__(cls, tuple.__add__(base, ((context_key, context_value),))) + else: + havemarks(base) + return tuple.__new__(cls, ((MarkedUnicode('', base.mark), base),)) + + @property + def key(self): + return key_sep.join((c[0] for c in self)) + + def enter_key(self, value, key): + return self.enter(value.keydict[key], value[key]) + + def enter_item(self, name, item): + return self.enter(MarkedUnicode(name, item.mark), item) + + def enter(self, context_key, context_value): + return Context.__new__(Context, self, context_key, context_value) diff --git a/powerline-bin/powerline/lint/imp.py b/powerline-bin/powerline/lint/imp.py new file mode 100644 index 0000000..399654e --- /dev/null +++ b/powerline-bin/powerline/lint/imp.py @@ -0,0 +1,56 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from powerline.lint.selfcheck import havemarks + + +class WithPath(object): + def __init__(self, import_paths): + self.import_paths = import_paths + + def __enter__(self): + self.oldpath = sys.path + sys.path = self.import_paths + sys.path + + def __exit__(self, *args): + sys.path = self.oldpath + + +def import_function(function_type, name, data, context, echoerr, module): + havemarks(name, module) + + if module == 'powerline.segments.i3wm' and name == 'workspaces': + echoerr(context='Warning while checking segments (key {key})'.format(key=context.key), + context_mark=name.mark, + problem='segment {0} from {1} is deprecated'.format(name, module), + problem_mark=module.mark) + + with WithPath(data['import_paths']): + try: + func = getattr(__import__(str(module), fromlist=[str(name)]), str(name)) + except ImportError: + echoerr(context='Error while checking segments (key {key})'.format(key=context.key), + context_mark=name.mark, + problem='failed to import module {0}'.format(module), + problem_mark=module.mark) + return None + except AttributeError: + echoerr(context='Error while loading {0} function (key {key})'.format(function_type, key=context.key), + problem='failed to load function {0} from module {1}'.format(name, module), + problem_mark=name.mark) + return None + + if not callable(func): + echoerr(context='Error while checking segments (key {key})'.format(key=context.key), + context_mark=name.mark, + problem='imported “function” {0} from module {1} is not callable'.format(name, module), + problem_mark=module.mark) + return None + + return func + + +def import_segment(*args, **kwargs): + return import_function('segment', *args, **kwargs) diff --git a/powerline-bin/powerline/lint/inspect.py b/powerline-bin/powerline/lint/inspect.py new file mode 100644 index 0000000..15bb610 --- /dev/null +++ b/powerline-bin/powerline/lint/inspect.py @@ -0,0 +1,63 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from inspect import ArgSpec, getargspec + +from powerline.segments import Segment + + +def getconfigargspec(obj): + if hasattr(obj, 'powerline_origin'): + obj = obj.powerline_origin + else: + obj = obj + + args = [] + defaults = [] + + if isinstance(obj, Segment): + additional_args = obj.additional_args() + argspecobjs = obj.argspecobjs() + get_omitted_args = obj.omitted_args + else: + additional_args = () + argspecobjs = ((None, obj),) + get_omitted_args = lambda *args: () + + for arg in additional_args: + args.append(arg[0]) + if len(arg) > 1: + defaults.append(arg[1]) + + requires_segment_info = hasattr(obj, 'powerline_requires_segment_info') + requires_filesystem_watcher = hasattr(obj, 'powerline_requires_filesystem_watcher') + + for name, method in argspecobjs: + argspec = getargspec(method) + omitted_args = get_omitted_args(name, method) + largs = len(argspec.args) + for i, arg in enumerate(reversed(argspec.args)): + if ( + largs - (i + 1) in omitted_args + or arg in omitted_args + or arg == 'pl' + or arg == 'self' + or (arg == 'create_watcher' and requires_filesystem_watcher) + or (arg == 'segment_info' and requires_segment_info) + ): + continue + if argspec.defaults and len(argspec.defaults) > i: + if arg in args: + idx = args.index(arg) + if len(args) - idx > len(defaults): + args.pop(idx) + else: + continue + default = argspec.defaults[-(i + 1)] + defaults.append(default) + args.append(arg) + else: + if arg not in args: + args.insert(0, arg) + + return ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults)) diff --git a/powerline-bin/powerline/lint/markedjson/__init__.py b/powerline-bin/powerline/lint/markedjson/__init__.py new file mode 100644 index 0000000..dea5faf --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/__init__.py @@ -0,0 +1,19 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lint.markedjson.loader import Loader + + +def load(stream, Loader=Loader): + '''Parse JSON value and produce the corresponding Python object + + :return: + (hadproblem, object) where first argument is true if there were errors + during loading JSON stream and second is the corresponding JSON object. + ''' + loader = Loader(stream) + try: + r = loader.get_single_data() + return r, loader.haserrors + finally: + loader.dispose() diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..21707ef Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/composer.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/composer.cpython-312.pyc new file mode 100644 index 0000000..59e44ad Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/composer.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/constructor.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/constructor.cpython-312.pyc new file mode 100644 index 0000000..41267f7 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/constructor.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/error.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/error.cpython-312.pyc new file mode 100644 index 0000000..6b223d3 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/error.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/events.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/events.cpython-312.pyc new file mode 100644 index 0000000..0edf4bf Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/events.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/loader.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/loader.cpython-312.pyc new file mode 100644 index 0000000..b3df245 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/loader.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/markedvalue.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/markedvalue.cpython-312.pyc new file mode 100644 index 0000000..97e5542 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/markedvalue.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/nodes.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/nodes.cpython-312.pyc new file mode 100644 index 0000000..20ba55f Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/nodes.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/parser.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/parser.cpython-312.pyc new file mode 100644 index 0000000..956cdd9 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/parser.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/reader.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/reader.cpython-312.pyc new file mode 100644 index 0000000..055f839 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/reader.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/resolver.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/resolver.cpython-312.pyc new file mode 100644 index 0000000..4e0060e Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/resolver.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/scanner.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/scanner.cpython-312.pyc new file mode 100644 index 0000000..581067a Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/scanner.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/__pycache__/tokens.cpython-312.pyc b/powerline-bin/powerline/lint/markedjson/__pycache__/tokens.cpython-312.pyc new file mode 100644 index 0000000..3081a02 Binary files /dev/null and b/powerline-bin/powerline/lint/markedjson/__pycache__/tokens.cpython-312.pyc differ diff --git a/powerline-bin/powerline/lint/markedjson/composer.py b/powerline-bin/powerline/lint/markedjson/composer.py new file mode 100644 index 0000000..bd5620d --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/composer.py @@ -0,0 +1,119 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lint.markedjson import nodes +from powerline.lint.markedjson import events +from powerline.lint.markedjson.error import MarkedError + + +__all__ = ['Composer', 'ComposerError'] + + +class ComposerError(MarkedError): + pass + + +class Composer: + def __init__(self): + pass + + def check_node(self): + # Drop the STREAM-START event. + if self.check_event(events.StreamStartEvent): + self.get_event() + + # If there are more documents available? + return not self.check_event(events.StreamEndEvent) + + def get_node(self): + # Get the root node of the next document. + if not self.check_event(events.StreamEndEvent): + return self.compose_document() + + def get_single_node(self): + # Drop the STREAM-START event. + self.get_event() + + # Compose a document if the stream is not empty. + document = None + if not self.check_event(events.StreamEndEvent): + document = self.compose_document() + + # Ensure that the stream contains no more documents. + if not self.check_event(events.StreamEndEvent): + event = self.get_event() + raise ComposerError( + 'expected a single document in the stream', + document.start_mark, + 'but found another document', + event.start_mark + ) + + # Drop the STREAM-END event. + self.get_event() + + return document + + def compose_document(self): + # Drop the DOCUMENT-START event. + self.get_event() + + # Compose the root node. + node = self.compose_node(None, None) + + # Drop the DOCUMENT-END event. + self.get_event() + + return node + + def compose_node(self, parent, index): + self.descend_resolver(parent, index) + if self.check_event(events.ScalarEvent): + node = self.compose_scalar_node() + elif self.check_event(events.SequenceStartEvent): + node = self.compose_sequence_node() + elif self.check_event(events.MappingStartEvent): + node = self.compose_mapping_node() + self.ascend_resolver() + return node + + def compose_scalar_node(self): + event = self.get_event() + tag = event.tag + if tag is None or tag == '!': + tag = self.resolve(nodes.ScalarNode, event.value, event.implicit, event.start_mark) + node = nodes.ScalarNode(tag, event.value, event.start_mark, event.end_mark, style=event.style) + return node + + def compose_sequence_node(self): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(nodes.SequenceNode, None, start_event.implicit) + node = nodes.SequenceNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) + index = 0 + while not self.check_event(events.SequenceEndEvent): + node.value.append(self.compose_node(node, index)) + index += 1 + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + + def compose_mapping_node(self): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == '!': + tag = self.resolve(nodes.MappingNode, None, start_event.implicit) + node = nodes.MappingNode(tag, [], start_event.start_mark, None, flow_style=start_event.flow_style) + while not self.check_event(events.MappingEndEvent): + # key_event = self.peek_event() + item_key = self.compose_node(node, None) + # if item_key in node.value: + # raise ComposerError('while composing a mapping', start_event.start_mark, + # 'found duplicate key', key_event.start_mark) + item_value = self.compose_node(node, item_key) + # node.value[item_key] = item_value + node.value.append((item_key, item_value)) + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node diff --git a/powerline-bin/powerline/lint/markedjson/constructor.py b/powerline-bin/powerline/lint/markedjson/constructor.py new file mode 100644 index 0000000..2a95d84 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/constructor.py @@ -0,0 +1,285 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import collections +import types + +from functools import wraps + +from powerline.lint.markedjson.error import MarkedError + +from powerline.lint.markedjson import nodes +from powerline.lint.markedjson.markedvalue import gen_marked_value +from powerline.lib.unicode import unicode + + +def marked(func): + @wraps(func) + def f(self, node, *args, **kwargs): + return gen_marked_value(func(self, node, *args, **kwargs), node.start_mark) + return f + + +class ConstructorError(MarkedError): + pass + + +class BaseConstructor: + yaml_constructors = {} + + def __init__(self): + self.constructed_objects = {} + self.state_generators = [] + self.deep_construct = False + + def check_data(self): + # If there are more documents available? + return self.check_node() + + def get_data(self): + # Construct and return the next document. + if self.check_node(): + return self.construct_document(self.get_node()) + + def get_single_data(self): + # Ensure that the stream contains a single document and construct it. + node = self.get_single_node() + if node is not None: + return self.construct_document(node) + return None + + def construct_document(self, node): + data = self.construct_object(node) + while self.state_generators: + state_generators = self.state_generators + self.state_generators = [] + for generator in state_generators: + for dummy in generator: + pass + self.constructed_objects = {} + self.deep_construct = False + return data + + def construct_object(self, node, deep=False): + if node in self.constructed_objects: + return self.constructed_objects[node] + if deep: + old_deep = self.deep_construct + self.deep_construct = True + constructor = None + tag_suffix = None + if node.tag in self.yaml_constructors: + constructor = self.yaml_constructors[node.tag] + else: + raise ConstructorError(None, None, 'no constructor for tag %s' % node.tag) + if tag_suffix is None: + data = constructor(self, node) + else: + data = constructor(self, tag_suffix, node) + if isinstance(data, types.GeneratorType): + generator = data + data = next(generator) + if self.deep_construct: + for dummy in generator: + pass + else: + self.state_generators.append(generator) + self.constructed_objects[node] = data + if deep: + self.deep_construct = old_deep + return data + + @marked + def construct_scalar(self, node): + if not isinstance(node, nodes.ScalarNode): + raise ConstructorError( + None, None, + 'expected a scalar node, but found %s' % node.id, + node.start_mark + ) + return node.value + + def construct_sequence(self, node, deep=False): + if not isinstance(node, nodes.SequenceNode): + raise ConstructorError( + None, None, + 'expected a sequence node, but found %s' % node.id, + node.start_mark + ) + return [ + self.construct_object(child, deep=deep) + for child in node.value + ] + + @marked + def construct_mapping(self, node, deep=False): + if not isinstance(node, nodes.MappingNode): + raise ConstructorError( + None, None, + 'expected a mapping node, but found %s' % node.id, + node.start_mark + ) + mapping = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + if not isinstance(key, collections.Hashable): + self.echoerr( + 'While constructing a mapping', node.start_mark, + 'found unhashable key', key_node.start_mark + ) + continue + elif type(key.value) != unicode: + self.echoerr( + 'Error while constructing a mapping', node.start_mark, + 'found key that is not a string', key_node.start_mark + ) + continue + elif key in mapping: + self.echoerr( + 'Error while constructing a mapping', node.start_mark, + 'found duplicate key', key_node.start_mark + ) + continue + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping + + @classmethod + def add_constructor(cls, tag, constructor): + if 'yaml_constructors' not in cls.__dict__: + cls.yaml_constructors = cls.yaml_constructors.copy() + cls.yaml_constructors[tag] = constructor + + +class Constructor(BaseConstructor): + def construct_scalar(self, node): + if isinstance(node, nodes.MappingNode): + for key_node, value_node in node.value: + if key_node.tag == 'tag:yaml.org,2002:value': + return self.construct_scalar(value_node) + return BaseConstructor.construct_scalar(self, node) + + def flatten_mapping(self, node): + merge = [] + index = 0 + while index < len(node.value): + key_node, value_node = node.value[index] + if key_node.tag == 'tag:yaml.org,2002:merge': + del node.value[index] + if isinstance(value_node, nodes.MappingNode): + self.flatten_mapping(value_node) + merge.extend(value_node.value) + elif isinstance(value_node, nodes.SequenceNode): + submerge = [] + for subnode in value_node.value: + if not isinstance(subnode, nodes.MappingNode): + raise ConstructorError( + 'while constructing a mapping', + node.start_mark, + 'expected a mapping for merging, but found %s' % subnode.id, + subnode.start_mark + ) + self.flatten_mapping(subnode) + submerge.append(subnode.value) + submerge.reverse() + for value in submerge: + merge.extend(value) + else: + raise ConstructorError( + 'while constructing a mapping', + node.start_mark, + ('expected a mapping or list of mappings for merging, but found %s' % value_node.id), + value_node.start_mark + ) + elif key_node.tag == 'tag:yaml.org,2002:value': + key_node.tag = 'tag:yaml.org,2002:str' + index += 1 + else: + index += 1 + if merge: + node.value = merge + node.value + + def construct_mapping(self, node, deep=False): + if isinstance(node, nodes.MappingNode): + self.flatten_mapping(node) + return BaseConstructor.construct_mapping(self, node, deep=deep) + + @marked + def construct_yaml_null(self, node): + self.construct_scalar(node) + return None + + @marked + def construct_yaml_bool(self, node): + value = self.construct_scalar(node).value + return bool(value) + + @marked + def construct_yaml_int(self, node): + value = self.construct_scalar(node).value + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '0': + return 0 + else: + return sign * int(value) + + @marked + def construct_yaml_float(self, node): + value = self.construct_scalar(node).value + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + else: + return sign * float(value) + + def construct_yaml_str(self, node): + return self.construct_scalar(node) + + def construct_yaml_seq(self, node): + data = gen_marked_value([], node.start_mark) + yield data + data.extend(self.construct_sequence(node)) + + def construct_yaml_map(self, node): + data = gen_marked_value({}, node.start_mark) + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_undefined(self, node): + raise ConstructorError( + None, None, + 'could not determine a constructor for the tag %r' % node.tag, + node.start_mark + ) + + +Constructor.add_constructor( + 'tag:yaml.org,2002:null', Constructor.construct_yaml_null) + +Constructor.add_constructor( + 'tag:yaml.org,2002:bool', Constructor.construct_yaml_bool) + +Constructor.add_constructor( + 'tag:yaml.org,2002:int', Constructor.construct_yaml_int) + +Constructor.add_constructor( + 'tag:yaml.org,2002:float', Constructor.construct_yaml_float) + +Constructor.add_constructor( + 'tag:yaml.org,2002:str', Constructor.construct_yaml_str) + +Constructor.add_constructor( + 'tag:yaml.org,2002:seq', Constructor.construct_yaml_seq) + +Constructor.add_constructor( + 'tag:yaml.org,2002:map', Constructor.construct_yaml_map) + +Constructor.add_constructor( + None, Constructor.construct_undefined) diff --git a/powerline-bin/powerline/lint/markedjson/error.py b/powerline-bin/powerline/lint/markedjson/error.py new file mode 100644 index 0000000..732120b --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/error.py @@ -0,0 +1,241 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import re + +from powerline.lib.encoding import get_preferred_output_encoding + + +NON_PRINTABLE_STR = ( + '[^' + # ASCII control characters: 0x00-0x19 + + '\t\n' # Tab, newline: allowed ASCII control characters + + '\x20-\x7E' # ASCII printable characters + # Unicode control characters: 0x7F-0x9F + + '\u0085' # Allowed unicode control character: next line character + + '\u00A0-\uD7FF' + # Surrogate escapes: 0xD800-0xDFFF + + '\uE000-\uFFFD' + + (( + '\uD800-\uDFFF' + ) if sys.maxunicode < 0x10FFFF else ( + '\U00010000-\U0010FFFF' + )) + + ']' + + (( + # Paired surrogate escapes: allowed in UCS-2 builds as the only way to + # represent characters above 0xFFFF. Only paired variant is allowed. + '|(?' % ord(s.group()) + + +def strtrans(s): + return NON_PRINTABLE_RE.sub(repl, s.replace('\t', '>---')) + + +class Mark: + def __init__(self, name, line, column, buffer, pointer, old_mark=None, merged_marks=None): + self.name = name + self.line = line + self.column = column + self.buffer = buffer + self.pointer = pointer + self.old_mark = old_mark + self.merged_marks = merged_marks or [] + + def copy(self): + return Mark(self.name, self.line, self.column, self.buffer, self.pointer, self.old_mark, self.merged_marks[:]) + + def get_snippet(self, indent=4, max_length=75): + if self.buffer is None: + return None + head = '' + start = self.pointer + while start > 0 and self.buffer[start - 1] not in '\0\n': + start -= 1 + if self.pointer - start > max_length / 2 - 1: + head = ' ... ' + start += 5 + break + tail = '' + end = self.pointer + while end < len(self.buffer) and self.buffer[end] not in '\0\n': + end += 1 + if end - self.pointer > max_length / 2 - 1: + tail = ' ... ' + end -= 5 + break + snippet = [self.buffer[start:self.pointer], self.buffer[self.pointer], self.buffer[self.pointer + 1:end]] + snippet = [strtrans(s) for s in snippet] + return ( + ' ' * indent + head + ''.join(snippet) + tail + '\n' + + ' ' * (indent + len(head) + len(snippet[0])) + '^' + ) + + def advance_string(self, diff): + ret = self.copy() + # FIXME Currently does not work properly with escaped strings. + ret.column += diff + ret.pointer += diff + return ret + + def set_old_mark(self, old_mark): + if self is old_mark: + return + checked_marks = set([id(self)]) + older_mark = old_mark + while True: + if id(older_mark) in checked_marks: + raise ValueError('Trying to set recursive marks') + checked_marks.add(id(older_mark)) + older_mark = older_mark.old_mark + if not older_mark: + break + self.old_mark = old_mark + + def set_merged_mark(self, merged_mark): + self.merged_marks.append(merged_mark) + + def to_string(self, indent=0, head_text='in ', add_snippet=True): + mark = self + where = '' + processed_marks = set() + while mark: + indentstr = ' ' * indent + where += ('%s %s"%s", line %d, column %d' % ( + indentstr, head_text, mark.name, mark.line + 1, mark.column + 1)) + if add_snippet: + snippet = mark.get_snippet(indent=(indent + 4)) + if snippet: + where += ':\n' + snippet + if mark.merged_marks: + where += '\n' + indentstr + ' with additionally merged\n' + where += mark.merged_marks[0].to_string(indent + 4, head_text='', add_snippet=False) + for mmark in mark.merged_marks[1:]: + where += '\n' + indentstr + ' and\n' + where += mmark.to_string(indent + 4, head_text='', add_snippet=False) + if add_snippet: + processed_marks.add(id(mark)) + if mark.old_mark: + where += '\n' + indentstr + ' which replaced value\n' + indent += 4 + mark = mark.old_mark + if id(mark) in processed_marks: + raise ValueError('Trying to dump recursive mark') + return where + + if sys.version_info < (3,): + def __str__(self): + return self.to_string().encode('utf-8') + + def __unicode__(self): + return self.to_string() + else: + def __str__(self): + return self.to_string() + + def __eq__(self, other): + return self is other or ( + self.name == other.name + and self.line == other.line + and self.column == other.column + ) + + +if sys.version_info < (3,): + def echoerr(**kwargs): + stream = kwargs.pop('stream', sys.stderr) + stream.write('\n') + stream.write((format_error(**kwargs) + '\n').encode(get_preferred_output_encoding())) +else: + def echoerr(**kwargs): + stream = kwargs.pop('stream', sys.stderr) + stream.write('\n') + stream.write(format_error(**kwargs) + '\n') + + +def format_error(context=None, context_mark=None, problem=None, problem_mark=None, note=None, indent=0): + lines = [] + indentstr = ' ' * indent + if context is not None: + lines.append(indentstr + context) + if ( + context_mark is not None + and ( + problem is None or problem_mark is None + or context_mark != problem_mark + ) + ): + lines.append(context_mark.to_string(indent=indent)) + if problem is not None: + lines.append(indentstr + problem) + if problem_mark is not None: + lines.append(problem_mark.to_string(indent=indent)) + if note is not None: + lines.append(indentstr + note) + return '\n'.join(lines) + + +class MarkedError(Exception): + def __init__(self, context=None, context_mark=None, problem=None, problem_mark=None, note=None): + Exception.__init__(self, format_error(context, context_mark, problem, problem_mark, note)) + + +class EchoErr(object): + __slots__ = ('echoerr', 'logger', 'indent') + + def __init__(self, echoerr, logger, indent=0): + self.echoerr = echoerr + self.logger = logger + self.indent = indent + + def __call__(self, **kwargs): + kwargs = kwargs.copy() + kwargs.setdefault('indent', self.indent) + self.echoerr(**kwargs) + + +class DelayedEchoErr(EchoErr): + __slots__ = ('echoerr', 'logger', 'errs', 'message', 'separator_message', 'indent', 'indent_shift') + + def __init__(self, echoerr, message='', separator_message=''): + super(DelayedEchoErr, self).__init__(echoerr, echoerr.logger) + self.errs = [[]] + self.message = message + self.separator_message = separator_message + self.indent_shift = (4 if message or separator_message else 0) + self.indent = echoerr.indent + self.indent_shift + + def __call__(self, **kwargs): + kwargs = kwargs.copy() + kwargs['indent'] = kwargs.get('indent', 0) + self.indent + self.errs[-1].append(kwargs) + + def next_variant(self): + self.errs.append([]) + + def echo_all(self): + if self.message: + self.echoerr(problem=self.message, indent=(self.indent - self.indent_shift)) + for variant in self.errs: + if not variant: + continue + if self.separator_message and variant is not self.errs[0]: + self.echoerr(problem=self.separator_message, indent=(self.indent - self.indent_shift)) + for kwargs in variant: + self.echoerr(**kwargs) + + def __nonzero__(self): + return not not self.errs + + __bool__ = __nonzero__ diff --git a/powerline-bin/powerline/lint/markedjson/events.py b/powerline-bin/powerline/lint/markedjson/events.py new file mode 100644 index 0000000..ef8a70e --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/events.py @@ -0,0 +1,97 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + +# Abstract classes. +class Event(object): + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + + def __repr__(self): + attributes = [ + key for key in ['implicit', 'value'] + if hasattr(self, key) + ] + arguments = ', '.join([ + '%s=%r' % (key, getattr(self, key)) + for key in attributes + ]) + return '%s(%s)' % (self.__class__.__name__, arguments) + + +class NodeEvent(Event): + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + + +class CollectionStartEvent(NodeEvent): + def __init__(self, implicit, start_mark=None, end_mark=None, flow_style=None): + self.tag = None + self.implicit = implicit + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + + +class CollectionEndEvent(Event): + pass + + +# Implementations. +class StreamStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + + +class StreamEndEvent(Event): + pass + + +class DocumentStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, explicit=None, version=None, tags=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + self.version = version + self.tags = tags + + +class DocumentEndEvent(Event): + def __init__(self, start_mark=None, end_mark=None, explicit=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + + +class AliasEvent(NodeEvent): + pass + + +class ScalarEvent(NodeEvent): + def __init__(self, implicit, value, start_mark=None, end_mark=None, style=None): + self.tag = None + self.implicit = implicit + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + + +class SequenceStartEvent(CollectionStartEvent): + pass + + +class SequenceEndEvent(CollectionEndEvent): + pass + + +class MappingStartEvent(CollectionStartEvent): + pass + + +class MappingEndEvent(CollectionEndEvent): + pass diff --git a/powerline-bin/powerline/lint/markedjson/loader.py b/powerline-bin/powerline/lint/markedjson/loader.py new file mode 100644 index 0000000..3ee5686 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/loader.py @@ -0,0 +1,25 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lint.markedjson.reader import Reader +from powerline.lint.markedjson.scanner import Scanner +from powerline.lint.markedjson.parser import Parser +from powerline.lint.markedjson.composer import Composer +from powerline.lint.markedjson.constructor import Constructor +from powerline.lint.markedjson.resolver import Resolver +from powerline.lint.markedjson.error import echoerr + + +class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self) + self.haserrors = False + + def echoerr(self, *args, **kwargs): + echoerr(*args, **kwargs) + self.haserrors = True diff --git a/powerline-bin/powerline/lint/markedjson/markedvalue.py b/powerline-bin/powerline/lint/markedjson/markedvalue.py new file mode 100644 index 0000000..3b8db3e --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/markedvalue.py @@ -0,0 +1,151 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lib.unicode import unicode + + +def gen_new(cls): + def __new__(arg_cls, value, mark): + r = super(arg_cls, arg_cls).__new__(arg_cls, value) + r.mark = mark + r.value = value + return r + return __new__ + + +def gen_init(cls): + def __init__(self, value, mark): + return cls.__init__(self, value) + return __init__ + + +def gen_getnewargs(cls): + def __getnewargs__(self): + return (self.value, self.mark) + return __getnewargs__ + + +class MarkedUnicode(unicode): + __new__ = gen_new(unicode) + __getnewargs__ = gen_getnewargs(unicode) + + def _proc_partition(self, part_result): + pointdiff = 1 + r = [] + for s in part_result: + r.append(MarkedUnicode(s, self.mark.advance_string(pointdiff))) + pointdiff += len(s) + return tuple(r) + + def rpartition(self, sep): + return self._proc_partition(super(MarkedUnicode, self).rpartition(sep)) + + def partition(self, sep): + return self._proc_partition(super(MarkedUnicode, self).partition(sep)) + + +class MarkedInt(int): + __new__ = gen_new(int) + __getnewargs__ = gen_getnewargs(int) + + +class MarkedFloat(float): + __new__ = gen_new(float) + __getnewargs__ = gen_getnewargs(float) + + +class MarkedDict(dict): + __init__ = gen_init(dict) + __getnewargs__ = gen_getnewargs(dict) + + def __new__(arg_cls, value, mark): + r = super(arg_cls, arg_cls).__new__(arg_cls, value) + r.mark = mark + r.value = value + r.keydict = dict(((key, key) for key in r)) + return r + + def setmerged(self, d): + try: + self.mark.set_merged_mark(d.mark) + except AttributeError: + pass + + def __setitem__(self, key, value): + try: + old_value = self[key] + except KeyError: + pass + else: + try: + key.mark.set_old_mark(self.keydict[key].mark) + except AttributeError: + pass + except KeyError: + pass + try: + value.mark.set_old_mark(old_value.mark) + except AttributeError: + pass + dict.__setitem__(self, key, value) + self.keydict[key] = key + + def update(self, *args, **kwargs): + dict.update(self, *args, **kwargs) + self.keydict = dict(((key, key) for key in self)) + + def copy(self): + return MarkedDict(super(MarkedDict, self).copy(), self.mark) + + +class MarkedList(list): + __new__ = gen_new(list) + __init__ = gen_init(list) + __getnewargs__ = gen_getnewargs(list) + + +class MarkedValue: + def __init__(self, value, mark): + self.mark = mark + self.value = value + + __getinitargs__ = gen_getnewargs(None) + + +specialclasses = { + unicode: MarkedUnicode, + int: MarkedInt, + float: MarkedFloat, + dict: MarkedDict, + list: MarkedList, +} + +classcache = {} + + +def gen_marked_value(value, mark, use_special_classes=True): + if use_special_classes and value.__class__ in specialclasses: + Marked = specialclasses[value.__class__] + elif value.__class__ in classcache: + Marked = classcache[value.__class__] + else: + class Marked(MarkedValue): + for func in value.__class__.__dict__: + if func == 'copy': + def copy(self): + return self.__class__(self.value.copy(), self.mark) + elif func not in set(('__init__', '__new__', '__getattribute__')): + if func in set(('__eq__',)): + # HACK to make marked dictionaries always work + exec (( + 'def {0}(self, *args):\n' + ' return self.value.{0}(*[arg.value if isinstance(arg, MarkedValue) else arg for arg in args])' + ).format(func)) + else: + exec (( + 'def {0}(self, *args, **kwargs):\n' + ' return self.value.{0}(*args, **kwargs)\n' + ).format(func)) + classcache[value.__class__] = Marked + + return Marked(value, mark) diff --git a/powerline-bin/powerline/lint/markedjson/nodes.py b/powerline-bin/powerline/lint/markedjson/nodes.py new file mode 100644 index 0000000..66ad843 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/nodes.py @@ -0,0 +1,55 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + +class Node(object): + def __init__(self, tag, value, start_mark, end_mark): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + + def __repr__(self): + value = self.value + # if isinstance(value, list): + # if len(value) == 0: + # value = '' + # elif len(value) == 1: + # value = '<1 item>' + # else: + # value = '<%d items>' % len(value) + # else: + # if len(value) > 75: + # value = repr(value[:70]+u' ... ') + # else: + # value = repr(value) + value = repr(value) + return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) + + +class ScalarNode(Node): + id = 'scalar' + + def __init__(self, tag, value, start_mark=None, end_mark=None, style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + + +class CollectionNode(Node): + def __init__(self, tag, value, start_mark=None, end_mark=None, flow_style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + + +class SequenceNode(CollectionNode): + id = 'sequence' + + +class MappingNode(CollectionNode): + id = 'mapping' diff --git a/powerline-bin/powerline/lint/markedjson/parser.py b/powerline-bin/powerline/lint/markedjson/parser.py new file mode 100644 index 0000000..336a2a2 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/parser.py @@ -0,0 +1,255 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lint.markedjson.error import MarkedError +from powerline.lint.markedjson import tokens +from powerline.lint.markedjson import events + + +class ParserError(MarkedError): + pass + + +class Parser: + def __init__(self): + self.current_event = None + self.yaml_version = None + self.states = [] + self.marks = [] + self.state = self.parse_stream_start + + def dispose(self): + # Reset the state attributes (to clear self-references) + self.states = [] + self.state = None + + def check_event(self, *choices): + # Check the type of the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + if self.current_event is not None: + if not choices: + return True + for choice in choices: + if isinstance(self.current_event, choice): + return True + return False + + def peek_event(self): + # Get the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + return self.current_event + + def get_event(self): + # Get the next event and proceed further. + if self.current_event is None: + if self.state: + self.current_event = self.state() + value = self.current_event + self.current_event = None + return value + + # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END + # implicit_document ::= block_node DOCUMENT-END* + # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + + def parse_stream_start(self): + # Parse the stream start. + token = self.get_token() + event = events.StreamStartEvent(token.start_mark, token.end_mark, encoding=token.encoding) + + # Prepare the next state. + self.state = self.parse_implicit_document_start + + return event + + def parse_implicit_document_start(self): + # Parse an implicit document. + if not self.check_token(tokens.StreamEndToken): + token = self.peek_token() + start_mark = end_mark = token.start_mark + event = events.DocumentStartEvent(start_mark, end_mark, explicit=False) + + # Prepare the next state. + self.states.append(self.parse_document_end) + self.state = self.parse_node + + return event + + else: + return self.parse_document_start() + + def parse_document_start(self): + # Parse an explicit document. + if not self.check_token(tokens.StreamEndToken): + token = self.peek_token() + self.echoerr( + None, None, + ('expected \'\', but found %r' % token.id), token.start_mark + ) + return events.StreamEndEvent(token.start_mark, token.end_mark) + else: + # Parse the end of the stream. + token = self.get_token() + event = events.StreamEndEvent(token.start_mark, token.end_mark) + assert not self.states + assert not self.marks + self.state = None + return event + + def parse_document_end(self): + # Parse the document end. + token = self.peek_token() + start_mark = end_mark = token.start_mark + explicit = False + event = events.DocumentEndEvent(start_mark, end_mark, explicit=explicit) + + # Prepare the next state. + self.state = self.parse_document_start + + return event + + def parse_document_content(self): + return self.parse_node() + + def parse_node(self, indentless_sequence=False): + start_mark = end_mark = None + if start_mark is None: + start_mark = end_mark = self.peek_token().start_mark + event = None + implicit = True + if self.check_token(tokens.ScalarToken): + token = self.get_token() + end_mark = token.end_mark + if token.plain: + implicit = (True, False) + else: + implicit = (False, True) + event = events.ScalarEvent(implicit, token.value, start_mark, end_mark, style=token.style) + self.state = self.states.pop() + elif self.check_token(tokens.FlowSequenceStartToken): + end_mark = self.peek_token().end_mark + event = events.SequenceStartEvent(implicit, start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_sequence_first_entry + elif self.check_token(tokens.FlowMappingStartToken): + end_mark = self.peek_token().end_mark + event = events.MappingStartEvent(implicit, start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_mapping_first_key + else: + token = self.peek_token() + raise ParserError( + 'while parsing a flow node', start_mark, + 'expected the node content, but found %r' % token.id, + token.start_mark + ) + return event + + def parse_flow_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_sequence_entry(first=True) + + def parse_flow_sequence_entry(self, first=False): + if not self.check_token(tokens.FlowSequenceEndToken): + if not first: + if self.check_token(tokens.FlowEntryToken): + self.get_token() + if self.check_token(tokens.FlowSequenceEndToken): + token = self.peek_token() + self.echoerr( + 'While parsing a flow sequence', self.marks[-1], + ('expected sequence value, but got %r' % token.id), token.start_mark + ) + else: + token = self.peek_token() + raise ParserError( + 'while parsing a flow sequence', self.marks[-1], + ('expected \',\' or \']\', but got %r' % token.id), token.start_mark + ) + + if not self.check_token(tokens.FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry) + return self.parse_node() + token = self.get_token() + event = events.SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_sequence_entry_mapping_end(self): + self.state = self.parse_flow_sequence_entry + token = self.peek_token() + return events.MappingEndEvent(token.start_mark, token.start_mark) + + def parse_flow_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_mapping_key(first=True) + + def parse_flow_mapping_key(self, first=False): + if not self.check_token(tokens.FlowMappingEndToken): + if not first: + if self.check_token(tokens.FlowEntryToken): + self.get_token() + if self.check_token(tokens.FlowMappingEndToken): + token = self.peek_token() + self.echoerr( + 'While parsing a flow mapping', self.marks[-1], + ('expected mapping key, but got %r' % token.id), token.start_mark + ) + else: + token = self.peek_token() + raise ParserError( + 'while parsing a flow mapping', self.marks[-1], + ('expected \',\' or \'}\', but got %r' % token.id), token.start_mark + ) + if self.check_token(tokens.KeyToken): + token = self.get_token() + if not self.check_token(tokens.ValueToken, tokens.FlowEntryToken, tokens.FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_value) + return self.parse_node() + else: + token = self.peek_token() + raise ParserError( + 'while parsing a flow mapping', self.marks[-1], + ('expected value, but got %r' % token.id), token.start_mark + ) + elif not self.check_token(tokens.FlowMappingEndToken): + token = self.peek_token() + expect_key = self.check_token(tokens.ValueToken, tokens.FlowEntryToken) + if not expect_key: + self.get_token() + expect_key = self.check_token(tokens.ValueToken) + + if expect_key: + raise ParserError( + 'while parsing a flow mapping', self.marks[-1], + ('expected string key, but got %r' % token.id), token.start_mark + ) + else: + token = self.peek_token() + raise ParserError( + 'while parsing a flow mapping', self.marks[-1], + ('expected \':\', but got %r' % token.id), token.start_mark + ) + token = self.get_token() + event = events.MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_mapping_value(self): + if self.check_token(tokens.ValueToken): + token = self.get_token() + if not self.check_token(tokens.FlowEntryToken, tokens.FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_key) + return self.parse_node() + + token = self.peek_token() + raise ParserError( + 'while parsing a flow mapping', self.marks[-1], + ('expected mapping value, but got %r' % token.id), token.start_mark + ) diff --git a/powerline-bin/powerline/lint/markedjson/reader.py b/powerline-bin/powerline/lint/markedjson/reader.py new file mode 100644 index 0000000..0ca4516 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/reader.py @@ -0,0 +1,141 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import codecs + +from powerline.lint.markedjson.error import MarkedError, Mark, NON_PRINTABLE_RE +from powerline.lib.unicode import unicode + + +# This module contains abstractions for the input stream. You don’t have to +# looks further, there are no pretty code. + + +class ReaderError(MarkedError): + pass + + +class Reader(object): + # Reader: + # - determines the data encoding and converts it to a unicode string, + # - checks if characters are in allowed range, + # - adds '\0' to the end. + + # Reader accepts + # - a file-like object with its `read` method returning `str`, + + # Yeah, it’s ugly and slow. + def __init__(self, stream): + self.name = None + self.stream = None + self.stream_pointer = 0 + self.eof = True + self.buffer = '' + self.pointer = 0 + self.full_buffer = unicode('') + self.full_pointer = 0 + self.raw_buffer = None + self.raw_decode = codecs.utf_8_decode + self.encoding = 'utf-8' + self.index = 0 + self.line = 0 + self.column = 0 + + self.stream = stream + self.name = getattr(stream, 'name', '') + self.eof = False + self.raw_buffer = None + + while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): + self.update_raw() + self.update(1) + + def peek(self, index=0): + try: + return self.buffer[self.pointer + index] + except IndexError: + self.update(index + 1) + return self.buffer[self.pointer + index] + + def prefix(self, length=1): + if self.pointer + length >= len(self.buffer): + self.update(length) + return self.buffer[self.pointer:self.pointer + length] + + def update_pointer(self, length): + while length: + ch = self.buffer[self.pointer] + self.pointer += 1 + self.full_pointer += 1 + self.index += 1 + if ch == '\n': + self.line += 1 + self.column = 0 + else: + self.column += 1 + length -= 1 + + def forward(self, length=1): + if self.pointer + length + 1 >= len(self.buffer): + self.update(length + 1) + self.update_pointer(length) + + def get_mark(self): + return Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer) + + def check_printable(self, data): + match = NON_PRINTABLE_RE.search(data) + if match: + self.update_pointer(match.start()) + raise ReaderError( + 'while reading from stream', None, + 'found special characters which are not allowed', + Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer) + ) + + def update(self, length): + if self.raw_buffer is None: + return + self.buffer = self.buffer[self.pointer:] + self.pointer = 0 + while len(self.buffer) < length: + if not self.eof: + self.update_raw() + try: + data, converted = self.raw_decode(self.raw_buffer, 'strict', self.eof) + except UnicodeDecodeError as exc: + character = self.raw_buffer[exc.start] + position = self.stream_pointer - len(self.raw_buffer) + exc.start + data, converted = self.raw_decode(self.raw_buffer[:exc.start], 'strict', self.eof) + self.buffer += data + self.full_buffer += data + '<' + str(ord(character)) + '>' + self.raw_buffer = self.raw_buffer[converted:] + self.update_pointer(exc.start - 1) + raise ReaderError( + 'while reading from stream', None, + 'found character #x%04x that cannot be decoded by UTF-8 codec' % ord(character), + Mark(self.name, self.line, self.column, self.full_buffer, position) + ) + self.buffer += data + self.full_buffer += data + self.raw_buffer = self.raw_buffer[converted:] + self.check_printable(data) + if self.eof: + self.buffer += '\0' + self.raw_buffer = None + break + + def update_raw(self, size=-1): + # Was size=4096 + assert(size < 0) + # WARNING: reading the whole stream at once. To change this behaviour to + # former reading N characters at once one must make sure that reading + # never ends at partial unicode character. + data = self.stream.read(size) + if self.raw_buffer is None: + self.raw_buffer = data + else: + self.raw_buffer += data + self.stream_pointer += len(data) + if not data: + self.eof = True diff --git a/powerline-bin/powerline/lint/markedjson/resolver.py b/powerline-bin/powerline/lint/markedjson/resolver.py new file mode 100644 index 0000000..fa8ceaa --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/resolver.py @@ -0,0 +1,131 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re + +from powerline.lint.markedjson.error import MarkedError +from powerline.lint.markedjson import nodes + + +class ResolverError(MarkedError): + pass + + +class BaseResolver: + DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' + DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' + DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' + + yaml_implicit_resolvers = {} + yaml_path_resolvers = {} + + def __init__(self): + self.resolver_exact_paths = [] + self.resolver_prefix_paths = [] + + @classmethod + def add_implicit_resolver(cls, tag, regexp, first): + if 'yaml_implicit_resolvers' not in cls.__dict__: + cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() + if first is None: + first = [None] + for ch in first: + cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) + + def descend_resolver(self, current_node, current_index): + if not self.yaml_path_resolvers: + return + exact_paths = {} + prefix_paths = [] + if current_node: + depth = len(self.resolver_prefix_paths) + for path, kind in self.resolver_prefix_paths[-1]: + if self.check_resolver_prefix(depth, path, kind, current_node, current_index): + if len(path) > depth: + prefix_paths.append((path, kind)) + else: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + for path, kind in self.yaml_path_resolvers: + if not path: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + prefix_paths.append((path, kind)) + self.resolver_exact_paths.append(exact_paths) + self.resolver_prefix_paths.append(prefix_paths) + + def ascend_resolver(self): + if not self.yaml_path_resolvers: + return + self.resolver_exact_paths.pop() + self.resolver_prefix_paths.pop() + + def check_resolver_prefix(self, depth, path, kind, current_node, current_index): + node_check, index_check = path[depth - 1] + if isinstance(node_check, str): + if current_node.tag != node_check: + return + elif node_check is not None: + if not isinstance(current_node, node_check): + return + if index_check is True and current_index is not None: + return + if ((index_check is False or index_check is None) + and current_index is None): + return + if isinstance(index_check, str): + if not (isinstance(current_index, nodes.ScalarNode) and index_check == current_index.value): + return + elif isinstance(index_check, int) and not isinstance(index_check, bool): + if index_check != current_index: + return + return True + + def resolve(self, kind, value, implicit, mark=None): + if kind is nodes.ScalarNode and implicit[0]: + if value == '': + resolvers = self.yaml_implicit_resolvers.get('', []) + else: + resolvers = self.yaml_implicit_resolvers.get(value[0], []) + resolvers += self.yaml_implicit_resolvers.get(None, []) + for tag, regexp in resolvers: + if regexp.match(value): + return tag + else: + self.echoerr( + 'While resolving plain scalar', None, + 'expected floating-point value, integer, null or boolean, but got %r' % value, + mark + ) + return self.DEFAULT_SCALAR_TAG + if kind is nodes.ScalarNode: + return self.DEFAULT_SCALAR_TAG + elif kind is nodes.SequenceNode: + return self.DEFAULT_SEQUENCE_TAG + elif kind is nodes.MappingNode: + return self.DEFAULT_MAPPING_TAG + + +class Resolver(BaseResolver): + pass + + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:bool', + re.compile(r'''^(?:true|false)$''', re.X), + list('yYnNtTfFoO')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:float', + re.compile(r'^-?(?:0|[1-9]\d*)(?=[.eE])(?:\.\d+)?(?:[eE][-+]?\d+)?$', re.X), + list('-0123456789')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:int', + re.compile(r'^(?:0|-?[1-9]\d*)$', re.X), + list('-0123456789')) + +Resolver.add_implicit_resolver( + 'tag:yaml.org,2002:null', + re.compile(r'^null$', re.X), + ['n']) diff --git a/powerline-bin/powerline/lint/markedjson/scanner.py b/powerline-bin/powerline/lint/markedjson/scanner.py new file mode 100644 index 0000000..b0bddf3 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/scanner.py @@ -0,0 +1,499 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from string import hexdigits + +from powerline.lint.markedjson.error import MarkedError +from powerline.lint.markedjson import tokens +from powerline.lib.unicode import unicode, unichr, surrogate_pair_to_character + + +hexdigits_set = set(hexdigits) + + +# Scanner produces tokens of the following types: +# STREAM-START +# STREAM-END +# DOCUMENT-START +# DOCUMENT-END +# FLOW-SEQUENCE-START +# FLOW-MAPPING-START +# FLOW-SEQUENCE-END +# FLOW-MAPPING-END +# FLOW-ENTRY +# KEY +# VALUE +# SCALAR(value, plain, style) +# +# Read comments in the Scanner code for more details. + + +class ScannerError(MarkedError): + pass + + +class SimpleKey: + # See below simple keys treatment. + def __init__(self, token_number, index, line, column, mark): + self.token_number = token_number + self.index = index + self.line = line + self.column = column + self.mark = mark + + +class Scanner: + def __init__(self): + '''Initialize the scanner.''' + # It is assumed that Scanner and Reader will have a common descendant. + # Reader do the dirty work of checking for BOM and converting the + # input data to Unicode. It also adds NUL to the end. + # + # Reader supports the following methods + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. + + # Had we reached the end of the stream? + self.done = False + + # The number of unclosed '{' and '['. `flow_level == 0` means block + # context. + self.flow_level = 0 + + # List of processed tokens that are not yet emitted. + self.tokens = [] + + # Add the STREAM-START token. + self.fetch_stream_start() + + # Number of tokens that were emitted through the `get_token` method. + self.tokens_taken = 0 + + # Variables related to simple keys treatment. + + # A simple key is a key that is not denoted by the '?' indicator. + # We emit the KEY token before all keys, so when we find a potential + # simple key, we try to locate the corresponding ':' indicator. + # Simple keys should be limited to a single line. + + # Can a simple key start at the current position? A simple key may + # start: + # - after '{', '[', ',' (in the flow context), + self.allow_simple_key = False + + # Keep track of possible simple keys. This is a dictionary. The key + # is `flow_level`; there can be no more that one possible simple key + # for each level. The value is a SimpleKey record: + # (token_number, index, line, column, mark) + # A simple key may start with SCALAR(flow), '[', or '{' tokens. + self.possible_simple_keys = {} + + # Public methods. + + def check_token(self, *choices): + # Check if the next token is one of the given types. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + if not choices: + return True + for choice in choices: + if isinstance(self.tokens[0], choice): + return True + return False + + def peek_token(self): + # Return the next token, but do not delete if from the queue. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + return self.tokens[0] + + def get_token(self): + # Return the next token. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + self.tokens_taken += 1 + return self.tokens.pop(0) + + # Private methods. + + def need_more_tokens(self): + if self.done: + return False + if not self.tokens: + return True + # The current token may be a potential simple key, so we + # need to look further. + self.stale_possible_simple_keys() + if self.next_possible_simple_key() == self.tokens_taken: + return True + + def fetch_more_tokens(self): + + # Eat whitespaces and comments until we reach the next token. + self.scan_to_next_token() + + # Remove obsolete possible simple keys. + self.stale_possible_simple_keys() + + # Peek the next character. + ch = self.peek() + + # Is it the end of stream? + if ch == '\0': + return self.fetch_stream_end() + + # Note: the order of the following checks is NOT significant. + + # Is it the flow sequence start indicator? + if ch == '[': + return self.fetch_flow_sequence_start() + + # Is it the flow mapping start indicator? + if ch == '{': + return self.fetch_flow_mapping_start() + + # Is it the flow sequence end indicator? + if ch == ']': + return self.fetch_flow_sequence_end() + + # Is it the flow mapping end indicator? + if ch == '}': + return self.fetch_flow_mapping_end() + + # Is it the flow entry indicator? + if ch == ',': + return self.fetch_flow_entry() + + # Is it the value indicator? + if ch == ':' and self.flow_level: + return self.fetch_value() + + # Is it a double quoted scalar? + if ch == '"': + return self.fetch_double() + + # It must be a plain scalar then. + if self.check_plain(): + return self.fetch_plain() + + # No? It’s an error. Let’s produce a nice error message. + raise ScannerError( + 'while scanning for the next token', None, + 'found character %r that cannot start any token' % ch, + self.get_mark() + ) + + # Simple keys treatment. + + def next_possible_simple_key(self): + # Return the number of the nearest possible simple key. Actually we + # don’t need to loop through the whole dictionary. We may replace it + # with the following code: + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number + min_token_number = None + for level in self.possible_simple_keys: + key = self.possible_simple_keys[level] + if min_token_number is None or key.token_number < min_token_number: + min_token_number = key.token_number + return min_token_number + + def stale_possible_simple_keys(self): + # Remove entries that are no longer possible simple keys. According to + # the YAML specification, simple keys + # - should be limited to a single line, + # Disabling this procedure will allow simple keys of any length and + # height (may cause problems if indentation is broken though). + for level in list(self.possible_simple_keys): + key = self.possible_simple_keys[level] + if key.line != self.line: + del self.possible_simple_keys[level] + + def save_possible_simple_key(self): + # The next token may start a simple key. We check if it’s possible + # and save its position. This function is called for + # SCALAR(flow), '[', and '{'. + + # The next token might be a simple key. Let’s save it’s number and + # position. + if self.allow_simple_key: + self.remove_possible_simple_key() + token_number = self.tokens_taken + len(self.tokens) + key = SimpleKey(token_number, self.index, self.line, self.column, self.get_mark()) + self.possible_simple_keys[self.flow_level] = key + + def remove_possible_simple_key(self): + # Remove the saved possible key position at the current flow level. + if self.flow_level in self.possible_simple_keys: + del self.possible_simple_keys[self.flow_level] + + # Fetchers. + + def fetch_stream_start(self): + # We always add STREAM-START as the first token and STREAM-END as the + # last token. + + # Read the token. + mark = self.get_mark() + + # Add STREAM-START. + self.tokens.append(tokens.StreamStartToken(mark, mark, encoding=self.encoding)) + + def fetch_stream_end(self): + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + self.possible_simple_keys = {} + + # Read the token. + mark = self.get_mark() + + # Add STREAM-END. + self.tokens.append(tokens.StreamEndToken(mark, mark)) + + # The steam is finished. + self.done = True + + def fetch_flow_sequence_start(self): + self.fetch_flow_collection_start(tokens.FlowSequenceStartToken) + + def fetch_flow_mapping_start(self): + self.fetch_flow_collection_start(tokens.FlowMappingStartToken) + + def fetch_flow_collection_start(self, TokenClass): + # '[' and '{' may start a simple key. + self.save_possible_simple_key() + + # Increase the flow level. + self.flow_level += 1 + + # Simple keys are allowed after '[' and '{'. + self.allow_simple_key = True + + # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_end(self): + self.fetch_flow_collection_end(tokens.FlowSequenceEndToken) + + def fetch_flow_mapping_end(self): + self.fetch_flow_collection_end(tokens.FlowMappingEndToken) + + def fetch_flow_collection_end(self, TokenClass): + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Decrease the flow level. + self.flow_level -= 1 + + # No simple keys after ']' or '}'. + self.allow_simple_key = False + + # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_value(self): + # Do we determine a simple key? + if self.flow_level in self.possible_simple_keys: + + # Add KEY. + key = self.possible_simple_keys[self.flow_level] + del self.possible_simple_keys[self.flow_level] + self.tokens.insert(key.token_number - self.tokens_taken, tokens.KeyToken(key.mark, key.mark)) + + # There cannot be two simple keys one after another. + self.allow_simple_key = False + + # Add VALUE. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(tokens.ValueToken(start_mark, end_mark)) + + def fetch_flow_entry(self): + # Simple keys are allowed after ','. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add FLOW-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(tokens.FlowEntryToken(start_mark, end_mark)) + + def fetch_double(self): + # A flow scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after flow scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. + self.tokens.append(self.scan_flow_scalar()) + + def fetch_plain(self): + + self.save_possible_simple_key() + + # No simple keys after plain scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. May change `allow_simple_key`. + self.tokens.append(self.scan_plain()) + + # Checkers. + + def check_plain(self): + return self.peek() in '0123456789-ntf' + + # Scanners. + + def scan_to_next_token(self): + while self.peek() in ' \t\n': + self.forward() + + def scan_flow_scalar(self): + # See the specification for details. + # Note that we loose indentation rules for quoted scalars. Quoted + # scalars don’t need to adhere indentation because " and ' clearly + # mark the beginning and the end of them. Therefore we are less + # restrictive then the specification requires. We only need to check + # that document separators are not included in scalars. + chunks = [] + start_mark = self.get_mark() + quote = self.peek() + self.forward() + chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) + while self.peek() != quote: + chunks.extend(self.scan_flow_scalar_spaces(start_mark)) + chunks.extend(self.scan_flow_scalar_non_spaces(start_mark)) + self.forward() + end_mark = self.get_mark() + return tokens.ScalarToken(unicode().join(chunks), False, start_mark, end_mark, '"') + + ESCAPE_REPLACEMENTS = { + 'b': '\x08', + 't': '\x09', + 'n': '\x0A', + 'f': '\x0C', + 'r': '\x0D', + '"': '\"', + '\\': '\\', + } + + ESCAPE_CODES = { + 'u': 4, + } + + def scan_flow_scalar_non_spaces(self, start_mark): + # See the specification for details. + chunks = [] + while True: + length = 0 + while self.peek(length) not in '\"\\\0 \t\n': + length += 1 + if length: + chunks.append(self.prefix(length)) + self.forward(length) + ch = self.peek() + if ch == '\\': + self.forward() + ch = self.peek() + if ch in self.ESCAPE_REPLACEMENTS: + chunks.append(self.ESCAPE_REPLACEMENTS[ch]) + self.forward() + elif ch in self.ESCAPE_CODES: + length = self.ESCAPE_CODES[ch] + self.forward() + for k in range(length): + if self.peek(k) not in hexdigits: + raise ScannerError( + 'while scanning a double-quoted scalar', start_mark, + 'expected escape sequence of %d hexdecimal numbers, but found %r' % ( + length, self.peek(k)), + self.get_mark() + ) + code = int(self.prefix(length), 16) + self.forward(length) + if 0xD800 <= code <= 0xDC00: + # Start of the surrogate pair + next_char = self.prefix(6) + if ( + next_char[0] != '\\' + or next_char[1] != 'u' + or not (set(next_char[2:]) < hexdigits_set) + or not (0xDC00 <= int(next_char[2:], 16) <= 0xDFFF) + ): + raise ScannerError( + 'while scanning a double-quoted scalar', start_mark, + 'expected escape sequence with the next character in surrogate pair, but found %r' % ( + next_char + ), + self.get_mark() + ) + code = surrogate_pair_to_character(code, int(next_char[2:], 16)) + self.forward(6) + chunks.append(unichr(code)) + else: + raise ScannerError( + 'while scanning a double-quoted scalar', start_mark, + ('found unknown escape character %r' % ch), self.get_mark() + ) + else: + return chunks + + def scan_flow_scalar_spaces(self, start_mark): + # See the specification for details. + chunks = [] + length = 0 + while self.peek(length) in ' \t': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch == '\0': + raise ScannerError( + 'while scanning a quoted scalar', start_mark, + 'found unexpected end of stream', self.get_mark() + ) + elif ch == '\n': + raise ScannerError( + 'while scanning a quoted scalar', start_mark, + 'found unexpected line end', self.get_mark() + ) + else: + chunks.append(whitespaces) + return chunks + + def scan_plain(self): + chunks = [] + start_mark = self.get_mark() + spaces = [] + while True: + length = 0 + while True: + if self.peek(length) not in 'eE.0123456789nul-tr+fas': + break + length += 1 + if length == 0: + break + self.allow_simple_key = False + chunks.extend(spaces) + chunks.append(self.prefix(length)) + self.forward(length) + end_mark = self.get_mark() + return tokens.ScalarToken(''.join(chunks), True, start_mark, end_mark) diff --git a/powerline-bin/powerline/lint/markedjson/tokens.py b/powerline-bin/powerline/lint/markedjson/tokens.py new file mode 100644 index 0000000..6fa8bf1 --- /dev/null +++ b/powerline-bin/powerline/lint/markedjson/tokens.py @@ -0,0 +1,72 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + + +class Token(object): + def __init__(self, start_mark, end_mark): + self.start_mark = start_mark + self.end_mark = end_mark + + def __repr__(self): + attributes = [ + key for key in self.__dict__ + if not key.endswith('_mark') + ] + attributes.sort() + arguments = ', '.join([ + '%s=%r' % (key, getattr(self, key)) + for key in attributes + ]) + return '%s(%s)' % (self.__class__.__name__, arguments) + + +class StreamStartToken(Token): + id = '' + + def __init__(self, start_mark=None, end_mark=None, encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + + +class StreamEndToken(Token): + id = '' + + +class FlowSequenceStartToken(Token): + id = '[' + + +class FlowMappingStartToken(Token): + id = '{' + + +class FlowSequenceEndToken(Token): + id = ']' + + +class FlowMappingEndToken(Token): + id = '}' + + +class KeyToken(Token): + id = '?' + + +class ValueToken(Token): + id = ':' + + +class FlowEntryToken(Token): + id = ',' + + +class ScalarToken(Token): + id = '' + + def __init__(self, value, plain, start_mark, end_mark, style=None): + self.value = value + self.plain = plain + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style diff --git a/powerline-bin/powerline/lint/selfcheck.py b/powerline-bin/powerline/lint/selfcheck.py new file mode 100644 index 0000000..06d1fbe --- /dev/null +++ b/powerline-bin/powerline/lint/selfcheck.py @@ -0,0 +1,16 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lib.unicode import unicode + + +def havemarks(*args, **kwargs): + origin = kwargs.get('origin', '') + for i, v in enumerate(args): + if not hasattr(v, 'mark'): + raise AssertionError('Value #{0}/{1} ({2!r}) has no attribute `mark`'.format(origin, i, v)) + if isinstance(v, dict): + for key, val in v.items(): + havemarks(key, val, origin=(origin + '[' + unicode(i) + ']/' + unicode(key))) + elif isinstance(v, list): + havemarks(*v, origin=(origin + '[' + unicode(i) + ']')) diff --git a/powerline-bin/powerline/lint/spec.py b/powerline-bin/powerline/lint/spec.py new file mode 100644 index 0000000..f7b9155 --- /dev/null +++ b/powerline-bin/powerline/lint/spec.py @@ -0,0 +1,759 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import itertools +import re + +from copy import copy + +from powerline.lib.unicode import unicode +from powerline.lint.markedjson.error import echoerr, DelayedEchoErr, NON_PRINTABLE_STR +from powerline.lint.selfcheck import havemarks + + +NON_PRINTABLE_RE = re.compile( + NON_PRINTABLE_STR.translate({ + ord('\t'): None, + ord('\n'): None, + 0x0085: None, + }) +) + + +class Spec(object): + '''Class that describes some JSON value + + In powerline it is only used to describe JSON values stored in powerline + configuration. + + :param dict keys: + Dictionary that maps keys that may be present in the given JSON + dictionary to their descriptions. If this parameter is not empty it + implies that described value has dictionary type. Non-dictionary types + must be described using ``Spec()``: without arguments. + + .. note:: + Methods that create the specifications return ``self``, so calls to them + may be chained: ``Spec().type(unicode).re('^\w+$')``. This does not + apply to functions that *apply* specification like :py:meth`Spec.match`. + + .. note:: + Methods starting with ``check_`` return two values: first determines + whether caller should proceed on running other checks, second + determines whether there were any problems (i.e. whether error was + reported). One should not call these methods directly: there is + :py:meth:`Spec.match` method for checking values. + + .. note:: + In ``check_`` and ``match`` methods specifications are identified by + their indexes for the purpose of simplyfying :py:meth:`Spec.copy` + method. + + Some common parameters: + + ``data``: + Whatever data supplied by the first caller for checker functions. Is not + processed by :py:class:`Spec` methods in any fashion. + ``context``: + :py:class:`powerline.lint.context.Context` instance, describes context + of the value. :py:class:`Spec` methods only use its ``.key`` methods for + error messages. + ``echoerr``: + Callable that should be used to echo errors. Is supposed to take four + optional keyword arguments: ``problem``, ``problem_mark``, ``context``, + ``context_mark``. + ``value``: + Checked value. + ''' + + def __init__(self, **keys): + self.specs = [] + self.keys = {} + self.checks = [] + self.cmsg = '' + self.isoptional = False + self.uspecs = [] + self.ufailmsg = lambda key: 'found unknown key: {0}'.format(key) + self.did_type = False + self.update(**keys) + + def update(self, **keys): + '''Describe additional keys that may be present in given JSON value + + If called with some keyword arguments implies that described value is + a dictionary. If called without keyword parameters it is no-op. + + :return: self. + ''' + for k, v in keys.items(): + self.keys[k] = len(self.specs) + self.specs.append(v) + if self.keys and not self.did_type: + self.type(dict) + self.did_type = True + return self + + def copy(self, copied=None): + '''Deep copy the spec + + :param dict copied: + Internal dictionary used for storing already copied values. This + parameter should not be used. + + :return: New :py:class:`Spec` object that is a deep copy of ``self``. + ''' + copied = copied or {} + try: + return copied[id(self)] + except KeyError: + instance = self.__class__() + copied[id(self)] = instance + return self.__class__()._update(self.__dict__, copied) + + def _update(self, d, copied): + '''Helper for the :py:meth:`Spec.copy` function + + Populates new instance with values taken from the old one. + + :param dict d: + ``__dict__`` of the old instance. + :param dict copied: + Storage for already copied values. + ''' + self.__dict__.update(d) + self.keys = copy(self.keys) + self.checks = copy(self.checks) + self.uspecs = copy(self.uspecs) + self.specs = [spec.copy(copied) for spec in self.specs] + return self + + def unknown_spec(self, keyfunc, spec): + '''Define specification for non-static keys + + This method should be used if key names cannot be determined at runtime + or if a number of keys share identical spec (in order to not repeat it). + :py:meth:`Spec.match` method processes dictionary in the given order: + + * First it tries to use specifications provided at the initialization or + by the :py:meth:`Spec.update` method. + * If no specification for given key was provided it processes + specifications from ``keyfunc`` argument in order they were supplied. + Once some key matches specification supplied second ``spec`` argument + is used to determine correctness of the value. + + :param Spec keyfunc: + :py:class:`Spec` instance or a regular function that returns two + values (the same :py:meth:`Spec.match` returns). This argument is + used to match keys that were not provided at initialization or via + :py:meth:`Spec.update`. + :param Spec spec: + :py:class:`Spec` instance that will be used to check keys matched by + ``keyfunc``. + + :return: self. + ''' + if isinstance(keyfunc, Spec): + self.specs.append(keyfunc) + keyfunc = len(self.specs) - 1 + self.specs.append(spec) + self.uspecs.append((keyfunc, len(self.specs) - 1)) + return self + + def unknown_msg(self, msgfunc): + '''Define message which will be used when unknown key was found + + “Unknown” is a key that was not provided at the initialization and via + :py:meth:`Spec.update` and did not match any ``keyfunc`` proided via + :py:meth:`Spec.unknown_spec`. + + :param msgfunc: + Function that takes that unknown key as an argument and returns the + message text. Text will appear at the top (start of the sentence). + + :return: self. + ''' + self.ufailmsg = msgfunc + return self + + def context_message(self, msg): + '''Define message that describes context + + :param str msg: + Message that describes context. Is written using the + :py:meth:`str.format` syntax and is expected to display keyword + parameter ``key``. + + :return: self. + ''' + self.cmsg = msg + for spec in self.specs: + if not spec.cmsg: + spec.context_message(msg) + return self + + def check_type(self, value, context_mark, data, context, echoerr, types): + '''Check that given value matches given type(s) + + :param tuple types: + List of accepted types. Since :py:class:`Spec` is supposed to + describe JSON values only ``dict``, ``list``, ``unicode``, ``bool``, + ``float`` and ``NoneType`` types make any sense. + + :return: proceed, hadproblem. + ''' + havemarks(value) + if type(value.value) not in types: + echoerr( + context=self.cmsg.format(key=context.key), + context_mark=context_mark, + problem='{0!r} must be a {1} instance, not {2}'.format( + value, + ', '.join((t.__name__ for t in types)), + type(value.value).__name__ + ), + problem_mark=value.mark + ) + return False, True + return True, False + + def check_func(self, value, context_mark, data, context, echoerr, func, msg_func): + '''Check value using given function + + :param function func: + Callable that should accept four positional parameters: + + #. checked value, + #. ``data`` parameter with arbitrary data (supplied by top-level + caller), + #. current context and + #. function used for echoing errors. + + This callable should return three values: + + #. determines whether ``check_func`` caller should proceed + calling other checks, + #. determines whether ``check_func`` should echo error on its own + (it should be set to False if ``func`` echoes error itself) and + #. determines whether function has found some errors in the checked + value. + + :param function msg_func: + Callable that takes checked value as the only positional parameter + and returns a string that describes the problem. Only useful for + small checker functions since it is ignored when second returned + value is false. + + :return: proceed, hadproblem. + ''' + havemarks(value) + proceed, echo, hadproblem = func(value, data, context, echoerr) + if echo and hadproblem: + echoerr(context=self.cmsg.format(key=context.key), + context_mark=context_mark, + problem=msg_func(value), + problem_mark=value.mark) + return proceed, hadproblem + + def check_list(self, value, context_mark, data, context, echoerr, item_func, msg_func): + '''Check that each value in the list matches given specification + + :param function item_func: + Callable like ``func`` from :py:meth:`Spec.check_func`. Unlike + ``func`` this callable is called for each value in the list and may + be a :py:class:`Spec` object index. + :param func msg_func: + Callable like ``msg_func`` from :py:meth:`Spec.check_func`. Should + accept one problematic item and is not used for :py:class:`Spec` + object indicies in ``item_func`` method. + + :return: proceed, hadproblem. + ''' + havemarks(value) + i = 0 + hadproblem = False + for item in value: + havemarks(item) + if isinstance(item_func, int): + spec = self.specs[item_func] + proceed, fhadproblem = spec.match( + item, + value.mark, + data, + context.enter_item('list item ' + unicode(i), item), + echoerr + ) + else: + proceed, echo, fhadproblem = item_func(item, data, context, echoerr) + if echo and fhadproblem: + echoerr(context=self.cmsg.format(key=context.key + '/list item ' + unicode(i)), + context_mark=value.mark, + problem=msg_func(item), + problem_mark=item.mark) + if fhadproblem: + hadproblem = True + if not proceed: + return proceed, hadproblem + i += 1 + return True, hadproblem + + def check_either(self, value, context_mark, data, context, echoerr, start, end): + '''Check that given value matches one of the given specifications + + :param int start: + First specification index. + :param int end: + Specification index that is greater by 1 then last specification + index. + + This method does not give an error if any specification from + ``self.specs[start:end]`` is matched by the given value. + ''' + havemarks(value) + new_echoerr = DelayedEchoErr( + echoerr, + 'One of the either variants failed. Messages from the first variant:', + 'messages from the next variant:' + ) + + hadproblem = False + for spec in self.specs[start:end]: + proceed, hadproblem = spec.match(value, value.mark, data, context, new_echoerr) + new_echoerr.next_variant() + if not proceed: + break + if not hadproblem: + return True, False + + new_echoerr.echo_all() + + return False, hadproblem + + def check_tuple(self, value, context_mark, data, context, echoerr, start, end): + '''Check that given value is a list with items matching specifications + + :param int start: + First specification index. + :param int end: + Specification index that is greater by 1 then last specification + index. + + This method checks that each item in the value list matches + specification with index ``start + item_number``. + ''' + havemarks(value) + hadproblem = False + for (i, item, spec) in zip(itertools.count(), value, self.specs[start:end]): + proceed, ihadproblem = spec.match( + item, + value.mark, + data, + context.enter_item('tuple item ' + unicode(i), item), + echoerr + ) + if ihadproblem: + hadproblem = True + if not proceed: + return False, hadproblem + return True, hadproblem + + def check_printable(self, value, context_mark, data, context, echoerr, _): + '''Check that given unicode string contains only printable characters + ''' + hadproblem = False + for match in NON_PRINTABLE_RE.finditer(value): + hadproblem = True + echoerr( + context=self.cmsg.format(key=context.key), + context_mark=value.mark, + problem='found not printable character U+{0:04x} in a configuration string'.format( + ord(match.group(0))), + problem_mark=value.mark.advance_string(match.start() + 1) + ) + return True, hadproblem + + def printable(self, *args): + self.type(unicode) + self.checks.append(('check_printable', args)) + return self + + def type(self, *args): + '''Describe value that has one of the types given in arguments + + :param args: + List of accepted types. Since :py:class:`Spec` is supposed to + describe JSON values only ``dict``, ``list``, ``unicode``, ``bool``, + ``float`` and ``NoneType`` types make any sense. + + :return: self. + ''' + self.checks.append(('check_type', args)) + return self + + cmp_funcs = { + 'le': lambda x, y: x <= y, + 'lt': lambda x, y: x < y, + 'ge': lambda x, y: x >= y, + 'gt': lambda x, y: x > y, + 'eq': lambda x, y: x == y, + } + + cmp_msgs = { + 'le': 'lesser or equal to', + 'lt': 'lesser then', + 'ge': 'greater or equal to', + 'gt': 'greater then', + 'eq': 'equal to', + } + + def len(self, comparison, cint, msg_func=None): + '''Describe value that has given length + + :param str comparison: + Type of the comparison. Valid values: ``le``, ``lt``, ``ge``, + ``gt``, ``eq``. + :param int cint: + Integer with which length is compared. + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. Default value will emit + something like “length of ['foo', 'bar'] is not greater then 10”. + + :return: self. + ''' + cmp_func = self.cmp_funcs[comparison] + msg_func = ( + msg_func + or (lambda value: 'length of {0!r} is not {1} {2}'.format( + value, self.cmp_msgs[comparison], cint)) + ) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, not cmp_func(len(value), cint))), + msg_func + )) + return self + + def cmp(self, comparison, cint, msg_func=None): + '''Describe value that is a number or string that has given property + + :param str comparison: + Type of the comparison. Valid values: ``le``, ``lt``, ``ge``, + ``gt``, ``eq``. This argument will restrict the number or string to + emit True on the given comparison. + :param cint: + Number or string with which value is compared. Type of this + parameter affects required type of the checked value: ``str`` and + ``unicode`` types imply ``unicode`` values, ``float`` type implies + that value can be either ``int`` or ``float``, ``int`` type implies + ``int`` value and for any other type the behavior is undefined. + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. Default value will emit + something like “10 is not greater then 10”. + + :return: self. + ''' + if type(cint) is str: + self.type(unicode) + elif type(cint) is float: + self.type(int, float) + else: + self.type(type(cint)) + cmp_func = self.cmp_funcs[comparison] + msg_func = msg_func or (lambda value: '{0} is not {1} {2}'.format(value, self.cmp_msgs[comparison], cint)) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, not cmp_func(value.value, cint))), + msg_func + )) + return self + + def unsigned(self, msg_func=None): + '''Describe unsigned integer value + + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. + + :return: self. + ''' + self.type(int) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, value < 0)), + (lambda value: '{0} must be greater then zero'.format(value)) + )) + return self + + def list(self, item_func, msg_func=None): + '''Describe list with any number of elements, each matching given spec + + :param item_func: + :py:class:`Spec` instance or a callable. Check out + :py:meth:`Spec.check_list` documentation for more details. Note that + in :py:meth:`Spec.check_list` description :py:class:`Spec` instance + is replaced with its index in ``self.specs``. + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. Default value will emit just + “failed check”, which is rather indescriptive. + + :return: self. + ''' + self.type(list) + if isinstance(item_func, Spec): + self.specs.append(item_func) + item_func = len(self.specs) - 1 + self.checks.append(('check_list', item_func, msg_func or (lambda item: 'failed check'))) + return self + + def tuple(self, *specs): + '''Describe list with the given number of elements, each matching corresponding spec + + :param (Spec,) specs: + List of specifications. Last element(s) in this list may be + optional. Each element in this list describes element with the same + index in the checked value. Check out :py:meth:`Spec.check_tuple` + for more details, but note that there list of specifications is + replaced with start and end indicies in ``self.specs``. + + :return: self. + ''' + self.type(list) + + max_len = len(specs) + min_len = max_len + for spec in reversed(specs): + if spec.isoptional: + min_len -= 1 + else: + break + if max_len == min_len: + self.len('eq', len(specs)) + else: + if min_len > 0: + self.len('ge', min_len) + self.len('le', max_len) + + start = len(self.specs) + for i, spec in zip(itertools.count(), specs): + self.specs.append(spec) + self.checks.append(('check_tuple', start, len(self.specs))) + return self + + def func(self, func, msg_func=None): + '''Describe value that is checked by the given function + + Check out :py:meth:`Spec.check_func` documentation for more details. + ''' + self.checks.append(('check_func', func, msg_func or (lambda value: 'failed check'))) + return self + + def re(self, regex, msg_func=None): + '''Describe value that is a string that matches given regular expression + + :param str regex: + Regular expression that should be matched by the value. + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. Default value will emit + something like “String "xyz" does not match "[a-f]+"”. + + :return: self. + ''' + self.type(unicode) + compiled = re.compile(regex) + msg_func = msg_func or (lambda value: 'String "{0}" does not match "{1}"'.format(value, regex)) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, not compiled.match(value.value))), + msg_func + )) + return self + + def ident(self, msg_func=None): + '''Describe value that is an identifier like ``foo:bar`` or ``foo`` + + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. Default value will emit + something like “String "xyz" is not an … identifier”. + + :return: self. + ''' + msg_func = ( + msg_func + or (lambda value: 'String "{0}" is not an alphanumeric/underscore colon-separated identifier'.format(value)) + ) + return self.re('^\w+(?::\w+)?$', msg_func) + + def oneof(self, collection, msg_func=None): + '''Describe value that is equal to one of the value in the collection + + :param set collection: + A collection of possible values. + :param function msg_func: + Function that should accept checked value and return message that + describes the problem with this value. Default value will emit + something like “"xyz" must be one of {'abc', 'def', 'ghi'}”. + + :return: self. + ''' + msg_func = msg_func or (lambda value: '"{0}" must be one of {1!r}'.format(value, list(collection))) + self.checks.append(( + 'check_func', + (lambda value, *args: (True, True, value not in collection)), + msg_func + )) + return self + + def error(self, msg): + '''Describe value that must not be there + + Useful for giving more descriptive errors for some specific keys then + just “found unknown key: shutdown_event” or for forbidding certain + values when :py:meth:`Spec.unknown_spec` was used. + + :param str msg: + Message given for the offending value. It is formatted using + :py:meth:`str.format` with the only positional parameter which is + the value itself. + + :return: self. + ''' + self.checks.append(( + 'check_func', + (lambda *args: (True, True, True)), + (lambda value: msg.format(value)) + )) + return self + + def either(self, *specs): + '''Describes value that matches one of the given specs + + Check out :py:meth:`Spec.check_either` method documentation for more + details, but note that there a list of specs was replaced by start and + end indicies in ``self.specs``. + + :return: self. + ''' + start = len(self.specs) + self.specs.extend(specs) + self.checks.append(('check_either', start, len(self.specs))) + return self + + def optional(self): + '''Mark value as optional + + Only useful for key specs in :py:meth:`Spec.__init__` and + :py:meth:`Spec.update` and some last supplied to :py:meth:`Spec.tuple`. + + :return: self. + ''' + self.isoptional = True + return self + + def required(self): + '''Mark value as required + + Only useful for key specs in :py:meth:`Spec.__init__` and + :py:meth:`Spec.update` and some last supplied to :py:meth:`Spec.tuple`. + + .. note:: + Value is required by default. This method is only useful for + altering existing specification (or rather its copy). + + :return: self. + ''' + self.isoptional = False + return self + + def match_checks(self, *args): + '''Process checks registered for the given value + + Processes only “top-level” checks: key specifications given using at the + initialization or via :py:meth:`Spec.unknown_spec` are processed by + :py:meth:`Spec.match`. + + :return: proceed, hadproblem. + ''' + hadproblem = False + for check in self.checks: + proceed, chadproblem = getattr(self, check[0])(*(args + check[1:])) + if chadproblem: + hadproblem = True + if not proceed: + return False, hadproblem + return True, hadproblem + + def match(self, value, context_mark=None, data=None, context=(), echoerr=echoerr): + '''Check that given value matches this specification + + :return: proceed, hadproblem. + ''' + havemarks(value) + proceed, hadproblem = self.match_checks(value, context_mark, data, context, echoerr) + if proceed: + if self.keys or self.uspecs: + for key, vali in self.keys.items(): + valspec = self.specs[vali] + if key in value: + proceed, mhadproblem = valspec.match( + value[key], + value.mark, + data, + context.enter_key(value, key), + echoerr + ) + if mhadproblem: + hadproblem = True + if not proceed: + return False, hadproblem + else: + if not valspec.isoptional: + hadproblem = True + echoerr(context=self.cmsg.format(key=context.key), + context_mark=None, + problem='required key is missing: {0}'.format(key), + problem_mark=value.mark) + for key in value.keys(): + havemarks(key) + if key not in self.keys: + for keyfunc, vali in self.uspecs: + valspec = self.specs[vali] + if isinstance(keyfunc, int): + spec = self.specs[keyfunc] + proceed, khadproblem = spec.match(key, context_mark, data, context, echoerr) + else: + proceed, khadproblem = keyfunc(key, data, context, echoerr) + if khadproblem: + hadproblem = True + if proceed: + proceed, vhadproblem = valspec.match( + value[key], + value.mark, + data, + context.enter_key(value, key), + echoerr + ) + if vhadproblem: + hadproblem = True + break + else: + hadproblem = True + if self.ufailmsg: + echoerr(context=self.cmsg.format(key=context.key), + context_mark=None, + problem=self.ufailmsg(key), + problem_mark=key.mark) + return True, hadproblem + + def __getitem__(self, key): + '''Get specification for the given key + ''' + return self.specs[self.keys[key]] + + def __setitem__(self, key, value): + '''Set specification for the given key + ''' + self.update(**{key: value}) diff --git a/powerline-bin/powerline/listers/__init__.py b/powerline-bin/powerline/listers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/listers/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/listers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d1a2939 Binary files /dev/null and b/powerline-bin/powerline/listers/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/listers/__pycache__/i3wm.cpython-312.pyc b/powerline-bin/powerline/listers/__pycache__/i3wm.cpython-312.pyc new file mode 100644 index 0000000..54b8851 Binary files /dev/null and b/powerline-bin/powerline/listers/__pycache__/i3wm.cpython-312.pyc differ diff --git a/powerline-bin/powerline/listers/__pycache__/pdb.cpython-312.pyc b/powerline-bin/powerline/listers/__pycache__/pdb.cpython-312.pyc new file mode 100644 index 0000000..9b85917 Binary files /dev/null and b/powerline-bin/powerline/listers/__pycache__/pdb.cpython-312.pyc differ diff --git a/powerline-bin/powerline/listers/__pycache__/vim.cpython-312.pyc b/powerline-bin/powerline/listers/__pycache__/vim.cpython-312.pyc new file mode 100644 index 0000000..cfd985b Binary files /dev/null and b/powerline-bin/powerline/listers/__pycache__/vim.cpython-312.pyc differ diff --git a/powerline-bin/powerline/listers/i3wm.py b/powerline-bin/powerline/listers/i3wm.py new file mode 100644 index 0000000..0bbcfdc --- /dev/null +++ b/powerline-bin/powerline/listers/i3wm.py @@ -0,0 +1,68 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import requires_segment_info +from powerline.lib.dict import updated +from powerline.bindings.wm import get_i3_connection, get_connected_xrandr_outputs + + +@requires_segment_info +def output_lister(pl, segment_info): + '''List all outputs in segment_info format + ''' + + return ( + ( + updated(segment_info, output=output['name']), + { + 'draw_inner_divider': None + } + ) + for output in get_connected_xrandr_outputs(pl) + ) + + +@requires_segment_info +def workspace_lister(pl, segment_info, only_show=None, output=None): + '''List all workspaces in segment_info format + + Sets the segment info values of ``workspace`` and ``output`` to the name of + the i3 workspace and the ``xrandr`` output respectively and the keys + ``"visible"``, ``"urgent"`` and ``"focused"`` to a boolean indicating these + states. + + :param list only_show: + Specifies which workspaces to list. Valid entries are ``"visible"``, + ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces + are listed. + + :param str output: + May be set to the name of an X output. If specified, only workspaces + on that output are listed. Overrides automatic output detection by + the lemonbar renderer and bindings. Set to ``false`` to force + all workspaces to be shown. + ''' + + if output == None: + output = output or segment_info.get('output') + + return ( + ( + updated( + segment_info, + output=w['output'], + workspace={ + 'name': w['name'], + 'visible': w['visible'], + 'urgent': w['urgent'], + 'focused': w['focused'], + }, + ), + { + 'draw_inner_divider': None + } + ) + for w in get_i3_connection().get_workspaces() + if (((not only_show or any(w[typ] for typ in only_show)) + and (not output or w['output'] == output))) + ) diff --git a/powerline-bin/powerline/listers/pdb.py b/powerline-bin/powerline/listers/pdb.py new file mode 100644 index 0000000..24e11ea --- /dev/null +++ b/powerline-bin/powerline/listers/pdb.py @@ -0,0 +1,37 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import requires_segment_info + + +@requires_segment_info +def frame_lister(pl, segment_info, full_stack=False, maxframes=3): + '''List all frames in segment_info format + + :param bool full_stack: + If true, then all frames in the stack are listed. Normally N first + frames are discarded where N is a number of frames present at the first + invocation of the prompt minus one. + :param int maxframes: + Maximum number of frames to display. + ''' + if full_stack: + initial_stack_length = 0 + frames = segment_info['pdb'].stack + else: + initial_stack_length = segment_info['initial_stack_length'] + frames = segment_info['pdb'].stack[initial_stack_length:] + + if len(frames) > maxframes: + frames = frames[-maxframes:] + + return ( + ( + { + 'curframe': frame[0], + 'initial_stack_length': initial_stack_length, + }, + {} + ) + for frame in frames + ) diff --git a/powerline-bin/powerline/listers/vim.py b/powerline-bin/powerline/listers/vim.py new file mode 100644 index 0000000..583e0d3 --- /dev/null +++ b/powerline-bin/powerline/listers/vim.py @@ -0,0 +1,123 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import requires_segment_info +from powerline.bindings.vim import (current_tabpage, list_tabpages) + +try: + import vim +except ImportError: + vim = object() + + +def tabpage_updated_segment_info(segment_info, tabpage): + segment_info = segment_info.copy() + window = tabpage.window + buffer = window.buffer + segment_info.update( + tabpage=tabpage, + tabnr=tabpage.number, + window=window, + winnr=window.number, + window_id=int(window.vars.get('powerline_window_id', -1)), + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info + + +@requires_segment_info +def tablister(pl, segment_info, **kwargs): + '''List all tab pages in segment_info format + + Specifically generates a list of segment info dictionaries with ``window``, + ``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local + ones and additional ``tabpage`` and ``tabnr`` keys. + + Adds either ``tab:`` or ``tab_nc:`` prefix to all segment highlight groups. + + Works best with vim-7.4 or later: earlier versions miss tabpage object and + thus window objects are not available as well. + ''' + cur_tabpage = current_tabpage() + cur_tabnr = cur_tabpage.number + + def add_multiplier(tabpage, dct): + dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr)) + return dct + + return ( + (lambda tabpage, prefix: ( + tabpage_updated_segment_info(segment_info, tabpage), + add_multiplier(tabpage, { + 'highlight_group_prefix': prefix, + 'divider_highlight_group': 'tab:divider' + }) + ))(tabpage, 'tab' if tabpage == cur_tabpage else 'tab_nc') + for tabpage in list_tabpages() + ) + + +def buffer_updated_segment_info(segment_info, buffer): + segment_info = segment_info.copy() + segment_info.update( + window=None, + winnr=None, + window_id=None, + buffer=buffer, + bufnr=buffer.number, + ) + return segment_info + + +@requires_segment_info +def bufferlister(pl, segment_info, show_unlisted=False, **kwargs): + '''List all buffers in segment_info format + + Specifically generates a list of segment info dictionaries with ``buffer`` + and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and + ``window_id`` keys set to None. + + Adds one of ``buf:``, ``buf_nc:``, ``buf_mod:``, or ``buf_nc_mod`` + prefix to all segment highlight groups. + + :param bool show_unlisted: + True if unlisted buffers should be shown as well. Current buffer is + always shown. + ''' + cur_buffer = vim.current.buffer + cur_bufnr = cur_buffer.number + + def add_multiplier(buffer, dct): + dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr)) + return dct + + return ( + (lambda buffer, current, modified: ( + buffer_updated_segment_info(segment_info, buffer), + add_multiplier(buffer, { + 'highlight_group_prefix': '{0}{1}'.format(current, modified), + 'divider_highlight_group': 'tab:divider' + }) + ))( + buffer, + 'buf' if buffer is cur_buffer else 'buf_nc', + '_mod' if int(vim.eval('getbufvar({0}, \'&modified\')'.format(buffer.number))) > 0 else '' + ) + for buffer in vim.buffers if ( + buffer is cur_buffer + or show_unlisted + # We can't use vim_getbufoption(segment_info, 'buflisted') + # here for performance reasons. Querying the buffer options + # through the vim python module's option attribute caused + # vim to think it needed to update the tabline for every + # keystroke after any event that changed the buffer's + # options. + # + # Using the vim module's eval method to directly use the + # buflisted(nr) vim method instead does not cause vim to + # update the tabline after every keystroke, but rather after + # events that would change that status. Fixes #1281 + or int(vim.eval('buflisted(%s)' % buffer.number)) > 0 + ) + ) diff --git a/powerline-bin/powerline/matchers/__init__.py b/powerline-bin/powerline/matchers/__init__.py new file mode 100644 index 0000000..b2b9f10 --- /dev/null +++ b/powerline-bin/powerline/matchers/__init__.py @@ -0,0 +1,6 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) +from pkgutil import extend_path + + +__path__ = extend_path(__path__, __name__) diff --git a/powerline-bin/powerline/matchers/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/matchers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f38cc0b Binary files /dev/null and b/powerline-bin/powerline/matchers/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/matchers/vim/__init__.py b/powerline-bin/powerline/matchers/vim/__init__.py new file mode 100644 index 0000000..f6de45e --- /dev/null +++ b/powerline-bin/powerline/matchers/vim/__init__.py @@ -0,0 +1,19 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from powerline.bindings.vim import vim_getbufoption, buffer_name + + +def help(matcher_info): + return vim_getbufoption(matcher_info, 'buftype') == 'help' + + +def cmdwin(matcher_info): + name = buffer_name(matcher_info) + return name and os.path.basename(name) == b'[Command Line]' + + +def quickfix(matcher_info): + return vim_getbufoption(matcher_info, 'buftype') == 'quickfix' diff --git a/powerline-bin/powerline/matchers/vim/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/matchers/vim/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..91e3fe9 Binary files /dev/null and b/powerline-bin/powerline/matchers/vim/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/matchers/vim/plugin/__init__.py b/powerline-bin/powerline/matchers/vim/plugin/__init__.py new file mode 100644 index 0000000..b2b9f10 --- /dev/null +++ b/powerline-bin/powerline/matchers/vim/plugin/__init__.py @@ -0,0 +1,6 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) +from pkgutil import extend_path + + +__path__ = extend_path(__path__, __name__) diff --git a/powerline-bin/powerline/matchers/vim/plugin/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..34885cc Binary files /dev/null and b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/matchers/vim/plugin/__pycache__/commandt.cpython-312.pyc b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/commandt.cpython-312.pyc new file mode 100644 index 0000000..931dad1 Binary files /dev/null and b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/commandt.cpython-312.pyc differ diff --git a/powerline-bin/powerline/matchers/vim/plugin/__pycache__/gundo.cpython-312.pyc b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/gundo.cpython-312.pyc new file mode 100644 index 0000000..f915023 Binary files /dev/null and b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/gundo.cpython-312.pyc differ diff --git a/powerline-bin/powerline/matchers/vim/plugin/__pycache__/nerdtree.cpython-312.pyc b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/nerdtree.cpython-312.pyc new file mode 100644 index 0000000..fad7aed Binary files /dev/null and b/powerline-bin/powerline/matchers/vim/plugin/__pycache__/nerdtree.cpython-312.pyc differ diff --git a/powerline-bin/powerline/matchers/vim/plugin/commandt.py b/powerline-bin/powerline/matchers/vim/plugin/commandt.py new file mode 100644 index 0000000..7eefe9b --- /dev/null +++ b/powerline-bin/powerline/matchers/vim/plugin/commandt.py @@ -0,0 +1,14 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from powerline.bindings.vim import vim_getbufoption, buffer_name + + +def commandt(matcher_info): + name = buffer_name(matcher_info) + return ( + vim_getbufoption(matcher_info, 'filetype') == 'command-t' + or (name and os.path.basename(name) == b'GoToFile') + ) diff --git a/powerline-bin/powerline/matchers/vim/plugin/gundo.py b/powerline-bin/powerline/matchers/vim/plugin/gundo.py new file mode 100644 index 0000000..e0fe377 --- /dev/null +++ b/powerline-bin/powerline/matchers/vim/plugin/gundo.py @@ -0,0 +1,16 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from powerline.bindings.vim import buffer_name + + +def gundo(matcher_info): + name = buffer_name(matcher_info) + return name and os.path.basename(name) == b'__Gundo__' + + +def gundo_preview(matcher_info): + name = buffer_name(matcher_info) + return name and os.path.basename(name) == b'__Gundo_Preview__' diff --git a/powerline-bin/powerline/matchers/vim/plugin/nerdtree.py b/powerline-bin/powerline/matchers/vim/plugin/nerdtree.py new file mode 100644 index 0000000..d6e9f69 --- /dev/null +++ b/powerline-bin/powerline/matchers/vim/plugin/nerdtree.py @@ -0,0 +1,15 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import re + +from powerline.bindings.vim import buffer_name + + +NERD_TREE_RE = re.compile(b'NERD_tree_\\d+') + + +def nerdtree(matcher_info): + name = buffer_name(matcher_info) + return name and NERD_TREE_RE.match(os.path.basename(name)) diff --git a/powerline-bin/powerline/pdb.py b/powerline-bin/powerline/pdb.py new file mode 100644 index 0000000..b1e13ce --- /dev/null +++ b/powerline-bin/powerline/pdb.py @@ -0,0 +1,48 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import platform +import os + +from powerline import Powerline +from powerline.lib.overrides import parse_override_var +from powerline.lib.dict import mergeargs, mergedicts + + +class PDBPowerline(Powerline): + '''PDB-specific powerline bindings + ''' + def init(self, **kwargs): + return super(PDBPowerline, self).init( + ext='pdb', + renderer_module='pdb', + **kwargs + ) + + def do_setup(self, pdb): + self.update_renderer() + self.renderer.set_pdb(pdb) + + def load_main_config(self): + r = super(PDBPowerline, self).load_main_config() + config_overrides = os.environ.get('POWERLINE_CONFIG_OVERRIDES') + if config_overrides: + mergedicts(r, mergeargs(parse_override_var(config_overrides))) + return r + + def load_theme_config(self, name): + r = super(PDBPowerline, self).load_theme_config(name) + theme_overrides = os.environ.get('POWERLINE_THEME_OVERRIDES') + if theme_overrides: + theme_overrides_dict = mergeargs(parse_override_var(theme_overrides)) + if name in theme_overrides_dict: + mergedicts(r, theme_overrides_dict[name]) + return r + + def get_config_paths(self): + paths = [path for path in os.environ.get('POWERLINE_CONFIG_PATHS', '').split(':') if path] + return paths or super(PDBPowerline, self).get_config_paths() + + if sys.version_info < (3,) and platform.python_implementation() == 'PyPy': + get_encoding = staticmethod(lambda: 'ascii') diff --git a/powerline-bin/powerline/renderer.py b/powerline-bin/powerline/renderer.py new file mode 100644 index 0000000..040b406 --- /dev/null +++ b/powerline-bin/powerline/renderer.py @@ -0,0 +1,594 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import os +import re +import operator + +from itertools import chain + +from powerline.theme import Theme +from powerline.lib.unicode import unichr, strwidth_ucs_2, strwidth_ucs_4 + + +NBSP = ' ' + + +np_control_character_translations = dict(( + # Control characters: ^@ … ^Y + (i1, '^' + unichr(i1 + 0x40)) for i1 in range(0x20) +)) +'''Control character translations + +Dictionary that maps characters in range 0x00–0x1F (inclusive) to strings +``'^@'``, ``'^A'`` and so on. + +.. note: maps tab to ``^I`` and newline to ``^J``. +''' + +np_invalid_character_translations = dict(( + # Invalid unicode characters obtained using 'surrogateescape' error + # handler. + (i2, '<{0:02x}>'.format(i2 - 0xDC00)) for i2 in range(0xDC80, 0xDD00) +)) +'''Invalid unicode character translations + +When using ``surrogateescape`` encoding error handling method characters in +range 0x80–0xFF (inclusive) are transformed into unpaired surrogate escape +unicode codepoints 0xDC80–0xDD00. This dictionary maps such characters to +``<80>``, ``<81>``, and so on: in Python-3 they cannot be printed or +converted to UTF-8 because UTF-8 standard does not allow surrogate escape +characters, not even paired ones. Python-2 contains a bug that allows such +action, but printing them in any case makes no sense. +''' + +# XXX: not using `r` because it makes no sense. +np_invalid_character_re = re.compile('(? width: + for segment in chain(segments_priority, no_priority_segments): + if segment['truncate'] is not None: + segment['contents'] = segment['truncate'](self.pl, current_width - width, segment) + + segments_priority = iter(segments_priority) + if current_width > width and len(segments) > 100: + # When there are too many segments use faster, but less correct + # algorythm for width computation + diff = current_width - width + for segment in segments_priority: + segments.remove(segment) + diff -= segment['_len'] + if diff <= 0: + break + current_width = self._render_length(theme, segments, divider_widths) + if current_width > width: + # When there are not too much use more precise, but much slower + # width computation. It also finishes computations in case + # previous variant did not free enough space. + for segment in segments_priority: + segments.remove(segment) + current_width = self._render_length(theme, segments, divider_widths) + if current_width <= width: + break + del segments_priority + + # Distribute the remaining space on spacer segments + segments_spacers = [segment for segment in segments if segment['expand'] is not None] + if segments_spacers: + distribute_len, distribute_len_remainder = divmod(width - current_width, len(segments_spacers)) + for segment in segments_spacers: + segment['contents'] = ( + segment['expand']( + self.pl, + distribute_len + (1 if distribute_len_remainder > 0 else 0), + segment)) + distribute_len_remainder -= 1 + # `_len` key is not needed anymore, but current_width should have an + # actual value for various bindings. + current_width = width + elif output_width: + current_width = self._render_length(theme, segments, divider_widths) + + rendered_highlighted = self.hl_join([ + segment['_rendered_hl'] + for segment in self._render_segments(theme, segments) + ]) + if rendered_highlighted: + rendered_highlighted += self.hlstyle() + + return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width) + + def _prepare_segments(self, segments, calculate_contents_len): + '''Translate non-printable characters and calculate segment width + ''' + for segment in segments: + segment['contents'] = translate_np(segment['contents']) + if calculate_contents_len: + for segment in segments: + if segment['literal_contents'][1]: + segment['_contents_len'] = segment['literal_contents'][0] + else: + segment['_contents_len'] = self.strwidth(segment['contents']) + + def _render_length(self, theme, segments, divider_widths): + '''Update segments lengths and return them + ''' + segments_len = len(segments) + ret = 0 + divider_spaces = theme.get_spaces() + prev_segment = theme.EMPTY_SEGMENT + try: + first_segment = next(iter(( + segment + for segment in segments + if not segment['literal_contents'][1] + ))) + except StopIteration: + first_segment = None + try: + last_segment = next(iter(( + segment + for segment in reversed(segments) + if not segment['literal_contents'][1] + ))) + except StopIteration: + last_segment = None + for index, segment in enumerate(segments): + side = segment['side'] + segment_len = segment['_contents_len'] + if not segment['literal_contents'][1]: + if side == 'left': + if segment is not last_segment: + compare_segment = next(iter(( + segment + for segment in segments[index + 1:] + if not segment['literal_contents'][1] + ))) + else: + compare_segment = theme.EMPTY_SEGMENT + else: + compare_segment = prev_segment + + divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' + + outer_padding = int(bool( + segment is first_segment + if side == 'left' else + segment is last_segment + )) * theme.outer_padding + + draw_divider = segment['draw_' + divider_type + '_divider'] + segment_len += outer_padding + if draw_divider: + segment_len += divider_widths[side][divider_type] + divider_spaces + prev_segment = segment + + segment['_len'] = segment_len + ret += segment_len + return ret + + def _render_segments(self, theme, segments, render_highlighted=True): + '''Internal segment rendering method. + + This method loops through the segment array and compares the + foreground/background colors and divider properties and returns the + rendered statusline as a string. + + The method always renders the raw segment contents (i.e. without + highlighting strings added), and only renders the highlighted + statusline if render_highlighted is True. + ''' + segments_len = len(segments) + divider_spaces = theme.get_spaces() + prev_segment = theme.EMPTY_SEGMENT + try: + first_segment = next(iter(( + segment + for segment in segments + if not segment['literal_contents'][1] + ))) + except StopIteration: + first_segment = None + try: + last_segment = next(iter(( + segment + for segment in reversed(segments) + if not segment['literal_contents'][1] + ))) + except StopIteration: + last_segment = None + + for index, segment in enumerate(segments): + side = segment['side'] + if not segment['literal_contents'][1]: + if side == 'left': + if segment is not last_segment: + compare_segment = next(iter(( + segment + for segment in segments[index + 1:] + if not segment['literal_contents'][1] + ))) + else: + compare_segment = theme.EMPTY_SEGMENT + else: + compare_segment = prev_segment + outer_padding = int(bool( + segment is first_segment + if side == 'left' else + segment is last_segment + )) * theme.outer_padding * ' ' + divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard' + + divider_highlighted = '' + contents_raw = segment['contents'] + contents_highlighted = '' + draw_divider = segment['draw_' + divider_type + '_divider'] + + # XXX Make sure self.hl() calls are called in the same order + # segments are displayed. This is needed for Vim renderer to work. + if draw_divider: + divider_raw = self.escape(theme.get_divider(side, divider_type)) + if side == 'left': + contents_raw = outer_padding + contents_raw + (divider_spaces * ' ') + else: + contents_raw = (divider_spaces * ' ') + contents_raw + outer_padding + + if divider_type == 'soft': + divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight' + divider_fg = segment[divider_highlight_group_key]['fg'] + divider_bg = segment[divider_highlight_group_key]['bg'] + else: + divider_fg = segment['highlight']['bg'] + divider_bg = compare_segment['highlight']['bg'] + + if side == 'left': + if render_highlighted: + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + segment['_rendered_raw'] = contents_raw + divider_raw + segment['_rendered_hl'] = contents_highlighted + divider_highlighted + else: + if render_highlighted: + divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + segment['_rendered_raw'] = divider_raw + contents_raw + segment['_rendered_hl'] = divider_highlighted + contents_highlighted + else: + if side == 'left': + contents_raw = outer_padding + contents_raw + else: + contents_raw = contents_raw + outer_padding + + contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) + segment['_rendered_raw'] = contents_raw + segment['_rendered_hl'] = contents_highlighted + prev_segment = segment + else: + segment['_rendered_raw'] = ' ' * segment['literal_contents'][0] + segment['_rendered_hl'] = segment['literal_contents'][1] + yield segment + + def escape(self, string): + '''Method that escapes segment contents. + ''' + return string.translate(self.character_translations) + + def hlstyle(fg=None, bg=None, attrs=None): + '''Output highlight style string. + + Assuming highlighted string looks like ``{style}{contents}`` this method + should output ``{style}``. If it is called without arguments this method + is supposed to reset style to its default. + ''' + raise NotImplementedError + + def hl(self, contents, fg=None, bg=None, attrs=None): + '''Output highlighted chunk. + + This implementation just outputs :py:meth:`hlstyle` joined with + ``contents``. + ''' + return self.hlstyle(fg, bg, attrs) + (contents or '') diff --git a/powerline-bin/powerline/renderers/__init__.py b/powerline-bin/powerline/renderers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/renderers/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c9b8489 Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/__pycache__/i3bar.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/i3bar.cpython-312.pyc new file mode 100644 index 0000000..6046c22 Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/i3bar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/__pycache__/lemonbar.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/lemonbar.cpython-312.pyc new file mode 100644 index 0000000..642cc68 Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/lemonbar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/__pycache__/pango_markup.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/pango_markup.cpython-312.pyc new file mode 100644 index 0000000..c721f9c Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/pango_markup.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/__pycache__/pdb.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/pdb.cpython-312.pyc new file mode 100644 index 0000000..ab445d2 Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/pdb.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/__pycache__/tmux.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/tmux.cpython-312.pyc new file mode 100644 index 0000000..ef6852d Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/tmux.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/__pycache__/vim.cpython-312.pyc b/powerline-bin/powerline/renderers/__pycache__/vim.cpython-312.pyc new file mode 100644 index 0000000..657fe18 Binary files /dev/null and b/powerline-bin/powerline/renderers/__pycache__/vim.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/i3bar.py b/powerline-bin/powerline/renderers/i3bar.py new file mode 100644 index 0000000..4124a0f --- /dev/null +++ b/powerline-bin/powerline/renderers/i3bar.py @@ -0,0 +1,36 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import json + +from powerline.renderer import Renderer + + +class I3barRenderer(Renderer): + '''I3bar Segment Renderer. + + Currently works only for i3bgbar (i3 bar with custom patches). + ''' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None): + segment = { + 'full_text': contents, + 'separator': False, + 'separator_block_width': 0, # no seperators + } + + if fg is not None: + if fg is not False and fg[1] is not False: + segment['color'] = '#{0:06x}'.format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + segment['background'] = '#{0:06x}'.format(bg[1]) + return json.dumps(segment) + ',' + + +renderer = I3barRenderer diff --git a/powerline-bin/powerline/renderers/ipython/__init__.py b/powerline-bin/powerline/renderers/ipython/__init__.py new file mode 100644 index 0000000..8f463b5 --- /dev/null +++ b/powerline-bin/powerline/renderers/ipython/__init__.py @@ -0,0 +1,34 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import Theme +from powerline.renderers.shell import PromptRenderer + + +class IPythonRenderer(PromptRenderer): + '''Powerline ipython segment renderer.''' + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + r['ipython'] = segment_info + return r + + def get_theme(self, matcher_info): + if matcher_info == 'in': + return self.theme + else: + match = self.local_themes[matcher_info] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + main_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() diff --git a/powerline-bin/powerline/renderers/ipython/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/renderers/ipython/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..fda6c8d Binary files /dev/null and b/powerline-bin/powerline/renderers/ipython/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/ipython/__pycache__/pre_5.cpython-312.pyc b/powerline-bin/powerline/renderers/ipython/__pycache__/pre_5.cpython-312.pyc new file mode 100644 index 0000000..fa31d7c Binary files /dev/null and b/powerline-bin/powerline/renderers/ipython/__pycache__/pre_5.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/ipython/__pycache__/since_5.cpython-312.pyc b/powerline-bin/powerline/renderers/ipython/__pycache__/since_5.cpython-312.pyc new file mode 100644 index 0000000..e96b80c Binary files /dev/null and b/powerline-bin/powerline/renderers/ipython/__pycache__/since_5.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/ipython/pre_5.py b/powerline-bin/powerline/renderers/ipython/pre_5.py new file mode 100644 index 0000000..9fc8c21 --- /dev/null +++ b/powerline-bin/powerline/renderers/ipython/pre_5.py @@ -0,0 +1,56 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer +from powerline.renderers.shell.readline import ReadlineRenderer +from powerline.renderers.ipython import IPythonRenderer + + +class IPythonPre50Renderer(IPythonRenderer, ShellRenderer): + '''Powerline ipython segment renderer for pre-5.0 IPython versions.''' + def render(self, **kwargs): + # XXX super(ShellRenderer), *not* super(IPythonPre50Renderer) + return super(ShellRenderer, self).render(**kwargs) + + def do_render(self, segment_info, **kwargs): + segment_info.update(client_id='ipython') + return super(IPythonPre50Renderer, self).do_render( + segment_info=segment_info, + **kwargs + ) + + +class IPythonPromptRenderer(IPythonPre50Renderer, ReadlineRenderer): + '''Powerline ipython prompt (in and in2) renderer''' + pass + + +class IPythonNonPromptRenderer(IPythonPre50Renderer): + '''Powerline ipython non-prompt (out and rewrite) renderer''' + pass + + +class RendererProxy(object): + '''Powerline IPython renderer proxy which chooses appropriate renderer + + Instantiates two renderer objects: one will be used for prompts and the + other for non-prompts. + ''' + def __init__(self, **kwargs): + old_widths = {} + self.non_prompt_renderer = IPythonNonPromptRenderer(old_widths=old_widths, **kwargs) + self.prompt_renderer = IPythonPromptRenderer(old_widths=old_widths, **kwargs) + + def render_above_lines(self, *args, **kwargs): + return self.non_prompt_renderer.render_above_lines(*args, **kwargs) + + def render(self, is_prompt, *args, **kwargs): + return (self.prompt_renderer if is_prompt else self.non_prompt_renderer).render( + *args, **kwargs) + + def shutdown(self, *args, **kwargs): + self.prompt_renderer.shutdown(*args, **kwargs) + self.non_prompt_renderer.shutdown(*args, **kwargs) + + +renderer = RendererProxy diff --git a/powerline-bin/powerline/renderers/ipython/since_5.py b/powerline-bin/powerline/renderers/ipython/since_5.py new file mode 100644 index 0000000..8a26da7 --- /dev/null +++ b/powerline-bin/powerline/renderers/ipython/since_5.py @@ -0,0 +1,130 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import operator + +from collections import defaultdict + +try: + from __builtin__ import reduce +except ImportError: + from functools import reduce + +from pygments.token import Token +from prompt_toolkit.styles import DynamicStyle, Attrs + +from powerline.renderers.ipython import IPythonRenderer +from powerline.ipython import IPythonInfo +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +PowerlinePromptToken = Token.Generic.Prompt.Powerline + + +# Note: since 2.7 there is dict.__missing__ with same purpose. But in 2.6 one +# must use defaultdict to get __missing__ working. +class PowerlineStyleDict(defaultdict): + '''Dictionary used for getting pygments style for Powerline groups + ''' + def __new__(cls, missing_func): + return defaultdict.__new__(cls) + + def __init__(self, missing_func): + super(PowerlineStyleDict, self).__init__() + self.missing_func = missing_func + + def __missing__(self, key): + return self.missing_func(key) + + +class PowerlinePromptStyle(DynamicStyle): + def get_attrs_for_token(self, token): + if ( + token not in PowerlinePromptToken + or len(token) != len(PowerlinePromptToken) + 1 + or not token[-1].startswith('Pl') + or token[-1] == 'Pl' + ): + return super(PowerlinePromptStyle, self).get_attrs_for_token(token) + ret = { + 'color': None, + 'bgcolor': None, + 'bold': None, + 'underline': None, + 'italic': None, + 'reverse': False, + 'blink': False, + } + for prop in token[-1][3:].split('_'): + if prop[0] == 'a': + ret[prop[1:]] = True + elif prop[0] == 'f': + ret['color'] = prop[1:] + elif prop[0] == 'b': + ret['bgcolor'] = prop[1:] + return Attrs(**ret) + + def get_token_to_attributes_dict(self): + dct = super(PowerlinePromptStyle, self).get_token_to_attributes_dict() + + def fallback(key): + try: + return dct[key] + except KeyError: + return self.get_attrs_for_token(key) + + return PowerlineStyleDict(fallback) + + def invalidation_hash(self): + return super(PowerlinePromptStyle, self).invalidation_hash() + 1 + + +class IPythonPygmentsRenderer(IPythonRenderer): + reduce_initial = [] + + def get_segment_info(self, segment_info, mode): + return super(IPythonPygmentsRenderer, self).get_segment_info( + IPythonInfo(segment_info), mode) + + @staticmethod + def hl_join(segments): + return reduce(operator.iadd, segments, []) + + def hl(self, contents, fg=None, bg=None, attrs=None): + '''Output highlighted chunk. + + This implementation outputs a list containing a single pair + (:py:class:`pygments.token.Token`, + :py:class:`powerline.lib.unicode.unicode`). + ''' + guifg = None + guibg = None + attrs = [] + if fg is not None and fg is not False: + guifg = fg[1] + if bg is not None and bg is not False: + guibg = bg[1] + if attrs: + attrs = [] + if attrs & ATTR_BOLD: + attrs.append('bold') + if attrs & ATTR_ITALIC: + attrs.append('italic') + if attrs & ATTR_UNDERLINE: + attrs.append('underline') + name = ( + 'Pl' + + ''.join(('_a' + attr for attr in attrs)) + + (('_f%6x' % guifg) if guifg is not None else '') + + (('_b%6x' % guibg) if guibg is not None else '') + ) + return [(getattr(Token.Generic.Prompt.Powerline, name), contents)] + + def hlstyle(self, **kwargs): + return [] + + def get_client_id(self, segment_info): + return id(self) + + +renderer = IPythonPygmentsRenderer diff --git a/powerline-bin/powerline/renderers/lemonbar.py b/powerline-bin/powerline/renderers/lemonbar.py new file mode 100644 index 0000000..f378f23 --- /dev/null +++ b/powerline-bin/powerline/renderers/lemonbar.py @@ -0,0 +1,61 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.theme import Theme +from powerline.colorscheme import ATTR_UNDERLINE + + +class LemonbarRenderer(Renderer): + '''lemonbar (formerly bar/bar ain't recursive) renderer + + + See documentation of `lemonbar `_ and :ref:`the usage instructions ` + ''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%{}' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None): + text = '' + + if fg is not None: + if fg is not False and fg[1] is not False: + text += '%{{F#ff{0:06x}}}'.format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + text += '%{{B#ff{0:06x}}}'.format(bg[1]) + + if attrs & ATTR_UNDERLINE: + text += '%{+u}' + + return text + contents + '%{F-B--u}' + + def render(self, *args, **kwargs): + return '%{{l}}{0}%{{r}}{1}'.format( + super(LemonbarRenderer, self).render(side='left', segment_info={'output': kwargs.get('matcher_info')}, *args, **kwargs), + super(LemonbarRenderer, self).render(side='right', segment_info={'output': kwargs.get('matcher_info')}, *args, **kwargs), + ) + + def get_theme(self, matcher_info): + if not matcher_info or matcher_info not in self.local_themes: + return self.theme + match = self.local_themes[matcher_info] + + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + main_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + + +renderer = LemonbarRenderer diff --git a/powerline-bin/powerline/renderers/pango_markup.py b/powerline-bin/powerline/renderers/pango_markup.py new file mode 100644 index 0000000..1b7d624 --- /dev/null +++ b/powerline-bin/powerline/renderers/pango_markup.py @@ -0,0 +1,39 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from xml.sax.saxutils import escape as _escape + +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +class PangoMarkupRenderer(Renderer): + '''Powerline Pango markup segment renderer.''' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None): + '''Highlight a segment.''' + awesome_attr = [] + if fg is not None: + if fg is not False and fg[1] is not False: + awesome_attr += ['foreground="#{0:06x}"'.format(fg[1])] + if bg is not None: + if bg is not False and bg[1] is not False: + awesome_attr += ['background="#{0:06x}"'.format(bg[1])] + if attrs is not None and attrs is not False: + if attrs & ATTR_BOLD: + awesome_attr += ['font_weight="bold"'] + if attrs & ATTR_ITALIC: + awesome_attr += ['font_style="italic"'] + if attrs & ATTR_UNDERLINE: + awesome_attr += ['underline="single"'] + return '' + contents + '' + + escape = staticmethod(_escape) + + +renderer = PangoMarkupRenderer diff --git a/powerline-bin/powerline/renderers/pdb.py b/powerline-bin/powerline/renderers/pdb.py new file mode 100644 index 0000000..040f0e1 --- /dev/null +++ b/powerline-bin/powerline/renderers/pdb.py @@ -0,0 +1,50 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import platform + +from powerline.renderers.shell.readline import ReadlineRenderer +from powerline.renderer import Renderer + + +class PDBRenderer(ReadlineRenderer): + '''PDB-specific powerline renderer + ''' + pdb = None + initial_stack_length = None + + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + r['pdb'] = self.pdb + r['initial_stack_length'] = self.initial_stack_length + r['curframe'] = self.pdb.curframe + return r + + def set_pdb(self, pdb): + '''Record currently used :py:class:`pdb.Pdb` instance + + Must be called before first calling :py:meth:`render` method. + + :param pdb.Pdb pdb: + Used :py:class:`pdb.Pdb` instance. This instance will later be used + by :py:meth:`get_segment_info` for patching :ref:`segment_info + ` dictionary. + ''' + self.pdb = pdb + + def render(self, **kwargs): + if self.initial_stack_length is None: + self.initial_stack_length = len(self.pdb.stack) - 1 + return Renderer.render(self, **kwargs) + + if sys.version_info < (3,) and platform.python_implementation() == 'PyPy': + def do_render(self, **kwargs): + # Make sure that only ASCII characters survive + ret = super(PDBRenderer, self).do_render(**kwargs) + ret = ret.encode('ascii', 'replace') + ret = ret.decode('ascii') + return ret + + +renderer = PDBRenderer diff --git a/powerline-bin/powerline/renderers/shell/__init__.py b/powerline-bin/powerline/renderers/shell/__init__.py new file mode 100644 index 0000000..ebb0501 --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/__init__.py @@ -0,0 +1,182 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.theme import Theme +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +def int_to_rgb(num): + r = (num >> 16) & 0xff + g = (num >> 8) & 0xff + b = num & 0xff + return r, g, b + + +class PromptRenderer(Renderer): + '''Powerline generic prompt segment renderer''' + + def __init__(self, old_widths=None, **kwargs): + super(PromptRenderer, self).__init__(**kwargs) + self.old_widths = old_widths if old_widths is not None else {} + + def get_client_id(self, segment_info): + '''Get client ID given segment info + + This is used by daemon to correctly cache widths for different clients + using a single renderer instance. + + :param dict segment_info: + :ref:`Segment info dictionary `. Out of it only + ``client_id`` key is used. It is OK for this dictionary to not + contain this key. + + :return: Any hashable value or ``None``. + ''' + return segment_info.get('client_id') if isinstance(segment_info, dict) else None + + def do_render(self, output_width, segment_info, side, theme, width=None, **kwargs): + client_id = self.get_client_id(segment_info) + if client_id is not None: + local_key = (client_id, side, None if theme is self.theme else id(theme)) + key = (client_id, side, None) + did_width = False + if local_key[-1] != key[-1] and side == 'left': + try: + width = self.old_widths[key] + except KeyError: + pass + else: + did_width = True + if not did_width and width is not None: + if theme.cursor_space_multiplier is not None: + width = int(width * theme.cursor_space_multiplier) + elif theme.cursor_columns: + width -= theme.cursor_columns + + if side == 'right': + try: + width -= self.old_widths[(client_id, 'left', local_key[-1])] + except KeyError: + pass + res = super(PromptRenderer, self).do_render( + output_width=True, + width=width, + theme=theme, + segment_info=segment_info, + side=side, + **kwargs + ) + if client_id is not None: + self.old_widths[local_key] = res[-1] + ret = res if output_width else res[:-1] + if len(ret) == 1: + return ret[0] + else: + return ret + + +class ShellRenderer(PromptRenderer): + '''Powerline shell segment renderer.''' + escape_hl_start = '' + escape_hl_end = '' + term_truecolor = False + term_escape_style = 'auto' + tmux_escape = False + screen_escape = False + + character_translations = Renderer.character_translations.copy() + + def render(self, segment_info, **kwargs): + local_theme = segment_info.get('local_theme') + return super(ShellRenderer, self).render( + matcher_info=local_theme, + segment_info=segment_info, + **kwargs + ) + + def do_render(self, segment_info, **kwargs): + if self.term_escape_style == 'auto': + if segment_info['environ'].get('TERM') == 'fbterm': + self.used_term_escape_style = 'fbterm' + else: + self.used_term_escape_style = 'xterm' + else: + self.used_term_escape_style = self.term_escape_style + return super(ShellRenderer, self).do_render(segment_info=segment_info, **kwargs) + + def hlstyle(self, fg=None, bg=None, attrs=None): + '''Highlight a segment. + + If an argument is None, the argument is ignored. If an argument is + False, the argument is reset to the terminal defaults. If an argument + is a valid color or attribute, it’s added to the ANSI escape code. + ''' + ansi = [0] + is_fbterm = self.used_term_escape_style == 'fbterm' + term_truecolor = not is_fbterm and self.term_truecolor + if fg is not None: + if fg is False or fg[0] is False: + ansi += [39] + else: + if term_truecolor: + ansi += [38, 2] + list(int_to_rgb(fg[1])) + else: + ansi += [38, 5, fg[0]] + if bg is not None: + if bg is False or bg[0] is False: + ansi += [49] + else: + if term_truecolor: + ansi += [48, 2] + list(int_to_rgb(bg[1])) + else: + ansi += [48, 5, bg[0]] + if attrs is not None: + if attrs is False: + ansi += [22] + else: + if attrs & ATTR_BOLD: + ansi += [1] + elif attrs & ATTR_ITALIC: + # Note: is likely not to work or even be inverse in place of + # italic. Omit using this in colorschemes. + ansi += [3] + elif attrs & ATTR_UNDERLINE: + ansi += [4] + if is_fbterm: + r = [] + while ansi: + cur_ansi = ansi.pop(0) + if cur_ansi == 38: + ansi.pop(0) + r.append('\033[1;{0}}}'.format(ansi.pop(0))) + elif cur_ansi == 48: + ansi.pop(0) + r.append('\033[2;{0}}}'.format(ansi.pop(0))) + else: + r.append('\033[{0}m'.format(cur_ansi)) + r = ''.join(r) + else: + r = '\033[{0}m'.format(';'.join(str(attr) for attr in ansi)) + if self.tmux_escape: + r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\' + elif self.screen_escape: + r = '\033P' + r.replace('\033', '\033\033') + '\033\\' + return self.escape_hl_start + r + self.escape_hl_end + + def get_theme(self, matcher_info): + if not matcher_info: + return self.theme + match = self.local_themes[matcher_info] + try: + return match['theme'] + except KeyError: + match['theme'] = Theme( + theme_config=match['config'], + main_theme_config=self.theme_config, + **self.theme_kwargs + ) + return match['theme'] + + +renderer = ShellRenderer diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..277b13f Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/bash.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/bash.cpython-312.pyc new file mode 100644 index 0000000..ccab1cf Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/bash.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/ksh.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/ksh.cpython-312.pyc new file mode 100644 index 0000000..9352e9d Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/ksh.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/rcsh.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/rcsh.cpython-312.pyc new file mode 100644 index 0000000..2c1419d Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/rcsh.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/readline.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/readline.cpython-312.pyc new file mode 100644 index 0000000..f744f8d Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/readline.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/tcsh.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/tcsh.cpython-312.pyc new file mode 100644 index 0000000..a95fafe Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/tcsh.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/__pycache__/zsh.cpython-312.pyc b/powerline-bin/powerline/renderers/shell/__pycache__/zsh.cpython-312.pyc new file mode 100644 index 0000000..89a24a3 Binary files /dev/null and b/powerline-bin/powerline/renderers/shell/__pycache__/zsh.cpython-312.pyc differ diff --git a/powerline-bin/powerline/renderers/shell/bash.py b/powerline-bin/powerline/renderers/shell/bash.py new file mode 100644 index 0000000..783bd50 --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/bash.py @@ -0,0 +1,18 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class BashPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + escape_hl_start = '\[' + escape_hl_end = '\]' + + character_translations = ShellRenderer.character_translations.copy() + character_translations[ord('$')] = '\\$' + character_translations[ord('`')] = '\\`' + character_translations[ord('\\')] = '\\\\' + + +renderer = BashPromptRenderer diff --git a/powerline-bin/powerline/renderers/shell/ksh.py b/powerline-bin/powerline/renderers/shell/ksh.py new file mode 100644 index 0000000..0828e57 --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/ksh.py @@ -0,0 +1,19 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +ESCAPE_CHAR = '\001' + + +class KshPromptRenderer(ShellRenderer): + '''Powerline bash prompt segment renderer.''' + escape_hl_start = '\001' + escape_hl_end = '\001' + + def render(self, *args, **kwargs): + return '\001\r' + super(KshPromptRenderer, self).render(*args, **kwargs) + + +renderer = KshPromptRenderer diff --git a/powerline-bin/powerline/renderers/shell/rcsh.py b/powerline-bin/powerline/renderers/shell/rcsh.py new file mode 100644 index 0000000..75ccb22 --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/rcsh.py @@ -0,0 +1,7 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell.readline import ReadlineRenderer + + +renderer = ReadlineRenderer diff --git a/powerline-bin/powerline/renderers/shell/readline.py b/powerline-bin/powerline/renderers/shell/readline.py new file mode 100644 index 0000000..a72dff0 --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/readline.py @@ -0,0 +1,14 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class ReadlineRenderer(ShellRenderer): + '''Renderer useful for some applications that use readline + ''' + escape_hl_start = '\x01' + escape_hl_end = '\x02' + + +renderer = ReadlineRenderer diff --git a/powerline-bin/powerline/renderers/shell/tcsh.py b/powerline-bin/powerline/renderers/shell/tcsh.py new file mode 100644 index 0000000..bf0697d --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/tcsh.py @@ -0,0 +1,31 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell.zsh import ZshPromptRenderer + + +class TcshPromptRenderer(ZshPromptRenderer): + '''Powerline tcsh prompt segment renderer.''' + character_translations = ZshPromptRenderer.character_translations.copy() + character_translations[ord('%')] = '%%' + character_translations[ord('\\')] = '\\\\' + character_translations[ord('^')] = '\\^' + character_translations[ord('!')] = '\\!' + + def do_render(self, **kwargs): + ret = super(TcshPromptRenderer, self).do_render(**kwargs) + nbsp = self.character_translations.get(ord(' '), ' ') + end = self.hlstyle() + assert not ret or ret.endswith(end) + if ret.endswith(nbsp + end): + # Exchange nbsp and highlight end because tcsh removes trailing + # %{%} part of the prompt for whatever reason + ret = ret[:-(len(nbsp) + len(end))] + end + nbsp + else: + # We *must* end prompt with non-%{%} sequence for the reasons + # explained above. So add nbsp if it is not already there. + ret += nbsp + return ret + + +renderer = TcshPromptRenderer diff --git a/powerline-bin/powerline/renderers/shell/zsh.py b/powerline-bin/powerline/renderers/shell/zsh.py new file mode 100644 index 0000000..a231512 --- /dev/null +++ b/powerline-bin/powerline/renderers/shell/zsh.py @@ -0,0 +1,16 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class ZshPromptRenderer(ShellRenderer): + '''Powerline zsh prompt segment renderer.''' + escape_hl_start = '%{' + escape_hl_end = '%}' + + character_translations = ShellRenderer.character_translations.copy() + character_translations[ord('%')] = '%%' + + +renderer = ZshPromptRenderer diff --git a/powerline-bin/powerline/renderers/tmux.py b/powerline-bin/powerline/renderers/tmux.py new file mode 100644 index 0000000..ee6c09c --- /dev/null +++ b/powerline-bin/powerline/renderers/tmux.py @@ -0,0 +1,79 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE + + +def attrs_to_tmux_attrs(attrs): + if attrs is False: + return ['nobold', 'noitalics', 'nounderscore'] + else: + ret = [] + if attrs & ATTR_BOLD: + ret += ['bold'] + else: + ret += ['nobold'] + if attrs & ATTR_ITALIC: + ret += ['italics'] + else: + ret += ['noitalics'] + if attrs & ATTR_UNDERLINE: + ret += ['underscore'] + else: + ret += ['nounderscore'] + return ret + + +class TmuxRenderer(Renderer): + '''Powerline tmux segment renderer.''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('#')] = '##[]' + + def render(self, width=None, segment_info={}, **kwargs): + if width and segment_info: + width -= segment_info.get('width_adjust', 0) + if width < 10: + width = 10 + return super(TmuxRenderer, self).render(width=width, segment_info=segment_info, **kwargs) + + def hlstyle(self, fg=None, bg=None, attrs=None): + '''Highlight a segment.''' + # We don’t need to explicitly reset attributes, so skip those calls + if not attrs and not bg and not fg: + return '' + tmux_attrs = [] + if fg is not None: + if fg is False or fg[0] is False: + tmux_attrs += ['fg=default'] + else: + if self.term_truecolor and fg[1]: + tmux_attrs += ['fg=#{0:06x}'.format(int(fg[1]))] + else: + tmux_attrs += ['fg=colour' + str(fg[0])] + if bg is not None: + if bg is False or bg[0] is False: + tmux_attrs += ['bg=default'] + else: + if self.term_truecolor and bg[1]: + tmux_attrs += ['bg=#{0:06x}'.format(int(bg[1]))] + else: + tmux_attrs += ['bg=colour' + str(bg[0])] + if attrs is not None: + tmux_attrs += attrs_to_tmux_attrs(attrs) + return '#[' + ','.join(tmux_attrs) + ']' + + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + if segment_info: + r.update(segment_info) + if 'pane_id' in r: + varname = 'TMUX_PWD_' + str(r['pane_id']) + if varname in r['environ']: + r['getcwd'] = lambda: r['environ'][varname] + r['mode'] = mode + return r + + +renderer = TmuxRenderer diff --git a/powerline-bin/powerline/renderers/vim.py b/powerline-bin/powerline/renderers/vim.py new file mode 100644 index 0000000..281177c --- /dev/null +++ b/powerline-bin/powerline/renderers/vim.py @@ -0,0 +1,188 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +import vim + +from powerline.bindings.vim import vim_get_func, vim_getoption, environ, current_tabpage, get_vim_encoding +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE +from powerline.theme import Theme +from powerline.lib.unicode import unichr, register_strwidth_error + + +vim_mode = vim_get_func('mode', rettype='unicode') +if int(vim.eval('v:version')) >= 702: + _vim_mode = vim_mode + vim_mode = lambda: _vim_mode(1) + +mode_translations = { + unichr(ord('V') - 0x40): '^V', + unichr(ord('S') - 0x40): '^S', +} + + +class VimRenderer(Renderer): + '''Powerline vim segment renderer.''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%' + + segment_info = Renderer.segment_info.copy() + segment_info.update(environ=environ) + + def __init__(self, *args, **kwargs): + if not hasattr(vim, 'strwidth'): + # Hope nobody want to change this at runtime + if vim.eval('&ambiwidth') == 'double': + kwargs = dict(**kwargs) + kwargs['ambigious'] = 2 + super(VimRenderer, self).__init__(*args, **kwargs) + self.hl_groups = {} + self.prev_highlight = None + self.strwidth_error_name = register_strwidth_error(self.strwidth) + self.encoding = get_vim_encoding() + + def shutdown(self): + self.theme.shutdown() + for match in self.local_themes.values(): + if 'theme' in match: + match['theme'].shutdown() + + def add_local_theme(self, matcher, theme): + if matcher in self.local_themes: + raise KeyError('There is already a local theme with given matcher') + self.local_themes[matcher] = theme + + def get_matched_theme(self, match): + try: + return match['theme'] + except KeyError: + match['theme'] = Theme(theme_config=match['config'], main_theme_config=self.theme_config, **self.theme_kwargs) + return match['theme'] + + def get_theme(self, matcher_info): + if matcher_info is None: + return self.get_matched_theme(self.local_themes[None]) + for matcher in self.local_themes.keys(): + if matcher and matcher(matcher_info): + return self.get_matched_theme(self.local_themes[matcher]) + else: + return self.theme + + if hasattr(vim, 'strwidth'): + if sys.version_info < (3,): + def strwidth(self, string): + # Does not work with tabs, but neither is strwidth from default + # renderer + return vim.strwidth(string.encode(self.encoding, 'replace')) + else: + @staticmethod + def strwidth(string): + return vim.strwidth(string) + + def get_segment_info(self, segment_info, mode): + return segment_info or self.segment_info + + def render(self, window=None, window_id=None, winnr=None, is_tabline=False): + '''Render all segments.''' + segment_info = self.segment_info.copy() + + if window is vim.current.window: + mode = vim_mode() + mode = mode_translations.get(mode, mode) + else: + mode = 'nc' + + segment_info.update( + window=window, + mode=mode, + window_id=window_id, + winnr=winnr, + buffer=window.buffer, + tabpage=current_tabpage(), + encoding=self.encoding, + ) + segment_info['tabnr'] = segment_info['tabpage'].number + segment_info['bufnr'] = segment_info['buffer'].number + if is_tabline: + winwidth = int(vim_getoption('columns')) + else: + winwidth = segment_info['window'].width + + statusline = super(VimRenderer, self).render( + mode=mode, + width=winwidth, + segment_info=segment_info, + matcher_info=(None if is_tabline else segment_info), + ) + statusline = statusline.encode(self.encoding, self.strwidth_error_name) + return statusline + + def reset_highlight(self): + self.hl_groups.clear() + + def hlstyle(self, fg=None, bg=None, attrs=None): + '''Highlight a segment. + + If an argument is None, the argument is ignored. If an argument is + False, the argument is reset to the terminal defaults. If an argument + is a valid color or attribute, it’s added to the vim highlight group. + ''' + # In order not to hit E541 two consequent identical highlighting + # specifiers may be squashed into one. + attrs = attrs or 0 # Normalize `attrs` + if (fg, bg, attrs) == self.prev_highlight: + return '' + self.prev_highlight = (fg, bg, attrs) + + # We don’t need to explicitly reset attributes in vim, so skip those + # calls + if not attrs and not bg and not fg: + return '' + + if not (fg, bg, attrs) in self.hl_groups: + hl_group = { + 'ctermfg': 'NONE', + 'guifg': None, + 'ctermbg': 'NONE', + 'guibg': None, + 'attrs': ['NONE'], + 'name': '', + } + if fg is not None and fg is not False: + hl_group['ctermfg'] = fg[0] + hl_group['guifg'] = fg[1] + if bg is not None and bg is not False: + hl_group['ctermbg'] = bg[0] + hl_group['guibg'] = bg[1] + if attrs: + hl_group['attrs'] = [] + if attrs & ATTR_BOLD: + hl_group['attrs'].append('bold') + if attrs & ATTR_ITALIC: + hl_group['attrs'].append('italic') + if attrs & ATTR_UNDERLINE: + hl_group['attrs'].append('underline') + hl_group['name'] = ( + 'Pl_' + + str(hl_group['ctermfg']) + '_' + + str(hl_group['guifg']) + '_' + + str(hl_group['ctermbg']) + '_' + + str(hl_group['guibg']) + '_' + + ''.join(hl_group['attrs']) + ) + self.hl_groups[(fg, bg, attrs)] = hl_group + vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attrs} gui={attrs}'.format( + group=hl_group['name'], + ctermfg=hl_group['ctermfg'], + guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] is not None else 'NONE', + ctermbg=hl_group['ctermbg'], + guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE', + attrs=','.join(hl_group['attrs']), + )) + return '%#' + self.hl_groups[(fg, bg, attrs)]['name'] + '#' + + +renderer = VimRenderer diff --git a/powerline-bin/powerline/segment.py b/powerline-bin/powerline/segment.py new file mode 100644 index 0000000..c83bf6f --- /dev/null +++ b/powerline-bin/powerline/segment.py @@ -0,0 +1,450 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lib.watcher import create_file_watcher + + +def list_segment_key_values(segment, theme_configs, segment_data, key, function_name=None, name=None, module=None, default=None): + try: + yield segment[key] + except KeyError: + pass + found_module_key = False + for theme_config in theme_configs: + try: + segment_data = theme_config['segment_data'] + except KeyError: + pass + else: + if function_name and not name: + if module: + try: + yield segment_data[module + '.' + function_name][key] + found_module_key = True + except KeyError: + pass + if not found_module_key: + try: + yield segment_data[function_name][key] + except KeyError: + pass + if name: + try: + yield segment_data[name][key] + except KeyError: + pass + if segment_data is not None: + try: + yield segment_data[key] + except KeyError: + pass + yield default + + +def get_segment_key(merge, *args, **kwargs): + if merge: + ret = None + for value in list_segment_key_values(*args, **kwargs): + if ret is None: + ret = value + elif isinstance(ret, dict) and isinstance(value, dict): + old_ret = ret + ret = value.copy() + ret.update(old_ret) + else: + return ret + return ret + else: + return next(list_segment_key_values(*args, **kwargs)) + + +def get_function(data, segment): + function_name = segment['function'] + if '.' in function_name: + module, function_name = function_name.rpartition('.')[::2] + else: + module = data['default_module'] + function = data['get_module_attr'](module, function_name, prefix='segment_generator') + if not function: + raise ImportError('Failed to obtain segment function') + return None, function, module, function_name, segment.get('name') + + +def get_string(data, segment): + name = segment.get('name') + return data['get_key'](False, segment, None, None, name, 'contents'), None, None, None, name + + +segment_getters = { + 'function': get_function, + 'string': get_string, + 'segment_list': get_function, +} + + +def get_attr_func(contents_func, key, args, is_space_func=False): + try: + func = getattr(contents_func, key) + except AttributeError: + return None + else: + if is_space_func: + def expand_func(pl, amount, segment): + try: + return func(pl=pl, amount=amount, segment=segment, **args) + except Exception as e: + pl.exception('Exception while computing {0} function: {1}', key, str(e)) + return segment['contents'] + (' ' * amount) + return expand_func + else: + return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **args) + + +def process_segment_lister(pl, segment_info, parsed_segments, side, mode, colorscheme, + lister, subsegments, patcher_args): + subsegments = [ + subsegment + for subsegment in subsegments + if subsegment['display_condition'](pl, segment_info, mode) + ] + for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): + draw_inner_divider = subsegment_update.pop('draw_inner_divider', False) + old_pslen = len(parsed_segments) + for subsegment in subsegments: + if subsegment_update: + subsegment = subsegment.copy() + subsegment.update(subsegment_update) + if 'priority_multiplier' in subsegment_update and subsegment['priority']: + subsegment['priority'] *= subsegment_update['priority_multiplier'] + + process_segment( + pl, + side, + subsegment_info, + parsed_segments, + subsegment, + mode, + colorscheme, + ) + new_pslen = len(parsed_segments) + while parsed_segments[new_pslen - 1]['literal_contents'][1]: + new_pslen -= 1 + if new_pslen > old_pslen + 1 and draw_inner_divider is not None: + for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen): + parsed_segments[i]['draw_soft_divider'] = draw_inner_divider + return None + + +def set_segment_highlighting(pl, colorscheme, segment, mode): + if segment['literal_contents'][1]: + return True + try: + highlight_group_prefix = segment['highlight_group_prefix'] + except KeyError: + hl_groups = lambda hlgs: hlgs + else: + hl_groups = lambda hlgs: [highlight_group_prefix + ':' + hlg for hlg in hlgs] + hlgs + try: + segment['highlight'] = colorscheme.get_highlighting( + hl_groups(segment['highlight_groups']), + mode, + segment.get('gradient_level') + ) + if segment['divider_highlight_group']: + segment['divider_highlight'] = colorscheme.get_highlighting( + hl_groups([segment['divider_highlight_group']]), + mode + ) + else: + segment['divider_highlight'] = None + except Exception as e: + pl.exception('Failed to set highlight group: {0}', str(e)) + return False + else: + return True + + +def process_segment(pl, side, segment_info, parsed_segments, segment, mode, colorscheme): + segment = segment.copy() + pl.prefix = segment['name'] + if segment['type'] in ('function', 'segment_list'): + try: + if segment['type'] == 'function': + contents = segment['contents_func'](pl, segment_info) + else: + contents = segment['contents_func'](pl, segment_info, parsed_segments, side, mode, colorscheme) + except Exception as e: + pl.exception('Exception while computing segment: {0}', str(e)) + return + + if contents is None: + return + + if isinstance(contents, list): + # Needs copying here, but it was performed at the very start of the + # function + segment_base = segment + if contents: + draw_divider_position = -1 if side == 'left' else 0 + for key, i, newval in ( + ('before', 0, ''), + ('after', -1, ''), + ('draw_soft_divider', draw_divider_position, True), + ('draw_hard_divider', draw_divider_position, True), + ): + try: + contents[i][key] = segment_base.pop(key) + segment_base[key] = newval + except KeyError: + pass + + draw_inner_divider = None + if side == 'right': + append = parsed_segments.append + else: + pslen = len(parsed_segments) + append = lambda item: parsed_segments.insert(pslen, item) + + for subsegment in (contents if side == 'right' else reversed(contents)): + segment_copy = segment_base.copy() + segment_copy.update(subsegment) + if draw_inner_divider is not None: + segment_copy['draw_soft_divider'] = draw_inner_divider + draw_inner_divider = segment_copy.pop('draw_inner_divider', None) + if set_segment_highlighting(pl, colorscheme, segment_copy, mode): + append(segment_copy) + else: + segment['contents'] = contents + if set_segment_highlighting(pl, colorscheme, segment, mode): + parsed_segments.append(segment) + elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None): + if set_segment_highlighting(pl, colorscheme, segment, mode): + parsed_segments.append(segment) + + +always_true = lambda pl, segment_info, mode: True + +get_fallback_segment = { + 'name': 'fallback', + 'type': 'string', + 'highlight_groups': ['background'], + 'divider_highlight_group': None, + 'before': None, + 'after': None, + 'contents': '', + 'literal_contents': (0, ''), + 'priority': None, + 'draw_soft_divider': True, + 'draw_hard_divider': True, + 'draw_inner_divider': True, + 'display_condition': always_true, + 'width': None, + 'align': None, + 'expand': None, + 'truncate': None, + 'startup': None, + 'shutdown': None, + '_rendered_raw': '', + '_rendered_hl': '', + '_len': None, + '_contents_len': None, +}.copy + + +def gen_segment_getter(pl, ext, common_config, theme_configs, default_module, get_module_attr, top_theme): + data = { + 'default_module': default_module or 'powerline.segments.' + ext, + 'get_module_attr': get_module_attr, + 'segment_data': None, + } + + def get_key(merge, segment, module, function_name, name, key, default=None): + return get_segment_key(merge, segment, theme_configs, data['segment_data'], key, function_name, name, module, default) + data['get_key'] = get_key + + def get_selector(function_name): + if '.' in function_name: + module, function_name = function_name.rpartition('.')[::2] + else: + module = 'powerline.selectors.' + ext + function = get_module_attr(module, function_name, prefix='segment_generator/selector_function') + if not function: + pl.error('Failed to get segment selector, ignoring it') + return function + + def get_segment_selector(segment, selector_type): + try: + function_name = segment[selector_type + '_function'] + except KeyError: + function = None + else: + function = get_selector(function_name) + try: + modes = segment[selector_type + '_modes'] + except KeyError: + modes = None + + if modes: + if function: + return lambda pl, segment_info, mode: ( + mode in modes + or function(pl=pl, segment_info=segment_info, mode=mode) + ) + else: + return lambda pl, segment_info, mode: mode in modes + else: + if function: + return lambda pl, segment_info, mode: ( + function(pl=pl, segment_info=segment_info, mode=mode) + ) + else: + return None + + def gen_display_condition(segment): + include_function = get_segment_selector(segment, 'include') + exclude_function = get_segment_selector(segment, 'exclude') + if include_function: + if exclude_function: + return lambda *args: ( + include_function(*args) + and not exclude_function(*args)) + else: + return include_function + else: + if exclude_function: + return lambda *args: not exclude_function(*args) + else: + return always_true + + def get(segment, side): + segment_type = segment.get('type', 'function') + try: + get_segment_info = segment_getters[segment_type] + except KeyError: + pl.error('Unknown segment type: {0}', segment_type) + return None + + try: + contents, _contents_func, module, function_name, name = get_segment_info(data, segment) + except Exception as e: + pl.exception('Failed to generate segment from {0!r}: {1}', segment, str(e), prefix='segment_generator') + return None + + if not get_key(False, segment, module, function_name, name, 'display', True): + return None + + segment_datas = getattr(_contents_func, 'powerline_segment_datas', None) + if segment_datas: + try: + data['segment_data'] = segment_datas[top_theme] + except KeyError: + pass + + if segment_type == 'function': + highlight_groups = [function_name] + else: + highlight_groups = segment.get('highlight_groups') or [name] + + if segment_type in ('function', 'segment_list'): + args = dict(( + (str(k), v) + for k, v in + get_key(True, segment, module, function_name, name, 'args', {}).items() + )) + + display_condition = gen_display_condition(segment) + + if segment_type == 'segment_list': + # Handle startup and shutdown of _contents_func? + subsegments = [ + subsegment + for subsegment in ( + get(subsegment, side) + for subsegment in segment['segments'] + ) if subsegment + ] + return { + 'name': name or function_name, + 'type': segment_type, + 'highlight_groups': None, + 'divider_highlight_group': None, + 'before': None, + 'after': None, + 'contents_func': lambda pl, segment_info, parsed_segments, side, mode, colorscheme: ( + process_segment_lister( + pl, segment_info, parsed_segments, side, mode, colorscheme, + patcher_args=args, + subsegments=subsegments, + lister=_contents_func, + ) + ), + 'contents': None, + 'literal_contents': None, + 'priority': None, + 'draw_soft_divider': None, + 'draw_hard_divider': None, + 'draw_inner_divider': None, + 'side': side, + 'display_condition': display_condition, + 'width': None, + 'align': None, + 'expand': None, + 'truncate': None, + 'startup': None, + 'shutdown': None, + '_rendered_raw': '', + '_rendered_hl': '', + '_len': None, + '_contents_len': None, + } + + if segment_type == 'function': + startup_func = get_attr_func(_contents_func, 'startup', args) + shutdown_func = getattr(_contents_func, 'shutdown', None) + expand_func = get_attr_func(_contents_func, 'expand', args, True) + truncate_func = get_attr_func(_contents_func, 'truncate', args, True) + + if hasattr(_contents_func, 'powerline_requires_filesystem_watcher'): + create_watcher = lambda: create_file_watcher(pl, common_config['watcher']) + args[str('create_watcher')] = create_watcher + + if hasattr(_contents_func, 'powerline_requires_segment_info'): + contents_func = lambda pl, segment_info: _contents_func(pl=pl, segment_info=segment_info, **args) + else: + contents_func = lambda pl, segment_info: _contents_func(pl=pl, **args) + else: + startup_func = None + shutdown_func = None + contents_func = None + expand_func = None + truncate_func = None + + return { + 'name': name or function_name, + 'type': segment_type, + 'highlight_groups': highlight_groups, + 'divider_highlight_group': None, + 'before': get_key(False, segment, module, function_name, name, 'before', ''), + 'after': get_key(False, segment, module, function_name, name, 'after', ''), + 'contents_func': contents_func, + 'contents': contents, + 'literal_contents': (0, ''), + 'priority': segment.get('priority', None), + 'draw_hard_divider': segment.get('draw_hard_divider', True), + 'draw_soft_divider': segment.get('draw_soft_divider', True), + 'draw_inner_divider': segment.get('draw_inner_divider', False), + 'side': side, + 'display_condition': display_condition, + 'width': segment.get('width'), + 'align': segment.get('align', 'l'), + 'expand': expand_func, + 'truncate': truncate_func, + 'startup': startup_func, + 'shutdown': shutdown_func, + '_rendered_raw': '', + '_rendered_hl': '', + '_len': None, + '_contents_len': None, + } + + return get diff --git a/powerline-bin/powerline/segments/__init__.py b/powerline-bin/powerline/segments/__init__.py new file mode 100644 index 0000000..fa09e58 --- /dev/null +++ b/powerline-bin/powerline/segments/__init__.py @@ -0,0 +1,63 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from pkgutil import extend_path +from types import MethodType + + +__path__ = extend_path(__path__, __name__) + + +class Segment(object): + '''Base class for any segment that is not a function + + Required for powerline.lint.inspect to work properly: it defines methods for + omitting existing or adding new arguments. + + .. note:: + Until python-3.4 ``inspect.getargspec`` does not support querying + callable classes for arguments of their ``__call__`` method, requiring + to use this method directly (i.e. before 3.4 you should write + ``getargspec(obj.__call__)`` in place of ``getargspec(obj)``). + ''' + if sys.version_info < (3, 4): + def argspecobjs(self): + yield '__call__', self.__call__ + else: + def argspecobjs(self): + yield '__call__', self + + argspecobjs.__doc__ = ( + '''Return a list of valid arguments for inspect.getargspec + + Used to determine function arguments. + ''' + ) + + def omitted_args(self, name, method): + '''List arguments which should be omitted + + Returns a tuple with indexes of omitted arguments. + + .. note::``segment_info``, ``create_watcher`` and ``pl`` will be omitted + regardless of the below return (for ``segment_info`` and + ``create_watcher``: only if object was marked to require segment + info or filesystem watcher). + ''' + if isinstance(self.__call__, MethodType): + return (0,) + else: + return () + + @staticmethod + def additional_args(): + '''Returns a list of (additional argument name[, default value]) tuples. + ''' + return () + + +def with_docstring(instance, doc): + instance.__doc__ = doc + return instance diff --git a/powerline-bin/powerline/segments/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/segments/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c68b52c Binary files /dev/null and b/powerline-bin/powerline/segments/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/__pycache__/i3wm.cpython-312.pyc b/powerline-bin/powerline/segments/__pycache__/i3wm.cpython-312.pyc new file mode 100644 index 0000000..737f587 Binary files /dev/null and b/powerline-bin/powerline/segments/__pycache__/i3wm.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/__pycache__/ipython.cpython-312.pyc b/powerline-bin/powerline/segments/__pycache__/ipython.cpython-312.pyc new file mode 100644 index 0000000..dfc79f8 Binary files /dev/null and b/powerline-bin/powerline/segments/__pycache__/ipython.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/__pycache__/pdb.cpython-312.pyc b/powerline-bin/powerline/segments/__pycache__/pdb.cpython-312.pyc new file mode 100644 index 0000000..59da837 Binary files /dev/null and b/powerline-bin/powerline/segments/__pycache__/pdb.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/__pycache__/shell.cpython-312.pyc b/powerline-bin/powerline/segments/__pycache__/shell.cpython-312.pyc new file mode 100644 index 0000000..dc1c0a6 Binary files /dev/null and b/powerline-bin/powerline/segments/__pycache__/shell.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/__pycache__/tmux.cpython-312.pyc b/powerline-bin/powerline/segments/__pycache__/tmux.cpython-312.pyc new file mode 100644 index 0000000..18c97c4 Binary files /dev/null and b/powerline-bin/powerline/segments/__pycache__/tmux.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__init__.py b/powerline-bin/powerline/segments/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/segments/common/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0ff4988 Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/bat.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/bat.cpython-312.pyc new file mode 100644 index 0000000..5cc703f Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/bat.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/env.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000..bef466f Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/env.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/mail.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/mail.cpython-312.pyc new file mode 100644 index 0000000..f60be9a Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/mail.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/net.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/net.cpython-312.pyc new file mode 100644 index 0000000..e9ffbfa Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/net.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/players.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/players.cpython-312.pyc new file mode 100644 index 0000000..4965a0d Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/players.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/sys.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/sys.cpython-312.pyc new file mode 100644 index 0000000..bcc6763 Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/sys.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/time.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/time.cpython-312.pyc new file mode 100644 index 0000000..070f463 Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/time.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/vcs.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/vcs.cpython-312.pyc new file mode 100644 index 0000000..af387ff Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/vcs.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/__pycache__/wthr.cpython-312.pyc b/powerline-bin/powerline/segments/common/__pycache__/wthr.cpython-312.pyc new file mode 100644 index 0000000..053f92c Binary files /dev/null and b/powerline-bin/powerline/segments/common/__pycache__/wthr.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/common/bat.py b/powerline-bin/powerline/segments/common/bat.py new file mode 100644 index 0000000..c892f62 --- /dev/null +++ b/powerline-bin/powerline/segments/common/bat.py @@ -0,0 +1,302 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import sys +import re + +from powerline.lib.shell import run_cmd + + +def _fetch_battery_info(pl): + try: + import dbus + except ImportError: + pl.debug('Not using DBUS+UPower as dbus is not available') + else: + try: + bus = dbus.SystemBus() + except Exception as e: + pl.exception('Failed to connect to system bus: {0}', str(e)) + else: + interface = 'org.freedesktop.UPower' + try: + up = bus.get_object(interface, '/org/freedesktop/UPower') + except dbus.exceptions.DBusException as e: + if getattr(e, '_dbus_error_name', '').endswith('ServiceUnknown'): + pl.debug('Not using DBUS+UPower as UPower is not available via dbus') + else: + pl.exception('Failed to get UPower service with dbus: {0}', str(e)) + else: + devinterface = 'org.freedesktop.DBus.Properties' + devtype_name = interface + '.Device' + devices = [] + for devpath in up.EnumerateDevices(dbus_interface=interface): + dev = bus.get_object(interface, devpath) + devget = lambda what: dev.Get( + devtype_name, + what, + dbus_interface=devinterface + ) + if int(devget('Type')) != 2: + pl.debug('Not using DBUS+UPower with {0}: invalid type', devpath) + continue + if not bool(devget('IsPresent')): + pl.debug('Not using DBUS+UPower with {0}: not present', devpath) + continue + if not bool(devget('PowerSupply')): + pl.debug('Not using DBUS+UPower with {0}: not a power supply', devpath) + continue + devices.append(devpath) + pl.debug('Using DBUS+UPower with {0}', devpath) + if devices: + def _flatten_battery(pl): + energy = 0.0 + energy_full = 0.0 + state = True + for devpath in devices: + dev = bus.get_object(interface, devpath) + energy_full += float( + dbus.Interface(dev, dbus_interface=devinterface).Get( + devtype_name, + 'EnergyFull' + ), + ) + energy += float( + dbus.Interface(dev, dbus_interface=devinterface).Get( + devtype_name, + 'Energy' + ), + ) + state &= dbus.Interface(dev, dbus_interface=devinterface).Get( + devtype_name, + 'State' + ) != 2 + if energy_full > 0: + return (energy * 100.0 / energy_full), state + else: + return 0.0, state + return _flatten_battery + pl.debug('Not using DBUS+UPower as no batteries were found') + + if os.path.isdir('/sys/class/power_supply'): + # ENERGY_* attributes represents capacity in µWh only. + # CHARGE_* attributes represents capacity in µAh only. + linux_capacity_units = ('energy', 'charge') + linux_energy_full_fmt = '/sys/class/power_supply/{0}/{1}_full' + linux_energy_fmt = '/sys/class/power_supply/{0}/{1}_now' + linux_status_fmt = '/sys/class/power_supply/{0}/status' + devices = [] + for linux_supplier in os.listdir('/sys/class/power_supply'): + for unit in linux_capacity_units: + energy_path = linux_energy_fmt.format(linux_supplier, unit) + if not os.path.exists(energy_path): + continue + pl.debug('Using /sys/class/power_supply with battery {0} and unit {1}', + linux_supplier, unit) + devices.append((linux_supplier, unit)) + break # energy or charge, not both + if devices: + def _get_battery_status(pl): + energy = 0.0 + energy_full = 0.0 + state = True + for device, unit in devices: + with open(linux_energy_full_fmt.format(device, unit), 'r') as f: + energy_full += int(float(f.readline().split()[0])) + with open(linux_energy_fmt.format(device, unit), 'r') as f: + energy += int(float(f.readline().split()[0])) + try: + with open(linux_status_fmt.format(device), 'r') as f: + state &= (f.readline().strip() != 'Discharging') + except IOError: + state = None + return (energy * 100.0 / energy_full), state + return _get_battery_status + pl.debug('Not using /sys/class/power_supply as no batteries were found') + else: + pl.debug("Checking for first capacity battery percentage") + for batt in os.listdir('/sys/class/power_supply'): + if os.path.exists('/sys/class/power_supply/{0}/capacity'.format(batt)): + def _get_battery_perc(pl): + state = True + with open('/sys/class/power_supply/{0}/capacity'.format(batt), 'r') as f: + perc = int(f.readline().split()[0]) + try: + with open(linux_status_fmt.format(batt), 'r') as f: + state &= (f.readline().strip() != 'Discharging') + except IOError: + state = None + return perc, state + return _get_battery_perc + else: + pl.debug('Not using /sys/class/power_supply: no directory') + + try: + from shutil import which # Python-3.3 and later + except ImportError: + pl.info('Using dumb “which” which only checks for file in /usr/bin') + which = lambda f: (lambda fp: os.path.exists(fp) and fp)(os.path.join('/usr/bin', f)) + + if which('pmset'): + pl.debug('Using pmset') + + BATTERY_PERCENT_RE = re.compile(r'(\d+)%') + + def _get_battery_status(pl): + battery_summary = run_cmd(pl, ['pmset', '-g', 'batt']) + battery_percent = BATTERY_PERCENT_RE.search(battery_summary).group(1) + ac_charging = 'AC' in battery_summary + return int(battery_percent), ac_charging + return _get_battery_status + else: + pl.debug('Not using pmset: executable not found') + + if sys.platform.startswith('win') or sys.platform == 'cygwin': + # From http://stackoverflow.com/a/21083571/273566, reworked + try: + from win32com.client import GetObject + except ImportError: + pl.debug('Not using win32com.client as it is not available') + else: + try: + wmi = GetObject('winmgmts:') + except Exception as e: + pl.exception('Failed to run GetObject from win32com.client: {0}', str(e)) + else: + for battery in wmi.InstancesOf('Win32_Battery'): + pl.debug('Using win32com.client with Win32_Battery') + + def _get_battery_status(pl): + # http://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx + return battery.EstimatedChargeRemaining, battery.BatteryStatus == 6 + + return _get_battery_status + pl.debug('Not using win32com.client as no batteries were found') + from ctypes import Structure, c_byte, c_ulong, byref + if sys.platform == 'cygwin': + pl.debug('Using cdll to communicate with kernel32 (Cygwin)') + from ctypes import cdll + library_loader = cdll + else: + pl.debug('Using windll to communicate with kernel32 (Windows)') + from ctypes import windll + library_loader = windll + + class PowerClass(Structure): + _fields_ = [ + ('ACLineStatus', c_byte), + ('BatteryFlag', c_byte), + ('BatteryLifePercent', c_byte), + ('Reserved1', c_byte), + ('BatteryLifeTime', c_ulong), + ('BatteryFullLifeTime', c_ulong) + ] + + def _get_battery_status(pl): + powerclass = PowerClass() + result = library_loader.kernel32.GetSystemPowerStatus(byref(powerclass)) + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa372693(v=vs.85).aspx + if result: + return None + return powerclass.BatteryLifePercent, powerclass.ACLineStatus == 1 + + if _get_battery_status() is None: + pl.debug('Not using GetSystemPowerStatus because it failed') + else: + pl.debug('Using GetSystemPowerStatus') + + return _get_battery_status + + raise NotImplementedError + + +def _get_battery_status(pl): + global _get_battery_status + + def _failing_get_status(pl): + raise NotImplementedError + + try: + _get_battery_status = _fetch_battery_info(pl) + except NotImplementedError: + _get_battery_status = _failing_get_status + except Exception as e: + pl.exception('Exception while obtaining battery status: {0}', str(e)) + _get_battery_status = _failing_get_status + return _get_battery_status(pl) + + +def battery(pl, format='{ac_state} {capacity:3.0%}', steps=5, gamify=False, full_heart='O', empty_heart='O', online='C', offline=' '): + '''Return battery charge status. + + :param str format: + Percent format in case gamify is False. Format arguments: ``ac_state`` + which is equal to either ``online`` or ``offline`` string arguments and + ``capacity`` which is equal to current battery capacity in interval [0, + 100]. + :param int steps: + Number of discrete steps to show between 0% and 100% capacity if gamify + is True. + :param bool gamify: + Measure in hearts (♥) instead of percentages. For full hearts + ``battery_full`` highlighting group is preferred, for empty hearts there + is ``battery_empty``. ``battery_online`` or ``battery_offline`` group + will be used for leading segment containing ``online`` or ``offline`` + argument contents. + :param str full_heart: + Heart displayed for “full” part of battery. + :param str empty_heart: + Heart displayed for “used” part of battery. It is also displayed using + another gradient level and highlighting group, so it is OK for it to be + the same as full_heart as long as necessary highlighting groups are + defined. + :param str online: + Symbol used if computer is connected to a power supply. + :param str offline: + Symbol used if computer is not connected to a power supply. + + ``battery_gradient`` and ``battery`` groups are used in any case, first is + preferred. + + Highlight groups used: ``battery_full`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_empty`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_online`` or ``battery_ac_state`` or ``battery_gradient`` (gradient) or ``battery``, ``battery_offline`` or ``battery_ac_state`` or ``battery_gradient`` (gradient) or ``battery``. + ''' + try: + capacity, ac_powered = _get_battery_status(pl) + except NotImplementedError: + pl.info('Unable to get battery status.') + return None + + ret = [] + if gamify: + denom = int(steps) + numer = int(denom * capacity / 100) + ret.append({ + 'contents': online if ac_powered else offline, + 'draw_inner_divider': False, + 'highlight_groups': ['battery_online' if ac_powered else 'battery_offline', 'battery_ac_state', 'battery_gradient', 'battery'], + 'gradient_level': 0, + }) + ret.append({ + 'contents': full_heart * numer, + 'draw_inner_divider': False, + 'highlight_groups': ['battery_full', 'battery_gradient', 'battery'], + # Using zero as “nothing to worry about”: it is least alert color. + 'gradient_level': 0, + }) + ret.append({ + 'contents': empty_heart * (denom - numer), + 'draw_inner_divider': False, + 'highlight_groups': ['battery_empty', 'battery_gradient', 'battery'], + # Using a hundred as it is most alert color. + 'gradient_level': 100, + }) + else: + ret.append({ + 'contents': format.format(ac_state=(online if ac_powered else offline), capacity=(capacity / 100.0)), + 'highlight_groups': ['battery_gradient', 'battery'], + # Gradients are “least alert – most alert” by default, capacity has + # the opposite semantics. + 'gradient_level': 100 - capacity, + }) + return ret diff --git a/powerline-bin/powerline/segments/common/env.py b/powerline-bin/powerline/segments/common/env.py new file mode 100644 index 0000000..61f516e --- /dev/null +++ b/powerline-bin/powerline/segments/common/env.py @@ -0,0 +1,197 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from powerline.lib.unicode import out_u +from powerline.theme import requires_segment_info +from powerline.segments import Segment, with_docstring + + +@requires_segment_info +def environment(pl, segment_info, variable=None): + '''Return the value of any defined environment variable + + :param string variable: + The environment variable to return if found + ''' + return segment_info['environ'].get(variable, None) + + +@requires_segment_info +def virtualenv(pl, segment_info, ignore_venv=False, ignore_conda=False): + '''Return the name of the current Python or conda virtualenv. + + :param bool ignore_venv: + Whether to ignore virtual environments. Default is False. + :param bool ignore_conda: + Whether to ignore conda environments. Default is False. + ''' + return ( + (not ignore_venv and + os.path.basename(segment_info['environ'].get('VIRTUAL_ENV', ''))) or + (not ignore_conda and + segment_info['environ'].get('CONDA_DEFAULT_ENV', '')) or + None) + + +@requires_segment_info +class CwdSegment(Segment): + def argspecobjs(self): + for obj in super(CwdSegment, self).argspecobjs(): + yield obj + yield 'get_shortened_path', self.get_shortened_path + + def omitted_args(self, name, method): + if method is self.get_shortened_path: + return () + else: + return super(CwdSegment, self).omitted_args(name, method) + + def get_shortened_path(self, pl, segment_info, shorten_home=True, **kwargs): + try: + path = out_u(segment_info['getcwd']()) + except OSError as e: + if e.errno == 2: + # user most probably deleted the directory + # this happens when removing files from Mercurial repos for example + pl.warn('Current directory not found') + return '[not found]' + else: + raise + if shorten_home: + home = segment_info['home'] + if home: + home = out_u(home) + if path.startswith(home): + path = '~' + path[len(home):] + return path + + def __call__(self, pl, segment_info, + dir_shorten_len=None, + dir_limit_depth=None, + use_path_separator=False, + ellipsis='...', + **kwargs): + cwd = self.get_shortened_path(pl, segment_info, **kwargs) + cwd_split = cwd.split(os.sep) + cwd_split_len = len(cwd_split) + cwd = [i[0:dir_shorten_len] if dir_shorten_len and i else i for i in cwd_split[:-1]] + [cwd_split[-1]] + if dir_limit_depth and cwd_split_len > dir_limit_depth + 1: + del(cwd[0:-dir_limit_depth]) + if ellipsis is not None: + cwd.insert(0, ellipsis) + ret = [] + if not cwd[0]: + cwd[0] = '/' + draw_inner_divider = not use_path_separator + for part in cwd: + if not part: + continue + if use_path_separator: + part += os.sep + ret.append({ + 'contents': part, + 'divider_highlight_group': 'cwd:divider', + 'draw_inner_divider': draw_inner_divider, + }) + ret[-1]['highlight_groups'] = ['cwd:current_folder', 'cwd'] + if use_path_separator: + ret[-1]['contents'] = ret[-1]['contents'][:-1] + if len(ret) > 1 and ret[0]['contents'][0] == os.sep: + ret[0]['contents'] = ret[0]['contents'][1:] + return ret + + +cwd = with_docstring(CwdSegment(), +'''Return the current working directory. + +Returns a segment list to create a breadcrumb-like effect. + +:param int dir_shorten_len: + shorten parent directory names to this length (e.g. + :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) +:param int dir_limit_depth: + limit directory depth to this number (e.g. + :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) +:param bool use_path_separator: + Use path separator in place of soft divider. +:param bool shorten_home: + Shorten home directory to ``~``. +:param str ellipsis: + Specifies what to use in place of omitted directories. Use None to not + show this subsegment at all. + +Divider highlight group used: ``cwd:divider``. + +Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. +''') + + +try: + import psutil + + # psutil-2.0.0: psutil.Process.username is unbound method + if callable(psutil.Process.username): + def _get_user(): + return psutil.Process(os.getpid()).username() + # pre psutil-2.0.0: psutil.Process.username has type property + else: + def _get_user(): + return psutil.Process(os.getpid()).username +except ImportError: + try: + import pwd + except ImportError: + from getpass import getuser as _get_user + else: + try: + from os import geteuid as getuid + except ImportError: + from os import getuid + + def _get_user(): + return pwd.getpwuid(getuid()).pw_name + + +username = False +# os.geteuid is not available on windows +_geteuid = getattr(os, 'geteuid', lambda: 1) + + +@requires_segment_info +def user(pl, segment_info, hide_user=None, hide_domain=False): + '''Return the current user. + + :param str hide_user: + Omit showing segment for users with names equal to this string. + :param bool hide_domain: + Drop domain component if it exists in a username (delimited by '@'). + + Highlights the user with the ``superuser`` if the effective user ID is 0. + + Highlight groups used: ``superuser`` or ``user``. It is recommended to define all highlight groups. + ''' + global username + if ( + segment_info['environ'].get('_POWERLINE_RUNNING_SHELL_TESTS') + == 'ee5bcdc6-b749-11e7-9456-50465d597777' + ): + return 'user' + if username is False: + username = _get_user() + if username is None: + pl.warn('Failed to get username') + return None + if username == hide_user: + return None + if hide_domain: + try: + username = username[:username.index('@')] + except ValueError: + pass + euid = _geteuid() + return [{ + 'contents': username, + 'highlight_groups': ['user'] if euid != 0 else ['superuser', 'user'], + }] diff --git a/powerline-bin/powerline/segments/common/mail.py b/powerline-bin/powerline/segments/common/mail.py new file mode 100644 index 0000000..8202492 --- /dev/null +++ b/powerline-bin/powerline/segments/common/mail.py @@ -0,0 +1,78 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re + +from imaplib import IMAP4_SSL_PORT, IMAP4_SSL, IMAP4 +from collections import namedtuple + +from powerline.lib.threaded import KwThreadedSegment +from powerline.segments import with_docstring + + +_IMAPKey = namedtuple('Key', 'username password server port folder use_ssl') + + +class EmailIMAPSegment(KwThreadedSegment): + interval = 60 + + @staticmethod + def key(username, password, server='imap.gmail.com', port=IMAP4_SSL_PORT, folder='INBOX', use_ssl=None, **kwargs): + if use_ssl is None: + use_ssl = (port == IMAP4_SSL_PORT) + return _IMAPKey(username, password, server, port, folder, use_ssl) + + def compute_state(self, key): + if not key.username or not key.password: + self.warn('Username and password are not configured') + return None + if key.use_ssl: + mail = IMAP4_SSL(key.server, key.port) + else: + mail = IMAP4(key.server, key.port) + mail.login(key.username, key.password) + rc, message = mail.status(key.folder, '(UNSEEN)') + unread_str = message[0].decode('utf-8') + unread_count = int(re.search('UNSEEN (\d+)', unread_str).group(1)) + return unread_count + + @staticmethod + def render_one(unread_count, max_msgs=None, **kwargs): + if not unread_count: + return None + elif type(unread_count) != int or not max_msgs: + return [{ + 'contents': str(unread_count), + 'highlight_groups': ['email_alert'], + }] + else: + return [{ + 'contents': str(unread_count), + 'highlight_groups': ['email_alert_gradient', 'email_alert'], + 'gradient_level': min(unread_count * 100.0 / max_msgs, 100), + }] + + +email_imap_alert = with_docstring(EmailIMAPSegment(), +('''Return unread e-mail count for IMAP servers. + +:param str username: + login username +:param str password: + login password +:param str server: + e-mail server +:param int port: + e-mail server port +:param str folder: + folder to check for e-mails +:param int max_msgs: + Maximum number of messages. If there are more messages then max_msgs then it + will use gradient level equal to 100, otherwise gradient level is equal to + ``100 * msgs_num / max_msgs``. If not present gradient is not computed. +:param bool use_ssl: + If ``True`` then use SSL connection. If ``False`` then do not use it. + Default is ``True`` if port is equal to {ssl_port} and ``False`` otherwise. + +Highlight groups used: ``email_alert_gradient`` (gradient), ``email_alert``. +''').format(ssl_port=IMAP4_SSL_PORT)) diff --git a/powerline-bin/powerline/segments/common/net.py b/powerline-bin/powerline/segments/common/net.py new file mode 100644 index 0000000..b5d9062 --- /dev/null +++ b/powerline-bin/powerline/segments/common/net.py @@ -0,0 +1,315 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re +import os +import socket + +from powerline.lib.url import urllib_read +from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment +from powerline.lib.monotonic import monotonic +from powerline.lib.humanize_bytes import humanize_bytes +from powerline.segments import with_docstring +from powerline.theme import requires_segment_info + + +@requires_segment_info +def hostname(pl, segment_info, only_if_ssh=False, exclude_domain=False): + '''Return the current hostname. + + :param bool only_if_ssh: + only return the hostname if currently in an SSH session + :param bool exclude_domain: + return the hostname without domain if there is one + ''' + if ( + segment_info['environ'].get('_POWERLINE_RUNNING_SHELL_TESTS') + == 'ee5bcdc6-b749-11e7-9456-50465d597777' + ): + return 'hostname' + if only_if_ssh and not segment_info['environ'].get('SSH_CLIENT'): + return None + if exclude_domain: + return socket.gethostname().split('.')[0] + return socket.gethostname() + + +def _external_ip(query_url='http://ipv4.icanhazip.com/'): + return urllib_read(query_url).strip() + + +class ExternalIpSegment(ThreadedSegment): + interval = 300 + + def set_state(self, query_url='http://ipv4.icanhazip.com/', **kwargs): + self.query_url = query_url + super(ExternalIpSegment, self).set_state(**kwargs) + + def update(self, old_ip): + return _external_ip(query_url=self.query_url) + + def render(self, ip, **kwargs): + if not ip: + return None + return [{'contents': ip, 'divider_highlight_group': 'background:divider'}] + + +external_ip = with_docstring(ExternalIpSegment(), +'''Return external IP address. + +:param str query_url: + URI to query for IP address, should return only the IP address as a text string + + Suggested URIs: + + * http://ipv4.icanhazip.com/ + * http://ipv6.icanhazip.com/ + * http://icanhazip.com/ (returns IPv6 address if available, else IPv4) + +Divider highlight group used: ``background:divider``. +''') + + +try: + import netifaces +except ImportError: + def internal_ip(pl, interface='auto', ipv=4): + return None +else: + _interface_starts = { + 'eth': 10, # Regular ethernet adapters : eth1 + 'enp': 10, # Regular ethernet adapters, Gentoo : enp2s0 + 'en': 10, # OS X : en0 + 'ath': 9, # Atheros WiFi adapters : ath0 + 'wlan': 9, # Other WiFi adapters : wlan1 + 'wlp': 9, # Other WiFi adapters, Gentoo : wlp5s0 + 'teredo': 1, # miredo interface : teredo + 'lo': -10, # Loopback interface : lo + 'docker': -5, # Docker bridge interface : docker0 + 'vmnet': -5, # VMWare bridge interface : vmnet1 + 'vboxnet': -5, # VirtualBox bridge interface : vboxnet0 + } + + _interface_start_re = re.compile(r'^([a-z]+?)(\d|$)') + + def _interface_key(interface): + match = _interface_start_re.match(interface) + if match: + try: + base = _interface_starts[match.group(1)] * 100 + except KeyError: + base = 500 + if match.group(2): + return base - int(match.group(2)) + else: + return base + else: + return 0 + + def internal_ip(pl, interface='auto', ipv=4): + family = netifaces.AF_INET6 if ipv == 6 else netifaces.AF_INET + if interface == 'auto': + try: + interface = next(iter(sorted(netifaces.interfaces(), key=_interface_key, reverse=True))) + except StopIteration: + pl.info('No network interfaces found') + return None + elif interface == 'default_gateway': + try: + interface = netifaces.gateways()['default'][family][1] + except KeyError: + pl.info('No default gateway found for IPv{0}', ipv) + return None + addrs = netifaces.ifaddresses(interface) + try: + return addrs[family][0]['addr'] + except (KeyError, IndexError): + pl.info("No IPv{0} address found for interface {1}", ipv, interface) + return None + + +internal_ip = with_docstring(internal_ip, +'''Return internal IP address + +Requires ``netifaces`` module to work properly. + +:param str interface: + Interface on which IP will be checked. Use ``auto`` to automatically + detect interface. In this case interfaces with lower numbers will be + preferred over interfaces with similar names. Order of preference based on + names: + + #. ``eth`` and ``enp`` followed by number or the end of string. + #. ``ath``, ``wlan`` and ``wlp`` followed by number or the end of string. + #. ``teredo`` followed by number or the end of string. + #. Any other interface that is not ``lo*``. + #. ``lo`` followed by number or the end of string. + + Use ``default_gateway`` to detect the interface based on the machine's + `default gateway `_ (i.e., + the router to which it is connected). + +:param int ipv: + 4 or 6 for ipv4 and ipv6 respectively, depending on which IP address you + need exactly. +''') + + +try: + import psutil + + def _get_bytes(interface): + try: + io_counters = psutil.net_io_counters(pernic=True) + except AttributeError: + io_counters = psutil.network_io_counters(pernic=True) + if_io = io_counters.get(interface) + if not if_io: + return None + return if_io.bytes_recv, if_io.bytes_sent + + def _get_interfaces(): + try: + io_counters = psutil.net_io_counters(pernic=True) + except AttributeError: + io_counters = psutil.network_io_counters(pernic=True) + for interface, data in io_counters.items(): + if data: + yield interface, data.bytes_recv, data.bytes_sent +except ImportError: + def _get_bytes(interface): + with open('/sys/class/net/{interface}/statistics/rx_bytes'.format(interface=interface), 'rb') as file_obj: + rx = int(file_obj.read()) + with open('/sys/class/net/{interface}/statistics/tx_bytes'.format(interface=interface), 'rb') as file_obj: + tx = int(file_obj.read()) + return (rx, tx) + + def _get_interfaces(): + for interface in os.listdir('/sys/class/net'): + x = _get_bytes(interface) + if x is not None: + yield interface, x[0], x[1] + + +class NetworkLoadSegment(KwThreadedSegment): + interfaces = {} + replace_num_pat = re.compile(r'[a-zA-Z]+') + + @staticmethod + def key(interface='auto', **kwargs): + return interface + + def compute_state(self, interface): + if interface == 'auto': + proc_exists = getattr(self, 'proc_exists', None) + if proc_exists is None: + proc_exists = self.proc_exists = os.path.exists('/proc/net/route') + if proc_exists: + # Look for default interface in routing table + with open('/proc/net/route', 'rb') as f: + for line in f.readlines(): + parts = line.split() + if len(parts) > 1: + iface, destination = parts[:2] + if not destination.replace(b'0', b''): + interface = iface.decode('utf-8') + break + if interface == 'auto': + # Choose interface with most total activity, excluding some + # well known interface names + interface, total = 'eth0', -1 + for name, rx, tx in _get_interfaces(): + base = self.replace_num_pat.match(name) + if None in (base, rx, tx) or base.group() in ('lo', 'vmnet', 'sit'): + continue + activity = rx + tx + if activity > total: + total = activity + interface = name + + try: + idata = self.interfaces[interface] + try: + idata['prev'] = idata['last'] + except KeyError: + pass + except KeyError: + idata = {} + if self.run_once: + idata['prev'] = (monotonic(), _get_bytes(interface)) + self.shutdown_event.wait(self.interval) + self.interfaces[interface] = idata + + idata['last'] = (monotonic(), _get_bytes(interface)) + return idata.copy() + + def render_one(self, idata, recv_format='DL {value:>8}', sent_format='UL {value:>8}', suffix='B/s', si_prefix=False, **kwargs): + if not idata or 'prev' not in idata: + return None + + t1, b1 = idata['prev'] + t2, b2 = idata['last'] + measure_interval = t2 - t1 + + if None in (b1, b2): + return None + + r = [] + for i, key in zip((0, 1), ('recv', 'sent')): + format = locals()[key + '_format'] + try: + value = (b2[i] - b1[i]) / measure_interval + except ZeroDivisionError: + self.warn('Measure interval zero.') + value = 0 + max_key = key + '_max' + is_gradient = max_key in kwargs + hl_groups = ['network_load_' + key, 'network_load'] + if is_gradient: + hl_groups[:0] = (group + '_gradient' for group in hl_groups) + r.append({ + 'contents': format.format(value=humanize_bytes(value, suffix, si_prefix)), + 'divider_highlight_group': 'network_load:divider', + 'highlight_groups': hl_groups, + }) + if is_gradient: + max = kwargs[max_key] + if value >= max: + r[-1]['gradient_level'] = 100 + else: + r[-1]['gradient_level'] = value * 100.0 / max + + return r + + +network_load = with_docstring(NetworkLoadSegment(), +'''Return the network load. + +Uses the ``psutil`` module if available for multi-platform compatibility, +falls back to reading +:file:`/sys/class/net/{interface}/statistics/{rx,tx}_bytes`. + +:param str interface: + Network interface to measure (use the special value "auto" to have powerline + try to auto-detect the network interface). +:param str suffix: + String appended to each load string. +:param bool si_prefix: + Use SI prefix, e.g. MB instead of MiB. +:param str recv_format: + Format string that determines how download speed should look like. Receives + ``value`` as argument. +:param str sent_format: + Format string that determines how upload speed should look like. Receives + ``value`` as argument. +:param float recv_max: + Maximum number of received bytes per second. Is only used to compute + gradient level. +:param float sent_max: + Maximum number of sent bytes per second. Is only used to compute gradient + level. + +Divider highlight group used: ``network_load:divider``. + +Highlight groups used: ``network_load_sent_gradient`` (gradient) or ``network_load_recv_gradient`` (gradient) or ``network_load_gradient`` (gradient), ``network_load_sent`` or ``network_load_recv`` or ``network_load``. +''') diff --git a/powerline-bin/powerline/segments/common/players.py b/powerline-bin/powerline/segments/common/players.py new file mode 100644 index 0000000..9298bc9 --- /dev/null +++ b/powerline-bin/powerline/segments/common/players.py @@ -0,0 +1,607 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys + +from powerline.lib.shell import asrun, run_cmd +from powerline.lib.unicode import out_u +from powerline.segments import Segment, with_docstring + + +STATE_SYMBOLS = { + 'fallback': '', + 'play': '>', + 'pause': '~', + 'stop': 'X', +} + + +def _convert_state(state): + '''Guess player state''' + state = state.lower() + if 'play' in state: + return 'play' + if 'pause' in state: + return 'pause' + if 'stop' in state: + return 'stop' + return 'fallback' + + +def _convert_seconds(seconds): + '''Convert seconds to minutes:seconds format''' + return '{0:.0f}:{1:02.0f}'.format(*divmod(float(seconds), 60)) + + +class PlayerSegment(Segment): + def __call__(self, format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs): + stats = { + 'state': 'fallback', + 'album': None, + 'artist': None, + 'title': None, + 'elapsed': None, + 'total': None, + } + func_stats = self.get_player_status(**kwargs) + if not func_stats: + return None + stats.update(func_stats) + stats['state_symbol'] = state_symbols.get(stats['state']) + return [{ + 'contents': format.format(**stats), + 'highlight_groups': ['player_' + (stats['state'] or 'fallback'), 'player'], + }] + + def get_player_status(self, pl): + pass + + def argspecobjs(self): + for ret in super(PlayerSegment, self).argspecobjs(): + yield ret + yield 'get_player_status', self.get_player_status + + def omitted_args(self, name, method): + return () + + +_common_args = ''' +This player segment should be added like this: + +.. code-block:: json + + {{ + "function": "powerline.segments.common.players.{0}", + "name": "player" + }} + +(with additional ``"args": {{…}}`` if needed). + +Highlight groups used: ``player_fallback`` or ``player``, ``player_play`` or ``player``, ``player_pause`` or ``player``, ``player_stop`` or ``player``. + +:param str format: + Format used for displaying data from player. Should be a str.format-like + string with the following keyword parameters: + + +------------+-------------------------------------------------------------+ + |Parameter |Description | + +============+=============================================================+ + |state_symbol|Symbol displayed for play/pause/stop states. There is also | + | |“fallback” state used in case function failed to get player | + | |state. For this state symbol is by default empty. All | + | |symbols are defined in ``state_symbols`` argument. | + +------------+-------------------------------------------------------------+ + |album |Album that is currently played. | + +------------+-------------------------------------------------------------+ + |artist |Artist whose song is currently played | + +------------+-------------------------------------------------------------+ + |title |Currently played composition. | + +------------+-------------------------------------------------------------+ + |elapsed |Composition duration in format M:SS (minutes:seconds). | + +------------+-------------------------------------------------------------+ + |total |Composition length in format M:SS. | + +------------+-------------------------------------------------------------+ +:param dict state_symbols: + Symbols used for displaying state. Must contain all of the following keys: + + ======== ======================================================== + Key Description + ======== ======================================================== + play Displayed when player is playing. + pause Displayed when player is paused. + stop Displayed when player is not playing anything. + fallback Displayed if state is not one of the above or not known. + ======== ======================================================== +''' + + +_player = with_docstring(PlayerSegment(), _common_args.format('_player')) + + +class CmusPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + '''Return cmus player information. + + cmus-remote -Q returns data with multi-level information i.e. + status playing + file + tag artist + tag title + tag .. + tag n + set continue + set repeat + set .. + set n + + For the information we are looking for we don’t really care if we’re on + the tag level or the set level. The dictionary comprehension in this + method takes anything in ignore_levels and brings the key inside that + to the first level of the dictionary. + ''' + now_playing_str = run_cmd(pl, ['cmus-remote', '-Q']) + if not now_playing_str: + return + ignore_levels = ('tag', 'set',) + now_playing = dict(((token[0] if token[0] not in ignore_levels else token[1], + (' '.join(token[1:]) if token[0] not in ignore_levels else + ' '.join(token[2:]))) for token in [line.split(' ') for line in now_playing_str.split('\n')[:-1]])) + state = _convert_state(now_playing.get('status')) + return { + 'state': state, + 'album': now_playing.get('album'), + 'artist': now_playing.get('artist'), + 'title': now_playing.get('title'), + 'elapsed': _convert_seconds(now_playing.get('position', 0)), + 'total': _convert_seconds(now_playing.get('duration', 0)), + } + + +cmus = with_docstring(CmusPlayerSegment(), +('''Return CMUS player information + +Requires cmus-remote command be acessible from $PATH. + +{0} +''').format(_common_args.format('cmus'))) + + +class MpdPlayerSegment(PlayerSegment): + def get_player_status(self, pl, host='localhost', password=None, port=6600): + try: + import mpd + except ImportError: + if password: + host = password + '@' + host + now_playing = run_cmd(pl, [ + 'mpc', 'current', + '-f', '%album%\n%artist%\n%title%\n%time%', + '-h', host, + '-p', str(port) + ], strip=False) + if not now_playing: + return + now_playing = now_playing.split('\n') + return { + 'album': now_playing[0], + 'artist': now_playing[1], + 'title': now_playing[2], + 'total': now_playing[3], + } + else: + try: + client = mpd.MPDClient(use_unicode=True) + except TypeError: + # python-mpd 1.x does not support use_unicode + client = mpd.MPDClient() + client.connect(host, port) + if password: + client.password(password) + now_playing = client.currentsong() + if not now_playing: + return + status = client.status() + client.close() + client.disconnect() + return { + 'state': status.get('state'), + 'album': now_playing.get('album'), + 'artist': now_playing.get('artist'), + 'title': now_playing.get('title'), + 'elapsed': _convert_seconds(status.get('elapsed', 0)), + 'total': _convert_seconds(now_playing.get('time', 0)), + } + + +mpd = with_docstring(MpdPlayerSegment(), +('''Return Music Player Daemon information + +Requires ``mpd`` Python module (e.g. |python-mpd2|_ or |python-mpd|_ Python +package) or alternatively the ``mpc`` command to be acessible from $PATH. + +.. |python-mpd| replace:: ``python-mpd`` +.. _python-mpd: https://pypi.python.org/pypi/python-mpd + +.. |python-mpd2| replace:: ``python-mpd2`` +.. _python-mpd2: https://pypi.python.org/pypi/python-mpd2 + +{0} +:param str host: + Host on which mpd runs. +:param str password: + Password used for connecting to daemon. +:param int port: + Port which should be connected to. +''').format(_common_args.format('mpd'))) + + +try: + import dbus +except ImportError: + def _get_dbus_player_status(pl, player_name, **kwargs): + pl.error('Could not add {0} segment: requires dbus module', player_name) + return +else: + def _get_dbus_player_status(pl, bus_name, player_path, iface_prop, + iface_player, player_name='player'): + bus = dbus.SessionBus() + try: + player = bus.get_object(bus_name, player_path) + iface = dbus.Interface(player, iface_prop) + info = iface.Get(iface_player, 'Metadata') + status = iface.Get(iface_player, 'PlaybackStatus') + except dbus.exceptions.DBusException: + return + if not info: + return + + try: + elapsed = iface.Get(iface_player, 'Position') + except dbus.exceptions.DBusException: + pl.warning('Missing player elapsed time') + elapsed = None + else: + elapsed = _convert_seconds(elapsed / 1e6) + album = info.get('xesam:album') + title = info.get('xesam:title') + artist = info.get('xesam:artist') + state = _convert_state(status) + if album: + album = out_u(album) + if title: + title = out_u(title) + if artist: + artist = out_u(artist[0]) + return { + 'state': state, + 'album': album, + 'artist': artist, + 'title': title, + 'elapsed': elapsed, + 'total': _convert_seconds(info.get('mpris:length') / 1e6), + } + + +class DbusPlayerSegment(PlayerSegment): + get_player_status = staticmethod(_get_dbus_player_status) + + +dbus_player = with_docstring(DbusPlayerSegment(), +('''Return generic dbus player state + +Requires ``dbus`` python module. Only for players that support specific protocol + (e.g. like :py:func:`spotify` and :py:func:`clementine`). + +{0} +:param str player_name: + Player name. Used in error messages only. +:param str bus_name: + Dbus bus name. +:param str player_path: + Path to the player on the given bus. +:param str iface_prop: + Interface properties name for use with dbus.Interface. +:param str iface_player: + Player name. +''').format(_common_args.format('dbus_player'))) + + +class SpotifyDbusPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + player_status = _get_dbus_player_status( + pl=pl, + player_name='Spotify', + bus_name='org.mpris.MediaPlayer2.spotify', + player_path='/org/mpris/MediaPlayer2', + iface_prop='org.freedesktop.DBus.Properties', + iface_player='org.mpris.MediaPlayer2.Player', + ) + if player_status is not None: + return player_status + # Fallback for legacy spotify client with different DBus protocol + return _get_dbus_player_status( + pl=pl, + player_name='Spotify', + bus_name='com.spotify.qt', + player_path='/', + iface_prop='org.freedesktop.DBus.Properties', + iface_player='org.freedesktop.MediaPlayer2', + ) + + +spotify_dbus = with_docstring(SpotifyDbusPlayerSegment(), +('''Return spotify player information + +Requires ``dbus`` python module. + +{0} +''').format(_common_args.format('spotify_dbus'))) + + +class SpotifyAppleScriptPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + status_delimiter = '-~`/=' + ascript = ''' + tell application "System Events" + set process_list to (name of every process) + end tell + + if process_list contains "Spotify" then + tell application "Spotify" + if player state is playing or player state is paused then + set track_name to name of current track + set artist_name to artist of current track + set album_name to album of current track + set track_length to duration of current track + set now_playing to "" & player state & "{0}" & album_name & "{0}" & artist_name & "{0}" & track_name & "{0}" & track_length & "{0}" & player position + return now_playing + else + return player state + end if + + end tell + else + return "stopped" + end if + '''.format(status_delimiter) + + spotify = asrun(pl, ascript) + if not asrun: + return None + + spotify_status = spotify.split(status_delimiter) + state = _convert_state(spotify_status[0]) + if state == 'stop': + return None + return { + 'state': state, + 'album': spotify_status[1], + 'artist': spotify_status[2], + 'title': spotify_status[3], + 'total': _convert_seconds(int(spotify_status[4])/1000), + 'elapsed': _convert_seconds(spotify_status[5]), + } + + +spotify_apple_script = with_docstring(SpotifyAppleScriptPlayerSegment(), +('''Return spotify player information + +Requires ``osascript`` available in $PATH. + +{0} +''').format(_common_args.format('spotify_apple_script'))) + + +if not sys.platform.startswith('darwin'): + spotify = spotify_dbus + _old_name = 'spotify_dbus' +else: + spotify = spotify_apple_script + _old_name = 'spotify_apple_script' + + +spotify = with_docstring(spotify, spotify.__doc__.replace(_old_name, 'spotify')) + + +class ClementinePlayerSegment(PlayerSegment): + def get_player_status(self, pl): + return _get_dbus_player_status( + pl=pl, + player_name='Clementine', + bus_name='org.mpris.MediaPlayer2.clementine', + player_path='/org/mpris/MediaPlayer2', + iface_prop='org.freedesktop.DBus.Properties', + iface_player='org.mpris.MediaPlayer2.Player', + ) + + +clementine = with_docstring(ClementinePlayerSegment(), +('''Return clementine player information + +Requires ``dbus`` python module. + +{0} +''').format(_common_args.format('clementine'))) + + +class RhythmboxPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + now_playing = run_cmd(pl, [ + 'rhythmbox-client', + '--no-start', '--no-present', + '--print-playing-format', '%at\n%aa\n%tt\n%te\n%td' + ], strip=False) + if not now_playing: + return + now_playing = now_playing.split('\n') + return { + 'album': now_playing[0], + 'artist': now_playing[1], + 'title': now_playing[2], + 'elapsed': now_playing[3], + 'total': now_playing[4], + } + + +rhythmbox = with_docstring(RhythmboxPlayerSegment(), +('''Return rhythmbox player information + +Requires ``rhythmbox-client`` available in $PATH. + +{0} +''').format(_common_args.format('rhythmbox'))) + + +class RDIOPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + status_delimiter = '-~`/=' + ascript = ''' + tell application "System Events" + set rdio_active to the count(every process whose name is "Rdio") + if rdio_active is 0 then + return + end if + end tell + tell application "Rdio" + set rdio_name to the name of the current track + set rdio_artist to the artist of the current track + set rdio_album to the album of the current track + set rdio_duration to the duration of the current track + set rdio_state to the player state + set rdio_elapsed to the player position + return rdio_name & "{0}" & rdio_artist & "{0}" & rdio_album & "{0}" & rdio_elapsed & "{0}" & rdio_duration & "{0}" & rdio_state + end tell + '''.format(status_delimiter) + now_playing = asrun(pl, ascript) + if not now_playing: + return + now_playing = now_playing.split(status_delimiter) + if len(now_playing) != 6: + return + state = _convert_state(now_playing[5]) + total = _convert_seconds(now_playing[4]) + elapsed = _convert_seconds(float(now_playing[3]) * float(now_playing[4]) / 100) + return { + 'title': now_playing[0], + 'artist': now_playing[1], + 'album': now_playing[2], + 'elapsed': elapsed, + 'total': total, + 'state': state, + } + + +rdio = with_docstring(RDIOPlayerSegment(), +('''Return rdio player information + +Requires ``osascript`` available in $PATH. + +{0} +''').format(_common_args.format('rdio'))) + + +class ITunesPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + status_delimiter = '-~`/=' + ascript = ''' + tell application "System Events" + set process_list to (name of every process) + end tell + + if process_list contains "iTunes" then + tell application "iTunes" + if player state is playing then + set t_title to name of current track + set t_artist to artist of current track + set t_album to album of current track + set t_duration to duration of current track + set t_elapsed to player position + set t_state to player state + return t_title & "{0}" & t_artist & "{0}" & t_album & "{0}" & t_elapsed & "{0}" & t_duration & "{0}" & t_state + end if + end tell + end if + '''.format(status_delimiter) + now_playing = asrun(pl, ascript) + if not now_playing: + return + now_playing = now_playing.split(status_delimiter) + if len(now_playing) != 6: + return + title, artist, album = now_playing[0], now_playing[1], now_playing[2] + state = _convert_state(now_playing[5]) + total = _convert_seconds(now_playing[4]) + elapsed = _convert_seconds(now_playing[3]) + return { + 'title': title, + 'artist': artist, + 'album': album, + 'total': total, + 'elapsed': elapsed, + 'state': state + } + + +itunes = with_docstring(ITunesPlayerSegment(), +('''Return iTunes now playing information + +Requires ``osascript``. + +{0} +''').format(_common_args.format('itunes'))) + + +class MocPlayerSegment(PlayerSegment): + def get_player_status(self, pl): + '''Return Music On Console (mocp) player information. + + ``mocp -i`` returns current information i.e. + + .. code-block:: + + File: filename.format + Title: full title + Artist: artist name + SongTitle: song title + Album: album name + TotalTime: 00:00 + TimeLeft: 00:00 + TotalSec: 000 + CurrentTime: 00:00 + CurrentSec: 000 + Bitrate: 000kbps + AvgBitrate: 000kbps + Rate: 00kHz + + For the information we are looking for we don’t really care if we have + extra-timing information or bit rate level. The dictionary comprehension + in this method takes anything in ignore_info and brings the key inside + that to the right info of the dictionary. + ''' + now_playing_str = run_cmd(pl, ['mocp', '-i']) + if not now_playing_str: + return + + now_playing = dict(( + line.split(': ', 1) + for line in now_playing_str.split('\n')[:-1] + )) + state = _convert_state(now_playing.get('State', 'stop')) + return { + 'state': state, + 'album': now_playing.get('Album', ''), + 'artist': now_playing.get('Artist', ''), + 'title': now_playing.get('SongTitle', ''), + 'elapsed': _convert_seconds(now_playing.get('CurrentSec', 0)), + 'total': _convert_seconds(now_playing.get('TotalSec', 0)), + } + + +mocp = with_docstring(MocPlayerSegment(), +('''Return MOC (Music On Console) player information + +Requires version >= 2.3.0 and ``mocp`` executable in ``$PATH``. + +{0} +''').format(_common_args.format('mocp'))) + diff --git a/powerline-bin/powerline/segments/common/sys.py b/powerline-bin/powerline/segments/common/sys.py new file mode 100644 index 0000000..a83025b --- /dev/null +++ b/powerline-bin/powerline/segments/common/sys.py @@ -0,0 +1,183 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from multiprocessing import cpu_count as _cpu_count + +from powerline.lib.threaded import ThreadedSegment +from powerline.lib import add_divider_highlight_group +from powerline.segments import with_docstring + + +cpu_count = None + + +def system_load(pl, format='{avg:.1f}', threshold_good=1, threshold_bad=2, + track_cpu_count=False, short=False): + '''Return system load average. + + Highlights using ``system_load_good``, ``system_load_bad`` and + ``system_load_ugly`` highlighting groups, depending on the thresholds + passed to the function. + + :param str format: + format string, receives ``avg`` as an argument + :param float threshold_good: + threshold for gradient level 0: any normalized load average below this + value will have this gradient level. + :param float threshold_bad: + threshold for gradient level 100: any normalized load average above this + value will have this gradient level. Load averages between + ``threshold_good`` and ``threshold_bad`` receive gradient level that + indicates relative position in this interval: + (``100 * (cur-good) / (bad-good)``). + Note: both parameters are checked against normalized load averages. + :param bool track_cpu_count: + if True powerline will continuously poll the system to detect changes + in the number of CPUs. + :param bool short: + if True only the sys load over last 1 minute will be displayed. + + Divider highlight group used: ``background:divider``. + + Highlight groups used: ``system_load_gradient`` (gradient) or ``system_load``. + ''' + global cpu_count + try: + cpu_num = cpu_count = _cpu_count() if cpu_count is None or track_cpu_count else cpu_count + except NotImplementedError: + pl.warn('Unable to get CPU count: method is not implemented') + return None + ret = [] + for avg in os.getloadavg(): + normalized = avg / cpu_num + if normalized < threshold_good: + gradient_level = 0 + elif normalized < threshold_bad: + gradient_level = (normalized - threshold_good) * 100.0 / (threshold_bad - threshold_good) + else: + gradient_level = 100 + ret.append({ + 'contents': format.format(avg=avg), + 'highlight_groups': ['system_load_gradient', 'system_load'], + 'divider_highlight_group': 'background:divider', + 'gradient_level': gradient_level, + }) + + if short: + return ret + + ret[0]['contents'] += ' ' + ret[1]['contents'] += ' ' + return ret + + +try: + import psutil + + class CPULoadPercentSegment(ThreadedSegment): + interval = 1 + + def update(self, old_cpu): + return psutil.cpu_percent(interval=None) + + def run(self): + while not self.shutdown_event.is_set(): + try: + self.update_value = psutil.cpu_percent(interval=self.interval) + except Exception as e: + self.exception('Exception while calculating cpu_percent: {0}', str(e)) + + def render(self, cpu_percent, format='{0:.0f}%', **kwargs): + if not cpu_percent: + return None + return [{ + 'contents': format.format(cpu_percent), + 'gradient_level': cpu_percent, + 'highlight_groups': ['cpu_load_percent_gradient', 'cpu_load_percent'], + }] +except ImportError: + class CPULoadPercentSegment(ThreadedSegment): + interval = 1 + + @staticmethod + def startup(**kwargs): + pass + + @staticmethod + def start(): + pass + + @staticmethod + def shutdown(): + pass + + @staticmethod + def render(cpu_percent, pl, format='{0:.0f}%', **kwargs): + pl.warn('Module “psutil” is not installed, thus CPU load is not available') + return None + + +cpu_load_percent = with_docstring(CPULoadPercentSegment(), +'''Return the average CPU load as a percentage. + +Requires the ``psutil`` module. + +:param str format: + Output format. Accepts measured CPU load as the first argument. + +Highlight groups used: ``cpu_load_percent_gradient`` (gradient) or ``cpu_load_percent``. +''') + + +if os.path.exists('/proc/uptime'): + def _get_uptime(): + with open('/proc/uptime', 'r') as f: + return int(float(f.readline().split()[0])) +elif 'psutil' in globals(): + from time import time + + if hasattr(psutil, 'boot_time'): + def _get_uptime(): + return int(time() - psutil.boot_time()) + else: + def _get_uptime(): + return int(time() - psutil.BOOT_TIME) +else: + def _get_uptime(): + raise NotImplementedError + + +@add_divider_highlight_group('background:divider') +def uptime(pl, days_format='{days:d}d', hours_format=' {hours:d}h', minutes_format=' {minutes:d}m', seconds_format=' {seconds:d}s', shorten_len=3): + '''Return system uptime. + + :param str days_format: + day format string, will be passed ``days`` as the argument + :param str hours_format: + hour format string, will be passed ``hours`` as the argument + :param str minutes_format: + minute format string, will be passed ``minutes`` as the argument + :param str seconds_format: + second format string, will be passed ``seconds`` as the argument + :param int shorten_len: + shorten the amount of units (days, hours, etc.) displayed + + Divider highlight group used: ``background:divider``. + ''' + try: + seconds = _get_uptime() + except NotImplementedError: + pl.warn('Unable to get uptime. You should install psutil module') + return None + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + time_formatted = list(filter(None, [ + days_format.format(days=days) if days and days_format else None, + hours_format.format(hours=hours) if hours and hours_format else None, + minutes_format.format(minutes=minutes) if minutes and minutes_format else None, + seconds_format.format(seconds=seconds) if seconds and seconds_format else None, + ]))[0:shorten_len] + return ''.join(time_formatted).strip() diff --git a/powerline-bin/powerline/segments/common/time.py b/powerline-bin/powerline/segments/common/time.py new file mode 100644 index 0000000..1e2207b --- /dev/null +++ b/powerline-bin/powerline/segments/common/time.py @@ -0,0 +1,94 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from datetime import datetime + + +def date(pl, format='%Y-%m-%d', istime=False): + '''Return the current date. + + :param str format: + strftime-style date format string + :param bool istime: + If true then segment uses ``time`` highlight group. + + Divider highlight group used: ``time:divider``. + + Highlight groups used: ``time`` or ``date``. + ''' + try: + contents = datetime.now().strftime(format) + except UnicodeEncodeError: + contents = datetime.now().strftime(format.encode('utf-8')).decode('utf-8') + + return [{ + 'contents': contents, + 'highlight_groups': (['time'] if istime else []) + ['date'], + 'divider_highlight_group': 'time:divider' if istime else None, + }] + + +UNICODE_TEXT_TRANSLATION = { + ord('\''): '’', + ord('-'): '‐', +} + + +def fuzzy_time(pl, unicode_text=False): + '''Display the current time as fuzzy time, e.g. "quarter past six". + + :param bool unicode_text: + If true then hyphenminuses (regular ASCII ``-``) and single quotes are + replaced with unicode dashes and apostrophes. + ''' + hour_str = ['twelve', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven'] + minute_str = { + 5: 'five past', + 10: 'ten past', + 15: 'quarter past', + 20: 'twenty past', + 25: 'twenty-five past', + 30: 'half past', + 35: 'twenty-five to', + 40: 'twenty to', + 45: 'quarter to', + 50: 'ten to', + 55: 'five to', + } + special_case_str = { + (23, 58): 'round about midnight', + (23, 59): 'round about midnight', + (0, 0): 'midnight', + (0, 1): 'round about midnight', + (0, 2): 'round about midnight', + (12, 0): 'noon', + } + + now = datetime.now() + + try: + return special_case_str[(now.hour, now.minute)] + except KeyError: + pass + + hour = now.hour + if now.minute > 32: + if hour == 23: + hour = 0 + else: + hour += 1 + if hour > 11: + hour = hour - 12 + hour = hour_str[hour] + + minute = int(round(now.minute / 5.0) * 5) + if minute == 60 or minute == 0: + result = ' '.join([hour, 'o\'clock']) + else: + minute = minute_str[minute] + result = ' '.join([minute, hour]) + + if unicode_text: + result = result.translate(UNICODE_TEXT_TRANSLATION) + + return result diff --git a/powerline-bin/powerline/segments/common/vcs.py b/powerline-bin/powerline/segments/common/vcs.py new file mode 100644 index 0000000..07679ae --- /dev/null +++ b/powerline-bin/powerline/segments/common/vcs.py @@ -0,0 +1,89 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.lib.vcs import guess, tree_status +from powerline.segments import Segment, with_docstring +from powerline.theme import requires_segment_info, requires_filesystem_watcher + + +@requires_filesystem_watcher +@requires_segment_info +class BranchSegment(Segment): + divider_highlight_group = None + + @staticmethod + def get_directory(segment_info): + return segment_info['getcwd']() + + def __call__(self, pl, segment_info, create_watcher, status_colors=False, ignore_statuses=()): + name = self.get_directory(segment_info) + if name: + repo = guess(path=name, create_watcher=create_watcher) + if repo is not None: + branch = repo.branch() + scol = ['branch'] + if status_colors: + try: + status = tree_status(repo, pl) + except Exception as e: + pl.exception('Failed to compute tree status: {0}', str(e)) + status = '?' + else: + status = status and status.strip() + if status in ignore_statuses: + status = None + scol.insert(0, 'branch_dirty' if status else 'branch_clean') + return [{ + 'contents': branch, + 'highlight_groups': scol, + 'divider_highlight_group': self.divider_highlight_group, + }] + + +branch = with_docstring(BranchSegment(), +'''Return the current VCS branch. + +:param bool status_colors: + Determines whether repository status will be used to determine highlighting. + Default: False. +:param list ignore_statuses: + List of statuses which will not result in repo being marked as dirty. Most + useful is setting this option to ``["U"]``: this will ignore repository + which has just untracked files (i.e. repository with modified, deleted or + removed files will be marked as dirty, while just untracked files will make + segment show clean repository). Only applicable if ``status_colors`` option + is True. + +Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. +''') + + +@requires_filesystem_watcher +@requires_segment_info +class StashSegment(Segment): + divider_highlight_group = None + + @staticmethod + def get_directory(segment_info): + return segment_info['getcwd']() + + def __call__(self, pl, segment_info, create_watcher): + name = self.get_directory(segment_info) + if name: + repo = guess(path=name, create_watcher=create_watcher) + if repo is not None: + stash = getattr(repo, 'stash', None) + if stash: + stashes = stash() + if stashes: + return [{ + 'contents': str(stashes), + 'highlight_groups': ['stash'], + 'divider_highlight_group': self.divider_highlight_group + }] + +stash = with_docstring(StashSegment(), +'''Return the number of current VCS stash entries, if any. + +Highlight groups used: ``stash``. +''') diff --git a/powerline-bin/powerline/segments/common/wthr.py b/powerline-bin/powerline/segments/common/wthr.py new file mode 100644 index 0000000..1c6d080 --- /dev/null +++ b/powerline-bin/powerline/segments/common/wthr.py @@ -0,0 +1,235 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import json + +from powerline.lib.url import urllib_read, urllib_urlencode +from powerline.lib.threaded import KwThreadedSegment +from powerline.segments import with_docstring + + +# XXX Warning: module name must not be equal to the segment name as long as this +# segment is imported into powerline.segments.common module. + + +# Weather condition code descriptions available at +# http://developer.yahoo.com/weather/#codes +weather_conditions_codes = ( + ('tornado', 'stormy'), # 0 + ('tropical_storm', 'stormy'), # 1 + ('hurricane', 'stormy'), # 2 + ('severe_thunderstorms', 'stormy'), # 3 + ('thunderstorms', 'stormy'), # 4 + ('mixed_rain_and_snow', 'rainy' ), # 5 + ('mixed_rain_and_sleet', 'rainy' ), # 6 + ('mixed_snow_and_sleet', 'snowy' ), # 7 + ('freezing_drizzle', 'rainy' ), # 8 + ('drizzle', 'rainy' ), # 9 + ('freezing_rain', 'rainy' ), # 10 + ('showers', 'rainy' ), # 11 + ('showers', 'rainy' ), # 12 + ('snow_flurries', 'snowy' ), # 13 + ('light_snow_showers', 'snowy' ), # 14 + ('blowing_snow', 'snowy' ), # 15 + ('snow', 'snowy' ), # 16 + ('hail', 'snowy' ), # 17 + ('sleet', 'snowy' ), # 18 + ('dust', 'foggy' ), # 19 + ('fog', 'foggy' ), # 20 + ('haze', 'foggy' ), # 21 + ('smoky', 'foggy' ), # 22 + ('blustery', 'windy' ), # 23 + ('windy', ), # 24 + ('cold', 'day' ), # 25 + ('clouds', 'cloudy'), # 26 + ('mostly_cloudy_night', 'cloudy'), # 27 + ('mostly_cloudy_day', 'cloudy'), # 28 + ('partly_cloudy_night', 'cloudy'), # 29 + ('partly_cloudy_day', 'cloudy'), # 30 + ('clear_night', 'night' ), # 31 + ('sun', 'sunny' ), # 32 + ('fair_night', 'night' ), # 33 + ('fair_day', 'day' ), # 34 + ('mixed_rain_and_hail', 'rainy' ), # 35 + ('hot', 'sunny' ), # 36 + ('isolated_thunderstorms', 'stormy'), # 37 + ('scattered_thunderstorms', 'stormy'), # 38 + ('scattered_thunderstorms', 'stormy'), # 39 + ('scattered_showers', 'rainy' ), # 40 + ('heavy_snow', 'snowy' ), # 41 + ('scattered_snow_showers', 'snowy' ), # 42 + ('heavy_snow', 'snowy' ), # 43 + ('partly_cloudy', 'cloudy'), # 44 + ('thundershowers', 'rainy' ), # 45 + ('snow_showers', 'snowy' ), # 46 + ('isolated_thundershowers', 'rainy' ), # 47 +) +# ('day', (25, 34)), +# ('rainy', (5, 6, 8, 9, 10, 11, 12, 35, 40, 45, 47)), +# ('cloudy', (26, 27, 28, 29, 30, 44)), +# ('snowy', (7, 13, 14, 15, 16, 17, 18, 41, 42, 43, 46)), +# ('stormy', (0, 1, 2, 3, 4, 37, 38, 39)), +# ('foggy', (19, 20, 21, 22, 23)), +# ('sunny', (32, 36)), +# ('night', (31, 33))): +weather_conditions_icons = { + 'day': 'DAY', + 'blustery': 'WIND', + 'rainy': 'RAIN', + 'cloudy': 'CLOUDS', + 'snowy': 'SNOW', + 'stormy': 'STORM', + 'foggy': 'FOG', + 'sunny': 'SUN', + 'night': 'NIGHT', + 'windy': 'WINDY', + 'not_available': 'NA', + 'unknown': 'UKN', +} + +temp_conversions = { + 'C': lambda temp: temp, + 'F': lambda temp: (temp * 9 / 5) + 32, + 'K': lambda temp: temp + 273.15, +} + +# Note: there are also unicode characters for units: ℃, ℉ and K +temp_units = { + 'C': '°C', + 'F': '°F', + 'K': 'K', +} + + +class WeatherSegment(KwThreadedSegment): + interval = 600 + default_location = None + location_urls = {} + + @staticmethod + def key(location_query=None, **kwargs): + return location_query + + def get_request_url(self, location_query): + try: + return self.location_urls[location_query] + except KeyError: + if location_query is None: + location_data = json.loads(urllib_read('http://geoip.nekudo.com/api/')) + location = ','.join(( + location_data['city'], + location_data['country']['name'], + location_data['country']['code'] + )) + self.info('Location returned by nekudo is {0}', location) + else: + location = location_query + query_data = { + 'q': + 'use "https://raw.githubusercontent.com/yql/yql-tables/master/weather/weather.bylocation.xml" as we;' + 'select * from weather.forecast where woeid in' + ' (select woeid from geo.places(1) where text="{0}") and u="c"'.format(location).encode('utf-8'), + 'format': 'json', + } + self.location_urls[location_query] = url = ( + 'http://query.yahooapis.com/v1/public/yql?' + urllib_urlencode(query_data)) + return url + + def compute_state(self, location_query): + url = self.get_request_url(location_query) + raw_response = urllib_read(url) + if not raw_response: + self.error('Failed to get response') + return None + + response = json.loads(raw_response) + try: + condition = response['query']['results']['channel']['item']['condition'] + condition_code = int(condition['code']) + temp = float(condition['temp']) + except (KeyError, ValueError): + self.exception('Yahoo returned malformed or unexpected response: {0}', repr(raw_response)) + return None + + try: + icon_names = weather_conditions_codes[condition_code] + except IndexError: + if condition_code == 3200: + icon_names = ('not_available',) + self.warn('Weather is not available for location {0}', self.location) + else: + icon_names = ('unknown',) + self.error('Unknown condition code: {0}', condition_code) + + return (temp, icon_names) + + def render_one(self, weather, icons=None, unit='C', temp_format=None, temp_coldest=-30, temp_hottest=40, **kwargs): + if not weather: + return None + + temp, icon_names = weather + + for icon_name in icon_names: + if icons: + if icon_name in icons: + icon = icons[icon_name] + break + else: + icon = weather_conditions_icons[icon_names[-1]] + + temp_format = temp_format or ('{temp:.0f}' + temp_units[unit]) + converted_temp = temp_conversions[unit](temp) + if temp <= temp_coldest: + gradient_level = 0 + elif temp >= temp_hottest: + gradient_level = 100 + else: + gradient_level = (temp - temp_coldest) * 100.0 / (temp_hottest - temp_coldest) + groups = ['weather_condition_' + icon_name for icon_name in icon_names] + ['weather_conditions', 'weather'] + return [ + { + 'contents': icon + ' ', + 'highlight_groups': groups, + 'divider_highlight_group': 'background:divider', + }, + { + 'contents': temp_format.format(temp=converted_temp), + 'highlight_groups': ['weather_temp_gradient', 'weather_temp', 'weather'], + 'divider_highlight_group': 'background:divider', + 'gradient_level': gradient_level, + }, + ] + + +weather = with_docstring(WeatherSegment(), +'''Return weather from Yahoo! Weather. + +Uses GeoIP lookup from http://geoip.nekudo.com to automatically determine +your current location. This should be changed if you’re in a VPN or if your +IP address is registered at another location. + +Returns a list of colorized icon and temperature segments depending on +weather conditions. + +:param str unit: + temperature unit, can be one of ``F``, ``C`` or ``K`` +:param str location_query: + location query for your current location, e.g. ``oslo, norway`` +:param dict icons: + dict for overriding default icons, e.g. ``{'heavy_snow' : u'❆'}`` +:param str temp_format: + format string, receives ``temp`` as an argument. Should also hold unit. +:param float temp_coldest: + coldest temperature. Any temperature below it will have gradient level equal + to zero. +:param float temp_hottest: + hottest temperature. Any temperature above it will have gradient level equal + to 100. Temperatures between ``temp_coldest`` and ``temp_hottest`` receive + gradient level that indicates relative position in this interval + (``100 * (cur-coldest) / (hottest-coldest)``). + +Divider highlight group used: ``background:divider``. + +Highlight groups used: ``weather_conditions`` or ``weather``, ``weather_temp_gradient`` (gradient) or ``weather``. +Also uses ``weather_conditions_{condition}`` for all weather conditions supported by Yahoo. +''') diff --git a/powerline-bin/powerline/segments/i3wm.py b/powerline-bin/powerline/segments/i3wm.py new file mode 100644 index 0000000..3f508ee --- /dev/null +++ b/powerline-bin/powerline/segments/i3wm.py @@ -0,0 +1,155 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import re + +from powerline.theme import requires_segment_info +from powerline.bindings.wm import get_i3_connection + + +WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?') + + +def workspace_groups(w): + group = [] + if w['focused']: + group.append('w_focused') + if w['urgent']: + group.append('w_urgent') + if w['visible']: + group.append('w_visible') + group.append('workspace') + return group + + +def format_name(name, strip=False): + if strip: + return WORKSPACE_REGEX.sub('', name, count=1) + return name + + +@requires_segment_info +def workspaces(pl, segment_info, only_show=None, output=None, strip=0): + '''Return list of used workspaces + + :param list only_show: + Specifies which workspaces to show. Valid entries are ``"visible"``, + ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces + are shown. + + :param str output: + May be set to the name of an X output. If specified, only workspaces + on that output are shown. Overrides automatic output detection by + the lemonbar renderer and bindings. + + :param int strip: + Specifies how many characters from the front of each workspace name + should be stripped (e.g. to remove workspace numbers). Defaults to zero. + + Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``. + ''' + output = output or segment_info.get('output') + + return [ + { + 'contents': w['name'][strip:], + 'highlight_groups': workspace_groups(w) + } + for w in get_i3_connection().get_workspaces() + if ((not only_show or any(w[typ] for typ in only_show)) + and (not output or w['output'] == output)) + ] + + +@requires_segment_info +def workspace(pl, segment_info, workspace=None, strip=False): + '''Return the specified workspace name + + :param str workspace: + Specifies which workspace to show. If unspecified, may be set by the + ``list_workspaces`` lister if used, otherwise falls back to + currently focused workspace. + + :param bool strip: + Specifies whether workspace numbers (in the ``1: name`` format) should + be stripped from workspace names before being displayed. Defaults to false. + + Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``. + ''' + if workspace: + try: + w = next(( + w for w in get_i3_connection().get_workspaces() + if w['name'] == workspace + )) + except StopIteration: + return None + elif segment_info.get('workspace'): + w = segment_info['workspace'] + else: + try: + w = next(( + w for w in get_i3_connection().get_workspaces() + if w['focused'] + )) + except StopIteration: + return None + + return [{ + 'contents': format_name(w['name'], strip=strip), + 'highlight_groups': workspace_groups(w) + }] + + +@requires_segment_info +def mode(pl, segment_info, names={'default': None}): + '''Returns current i3 mode + + :param dict names: + Specifies the string to show for various modes. + Use ``null`` to hide a mode (``default`` is hidden by default). + + Highligh groups used: ``mode`` + ''' + mode = segment_info['mode'] + if mode in names: + return names[mode] + return mode + + +def scratchpad_groups(w): + group = [] + if w.urgent: + group.append('scratchpad:urgent') + if w.nodes[0].focused: + group.append('scratchpad:focused') + if w.workspace().name != '__i3_scratch': + group.append('scratchpad:visible') + group.append('scratchpad') + return group + + +SCRATCHPAD_ICONS = { + 'fresh': 'O', + 'changed': 'X', +} + + +def scratchpad(pl, icons=SCRATCHPAD_ICONS): + '''Returns the windows currently on the scratchpad + + :param dict icons: + Specifies the strings to show for the different scratchpad window states. Must + contain the keys ``fresh`` and ``changed``. + + Highlight groups used: ``scratchpad`` or ``scratchpad:visible``, ``scratchpad`` or ``scratchpad:focused``, ``scratchpad`` or ``scratchpad:urgent``. + ''' + + return [ + { + 'contents': icons.get(w.scratchpad_state, icons['changed']), + 'highlight_groups': scratchpad_groups(w) + } + for w in get_i3_connection().get_tree().descendents() + if w.scratchpad_state != 'none' + ] diff --git a/powerline-bin/powerline/segments/ipython.py b/powerline-bin/powerline/segments/ipython.py new file mode 100644 index 0000000..622e0a5 --- /dev/null +++ b/powerline-bin/powerline/segments/ipython.py @@ -0,0 +1,9 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import requires_segment_info + + +@requires_segment_info +def prompt_count(pl, segment_info): + return str(segment_info['ipython'].prompt_count) diff --git a/powerline-bin/powerline/segments/pdb.py b/powerline-bin/powerline/segments/pdb.py new file mode 100644 index 0000000..bd6a38b --- /dev/null +++ b/powerline-bin/powerline/segments/pdb.py @@ -0,0 +1,61 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from powerline.theme import requires_segment_info + + +@requires_segment_info +def current_line(pl, segment_info): + '''Displays line number that is next to be run + ''' + return str(segment_info['curframe'].f_lineno) + + +@requires_segment_info +def current_file(pl, segment_info, basename=True): + '''Displays current file name + + :param bool basename: + If true only basename is displayed. + ''' + filename = segment_info['curframe'].f_code.co_filename + if basename: + filename = os.path.basename(filename) + return filename + + +@requires_segment_info +def current_code_name(pl, segment_info): + '''Displays name of the code object of the current frame + ''' + return segment_info['curframe'].f_code.co_name + + +@requires_segment_info +def current_context(pl, segment_info): + '''Displays currently executed context name + + This is similar to :py:func:`current_code_name`, but gives more details. + + Currently it only gives module file name if code_name happens to be + ````. + ''' + name = segment_info['curframe'].f_code.co_name + if name == '': + name = os.path.basename(segment_info['curframe'].f_code.co_filename) + return name + + +@requires_segment_info +def stack_depth(pl, segment_info, full_stack=False): + '''Displays current stack depth + + Result is relative to the stack depth at the time prompt was first run. + + :param bool full_stack: + If true then absolute depth is used. + ''' + return str(len(segment_info['pdb'].stack) - ( + 0 if full_stack else segment_info['initial_stack_length'])) diff --git a/powerline-bin/powerline/segments/shell.py b/powerline-bin/powerline/segments/shell.py new file mode 100644 index 0000000..a9e9609 --- /dev/null +++ b/powerline-bin/powerline/segments/shell.py @@ -0,0 +1,174 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import requires_segment_info +from powerline.segments import with_docstring +from powerline.segments.common.env import CwdSegment +from powerline.lib.unicode import out_u + + +@requires_segment_info +def jobnum(pl, segment_info, show_zero=False): + '''Return the number of jobs. + + :param bool show_zero: + If False (default) shows nothing if there are no jobs. Otherwise shows + zero for no jobs. + ''' + jobnum = segment_info['args'].jobnum + if jobnum is None or (not show_zero and jobnum == 0): + return None + else: + return str(jobnum) + + +@requires_segment_info +def last_status(pl, segment_info): + '''Return last exit code. + + Highlight groups used: ``exit_fail`` + ''' + if not segment_info['args'].last_exit_code: + return None + return [{'contents': str(segment_info['args'].last_exit_code), 'highlight_groups': ['exit_fail']}] + + +@requires_segment_info +def last_pipe_status(pl, segment_info): + '''Return last pipe status. + + Highlight groups used: ``exit_fail``, ``exit_success`` + ''' + last_pipe_status = ( + segment_info['args'].last_pipe_status + or (segment_info['args'].last_exit_code,) + ) + if any(last_pipe_status): + return [ + { + 'contents': str(status), + 'highlight_groups': ['exit_fail' if status else 'exit_success'], + 'draw_inner_divider': True + } + for status in last_pipe_status + ] + else: + return None + + +@requires_segment_info +def mode(pl, segment_info, override={'vicmd': 'COMMND', 'viins': 'INSERT'}, default=None): + '''Return the current mode. + + :param dict override: + dict for overriding mode strings. + :param str default: + If current mode is equal to this string then this segment will not get + displayed. If not specified the value is taken from + ``$POWERLINE_DEFAULT_MODE`` variable. This variable is set by zsh + bindings for any mode that does not start from ``vi``. + ''' + mode = segment_info.get('mode', None) + if not mode: + pl.debug('No mode specified') + return None + default = default or segment_info.get('default_mode', None) + if mode == default: + return None + try: + return override[mode] + except KeyError: + # Note: with zsh line editor you can emulate as much modes as you wish. + # Thus having unknown mode is not an error: maybe just some developer + # added support for his own zle widgets. As there is no built-in mode() + # function like in VimL and mode is likely be defined by our code or by + # somebody knowing what he is doing there is absolutely no need in + # keeping translations dictionary. + return mode.upper() + + +@requires_segment_info +def continuation(pl, segment_info, omit_cmdsubst=True, right_align=False, renames={}): + '''Display parser state. + + :param bool omit_cmdsubst: + Do not display cmdsubst parser state if it is the last one. + :param bool right_align: + Align to the right. + :param dict renames: + Rename states: ``{old_name : new_name}``. If ``new_name`` is ``None`` + then given state is not displayed. + + Highlight groups used: ``continuation``, ``continuation:current``. + ''' + if not segment_info.get('parser_state'): + return [{ + 'contents': '', + 'width': 'auto', + 'highlight_groups': ['continuation:current', 'continuation'], + }] + ret = [] + + for state in segment_info['parser_state'].split(): + state = renames.get(state, state) + if state: + ret.append({ + 'contents': state, + 'highlight_groups': ['continuation'], + 'draw_inner_divider': True, + }) + + if omit_cmdsubst and ret[-1]['contents'] == 'cmdsubst': + ret.pop(-1) + + if not ret: + ret.append({ + 'contents': '' + }) + + if right_align: + ret[0].update(width='auto', align='r') + ret[-1]['highlight_groups'] = ['continuation:current', 'continuation'] + else: + ret[-1].update(width='auto', align='l', highlight_groups=['continuation:current', 'continuation']) + + return ret + + +@requires_segment_info +class ShellCwdSegment(CwdSegment): + def get_shortened_path(self, pl, segment_info, use_shortened_path=True, **kwargs): + if use_shortened_path: + try: + return out_u(segment_info['shortened_path']) + except KeyError: + pass + return super(ShellCwdSegment, self).get_shortened_path(pl, segment_info, **kwargs) + + +cwd = with_docstring(ShellCwdSegment(), +'''Return the current working directory. + +Returns a segment list to create a breadcrumb-like effect. + +:param int dir_shorten_len: + shorten parent directory names to this length (e.g. + :file:`/long/path/to/powerline` → :file:`/l/p/t/powerline`) +:param int dir_limit_depth: + limit directory depth to this number (e.g. + :file:`/long/path/to/powerline` → :file:`⋯/to/powerline`) +:param bool use_path_separator: + Use path separator in place of soft divider. +:param bool use_shortened_path: + Use path from shortened_path ``--renderer-arg`` argument. If this argument + is present ``shorten_home`` argument is ignored. +:param bool shorten_home: + Shorten home directory to ``~``. +:param str ellipsis: + Specifies what to use in place of omitted directories. Use None to not + show this subsegment at all. + +Divider highlight group used: ``cwd:divider``. + +Highlight groups used: ``cwd:current_folder`` or ``cwd``. It is recommended to define all highlight groups. +''') diff --git a/powerline-bin/powerline/segments/tmux.py b/powerline-bin/powerline/segments/tmux.py new file mode 100644 index 0000000..1f34389 --- /dev/null +++ b/powerline-bin/powerline/segments/tmux.py @@ -0,0 +1,22 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.bindings.tmux import get_tmux_output + + +def attached_clients(pl, minimum=1): + '''Return the number of tmux clients attached to the currently active session + + :param int minimum: + The minimum number of attached clients that must be present for this + segment to be visible. + ''' + session_output = get_tmux_output(pl, 'list-panes', '-F', '#{session_name}') + if not session_output: + return None + session_name = session_output.rstrip().split('\n')[0] + + attached_clients_output = get_tmux_output(pl, 'list-clients', '-t', session_name) + attached_count = len(attached_clients_output.rstrip().split('\n')) + + return None if attached_count < minimum else str(attached_count) diff --git a/powerline-bin/powerline/segments/vim/__init__.py b/powerline-bin/powerline/segments/vim/__init__.py new file mode 100644 index 0000000..b637961 --- /dev/null +++ b/powerline-bin/powerline/segments/vim/__init__.py @@ -0,0 +1,793 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os +import re +import csv +import sys + +from collections import defaultdict + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, + buffer_name, vim_getwinvar, + register_buffer_cache, current_tabpage, + list_tabpage_buffers_segment_info) +from powerline.theme import requires_segment_info, requires_filesystem_watcher +from powerline.lib import add_divider_highlight_group +from powerline.lib.vcs import guess +from powerline.lib.humanize_bytes import humanize_bytes +from powerline.lib import wraps_saveargs as wraps +from powerline.segments.common.vcs import BranchSegment, StashSegment +from powerline.segments import with_docstring +from powerline.lib.unicode import string, unicode + +try: + from __builtin__ import xrange as range +except ImportError: + pass + + +vim_funcs = { + 'virtcol': vim_get_func('virtcol', rettype='int'), + 'getpos': vim_get_func('getpos'), + 'fnamemodify': vim_get_func('fnamemodify', rettype='bytes'), + 'line2byte': vim_get_func('line2byte', rettype='int'), + 'line': vim_get_func('line', rettype='int'), +} + +vim_modes = { + 'n': 'NORMAL', + 'no': 'N-OPER', + 'v': 'VISUAL', + 'V': 'V-LINE', + '^V': 'V-BLCK', + 's': 'SELECT', + 'S': 'S-LINE', + '^S': 'S-BLCK', + 'i': 'INSERT', + 'ic': 'I-COMP', + 'ix': 'I-C_X ', + 'R': 'RPLACE', + 'Rv': 'V-RPLC', + 'Rc': 'R-COMP', + 'Rx': 'R-C_X ', + 'c': 'COMMND', + 'cv': 'VIM-EX', + 'ce': 'NRM-EX', + 'r': 'PROMPT', + 'rm': '-MORE-', + 'r?': 'CNFIRM', + '!': '!SHELL', + 't': 'TERM ', +} + + +# TODO Remove cache when needed +def window_cached(func): + cache = {} + + @requires_segment_info + @wraps(func) + def ret(segment_info, **kwargs): + window_id = segment_info['window_id'] + if segment_info['mode'] == 'nc': + return cache.get(window_id) + else: + if getattr(func, 'powerline_requires_segment_info', False): + r = func(segment_info=segment_info, **kwargs) + else: + r = func(**kwargs) + cache[window_id] = r + return r + + return ret + + +@requires_segment_info +def mode(pl, segment_info, override=None): + '''Return the current vim mode. + + If mode (returned by ``mode()`` VimL function, see ``:h mode()`` in Vim) + consists of multiple characters and necessary mode is not known to powerline + then it will fall back to mode with last character(s) ignored. + + :param dict override: + dict for overriding default mode strings, e.g. ``{ 'n': 'NORM' }`` + ''' + mode = segment_info['mode'] + if mode == 'nc': + return None + while mode: + try: + if not override: + return vim_modes[mode] + try: + return override[mode] + except KeyError: + return vim_modes[mode] + except KeyError: + mode = mode[:-1] + return 'BUG' + + +@window_cached +@requires_segment_info +def visual_range(pl, segment_info, CTRL_V_text='{rows} x {vcols}', v_text_oneline='C:{vcols}', v_text_multiline='L:{rows}', V_text='L:{rows}'): + '''Return the current visual selection range. + + :param str CTRL_V_text: + Text to display when in block visual or select mode. + :param str v_text_oneline: + Text to display when in charaterwise visual or select mode, assuming + selection occupies only one line. + :param str v_text_multiline: + Text to display when in charaterwise visual or select mode, assuming + selection occupies more then one line. + :param str V_text: + Text to display when in linewise visual or select mode. + + All texts are format strings which are passed the following parameters: + + ========= ============================================================= + Parameter Description + ========= ============================================================= + sline Line number of the first line of the selection + eline Line number of the last line of the selection + scol Column number of the first character of the selection + ecol Column number of the last character of the selection + svcol Virtual column number of the first character of the selection + secol Virtual column number of the last character of the selection + rows Number of lines in the selection + cols Number of columns in the selection + vcols Number of virtual columns in the selection + ========= ============================================================= + ''' + sline, scol, soff = [int(v) for v in vim_funcs['getpos']('v')[1:]] + eline, ecol, eoff = [int(v) for v in vim_funcs['getpos']('.')[1:]] + svcol = vim_funcs['virtcol']([sline, scol, soff]) + evcol = vim_funcs['virtcol']([eline, ecol, eoff]) + rows = abs(eline - sline) + 1 + cols = abs(ecol - scol) + 1 + vcols = abs(evcol - svcol) + 1 + return { + '^': CTRL_V_text, + 's': v_text_oneline if rows == 1 else v_text_multiline, + 'S': V_text, + 'v': v_text_oneline if rows == 1 else v_text_multiline, + 'V': V_text, + }.get(segment_info['mode'][0], '').format( + sline=sline, eline=eline, + scol=scol, ecol=ecol, + svcol=svcol, evcol=evcol, + rows=rows, cols=cols, vcols=vcols, + ) + + +@requires_segment_info +def modified_indicator(pl, segment_info, text='+'): + '''Return a file modified indicator. + + :param string text: + text to display if the current buffer is modified + ''' + return text if int(vim_getbufoption(segment_info, 'modified')) else None + + +@requires_segment_info +def tab_modified_indicator(pl, segment_info, text='+'): + '''Return a file modified indicator for tabpages. + + :param string text: + text to display if any buffer in the current tab is modified + + Highlight groups used: ``tab_modified_indicator`` or ``modified_indicator``. + ''' + for buf_segment_info in list_tabpage_buffers_segment_info(segment_info): + if int(vim_getbufoption(buf_segment_info, 'modified')): + return [{ + 'contents': text, + 'highlight_groups': ['tab_modified_indicator', 'modified_indicator'], + }] + return None + + +@requires_segment_info +def paste_indicator(pl, segment_info, text='PASTE'): + '''Return a paste mode indicator. + + :param string text: + text to display if paste mode is enabled + ''' + return text if int(vim.eval('&paste')) else None + + +@requires_segment_info +def readonly_indicator(pl, segment_info, text='RO'): + '''Return a read-only indicator. + + :param string text: + text to display if the current buffer is read-only + ''' + return text if int(vim_getbufoption(segment_info, 'readonly')) else None + + +SCHEME_RE = re.compile(b'^\\w[\\w\\d+\\-.]*(?=:)') + + +@requires_segment_info +def file_scheme(pl, segment_info): + '''Return the protocol part of the file. + + Protocol is the part of the full filename just before the colon which + starts with a latin letter and contains only latin letters, digits, plus, + period or hyphen (refer to `RFC3986 + `_ for the description of + URI scheme). If there is no such a thing ``None`` is returned, effectively + removing segment. + + .. note:: + Segment will not check whether there is ``//`` just after the + colon or if there is at least one slash after the scheme. Reason: it is + not always present. E.g. when opening file inside a zip archive file + name will look like :file:`zipfile:/path/to/archive.zip::file.txt`. + ``file_scheme`` segment will catch ``zipfile`` part here. + ''' + name = buffer_name(segment_info) + if not name: + return None + match = SCHEME_RE.match(name) + if match: + return match.group(0).decode('ascii') + + +@requires_segment_info +def file_directory(pl, segment_info, remove_scheme=True, shorten_user=True, shorten_cwd=True, shorten_home=False): + '''Return file directory (head component of the file path). + + :param bool remove_scheme: + Remove scheme part from the segment name, if present. See documentation + of file_scheme segment for the description of what scheme is. Also + removes the colon. + + :param bool shorten_user: + Shorten ``$HOME`` directory to :file:`~/`. Does not work for files with + scheme. + + :param bool shorten_cwd: + Shorten current directory to :file:`./`. Does not work for files with + scheme present. + + :param bool shorten_home: + Shorten all directories in :file:`/home/` to :file:`~user/` instead of + :file:`/home/user/`. Does not work for files with scheme present. + ''' + name = buffer_name(segment_info) + if not name: + return None + match = SCHEME_RE.match(name) + if match: + if remove_scheme: + name = name[len(match.group(0)) + 1:] # Remove scheme and colon + file_directory = vim_funcs['fnamemodify'](name, ':h') + else: + file_directory = vim_funcs['fnamemodify']( + name, + (':~' if shorten_user else '') + (':.' if shorten_cwd else '') + ':h' + ) + if not file_directory: + return None + if shorten_home and file_directory.startswith('/home/'): + file_directory = b'~' + file_directory[6:] + file_directory = file_directory.decode(segment_info['encoding'], 'powerline_vim_strtrans_error') + return file_directory + os.sep + + +@requires_segment_info +def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]'): + '''Return file name (tail component of the file path). + + :param bool display_no_file: + display a string if the buffer is missing a file name + :param str no_file_text: + the string to display if the buffer is missing a file name + + Highlight groups used: ``file_name_no_file`` or ``file_name``, ``file_name``. + ''' + name = buffer_name(segment_info) + if not name: + if display_no_file: + return [{ + 'contents': no_file_text, + 'highlight_groups': ['file_name_no_file', 'file_name'], + }] + else: + return None + return os.path.basename(name).decode(segment_info['encoding'], 'powerline_vim_strtrans_error') + + +@window_cached +def file_size(pl, suffix='B', si_prefix=False): + '''Return file size in &encoding. + + :param str suffix: + string appended to the file size + :param bool si_prefix: + use SI prefix, e.g. MB instead of MiB + :return: file size or None if the file isn’t saved or if the size is too big to fit in a number + ''' + # Note: returns file size in &encoding, not in &fileencoding. But returned + # size is updated immediately; and it is valid for any buffer + file_size = vim_funcs['line2byte'](len(vim.current.buffer) + 1) - 1 + if file_size < 0: + file_size = 0 + return humanize_bytes(file_size, suffix, si_prefix) + + +@requires_segment_info +@add_divider_highlight_group('background:divider') +def file_format(pl, segment_info): + '''Return file format (i.e. line ending type). + + :return: file format or None if unknown or missing file format + + Divider highlight group used: ``background:divider``. + ''' + return vim_getbufoption(segment_info, 'fileformat') or None + + +@requires_segment_info +@add_divider_highlight_group('background:divider') +def file_encoding(pl, segment_info): + '''Return file encoding/character set. + + :return: file encoding/character set or None if unknown or missing file encoding + + Divider highlight group used: ``background:divider``. + ''' + return vim_getbufoption(segment_info, 'fileencoding') or None + + +@requires_segment_info +@add_divider_highlight_group('background:divider') +def file_type(pl, segment_info): + '''Return file type. + + :return: file type or None if unknown file type + + Divider highlight group used: ``background:divider``. + ''' + return vim_getbufoption(segment_info, 'filetype') or None + + +@requires_segment_info +def window_title(pl, segment_info): + '''Return the window title. + + This currently looks at the ``quickfix_title`` window variable, + which is used by Syntastic and Vim itself. + + It is used in the quickfix theme.''' + try: + return vim_getwinvar(segment_info, 'quickfix_title') + except KeyError: + return None + + +@requires_segment_info +def line_percent(pl, segment_info, gradient=False): + '''Return the cursor position in the file as a percentage. + + :param bool gradient: + highlight the percentage with a color gradient (by default a green to red gradient) + + Highlight groups used: ``line_percent_gradient`` (gradient), ``line_percent``. + ''' + line_current = segment_info['window'].cursor[0] + line_last = len(segment_info['buffer']) + percentage = line_current * 100.0 / line_last + if not gradient: + return str(int(round(percentage))) + return [{ + 'contents': str(int(round(percentage))), + 'highlight_groups': ['line_percent_gradient', 'line_percent'], + 'gradient_level': percentage, + }] + + +@window_cached +def position(pl, position_strings={'top': 'Top', 'bottom': 'Bot', 'all': 'All'}, gradient=False): + '''Return the position of the current view in the file as a percentage. + + :param dict position_strings: + dict for translation of the position strings, e.g. ``{"top":"Oben", "bottom":"Unten", "all":"Alles"}`` + + :param bool gradient: + highlight the percentage with a color gradient (by default a green to red gradient) + + Highlight groups used: ``position_gradient`` (gradient), ``position``. + ''' + line_last = len(vim.current.buffer) + + winline_first = vim_funcs['line']('w0') + winline_last = vim_funcs['line']('w$') + if winline_first == 1 and winline_last == line_last: + percentage = 0.0 + content = position_strings['all'] + elif winline_first == 1: + percentage = 0.0 + content = position_strings['top'] + elif winline_last == line_last: + percentage = 100.0 + content = position_strings['bottom'] + else: + percentage = winline_first * 100.0 / (line_last - winline_last + winline_first) + content = str(int(round(percentage))) + '%' + + if not gradient: + return content + return [{ + 'contents': content, + 'highlight_groups': ['position_gradient', 'position'], + 'gradient_level': percentage, + }] + + +@requires_segment_info +def line_current(pl, segment_info): + '''Return the current cursor line.''' + return str(segment_info['window'].cursor[0]) + + +@requires_segment_info +def line_count(pl, segment_info): + '''Return the line count of the current buffer.''' + return str(len(segment_info['buffer'])) + + +@requires_segment_info +def col_current(pl, segment_info): + '''Return the current cursor column. + ''' + return str(segment_info['window'].cursor[1] + 1) + + +@window_cached +def virtcol_current(pl, gradient=True): + '''Return current visual column with concealed characters ingored + + :param bool gradient: + Determines whether it should show textwidth-based gradient (gradient level is ``virtcol * 100 / textwidth``). + + Highlight groups used: ``virtcol_current_gradient`` (gradient), ``virtcol_current`` or ``col_current``. + ''' + col = vim_funcs['virtcol']('.') + r = [{'contents': str(col), 'highlight_groups': ['virtcol_current', 'col_current']}] + if gradient: + textwidth = int(getbufvar('%', '&textwidth')) + r[-1]['gradient_level'] = min(col * 100 / textwidth, 100) if textwidth else 0 + r[-1]['highlight_groups'].insert(0, 'virtcol_current_gradient') + return r + + +def modified_buffers(pl, text='+ ', join_str=','): + '''Return a comma-separated list of modified buffers. + + :param str text: + text to display before the modified buffer list + :param str join_str: + string to use for joining the modified buffer list + ''' + buffer_mod_text = join_str.join(( + str(buffer.number) + for buffer in vim.buffers + if int(vim_getbufoption({'buffer': buffer, 'bufnr': buffer.number}, 'modified')) + )) + if buffer_mod_text: + return text + buffer_mod_text + return None + + +@requires_filesystem_watcher +@requires_segment_info +class VimBranchSegment(BranchSegment): + divider_highlight_group = 'branch:divider' + + @staticmethod + def get_directory(segment_info): + if vim_getbufoption(segment_info, 'buftype'): + return None + return buffer_name(segment_info) + + +branch = with_docstring(VimBranchSegment(), +'''Return the current working branch. + +:param bool status_colors: + Determines whether repository status will be used to determine highlighting. + Default: False. +:param bool ignore_statuses: + List of statuses which will not result in repo being marked as dirty. Most + useful is setting this option to ``["U"]``: this will ignore repository + which has just untracked files (i.e. repository with modified, deleted or + removed files will be marked as dirty, while just untracked files will make + segment show clean repository). Only applicable if ``status_colors`` option + is True. + +Highlight groups used: ``branch_clean``, ``branch_dirty``, ``branch``. + +Divider highlight group used: ``branch:divider``. +''') + + +@requires_filesystem_watcher +@requires_segment_info +class VimStashSegment(StashSegment): + divider_highlight_group = 'stash:divider' + + @staticmethod + def get_directory(segment_info): + if vim_getbufoption(segment_info, 'buftype'): + return None + return buffer_name(segment_info) + + +stash = with_docstring(VimStashSegment(), +'''Return the number of stashes in the current working branch. + +Highlight groups used: ``stash``. +''') + + +@requires_filesystem_watcher +@requires_segment_info +def file_vcs_status(pl, segment_info, create_watcher): + '''Return the VCS status for this buffer. + + Highlight groups used: ``file_vcs_status``. + ''' + name = buffer_name(segment_info) + skip = not (name and (not vim_getbufoption(segment_info, 'buftype'))) + if not skip: + repo = guess(path=name, create_watcher=create_watcher) + if repo is not None: + status = repo.status(os.path.relpath(name, repo.directory)) + if not status: + return None + status = status.strip() + ret = [] + for status in status: + ret.append({ + 'contents': status, + 'highlight_groups': ['file_vcs_status_' + status, 'file_vcs_status'], + }) + return ret + + +trailing_whitespace_cache = None + + +@requires_segment_info +def trailing_whitespace(pl, segment_info): + '''Return the line number for trailing whitespaces + + It is advised not to use this segment in insert mode: in Insert mode it will + iterate over all lines in buffer each time you happen to type a character + which may cause lags. It will also show you whitespace warning each time you + happen to type space. + + Highlight groups used: ``trailing_whitespace`` or ``warning``. + ''' + global trailing_whitespace_cache + if trailing_whitespace_cache is None: + trailing_whitespace_cache = register_buffer_cache(defaultdict(lambda: (0, None))) + bufnr = segment_info['bufnr'] + changedtick = getbufvar(bufnr, 'changedtick') + if trailing_whitespace_cache[bufnr][0] == changedtick: + return trailing_whitespace_cache[bufnr][1] + else: + buf = segment_info['buffer'] + bws = b' \t' + sws = str(' \t') # Ignore unicode_literals and use native str. + for i in range(len(buf)): + try: + line = buf[i] + except UnicodeDecodeError: # May happen in Python 3 + if hasattr(vim, 'bindeval'): + line = vim.bindeval('getbufline({0}, {1})'.format( + bufnr, i + 1)) + has_trailing_ws = (line[-1] in bws) + else: + line = vim.eval('strtrans(getbufline({0}, {1}))'.format( + bufnr, i + 1)) + has_trailing_ws = (line[-1] in bws) + else: + has_trailing_ws = (line and line[-1] in sws) + if has_trailing_ws: + break + if has_trailing_ws: + ret = [{ + 'contents': str(i + 1), + 'highlight_groups': ['trailing_whitespace', 'warning'], + }] + else: + ret = None + trailing_whitespace_cache[bufnr] = (changedtick, ret) + return ret + + +@requires_segment_info +def tabnr(pl, segment_info, show_current=True): + '''Show tabpage number + + :param bool show_current: + If False do not show current tabpage number. This is default because + tabnr is by default only present in tabline. + ''' + try: + tabnr = segment_info['tabnr'] + except KeyError: + return None + if show_current or tabnr != current_tabpage().number: + return str(tabnr) + + +@requires_segment_info +def bufnr(pl, segment_info, show_current=True): + '''Show buffer number + + :param bool show_current: + If False do not show current window number. + ''' + bufnr = segment_info['bufnr'] + if show_current or bufnr != vim.current.buffer.number: + return str(bufnr) + + +@requires_segment_info +def winnr(pl, segment_info, show_current=True): + '''Show window number + + :param bool show_current: + If False do not show current window number. + ''' + winnr = segment_info['winnr'] + if show_current or winnr != vim.current.window.number: + return str(winnr) + + +csv_cache = None +sniffer = csv.Sniffer() + + +def detect_text_csv_dialect(text, display_name, header_text=None): + return ( + sniffer.sniff(string(text)), + sniffer.has_header(string(header_text or text)) if display_name == 'auto' else display_name, + ) + + +CSV_SNIFF_LINES = 100 +CSV_PARSE_LINES = 10 + + +if sys.version_info < (2, 7): + def read_csv(l, dialect, fin=next): + try: + return fin(csv.reader(l, dialect)) + except csv.Error as e: + if str(e) == 'newline inside string' and dialect.quotechar: + # Maybe we are inside an unfinished quoted string. Python-2.6 + # does not handle this fine + return fin(csv.reader(l[:-1] + [l[-1] + dialect.quotechar])) + else: + raise +else: + def read_csv(l, dialect, fin=next): + return fin(csv.reader(l, dialect)) + + +def process_csv_buffer(pl, buffer, line, col, display_name): + global csv_cache + if csv_cache is None: + csv_cache = register_buffer_cache(defaultdict(lambda: (None, None, None))) + try: + cur_first_line = buffer[0] + except UnicodeDecodeError: + cur_first_line = vim.eval('strtrans(getline(1))') + dialect, has_header, first_line = csv_cache[buffer.number] + if dialect is None or (cur_first_line != first_line and display_name == 'auto'): + try: + text = '\n'.join(buffer[:CSV_SNIFF_LINES]) + except UnicodeDecodeError: # May happen in Python 3 + text = vim.eval('join(map(getline(1, {0}), "strtrans(v:val)"), "\\n")'.format(CSV_SNIFF_LINES)) + try: + dialect, has_header = detect_text_csv_dialect(text, display_name) + except csv.Error as e: + pl.warn('Failed to detect csv format: {0}', str(e)) + # Try detecting using three lines only: + if line == 1: + rng = (0, line + 2) + elif line == len(buffer): + rng = (line - 3, line) + else: + rng = (line - 2, line + 1) + try: + dialect, has_header = detect_text_csv_dialect( + '\n'.join(buffer[rng[0]:rng[1]]), + display_name, + header_text='\n'.join(buffer[:4]), + ) + except csv.Error as e: + pl.error('Failed to detect csv format: {0}', str(e)) + return None, None + if len(buffer) > 2: + csv_cache[buffer.number] = dialect, has_header, cur_first_line + column_number = len(read_csv( + buffer[max(0, line - CSV_PARSE_LINES):line - 1] + [buffer[line - 1][:col]], + dialect=dialect, + fin=list, + )[-1]) or 1 + if has_header: + try: + header = read_csv(buffer[0:1], dialect=dialect) + except UnicodeDecodeError: + header = read_csv([vim.eval('strtrans(getline(1))')], dialect=dialect) + column_name = header[column_number - 1] + else: + column_name = None + return unicode(column_number), column_name + + +@requires_segment_info +def csv_col_current(pl, segment_info, display_name='auto', name_format=' ({column_name:.15})'): + '''Display CSV column number and column name + + Requires filetype to be set to ``csv``. + + :param bool or str name: + May be ``True``, ``False`` and ``"auto"``. In the first case value from + the first raw will always be displayed. In the second case it will never + be displayed. In thi last case ``csv.Sniffer().has_header()`` will be + used to detect whether current file contains header in the first column. + :param str name_format: + String used to format column name (in case ``display_name`` is set to + ``True`` or ``"auto"``). Accepts ``column_name`` keyword argument. + + Highlight groups used: ``csv:column_number`` or ``csv``, ``csv:column_name`` or ``csv``. + ''' + if vim_getbufoption(segment_info, 'filetype') != 'csv': + return None + line, col = segment_info['window'].cursor + column_number, column_name = process_csv_buffer(pl, segment_info['buffer'], line, col, display_name) + if not column_number: + return None + return [{ + 'contents': column_number, + 'highlight_groups': ['csv:column_number', 'csv'], + }] + ([{ + 'contents': name_format.format(column_name=column_name), + 'highlight_groups': ['csv:column_name', 'csv'], + }] if column_name else []) + + +@requires_segment_info +def tab(pl, segment_info, end=False): + '''Mark start of the clickable region for tabpage + + :param bool end: + In place of starting region for the current tab end it. + + No highlight groups are used (literal segment). + ''' + try: + return [{ + 'contents': None, + 'literal_contents': (0, '%{tabnr}T'.format(tabnr=('' if end else segment_info['tabnr']))), + }] + except KeyError: + return None diff --git a/powerline-bin/powerline/segments/vim/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/segments/vim/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0c116e5 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__init__.py b/powerline-bin/powerline/segments/vim/plugin/__init__.py new file mode 100644 index 0000000..b2b9f10 --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/__init__.py @@ -0,0 +1,6 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) +from pkgutil import extend_path + + +__path__ = extend_path(__path__, __name__) diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c00b633 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/ale.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/ale.cpython-312.pyc new file mode 100644 index 0000000..e7cfc7c Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/ale.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/capslock.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/capslock.cpython-312.pyc new file mode 100644 index 0000000..63d7ab2 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/capslock.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/commandt.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/commandt.cpython-312.pyc new file mode 100644 index 0000000..60f4b92 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/commandt.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/nerdtree.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/nerdtree.cpython-312.pyc new file mode 100644 index 0000000..e58f9a9 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/nerdtree.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/syntastic.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/syntastic.cpython-312.pyc new file mode 100644 index 0000000..4ba7b95 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/syntastic.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/__pycache__/tagbar.cpython-312.pyc b/powerline-bin/powerline/segments/vim/plugin/__pycache__/tagbar.cpython-312.pyc new file mode 100644 index 0000000..67a4973 Binary files /dev/null and b/powerline-bin/powerline/segments/vim/plugin/__pycache__/tagbar.cpython-312.pyc differ diff --git a/powerline-bin/powerline/segments/vim/plugin/ale.py b/powerline-bin/powerline/segments/vim/plugin/ale.py new file mode 100644 index 0000000..4f4bdee --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/ale.py @@ -0,0 +1,52 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import vim_global_exists +from powerline.theme import requires_segment_info + + +@requires_segment_info +def ale(segment_info, pl, err_format='ERR: ln {first_line} ({num}) ', warn_format='WARN: ln {first_line} ({num}) '): + '''Show whether ALE has found any errors or warnings + + :param str err_format: + Format string for errors. + + :param str warn_format: + Format string for warnings. + + Highlight groups used: ``ale:warning`` or ``warning``, ``ale:error`` or ``error``. + ''' + if not (vim_global_exists('ale_enabled') and int(vim.eval('g:ale_enabled'))): + return None + has_errors = int(vim.eval('ale#statusline#Count(' + str(segment_info['bufnr']) + ').total')) + if not has_errors: + return + error = None + warning = None + errors_count = 0 + warnings_count = 0 + for issue in vim.eval('ale#engine#GetLoclist(' + str(segment_info['bufnr']) + ')'): + if issue['type'] == 'E': + error = error or issue + errors_count += 1 + elif issue['type'] == 'W': + warning = warning or issue + warnings_count += 1 + segments = [] + if error: + segments.append({ + 'contents': err_format.format(first_line=error['lnum'], num=errors_count), + 'highlight_groups': ['ale:error', 'error'], + }) + if warning: + segments.append({ + 'contents': warn_format.format(first_line=warning['lnum'], num=warnings_count), + 'highlight_groups': ['ale:warning', 'warning'], + }) + return segments diff --git a/powerline-bin/powerline/segments/vim/plugin/capslock.py b/powerline-bin/powerline/segments/vim/plugin/capslock.py new file mode 100644 index 0000000..d2c474d --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/capslock.py @@ -0,0 +1,30 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import vim_func_exists +from powerline.theme import requires_segment_info + + +@requires_segment_info +def capslock_indicator(pl, segment_info, text='CAPS'): + '''Shows the indicator if tpope/vim-capslock plugin is enabled + + .. note:: + In the current state plugin automatically disables itself when leaving + insert mode. So trying to use this segment not in insert or replace + modes is useless. + + :param str text: + String to show when software capslock presented by this plugin is + active. + ''' + if not vim_func_exists('CapsLockStatusline'): + return None + # CapsLockStatusline() function returns an empty string when plugin is + # disabled. If it is not then string is non-empty. + return text if vim.eval('CapsLockStatusline()') else None diff --git a/powerline-bin/powerline/segments/vim/plugin/commandt.py b/powerline-bin/powerline/segments/vim/plugin/commandt.py new file mode 100644 index 0000000..7e5262e --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/commandt.py @@ -0,0 +1,97 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import create_ruby_dpowerline + + +def initialize(): + global initialized + if initialized: + return + initialized = True + create_ruby_dpowerline() + vim.command(( + # When using :execute (vim.command uses the same code) one should not + # use << EOF. + ''' + ruby + if (not ($command_t.respond_to? 'active_finder')) + def $command_t.active_finder + @active_finder and @active_finder.class.name or '' + end + end + if (not ($command_t.respond_to? 'path')) + def $command_t.path + @path or '' + end + end + def $powerline.commandt_set_active_finder + ::VIM::command "let g:powerline_commandt_reply = '#{$command_t.active_finder}'" + end + def $powerline.commandt_set_path + ::VIM::command "let g:powerline_commandt_reply = '#{($command_t.path or '').gsub(/'/, "''")}'" + end + ''' + )) + + +initialized = False + + +def finder(pl): + '''Display Command-T finder name + + Requires $command_t.active_finder and methods (code above may monkey-patch + $command_t to add them). All Command-T finders have ``CommandT::`` module + prefix, but it is stripped out (actually, any ``CommandT::`` substring will + be stripped out). + + Highlight groups used: ``commandt:finder``. + ''' + initialize() + vim.command('ruby $powerline.commandt_set_active_finder') + return [{ + 'highlight_groups': ['commandt:finder'], + 'contents': vim.eval('g:powerline_commandt_reply').replace('CommandT::', '').replace('Finder::', '') + }] + + +FINDERS_WITHOUT_PATH = set(( + 'CommandT::MRUBufferFinder', + 'CommandT::BufferFinder', + 'CommandT::TagFinder', + 'CommandT::JumpFinder', + 'CommandT::Finder::MRUBufferFinder', + 'CommandT::Finder::BufferFinder', + 'CommandT::Finder::TagFinder', + 'CommandT::Finder::JumpFinder', +)) + + +def path(pl): + '''Display path used by Command-T + + Requires $command_t.active_finder and .path methods (code above may + monkey-patch $command_t to add them). + + $command_t.active_finder is required in order to omit displaying path for + finders ``MRUBufferFinder``, ``BufferFinder``, ``TagFinder`` and + ``JumpFinder`` (pretty much any finder, except ``FileFinder``). + + Highlight groups used: ``commandt:path``. + ''' + initialize() + vim.command('ruby $powerline.commandt_set_active_finder') + finder = vim.eval('g:powerline_commandt_reply') + if finder in FINDERS_WITHOUT_PATH: + return None + vim.command('ruby $powerline.commandt_set_path') + return [{ + 'highlight_groups': ['commandt:path'], + 'contents': vim.eval('g:powerline_commandt_reply') + }] diff --git a/powerline-bin/powerline/segments/vim/plugin/nerdtree.py b/powerline-bin/powerline/segments/vim/plugin/nerdtree.py new file mode 100644 index 0000000..f11be14 --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/nerdtree.py @@ -0,0 +1,25 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import bufvar_exists +from powerline.segments.vim import window_cached + + +@window_cached +def nerdtree(pl): + '''Return directory that is shown by the current buffer. + + Highlight groups used: ``nerdtree:path`` or ``file_name``. + ''' + if not bufvar_exists(None, 'NERDTreeRoot'): + return None + path_str = vim.eval('getbufvar("%", "NERDTreeRoot").path.str()') + return [{ + 'contents': path_str, + 'highlight_groups': ['nerdtree:path', 'file_name'], + }] diff --git a/powerline-bin/powerline/segments/vim/plugin/syntastic.py b/powerline-bin/powerline/segments/vim/plugin/syntastic.py new file mode 100644 index 0000000..5bef3c7 --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/syntastic.py @@ -0,0 +1,43 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + import vim +except ImportError: + vim = object() + +from powerline.segments.vim import window_cached +from powerline.bindings.vim import vim_global_exists + + +@window_cached +def syntastic(pl, err_format='ERR:  {first_line} ({num}) ', warn_format='WARN:  {first_line} ({num}) '): + '''Show whether syntastic has found any errors or warnings + + :param str err_format: + Format string for errors. + + :param str warn_format: + Format string for warnings. + + Highlight groups used: ``syntastic:warning`` or ``warning``, ``syntastic:error`` or ``error``. + ''' + if not vim_global_exists('SyntasticLoclist'): + return None + has_errors = int(vim.eval('g:SyntasticLoclist.current().hasErrorsOrWarningsToDisplay()')) + if not has_errors: + return + errors = vim.eval('g:SyntasticLoclist.current().errors()') + warnings = vim.eval('g:SyntasticLoclist.current().warnings()') + segments = [] + if errors: + segments.append({ + 'contents': err_format.format(first_line=errors[0]['lnum'], num=len(errors)), + 'highlight_groups': ['syntastic:error', 'error'], + }) + if warnings: + segments.append({ + 'contents': warn_format.format(first_line=warnings[0]['lnum'], num=len(warnings)), + 'highlight_groups': ['syntastic:warning', 'warning'], + }) + return segments diff --git a/powerline-bin/powerline/segments/vim/plugin/tagbar.py b/powerline-bin/powerline/segments/vim/plugin/tagbar.py new file mode 100644 index 0000000..e683758 --- /dev/null +++ b/powerline-bin/powerline/segments/vim/plugin/tagbar.py @@ -0,0 +1,51 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import vim_command_exists, vim_get_autoload_func +from powerline.theme import requires_segment_info + + +currenttag = None +tag_cache = {} + + +@requires_segment_info +def current_tag(segment_info, pl, flags='s'): + '''Return tag that is near the cursor. + + :param str flags: + Specifies additional properties of the displayed tag. Supported values: + + * s - display complete signature + * f - display the full hierarchy of the tag + * p - display the raw prototype + + More info in the `official documentation`_ (search for + “tagbar#currenttag”). + + .. _`official documentation`: https://github.com/majutsushi/tagbar/blob/master/doc/tagbar.txt + ''' + global currenttag + global tag_cache + window_id = segment_info['window_id'] + if segment_info['mode'] == 'nc': + return tag_cache.get(window_id, (None,))[-1] + if not currenttag: + if vim_command_exists('Tagbar'): + currenttag = vim_get_autoload_func('tagbar#currenttag') + if not currenttag: + return None + else: + return None + prev_key, r = tag_cache.get(window_id, (None, None)) + key = (int(vim.eval('b:changedtick')), segment_info['window'].cursor[0]) + if prev_key and key == prev_key: + return r + r = currenttag('%s', '', flags) + tag_cache[window_id] = (key, r) + return r diff --git a/powerline-bin/powerline/selectors/__init__.py b/powerline-bin/powerline/selectors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline/selectors/__pycache__/__init__.cpython-312.pyc b/powerline-bin/powerline/selectors/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e873efd Binary files /dev/null and b/powerline-bin/powerline/selectors/__pycache__/__init__.cpython-312.pyc differ diff --git a/powerline-bin/powerline/selectors/__pycache__/vim.cpython-312.pyc b/powerline-bin/powerline/selectors/__pycache__/vim.cpython-312.pyc new file mode 100644 index 0000000..b1f56a4 Binary files /dev/null and b/powerline-bin/powerline/selectors/__pycache__/vim.cpython-312.pyc differ diff --git a/powerline-bin/powerline/selectors/vim.py b/powerline-bin/powerline/selectors/vim.py new file mode 100644 index 0000000..d111de9 --- /dev/null +++ b/powerline-bin/powerline/selectors/vim.py @@ -0,0 +1,10 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.bindings.vim import list_tabpages + + +def single_tab(pl, segment_info, mode): + '''Returns True if Vim has only one tab opened + ''' + return len(list_tabpages()) == 1 diff --git a/powerline-bin/powerline/shell.py b/powerline-bin/powerline/shell.py new file mode 100644 index 0000000..e10692c --- /dev/null +++ b/powerline-bin/powerline/shell.py @@ -0,0 +1,38 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline import Powerline +from powerline.lib.dict import mergedicts + + +class ShellPowerline(Powerline): + def init(self, args, **kwargs): + self.args = args + super(ShellPowerline, self).init(args.ext[0], args.renderer_module, **kwargs) + + def load_main_config(self): + r = super(ShellPowerline, self).load_main_config() + if self.args.config_override: + mergedicts(r, self.args.config_override) + return r + + def load_theme_config(self, name): + r = super(ShellPowerline, self).load_theme_config(name) + if self.args.theme_override and name in self.args.theme_override: + mergedicts(r, self.args.theme_override[name]) + return r + + def get_config_paths(self): + return self.args.config_path or super(ShellPowerline, self).get_config_paths() + + def get_local_themes(self, local_themes): + if not local_themes: + return {} + + return dict(( + (key, {'config': self.load_theme_config(val)}) + for key, val in local_themes.items() + )) + + def do_setup(self, obj): + obj.powerline = self diff --git a/powerline-bin/powerline/theme.py b/powerline-bin/powerline/theme.py new file mode 100644 index 0000000..b3a23a1 --- /dev/null +++ b/powerline-bin/powerline/theme.py @@ -0,0 +1,182 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import itertools + +from powerline.segment import gen_segment_getter, process_segment, get_fallback_segment +from powerline.lib.unicode import u, safe_unicode + + +def requires_segment_info(func): + func.powerline_requires_segment_info = True + return func + + +def requires_filesystem_watcher(func): + func.powerline_requires_filesystem_watcher = True + return func + + +def new_empty_segment_line(): + return { + 'left': [], + 'right': [] + } + + +def add_spaces_left(pl, amount, segment): + return (' ' * amount) + segment['contents'] + + +def add_spaces_right(pl, amount, segment): + return segment['contents'] + (' ' * amount) + + +def add_spaces_center(pl, amount, segment): + amount, remainder = divmod(amount, 2) + return (' ' * (amount + remainder)) + segment['contents'] + (' ' * amount) + + +expand_functions = { + 'l': add_spaces_right, + 'r': add_spaces_left, + 'c': add_spaces_center, +} + + +class Theme(object): + def __init__(self, + ext, + theme_config, + common_config, + pl, + get_module_attr, + top_theme, + colorscheme, + main_theme_config=None, + run_once=False, + shutdown_event=None): + self.colorscheme = colorscheme + self.dividers = theme_config['dividers'] + self.dividers = dict(( + (key, dict((k, u(v)) + for k, v in val.items())) + for key, val in self.dividers.items() + )) + try: + self.cursor_space_multiplier = 1 - (theme_config['cursor_space'] / 100) + except KeyError: + self.cursor_space_multiplier = None + self.cursor_columns = theme_config.get('cursor_columns') + self.spaces = theme_config['spaces'] + self.outer_padding = int(theme_config.get('outer_padding', 1)) + self.segments = [] + self.EMPTY_SEGMENT = { + 'contents': None, + 'highlight': {'fg': False, 'bg': False, 'attrs': 0} + } + self.pl = pl + theme_configs = [theme_config] + if main_theme_config: + theme_configs.append(main_theme_config) + get_segment = gen_segment_getter( + pl, + ext, + common_config, + theme_configs, + theme_config.get('default_module'), + get_module_attr, + top_theme + ) + for segdict in itertools.chain((theme_config['segments'],), + theme_config['segments'].get('above', ())): + self.segments.append(new_empty_segment_line()) + for side in ['left', 'right']: + for segment in segdict.get(side, []): + segment = get_segment(segment, side) + if segment: + if not run_once: + if segment['startup']: + try: + segment['startup'](pl, shutdown_event) + except Exception as e: + pl.error('Exception during {0} startup: {1}', segment['name'], str(e)) + continue + self.segments[-1][side].append(segment) + + def shutdown(self): + for line in self.segments: + for segments in line.values(): + for segment in segments: + try: + segment['shutdown']() + except TypeError: + pass + + def get_divider(self, side='left', type='soft'): + '''Return segment divider.''' + return self.dividers[side][type] + + def get_spaces(self): + return self.spaces + + def get_line_number(self): + return len(self.segments) + + def get_segments(self, side=None, line=0, segment_info=None, mode=None): + '''Return all segments. + + Function segments are called, and all segments get their before/after + and ljust/rjust properties applied. + + :param int line: + Line number for which segments should be obtained. Is counted from + zero (botmost line). + ''' + for side in [side] if side else ['left', 'right']: + parsed_segments = [] + for segment in self.segments[line][side]: + if segment['display_condition'](self.pl, segment_info, mode): + process_segment( + self.pl, + side, + segment_info, + parsed_segments, + segment, + mode, + self.colorscheme, + ) + for segment in parsed_segments: + self.pl.prefix = segment['name'] + try: + width = segment['width'] + align = segment['align'] + if width == 'auto' and segment['expand'] is None: + segment['expand'] = expand_functions.get(align) + if segment['expand'] is None: + self.pl.error('Align argument must be “r”, “l” or “c”, not “{0}”', align) + + try: + segment['contents'] = segment['before'] + u( + segment['contents'] if segment['contents'] is not None else '' + ) + segment['after'] + except Exception as e: + self.pl.exception('Failed to compute segment contents: {0}', str(e)) + segment['contents'] = safe_unicode(segment.get('contents')) + # Align segment contents + if segment['width'] and segment['width'] != 'auto': + if segment['align'] == 'l': + segment['contents'] = segment['contents'].ljust(segment['width']) + elif segment['align'] == 'r': + segment['contents'] = segment['contents'].rjust(segment['width']) + elif segment['align'] == 'c': + segment['contents'] = segment['contents'].center(segment['width']) + # We need to yield a copy of the segment, or else mode-dependent + # segment contents can’t be cached correctly e.g. when caching + # non-current window contents for vim statuslines + yield segment.copy() + except Exception as e: + self.pl.exception('Failed to compute segment: {0}', str(e)) + fallback = get_fallback_segment() + fallback.update(side=side) + yield fallback diff --git a/powerline-bin/powerline/vim.py b/powerline-bin/powerline/vim.py new file mode 100644 index 0000000..603a6a5 --- /dev/null +++ b/powerline-bin/powerline/vim.py @@ -0,0 +1,347 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import json +import logging + +from itertools import count + +try: + import vim +except ImportError: + vim = object() + +from powerline.bindings.vim import vim_get_func, vim_getvar, get_vim_encoding, python_to_vim +from powerline import Powerline, FailedUnicode, finish_common_config +from powerline.lib.dict import mergedicts +from powerline.lib.unicode import u + + +def _override_from(config, override_varname, key=None): + try: + overrides = vim_getvar(override_varname) + except KeyError: + return config + if key is not None: + try: + overrides = overrides[key] + except KeyError: + return config + mergedicts(config, overrides) + return config + + +class VimVarHandler(logging.Handler, object): + '''Vim-specific handler which emits messages to Vim global variables + + :param str varname: + Variable where + ''' + def __init__(self, varname): + super(VimVarHandler, self).__init__() + utf_varname = u(varname) + self.vim_varname = utf_varname.encode('ascii') + vim.command('unlet! g:' + utf_varname) + vim.command('let g:' + utf_varname + ' = []') + + def emit(self, record): + message = u(record.message) + if record.exc_text: + message += '\n' + u(record.exc_text) + vim.eval(b'add(g:' + self.vim_varname + b', ' + python_to_vim(message) + b')') + + +class VimPowerline(Powerline): + def init(self, pyeval='PowerlinePyeval', **kwargs): + super(VimPowerline, self).init('vim', **kwargs) + self.last_window_id = 1 + self.pyeval = pyeval + self.construct_window_statusline = self.create_window_statusline_constructor() + if all((hasattr(vim.current.window, attr) for attr in ('options', 'vars', 'number'))): + self.win_idx = self.new_win_idx + else: + self.win_idx = self.old_win_idx + self._vim_getwinvar = vim_get_func('getwinvar', 'bytes') + self._vim_setwinvar = vim_get_func('setwinvar') + + if sys.version_info < (3,): + def create_window_statusline_constructor(self): + window_statusline = b'%!' + str(self.pyeval) + b'(\'powerline.statusline({0})\')' + return window_statusline.format + else: + def create_window_statusline_constructor(self): + startstr = b'%!' + self.pyeval.encode('ascii') + b'(\'powerline.statusline(' + endstr = b')\')' + return lambda idx: ( + startstr + str(idx).encode('ascii') + endstr + ) + + create_window_statusline_constructor.__doc__ = ( + '''Create function which returns &l:stl value being given window index + + Created function must return :py:class:`bytes` instance because this is + what ``window.options['statusline']`` returns (``window`` is + :py:class:`vim.Window` instance). + + :return: + Function with type ``int → bytes``. + ''' + ) + + default_log_stream = sys.stdout + + def add_local_theme(self, key, config): + '''Add local themes at runtime (during vim session). + + :param str key: + Matcher name (in format ``{matcher_module}.{module_attribute}`` or + ``{module_attribute}`` if ``{matcher_module}`` is + ``powerline.matchers.vim``). Function pointed by + ``{module_attribute}`` should be hashable and accept a dictionary + with information about current buffer and return boolean value + indicating whether current window matched conditions. See also + :ref:`local_themes key description `. + + :param dict config: + :ref:`Theme ` dictionary. + + :return: + ``True`` if theme was added successfully and ``False`` if theme with + the same matcher already exists. + ''' + self.update_renderer() + matcher = self.get_matcher(key) + theme_config = {} + for cfg_path in self.theme_levels: + try: + lvl_config = self.load_config(cfg_path, 'theme') + except IOError: + pass + else: + mergedicts(theme_config, lvl_config) + mergedicts(theme_config, config) + try: + self.renderer.add_local_theme(matcher, {'config': theme_config}) + except KeyError: + return False + else: + # Hack for local themes support: when reloading modules it is not + # guaranteed that .add_local_theme will be called once again, so + # this function arguments will be saved here for calling from + # .do_setup(). + self.setup_kwargs.setdefault('_local_themes', []).append((key, config)) + return True + + get_encoding = staticmethod(get_vim_encoding) + + def load_main_config(self): + main_config = _override_from(super(VimPowerline, self).load_main_config(), 'powerline_config_overrides') + try: + use_var_handler = bool(int(vim_getvar('powerline_use_var_handler'))) + except KeyError: + use_var_handler = False + if use_var_handler: + main_config.setdefault('common', {}) + main_config['common'] = finish_common_config(self.get_encoding(), main_config['common']) + main_config['common']['log_file'].append(['powerline.vim.VimVarHandler', [['powerline_log_messages']]]) + return main_config + + def load_theme_config(self, name): + return _override_from( + super(VimPowerline, self).load_theme_config(name), + 'powerline_theme_overrides', + name + ) + + def get_local_themes(self, local_themes): + if not local_themes: + return {} + + return dict(( + (matcher, {'config': self.load_theme_config(val)}) + for matcher, key, val in ( + ( + (None if k == '__tabline__' else self.get_matcher(k)), + k, + v + ) + for k, v in local_themes.items() + ) if ( + matcher or + key == '__tabline__' + ) + )) + + def get_matcher(self, match_name): + match_module, separator, match_function = match_name.rpartition('.') + if not separator: + match_module = 'powerline.matchers.{0}'.format(self.ext) + match_function = match_name + return self.get_module_attr(match_module, match_function, prefix='matcher_generator') + + def get_config_paths(self): + try: + return vim_getvar('powerline_config_paths') + except KeyError: + return super(VimPowerline, self).get_config_paths() + + def do_setup(self, pyeval=None, pycmd=None, can_replace_pyeval=True, _local_themes=()): + import __main__ + if not pyeval: + pyeval = 'pyeval' if sys.version_info < (3,) else 'py3eval' + can_replace_pyeval = True + if not pycmd: + pycmd = get_default_pycmd() + + set_pycmd(pycmd) + + # pyeval() and vim.bindeval were both introduced in one patch + if (not hasattr(vim, 'bindeval') and can_replace_pyeval) or pyeval == 'PowerlinePyeval': + vim.command((''' + function! PowerlinePyeval(e) + {pycmd} powerline.do_pyeval() + endfunction + ''').format(pycmd=pycmd)) + pyeval = 'PowerlinePyeval' + + self.pyeval = pyeval + self.construct_window_statusline = self.create_window_statusline_constructor() + + self.update_renderer() + __main__.powerline = self + + try: + if ( + bool(int(vim.eval('has(\'gui_running\') && argc() == 0'))) + and not vim.current.buffer.name + and len(vim.windows) == 1 + ): + # Hack to show startup screen. Problems in GUI: + # - Defining local value of &statusline option while computing + # global value purges startup screen. + # - Defining highlight group while computing statusline purges + # startup screen. + # This hack removes the “while computing statusline” part: both + # things are defined, but they are defined right now. + # + # The above condition disables this hack if no GUI is running, + # Vim did not open any files and there is only one window. + # Without GUI everything works, in other cases startup screen is + # not shown. + self.new_window() + except UnicodeDecodeError: + # vim.current.buffer.name may raise UnicodeDecodeError when using + # Python-3*. Fortunately, this means that current buffer is not + # empty buffer, so the above condition should be False. + pass + + # Cannot have this in one line due to weird newline handling (in :execute + # context newline is considered part of the command in just the same cases + # when bar is considered part of the command (unless defining function + # inside :execute)). vim.command is :execute equivalent regarding this case. + vim.command('augroup Powerline') + vim.command(' autocmd! ColorScheme * :{pycmd} powerline.reset_highlight()'.format(pycmd=pycmd)) + vim.command(' autocmd! VimLeavePre * :{pycmd} powerline.shutdown()'.format(pycmd=pycmd)) + vim.command('augroup END') + + # Hack for local themes support after reloading. + for args in _local_themes: + self.add_local_theme(*args) + + def reset_highlight(self): + try: + self.renderer.reset_highlight() + except AttributeError: + # Renderer object appears only after first `.render()` call. Thus if + # ColorScheme event happens before statusline is drawn for the first + # time AttributeError will be thrown for the self.renderer. It is + # fine to ignore it: no renderer == no colors to reset == no need to + # do anything. + pass + + def new_win_idx(self, window_id): + r = None + for window in vim.windows: + try: + curwindow_id = window.vars['powerline_window_id'] + if r is not None and curwindow_id == window_id: + raise KeyError + except KeyError: + curwindow_id = self.last_window_id + self.last_window_id += 1 + window.vars['powerline_window_id'] = curwindow_id + statusline = self.construct_window_statusline(curwindow_id) + if window.options['statusline'] != statusline: + window.options['statusline'] = statusline + if curwindow_id == window_id if window_id else window is vim.current.window: + r = (window, curwindow_id, window.number) + return r + + def old_win_idx(self, window_id): + r = None + for winnr, window in zip(count(1), vim.windows): + curwindow_id = self._vim_getwinvar(winnr, 'powerline_window_id') + if curwindow_id and not (r is not None and curwindow_id == window_id): + curwindow_id = int(curwindow_id) + else: + curwindow_id = self.last_window_id + self.last_window_id += 1 + self._vim_setwinvar(winnr, 'powerline_window_id', curwindow_id) + statusline = self.construct_window_statusline(curwindow_id) + if self._vim_getwinvar(winnr, '&statusline') != statusline: + self._vim_setwinvar(winnr, '&statusline', statusline) + if curwindow_id == window_id if window_id else window is vim.current.window: + r = (window, curwindow_id, winnr) + return r + + def statusline(self, window_id): + window, window_id, winnr = self.win_idx(window_id) or (None, None, None) + if not window: + return FailedUnicode('No window {0}'.format(window_id)) + return self.render(window, window_id, winnr) + + def tabline(self): + return self.render(*self.win_idx(None), is_tabline=True) + + def new_window(self): + return self.render(*self.win_idx(None)) + + @staticmethod + def do_pyeval(): + '''Evaluate python string passed to PowerlinePyeval + + Is here to reduce the number of requirements to __main__ globals to just + one powerline object (previously it required as well vim and json). + ''' + import __main__ + vim.command('return ' + json.dumps(eval(vim.eval('a:e'), __main__.__dict__))) + + def setup_components(self, components): + if components is None: + components = ('statusline', 'tabline') + if 'statusline' in components: + # Is immediately changed after new_window function is run. Good for + # global value. + vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format( + pyeval=self.pyeval)) + if 'tabline' in components: + vim.command('set tabline=%!{pyeval}(\'powerline.tabline()\')'.format( + pyeval=self.pyeval)) + + +pycmd = None + + +def set_pycmd(new_pycmd): + global pycmd + pycmd = new_pycmd + + +def get_default_pycmd(): + return 'python' if sys.version_info < (3,) else 'python3' + + +def setup(*args, **kwargs): + powerline = VimPowerline() + return powerline.setup(*args, **kwargs) diff --git a/powerline-bin/powerline_status-2.7.dist-info/INSTALLER b/powerline-bin/powerline_status-2.7.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/powerline-bin/powerline_status-2.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/powerline-bin/powerline_status-2.7.dist-info/METADATA b/powerline-bin/powerline_status-2.7.dist-info/METADATA new file mode 100644 index 0000000..cedcc1b --- /dev/null +++ b/powerline-bin/powerline_status-2.7.dist-info/METADATA @@ -0,0 +1,123 @@ +Metadata-Version: 2.1 +Name: powerline-status +Version: 2.7 +Summary: The ultimate statusline/prompt utility. +Home-page: https://github.com/powerline/powerline +Download-URL: https://github.com/powerline/powerline/archive/develop.zip +Author: Kim Silkebaekken +Author-email: kim.silkebaekken+vim@gmail.com +License: MIT +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: Plugins +Classifier: Intended Audience :: End Users/Desktop +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Provides-Extra: docs +Requires-Dist: Sphinx ; extra == 'docs' +Requires-Dist: sphinx-rtd-theme ; extra == 'docs' + +Powerline +========= + +:Author: Kim Silkebækken (kim.silkebaekken+vim@gmail.com) +:Source: https://github.com/powerline/powerline +:Version: beta + +**Powerline is a statusline plugin for vim, and provides statuslines and +prompts for several other applications, including zsh, bash, fish, tmux, +IPython, Awesome, i3 and Qtile.** + +* `Support forum`_ (powerline-support@googlegroups.com) +* `Development discussion`_ (powerline-dev@googlegroups.com) + +.. image:: https://api.travis-ci.org/powerline/powerline.svg?branch=develop + :target: `travis-build-status`_ + :alt: Build status + +.. _travis-build-status: https://travis-ci.org/powerline/powerline +.. _`Support forum`: https://groups.google.com/forum/#!forum/powerline-support +.. _`Development discussion`: https://groups.google.com/forum/#!forum/powerline-dev + +Features +-------- + +* **Extensible and feature rich, written in Python.** Powerline was + completely rewritten in Python to get rid of as much vimscript as + possible. This has allowed much better extensibility, leaner and better + config files, and a structured, object-oriented codebase with no mandatory + third-party dependencies other than a Python interpreter. +* **Stable and testable code base.** Using Python has allowed unit testing + of all the project code. The code is tested to work in Python 2.6+ and + Python 3. +* **Support for prompts and statuslines in many applications.** Originally + created exclusively for vim statuslines, the project has evolved to + provide statuslines in tmux and several WMs, and prompts for shells like + bash/zsh and other applications. It’s simple to write renderers for any + other applications that Powerline doesn’t yet support. +* **Configuration and colorschemes written in JSON.** JSON is + a standardized, simple and easy to use file format that allows for easy + user configuration across all of Powerline’s supported applications. +* **Fast and lightweight, with daemon support for even better performance.** + Although the code base spans a couple of thousand lines of code with no + goal of “less than X lines of code”, the main focus is on good performance + and as little code as possible while still providing a rich set of + features. The new daemon also ensures that only one Python instance is + launched for prompts and statuslines, which provides excellent + performance. + +*But I hate Python / I don’t need shell prompts / this is just too much +hassle for me / what happened to the original vim-powerline project / …* + +You should check out some of the Powerline derivatives. The most lightweight +and feature-rich alternative is currently Bailey Ling’s `vim-airline +`_ project. + +------ + +* Consult the `documentation + `_ for more information and + installation instructions. +* Check out `powerline-fonts `_ for + pre-patched versions of popular, open source coding fonts. + +Screenshots +----------- + +Vim statusline +^^^^^^^^^^^^^^ + +**Mode-dependent highlighting** + +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-mode-normal.png + :alt: Normal mode +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-mode-insert.png + :alt: Insert mode +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-mode-visual.png + :alt: Visual mode +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-mode-replace.png + :alt: Replace mode + +**Automatic truncation of segments in small windows** + +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-truncate1.png + :alt: Truncation illustration +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-truncate2.png + :alt: Truncation illustration +* .. image:: https://raw.github.com/powerline/powerline/develop/docs/source/_static/img/pl-truncate3.png + :alt: Truncation illustration + +---- + +The font in the screenshots is `Pragmata Pro`_ by Fabrizio Schiavi. + +.. _`Pragmata Pro`: http://www.fsd.it/shop/fonts/pragmatapro diff --git a/powerline-bin/powerline_status-2.7.dist-info/RECORD b/powerline-bin/powerline_status-2.7.dist-info/RECORD new file mode 100644 index 0000000..c5c181d --- /dev/null +++ b/powerline-bin/powerline_status-2.7.dist-info/RECORD @@ -0,0 +1,341 @@ +../../bin/powerline,sha256=lDOwPm5LtMmDIzcyrGpRGulaCIk4zRBFYPC6VrRQmU0,16896 +../../bin/powerline-config,sha256=0LegBM-f5VjuYJDHj1tsUbRHvyupwxw8Ap-t3pTHL7g,589 +../../bin/powerline-daemon,sha256=b6LyMVUKqiUF1RlZb1QxpfnTYJRyIlJAz29VfSUZ8oU,12815 +../../bin/powerline-lint,sha256=uzDZilpsl82smzojbX4zFYILqZt17wJUXfFadWuoZG0,350 +../../bin/powerline-render,sha256=gAVsMPgvET2-aPxFKJpU2oBX2UWWrvmPCaQ2Ggt5PFQ,893 +powerline/__init__.py,sha256=OlONs_kZNRukhHFl1Ob92trLXQzln9jOOR65yYlIOz4,31815 +powerline/__pycache__/__init__.cpython-312.pyc,, +powerline/__pycache__/colorscheme.cpython-312.pyc,, +powerline/__pycache__/config.cpython-312.pyc,, +powerline/__pycache__/ipython.cpython-312.pyc,, +powerline/__pycache__/lemonbar.cpython-312.pyc,, +powerline/__pycache__/pdb.cpython-312.pyc,, +powerline/__pycache__/renderer.cpython-312.pyc,, +powerline/__pycache__/segment.cpython-312.pyc,, +powerline/__pycache__/shell.cpython-312.pyc,, +powerline/__pycache__/theme.cpython-312.pyc,, +powerline/__pycache__/vim.cpython-312.pyc,, +powerline/bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/bindings/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/__pycache__/config.cpython-312.pyc,, +powerline/bindings/awesome/__pycache__/powerline-awesome.cpython-312.pyc,, +powerline/bindings/awesome/powerline-awesome.py,sha256=jErhi5Tw6B6i5Tfc9CrguqexXupIMlGes8HJzFtS3nA,424 +powerline/bindings/awesome/powerline.lua,sha256=h3mRPj2oYu_6g2WNEH9sxz3kIUBJAdK8ASq1_yT-nRI,436 +powerline/bindings/bar/__pycache__/powerline-bar.cpython-312.pyc,, +powerline/bindings/bar/powerline-bar.py,sha256=gXE7tb9JBH9K48X72J1SHdk1-aLIMyo2FvzvyxAI9Ew,1501 +powerline/bindings/bash/powerline.sh,sha256=tKP6vwHIU5V05h61XP4EB3nZuwAoAOWyi3VafdMKz4E,4173 +powerline/bindings/config.py,sha256=nhGg7trWu8sm0eBy6CmuyBGrDFcIS0Hl-JtQNTb9pVY,10072 +powerline/bindings/fish/powerline-setup.fish,sha256=uhlgtsda5n9xw6JsXPeP_QBwoJmNlIW5dPDmhgbzIgY,3379 +powerline/bindings/i3/__pycache__/powerline-i3.cpython-312.pyc,, +powerline/bindings/i3/powerline-i3.py,sha256=Z4peBk1b99K8tjZ2x4QfAF-lBwBOjL1aKIAkqPGi_JI,1067 +powerline/bindings/ipython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/bindings/ipython/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/ipython/__pycache__/post_0_11.cpython-312.pyc,, +powerline/bindings/ipython/__pycache__/pre_0_11.cpython-312.pyc,, +powerline/bindings/ipython/__pycache__/since_5.cpython-312.pyc,, +powerline/bindings/ipython/post_0_11.py,sha256=xQRqEGH3eY2kCjSVxS7RPfpH-uSxV41YZffUajDq-Ng,3366 +powerline/bindings/ipython/pre_0_11.py,sha256=qcBmfNpEYP3e8wpegvQaNqaBYo9e0gIjAA39y7KtjN8,3856 +powerline/bindings/ipython/since_5.py,sha256=EEePucPOQbKZitaes1W-xizhztILxRpgH7mDRc7Eky4,2645 +powerline/bindings/lemonbar/__pycache__/powerline-lemonbar.cpython-312.pyc,, +powerline/bindings/lemonbar/powerline-lemonbar.py,sha256=tXb5UK0nocAfu8sTbabZXdcO11AJCf5Bt7F-yIELJaE,1616 +powerline/bindings/pdb/__init__.py,sha256=Ng4LTyOPm6ndxsHIKDQvu57fwj_X8vDV6iV5X2kqipw,4750 +powerline/bindings/pdb/__main__.py,sha256=BWOLmcfeZywZo3kPk7xgNJEhmdFwHZcqvFtQhMeWFeM,215 +powerline/bindings/pdb/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/pdb/__pycache__/__main__.cpython-312.pyc,, +powerline/bindings/qtile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/bindings/qtile/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/qtile/__pycache__/widget.cpython-312.pyc,, +powerline/bindings/qtile/widget.py,sha256=sLDrchQEc-Bi-Q1cf_Ex_efRSvzG8C9RyH7toQLZYSs,1638 +powerline/bindings/rc/powerline.rc,sha256=81uihtm7MWosbkxLCJU4jXZD5OzUHlVbmU4EsZVGd8M,2345 +powerline/bindings/shell/powerline.sh,sha256=vp4aw9uhoyA4yWvon7UVItNswAZ4J8E538yNtHv9HNA,6419 +powerline/bindings/tcsh/powerline.tcsh,sha256=ae8WFFpx1i0j92AojhUeRGusTc_ATtBbMqPf522L6i4,2869 +powerline/bindings/tmux/__init__.py,sha256=el5txQKGcZtpw8-CzJCjMN6mNXeeWhaMx0GSDqDtaxs,2216 +powerline/bindings/tmux/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/tmux/powerline-base.conf,sha256=Odsp62RkZwbARVWUlN7Jsj2aml0mQgDe2FQPvt8eSFE,1039 +powerline/bindings/tmux/powerline.conf,sha256=EKvp1Db-_kQZaJa6ZNK3KxE9Uvjrsc45O7oMmfwDc7Y,114 +powerline/bindings/tmux/powerline_tmux_1.7_plus.conf,sha256=g_k1zyVu13CD2OHUNlgFbtTNdj63qsYjvagB3mjUxaQ,589 +powerline/bindings/tmux/powerline_tmux_1.8.conf,sha256=dkrD7xg28eu4osBQNJrU5o4xqM8ne45KfR4ehiMxWUI,243 +powerline/bindings/tmux/powerline_tmux_1.8_minus.conf,sha256=QnwfBXl9BOBVHSkYc-ZiIP9-ERrJZ2kKhzIZSP7DzcE,670 +powerline/bindings/tmux/powerline_tmux_1.8_plus.conf,sha256=gKmxjWste_fW-2u0B3kKiKrCrw7TmhZLkplHfiwHmgQ,750 +powerline/bindings/tmux/powerline_tmux_1.9_plus.conf,sha256=5bbJI1_MIIPMUt_t-Xtx-aXvc1D09sFprlDklMSCxOg,503 +powerline/bindings/tmux/powerline_tmux_2.1_plus.conf,sha256=3sD3pR30nQFG_JkctD8SSN_mQ0Q_opy89S69WXXZ6tU,907 +powerline/bindings/vim/__init__.py,sha256=epnmNJcLo5muWv65mITJAZBUtQ2n_UFBk5TISJugj6s,11453 +powerline/bindings/vim/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/vim/autoload/powerline/debug.vim,sha256=Q-SNvTvLX6RXy1HUhXqlnaHDv1bn7l4gU8KKPPb8kxU,507 +powerline/bindings/vim/plugin/powerline.vim,sha256=oq1kd4eq5zr2DPSWu-TTkiu0w_Lpo-qpYA02wpojtw0,7008 +powerline/bindings/wm/__init__.py,sha256=10woQI2J3DmOFQgXuodLN577JGNnJhp3jrJnhv19LeI,1712 +powerline/bindings/wm/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/wm/__pycache__/awesome.cpython-312.pyc,, +powerline/bindings/wm/awesome.py,sha256=mFHelMeRS7Y0zTN8FOOFyCgLy9rAYMArdlJ-9qmTji0,1773 +powerline/bindings/zsh/__init__.py,sha256=ca0pbLjmpPWT6NWBRoi0XhZEXvOBygRRAbFSH8IVAQ4,5559 +powerline/bindings/zsh/__pycache__/__init__.cpython-312.pyc,, +powerline/bindings/zsh/powerline.zsh,sha256=OGK2wDuXdBbGWksJZhDpSpdDt6zmDtnimlZFuzGkpUM,6680 +powerline/colorscheme.py,sha256=En2pQE-hExE4NNWrJc2chnY8pKSdqLkT4cgWh7k8sDU,6356 +powerline/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/commands/__pycache__/__init__.cpython-312.pyc,, +powerline/commands/__pycache__/config.cpython-312.pyc,, +powerline/commands/__pycache__/daemon.cpython-312.pyc,, +powerline/commands/__pycache__/lemonbar.cpython-312.pyc,, +powerline/commands/__pycache__/lint.cpython-312.pyc,, +powerline/commands/__pycache__/main.cpython-312.pyc,, +powerline/commands/config.py,sha256=0w755lRs06_DmLOtUUkeHtNf0cXWiEZ8u_m_hccseHc,3943 +powerline/commands/daemon.py,sha256=nIAXrrMxVIfqaO9T-freTpzreiKAiwIeaKN57A60YmE,1203 +powerline/commands/lemonbar.py,sha256=YKv-sdm1S8gQBSgGPrpDz1RLz3Dmvzill4n-ug5Qp14,960 +powerline/commands/lint.py,sha256=WqSY2Mo-yHBEuwp1iB0l7XZ0RbtblVd10ZQUPjmi-78,782 +powerline/commands/main.py,sha256=-sO1UTNaWJfDhwuf065cjcnIPttsLgQXQUVaPKxfpGM,7102 +powerline/config.py,sha256=OLvRWmts34yPleQ4grfSue0m2b1r38q9HAcWy4Y5H6Q,378 +powerline/config_files/colors.json,sha256=gTilZEvJMB7VgaR9sqPlhuh2WwU6Y2rUInqWcCpv76I,13825 +powerline/config_files/colorschemes/default.json,sha256=w5sPCE0R6ULPZ5OyN-EfNmUVknOHOnQrJczwk-d8BjY,4034 +powerline/config_files/colorschemes/ipython/__main__.json,sha256=3A0gtfqooxa8NgpDCvsdLVj_Ts_kUJVjYxygc815qg8,108 +powerline/config_files/colorschemes/pdb/__main__.json,sha256=fZqc5rKVadYU0f8qzF-dfEXf4EUnI85sfg2lwLmDOCM,204 +powerline/config_files/colorschemes/pdb/default.json,sha256=mULVwjN5KbdOPBX7RSwkYt3hWLTGs4pWt0JFOpv2uXM,90 +powerline/config_files/colorschemes/pdb/solarized.json,sha256=_2399-dZGXgzYwsERJQSdqzablOYlMFY5Rv8R-Nvqx0,110 +powerline/config_files/colorschemes/shell/__main__.json,sha256=bMmasTlTMNVD7JES7UNBvMkVpTnmJGXR8C1_fghBMwI,287 +powerline/config_files/colorschemes/shell/default.json,sha256=FZ6EEWm8vY3-SkXSrOtuH7OR3q3vtgPq93JSfCqNMYM,526 +powerline/config_files/colorschemes/shell/solarized.json,sha256=rTw2_fQIkvUZ3zECrKjsDlsIIytGLNUTQi6XwxCk4w4,287 +powerline/config_files/colorschemes/solarized.json,sha256=AecXleKFHJ08fk1Jy9aWaiESlmczT2i5myZPNp375ng,3234 +powerline/config_files/colorschemes/tmux/default.json,sha256=4ZGEbiteAao1M-uh0l-DniA3ooou0Yy_lpSGz23nC5c,827 +powerline/config_files/colorschemes/tmux/solarized.json,sha256=A6u53LgitzpISL1PbfqyIzn-e_vt_4hkBJgrlKboAjE,975 +powerline/config_files/colorschemes/vim/__main__.json,sha256=0vWv_ElYejCdPLvWYwlqVoBj1NHR8hdphbHlhVL-xEw,1898 +powerline/config_files/colorschemes/vim/default.json,sha256=XCssH0VrZwcXaWUWs-uqc8GjfEDZQjJuLecsEWxew8U,5751 +powerline/config_files/colorschemes/vim/solarized.json,sha256=Mhyut7AAGdANwHMRhSNCqGzpZ_WpcH1d0yGYbPM1imM,7308 +powerline/config_files/colorschemes/vim/solarizedlight.json,sha256=l5KSVo67x8A4kUpwP2uxSu91PIpx12_bkzTMyt2tnP0,7370 +powerline/config_files/config.json,sha256=Nz7mX1CZT2Si9xNsbmZIkYqaUOcriS-3QLxQrm-xNPc,1085 +powerline/config_files/themes/ascii.json,sha256=c4gS2nfCnyt2xtmgQCfgEgeWqdMhg4pYaZ9MF86o7TI,2802 +powerline/config_files/themes/ipython/in.json,sha256=AFRzWaHMR-Jxr5-EaxJ3eS5kvNcxfFZ4vIazRG1x8eg,441 +powerline/config_files/themes/ipython/in2.json,sha256=jHbda0B8okwFrppjnt5F7r1pDlfZmNey646Dlg6lPSo,146 +powerline/config_files/themes/ipython/out.json,sha256=AvuQXwpG1quV4X83lTa4QxLFtMfT16S5PcLEoZDdzwQ,412 +powerline/config_files/themes/ipython/rewrite.json,sha256=7eWe1-M1SZMNuhmwfk8ZyT_rDVNrp37WvzY8lPBqzyQ,390 +powerline/config_files/themes/pdb/default.json,sha256=YtHs_V275lLBP4CJ6K1y5SWZ8VIPTrdwalH5Q0JnPfw,428 +powerline/config_files/themes/powerline.json,sha256=g5AwjK07wb7iKeBwXg73LL4eYToMghZC1CcEPUKTsfE,2787 +powerline/config_files/themes/powerline_terminus.json,sha256=5ZotCW0WovP5_ELLyIQz1f0Kd7cITjiWBQOq-XPd2Jk,2793 +powerline/config_files/themes/powerline_unicode7.json,sha256=Om9dy9k131I91Pf3w3VNLGP9JR24fPXCHdGsFL98Yag,3473 +powerline/config_files/themes/shell/__main__.json,sha256=UentPO6d_fPTLzGoSLWLV2XW7V93XiORJR4bb6c5LkI,147 +powerline/config_files/themes/shell/continuation.json,sha256=tlkwVk-mESTzA7becsa9ESN_ZQWSGk79uwcxg96DdWU,144 +powerline/config_files/themes/shell/default.json,sha256=I9al25oqMm3OB9i-oJTFZJoR335HhTUKlHDX4P_xDso,796 +powerline/config_files/themes/shell/default_leftonly.json,sha256=1CTifJCK_8Le-f3Z-IFeifAdassqhZS_nFa_DA3hrHA,635 +powerline/config_files/themes/shell/select.json,sha256=jvkut6Au6Na90H8-D4okw-lAPzzO2Y_bW6l-TKns_Zc,192 +powerline/config_files/themes/tmux/default.json,sha256=4Im0rfgNa0uvd6wEzEPy6OtU-cJzywY27ZlDoNt8x0A,498 +powerline/config_files/themes/unicode.json,sha256=ssO-IdtPd39b9Xfj4Am-xTz2NOoP643WtOKGyIhZpb8,2786 +powerline/config_files/themes/unicode_terminus.json,sha256=4n78w2nMKWyprqYERgjBVsN_3FKG2_N_2ucgBx-O9a8,2785 +powerline/config_files/themes/unicode_terminus_condensed.json,sha256=vk3U1cN0o08hpOueUgQH6uI5IZ7_sXaXec79yHXDZJg,2667 +powerline/config_files/themes/vim/__main__.json,sha256=LVOwJEC2WuzSBHvH-xF37c1JOLVFI0qEuhDOTxHZ9v0,106 +powerline/config_files/themes/vim/cmdwin.json,sha256=mcmvOtO0qHcb-T6mP46g-ShKbhH4FPaLDM3PdTMJbew,297 +powerline/config_files/themes/vim/default.json,sha256=37TpZllWHi7E4pkn50RbXCUGl6MGjUaakDshu5qaGrY,2475 +powerline/config_files/themes/vim/help.json,sha256=Mt26DhFdSiLunLM3ES2sIl3kbzcZVrqIG_uvYfcQsw4,618 +powerline/config_files/themes/vim/plugin_commandt.json,sha256=fGnBxCTrkaAerrLKdS8gCNSpxPTg-ABjQu3QP3XKWaw,474 +powerline/config_files/themes/vim/plugin_gundo-preview.json,sha256=VcnI5CeuT2IrV7RTXJY1_zXS56BDqpT5vpa_8djrCac,328 +powerline/config_files/themes/vim/plugin_gundo.json,sha256=d79HaLwYDU6Whosx6CcoDm_A_Zlzzw0NkNr22LSMFDg,328 +powerline/config_files/themes/vim/plugin_nerdtree.json,sha256=gVCDBnf4iNZQDpU74hTmxbX8I2q0c_s8SgYaODTyFGQ,293 +powerline/config_files/themes/vim/quickfix.json,sha256=SPlmLOiKebNdwZ58EI9rSAYab9ve0RNObrM9o0jIKuU,700 +powerline/config_files/themes/vim/tabline.json,sha256=6Z-8vbFxGmrAvH5fXXNCABqLP5gEhsO_272h2bCZxWY,1701 +powerline/config_files/themes/wm/default.json,sha256=wkNXPLWEAiCM5PhPB9N6fa7TzAUuYftFEnAgvPRuKVE,500 +powerline/dist/systemd/powerline-daemon.service,sha256=sJuTXtGPYazfUDD8kinEH0zFoe_PpcIqOkpDs6Uqoaw,273 +powerline/ipython.py,sha256=4ppy2iykXpjtLLaBoXEbhk4FX1HcBh0ROsadcWrJoOY,1687 +powerline/lemonbar.py,sha256=oYj8FRDLrg5S82DtTqLoWrbEZY3oV9-IqYtlhEDNNzE,560 +powerline/lib/__init__.py,sha256=ybY2S45XodNfOA8OJqRNo8sb9kWkvFmVV924QcVngIU,596 +powerline/lib/__pycache__/__init__.cpython-312.pyc,, +powerline/lib/__pycache__/config.cpython-312.pyc,, +powerline/lib/__pycache__/debug.cpython-312.pyc,, +powerline/lib/__pycache__/dict.cpython-312.pyc,, +powerline/lib/__pycache__/encoding.cpython-312.pyc,, +powerline/lib/__pycache__/humanize_bytes.cpython-312.pyc,, +powerline/lib/__pycache__/inotify.cpython-312.pyc,, +powerline/lib/__pycache__/memoize.cpython-312.pyc,, +powerline/lib/__pycache__/monotonic.cpython-312.pyc,, +powerline/lib/__pycache__/overrides.cpython-312.pyc,, +powerline/lib/__pycache__/path.cpython-312.pyc,, +powerline/lib/__pycache__/shell.cpython-312.pyc,, +powerline/lib/__pycache__/threaded.cpython-312.pyc,, +powerline/lib/__pycache__/unicode.cpython-312.pyc,, +powerline/lib/__pycache__/url.cpython-312.pyc,, +powerline/lib/config.py,sha256=7i6gmddF_tGgbN0C9UcXUlX3F92jW4G80JtVMusm2Rw,6210 +powerline/lib/debug.py,sha256=mLHmTUf9kvjcWq_oCFpGRCEkXgPGvASSJeYwrFc3ujY,3024 +powerline/lib/dict.py,sha256=VsBJRzJtE3qDhNdJCYRbwcDuDXqP8yu7H0RGLy4Y3_o,1943 +powerline/lib/encoding.py,sha256=Hl7Lrgb8xOsHjxSoYBflduPbchQVJ3p69G-gJl9KQTY,3263 +powerline/lib/humanize_bytes.py,sha256=2i7DURd0IgcTaRwDJVyXxzmsjJRiJXmuNUOx3LCAnww,808 +powerline/lib/inotify.py,sha256=HaXM3SjVNj2lX4JwDUn0z77DEuMNj4qEr6AC5Gp-9dM,6081 +powerline/lib/memoize.py,sha256=0noBcRaXd4rTGwoRDQ_qND_r1NSgsbK8qKuydyPI8AM,1235 +powerline/lib/monotonic.py,sha256=1AO_lYxiKza6GsyK2jWLC2SD5nDfoqGNpLqbmehz41Y,2721 +powerline/lib/overrides.py,sha256=c1HQOmbYUUJOLKwLczldQgJED5siEdVfShSRHXDp_88,1815 +powerline/lib/path.py,sha256=J9UQ7c_F5MASkv0zyaN8tzWyv5msmdgSHFwaOklIPmw,421 +powerline/lib/shell.py,sha256=S1BfEY7KaZhPrbgOSHIKiKv-kYnWnwxAtiCnSvT9EEE,4164 +powerline/lib/threaded.py,sha256=YpIdwndgfQC-MjdU5PxsDmdu7y6tw-CTq6s1FTPHZl0,7276 +powerline/lib/unicode.py,sha256=nKdvJREpmN2Ex-3-plFGzQAuWvFqa5rBAw5gJHOg6co,9268 +powerline/lib/url.py,sha256=FKBySrtwCc2mGInyf61rh5Sv3RDxz5CL6TmniN1TbXI,514 +powerline/lib/vcs/__init__.py,sha256=rdm81UTHCTXMCUnNLHBbocmWYN-DoVu7pJlpSFbi9F0,7621 +powerline/lib/vcs/__pycache__/__init__.cpython-312.pyc,, +powerline/lib/vcs/__pycache__/bzr.cpython-312.pyc,, +powerline/lib/vcs/__pycache__/git.cpython-312.pyc,, +powerline/lib/vcs/__pycache__/mercurial.cpython-312.pyc,, +powerline/lib/vcs/bzr.py,sha256=dFPr36COABRSFuko1096XzwtRx2ofqohHDTG5Zka9wM,2999 +powerline/lib/vcs/git.py,sha256=88FgfncnsLjsxjUVnMSzghK_nnXA4rzAESuuAmPK2S0,5975 +powerline/lib/vcs/mercurial.py,sha256=Jgzx_ruFwX7OMBjBTJ8YQWmSWAAtM_4pWIxUIoqTAA0,2678 +powerline/lib/watcher/__init__.py,sha256=TNzc6BIf8RIK8mSjRa8smzW_DM-Ovx0WmopILnRvO_Y,2796 +powerline/lib/watcher/__pycache__/__init__.cpython-312.pyc,, +powerline/lib/watcher/__pycache__/inotify.cpython-312.pyc,, +powerline/lib/watcher/__pycache__/stat.cpython-312.pyc,, +powerline/lib/watcher/__pycache__/tree.cpython-312.pyc,, +powerline/lib/watcher/__pycache__/uv.cpython-312.pyc,, +powerline/lib/watcher/inotify.py,sha256=gPeRgOMDrrsW9k0lXrxrKlQouRdmBWK-UMqIrHeskrU,7921 +powerline/lib/watcher/stat.py,sha256=V7_jb2l5cfh71xvfaDIuB3Xnx1EyizQ29mTW3xMRI3c,956 +powerline/lib/watcher/tree.py,sha256=BGAMlLaB52hGpVI2olPkmGdMdImkCGPxxmOBgMNCcu4,2572 +powerline/lib/watcher/uv.py,sha256=97hn-LQWlzVUixWpzZeGSjHf6i_JSAZwPAYmQ8S-oec,4854 +powerline/lint/__init__.py,sha256=0NTfOhzee6zin5hhYgnGZF2vn28WKc14EagiMUsQmjo,21363 +powerline/lint/__pycache__/__init__.cpython-312.pyc,, +powerline/lint/__pycache__/checks.cpython-312.pyc,, +powerline/lint/__pycache__/context.cpython-312.pyc,, +powerline/lint/__pycache__/imp.cpython-312.pyc,, +powerline/lint/__pycache__/inspect.cpython-312.pyc,, +powerline/lint/__pycache__/selfcheck.cpython-312.pyc,, +powerline/lint/__pycache__/spec.cpython-312.pyc,, +powerline/lint/checks.py,sha256=-zkw6THzJtBXEbaSwp1_XCgqNGHCc3J1zsCg7Bt33mM,29244 +powerline/lint/context.py,sha256=wb4S4Z449LXUQ9aOdwUaId4gVnhQIm9MFVOSakrXX80,2046 +powerline/lint/imp.py,sha256=p3GB4I3bVL1gezlTeiT4GEf7sD8NkfnX4pWIfnKKhzQ,1876 +powerline/lint/inspect.py,sha256=CUHYLKfWXNgVbLQ9qEjKVj7EYbkWR8e6MmMobIOi4WU,1704 +powerline/lint/markedjson/__init__.py,sha256=GHS-laZyga74Xi0w3lUW0tZRdgSW2AZIpB27W-cwEOQ,552 +powerline/lint/markedjson/__pycache__/__init__.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/composer.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/constructor.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/error.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/events.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/loader.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/markedvalue.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/nodes.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/parser.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/reader.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/resolver.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/scanner.cpython-312.pyc,, +powerline/lint/markedjson/__pycache__/tokens.cpython-312.pyc,, +powerline/lint/markedjson/composer.py,sha256=yhUkR9XneZmxi6FzVnavtlUkU-kr4ebat0_8JKPwvEU,3589 +powerline/lint/markedjson/constructor.py,sha256=IH73Kumw91zVSLXnUFctMNKEapuVi1SXlyqhjSdlixo,7767 +powerline/lint/markedjson/error.py,sha256=vllFjKY9d5ANxWwkaO5lZgfRhIVyUSYHxMyTObHaREw,6948 +powerline/lint/markedjson/events.py,sha256=V7Gx1Y7qifj0fPaHDEzcUscFXwWHtyjW-Ksp-O_4d2I,2157 +powerline/lint/markedjson/loader.py,sha256=F6fZ6jg8lSYzkXFYEbMJnRMfSTshx11SmBQy1m5UXCk,879 +powerline/lint/markedjson/markedvalue.py,sha256=bdo2n3iPQxsLnqgs87U0CZn7zRcB-3-peIma0VTX4Rc,3548 +powerline/lint/markedjson/nodes.py,sha256=A1jE5ZS6AZBtrRi0G2RP_hLUShFEphKQqZsRoeLv9-M,1309 +powerline/lint/markedjson/parser.py,sha256=OQ7LW2Mw5nXOZmnY2dUHx6DqqFPTAwKla61tlpfrd3A,8124 +powerline/lint/markedjson/reader.py,sha256=iiZfGpQVAGsYiRV7GExwDIu9nt-egWlr0sszVymdc6k,4047 +powerline/lint/markedjson/resolver.py,sha256=q_6LKnxIznBBphAa6mMPVbVttPGox6nxTysQ85X8K64,3880 +powerline/lint/markedjson/scanner.py,sha256=LZ8mK7JeU6U1rFVmqEB1WIEDid22qsn--qhC3Vkuyf0,14219 +powerline/lint/markedjson/tokens.py,sha256=eKQJVOF2bupR-47Zarw2WAunpLzUxm4Ac8uihehz0G0,1319 +powerline/lint/selfcheck.py,sha256=_4Awt3l9_ysO6RnxEYy6KXE72lJhcHMXNsT0T5RuRYc,606 +powerline/lint/spec.py,sha256=MH6fCtdKqn3pt9XpNm5_QyT4Tu2C4QsozSzzfiUtY4g,23514 +powerline/listers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/listers/__pycache__/__init__.cpython-312.pyc,, +powerline/listers/__pycache__/i3wm.cpython-312.pyc,, +powerline/listers/__pycache__/pdb.cpython-312.pyc,, +powerline/listers/__pycache__/vim.cpython-312.pyc,, +powerline/listers/i3wm.py,sha256=zPG3kOkxF92_EAM6lu-jc-7ehlUlK5becDZl-gW1o_U,1866 +powerline/listers/pdb.py,sha256=E0cdeqJJkJHrD6IVkQw2LIjRDc0vaquLqdby5byRQ4w,984 +powerline/listers/vim.py,sha256=sk89nbMjoMU7XHvjH41lVmR9g9CE2VYFgqSm-C7ybM0,3819 +powerline/matchers/__init__.py,sha256=ip96xwRq4jc1fWAFrUjksQ0MyrY0clAGaKEPntRhAd8,192 +powerline/matchers/__pycache__/__init__.cpython-312.pyc,, +powerline/matchers/vim/__init__.py,sha256=BueTiyWuUIotNe-Pq88NkzYeP-EI8iOlMM6NKAsTT-4,495 +powerline/matchers/vim/__pycache__/__init__.cpython-312.pyc,, +powerline/matchers/vim/plugin/__init__.py,sha256=ip96xwRq4jc1fWAFrUjksQ0MyrY0clAGaKEPntRhAd8,192 +powerline/matchers/vim/plugin/__pycache__/__init__.cpython-312.pyc,, +powerline/matchers/vim/plugin/__pycache__/commandt.cpython-312.pyc,, +powerline/matchers/vim/plugin/__pycache__/gundo.cpython-312.pyc,, +powerline/matchers/vim/plugin/__pycache__/nerdtree.cpython-312.pyc,, +powerline/matchers/vim/plugin/commandt.py,sha256=lctjEpONmRDw8s3pIQQKORBE2cEB3de_zBp-iljX1oM,383 +powerline/matchers/vim/plugin/gundo.py,sha256=CaqDfQ_KV0_8GPvKE2zvc1Vch524Y8p_ZWEyWMwjx0c,424 +powerline/matchers/vim/plugin/nerdtree.py,sha256=NhHk99gDkcJS3CjidlatZKj-8gEMU9oCCyhgwuWP7hY,355 +powerline/pdb.py,sha256=8u1zBDdrZmF5TIo02zDpiAintfoqbIs8NeUFVStlnqc,1475 +powerline/renderer.py,sha256=VhIsTVQ8kMW4kHEdHZR8lt1Z0nx4vsdmYAfs9wSMkEM,20587 +powerline/renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/renderers/__pycache__/__init__.cpython-312.pyc,, +powerline/renderers/__pycache__/i3bar.cpython-312.pyc,, +powerline/renderers/__pycache__/lemonbar.cpython-312.pyc,, +powerline/renderers/__pycache__/pango_markup.cpython-312.pyc,, +powerline/renderers/__pycache__/pdb.cpython-312.pyc,, +powerline/renderers/__pycache__/tmux.cpython-312.pyc,, +powerline/renderers/__pycache__/vim.cpython-312.pyc,, +powerline/renderers/i3bar.py,sha256=ki85GqThA8V_SeINRDBuOQSc8yJf9bUmNVw7dc5r00s,900 +powerline/renderers/ipython/__init__.py,sha256=1jbx5bgIdqti2pZEDjGpzDzVXYWBQ_MVTBFYVLV3gI0,903 +powerline/renderers/ipython/__pycache__/__init__.cpython-312.pyc,, +powerline/renderers/ipython/__pycache__/pre_5.cpython-312.pyc,, +powerline/renderers/ipython/__pycache__/since_5.cpython-312.pyc,, +powerline/renderers/ipython/pre_5.py,sha256=Kx8VaP1O_NiaEiRJNuhQo9_bRljDlVEFZMg8mMu5JyI,1875 +powerline/renderers/ipython/since_5.py,sha256=bUNQdrP0mZscpQzaBmhRwXcAi7LUEJfpdcOeDonA0uw,3385 +powerline/renderers/lemonbar.py,sha256=R5la61s1f13_sp7nB1hGl1P7IKF3kjUm3I-vUyc-LGU,1810 +powerline/renderers/pango_markup.py,sha256=GplF7EMeZs1uROAp8pq7TYUBjBEjBiZ-OrNOu6NuGEw,1251 +powerline/renderers/pdb.py,sha256=iQ8Mfbg3Qs6nZ2cx_b0GfeFXULiVD4WOr_7oTrwmsGs,1431 +powerline/renderers/shell/__init__.py,sha256=KDXxJ6d_f6uHZfu-t_Y4IU0gy4JFlTSNxCOHe7GnHRo,5235 +powerline/renderers/shell/__pycache__/__init__.cpython-312.pyc,, +powerline/renderers/shell/__pycache__/bash.cpython-312.pyc,, +powerline/renderers/shell/__pycache__/ksh.cpython-312.pyc,, +powerline/renderers/shell/__pycache__/rcsh.cpython-312.pyc,, +powerline/renderers/shell/__pycache__/readline.cpython-312.pyc,, +powerline/renderers/shell/__pycache__/tcsh.cpython-312.pyc,, +powerline/renderers/shell/__pycache__/zsh.cpython-312.pyc,, +powerline/renderers/shell/bash.py,sha256=wvAjTulqUkuwZ7Kk-ZctGaiznlaw4_gpziuS17-BihU,535 +powerline/renderers/shell/ksh.py,sha256=cTiRDpAiCzcSYC1mJKC-pCRO1A2zpWz0kObgWThNvyY,473 +powerline/renderers/shell/rcsh.py,sha256=-7OksGhKeKKfAlQOObm2LBeHTOgsWAYgb_nByJu9mdk,210 +powerline/renderers/shell/readline.py,sha256=xis-_uZfRyf3jUZw3z27-X7cwu-qdwhiqDmJl1Vw_Eo,354 +powerline/renderers/shell/tcsh.py,sha256=sp4JeHCQN7GMNFwUdNzKPz_9X4U7DOZorcqektJzUt8,1119 +powerline/renderers/shell/zsh.py,sha256=okM9vrdadiqTJzH9YVUOtIepFvTZJXf2BJeVr3OZHeQ,445 +powerline/renderers/tmux.py,sha256=Xr1DriSTIXOp80GJKWn3OoAvygAcA9KLFiMCMLzEoYo,2201 +powerline/renderers/vim.py,sha256=uWvI-QM_GP-oen_EwlvYMrr3XS88rw4EwlwiZ3vVXQQ,5785 +powerline/segment.py,sha256=15Kii41kvxVKPFzOeydZnDLqEUctIq3oj6_JNkCdyzU,13488 +powerline/segments/__init__.py,sha256=7fHFg6DMuhgBTeO_vfgTOO3yr0m01hS0xeKL3858ClQ,1687 +powerline/segments/__pycache__/__init__.cpython-312.pyc,, +powerline/segments/__pycache__/i3wm.cpython-312.pyc,, +powerline/segments/__pycache__/ipython.cpython-312.pyc,, +powerline/segments/__pycache__/pdb.cpython-312.pyc,, +powerline/segments/__pycache__/shell.cpython-312.pyc,, +powerline/segments/__pycache__/tmux.cpython-312.pyc,, +powerline/segments/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/segments/common/__pycache__/__init__.cpython-312.pyc,, +powerline/segments/common/__pycache__/bat.cpython-312.pyc,, +powerline/segments/common/__pycache__/env.cpython-312.pyc,, +powerline/segments/common/__pycache__/mail.cpython-312.pyc,, +powerline/segments/common/__pycache__/net.cpython-312.pyc,, +powerline/segments/common/__pycache__/players.cpython-312.pyc,, +powerline/segments/common/__pycache__/sys.cpython-312.pyc,, +powerline/segments/common/__pycache__/time.cpython-312.pyc,, +powerline/segments/common/__pycache__/vcs.cpython-312.pyc,, +powerline/segments/common/__pycache__/wthr.cpython-312.pyc,, +powerline/segments/common/bat.py,sha256=WPlGqyKdzjdUm6gAQoQTN60GNt6aPj6b2y0pxpP7gSc,10782 +powerline/segments/common/env.py,sha256=Tywi3DMdfvFqF151W_kS3vDSbaQoOFUcskw9FwvioLE,5687 +powerline/segments/common/mail.py,sha256=yXX1ExWwW9HR8mPgUU8Wwt2AApIhm5wqawK7YBlbRDk,2474 +powerline/segments/common/net.py,sha256=hddUbzn9feolec2mDRcshZH4Gi8wJF6W2bOxMRvKMMg,9864 +powerline/segments/common/players.py,sha256=YcIEwsVUt4oFlVW78iUnyz3g0Xn4MDMylU4uqjbXRNc,17677 +powerline/segments/common/sys.py,sha256=1AmKHbgF9bge3mXS4eejbloSKPPHLhv5Uz37DHWIKBc,5645 +powerline/segments/common/time.py,sha256=4lQIuE4T8a-i2gmnirHFQmotSM4Bn0OHJL6apBinNhU,2205 +powerline/segments/common/vcs.py,sha256=41F4SZ1ngbCfVhOG_GzgSkK5LPbN1VS7Ml-sR2kmyeg,2758 +powerline/segments/common/wthr.py,sha256=spU10WyraCp05PTTJwJxCHboz6DDpGNBtq2tVPOQLmE,8203 +powerline/segments/i3wm.py,sha256=1TlekX3m1C_hVx61NK39Ozb9M0wXf1FSf5jI2vHAdgo,4123 +powerline/segments/ipython.py,sha256=TX-6onAXjhJFXMsdQOGnDytSrT0nT1N6s69bZyaMgZY,277 +powerline/segments/pdb.py,sha256=-AF_KxMQ8dPBaum8H--yxLPwWr721t9CL5r0hVvyJ_I,1611 +powerline/segments/shell.py,sha256=FE5V87MJtltMNtNz5AOry8GKRP6YI00WvjT5_Zr18b8,5215 +powerline/segments/tmux.py,sha256=UCXWF1SQlMnoIR_KpHmniaGsXd0NMDQEzz1KUZn0EAs,799 +powerline/segments/vim/__init__.py,sha256=XjYRpj7noYor23wIUiXVKeBpTfnoPO2w-g9bSkimL-0,24115 +powerline/segments/vim/__pycache__/__init__.cpython-312.pyc,, +powerline/segments/vim/plugin/__init__.py,sha256=ip96xwRq4jc1fWAFrUjksQ0MyrY0clAGaKEPntRhAd8,192 +powerline/segments/vim/plugin/__pycache__/__init__.cpython-312.pyc,, +powerline/segments/vim/plugin/__pycache__/ale.cpython-312.pyc,, +powerline/segments/vim/plugin/__pycache__/capslock.cpython-312.pyc,, +powerline/segments/vim/plugin/__pycache__/commandt.cpython-312.pyc,, +powerline/segments/vim/plugin/__pycache__/nerdtree.cpython-312.pyc,, +powerline/segments/vim/plugin/__pycache__/syntastic.cpython-312.pyc,, +powerline/segments/vim/plugin/__pycache__/tagbar.cpython-312.pyc,, +powerline/segments/vim/plugin/ale.py,sha256=kQBRcM3tDo1k_49Tu_wiFQmGNdVhYrAjQeWbEY3X334,1558 +powerline/segments/vim/plugin/capslock.py,sha256=vnVEXijHKm5IQtPxKoMEVceq3B1o0la9geECZg29fgE,940 +powerline/segments/vim/plugin/commandt.py,sha256=na8R4jQW7kXCpXE_o_DnNpsEJdBXQ7e4kgwmqvXZRa0,2624 +powerline/segments/vim/plugin/nerdtree.py,sha256=qbo3a2sJGd8WVfW2Ch78mTmcMgJB60lsn1FFhnJVHUU,645 +powerline/segments/vim/plugin/syntastic.py,sha256=T2L71Az8GFt8dmKF-WMGmoSXlXW4CKpcwVByRrJZOT4,1361 +powerline/segments/vim/plugin/tagbar.py,sha256=OLEjLthIpFzbLmkpi6XxSRuFR8p5qOeWWiLsMlE2rk4,1423 +powerline/selectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline/selectors/__pycache__/__init__.cpython-312.pyc,, +powerline/selectors/__pycache__/vim.cpython-312.pyc,, +powerline/selectors/vim.py,sha256=EKHX_ypy-ACT9-awzUbAZHz8mqZP2bXR-YE3DK1Wecw,294 +powerline/shell.py,sha256=mKWy9UWDUgY8YP4h3Vsn76QTg8h9YEVlVVxqXglEr_U,1103 +powerline/theme.py,sha256=-mEVwqPeQQZiUsqXbMeRK6wufn4qcN2Ony7H9lq1xQE,5429 +powerline/vim.py,sha256=-ItslbHpAhnFYBDMvHPOpyCjz1qZZNMTNRa9Nmp3HDA,11482 +powerline_status-2.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +powerline_status-2.7.dist-info/METADATA,sha256=QO1ra8YkyoXN10YnhRrFv9ToCIt-_J6o1ACDsczx7dg,5405 +powerline_status-2.7.dist-info/RECORD,, +powerline_status-2.7.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +powerline_status-2.7.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91 +powerline_status-2.7.dist-info/top_level.txt,sha256=yOtI5-1MUJDdEB5ZZTfROhrEsXn91DIMg4qb6CcEmd0,10 diff --git a/powerline-bin/powerline_status-2.7.dist-info/REQUESTED b/powerline-bin/powerline_status-2.7.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/powerline-bin/powerline_status-2.7.dist-info/WHEEL b/powerline-bin/powerline_status-2.7.dist-info/WHEEL new file mode 100644 index 0000000..0fde4dd --- /dev/null +++ b/powerline-bin/powerline_status-2.7.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (74.1.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/powerline-bin/powerline_status-2.7.dist-info/top_level.txt b/powerline-bin/powerline_status-2.7.dist-info/top_level.txt new file mode 100644 index 0000000..f2ffc12 --- /dev/null +++ b/powerline-bin/powerline_status-2.7.dist-info/top_level.txt @@ -0,0 +1 @@ +powerline diff --git a/tmux-powerline/config.sh b/tmux-powerline/config.sh old mode 100755 new mode 100644 diff --git a/tmux-powerline/themes/tablet.sh b/tmux-powerline/themes/tablet.sh old mode 100755 new mode 100644 diff --git a/tmux.conf.not b/tmux.conf.not old mode 100755 new mode 100644 diff --git a/tmux/tmux.conf b/tmux/tmux.conf old mode 100755 new mode 100644