Skip to content

Commit 810ab19

Browse files
committed
Add a cut variation of Reline::Unicode.take_range method take_mbchar_range
1 parent 080869f commit 810ab19

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

lib/reline/unicode.rb

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,24 @@ def self.split_by_width(str, max_width, encoding = str.encoding)
173173

174174
# Take a chunk of a String cut by width with escape sequences.
175175
def self.take_range(str, start_col, max_width)
176+
take_mbchar_range(str, start_col, max_width).first
177+
end
178+
179+
def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
176180
chunk = String.new(encoding: str.encoding)
177181
total_width = 0
178182
rest = str.encode(Encoding::UTF_8)
179183
in_zero_width = false
184+
chunk_start_col = nil
185+
chunk_end_col = nil
180186
rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
181187
case
182188
when non_printing_start
183189
in_zero_width = true
190+
chunk << NON_PRINTING_START
184191
when non_printing_end
185192
in_zero_width = false
193+
chunk << NON_PRINTING_END
186194
when csi
187195
chunk << csi
188196
when osc
@@ -192,13 +200,28 @@ def self.take_range(str, start_col, max_width)
192200
chunk << gc
193201
else
194202
mbchar_width = get_mbchar_width(gc)
203+
prev_width = total_width
195204
total_width += mbchar_width
196-
break if (start_col + max_width) < total_width
197-
chunk << gc if start_col < total_width
205+
break if !cover_end && total_width > start_col + width
206+
if cover_begin ? start_col < total_width : start_col <= prev_width
207+
chunk << gc
208+
chunk_start_col ||= prev_width
209+
chunk_end_col = total_width
210+
elsif padding && prev_width < start_col && start_col < total_width
211+
chunk << ' '
212+
chunk_start_col = start_col
213+
end
214+
break if total_width >= start_col + width
198215
end
199216
end
200217
end
201-
chunk
218+
chunk_start_col ||= start_col
219+
chunk_end_col ||= start_col
220+
if padding && chunk_end_col < start_col + width
221+
chunk << ' ' * (start_col + width - chunk_end_col)
222+
chunk_end_col = start_col + width
223+
end
224+
[chunk, chunk_start_col, chunk_end_col - chunk_start_col]
202225
end
203226

204227
def self.get_next_mbchar_size(line, byte_pointer)

test/reline/test_unicode.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ def test_split_by_width
4141
def test_take_range
4242
assert_equal 'cdef', Reline::Unicode.take_range('abcdefghi', 2, 4)
4343
assert_equal 'あde', Reline::Unicode.take_range('abあdef', 2, 4)
44-
assert_equal 'zerocdef', Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
45-
assert_equal 'bzerocde', Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
44+
assert_equal "\1zero\2cdef", Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
45+
assert_equal "b\1zero\2cde", Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
4646
assert_equal "\e[31mcd\e[42mef", Reline::Unicode.take_range("\e[31mabcd\e[42mefg", 2, 4)
4747
assert_equal "\e]0;1\acd", Reline::Unicode.take_range("ab\e]0;1\acd", 2, 3)
4848
assert_equal 'いう', Reline::Unicode.take_range('あいうえお', 2, 4)
@@ -62,4 +62,24 @@ def test_calculate_width
6262
assert_equal 10, Reline::Unicode.calculate_width('あいうえお')
6363
assert_equal 10, Reline::Unicode.calculate_width('あいうえお', true)
6464
end
65+
66+
def test_take_mbchar_range
67+
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4)
68+
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, padding: true)
69+
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_begin: true)
70+
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_end: true)
71+
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4)
72+
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, padding: true)
73+
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_begin: true)
74+
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_end: true)
75+
assert_equal ['う', 4, 2], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4)
76+
assert_equal [' う ', 3, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, padding: true)
77+
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true)
78+
assert_equal ['うえ', 4, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true)
79+
assert_equal ['いう ', 2, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true, padding: true)
80+
assert_equal [' うえ', 3, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true, padding: true)
81+
assert_equal [' うえお ', 3, 10], Reline::Unicode.take_mbchar_range('あいうえお', 3, 10, padding: true)
82+
assert_equal ["\e[31mc\1ABC\2d\e[0mef", 2, 4], Reline::Unicode.take_mbchar_range("\e[31mabc\1ABC\2d\e[0mefghi", 2, 4)
83+
assert_equal ["\e[41m \e[42mい\e[43m ", 1, 4], Reline::Unicode.take_mbchar_range("\e[41mあ\e[42mい\e[43mう\e[0m", 1, 4, padding: true)
84+
end
6585
end

0 commit comments

Comments
 (0)