From d133ffac25428472bbf1e5f6942ab7a19a12c832 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 May 2018 15:34:11 +0530 Subject: [PATCH] Make using tui.operations more convenient --- kittens/diff/main.py | 36 +++++++++++-------------- kittens/tui/handler.py | 4 +++ kittens/tui/operations.py | 56 +++++++++++++++++++++++++-------------- 3 files changed, 56 insertions(+), 40 deletions(-) diff --git a/kittens/diff/main.py b/kittens/diff/main.py index f51c860a9..f94ba488a 100644 --- a/kittens/diff/main.py +++ b/kittens/diff/main.py @@ -12,11 +12,6 @@ from kitty.key_encoding import DOWN, ESCAPE, PRESS, REPEAT, UP from ..tui.handler import Handler from ..tui.loop import Loop -from ..tui.operations import ( - bell, clear_screen, scroll_screen, set_cursor_position, set_cursor_visible, - set_default_colors, set_line_wrapping, set_scrolling_region, - set_window_title -) from .collect import create_collection, data_for_path from .config import init_config from .patch import Differ @@ -62,31 +57,31 @@ class DiffHandler(Handler): return self.screen_size.rows - 1 def set_scrolling_region(self): - self.write(set_scrolling_region(self.screen_size, 0, self.num_lines - 2)) + self.cmd.set_scrolling_region(self.screen_size, 0, self.num_lines - 2) def scroll_lines(self, amt=1): new_pos = max(0, min(self.scroll_pos + amt, self.max_scroll_pos)) if new_pos == self.scroll_pos: - self.write(bell()) + self.cmd.bell() return if abs(new_pos - self.scroll_pos) >= self.num_lines - 1: self.scroll_pos = new_pos self.draw_screen() return self.enforce_cursor_state() - self.write(scroll_screen(amt)) + self.cmd.scroll_screen(amt) self.scroll_pos = new_pos if amt < 0: - self.write(set_cursor_position(0, 0)) + self.cmd.set_cursor_position(0, 0) self.draw_lines(-amt) else: - self.write(set_cursor_position(0, self.num_lines - amt)) + self.cmd.set_cursor_position(0, self.num_lines - amt) self.draw_lines(amt, self.num_lines - amt) def init_terminal_state(self): - self.write(set_line_wrapping(False)) - self.write(set_window_title('kitty +diff')) - self.write(set_default_colors(self.opts.foreground, self.opts.background)) + self.cmd.set_line_wrapping(False) + self.cmd.set_window_title('kitty +diff') + self.cmd.set_default_colors(self.opts.foreground, self.opts.background) def initialize(self): self.init_terminal_state() @@ -95,11 +90,11 @@ class DiffHandler(Handler): self.create_collection() def enforce_cursor_state(self): - self.write(set_cursor_visible(self.state > DIFFED)) + self.cmd.set_cursor_visible(self.state > DIFFED) def finalize(self): - self.write(set_cursor_visible(True)) - self.write(set_default_colors()) + self.cmd.set_cursor_visible(True) + self.cmd.set_default_colors() def draw_lines(self, num, offset=0): offset += self.scroll_pos @@ -116,16 +111,17 @@ class DiffHandler(Handler): def draw_screen(self): self.enforce_cursor_state() if self.state < DIFFED: - self.write(clear_screen()) + self.cmd.clear_screen() self.write(_('Calculating diff, please wait...')) return - self.write(clear_screen()) + self.cmd.clear_screen() self.draw_lines(self.num_lines) self.draw_status_line() def draw_status_line(self): - self.write(set_cursor_position(0, self.num_lines)) - self.write('\033[K:') + self.cmd.set_cursor_position(0, self.num_lines) + self.cmd.clear_to_eol() + self.write(':') def on_text(self, text, in_bracketed_paste=False): if text == 'q': diff --git a/kittens/tui/handler.py b/kittens/tui/handler.py index ff00e3324..8a579ae07 100644 --- a/kittens/tui/handler.py +++ b/kittens/tui/handler.py @@ -3,12 +3,16 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal +from kittens.tui.operations import commander + + class Handler: def _initialize(self, screen_size, quit_loop, wakeup, start_job): self.screen_size, self.quit_loop = screen_size, quit_loop self.wakeup = wakeup self.start_job = start_job + self.cmd = commander(self) def __enter__(self): self.initialize() diff --git a/kittens/tui/operations.py b/kittens/tui/operations.py index 0b51e617a..4fe017cb7 100644 --- a/kittens/tui/operations.py +++ b/kittens/tui/operations.py @@ -4,9 +4,9 @@ import sys from contextlib import contextmanager +from functools import wraps from kitty.rgb import Color, color_as_sharp, to_color -from kitty.terminfo import string_capabilities S7C1T = '\033 F' SAVE_CURSOR = '\0337' @@ -36,46 +36,45 @@ MODES = dict( ) -def set_mode(which, private=True): +def set_mode(which, private=True) -> str: num, private = MODES[which] return '\033[{}{}h'.format(private, num) -def reset_mode(which): +def reset_mode(which) -> str: num, private = MODES[which] return '\033[{}{}l'.format(private, num) -def simple_capability(name): - ans = string_capabilities[name].replace(r'\E', '\033') - return lambda: ans +def clear_screen() -> str: + return '\033[H\033[2J' -clear_screen = simple_capability('clear') -clear_to_bottom = simple_capability('ed') +def clear_to_eol() -> str: + return '\033[K' -def bell(): +def bell() -> str: return '\a' -def set_window_title(value): +def set_window_title(value) -> str: return ('\033]2;' + value.replace('\033', '').replace('\x9c', '') + '\033\\') -def set_line_wrapping(yes_or_no): +def set_line_wrapping(yes_or_no) -> str: return (set_mode if yes_or_no else reset_mode)('DECAWM') -def set_cursor_visible(yes_or_no): +def set_cursor_visible(yes_or_no) -> str: return (set_mode if yes_or_no else reset_mode)('DECTCEM') -def set_cursor_position(x, y): # (0, 0) is top left +def set_cursor_position(x, y) -> str: # (0, 0) is top left return '\033[{};{}H'.format(y + 1, x + 1) -def set_scrolling_region(screen_size, top=None, bottom=None): +def set_scrolling_region(screen_size, top=None, bottom=None) -> str: if top is None: top = 0 if bottom is None: @@ -87,7 +86,7 @@ def set_scrolling_region(screen_size, top=None, bottom=None): return '\033[{};{}r'.format(top + 1, bottom + 1) -def scroll_screen(amt=1): +def scroll_screen(amt=1) -> str: return '\033[' + str(abs(amt)) + ('T' if amt < 0 else 'S') @@ -107,20 +106,20 @@ def color_code(color, intense=False, base=30): return e -def sgr(*parts): +def sgr(*parts) -> str: return '\033[{}m'.format(';'.join(parts)) -def colored(text, color, intense=False, reset_to=None, reset_to_intense=False): +def colored(text, color, intense=False, reset_to=None, reset_to_intense=False) -> str: e = color_code(color, intense) return '\033[{}m{}\033[{}m'.format(e, text, 39 if reset_to is None else color_code(reset_to, reset_to_intense)) -def faint(text): +def faint(text) -> str: return colored(text, 'black', True) -def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=None, bold=None, underline=None, underline_color=None, reverse=None): +def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=None, bold=None, underline=None, underline_color=None, reverse=None) -> str: start, end = [], [] if fg is not None: start.append(color_code(fg, fg_intense)) @@ -192,7 +191,7 @@ def alternate_screen(f=None): print(reset_mode('ALTERNATE_SCREEN'), end='', file=f) -def set_default_colors(fg=None, bg=None): +def set_default_colors(fg=None, bg=None) -> str: ans = '' if fg is None: ans += '\x1b]110\x1b\\' @@ -203,3 +202,20 @@ def set_default_colors(fg=None, bg=None): else: ans += '\x1b]11;{}\x1b\\'.format(color_as_sharp(bg if isinstance(bg, Color) else to_color(bg))) return ans + + +all_cmds = tuple( + (name, obj) for name, obj in globals().items() + if hasattr(obj, '__annotations__') and obj.__annotations__.get('return') is str) + + +def writer(handler, func): + @wraps(func) + def f(self, *a, **kw): + handler.write(func(*a, **kw)) + return f + + +def commander(handler): + ans = {name: writer(handler, obj) for name, obj in all_cmds} + return type('CMD', (), ans)()