Skip to content

Commit

Permalink
Move width calculator methods to Reline::Unicode
Browse files Browse the repository at this point in the history
  • Loading branch information
aycabta committed Aug 27, 2020
1 parent 3d2fe49 commit 5a920f6
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 62 deletions.
64 changes: 2 additions & 62 deletions lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,6 @@ module CompletionState
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
MenuInfo = Struct.new('MenuInfo', :target, :list)

CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
NON_PRINTING_START = "\1"
NON_PRINTING_END = "\2"
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/

def initialize(config, encoding)
@config = config
@completion_append_character = ''
Expand Down Expand Up @@ -235,39 +229,7 @@ def multiline_off
end

private def split_by_width(prompt, str, max_width)
lines = [String.new(encoding: @encoding)]
height = 1
width = 0
rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
in_zero_width = false
rest.scan(WIDTH_SCANNER) do |gc|
case gc
when NON_PRINTING_START
in_zero_width = true
when NON_PRINTING_END
in_zero_width = false
when CSI_REGEXP, OSC_REGEXP
lines.last << gc
else
unless in_zero_width
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
if (width += mbchar_width) > max_width
width = mbchar_width
lines << nil
lines << String.new(encoding: @encoding)
height += 1
end
end
lines.last << gc
end
end
# The cursor moves to next line in first
if width == max_width
lines << nil
lines << String.new(encoding: @encoding)
height += 1
end
[lines, height]
Reline::Unicode.split_by_width(prompt, str, max_width, @encoding)
end

private def scroll_down(val)
Expand Down Expand Up @@ -1081,29 +1043,7 @@ def finish
end

private def calculate_width(str, allow_escape_code = false)
if allow_escape_code
width = 0
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
rest.scan(WIDTH_SCANNER) do |gc|
case gc
when NON_PRINTING_START
in_zero_width = true
when NON_PRINTING_END
in_zero_width = false
when CSI_REGEXP, OSC_REGEXP
else
unless in_zero_width
width += Reline::Unicode.get_mbchar_width(gc)
end
end
end
width
else
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
w + Reline::Unicode.get_mbchar_width(gc)
}
end
Reline::Unicode.calculate_width(str, allow_escape_code)
end

private def key_delete(key)
Expand Down
68 changes: 68 additions & 0 deletions lib/reline/unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class Reline::Unicode
}
EscapedChars = EscapedPairs.keys.map(&:chr)

CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
NON_PRINTING_START = "\1"
NON_PRINTING_END = "\2"
WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/

def self.get_mbchar_byte_size_by_first_char(c)
# Checks UTF-8 character byte size
case c.ord
Expand Down Expand Up @@ -85,6 +91,68 @@ def self.get_mbchar_width(mbchar)
end
end

def self.calculate_width(str, allow_escape_code = false)
if allow_escape_code
width = 0
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
rest.scan(WIDTH_SCANNER) do |gc|
case gc
when NON_PRINTING_START
in_zero_width = true
when NON_PRINTING_END
in_zero_width = false
when CSI_REGEXP, OSC_REGEXP
else
unless in_zero_width
width += get_mbchar_width(gc)
end
end
end
width
else
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc|
w + get_mbchar_width(gc)
}
end
end

def self.split_by_width(prompt, str, max_width, encoding)
lines = [String.new(encoding: encoding)]
height = 1
width = 0
rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
in_zero_width = false
rest.scan(WIDTH_SCANNER) do |gc|
case gc
when NON_PRINTING_START
in_zero_width = true
when NON_PRINTING_END
in_zero_width = false
when CSI_REGEXP, OSC_REGEXP
lines.last << gc
else
unless in_zero_width
mbchar_width = get_mbchar_width(gc)
if (width += mbchar_width) > max_width
width = mbchar_width
lines << nil
lines << String.new(encoding: encoding)
height += 1
end
end
lines.last << gc
end
end
# The cursor moves to next line in first
if width == max_width
lines << nil
lines << String.new(encoding: encoding)
height += 1
end
[lines, height]
end

def self.get_next_mbchar_size(line, byte_pointer)
grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
grapheme ? grapheme.bytesize : 0
Expand Down

0 comments on commit 5a920f6

Please sign in to comment.