From a4cc10c41ba0bcaea279d7dd44cbd65f73f0b132 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 4 Mar 2020 06:10:57 +0530 Subject: [PATCH] More typing work --- docs/kittens/custom.rst | 7 +- kittens/ask/main.py | 2 + kittens/icat/main.py | 7 +- kitty/borders.py | 2 +- kitty/config_data.py | 13 +- kitty/fast_data_types.pyi | 267 ++++++++++++++++++++++++++++++++++++- kitty/fontconfig.c | 2 +- kitty/fonts/box_drawing.py | 14 +- kitty/notify.py | 5 +- kitty/shaders.c | 1 - kitty/tab_bar.py | 3 +- kitty/window.py | 82 ++++++------ 12 files changed, 340 insertions(+), 65 deletions(-) diff --git a/docs/kittens/custom.rst b/docs/kittens/custom.rst index 6e3dbc1cc..ace34079f 100644 --- a/docs/kittens/custom.rst +++ b/docs/kittens/custom.rst @@ -77,10 +77,11 @@ function, telling kitty what kind of input your kitten would like. For example: # in handle_result, STDIN is for the kitty process itself, rather # than the kitten process and should not be read from. + from kittens.tui.handler import result_handler + @result_handler(type_of_input='text') def handle_result(args, stdin_data, target_window_id, boss): pass - handle_result.type_of_input = 'text' This will send the plain text of the active window to the kitten's :file:`STDIN`. For text with formatting escape codes, use ``ansi`` @@ -106,6 +107,8 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py` def main(args): pass + from kittens.tui.handler import result_handler + @result_handler(no_ui=True) def handle_result(args, answer, target_window_id, boss): tab = boss.active_tab if tab is not None: @@ -114,8 +117,6 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py` else: tab.goto_layout('stack') - handle_result.no_ui = True - Now in kitty.conf add:: diff --git a/kittens/ask/main.py b/kittens/ask/main.py index 03d81f8cb..a77357662 100644 --- a/kittens/ask/main.py +++ b/kittens/ask/main.py @@ -9,6 +9,7 @@ from kitty.cli import parse_args from kitty.constants import cache_dir from ..tui.operations import alternate_screen, styled +from ..tui.handler import result_handler readline = None @@ -108,6 +109,7 @@ def main(args): return ans +@result_handler() def handle_result(args, data, target_window_id, boss): if 'response' in data: func, *args = data['items'] diff --git a/kittens/icat/main.py b/kittens/icat/main.py index feb61510a..221d3fdb2 100755 --- a/kittens/icat/main.py +++ b/kittens/icat/main.py @@ -423,6 +423,7 @@ def main(args=sys.argv): if __name__ == '__main__': main() elif __name__ == '__doc__': - sys.cli_docs['usage'] = usage - sys.cli_docs['options'] = options_spec - sys.cli_docs['help_text'] = help_text + cd = sys.cli_docs # type: ignore + cd['usage'] = usage + cd['options'] = options_spec + cd['help_text'] = help_text diff --git a/kitty/borders.py b/kitty/borders.py index 59daecfeb..35fde7328 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -13,7 +13,7 @@ from .utils import load_shaders try: from enum import IntFlag except ImportError: - from enum import IntEnum as IntFlag + from enum import IntEnum as IntFlag # type: ignore class BorderColor(IntFlag): diff --git a/kitty/config_data.py b/kitty/config_data.py index 11d4f018d..1b3def206 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -5,9 +5,10 @@ # Utils {{{ import os from gettext import gettext as _ +from typing import Mapping, Union from . import fast_data_types as defines -from .conf.definition import option_func +from .conf.definition import Option, Shortcut, option_func from .conf.utils import ( choices, positive_float, positive_int, to_bool, to_cmdline, to_color, to_color_or_none, unit_float @@ -54,7 +55,7 @@ def uniq(vals, result_type=list): # Groups {{{ -all_options = {} +all_options: Mapping[str, Union[Option, Shortcut]] = {} o, k, g, all_groups = option_func(all_options, { @@ -475,11 +476,11 @@ The color and style for highlighting URLs on mouse-over. :code:`url_style` can be one of: none, single, double, curly''')) -def url_style(x): - return url_style.map.get(x, url_style.map['curly']) +def url_style(x: str) -> int: + return url_style_map.get(x, url_style_map['curly']) -url_style.map = dict( +url_style_map = dict( ((v, i) for i, v in enumerate('none single double curly'.split())) ) @@ -1415,4 +1416,4 @@ the line (same as pressing the Home key):: # }}} # }}} -type_map = {o.name: o.option_type for o in all_options.values() if hasattr(o, 'option_type')} +type_map = {o.name: o.option_type for o in all_options.values() if isinstance(o, Option)} diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index d1e3c0580..3a48a1472 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1,8 +1,115 @@ -from typing import Callable, Mapping, Optional, Tuple +from collections import namedtuple +from typing import ( + Any, Callable, List, Mapping, NewType, Optional, Tuple, Union +) from kitty.cli import Namespace GLFW_IBEAM_CURSOR: int +CURSOR_BEAM: int +CURSOR_BLOCK: int +CURSOR_UNDERLINE: int +DECAWM: int +BGIMAGE_PROGRAM: int +BLIT_PROGRAM: int +CELL_BG_PROGRAM: int +CELL_FG_PROGRAM: int +CELL_PROGRAM: int +CELL_SPECIAL_PROGRAM: int +CSI: int +DCS: int +DECORATION: int +DIM: int +GRAPHICS_ALPHA_MASK_PROGRAM: int +GRAPHICS_PREMULT_PROGRAM: int +GRAPHICS_PROGRAM: int +MARK: int +MARK_MASK: int +OSC: int +REVERSE: int +SCROLL_FULL: int +SCROLL_LINE: int +SCROLL_PAGE: int +STRIKETHROUGH: int +TINT_PROGRAM: int +FC_MONO: int = 100 +FC_DUAL: int +FC_WEIGHT_REGULAR: int +FC_WEIGHT_BOLD: int +FC_SLANT_ROMAN: int +FC_SLANT_ITALIC: int +BORDERS_PROGRAM: int + + +def default_color_table() -> Tuple[int, ...]: + pass + + +FontConfigPattern = Mapping[str, Union[str, int, bool, float]] + + +def fc_list(spacing: int = -1, allow_bitmapped_fonts: bool = False) -> Tuple[FontConfigPattern, ...]: + pass + + +def fc_match( + family: Optional[str] = None, + bold: bool = False, + italic: bool = False, + spacing: int = FC_MONO, + allow_bitmapped_fonts: bool = False, + size_in_pts: float = 0., + dpi: float = 0. +) -> FontConfigPattern: + pass + + +def coretext_all_fonts() -> Tuple[Mapping[str, Any], ...]: + pass + + +def add_timer(callback: Callable[[int], None], interval: float, repeats: bool = True) -> int: + pass + + +def monitor_pid(pid: int) -> None: + pass + + +def add_window(os_window_id: int, tab_id: int, title: str) -> int: + pass + + +def compile_program(which: int, vertex_shader: str, fragment_shader: str) -> int: + pass + + +def init_cell_program() -> None: + pass + + +def set_titlebar_color(os_window_id: int, color: int) -> bool: + pass + + +def add_borders_rect(os_window_id: int, tab_id: int, left: int, top: int, right: int, bottom: int, color: int) -> None: + pass + + +def init_borders_program() -> None: + pass + + +def os_window_has_background_image(os_window_id: int) -> bool: + pass + + +def dbus_send_notification(app_name: str, icon: str, summary: str, body: str, action_name: str, timeout: int = -1) -> int: + pass + + +def cocoa_send_notification(identifier: Optional[str], title: str, informative_text: str, path_to_img: Optional[str], subtitle: Optional[str] = None) -> None: + pass def create_os_window( @@ -18,6 +125,14 @@ def create_os_window( pass +def update_window_title(os_window_id: int, tab_id: int, window_id: int, title: str) -> None: + pass + + +def update_window_visibility(os_window_id: int, tab_id: int, window_id: int, window_idx: int, visible: bool) -> None: + pass + + def set_options( opts: Namespace, is_wayland: bool = False, @@ -130,7 +245,7 @@ def background_opacity_of(os_window_id: int) -> Optional[float]: pass -def read_command_response(fd: int, timeout: float, list) -> None: +def read_command_response(fd: int, timeout: float, list: List) -> None: pass @@ -142,6 +257,154 @@ def is_emoji_presentation_base(code: int) -> bool: pass +def x11_window_id(os_window_id: int) -> int: + pass + + +def swap_tabs(os_window_id: int, a: int, b: int) -> None: + pass + + +def set_active_tab(os_window_id: int, a: int) -> None: + pass + + +def ring_bell() -> None: + pass + + +def remove_window(os_window_id: int, tab_id: int, window_id: int) -> None: + pass + + +def remove_tab(os_window_id: int, tab_id: int) -> None: + pass + + +def pt_to_px(pt: float, os_window_id: int = 0) -> float: + pass + + +def next_window_id() -> int: + pass + + +def mark_tab_bar_dirty(os_window_id: int) -> None: + pass + + +def detach_window(os_window_id: int, tab_id: int, window_id: int) -> None: + pass + + +def attach_window(os_window_id: int, tab_id: int, window_id: int) -> None: + pass + + +def add_tab(os_window_id: int) -> int: + pass + + +def cell_size_for_window(os_window_id: int) -> Tuple[int, int]: + pass + + +Region = namedtuple('Region', 'left top right bottom width height') + + +def viewport_for_window(os_window_id: int) -> Tuple[Region, Region, int, int, int, int]: + pass + + +TermiosPtr = NewType('TermiosPtr', int) + + +def raw_tty(fd: int, termios_ptr: TermiosPtr) -> None: + pass + + +def close_tty(fd: int, termios_ptr: TermiosPtr) -> None: + pass + + +def normal_tty(fd: int, termios_ptr: TermiosPtr) -> None: + pass + + +def open_tty(read_with_timeout: bool = False) -> Tuple[int, TermiosPtr]: + pass + + +def parse_input_from_terminal( + text_callback: Callable[[str], None], + dcs_callback: Callable[[str], None], + csi_callback: Callable[[str], None], + osc_callback: Callable[[str], None], + pm_callback: Callable[[str], None], + apc_callback: Callable[[str], None], + data: str, + in_bracketed_paste: bool +): + pass + + +class Line: + pass + + +def test_shape(line: Line, path: Optional[str] = None, index: int = 0) -> List[Tuple[int, int, int, Tuple[int, ...]]]: + pass + + +def test_render_line(line: Line) -> None: + pass + + +def sprite_map_set_limits(w: int, h: int) -> None: + pass + + +def set_send_sprite_to_gpu(func: Callable[[int, int, int, bytes], None]) -> None: + pass + + +def set_font_data( + box_drawing_func: Callable[[int, int, int, float], Tuple[int, Union[bytearray, bytes]]], + prerender_func: Callable[[int, int, int, int, int, float, float, float, float], Tuple[int, ...]], + descriptor_for_idx: Callable[[int], Tuple[dict, bool, bool]], + bold: int, italic: int, bold_italic: int, num_symbol_fonts: int, + symbol_maps: Tuple[Tuple[int, int, int], ...], + font_sz_in_pts: float, + font_feature_settings: Mapping[str, Tuple[bytes, ...]] +): + pass + + +def get_fallback_font(text: str, bold: bool, italic: bool): + pass + + +def create_test_font_group(sz: float, dpix: float, dpiy: float) -> Tuple[int, int]: + pass + + +class Screen: + pass + + +def set_tab_bar_render_data(os_window_id: int, xstart: float, ystart: float, dx: float, dy: float, screen: Screen) -> None: + pass + + +def set_window_render_data( + os_window_id: int, tab_id: int, window_id: int, window_idx: int, + xstart: float, ystart: float, dx: float, dy: float, + screen: Screen, + left: int, top: int, right: int, bottom: int +): + pass + + class ChildMonitor: def __init__( diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index 7d3ee9da5..fa1e32a59 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -152,7 +152,7 @@ fc_match(PyObject UNUSED *self, PyObject *args) { FcPattern *pat = NULL; PyObject *ans = NULL; - if (!PyArg_ParseTuple(args, "|zppppdd", &family, &bold, &italic, &spacing, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL; + if (!PyArg_ParseTuple(args, "|zppipdd", &family, &bold, &italic, &spacing, &allow_bitmapped_fonts, &size_in_pts, &dpi)) return NULL; pat = FcPatternCreate(); if (pat == NULL) return PyErr_NoMemory(); diff --git a/kitty/fonts/box_drawing.py b/kitty/fonts/box_drawing.py index d03f5bec9..08ce2e500 100644 --- a/kitty/fonts/box_drawing.py +++ b/kitty/fonts/box_drawing.py @@ -10,6 +10,8 @@ import math from functools import partial as p from itertools import repeat +from typing import cast, Callable + scale = (0.001, 1, 1.5, 2) _dpi = 96.0 @@ -638,24 +640,24 @@ for start in '┌┐└┘': for ch, c in zip('╭╮╯╰', '┌┐┘└'): box_chars[ch] = [p(corner, which=c)] # TODO: Make these rounded -for i, (a, b, c, d) in enumerate(( +for i, (a_, b_, c_, d_) in enumerate(( (t, t, t, t), (f, t, t, t), (t, f, t, t), (f, f, t, t), (t, t, f, t), (t, t, t, f), (t, t, f, f), (f, t, f, t), (t, f, f, t), (f, t, t, f), (t, f, t, f), (f, f, f, t), (f, f, t, f), (f, t, f, f), (t, f, f, f), (f, f, f, f) )): - box_chars[chr(ord('┼') + i)] = [p(cross, a=a, b=b, c=c, d=d)] + box_chars[chr(ord('┼') + i)] = [p(cross, a=a_, b=b_, c=c_, d=d_)] for starts, func, pattern in ( ('├┤', vert_t, ((t, t, t), (t, f, t), (f, t, t), (t, t, f), (f, t, f), (f, f, t), (t, f, f), (f, f, f))), ('┬┴', horz_t, ((t, t, t), (f, t, t), (t, f, t), (f, f, t), (t, t, f), (f, t, f), (t, f, f), (f, f, f))), ): for start in starts: - for i, (a, b, c) in enumerate(pattern): - box_chars[chr(ord(start) + i)] = [p(func, which=start, a=a, b=b, c=c)] + for i, (a_, b_, c_) in enumerate(pattern): + box_chars[chr(ord(start) + i)] = [p(func, which=start, a=a_, b=b_, c=c_)] -for chars, func in (('╒╕╘╛', dvcorner), ('╓╖╙╜', dhcorner), ('╔╗╚╝', dcorner), ('╟╢╤╧', dpip)): +for chars, func_ in (('╒╕╘╛', dvcorner), ('╓╖╙╜', dhcorner), ('╔╗╚╝', dcorner), ('╟╢╤╧', dpip)): for ch in chars: - box_chars[ch] = [p(func, which=ch)] + box_chars[ch] = [p(cast(Callable, func_), which=ch)] def render_box_char(ch, buf, width, height, dpi=96.0): diff --git a/kitty/notify.py b/kitty/notify.py index 919df888f..e33bb132f 100644 --- a/kitty/notify.py +++ b/kitty/notify.py @@ -2,6 +2,7 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2019, Kovid Goyal +from typing import Mapping from .constants import is_macos, logo_png_file @@ -25,8 +26,8 @@ else: from .fast_data_types import dbus_send_notification from .constants import get_boss - alloc_map = {} - identifier_map = {} + alloc_map: Mapping[int, str] = {} + identifier_map: Mapping[str, int] = {} def dbus_notification_created(alloc_id, notification_id): identifier = alloc_map.pop(alloc_id, None) diff --git a/kitty/shaders.c b/kitty/shaders.c index 550b6a4c6..7315ffd01 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -749,7 +749,6 @@ end: if (fragment_shader_id != 0) glDeleteShader(fragment_shader_id); if (PyErr_Occurred()) { glDeleteProgram(program->id); program->id = 0; return NULL;} return Py_BuildValue("I", program->id); - Py_RETURN_NONE; } #define PYWRAP0(name) static PyObject* py##name(PYNOARG) diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index 9769306e6..fa79fd167 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -3,6 +3,7 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal from collections import namedtuple +from typing import Set from .config import build_ansi_color_table from .constants import WindowGeometry @@ -26,7 +27,7 @@ def as_rgb(x): return (x << 8) | 2 -template_failures = set() +template_failures: Set[str] = set() def draw_title(draw_data, screen, tab, index): diff --git a/kitty/window.py b/kitty/window.py index 2e329f74b..aa6c785f3 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -57,51 +57,55 @@ def calculate_gl_geometry(window_geometry, viewport_width, viewport_height, cell return ScreenGeometry(xstart, ystart, window_geometry.xnum, window_geometry.ynum, dx, dy) -def load_shader_programs(semi_transparent=False): - compile_program(BLIT_PROGRAM, *load_shaders('blit')) - v, f = load_shaders('cell') +class LoadShaderPrograms: - for which, p in { - 'SIMPLE': CELL_PROGRAM, - 'BACKGROUND': CELL_BG_PROGRAM, - 'SPECIAL': CELL_SPECIAL_PROGRAM, - 'FOREGROUND': CELL_FG_PROGRAM, - }.items(): - vv, ff = v.replace('WHICH_PROGRAM', which), f.replace('WHICH_PROGRAM', which) - for gln, pyn in { - 'REVERSE_SHIFT': REVERSE, - 'STRIKE_SHIFT': STRIKETHROUGH, - 'DIM_SHIFT': DIM, - 'DECORATION_SHIFT': DECORATION, - 'MARK_SHIFT': MARK, - 'MARK_MASK': MARK_MASK, + use_selection_fg = True + + def __call__(self, semi_transparent=False): + compile_program(BLIT_PROGRAM, *load_shaders('blit')) + v, f = load_shaders('cell') + + for which, p in { + 'SIMPLE': CELL_PROGRAM, + 'BACKGROUND': CELL_BG_PROGRAM, + 'SPECIAL': CELL_SPECIAL_PROGRAM, + 'FOREGROUND': CELL_FG_PROGRAM, }.items(): - vv = vv.replace('{{{}}}'.format(gln), str(pyn), 1) - if semi_transparent: - vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') - ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') - if not load_shader_programs.use_selection_fg: - vv = vv.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG') - ff = ff.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG') - compile_program(p, vv, ff) + vv, ff = v.replace('WHICH_PROGRAM', which), f.replace('WHICH_PROGRAM', which) + for gln, pyn in { + 'REVERSE_SHIFT': REVERSE, + 'STRIKE_SHIFT': STRIKETHROUGH, + 'DIM_SHIFT': DIM, + 'DECORATION_SHIFT': DECORATION, + 'MARK_SHIFT': MARK, + 'MARK_MASK': MARK_MASK, + }.items(): + vv = vv.replace('{{{}}}'.format(gln), str(pyn), 1) + if semi_transparent: + vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') + ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') + if not load_shader_programs.use_selection_fg: + vv = vv.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG') + ff = ff.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG') + compile_program(p, vv, ff) - v, f = load_shaders('graphics') - for which, p in { - 'SIMPLE': GRAPHICS_PROGRAM, - 'PREMULT': GRAPHICS_PREMULT_PROGRAM, - 'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM, - }.items(): - ff = f.replace('ALPHA_TYPE', which) - compile_program(p, v, ff) + v, f = load_shaders('graphics') + for which, p in { + 'SIMPLE': GRAPHICS_PROGRAM, + 'PREMULT': GRAPHICS_PREMULT_PROGRAM, + 'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM, + }.items(): + ff = f.replace('ALPHA_TYPE', which) + compile_program(p, v, ff) - v, f = load_shaders('bgimage') - compile_program(BGIMAGE_PROGRAM, v, f) - v, f = load_shaders('tint') - compile_program(TINT_PROGRAM, v, f) - init_cell_program() + v, f = load_shaders('bgimage') + compile_program(BGIMAGE_PROGRAM, v, f) + v, f = load_shaders('tint') + compile_program(TINT_PROGRAM, v, f) + init_cell_program() -load_shader_programs.use_selection_fg = True +load_shader_programs = LoadShaderPrograms() def setup_colors(screen, opts):