reline 0.1.5 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,11 +35,16 @@ class Reline::Unicode
35
35
  }
36
36
  EscapedChars = EscapedPairs.keys.map(&:chr)
37
37
 
38
- CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
39
- OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
40
38
  NON_PRINTING_START = "\1"
41
39
  NON_PRINTING_END = "\2"
42
- WIDTH_SCANNER = /\G(?:#{NON_PRINTING_START}|#{NON_PRINTING_END}|#{CSI_REGEXP}|#{OSC_REGEXP}|\X)/
40
+ CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/
41
+ OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/
42
+ WIDTH_SCANNER = /\G(?:(#{NON_PRINTING_START})|(#{NON_PRINTING_END})|(#{CSI_REGEXP})|(#{OSC_REGEXP})|(\X))/o
43
+ NON_PRINTING_START_INDEX = 0
44
+ NON_PRINTING_END_INDEX = 1
45
+ CSI_REGEXP_INDEX = 2
46
+ OSC_REGEXP_INDEX = 3
47
+ GRAPHEME_CLUSTER_INDEX = 4
43
48
 
44
49
  def self.get_mbchar_byte_size_by_first_char(c)
45
50
  # Checks UTF-8 character byte size
@@ -72,20 +77,51 @@ class Reline::Unicode
72
77
  }.join
73
78
  end
74
79
 
80
+ require 'reline/unicode/east_asian_width'
81
+
82
+ HalfwidthDakutenHandakuten = /[\u{FF9E}\u{FF9F}]/
83
+
84
+ MBCharWidthRE = /
85
+ (?<width_2_1>
86
+ [#{ EscapedChars.map {|c| "\\x%02x" % c.ord }.join }] (?# ^ + char, such as ^M, ^H, ^[, ...)
87
+ )
88
+ | (?<width_3>^\u{2E3B}) (?# THREE-EM DASH)
89
+ | (?<width_0>^\p{M})
90
+ | (?<width_2_2>
91
+ #{ EastAsianWidth::TYPE_F }
92
+ | #{ EastAsianWidth::TYPE_W }
93
+ )
94
+ | (?<width_1>
95
+ #{ EastAsianWidth::TYPE_H }
96
+ | #{ EastAsianWidth::TYPE_NA }
97
+ | #{ EastAsianWidth::TYPE_N }
98
+ )(?!#{ HalfwidthDakutenHandakuten })
99
+ | (?<width_2_3>
100
+ (?: #{ EastAsianWidth::TYPE_H }
101
+ | #{ EastAsianWidth::TYPE_NA }
102
+ | #{ EastAsianWidth::TYPE_N })
103
+ #{ HalfwidthDakutenHandakuten }
104
+ )
105
+ | (?<ambiguous_width>
106
+ #{EastAsianWidth::TYPE_A}
107
+ )
108
+ /x
109
+
75
110
  def self.get_mbchar_width(mbchar)
76
- case mbchar.encode(Encoding::UTF_8)
77
- when *EscapedChars # ^ + char, such as ^M, ^H, ^[, ...
78
- 2
79
- when /^\u{2E3B}/ # THREE-EM DASH
80
- 3
81
- when /^\p{M}/
82
- 0
83
- when EastAsianWidth::TYPE_A
84
- Reline.ambiguous_width
85
- when EastAsianWidth::TYPE_F, EastAsianWidth::TYPE_W
86
- 2
87
- when EastAsianWidth::TYPE_H, EastAsianWidth::TYPE_NA, EastAsianWidth::TYPE_N
88
- 1
111
+ ord = mbchar.ord
112
+ if (0x00 <= ord and ord <= 0x1F) # in EscapedPairs
113
+ return 2
114
+ elsif (0x20 <= ord and ord <= 0x7E) # printable ASCII chars
115
+ return 1
116
+ end
117
+ m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
118
+ case
119
+ when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
120
+ when m[:width_2_1], m[:width_2_2], m[:width_2_3] then 2
121
+ when m[:width_3] then 3
122
+ when m[:width_0] then 0
123
+ when m[:width_1] then 1
124
+ when m[:ambiguous_width] then Reline.ambiguous_width
89
125
  else
90
126
  nil
91
127
  end
@@ -97,13 +133,14 @@ class Reline::Unicode
97
133
  rest = str.encode(Encoding::UTF_8)
98
134
  in_zero_width = false
99
135
  rest.scan(WIDTH_SCANNER) do |gc|
100
- case gc
101
- when NON_PRINTING_START
136
+ case
137
+ when gc[NON_PRINTING_START_INDEX]
102
138
  in_zero_width = true
103
- when NON_PRINTING_END
139
+ when gc[NON_PRINTING_END_INDEX]
104
140
  in_zero_width = false
105
- when CSI_REGEXP, OSC_REGEXP
106
- else
141
+ when gc[CSI_REGEXP_INDEX], gc[OSC_REGEXP_INDEX]
142
+ when gc[GRAPHEME_CLUSTER_INDEX]
143
+ gc = gc[GRAPHEME_CLUSTER_INDEX]
107
144
  unless in_zero_width
108
145
  width += get_mbchar_width(gc)
109
146
  end
@@ -124,14 +161,17 @@ class Reline::Unicode
124
161
  rest = str.encode(Encoding::UTF_8)
125
162
  in_zero_width = false
126
163
  rest.scan(WIDTH_SCANNER) do |gc|
127
- case gc
128
- when NON_PRINTING_START
164
+ case
165
+ when gc[NON_PRINTING_START_INDEX]
129
166
  in_zero_width = true
130
- when NON_PRINTING_END
167
+ when gc[NON_PRINTING_END_INDEX]
131
168
  in_zero_width = false
132
- when CSI_REGEXP, OSC_REGEXP
133
- lines.last << gc
134
- else
169
+ when gc[CSI_REGEXP_INDEX]
170
+ lines.last << gc[CSI_REGEXP_INDEX]
171
+ when gc[OSC_REGEXP_INDEX]
172
+ lines.last << gc[OSC_REGEXP_INDEX]
173
+ when gc[GRAPHEME_CLUSTER_INDEX]
174
+ gc = gc[GRAPHEME_CLUSTER_INDEX]
135
175
  unless in_zero_width
136
176
  mbchar_width = get_mbchar_width(gc)
137
177
  if (width += mbchar_width) > max_width
@@ -153,6 +193,37 @@ class Reline::Unicode
153
193
  [lines, height]
154
194
  end
155
195
 
196
+ # Take a chunk of a String cut by width with escape sequences.
197
+ def self.take_range(str, start_col, max_width, encoding = str.encoding)
198
+ chunk = String.new(encoding: encoding)
199
+ total_width = 0
200
+ rest = str.encode(Encoding::UTF_8)
201
+ in_zero_width = false
202
+ rest.scan(WIDTH_SCANNER) do |gc|
203
+ case
204
+ when gc[NON_PRINTING_START_INDEX]
205
+ in_zero_width = true
206
+ when gc[NON_PRINTING_END_INDEX]
207
+ in_zero_width = false
208
+ when gc[CSI_REGEXP_INDEX]
209
+ chunk << gc[CSI_REGEXP_INDEX]
210
+ when gc[OSC_REGEXP_INDEX]
211
+ chunk << gc[OSC_REGEXP_INDEX]
212
+ when gc[GRAPHEME_CLUSTER_INDEX]
213
+ gc = gc[GRAPHEME_CLUSTER_INDEX]
214
+ if in_zero_width
215
+ chunk << gc
216
+ else
217
+ mbchar_width = get_mbchar_width(gc)
218
+ total_width += mbchar_width
219
+ break if (start_col + max_width) < total_width
220
+ chunk << gc if start_col < total_width
221
+ end
222
+ end
223
+ end
224
+ chunk
225
+ end
226
+
156
227
  def self.get_next_mbchar_size(line, byte_pointer)
157
228
  grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
158
229
  grapheme ? grapheme.bytesize : 0
@@ -427,8 +498,8 @@ class Reline::Unicode
427
498
  [byte_size, width]
428
499
  end
429
500
 
430
- def self.vi_forward_word(line, byte_pointer)
431
- if (line.bytesize - 1) > byte_pointer
501
+ def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false)
502
+ if line.bytesize > byte_pointer
432
503
  size = get_next_mbchar_size(line, byte_pointer)
433
504
  mbchar = line.byteslice(byte_pointer, size)
434
505
  if mbchar =~ /\w/
@@ -443,7 +514,7 @@ class Reline::Unicode
443
514
  else
444
515
  return [0, 0]
445
516
  end
446
- while (line.bytesize - 1) > (byte_pointer + byte_size)
517
+ while line.bytesize > (byte_pointer + byte_size)
447
518
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
448
519
  mbchar = line.byteslice(byte_pointer + byte_size, size)
449
520
  case started_by
@@ -457,7 +528,8 @@ class Reline::Unicode
457
528
  width += get_mbchar_width(mbchar)
458
529
  byte_size += size
459
530
  end
460
- while (line.bytesize - 1) > (byte_pointer + byte_size)
531
+ return [byte_size, width] if drop_terminate_spaces
532
+ while line.bytesize > (byte_pointer + byte_size)
461
533
  size = get_next_mbchar_size(line, byte_pointer + byte_size)
462
534
  mbchar = line.byteslice(byte_pointer + byte_size, size)
463
535
  break if mbchar =~ /\S/
@@ -591,5 +663,3 @@ class Reline::Unicode
591
663
  [byte_size, width]
592
664
  end
593
665
  end
594
-
595
- require 'reline/unicode/east_asian_width'
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.1.5'
2
+ VERSION = '0.3.1'
3
3
  end