From 34ca8419bbcbe89aae3495152647a14318e510c2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Feb 2018 23:03:05 +0530 Subject: [PATCH] Finish chose unicode symbol by recently used --- kittens/unicode_input/main.py | 120 ++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 12 deletions(-) diff --git a/kittens/unicode_input/main.py b/kittens/unicode_input/main.py index 64f3203a1..97423e32b 100644 --- a/kittens/unicode_input/main.py +++ b/kittens/unicode_input/main.py @@ -2,6 +2,7 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2018, Kovid Goyal +import string import sys from functools import lru_cache from gettext import gettext as _ @@ -21,13 +22,13 @@ from ..tui.operations import ( HEX, NAME = 'HEX', 'NAME' -@lru_cache() +@lru_cache(maxsize=256) def points_for_word(w): from .unicode_names import codepoints_for_word return codepoints_for_word(w.lower()) -@lru_cache() +@lru_cache(maxsize=256) def name(cp): from .unicode_names import name_for_codepoint if isinstance(cp, str): @@ -36,31 +37,120 @@ def name(cp): FAINT = 242 +DEFAULT_SET = tuple(map( + ord, + '‘’“”‹›«»‚„' '😀😛😇😈😉😍😎😮👍👎' '—–§¶†‡©®™' '→⇒•·°±−×÷¼½½¾' + '…µ¢£€¿¡¨´¸ˆ˜' 'ÀÁÂÃÄÅÆÇÈÉÊË' 'ÌÍÎÏÐÑÒÓÔÕÖØ' 'ŒŠÙÚÛÜÝŸÞßàá' 'âãäåæçèéêëìí' + 'îïðñòóôõöøœš' 'ùúûüýÿþªºαΩ∞' +)) +EMOTICONS_SET = tuple(range(0x1f600, 0x1f64f + 1)) + + +def encode_hint(num, digits=string.digits + string.ascii_lowercase): + res = '' + d = len(digits) + while not res or num > 0: + num, i = divmod(num, d) + res = digits[i] + res + return res + + +def decode_hint(x): + return int(x, 36) + + +class Table: + + def __init__(self): + self.layout_dirty = True + self.last_rows = self.last_cols = -1 + self.codepoints = [] + self.text = '' + self.num_cols = self.num_rows = 0 + self.mode = HEX + + def set_codepoints(self, codepoints, mode=HEX): + self.codepoints = codepoints + self.mode = HEX + self.layout_dirty = True + + def codepoint_at_hint(self, hint): + return self.codepoints[decode_hint(hint)] + + def layout(self, rows, cols): + if not self.layout_dirty and self.last_cols == cols and self.last_rows == rows: + return self.text + self.last_cols, self.last_rows = cols, rows + self.layout_dirty = False + + def as_parts(i, codepoint): + return encode_hint(i).ljust(idx_size), chr(codepoint), name(codepoint) + + num = len(self.codepoints) + if num < 1: + self.text = '' + self.num_cols = self.num_rows = 0 + return self.text + idx_size = len(encode_hint(num - 1)) + + parts = [as_parts(i, c) for i, c in enumerate(self.codepoints)] + sizes = [idx_size + 2 + len(p[2]) + 2 for p in parts] + longest = max(sizes) if sizes else 0 + col_width = longest + 2 + col_width = min(col_width, 40) + space_for_desc = col_width - 2 - idx_size - 4 + num_cols = cols // col_width + buf = [] + rows_left = rows + a = buf.append + for i, (idx, c, desc) in enumerate(parts): + if i > 0 and i % num_cols == 0: + rows_left -= 1 + if rows_left == 0: + break + a('\r\n') + a(colored(idx, 'green')), a(' '), a(colored(c, 'gray', True)), a(' ') + w = wcswidth(c) + if w < 2: + a(' ' * (2 - w)) + if len(desc) > space_for_desc: + desc = desc[:space_for_desc - 1] + '…' + a(colored(desc, FAINT)) + extra = space_for_desc - len(desc) + if extra > 0: + a(' ' * extra) + a(' ') + self.text = ''.join(buf) + return self.text class UnicodeInput(Handler): def __init__(self, cached_values): self.cached_values = cached_values + self.recent = list(self.cached_values.get('recent', DEFAULT_SET)) self.current_input = '' self.current_char = None self.prompt_template = '{}> ' self.choice_line = '' self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX') + self.table = Table() + if self.mode is HEX: + self.table.set_codepoints(self.recent, self.mode) self.update_prompt() def update_current_char(self): + self.current_char = None if self.mode is HEX: try: - code = int(self.current_input, 16) - if code <= 32 or code == 127 or 128 <= code <= 159 or 0xd800 <= code <= 0xdbff or 0xDC00 <= code <= 0xDFFF: - self.current_char = None + if self.current_input.startswith('r') and len(self.current_input) > 1: + self.current_char = chr(self.table.codepoint_at_hint(self.current_input[1:])) else: + code = int(self.current_input, 16) self.current_char = chr(code) except Exception: - self.current_char = None + pass else: - self.current_char = None parts = self.current_input.split() if parts and parts[0]: codepoints = points_for_word(parts[0]) @@ -72,6 +162,10 @@ class UnicodeInput(Handler): codepoints = tuple(sorted(codepoints)) self.current_char = chr(codepoints[0]) # name_map = {c: name(c) for c in codepoints} + if self.current_char is not None: + code = ord(self.current_char) + if code <= 32 or code == 127 or 128 <= code <= 159 or 0xd800 <= code <= 0xdbff or 0xDC00 <= code <= 0xDFFF: + self.current_char = None def update_prompt(self): self.update_current_char() @@ -80,11 +174,9 @@ class UnicodeInput(Handler): self.choice_line = '' else: c, color = self.current_char, 'green' - self.choice_line = _('Chosen:') + ' {} ({}) {}'.format( + self.choice_line = _('Chosen:') + ' {} U+{} {}'.format( colored(c, 'green'), hex(ord(c))[2:], styled(name(c) or '', italic=True, fg=FAINT)) - w = wcswidth(c) self.prompt = self.prompt_template.format(colored(c, color)) - self.promt_len = w + len(self.prompt_template) - 2 def initialize(self, *args): Handler.initialize(self, *args) @@ -130,6 +222,8 @@ class UnicodeInput(Handler): writeln(self.choice_line) if self.mode is HEX: writeln(styled('Use r followed by the index for the recent entries below', fg=FAINT)) + self.table_at = y + self.write(self.table.layout(self.screen_size.rows - self.table_at, self.screen_size.cols)) def refresh(self): self.update_prompt() @@ -178,6 +272,8 @@ def main(args=sys.argv): with cached_values_for('unicode-input') as cached_values: handler = UnicodeInput(cached_values) loop.loop(handler) - if handler.current_char and loop.return_code == 0: - print('OK:', hex(ord(handler.current_char))[2:]) + if handler.current_char and loop.return_code == 0: + print('OK:', hex(ord(handler.current_char))[2:]) + recent = [ord(handler.current_char)] + handler.recent + cached_values['recent'] = recent[:len(DEFAULT_SET)] raise SystemExit(loop.return_code)