Skip to content

Commit f21dfdb

Browse files
committed
Fix breaking to input Emoji with ZWJ.
1 parent b47b02a commit f21dfdb

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

lib/reline/line_editor.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,12 @@ def finish
12651265
else
12661266
@line = byteinsert(@line, @byte_pointer, str)
12671267
end
1268+
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
12681269
@byte_pointer += bytesize
1270+
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
1271+
if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
1272+
width = 0
1273+
end
12691274
@cursor += width
12701275
@cursor_max += width
12711276
end

test/reline/test_key_actor_emacs.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,6 +2105,68 @@ def test_ed_search_next_history_with_empty
21052105
assert_line('')
21062106
end
21072107

2108+
# Unicode emoji test
2109+
if RELINE_TEST_ENCODING == Encoding::UTF_8
2110+
def test_ed_insert_for_include_zwj_emoji
2111+
# U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466 is family: man, woman, girl, boy "👨‍👩‍👧‍👦"
2112+
input_keys("\u{1F468}") # U+1F468 is man "👨"
2113+
assert_line("\u{1F468}")
2114+
assert_byte_pointer_size("\u{1F468}")
2115+
assert_cursor(2)
2116+
assert_cursor_max(2)
2117+
input_keys("\u200D") # U+200D is ZERO WIDTH JOINER
2118+
assert_line("\u{1F468 200D}")
2119+
assert_byte_pointer_size("\u{1F468 200D}")
2120+
assert_cursor(2)
2121+
assert_cursor_max(2)
2122+
input_keys("\u{1F469}") # U+1F469 is woman "👩"
2123+
assert_line("\u{1F468 200D 1F469}")
2124+
assert_byte_pointer_size("\u{1F468 200D 1F469}")
2125+
assert_cursor(2)
2126+
assert_cursor_max(2)
2127+
input_keys("\u200D") # U+200D is ZERO WIDTH JOINER
2128+
assert_line("\u{1F468 200D 1F469 200D}")
2129+
assert_byte_pointer_size("\u{1F468 200D 1F469 200D}")
2130+
assert_cursor(2)
2131+
assert_cursor_max(2)
2132+
input_keys("\u{1F467}") # U+1F467 is girl "👧"
2133+
assert_line("\u{1F468 200D 1F469 200D 1F467}")
2134+
assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467}")
2135+
assert_cursor(2)
2136+
assert_cursor_max(2)
2137+
input_keys("\u200D") # U+200D is ZERO WIDTH JOINER
2138+
assert_line("\u{1F468 200D 1F469 200D 1F467 200D}")
2139+
assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467 200D}")
2140+
assert_cursor(2)
2141+
assert_cursor_max(2)
2142+
input_keys("\u{1F466}") # U+1F466 is boy "👦"
2143+
assert_line("\u{1F468 200D 1F469 200D 1F467 200D 1F466}")
2144+
assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467 200D 1F466}")
2145+
assert_cursor(2)
2146+
assert_cursor_max(2)
2147+
# U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466 is family: man, woman, girl, boy "👨‍👩‍👧‍👦"
2148+
input_keys("\u{1F468 200D 1F469 200D 1F467 200D 1F466}")
2149+
assert_line("\u{1F468 200D 1F469 200D 1F467 200D 1F466 1F468 200D 1F469 200D 1F467 200D 1F466}")
2150+
assert_byte_pointer_size("\u{1F468 200D 1F469 200D 1F467 200D 1F466 1F468 200D 1F469 200D 1F467 200D 1F466}")
2151+
assert_cursor(4)
2152+
assert_cursor_max(4)
2153+
end
2154+
2155+
def test_ed_insert_for_include_valiation_selector
2156+
# U+0030 U+FE00 is DIGIT ZERO + VARIATION SELECTOR-1 "0︀"
2157+
input_keys("\u0030") # U+0030 is DIGIT ZERO
2158+
assert_line("\u0030")
2159+
assert_byte_pointer_size("\u0030")
2160+
assert_cursor(1)
2161+
assert_cursor_max(1)
2162+
input_keys("\uFE00") # U+FE00 is VARIATION SELECTOR-1
2163+
assert_line("\u{0030 FE00}")
2164+
assert_byte_pointer_size("\u{0030 FE00}")
2165+
assert_cursor(1)
2166+
assert_cursor_max(1)
2167+
end
2168+
end
2169+
21082170
=begin # TODO: move KeyStroke instance from Reline to LineEditor
21092171
def test_key_delete
21102172
input_keys('ab')

0 commit comments

Comments
 (0)