reline 0.1.5 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -0
- data/lib/reline/ansi.rb +206 -58
- data/lib/reline/config.rb +71 -21
- data/lib/reline/general_io.rb +31 -3
- data/lib/reline/key_actor/base.rb +12 -0
- data/lib/reline/key_actor/emacs.rb +2 -2
- data/lib/reline/key_actor/vi_command.rb +2 -2
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/kill_ring.rb +12 -0
- data/lib/reline/line_editor.rb +1332 -291
- data/lib/reline/sibori.rb +170 -0
- data/lib/reline/terminfo.rb +171 -0
- data/lib/reline/unicode/east_asian_width.rb +1149 -1130
- data/lib/reline/unicode.rb +103 -33
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +324 -109
- data/lib/reline.rb +184 -39
- data/license_of_rb-readline +25 -0
- metadata +6 -45
data/lib/reline/unicode.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
77
|
-
|
78
|
-
2
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
when
|
86
|
-
|
87
|
-
when
|
88
|
-
|
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
|
101
|
-
when
|
136
|
+
case
|
137
|
+
when gc[NON_PRINTING_START_INDEX]
|
102
138
|
in_zero_width = true
|
103
|
-
when
|
139
|
+
when gc[NON_PRINTING_END_INDEX]
|
104
140
|
in_zero_width = false
|
105
|
-
when
|
106
|
-
|
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
|
128
|
-
when
|
164
|
+
case
|
165
|
+
when gc[NON_PRINTING_START_INDEX]
|
129
166
|
in_zero_width = true
|
130
|
-
when
|
167
|
+
when gc[NON_PRINTING_END_INDEX]
|
131
168
|
in_zero_width = false
|
132
|
-
when
|
133
|
-
lines.last << gc
|
134
|
-
|
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
|
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
|
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
|
-
|
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'
|
data/lib/reline/version.rb
CHANGED