mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Support CSI sequences in prompt
This commit is contained in:
parent
ca435ed04a
commit
c00d805672
1 changed files with 61 additions and 16 deletions
|
@ -79,6 +79,8 @@ class Reline::LineEditor
|
||||||
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
||||||
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
||||||
|
|
||||||
|
CSI_REGEXP = /\e\[(?:\d+;?)*[ABCDEFGHJKSTfminsuhl]/
|
||||||
|
|
||||||
def initialize(config)
|
def initialize(config)
|
||||||
@config = config
|
@config = config
|
||||||
reset_variables
|
reset_variables
|
||||||
|
@ -93,7 +95,7 @@ class Reline::LineEditor
|
||||||
def reset_variables(prompt = '', encoding = Encoding.default_external)
|
def reset_variables(prompt = '', encoding = Encoding.default_external)
|
||||||
@prompt = prompt
|
@prompt = prompt
|
||||||
@encoding = encoding
|
@encoding = encoding
|
||||||
@prompt_width = calculate_width(@prompt)
|
@prompt_width = calculate_width(@prompt, true)
|
||||||
@is_multiline = false
|
@is_multiline = false
|
||||||
@finished = false
|
@finished = false
|
||||||
@cleared = false
|
@cleared = false
|
||||||
|
@ -159,21 +161,47 @@ class Reline::LineEditor
|
||||||
height
|
height
|
||||||
end
|
end
|
||||||
|
|
||||||
private def split_by_width(str, max_width)
|
private def split_by_width(prompt, str, max_width)
|
||||||
lines = [String.new(encoding: @encoding)]
|
lines = [String.new(encoding: @encoding)]
|
||||||
|
height = 1
|
||||||
width = 0
|
width = 0
|
||||||
|
rest_prompt = prompt.encode(Encoding::UTF_8)
|
||||||
|
loop do
|
||||||
|
break if rest_prompt.empty?
|
||||||
|
if rest_prompt =~ /^#{CSI_REGEXP}/
|
||||||
|
lines.last << $&
|
||||||
|
rest_prompt = $'
|
||||||
|
else
|
||||||
|
gcs = rest_prompt.grapheme_clusters
|
||||||
|
gc = gcs.first
|
||||||
|
rest_prompt = gcs[1..-1].join
|
||||||
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
||||||
|
width += mbchar_width
|
||||||
|
if width > max_width
|
||||||
|
width = mbchar_width
|
||||||
|
lines << nil
|
||||||
|
lines << String.new(encoding: @encoding)
|
||||||
|
height += 1
|
||||||
|
end
|
||||||
|
lines.last << gc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
lines << :split
|
||||||
|
lines << String.new(encoding: @encoding)
|
||||||
str.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
|
str.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
|
||||||
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
||||||
width += mbchar_width
|
width += mbchar_width
|
||||||
if width > max_width
|
if width > max_width
|
||||||
width = mbchar_width
|
width = mbchar_width
|
||||||
|
lines << nil
|
||||||
lines << String.new(encoding: @encoding)
|
lines << String.new(encoding: @encoding)
|
||||||
|
height += 1
|
||||||
end
|
end
|
||||||
lines.last << gc
|
lines.last << gc
|
||||||
end
|
end
|
||||||
# The cursor moves to next line in first
|
# The cursor moves to next line in first
|
||||||
lines << String.new(encoding: @encoding) if width == max_width
|
lines << String.new(encoding: @encoding) if width == max_width
|
||||||
lines
|
[lines, height]
|
||||||
end
|
end
|
||||||
|
|
||||||
private def scroll_down(val)
|
private def scroll_down(val)
|
||||||
|
@ -367,38 +395,52 @@ class Reline::LineEditor
|
||||||
end
|
end
|
||||||
|
|
||||||
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
|
||||||
whole_line = prompt + (line_to_render.nil? ? '' : line_to_render)
|
visual_lines, height = split_by_width(prompt, line_to_render.nil? ? '' : line_to_render, @screen_size.last)
|
||||||
visual_lines = split_by_width(whole_line, @screen_size.last)
|
|
||||||
if with_control
|
if with_control
|
||||||
if visual_lines.size > @highest_in_this
|
if height > @highest_in_this
|
||||||
diff = visual_lines.size - @highest_in_this
|
diff = height - @highest_in_this
|
||||||
scroll_down(diff)
|
scroll_down(diff)
|
||||||
@highest_in_all += diff
|
@highest_in_all += diff
|
||||||
@highest_in_this = visual_lines.size
|
@highest_in_this = height
|
||||||
move_cursor_up(1)
|
move_cursor_up(1)
|
||||||
end
|
end
|
||||||
move_cursor_up(@started_from)
|
move_cursor_up(@started_from)
|
||||||
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
||||||
end
|
end
|
||||||
visual_lines.each_with_index do |line, index|
|
index = 0
|
||||||
Reline::IOGate.move_cursor_column(0)
|
is_prompt = true
|
||||||
escaped_print line
|
Reline::IOGate.move_cursor_column(0)
|
||||||
|
visual_lines.each do |line|
|
||||||
|
if line == :split
|
||||||
|
is_prompt = false
|
||||||
|
next
|
||||||
|
end
|
||||||
|
if line.nil?
|
||||||
|
Reline::IOGate.erase_after_cursor
|
||||||
|
move_cursor_down(1)
|
||||||
|
Reline::IOGate.move_cursor_column(0)
|
||||||
|
next
|
||||||
|
end
|
||||||
|
if is_prompt
|
||||||
|
@output.print line
|
||||||
|
else
|
||||||
|
escaped_print line
|
||||||
|
end
|
||||||
if @first_prompt
|
if @first_prompt
|
||||||
@first_prompt = false
|
@first_prompt = false
|
||||||
@pre_input_hook&.call
|
@pre_input_hook&.call
|
||||||
end
|
end
|
||||||
Reline::IOGate.erase_after_cursor
|
|
||||||
move_cursor_down(1) if index < (visual_lines.size - 1)
|
|
||||||
end
|
end
|
||||||
|
Reline::IOGate.erase_after_cursor
|
||||||
if with_control
|
if with_control
|
||||||
if finished?
|
if finished?
|
||||||
@output.puts
|
@output.puts
|
||||||
else
|
else
|
||||||
move_cursor_up((visual_lines.size - 1) - @started_from)
|
move_cursor_up((height - 1) - @started_from)
|
||||||
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
visual_lines.size
|
height
|
||||||
end
|
end
|
||||||
|
|
||||||
def editing_mode
|
def editing_mode
|
||||||
|
@ -759,7 +801,10 @@ class Reline::LineEditor
|
||||||
new_str
|
new_str
|
||||||
end
|
end
|
||||||
|
|
||||||
private def calculate_width(str)
|
private def calculate_width(str, allow_csi = false)
|
||||||
|
if allow_csi
|
||||||
|
str = str.gsub(CSI_REGEXP, '')
|
||||||
|
end
|
||||||
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
|
str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
|
||||||
width + Reline::Unicode.get_mbchar_width(gc)
|
width + Reline::Unicode.get_mbchar_width(gc)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue