Skip to content

Commit

Permalink
Add a cut variation of Reline::Unicode.take_range method take_mbchar_…
Browse files Browse the repository at this point in the history
…range
  • Loading branch information
tompng committed Nov 14, 2023
1 parent e861087 commit 5adc98d
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 5 deletions.
30 changes: 27 additions & 3 deletions lib/reline/unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,24 @@ def self.split_by_width(str, max_width, encoding = str.encoding)

# Take a chunk of a String cut by width with escape sequences.
def self.take_range(str, start_col, max_width)
take_mbchar_range(str, start_col, max_width).first
end

def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
chunk = String.new(encoding: str.encoding)
total_width = 0
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
chunk_start_col = nil
chunk_end_col = nil
rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
case
when non_printing_start
in_zero_width = true
chunk << NON_PRINTING_START
when non_printing_end
in_zero_width = false
chunk << NON_PRINTING_END
when csi
chunk << csi
when osc
Expand All @@ -192,13 +200,29 @@ def self.take_range(str, start_col, max_width)
chunk << gc
else
mbchar_width = get_mbchar_width(gc)
prev_width = total_width
total_width += mbchar_width
break if (start_col + max_width) < total_width
chunk << gc if start_col < total_width
break if !cover_end && total_width > start_col + width
if cover_begin ? start_col < total_width : start_col <= prev_width
if padding && chunk_start_col.nil? && start_col < prev_width
chunk << ' ' * (prev_width - start_col)
chunk_start_col = start_col
end
chunk << gc
chunk_start_col ||= prev_width
chunk_end_col = total_width
end
break if total_width >= start_col + width
end
end
end
chunk
chunk_start_col ||= start_col
chunk_end_col ||= start_col
if padding && chunk_end_col < start_col + width
chunk << ' ' * (start_col + width - chunk_end_col)
chunk_end_col = start_col + width
end
[chunk, chunk_start_col, chunk_end_col - chunk_start_col]
end

def self.get_next_mbchar_size(line, byte_pointer)
Expand Down
24 changes: 22 additions & 2 deletions test/reline/test_unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def test_split_by_width
def test_take_range
assert_equal 'cdef', Reline::Unicode.take_range('abcdefghi', 2, 4)
assert_equal 'あde', Reline::Unicode.take_range('abあdef', 2, 4)
assert_equal 'zerocdef', Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
assert_equal 'bzerocde', Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
assert_equal "\1zero\2cdef", Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
assert_equal "b\1zero\2cde", Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
assert_equal "\e[31mcd\e[42mef", Reline::Unicode.take_range("\e[31mabcd\e[42mefg", 2, 4)
assert_equal "\e]0;1\acd", Reline::Unicode.take_range("ab\e]0;1\acd", 2, 3)
assert_equal 'いう', Reline::Unicode.take_range('あいうえお', 2, 4)
Expand All @@ -62,4 +62,24 @@ def test_calculate_width
assert_equal 10, Reline::Unicode.calculate_width('あいうえお')
assert_equal 10, Reline::Unicode.calculate_width('あいうえお', true)
end

def test_take_mbchar_range
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4)
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, padding: true)
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_begin: true)
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_end: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, padding: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_begin: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_end: true)
assert_equal ['う', 4, 2], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4)
assert_equal [' う ', 3, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, padding: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true)
assert_equal ['うえ', 4, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true)
assert_equal ['いう ', 2, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true, padding: true)
assert_equal [' うえ', 3, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true, padding: true)
assert_equal [' うえお ', 3, 10], Reline::Unicode.take_mbchar_range('あいうえお', 3, 10, padding: true)
assert_equal ["\e[31mc\1ABC\2d\e[0mef", 2, 4], Reline::Unicode.take_mbchar_range("\e[31mabc\1ABC\2d\e[0mefghi", 2, 4)
assert_equal ["\e[47m い ", 1, 4], Reline::Unicode.take_mbchar_range("\e[47mあいうえお\e[0m", 1, 4, padding: true)
end
end

0 comments on commit 5adc98d

Please sign in to comment.