Как перемещать курсор в оболочке bash при отражении смайликов? - PullRequest
4 голосов
/ 27 июня 2019

Я пишу игровой движок для Bash, используя функцию перемещения курсора, описанную здесь .Однако, если я повторяю смайлики или другие символы UTF-8, которые занимают более 1 байта, позиция курсора, кажется, запуталась.

Например, следующий код должен отображать "1ho3",переместите курсор назад на 3 позиции и затем повторите «abc» в том же месте.Результат должен быть только «abc» (в идеале).Вместо этого я вижу «1abc»

~ $ echo -e "1?3\033[3Dabc"
1abc

Аналогичная проблема может быть проиллюстрирована с подачей каретки:

~ $ echo -e "1?3\rabc"
abc3

Есть ли хороший способ решения этой проблемы?Я использую приложение Terminal на macOS.Есть ли какой-нибудь переносимый способ сделать это?

Примечание: обратите внимание: не все символы UTF-8, похоже, ведут себя таким образом.В основном, я смог воспроизвести эту проблему только с эмодзи:

~ $ while true; do read -p "Enter emoji: " x; echo $x | hexdump; echo -e "1${x}3\033[3Dabc"; done
Enter emoji: ?
0000000 f0 9f 94 88 0a                                 
0000005
1abc
Enter emoji: ♞
0000000 e2 99 9e 0a                                    
0000004
abc
Enter emoji: ☞
0000000 e2 98 9e 0a                                    
0000004
abc
Enter emoji: ?
0000000 f0 9f 98 8b 0a                                 
0000005
1abc
Enter emoji: ?
0000000 f0 9f 83 98 0a                                 
0000005
abc
Enter emoji: ?
0000000 f0 9f 80 96 0a                                 
0000005
abc
Enter emoji: ?
0000000 f0 9d 95 ad 0a                                 
0000005
abc
Enter emoji: ??
0000000 f0 9f 87 ba f0 9f 87 b8 0a                     
0000009
1abc
Enter emoji: ✎
0000000 e2 9c 8e 0a                                    
0000004
abc

Ответы [ 2 ]

3 голосов
/ 28 июня 2019

Проблема возникает из-за того, что actually фактически отображается в двух столбцах.В моей системе четыре смайлика и восемь цифр одинаково длинны:

????
12345678

Ожидается, что один широкий символ потребует два узких символа, чтобы перезаписать его.

Обработка этих смайликов как широкихрекомендуется Unicode TR51-16 :

В настоящее время для смайликов используется квадратное соотношение сторон, основанное на их происхождении на японском языке.Для совместимости рекомендуется продолжить эту практику с текущими и будущими смайликами.Как правило, они будут иметь примерно такое же вертикальное расположение и ширину продвижения, как и идеограммы CJK.

Учитывая рекомендацию, мне было бы удобно просто жестко кодировать что-либо в блоке Unicode "Emoticon" как широкое.Ваши другие символы, которые работают, такие как ? и ☞, не находятся в блоке смайликов (они в маджонге и различных символах соответственно).

Если вы хотите определить ширину во время выполнения, вы можете, например, задать Python, который сообщает о своей ширине в Восточной Азии как полную / широкую, даже если сами таблицы Unicode помечают ее как нейтральную:

$ python3 -c 'import sys; import unicodedata as u; print(u.east_asian_width(sys.argv[1]))' ?
W

$ python3 -c 'import sys; import unicodedata as u; print(u.east_asian_width(sys.argv[1]))' ♞
N

?? - это особый случай, так как он состоит из двух разных символов региональных индикаторов с отдельными кодовыми точками, но Python помечает каждый из них как нейтральный, поэтому, если вы возьмете это в 1, это все равно будет равно 2.

1 голос
/ 28 июня 2019

Попробуйте это:

s="1?3" ; printf "$s"; sleep 2; printf "\033[$((${#s}+1))Dabc%${#s}s\n" ' '

Я установил задержку между printf с, чтобы было легче увидеть, что происходит. Сначала есть:

1? 3

Через две секунды вышеприведенное будет перезаписано:

abc

Как это работает: мы помещаем Unicode в строку $s. ${#s} возвращает длину в байтах этой строки. Длина используется в $((${#s}+1)) для вычисления количества пробелов для перемещения, затем %${#s}s сообщает printf, сколько пробелов нужно (плюс еще несколько), чтобы перезаписать любые оставшиеся символы.

Если «еще несколько» пробелов слишком много, подсчет перезаписываемой строки дает более точный результат:

s="1?3" t="abc" 
printf "${s}"; sleep 2; printf "\033[$((${#s}+1))D$t%$((1+${#s}-${#t}))s\n" ''
...