reline 0.5.12 → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  class Reline::Unicode::EastAsianWidth
2
2
  # This is based on EastAsianWidth.txt
3
- # UNICODE_VERSION = '15.1.0'
3
+ # UNICODE_VERSION = '16.0.0'
4
4
 
5
5
  CHUNK_LAST, CHUNK_WIDTH = [
6
6
  [0x1f, 2],
@@ -174,7 +174,7 @@ class Reline::Unicode::EastAsianWidth
174
174
  [0x82d, 0],
175
175
  [0x858, 1],
176
176
  [0x85b, 0],
177
- [0x897, 1],
177
+ [0x896, 1],
178
178
  [0x89f, 0],
179
179
  [0x8c9, 1],
180
180
  [0x8e1, 0],
@@ -646,6 +646,8 @@ class Reline::Unicode::EastAsianWidth
646
646
  [0x261c, -1],
647
647
  [0x261d, 1],
648
648
  [0x261e, -1],
649
+ [0x262f, 1],
650
+ [0x2637, 2],
649
651
  [0x263f, 1],
650
652
  [0x2640, -1],
651
653
  [0x2641, 1],
@@ -664,6 +666,8 @@ class Reline::Unicode::EastAsianWidth
664
666
  [0x266f, -1],
665
667
  [0x267e, 1],
666
668
  [0x267f, 2],
669
+ [0x2689, 1],
670
+ [0x268f, 2],
667
671
  [0x2692, 1],
668
672
  [0x2693, 2],
669
673
  [0x269d, 1],
@@ -753,14 +757,12 @@ class Reline::Unicode::EastAsianWidth
753
757
  [0x3130, 1],
754
758
  [0x318e, 2],
755
759
  [0x318f, 1],
756
- [0x31e3, 2],
760
+ [0x31e5, 2],
757
761
  [0x31ee, 1],
758
762
  [0x321e, 2],
759
763
  [0x321f, 1],
760
764
  [0x3247, 2],
761
765
  [0x324f, -1],
762
- [0x4dbf, 2],
763
- [0x4dff, 1],
764
766
  [0xa48c, 2],
765
767
  [0xa48f, 1],
766
768
  [0xa4c6, 2],
@@ -879,9 +881,11 @@ class Reline::Unicode::EastAsianWidth
879
881
  [0x10ae6, 0],
880
882
  [0x10d23, 1],
881
883
  [0x10d27, 0],
884
+ [0x10d68, 1],
885
+ [0x10d6d, 0],
882
886
  [0x10eaa, 1],
883
887
  [0x10eac, 0],
884
- [0x10efc, 1],
888
+ [0x10efb, 1],
885
889
  [0x10eff, 0],
886
890
  [0x10f45, 1],
887
891
  [0x10f50, 0],
@@ -943,6 +947,16 @@ class Reline::Unicode::EastAsianWidth
943
947
  [0x1136c, 0],
944
948
  [0x1136f, 1],
945
949
  [0x11374, 0],
950
+ [0x113ba, 1],
951
+ [0x113c0, 0],
952
+ [0x113cd, 1],
953
+ [0x113ce, 0],
954
+ [0x113cf, 1],
955
+ [0x113d0, 0],
956
+ [0x113d1, 1],
957
+ [0x113d2, 0],
958
+ [0x113e0, 1],
959
+ [0x113e2, 0],
946
960
  [0x11437, 1],
947
961
  [0x1143f, 0],
948
962
  [0x11441, 1],
@@ -982,6 +996,8 @@ class Reline::Unicode::EastAsianWidth
982
996
  [0x116b6, 1],
983
997
  [0x116b7, 0],
984
998
  [0x1171c, 1],
999
+ [0x1171d, 0],
1000
+ [0x1171e, 1],
985
1001
  [0x1171f, 0],
986
1002
  [0x11721, 1],
987
1003
  [0x11725, 0],
@@ -1059,10 +1075,16 @@ class Reline::Unicode::EastAsianWidth
1059
1075
  [0x11f40, 0],
1060
1076
  [0x11f41, 1],
1061
1077
  [0x11f42, 0],
1078
+ [0x11f59, 1],
1079
+ [0x11f5a, 0],
1062
1080
  [0x1343f, 1],
1063
1081
  [0x13440, 0],
1064
1082
  [0x13446, 1],
1065
1083
  [0x13455, 0],
1084
+ [0x1611d, 1],
1085
+ [0x16129, 0],
1086
+ [0x1612c, 1],
1087
+ [0x1612f, 0],
1066
1088
  [0x16aef, 1],
1067
1089
  [0x16af4, 0],
1068
1090
  [0x16b2f, 1],
@@ -1080,7 +1102,7 @@ class Reline::Unicode::EastAsianWidth
1080
1102
  [0x187f7, 2],
1081
1103
  [0x187ff, 1],
1082
1104
  [0x18cd5, 2],
1083
- [0x18cff, 1],
1105
+ [0x18cfe, 1],
1084
1106
  [0x18d08, 2],
1085
1107
  [0x1afef, 1],
1086
1108
  [0x1aff3, 2],
@@ -1116,6 +1138,10 @@ class Reline::Unicode::EastAsianWidth
1116
1138
  [0x1d1ad, 0],
1117
1139
  [0x1d241, 1],
1118
1140
  [0x1d244, 0],
1141
+ [0x1d2ff, 1],
1142
+ [0x1d356, 2],
1143
+ [0x1d35f, 1],
1144
+ [0x1d376, 2],
1119
1145
  [0x1d9ff, 1],
1120
1146
  [0x1da36, 0],
1121
1147
  [0x1da3a, 1],
@@ -1148,6 +1174,8 @@ class Reline::Unicode::EastAsianWidth
1148
1174
  [0x1e2ef, 0],
1149
1175
  [0x1e4eb, 1],
1150
1176
  [0x1e4ef, 0],
1177
+ [0x1e5ed, 1],
1178
+ [0x1e5ef, 0],
1151
1179
  [0x1e8cf, 1],
1152
1180
  [0x1e8d6, 0],
1153
1181
  [0x1e943, 1],
@@ -1241,15 +1269,13 @@ class Reline::Unicode::EastAsianWidth
1241
1269
  [0x1fa6f, 1],
1242
1270
  [0x1fa7c, 2],
1243
1271
  [0x1fa7f, 1],
1244
- [0x1fa88, 2],
1245
- [0x1fa8f, 1],
1246
- [0x1fabd, 2],
1247
- [0x1fabe, 1],
1248
- [0x1fac5, 2],
1272
+ [0x1fa89, 2],
1273
+ [0x1fa8e, 1],
1274
+ [0x1fac6, 2],
1249
1275
  [0x1facd, 1],
1250
- [0x1fadb, 2],
1251
- [0x1fadf, 1],
1252
- [0x1fae8, 2],
1276
+ [0x1fadc, 2],
1277
+ [0x1fade, 1],
1278
+ [0x1fae9, 2],
1253
1279
  [0x1faef, 1],
1254
1280
  [0x1faf8, 2],
1255
1281
  [0x1ffff, 1],
@@ -28,12 +28,12 @@ class Reline::Unicode
28
28
  0x19 => '^Y',
29
29
  0x1A => '^Z', # C-z
30
30
  0x1B => '^[', # C-[ C-3
31
+ 0x1C => '^\\', # C-\
31
32
  0x1D => '^]', # C-]
32
33
  0x1E => '^^', # C-~ C-6
33
34
  0x1F => '^_', # C-_ C-7
34
35
  0x7F => '^?', # C-? C-8
35
36
  }
36
- EscapedChars = EscapedPairs.keys.map(&:chr)
37
37
 
38
38
  NON_PRINTING_START = "\1"
39
39
  NON_PRINTING_END = "\2"
@@ -61,7 +61,7 @@ class Reline::Unicode
61
61
 
62
62
  # This code is essentially doing the same thing as
63
63
  # `str.encode(utf8, **replace_options).encode(encoding, **replace_options)`
64
- # but also avoids unneccesary irreversible encoding conversion.
64
+ # but also avoids unnecessary irreversible encoding conversion.
65
65
  converted.gsub(/\X/) do |c|
66
66
  c.encode(Encoding::UTF_8)
67
67
  c
@@ -262,375 +262,126 @@ class Reline::Unicode
262
262
  end
263
263
 
264
264
  def self.em_forward_word(line, byte_pointer)
265
- byte_size = 0
266
- while line.bytesize > (byte_pointer + byte_size)
267
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
268
- mbchar = line.byteslice(byte_pointer + byte_size, size)
269
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
270
- byte_size += size
271
- end
272
- while line.bytesize > (byte_pointer + byte_size)
273
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
274
- mbchar = line.byteslice(byte_pointer + byte_size, size)
275
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
276
- byte_size += size
277
- end
278
- byte_size
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)
279
269
  end
280
270
 
281
271
  def self.em_forward_word_with_capitalization(line, byte_pointer)
282
- byte_size = 0
283
- new_str = String.new
284
- while line.bytesize > (byte_pointer + byte_size)
285
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
286
- mbchar = line.byteslice(byte_pointer + byte_size, size)
287
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
288
- new_str += mbchar
289
- byte_size += size
290
- end
291
- first = true
292
- while line.bytesize > (byte_pointer + byte_size)
293
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
294
- mbchar = line.byteslice(byte_pointer + byte_size, size)
295
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
296
- if first
297
- new_str += mbchar.upcase
298
- first = false
299
- else
300
- new_str += mbchar.downcase
301
- end
302
- byte_size += size
303
- end
304
- [byte_size, 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]
305
276
  end
306
277
 
307
278
  def self.em_backward_word(line, byte_pointer)
308
- byte_size = 0
309
- while 0 < (byte_pointer - byte_size)
310
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
311
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
312
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
313
- byte_size += size
314
- end
315
- while 0 < (byte_pointer - byte_size)
316
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
317
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
318
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
319
- byte_size += size
320
- end
321
- byte_size
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)
322
283
  end
323
284
 
324
285
  def self.em_big_backward_word(line, byte_pointer)
325
- byte_size = 0
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
- byte_size += size
331
- end
332
- while 0 < (byte_pointer - byte_size)
333
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
334
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
335
- break if mbchar =~ /\s/
336
- byte_size += size
337
- end
338
- byte_size
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)
339
290
  end
340
291
 
341
292
  def self.ed_transpose_words(line, byte_pointer)
342
- right_word_start = nil
343
- size = get_next_mbchar_size(line, byte_pointer)
344
- mbchar = line.byteslice(byte_pointer, size)
345
- if size.zero?
346
- # ' aaa bbb [cursor]'
347
- byte_size = 0
348
- while 0 < (byte_pointer + byte_size)
349
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
350
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
351
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
352
- byte_size -= size
353
- end
354
- while 0 < (byte_pointer + byte_size)
355
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
356
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
357
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
358
- byte_size -= size
359
- end
360
- right_word_start = byte_pointer + byte_size
361
- byte_size = 0
362
- while line.bytesize > (byte_pointer + byte_size)
363
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
364
- mbchar = line.byteslice(byte_pointer + byte_size, size)
365
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
366
- byte_size += size
367
- end
368
- after_start = byte_pointer + byte_size
369
- elsif mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
370
- # ' aaa bb[cursor]b'
371
- byte_size = 0
372
- while 0 < (byte_pointer + byte_size)
373
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
374
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
375
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
376
- byte_size -= size
377
- end
378
- right_word_start = byte_pointer + byte_size
379
- byte_size = 0
380
- while line.bytesize > (byte_pointer + byte_size)
381
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
382
- mbchar = line.byteslice(byte_pointer + byte_size, size)
383
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
384
- byte_size += size
385
- end
386
- after_start = byte_pointer + byte_size
387
- else
388
- byte_size = 0
389
- while (line.bytesize - 1) > (byte_pointer + byte_size)
390
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
391
- mbchar = line.byteslice(byte_pointer + byte_size, size)
392
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
393
- byte_size += size
394
- end
395
- if (byte_pointer + byte_size) == (line.bytesize - 1)
396
- # ' aaa bbb [cursor] '
397
- after_start = line.bytesize
398
- while 0 < (byte_pointer + byte_size)
399
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
400
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
401
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
402
- byte_size -= size
403
- end
404
- while 0 < (byte_pointer + byte_size)
405
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
406
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
407
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
408
- byte_size -= size
409
- end
410
- right_word_start = byte_pointer + byte_size
411
- else
412
- # ' aaa [cursor] bbb '
413
- right_word_start = byte_pointer + byte_size
414
- while line.bytesize > (byte_pointer + byte_size)
415
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
416
- mbchar = line.byteslice(byte_pointer + byte_size, size)
417
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
418
- byte_size += size
419
- end
420
- after_start = byte_pointer + byte_size
421
- end
422
- end
423
- byte_size = right_word_start - byte_pointer
424
- while 0 < (byte_pointer + byte_size)
425
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
426
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
427
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
428
- byte_size -= size
429
- end
430
- middle_start = byte_pointer + byte_size
431
- byte_size = middle_start - byte_pointer
432
- while 0 < (byte_pointer + byte_size)
433
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
434
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
435
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
436
- 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)
437
313
  end
438
- left_word_start = byte_pointer + byte_size
439
- [left_word_start, middle_start, right_word_start, after_start]
440
314
  end
441
315
 
442
316
  def self.vi_big_forward_word(line, byte_pointer)
443
- byte_size = 0
444
- while (line.bytesize - 1) > (byte_pointer + byte_size)
445
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
446
- mbchar = line.byteslice(byte_pointer + byte_size, size)
447
- break if mbchar =~ /\s/
448
- byte_size += size
449
- end
450
- while (line.bytesize - 1) > (byte_pointer + byte_size)
451
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
452
- mbchar = line.byteslice(byte_pointer + byte_size, size)
453
- break if mbchar =~ /\S/
454
- byte_size += size
455
- end
456
- byte_size
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)
457
321
  end
458
322
 
459
323
  def self.vi_big_forward_end_word(line, byte_pointer)
460
- if (line.bytesize - 1) > byte_pointer
461
- size = get_next_mbchar_size(line, byte_pointer)
462
- byte_size = size
463
- else
464
- return 0
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
- byte_size += size
471
- end
472
- prev_byte_size = byte_size
473
- while line.bytesize > (byte_pointer + byte_size)
474
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
475
- mbchar = line.byteslice(byte_pointer + byte_size, size)
476
- break if mbchar =~ /\s/
477
- prev_byte_size = byte_size
478
- byte_size += size
479
- end
480
- prev_byte_size
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)
481
331
  end
482
332
 
483
333
  def self.vi_big_backward_word(line, byte_pointer)
484
- byte_size = 0
485
- while 0 < (byte_pointer - byte_size)
486
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
487
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
488
- break if mbchar =~ /\S/
489
- byte_size += size
490
- end
491
- while 0 < (byte_pointer - byte_size)
492
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
493
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
494
- break if mbchar =~ /\s/
495
- byte_size += size
496
- end
497
- byte_size
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)
498
338
  end
499
339
 
500
340
  def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false)
501
- if line.bytesize > byte_pointer
502
- size = get_next_mbchar_size(line, byte_pointer)
503
- mbchar = line.byteslice(byte_pointer, size)
504
- if mbchar =~ /\w/
505
- started_by = :word
506
- elsif mbchar =~ /\s/
507
- started_by = :space
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) }
508
350
  else
509
- started_by = :non_word_printable
510
- end
511
- byte_size = size
512
- else
513
- return 0
514
- end
515
- while line.bytesize > (byte_pointer + byte_size)
516
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
517
- mbchar = line.byteslice(byte_pointer + byte_size, size)
518
- case started_by
519
- when :word
520
- break if mbchar =~ /\W/
521
- when :space
522
- break if mbchar =~ /\S/
523
- when :non_word_printable
524
- break if mbchar =~ /\w|\s/
351
+ gcs.take_while { |c| !word_character?(c) && !space_character?(c) }
525
352
  end
526
- byte_size += size
527
- end
528
- return byte_size if drop_terminate_spaces
529
- while line.bytesize > (byte_pointer + byte_size)
530
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
531
- mbchar = line.byteslice(byte_pointer + byte_size, size)
532
- break if mbchar =~ /\S/
533
- byte_size += size
534
- end
535
- byte_size
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)
536
358
  end
537
359
 
538
360
  def self.vi_forward_end_word(line, byte_pointer)
539
- if (line.bytesize - 1) > byte_pointer
540
- size = get_next_mbchar_size(line, byte_pointer)
541
- mbchar = line.byteslice(byte_pointer, size)
542
- if mbchar =~ /\w/
543
- started_by = :word
544
- elsif mbchar =~ /\s/
545
- started_by = :space
546
- else
547
- started_by = :non_word_printable
548
- end
549
- byte_size = size
550
- else
551
- return 0
552
- end
553
- if (line.bytesize - 1) > (byte_pointer + byte_size)
554
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
555
- mbchar = line.byteslice(byte_pointer + byte_size, size)
556
- if mbchar =~ /\w/
557
- second = :word
558
- elsif mbchar =~ /\s/
559
- second = :space
560
- else
561
- second = :non_word_printable
562
- end
563
- second_byte_size = size
564
- else
565
- return byte_size
566
- end
567
- if second == :space
568
- byte_size += second_byte_size
569
- while (line.bytesize - 1) > (byte_pointer + byte_size)
570
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
571
- mbchar = line.byteslice(byte_pointer + byte_size, size)
572
- if mbchar =~ /\S/
573
- if mbchar =~ /\w/
574
- started_by = :word
575
- else
576
- started_by = :non_word_printable
577
- end
578
- break
579
- end
580
- byte_size += size
581
- end
582
- else
583
- case [started_by, second]
584
- when [:word, :non_word_printable], [:non_word_printable, :word]
585
- started_by = second
586
- else
587
- byte_size += second_byte_size
588
- started_by = second
589
- end
590
- end
591
- prev_byte_size = byte_size
592
- while line.bytesize > (byte_pointer + byte_size)
593
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
594
- mbchar = line.byteslice(byte_pointer + byte_size, size)
595
- case started_by
596
- when :word
597
- break if mbchar =~ /\W/
598
- when :non_word_printable
599
- break if mbchar =~ /[\w\s]/
600
- end
601
- prev_byte_size = byte_size
602
- byte_size += size
603
- end
604
- prev_byte_size
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)
605
376
  end
606
377
 
607
378
  def self.vi_backward_word(line, byte_pointer)
608
- byte_size = 0
609
- while 0 < (byte_pointer - byte_size)
610
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
611
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
612
- if mbchar =~ /\S/
613
- if mbchar =~ /\w/
614
- started_by = :word
615
- else
616
- started_by = :non_word_printable
617
- end
618
- break
619
- end
620
- byte_size += size
621
- end
622
- while 0 < (byte_pointer - byte_size)
623
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
624
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
625
- case started_by
626
- when :word
627
- break if mbchar =~ /\W/
628
- when :non_word_printable
629
- break if mbchar =~ /[\w\s]/
630
- end
631
- byte_size += size
632
- end
633
- byte_size
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)
634
385
  end
635
386
 
636
387
  def self.common_prefix(list, ignore_case: false)
@@ -647,15 +398,18 @@ class Reline::Unicode
647
398
  end
648
399
 
649
400
  def self.vi_first_print(line)
650
- byte_size = 0
651
- while (line.bytesize - 1) > byte_size
652
- size = get_next_mbchar_size(line, byte_size)
653
- mbchar = line.byteslice(byte_size, size)
654
- if mbchar =~ /\S/
655
- break
656
- end
657
- byte_size += size
658
- end
659
- byte_size
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
660
414
  end
661
415
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.12'
2
+ VERSION = '0.6.1'
3
3
  end