reline 0.5.11 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -121,9 +121,14 @@ class Reline::Unicode
121
121
  end
122
122
  end
123
123
 
124
- def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0)
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)
125
131
  lines = [String.new(encoding: encoding)]
126
- height = 1
127
132
  width = offset
128
133
  rest = str.encode(Encoding::UTF_8)
129
134
  in_zero_width = false
@@ -151,9 +156,7 @@ class Reline::Unicode
151
156
  mbchar_width = get_mbchar_width(gc)
152
157
  if (width += mbchar_width) > max_width
153
158
  width = mbchar_width
154
- lines << nil
155
159
  lines << seq.dup
156
- height += 1
157
160
  end
158
161
  end
159
162
  lines.last << gc
@@ -161,11 +164,9 @@ class Reline::Unicode
161
164
  end
162
165
  # The cursor moves to next line in first
163
166
  if width == max_width
164
- lines << nil
165
167
  lines << String.new(encoding: encoding)
166
- height += 1
167
168
  end
168
- [lines, height]
169
+ lines
169
170
  end
170
171
 
171
172
  def self.strip_non_printing_start_end(prompt)
@@ -261,427 +262,154 @@ class Reline::Unicode
261
262
  end
262
263
 
263
264
  def self.em_forward_word(line, byte_pointer)
264
- width = 0
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
- width += get_mbchar_width(mbchar)
271
- byte_size += size
272
- end
273
- while line.bytesize > (byte_pointer + byte_size)
274
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
275
- mbchar = line.byteslice(byte_pointer + byte_size, size)
276
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
277
- width += get_mbchar_width(mbchar)
278
- byte_size += size
279
- end
280
- [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)
281
269
  end
282
270
 
283
271
  def self.em_forward_word_with_capitalization(line, byte_pointer)
284
- width = 0
285
- byte_size = 0
286
- new_str = String.new
287
- while line.bytesize > (byte_pointer + byte_size)
288
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
289
- mbchar = line.byteslice(byte_pointer + byte_size, size)
290
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
291
- new_str += mbchar
292
- width += get_mbchar_width(mbchar)
293
- byte_size += size
294
- end
295
- first = true
296
- while line.bytesize > (byte_pointer + byte_size)
297
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
298
- mbchar = line.byteslice(byte_pointer + byte_size, size)
299
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
300
- if first
301
- new_str += mbchar.upcase
302
- first = false
303
- else
304
- new_str += mbchar.downcase
305
- end
306
- width += get_mbchar_width(mbchar)
307
- byte_size += size
308
- end
309
- [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]
310
276
  end
311
277
 
312
278
  def self.em_backward_word(line, byte_pointer)
313
- width = 0
314
- byte_size = 0
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
- width += get_mbchar_width(mbchar)
320
- byte_size += size
321
- end
322
- while 0 < (byte_pointer - byte_size)
323
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
324
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
325
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
326
- width += get_mbchar_width(mbchar)
327
- byte_size += size
328
- end
329
- [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)
330
283
  end
331
284
 
332
285
  def self.em_big_backward_word(line, byte_pointer)
333
- width = 0
334
- byte_size = 0
335
- while 0 < (byte_pointer - byte_size)
336
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
337
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
338
- break if mbchar =~ /\S/
339
- width += get_mbchar_width(mbchar)
340
- byte_size += size
341
- end
342
- while 0 < (byte_pointer - byte_size)
343
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
344
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
345
- break if mbchar =~ /\s/
346
- width += get_mbchar_width(mbchar)
347
- byte_size += size
348
- end
349
- [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)
350
290
  end
351
291
 
352
292
  def self.ed_transpose_words(line, byte_pointer)
353
- right_word_start = nil
354
- size = get_next_mbchar_size(line, byte_pointer)
355
- mbchar = line.byteslice(byte_pointer, size)
356
- if size.zero?
357
- # ' aaa bbb [cursor]'
358
- byte_size = 0
359
- while 0 < (byte_pointer + byte_size)
360
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
361
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
362
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
363
- byte_size -= size
364
- end
365
- while 0 < (byte_pointer + byte_size)
366
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
367
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
368
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
369
- byte_size -= size
370
- end
371
- right_word_start = byte_pointer + byte_size
372
- byte_size = 0
373
- while line.bytesize > (byte_pointer + byte_size)
374
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
375
- mbchar = line.byteslice(byte_pointer + byte_size, size)
376
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
377
- byte_size += size
378
- end
379
- after_start = byte_pointer + byte_size
380
- elsif mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
381
- # ' aaa bb[cursor]b'
382
- byte_size = 0
383
- while 0 < (byte_pointer + byte_size)
384
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
385
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
386
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
387
- byte_size -= size
388
- end
389
- right_word_start = byte_pointer + byte_size
390
- byte_size = 0
391
- while line.bytesize > (byte_pointer + byte_size)
392
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
393
- mbchar = line.byteslice(byte_pointer + byte_size, size)
394
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
395
- byte_size += size
396
- end
397
- after_start = byte_pointer + byte_size
398
- else
399
- byte_size = 0
400
- while (line.bytesize - 1) > (byte_pointer + byte_size)
401
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
402
- mbchar = line.byteslice(byte_pointer + byte_size, size)
403
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
404
- byte_size += size
405
- end
406
- if (byte_pointer + byte_size) == (line.bytesize - 1)
407
- # ' aaa bbb [cursor] '
408
- after_start = line.bytesize
409
- while 0 < (byte_pointer + byte_size)
410
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
411
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
412
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
413
- byte_size -= size
414
- end
415
- while 0 < (byte_pointer + byte_size)
416
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
417
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
418
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
419
- byte_size -= size
420
- end
421
- right_word_start = byte_pointer + byte_size
422
- else
423
- # ' aaa [cursor] bbb '
424
- right_word_start = byte_pointer + byte_size
425
- while line.bytesize > (byte_pointer + byte_size)
426
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
427
- mbchar = line.byteslice(byte_pointer + byte_size, size)
428
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
429
- byte_size += size
430
- end
431
- after_start = byte_pointer + byte_size
432
- end
433
- end
434
- byte_size = right_word_start - byte_pointer
435
- while 0 < (byte_pointer + byte_size)
436
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
437
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
438
- break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/
439
- byte_size -= size
440
- end
441
- middle_start = byte_pointer + byte_size
442
- byte_size = middle_start - byte_pointer
443
- while 0 < (byte_pointer + byte_size)
444
- size = get_prev_mbchar_size(line, byte_pointer + byte_size)
445
- mbchar = line.byteslice(byte_pointer + byte_size - size, size)
446
- break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/
447
- 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)
448
313
  end
449
- left_word_start = byte_pointer + byte_size
450
- [left_word_start, middle_start, right_word_start, after_start]
451
314
  end
452
315
 
453
316
  def self.vi_big_forward_word(line, byte_pointer)
454
- width = 0
455
- byte_size = 0
456
- while (line.bytesize - 1) > (byte_pointer + byte_size)
457
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
458
- mbchar = line.byteslice(byte_pointer + byte_size, size)
459
- break if mbchar =~ /\s/
460
- width += get_mbchar_width(mbchar)
461
- byte_size += size
462
- end
463
- while (line.bytesize - 1) > (byte_pointer + byte_size)
464
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
465
- mbchar = line.byteslice(byte_pointer + byte_size, size)
466
- break if mbchar =~ /\S/
467
- width += get_mbchar_width(mbchar)
468
- byte_size += size
469
- end
470
- [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)
471
321
  end
472
322
 
473
323
  def self.vi_big_forward_end_word(line, byte_pointer)
474
- if (line.bytesize - 1) > byte_pointer
475
- size = get_next_mbchar_size(line, byte_pointer)
476
- mbchar = line.byteslice(byte_pointer, size)
477
- width = get_mbchar_width(mbchar)
478
- byte_size = size
479
- else
480
- return [0, 0]
481
- end
482
- while (line.bytesize - 1) > (byte_pointer + byte_size)
483
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
484
- mbchar = line.byteslice(byte_pointer + byte_size, size)
485
- break if mbchar =~ /\S/
486
- width += get_mbchar_width(mbchar)
487
- byte_size += size
488
- end
489
- prev_width = width
490
- prev_byte_size = byte_size
491
- while line.bytesize > (byte_pointer + byte_size)
492
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
493
- mbchar = line.byteslice(byte_pointer + byte_size, size)
494
- break if mbchar =~ /\s/
495
- prev_width = width
496
- prev_byte_size = byte_size
497
- width += get_mbchar_width(mbchar)
498
- byte_size += size
499
- end
500
- [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)
501
331
  end
502
332
 
503
333
  def self.vi_big_backward_word(line, byte_pointer)
504
- width = 0
505
- byte_size = 0
506
- while 0 < (byte_pointer - byte_size)
507
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
508
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
509
- break if mbchar =~ /\S/
510
- width += get_mbchar_width(mbchar)
511
- byte_size += size
512
- end
513
- while 0 < (byte_pointer - byte_size)
514
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
515
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
516
- break if mbchar =~ /\s/
517
- width += get_mbchar_width(mbchar)
518
- byte_size += size
519
- end
520
- [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)
521
338
  end
522
339
 
523
340
  def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false)
524
- if line.bytesize > byte_pointer
525
- size = get_next_mbchar_size(line, byte_pointer)
526
- mbchar = line.byteslice(byte_pointer, size)
527
- if mbchar =~ /\w/
528
- started_by = :word
529
- elsif mbchar =~ /\s/
530
- 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) }
531
350
  else
532
- started_by = :non_word_printable
533
- end
534
- width = get_mbchar_width(mbchar)
535
- byte_size = size
536
- else
537
- return [0, 0]
538
- end
539
- while line.bytesize > (byte_pointer + byte_size)
540
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
541
- mbchar = line.byteslice(byte_pointer + byte_size, size)
542
- case started_by
543
- when :word
544
- break if mbchar =~ /\W/
545
- when :space
546
- break if mbchar =~ /\S/
547
- when :non_word_printable
548
- break if mbchar =~ /\w|\s/
351
+ gcs.take_while { |c| !word_character?(c) && !space_character?(c) }
549
352
  end
550
- width += get_mbchar_width(mbchar)
551
- byte_size += size
552
- end
553
- return [byte_size, width] if drop_terminate_spaces
554
- while line.bytesize > (byte_pointer + byte_size)
555
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
556
- mbchar = line.byteslice(byte_pointer + byte_size, size)
557
- break if mbchar =~ /\S/
558
- width += get_mbchar_width(mbchar)
559
- byte_size += size
560
- end
561
- [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)
562
358
  end
563
359
 
564
360
  def self.vi_forward_end_word(line, byte_pointer)
565
- if (line.bytesize - 1) > byte_pointer
566
- size = get_next_mbchar_size(line, byte_pointer)
567
- mbchar = line.byteslice(byte_pointer, size)
568
- if mbchar =~ /\w/
569
- started_by = :word
570
- elsif mbchar =~ /\s/
571
- started_by = :space
572
- else
573
- started_by = :non_word_printable
574
- end
575
- width = get_mbchar_width(mbchar)
576
- byte_size = size
577
- else
578
- return [0, 0]
579
- end
580
- if (line.bytesize - 1) > (byte_pointer + byte_size)
581
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
582
- mbchar = line.byteslice(byte_pointer + byte_size, size)
583
- if mbchar =~ /\w/
584
- second = :word
585
- elsif mbchar =~ /\s/
586
- second = :space
587
- else
588
- second = :non_word_printable
589
- end
590
- second_width = get_mbchar_width(mbchar)
591
- second_byte_size = size
592
- else
593
- return [byte_size, width]
594
- end
595
- if second == :space
596
- width += second_width
597
- byte_size += second_byte_size
598
- while (line.bytesize - 1) > (byte_pointer + byte_size)
599
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
600
- mbchar = line.byteslice(byte_pointer + byte_size, size)
601
- if mbchar =~ /\S/
602
- if mbchar =~ /\w/
603
- started_by = :word
604
- else
605
- started_by = :non_word_printable
606
- end
607
- break
608
- end
609
- width += get_mbchar_width(mbchar)
610
- byte_size += size
611
- end
612
- else
613
- case [started_by, second]
614
- when [:word, :non_word_printable], [:non_word_printable, :word]
615
- started_by = second
616
- else
617
- width += second_width
618
- byte_size += second_byte_size
619
- started_by = second
620
- end
621
- end
622
- prev_width = width
623
- prev_byte_size = byte_size
624
- while line.bytesize > (byte_pointer + byte_size)
625
- size = get_next_mbchar_size(line, byte_pointer + byte_size)
626
- mbchar = line.byteslice(byte_pointer + byte_size, size)
627
- case started_by
628
- when :word
629
- break if mbchar =~ /\W/
630
- when :non_word_printable
631
- break if mbchar =~ /[\w\s]/
632
- end
633
- prev_width = width
634
- prev_byte_size = byte_size
635
- width += get_mbchar_width(mbchar)
636
- byte_size += size
637
- end
638
- [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)
639
376
  end
640
377
 
641
378
  def self.vi_backward_word(line, byte_pointer)
642
- width = 0
643
- byte_size = 0
644
- while 0 < (byte_pointer - byte_size)
645
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
646
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
647
- if mbchar =~ /\S/
648
- if mbchar =~ /\w/
649
- started_by = :word
650
- else
651
- started_by = :non_word_printable
652
- end
653
- break
654
- end
655
- width += get_mbchar_width(mbchar)
656
- byte_size += size
657
- end
658
- while 0 < (byte_pointer - byte_size)
659
- size = get_prev_mbchar_size(line, byte_pointer - byte_size)
660
- mbchar = line.byteslice(byte_pointer - byte_size - size, size)
661
- case started_by
662
- when :word
663
- break if mbchar =~ /\W/
664
- when :non_word_printable
665
- 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]
666
395
  end
667
- width += get_mbchar_width(mbchar)
668
- byte_size += size
669
396
  end
670
- [byte_size, width]
397
+ common_prefix_gcs.join
671
398
  end
672
399
 
673
400
  def self.vi_first_print(line)
674
- width = 0
675
- byte_size = 0
676
- while (line.bytesize - 1) > byte_size
677
- size = get_next_mbchar_size(line, byte_size)
678
- mbchar = line.byteslice(byte_size, size)
679
- if mbchar =~ /\S/
680
- break
681
- end
682
- width += get_mbchar_width(mbchar)
683
- byte_size += size
684
- end
685
- [byte_size, width]
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
686
414
  end
687
415
  end
@@ -1,3 +1,3 @@
1
1
  module Reline
2
- VERSION = '0.5.11'
2
+ VERSION = '0.6.0'
3
3
  end
data/lib/reline.rb CHANGED
@@ -6,7 +6,6 @@ require 'reline/key_actor'
6
6
  require 'reline/key_stroke'
7
7
  require 'reline/line_editor'
8
8
  require 'reline/history'
9
- require 'reline/terminfo'
10
9
  require 'reline/io'
11
10
  require 'reline/face'
12
11
  require 'rbconfig'
@@ -18,10 +17,12 @@ module Reline
18
17
 
19
18
  class ConfigEncodingConversionError < StandardError; end
20
19
 
21
- Key = Struct.new(:char, :combined_char, :with_meta) do
20
+ # EOF key: { char: nil, method_symbol: nil }
21
+ # Other key: { char: String, method_symbol: Symbol }
22
+ Key = Struct.new(:char, :method_symbol, :unused_boolean) do
22
23
  # For dialog_proc `key.match?(dialog.name)`
23
24
  def match?(sym)
24
- combined_char.is_a?(Symbol) && combined_char == sym
25
+ method_symbol && method_symbol == sym
25
26
  end
26
27
  end
27
28
  CursorPos = Struct.new(:x, :y)
@@ -182,9 +183,7 @@ module Reline
182
183
  def output=(val)
183
184
  raise TypeError unless val.respond_to?(:write) or val.nil?
184
185
  @output = val
185
- if io_gate.respond_to?(:output=)
186
- io_gate.output = val
187
- end
186
+ io_gate.output = val
188
187
  end
189
188
 
190
189
  def vi_editing_mode
@@ -308,6 +307,7 @@ module Reline
308
307
  otio = io_gate.prep
309
308
 
310
309
  may_req_ambiguous_char_width
310
+ key_stroke.encoding = encoding
311
311
  line_editor.reset(prompt)
312
312
  if multiline
313
313
  line_editor.multiline_on
@@ -317,7 +317,6 @@ module Reline
317
317
  else
318
318
  line_editor.multiline_off
319
319
  end
320
- line_editor.output = output
321
320
  line_editor.completion_proc = completion_proc
322
321
  line_editor.completion_append_character = completion_append_character
323
322
  line_editor.output_modifier_proc = output_modifier_proc
@@ -344,13 +343,14 @@ module Reline
344
343
  read_io(config.keyseq_timeout) { |inputs|
345
344
  line_editor.set_pasting_state(io_gate.in_pasting?)
346
345
  inputs.each do |key|
347
- if key.char == :bracketed_paste_start
348
- text = io_gate.read_bracketed_paste
349
- line_editor.insert_multiline_text(text)
350
- line_editor.scroll_into_view
351
- else
352
- line_editor.update(key)
346
+ case key.method_symbol
347
+ when :bracketed_paste_start
348
+ # io_gate is Reline::ANSI because the key :bracketed_paste_start is only assigned in Reline::ANSI
349
+ key = Reline::Key.new(io_gate.read_bracketed_paste, :insert_multiline_text)
350
+ when :quoted_insert, :ed_quoted_insert
351
+ key = Reline::Key.new(io_gate.read_single_char(config.keyseq_timeout), :insert_raw_char)
353
352
  end
353
+ line_editor.update(key)
354
354
  end
355
355
  }
356
356
  if line_editor.finished?
@@ -486,7 +486,7 @@ module Reline
486
486
  def self.core
487
487
  @core ||= Core.new { |core|
488
488
  core.config = Reline::Config.new
489
- core.key_stroke = Reline::KeyStroke.new(core.config)
489
+ core.key_stroke = Reline::KeyStroke.new(core.config, core.encoding)
490
490
  core.line_editor = Reline::LineEditor.new(core.config)
491
491
 
492
492
  core.basic_word_break_characters = " \t\n`><=;|&{("