summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2019-05-28 00:15:38 +0900
committeraycabta <aycabta@gmail.com>2019-05-28 00:48:02 +0900
commit9a68aba79f8a3e144e6409988c2e17a5d3f11f26 (patch)
treef17d908ed83b9b3abc28ef67f0f6ef36c83e8952 /lib
parent70166b3ca3eff9747d8c861b3a30e3ef96ffbb72 (diff)
Support OSC and treat \1 \2 correctly
Diffstat (limited to 'lib')
-rw-r--r--lib/reline/line_editor.rb65
1 files changed, 53 insertions, 12 deletions
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index cba90fde05..75b93be44d 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -82,7 +82,9 @@ class Reline::LineEditor
MenuInfo = Struct.new('MenuInfo', :target, :list)
CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
- NON_PRINTING_ESCAPES = "\1\2"
+ OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
+ NON_PRINTING_START = "\1"
+ NON_PRINTING_END = "\2"
def initialize(config)
@config = config
@@ -180,19 +182,31 @@ class Reline::LineEditor
lines = [String.new(encoding: @encoding)]
height = 1
width = 0
- prompt = prompt.tr(NON_PRINTING_ESCAPES, '')
- str = str.tr(NON_PRINTING_ESCAPES, '')
rest = "#{prompt}#{str}".encode(Encoding::UTF_8)
+ in_zero_width = false
loop do
break if rest.empty?
- if rest =~ /\A#{CSI_REGEXP}/
+ if rest.start_with?(NON_PRINTING_START)
+ rest.delete_prefix!(NON_PRINTING_START)
+ in_zero_width = true
+ elsif rest.start_with?(NON_PRINTING_END)
+ rest.delete_prefix!(NON_PRINTING_END)
+ in_zero_width = false
+ elsif rest.start_with?(CSI_REGEXP)
+ lines.last << $&
+ rest = $'
+ elsif rest.start_with?(OSC_REGEXP)
lines.last << $&
rest = $'
else
gcs = rest.grapheme_clusters
gc = gcs.first
rest = gcs[1..-1].join
- mbchar_width = Reline::Unicode.get_mbchar_width(gc)
+ if in_zero_width
+ mbchar_width = 0
+ else
+ mbchar_width = Reline::Unicode.get_mbchar_width(gc)
+ end
width += mbchar_width
if width > max_width
width = mbchar_width
@@ -825,14 +839,41 @@ class Reline::LineEditor
new_str
end
- private def calculate_width(str, allow_csi = false)
- if allow_csi
- str = str.gsub(CSI_REGEXP, '')
- str = str.tr(NON_PRINTING_ESCAPES, '')
+ 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
+ loop do
+ break if rest.empty?
+ if rest.start_with?(NON_PRINTING_START)
+ rest.delete_prefix!(NON_PRINTING_START)
+ in_zero_width = true
+ elsif rest.start_with?(NON_PRINTING_END)
+ rest.delete_prefix!(NON_PRINTING_END)
+ in_zero_width = false
+ elsif rest.start_with?(CSI_REGEXP)
+ rest = $'
+ elsif rest.start_with?(OSC_REGEXP)
+ rest = $'
+ else
+ gcs = rest.grapheme_clusters
+ gc = gcs.first
+ rest = gcs[1..-1].join
+ if in_zero_width
+ mbchar_width = 0
+ else
+ mbchar_width = Reline::Unicode.get_mbchar_width(gc)
+ end
+ width += mbchar_width
+ end
+ end
+ width
+ else
+ str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
+ width + Reline::Unicode.get_mbchar_width(gc)
+ }
end
- str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |width, gc|
- width + Reline::Unicode.get_mbchar_width(gc)
- }
end
private def key_delete(key)