reline 0.1.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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