reline 0.5.10 → 0.6.0
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/lib/reline/config.rb +22 -26
- data/lib/reline/history.rb +3 -3
- data/lib/reline/io/ansi.rb +64 -111
- data/lib/reline/io/dumb.rb +16 -2
- data/lib/reline/io/windows.rb +77 -60
- data/lib/reline/io.rb +14 -0
- data/lib/reline/key_actor/base.rb +10 -4
- data/lib/reline/key_actor/emacs.rb +96 -96
- data/lib/reline/key_actor/vi_command.rb +182 -182
- data/lib/reline/key_actor/vi_insert.rb +137 -137
- data/lib/reline/key_stroke.rb +26 -16
- data/lib/reline/line_editor.rb +238 -404
- data/lib/reline/unicode.rb +140 -396
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +18 -18
- metadata +6 -4
- data/lib/reline/terminfo.rb +0 -158
data/lib/reline/unicode.rb
CHANGED
@@ -54,6 +54,22 @@ class Reline::Unicode
|
|
54
54
|
}.join
|
55
55
|
end
|
56
56
|
|
57
|
+
def self.safe_encode(str, encoding)
|
58
|
+
# Reline only supports utf-8 convertible string.
|
59
|
+
converted = str.encode(encoding, invalid: :replace, undef: :replace)
|
60
|
+
return converted if str.encoding == Encoding::UTF_8 || converted.encoding == Encoding::UTF_8 || converted.ascii_only?
|
61
|
+
|
62
|
+
# This code is essentially doing the same thing as
|
63
|
+
# `str.encode(utf8, **replace_options).encode(encoding, **replace_options)`
|
64
|
+
# but also avoids unneccesary irreversible encoding conversion.
|
65
|
+
converted.gsub(/\X/) do |c|
|
66
|
+
c.encode(Encoding::UTF_8)
|
67
|
+
c
|
68
|
+
rescue Encoding::UndefinedConversionError
|
69
|
+
'?'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
57
73
|
require 'reline/unicode/east_asian_width'
|
58
74
|
|
59
75
|
def self.get_mbchar_width(mbchar)
|
@@ -105,9 +121,14 @@ class Reline::Unicode
|
|
105
121
|
end
|
106
122
|
end
|
107
123
|
|
108
|
-
|
124
|
+
# This method is used by IRB
|
125
|
+
def self.split_by_width(str, max_width)
|
126
|
+
lines = split_line_by_width(str, max_width)
|
127
|
+
[lines, lines.size]
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.split_line_by_width(str, max_width, encoding = str.encoding, offset: 0)
|
109
131
|
lines = [String.new(encoding: encoding)]
|
110
|
-
height = 1
|
111
132
|
width = offset
|
112
133
|
rest = str.encode(Encoding::UTF_8)
|
113
134
|
in_zero_width = false
|
@@ -116,10 +137,8 @@ class Reline::Unicode
|
|
116
137
|
case
|
117
138
|
when non_printing_start
|
118
139
|
in_zero_width = true
|
119
|
-
lines.last << NON_PRINTING_START
|
120
140
|
when non_printing_end
|
121
141
|
in_zero_width = false
|
122
|
-
lines.last << NON_PRINTING_END
|
123
142
|
when csi
|
124
143
|
lines.last << csi
|
125
144
|
unless in_zero_width
|
@@ -131,15 +150,13 @@ class Reline::Unicode
|
|
131
150
|
end
|
132
151
|
when osc
|
133
152
|
lines.last << osc
|
134
|
-
seq << osc
|
153
|
+
seq << osc unless in_zero_width
|
135
154
|
when gc
|
136
155
|
unless in_zero_width
|
137
156
|
mbchar_width = get_mbchar_width(gc)
|
138
157
|
if (width += mbchar_width) > max_width
|
139
158
|
width = mbchar_width
|
140
|
-
lines << nil
|
141
159
|
lines << seq.dup
|
142
|
-
height += 1
|
143
160
|
end
|
144
161
|
end
|
145
162
|
lines.last << gc
|
@@ -147,11 +164,13 @@ class Reline::Unicode
|
|
147
164
|
end
|
148
165
|
# The cursor moves to next line in first
|
149
166
|
if width == max_width
|
150
|
-
lines << nil
|
151
167
|
lines << String.new(encoding: encoding)
|
152
|
-
height += 1
|
153
168
|
end
|
154
|
-
|
169
|
+
lines
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.strip_non_printing_start_end(prompt)
|
173
|
+
prompt.gsub(/\x01([^\x02]*)(?:\x02|\z)/) { $1 }
|
155
174
|
end
|
156
175
|
|
157
176
|
# Take a chunk of a String cut by width with escape sequences.
|
@@ -173,10 +192,8 @@ class Reline::Unicode
|
|
173
192
|
case
|
174
193
|
when non_printing_start
|
175
194
|
in_zero_width = true
|
176
|
-
chunk << NON_PRINTING_START
|
177
195
|
when non_printing_end
|
178
196
|
in_zero_width = false
|
179
|
-
chunk << NON_PRINTING_END
|
180
197
|
when csi
|
181
198
|
has_csi = true
|
182
199
|
chunk << csi
|
@@ -245,427 +262,154 @@ class Reline::Unicode
|
|
245
262
|
end
|
246
263
|
|
247
264
|
def self.em_forward_word(line, byte_pointer)
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
253
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
254
|
-
width += get_mbchar_width(mbchar)
|
255
|
-
byte_size += size
|
256
|
-
end
|
257
|
-
while line.bytesize > (byte_pointer + byte_size)
|
258
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
259
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
260
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
261
|
-
width += get_mbchar_width(mbchar)
|
262
|
-
byte_size += size
|
263
|
-
end
|
264
|
-
[byte_size, width]
|
265
|
+
gcs = line.byteslice(byte_pointer..).grapheme_clusters
|
266
|
+
nonwords = gcs.take_while { |c| !word_character?(c) }
|
267
|
+
words = gcs.drop(nonwords.size).take_while { |c| word_character?(c) }
|
268
|
+
nonwords.sum(&:bytesize) + words.sum(&:bytesize)
|
265
269
|
end
|
266
270
|
|
267
271
|
def self.em_forward_word_with_capitalization(line, byte_pointer)
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
273
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
274
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
275
|
-
new_str += mbchar
|
276
|
-
width += get_mbchar_width(mbchar)
|
277
|
-
byte_size += size
|
278
|
-
end
|
279
|
-
first = true
|
280
|
-
while line.bytesize > (byte_pointer + byte_size)
|
281
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
282
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
283
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
284
|
-
if first
|
285
|
-
new_str += mbchar.upcase
|
286
|
-
first = false
|
287
|
-
else
|
288
|
-
new_str += mbchar.downcase
|
289
|
-
end
|
290
|
-
width += get_mbchar_width(mbchar)
|
291
|
-
byte_size += size
|
292
|
-
end
|
293
|
-
[byte_size, width, new_str]
|
272
|
+
gcs = line.byteslice(byte_pointer..).grapheme_clusters
|
273
|
+
nonwords = gcs.take_while { |c| !word_character?(c) }
|
274
|
+
words = gcs.drop(nonwords.size).take_while { |c| word_character?(c) }
|
275
|
+
[nonwords.sum(&:bytesize) + words.sum(&:bytesize), nonwords.join + words.join.capitalize]
|
294
276
|
end
|
295
277
|
|
296
278
|
def self.em_backward_word(line, byte_pointer)
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
302
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
303
|
-
width += get_mbchar_width(mbchar)
|
304
|
-
byte_size += size
|
305
|
-
end
|
306
|
-
while 0 < (byte_pointer - byte_size)
|
307
|
-
size = get_prev_mbchar_size(line, byte_pointer - byte_size)
|
308
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
309
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
310
|
-
width += get_mbchar_width(mbchar)
|
311
|
-
byte_size += size
|
312
|
-
end
|
313
|
-
[byte_size, width]
|
279
|
+
gcs = line.byteslice(0, byte_pointer).grapheme_clusters.reverse
|
280
|
+
nonwords = gcs.take_while { |c| !word_character?(c) }
|
281
|
+
words = gcs.drop(nonwords.size).take_while { |c| word_character?(c) }
|
282
|
+
nonwords.sum(&:bytesize) + words.sum(&:bytesize)
|
314
283
|
end
|
315
284
|
|
316
285
|
def self.em_big_backward_word(line, byte_pointer)
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
322
|
-
break if mbchar =~ /\S/
|
323
|
-
width += get_mbchar_width(mbchar)
|
324
|
-
byte_size += size
|
325
|
-
end
|
326
|
-
while 0 < (byte_pointer - byte_size)
|
327
|
-
size = get_prev_mbchar_size(line, byte_pointer - byte_size)
|
328
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
329
|
-
break if mbchar =~ /\s/
|
330
|
-
width += get_mbchar_width(mbchar)
|
331
|
-
byte_size += size
|
332
|
-
end
|
333
|
-
[byte_size, width]
|
286
|
+
gcs = line.byteslice(0, byte_pointer).grapheme_clusters.reverse
|
287
|
+
spaces = gcs.take_while { |c| space_character?(c) }
|
288
|
+
nonspaces = gcs.drop(spaces.size).take_while { |c| !space_character?(c) }
|
289
|
+
spaces.sum(&:bytesize) + nonspaces.sum(&:bytesize)
|
334
290
|
end
|
335
291
|
|
336
292
|
def self.ed_transpose_words(line, byte_pointer)
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
while line.bytesize > (byte_pointer + byte_size)
|
358
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
359
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
360
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
361
|
-
byte_size += size
|
362
|
-
end
|
363
|
-
after_start = byte_pointer + byte_size
|
364
|
-
elsif mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
365
|
-
# ' aaa bb[cursor]b'
|
366
|
-
byte_size = 0
|
367
|
-
while 0 < (byte_pointer + byte_size)
|
368
|
-
size = get_prev_mbchar_size(line, byte_pointer + byte_size)
|
369
|
-
mbchar = line.byteslice(byte_pointer + byte_size - size, size)
|
370
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
371
|
-
byte_size -= size
|
372
|
-
end
|
373
|
-
right_word_start = byte_pointer + byte_size
|
374
|
-
byte_size = 0
|
375
|
-
while line.bytesize > (byte_pointer + byte_size)
|
376
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
377
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
378
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
379
|
-
byte_size += size
|
380
|
-
end
|
381
|
-
after_start = byte_pointer + byte_size
|
382
|
-
else
|
383
|
-
byte_size = 0
|
384
|
-
while (line.bytesize - 1) > (byte_pointer + byte_size)
|
385
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
386
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
387
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
388
|
-
byte_size += size
|
389
|
-
end
|
390
|
-
if (byte_pointer + byte_size) == (line.bytesize - 1)
|
391
|
-
# ' aaa bbb [cursor] '
|
392
|
-
after_start = line.bytesize
|
393
|
-
while 0 < (byte_pointer + byte_size)
|
394
|
-
size = get_prev_mbchar_size(line, byte_pointer + byte_size)
|
395
|
-
mbchar = line.byteslice(byte_pointer + byte_size - size, size)
|
396
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
397
|
-
byte_size -= size
|
398
|
-
end
|
399
|
-
while 0 < (byte_pointer + byte_size)
|
400
|
-
size = get_prev_mbchar_size(line, byte_pointer + byte_size)
|
401
|
-
mbchar = line.byteslice(byte_pointer + byte_size - size, size)
|
402
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
403
|
-
byte_size -= size
|
404
|
-
end
|
405
|
-
right_word_start = byte_pointer + byte_size
|
406
|
-
else
|
407
|
-
# ' aaa [cursor] bbb '
|
408
|
-
right_word_start = byte_pointer + byte_size
|
409
|
-
while line.bytesize > (byte_pointer + byte_size)
|
410
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
411
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
412
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
413
|
-
byte_size += size
|
414
|
-
end
|
415
|
-
after_start = byte_pointer + byte_size
|
416
|
-
end
|
417
|
-
end
|
418
|
-
byte_size = right_word_start - byte_pointer
|
419
|
-
while 0 < (byte_pointer + byte_size)
|
420
|
-
size = get_prev_mbchar_size(line, byte_pointer + byte_size)
|
421
|
-
mbchar = line.byteslice(byte_pointer + byte_size - size, size)
|
422
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
|
423
|
-
byte_size -= size
|
424
|
-
end
|
425
|
-
middle_start = byte_pointer + byte_size
|
426
|
-
byte_size = middle_start - byte_pointer
|
427
|
-
while 0 < (byte_pointer + byte_size)
|
428
|
-
size = get_prev_mbchar_size(line, byte_pointer + byte_size)
|
429
|
-
mbchar = line.byteslice(byte_pointer + byte_size - size, size)
|
430
|
-
break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
|
431
|
-
byte_size -= size
|
293
|
+
gcs = line.byteslice(0, byte_pointer).grapheme_clusters
|
294
|
+
pos = gcs.size
|
295
|
+
gcs += line.byteslice(byte_pointer..).grapheme_clusters
|
296
|
+
pos += 1 while pos < gcs.size && !word_character?(gcs[pos])
|
297
|
+
if pos == gcs.size # 'aaa bbb [cursor] '
|
298
|
+
pos -= 1 while pos > 0 && !word_character?(gcs[pos - 1])
|
299
|
+
second_word_end = gcs.size
|
300
|
+
else # 'aaa [cursor]bbb'
|
301
|
+
pos += 1 while pos < gcs.size && word_character?(gcs[pos])
|
302
|
+
second_word_end = pos
|
303
|
+
end
|
304
|
+
pos -= 1 while pos > 0 && word_character?(gcs[pos - 1])
|
305
|
+
second_word_start = pos
|
306
|
+
pos -= 1 while pos > 0 && !word_character?(gcs[pos - 1])
|
307
|
+
first_word_end = pos
|
308
|
+
pos -= 1 while pos > 0 && word_character?(gcs[pos - 1])
|
309
|
+
first_word_start = pos
|
310
|
+
|
311
|
+
[first_word_start, first_word_end, second_word_start, second_word_end].map do |idx|
|
312
|
+
gcs.take(idx).sum(&:bytesize)
|
432
313
|
end
|
433
|
-
left_word_start = byte_pointer + byte_size
|
434
|
-
[left_word_start, middle_start, right_word_start, after_start]
|
435
314
|
end
|
436
315
|
|
437
316
|
def self.vi_big_forward_word(line, byte_pointer)
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
443
|
-
break if mbchar =~ /\s/
|
444
|
-
width += get_mbchar_width(mbchar)
|
445
|
-
byte_size += size
|
446
|
-
end
|
447
|
-
while (line.bytesize - 1) > (byte_pointer + byte_size)
|
448
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
449
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
450
|
-
break if mbchar =~ /\S/
|
451
|
-
width += get_mbchar_width(mbchar)
|
452
|
-
byte_size += size
|
453
|
-
end
|
454
|
-
[byte_size, width]
|
317
|
+
gcs = line.byteslice(byte_pointer..).grapheme_clusters
|
318
|
+
nonspaces = gcs.take_while { |c| !space_character?(c) }
|
319
|
+
spaces = gcs.drop(nonspaces.size).take_while { |c| space_character?(c) }
|
320
|
+
nonspaces.sum(&:bytesize) + spaces.sum(&:bytesize)
|
455
321
|
end
|
456
322
|
|
457
323
|
def self.vi_big_forward_end_word(line, byte_pointer)
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
end
|
466
|
-
while (line.bytesize - 1) > (byte_pointer + byte_size)
|
467
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
468
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
469
|
-
break if mbchar =~ /\S/
|
470
|
-
width += get_mbchar_width(mbchar)
|
471
|
-
byte_size += size
|
472
|
-
end
|
473
|
-
prev_width = width
|
474
|
-
prev_byte_size = byte_size
|
475
|
-
while line.bytesize > (byte_pointer + byte_size)
|
476
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
477
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
478
|
-
break if mbchar =~ /\s/
|
479
|
-
prev_width = width
|
480
|
-
prev_byte_size = byte_size
|
481
|
-
width += get_mbchar_width(mbchar)
|
482
|
-
byte_size += size
|
483
|
-
end
|
484
|
-
[prev_byte_size, prev_width]
|
324
|
+
gcs = line.byteslice(byte_pointer..).grapheme_clusters
|
325
|
+
first = gcs.shift(1)
|
326
|
+
spaces = gcs.take_while { |c| space_character?(c) }
|
327
|
+
nonspaces = gcs.drop(spaces.size).take_while { |c| !space_character?(c) }
|
328
|
+
matched = spaces + nonspaces
|
329
|
+
matched.pop
|
330
|
+
first.sum(&:bytesize) + matched.sum(&:bytesize)
|
485
331
|
end
|
486
332
|
|
487
333
|
def self.vi_big_backward_word(line, byte_pointer)
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
493
|
-
break if mbchar =~ /\S/
|
494
|
-
width += get_mbchar_width(mbchar)
|
495
|
-
byte_size += size
|
496
|
-
end
|
497
|
-
while 0 < (byte_pointer - byte_size)
|
498
|
-
size = get_prev_mbchar_size(line, byte_pointer - byte_size)
|
499
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
500
|
-
break if mbchar =~ /\s/
|
501
|
-
width += get_mbchar_width(mbchar)
|
502
|
-
byte_size += size
|
503
|
-
end
|
504
|
-
[byte_size, width]
|
334
|
+
gcs = line.byteslice(0, byte_pointer).grapheme_clusters.reverse
|
335
|
+
spaces = gcs.take_while { |c| space_character?(c) }
|
336
|
+
nonspaces = gcs.drop(spaces.size).take_while { |c| !space_character?(c) }
|
337
|
+
spaces.sum(&:bytesize) + nonspaces.sum(&:bytesize)
|
505
338
|
end
|
506
339
|
|
507
340
|
def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false)
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
341
|
+
gcs = line.byteslice(byte_pointer..).grapheme_clusters
|
342
|
+
return 0 if gcs.empty?
|
343
|
+
|
344
|
+
c = gcs.first
|
345
|
+
matched =
|
346
|
+
if word_character?(c)
|
347
|
+
gcs.take_while { |c| word_character?(c) }
|
348
|
+
elsif space_character?(c)
|
349
|
+
gcs.take_while { |c| space_character?(c) }
|
515
350
|
else
|
516
|
-
|
517
|
-
end
|
518
|
-
width = get_mbchar_width(mbchar)
|
519
|
-
byte_size = size
|
520
|
-
else
|
521
|
-
return [0, 0]
|
522
|
-
end
|
523
|
-
while line.bytesize > (byte_pointer + byte_size)
|
524
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
525
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
526
|
-
case started_by
|
527
|
-
when :word
|
528
|
-
break if mbchar =~ /\W/
|
529
|
-
when :space
|
530
|
-
break if mbchar =~ /\S/
|
531
|
-
when :non_word_printable
|
532
|
-
break if mbchar =~ /\w|\s/
|
351
|
+
gcs.take_while { |c| !word_character?(c) && !space_character?(c) }
|
533
352
|
end
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
540
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
541
|
-
break if mbchar =~ /\S/
|
542
|
-
width += get_mbchar_width(mbchar)
|
543
|
-
byte_size += size
|
544
|
-
end
|
545
|
-
[byte_size, width]
|
353
|
+
|
354
|
+
return matched.sum(&:bytesize) if drop_terminate_spaces
|
355
|
+
|
356
|
+
spaces = gcs.drop(matched.size).take_while { |c| space_character?(c) }
|
357
|
+
matched.sum(&:bytesize) + spaces.sum(&:bytesize)
|
546
358
|
end
|
547
359
|
|
548
360
|
def self.vi_forward_end_word(line, byte_pointer)
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
if (line.bytesize - 1) > (byte_pointer + byte_size)
|
565
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
566
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
567
|
-
if mbchar =~ /\w/
|
568
|
-
second = :word
|
569
|
-
elsif mbchar =~ /\s/
|
570
|
-
second = :space
|
571
|
-
else
|
572
|
-
second = :non_word_printable
|
573
|
-
end
|
574
|
-
second_width = get_mbchar_width(mbchar)
|
575
|
-
second_byte_size = size
|
576
|
-
else
|
577
|
-
return [byte_size, width]
|
578
|
-
end
|
579
|
-
if second == :space
|
580
|
-
width += second_width
|
581
|
-
byte_size += second_byte_size
|
582
|
-
while (line.bytesize - 1) > (byte_pointer + byte_size)
|
583
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
584
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
585
|
-
if mbchar =~ /\S/
|
586
|
-
if mbchar =~ /\w/
|
587
|
-
started_by = :word
|
588
|
-
else
|
589
|
-
started_by = :non_word_printable
|
590
|
-
end
|
591
|
-
break
|
592
|
-
end
|
593
|
-
width += get_mbchar_width(mbchar)
|
594
|
-
byte_size += size
|
595
|
-
end
|
596
|
-
else
|
597
|
-
case [started_by, second]
|
598
|
-
when [:word, :non_word_printable], [:non_word_printable, :word]
|
599
|
-
started_by = second
|
600
|
-
else
|
601
|
-
width += second_width
|
602
|
-
byte_size += second_byte_size
|
603
|
-
started_by = second
|
604
|
-
end
|
605
|
-
end
|
606
|
-
prev_width = width
|
607
|
-
prev_byte_size = byte_size
|
608
|
-
while line.bytesize > (byte_pointer + byte_size)
|
609
|
-
size = get_next_mbchar_size(line, byte_pointer + byte_size)
|
610
|
-
mbchar = line.byteslice(byte_pointer + byte_size, size)
|
611
|
-
case started_by
|
612
|
-
when :word
|
613
|
-
break if mbchar =~ /\W/
|
614
|
-
when :non_word_printable
|
615
|
-
break if mbchar =~ /[\w\s]/
|
616
|
-
end
|
617
|
-
prev_width = width
|
618
|
-
prev_byte_size = byte_size
|
619
|
-
width += get_mbchar_width(mbchar)
|
620
|
-
byte_size += size
|
621
|
-
end
|
622
|
-
[prev_byte_size, prev_width]
|
361
|
+
gcs = line.byteslice(byte_pointer..).grapheme_clusters
|
362
|
+
return 0 if gcs.empty?
|
363
|
+
return gcs.first.bytesize if gcs.size == 1
|
364
|
+
|
365
|
+
start = gcs.shift
|
366
|
+
skips = [start]
|
367
|
+
if space_character?(start) || space_character?(gcs.first)
|
368
|
+
spaces = gcs.take_while { |c| space_character?(c) }
|
369
|
+
skips += spaces
|
370
|
+
gcs.shift(spaces.size)
|
371
|
+
end
|
372
|
+
start_with_word = word_character?(gcs.first)
|
373
|
+
matched = gcs.take_while { |c| start_with_word ? word_character?(c) : !word_character?(c) && !space_character?(c) }
|
374
|
+
matched.pop
|
375
|
+
skips.sum(&:bytesize) + matched.sum(&:bytesize)
|
623
376
|
end
|
624
377
|
|
625
378
|
def self.vi_backward_word(line, byte_pointer)
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
while 0 < (byte_pointer - byte_size)
|
643
|
-
size = get_prev_mbchar_size(line, byte_pointer - byte_size)
|
644
|
-
mbchar = line.byteslice(byte_pointer - byte_size - size, size)
|
645
|
-
case started_by
|
646
|
-
when :word
|
647
|
-
break if mbchar =~ /\W/
|
648
|
-
when :non_word_printable
|
649
|
-
break if mbchar =~ /[\w\s]/
|
379
|
+
gcs = line.byteslice(0, byte_pointer).grapheme_clusters.reverse
|
380
|
+
spaces = gcs.take_while { |c| space_character?(c) }
|
381
|
+
gcs.shift(spaces.size)
|
382
|
+
start_with_word = word_character?(gcs.first)
|
383
|
+
matched = gcs.take_while { |c| start_with_word ? word_character?(c) : !word_character?(c) && !space_character?(c) }
|
384
|
+
spaces.sum(&:bytesize) + matched.sum(&:bytesize)
|
385
|
+
end
|
386
|
+
|
387
|
+
def self.common_prefix(list, ignore_case: false)
|
388
|
+
return '' if list.empty?
|
389
|
+
|
390
|
+
common_prefix_gcs = list.first.grapheme_clusters
|
391
|
+
list.each do |item|
|
392
|
+
gcs = item.grapheme_clusters
|
393
|
+
common_prefix_gcs = common_prefix_gcs.take_while.with_index do |gc, i|
|
394
|
+
ignore_case ? gc.casecmp?(gcs[i]) : gc == gcs[i]
|
650
395
|
end
|
651
|
-
width += get_mbchar_width(mbchar)
|
652
|
-
byte_size += size
|
653
396
|
end
|
654
|
-
|
397
|
+
common_prefix_gcs.join
|
655
398
|
end
|
656
399
|
|
657
400
|
def self.vi_first_print(line)
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
401
|
+
gcs = line.grapheme_clusters
|
402
|
+
spaces = gcs.take_while { |c| space_character?(c) }
|
403
|
+
spaces.sum(&:bytesize)
|
404
|
+
end
|
405
|
+
|
406
|
+
def self.word_character?(s)
|
407
|
+
s.encode(Encoding::UTF_8).match?(/\p{Word}/) if s
|
408
|
+
rescue Encoding::UndefinedConversionError
|
409
|
+
false
|
410
|
+
end
|
411
|
+
|
412
|
+
def self.space_character?(s)
|
413
|
+
s.match?(/\s/) if s
|
670
414
|
end
|
671
415
|
end
|
data/lib/reline/version.rb
CHANGED