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.
- 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