unicode input kitten: Add an option :option:kitty +kitten unicode_input --emoji-variation to control the presentation variant of selected emojis
Fixes #2139
This commit is contained in:
parent
d5682fe49a
commit
738878c2ff
@ -30,6 +30,10 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
|
|
||||||
- diff kitten: Allow diffing remote files easily via ssh (:iss:`727`)
|
- 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
|
- Add specialised rendering for a few more box powerline and unicode symbols
|
||||||
(:pull:`2074` and :pull:`2021`)
|
(:pull:`2074` and :pull:`2021`)
|
||||||
|
|
||||||
|
|||||||
@ -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
|
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
|
a leading period and the index for the match if you don't like to use arrow
|
||||||
keys.
|
keys.
|
||||||
|
|
||||||
|
Command Line Interface
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. include:: ../generated/cli-kitten-unicode_input.rst
|
||||||
|
|||||||
@ -5,25 +5,27 @@
|
|||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from contextlib import suppress
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from contextlib import suppress
|
|
||||||
|
|
||||||
|
from kitty.cli import parse_args
|
||||||
from kitty.config import cached_values_for
|
from kitty.config import cached_values_for
|
||||||
from kitty.constants import config_dir
|
from kitty.constants import config_dir
|
||||||
from kitty.utils import get_editor
|
from kitty.fast_data_types import wcswidth, is_emoji_presentation_base
|
||||||
from kitty.fast_data_types import wcswidth
|
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import (
|
||||||
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
|
||||||
enter_key
|
enter_key
|
||||||
)
|
)
|
||||||
|
from kitty.utils import get_editor
|
||||||
|
|
||||||
from ..tui.line_edit import LineEdit
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
|
from ..tui.line_edit import LineEdit
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
from ..tui.operations import (
|
from ..tui.operations import (
|
||||||
clear_screen, colored, cursor, faint, set_line_wrapping,
|
clear_screen, colored, cursor, faint, set_line_wrapping, set_window_title,
|
||||||
set_window_title, sgr, styled
|
sgr, styled
|
||||||
)
|
)
|
||||||
|
|
||||||
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
|
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
|
||||||
@ -131,7 +133,8 @@ def decode_hint(x):
|
|||||||
|
|
||||||
class Table:
|
class Table:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, emoji_variation):
|
||||||
|
self.emoji_variation = emoji_variation
|
||||||
self.layout_dirty = True
|
self.layout_dirty = True
|
||||||
self.last_rows = self.last_cols = -1
|
self.last_rows = self.last_cols = -1
|
||||||
self.codepoints = []
|
self.codepoints = []
|
||||||
@ -161,7 +164,10 @@ class Table:
|
|||||||
self.layout_dirty = False
|
self.layout_dirty = False
|
||||||
|
|
||||||
def safe_chr(codepoint):
|
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:
|
if self.mode is NAME:
|
||||||
def as_parts(i, codepoint):
|
def as_parts(i, codepoint):
|
||||||
@ -248,8 +254,13 @@ def is_index(w):
|
|||||||
|
|
||||||
class UnicodeInput(Handler):
|
class UnicodeInput(Handler):
|
||||||
|
|
||||||
def __init__(self, cached_values):
|
def __init__(self, cached_values, emoji_variation='none'):
|
||||||
self.cached_values = cached_values
|
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.line_edit = LineEdit()
|
||||||
self.recent = list(self.cached_values.get('recent', DEFAULT_SET))
|
self.recent = list(self.cached_values.get('recent', DEFAULT_SET))
|
||||||
self.current_char = None
|
self.current_char = None
|
||||||
@ -257,9 +268,17 @@ class UnicodeInput(Handler):
|
|||||||
self.last_updated_code_point_at = None
|
self.last_updated_code_point_at = None
|
||||||
self.choice_line = ''
|
self.choice_line = ''
|
||||||
self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX')
|
self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX')
|
||||||
self.table = Table()
|
self.table = Table(self.emoji_variation)
|
||||||
self.update_prompt()
|
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):
|
def update_codepoints(self):
|
||||||
codepoints = None
|
codepoints = None
|
||||||
if self.mode is HEX:
|
if self.mode is HEX:
|
||||||
@ -320,8 +339,10 @@ class UnicodeInput(Handler):
|
|||||||
self.choice_line = ''
|
self.choice_line = ''
|
||||||
else:
|
else:
|
||||||
c, color = self.current_char, 'green'
|
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(
|
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))
|
self.prompt = self.prompt_template.format(colored(c, color))
|
||||||
|
|
||||||
def init_terminal_state(self):
|
def init_terminal_state(self):
|
||||||
@ -475,17 +496,43 @@ class UnicodeInput(Handler):
|
|||||||
self.refresh()
|
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):
|
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()
|
loop = Loop()
|
||||||
with cached_values_for('unicode-input') as cached_values:
|
with cached_values_for('unicode-input') as cached_values:
|
||||||
handler = UnicodeInput(cached_values)
|
handler = UnicodeInput(cached_values, args.emoji_variation)
|
||||||
loop.loop(handler)
|
loop.loop(handler)
|
||||||
if handler.current_char and loop.return_code == 0:
|
if handler.current_char and loop.return_code == 0:
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
handler.recent.remove(ord(handler.current_char))
|
handler.recent.remove(ord(handler.current_char))
|
||||||
recent = [ord(handler.current_char)] + handler.recent
|
recent = [ord(handler.current_char)] + handler.recent
|
||||||
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
cached_values['recent'] = recent[:len(DEFAULT_SET)]
|
||||||
return handler.current_char
|
return handler.resolved_current_char
|
||||||
if loop.return_code != 0:
|
if loop.return_code != 0:
|
||||||
raise SystemExit(loop.return_code)
|
raise SystemExit(loop.return_code)
|
||||||
|
|
||||||
@ -497,10 +544,10 @@ def handle_result(args, current_char, target_window_id, boss):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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)
|
ans = main(sys.argv)
|
||||||
if ans:
|
if ans:
|
||||||
print(ans)
|
print(ans)
|
||||||
|
elif __name__ == '__doc__':
|
||||||
|
sys.cli_docs['usage'] = usage
|
||||||
|
sys.cli_docs['options'] = OPTIONS
|
||||||
|
sys.cli_docs['help_text'] = help_text
|
||||||
|
|||||||
@ -2267,6 +2267,13 @@ wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
|
|||||||
return PyLong_FromLong(wcwidth_std(PyLong_AsLong(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 MND(name, args) {#name, (PyCFunction)name, args, #name},
|
||||||
#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)
|
#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)
|
||||||
@ -2380,6 +2387,7 @@ PyTypeObject Screen_Type = {
|
|||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
||||||
{"wcswidth", (PyCFunction)screen_wcswidth, 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, ""},
|
{"truncate_point_for_length", (PyCFunction)screen_truncate_point_for_length, METH_VARARGS, ""},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user