Skip to content

Commit

Permalink
unicode input kitten: Add an option :option:`kitty +kitten unicode_in…
Browse files Browse the repository at this point in the history
…put --emoji-variation` to control the presentation variant of selected emojis

Fixes #2139
  • Loading branch information
kovidgoyal committed Nov 17, 2019
1 parent d5682fe commit 738878c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 17 deletions.
4 changes: 4 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ To update |kitty|, :doc:`follow the instructions <binary>`.

- diff kitten: Allow diffing remote files easily via ssh (:iss:`727`)

- unicode input kitten: Add an option :option:`kitty +kitten unicode_input
--emoji-variation` to control the presentation variant of selected emojis
(:iss:`2139`)

- Add specialised rendering for a few more box powerline and unicode symbols
(:pull:`2074` and :pull:`2021`)

Expand Down
5 changes: 5 additions & 0 deletions docs/kittens/unicode-input.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ In :guilabel:`Name` mode you instead type words from the character name and use
keys/tab to select the character from the displayed matches. You can also type
a leading period and the index for the match if you don't like to use arrow
keys.

Command Line Interface
-------------------------

.. include:: ../generated/cli-kitten-unicode_input.rst
81 changes: 64 additions & 17 deletions kittens/unicode_input/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,27 @@
import os
import string
import subprocess
import sys
from contextlib import suppress
from functools import lru_cache
from gettext import gettext as _
from contextlib import suppress

from kitty.cli import parse_args
from kitty.config import cached_values_for
from kitty.constants import config_dir
from kitty.utils import get_editor
from kitty.fast_data_types import wcswidth
from kitty.fast_data_types import wcswidth, is_emoji_presentation_base
from kitty.key_encoding import (
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
enter_key
)
from kitty.utils import get_editor

from ..tui.line_edit import LineEdit
from ..tui.handler import Handler
from ..tui.line_edit import LineEdit
from ..tui.loop import Loop
from ..tui.operations import (
clear_screen, colored, cursor, faint, set_line_wrapping,
set_window_title, sgr, styled
clear_screen, colored, cursor, faint, set_line_wrapping, set_window_title,
sgr, styled
)

HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
Expand Down Expand Up @@ -131,7 +133,8 @@ def decode_hint(x):

class Table:

def __init__(self):
def __init__(self, emoji_variation):
self.emoji_variation = emoji_variation
self.layout_dirty = True
self.last_rows = self.last_cols = -1
self.codepoints = []
Expand Down Expand Up @@ -161,7 +164,10 @@ def layout(self, rows, cols):
self.layout_dirty = False

def safe_chr(codepoint):
return chr(codepoint).encode('utf-8', 'replace').decode('utf-8')
ans = chr(codepoint).encode('utf-8', 'replace').decode('utf-8')
if self.emoji_variation and is_emoji_presentation_base(codepoint):
ans += self.emoji_variation
return ans

if self.mode is NAME:
def as_parts(i, codepoint):
Expand Down Expand Up @@ -248,18 +254,31 @@ def is_index(w):

class UnicodeInput(Handler):

def __init__(self, cached_values):
def __init__(self, cached_values, emoji_variation='none'):
self.cached_values = cached_values
self.emoji_variation = ''
if emoji_variation == 'text':
self.emoji_variation = '\ufe0e'
elif emoji_variation == 'graphic':
self.emoji_variation = '\ufe0f'
self.line_edit = LineEdit()
self.recent = list(self.cached_values.get('recent', DEFAULT_SET))
self.current_char = None
self.prompt_template = '{}> '
self.last_updated_code_point_at = None
self.choice_line = ''
self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX')
self.table = Table()
self.table = Table(self.emoji_variation)
self.update_prompt()

@property
def resolved_current_char(self):
ans = self.current_char
if ans:
if self.emoji_variation and is_emoji_presentation_base(ord(ans[0])):
ans += self.emoji_variation
return ans

def update_codepoints(self):
codepoints = None
if self.mode is HEX:
Expand Down Expand Up @@ -320,8 +339,10 @@ def update_prompt(self):
self.choice_line = ''
else:
c, color = self.current_char, 'green'
if self.emoji_variation and is_emoji_presentation_base(ord(c[0])):
c += self.emoji_variation
self.choice_line = _('Chosen:') + ' {} U+{} {}'.format(
colored(c, 'green'), hex(ord(c))[2:], faint(styled(name(c) or '', italic=True)))
colored(c, 'green'), hex(ord(c[0]))[2:], faint(styled(name(c) or '', italic=True)))
self.prompt = self.prompt_template.format(colored(c, color))

def init_terminal_state(self):
Expand Down Expand Up @@ -475,17 +496,43 @@ def on_resize(self, new_size):
self.refresh()


help_text = 'Input a unicode character'
usage = ''
OPTIONS = '''
--emoji-variation
type=choices
default=none
choices=none,graphic,text
Whether to use the textual or the graphical form for emoji. By default the
default form specified in the unicode standard for the symbol is used.
'''.format


def parse_unicode_input_args(args):
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten unicode_input')


def main(args):
try:
args, items = parse_unicode_input_args(args[1:])
except SystemExit as e:
if e.code != 0:
print(e.args[0], file=sys.stderr)
input(_('Press Enter to quit'))
return

loop = Loop()
with cached_values_for('unicode-input') as cached_values:
handler = UnicodeInput(cached_values)
handler = UnicodeInput(cached_values, args.emoji_variation)
loop.loop(handler)
if handler.current_char and loop.return_code == 0:
with suppress(Exception):
handler.recent.remove(ord(handler.current_char))
recent = [ord(handler.current_char)] + handler.recent
cached_values['recent'] = recent[:len(DEFAULT_SET)]
return handler.current_char
return handler.resolved_current_char
if loop.return_code != 0:
raise SystemExit(loop.return_code)

Expand All @@ -497,10 +544,10 @@ def handle_result(args, current_char, target_window_id, boss):


if __name__ == '__main__':
import sys
if '-h' in sys.argv or '--help' in sys.argv:
print('Choose a unicode character to input into the terminal')
raise SystemExit(0)
ans = main(sys.argv)
if ans:
print(ans)
elif __name__ == '__doc__':
sys.cli_docs['usage'] = usage
sys.cli_docs['options'] = OPTIONS
sys.cli_docs['help_text'] = help_text
8 changes: 8 additions & 0 deletions kitty/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -2267,6 +2267,13 @@ wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
return PyLong_FromLong(wcwidth_std(PyLong_AsLong(chr)));
}

static PyObject*
screen_is_emoji_presentation_base(PyObject UNUSED *self, PyObject *code_) {
unsigned long code = PyLong_AsUnsignedLong(code_);
if (is_emoji_presentation_base(code)) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}


#define MND(name, args) {#name, (PyCFunction)name, args, #name},
#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)
Expand Down Expand Up @@ -2380,6 +2387,7 @@ PyTypeObject Screen_Type = {
static PyMethodDef module_methods[] = {
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"wcswidth", (PyCFunction)screen_wcswidth, METH_O, ""},
{"is_emoji_presentation_base", (PyCFunction)screen_is_emoji_presentation_base, METH_O, ""},
{"truncate_point_for_length", (PyCFunction)screen_truncate_point_for_length, METH_VARARGS, ""},
{NULL} /* Sentinel */
};
Expand Down

0 comments on commit 738878c

Please sign in to comment.