reline 0.5.11 → 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/io/ansi.rb +40 -63
- data/lib/reline/io/dumb.rb +15 -1
- data/lib/reline/io/windows.rb +11 -1
- 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 +198 -352
- data/lib/reline/unicode.rb +119 -391
- data/lib/reline/version.rb +1 -1
- data/lib/reline.rb +14 -14
- metadata +6 -4
- data/lib/reline/terminfo.rb +0 -158
data/lib/reline/unicode.rb
CHANGED
@@ -121,9 +121,14 @@ class Reline::Unicode
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
|
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
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
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
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
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
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
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
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
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
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
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
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
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
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
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
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
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
|
-
|
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
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
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
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
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
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
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
|
-
|
397
|
+
common_prefix_gcs.join
|
671
398
|
end
|
672
399
|
|
673
400
|
def self.vi_first_print(line)
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
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
|
data/lib/reline/version.rb
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
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`><=;|&{("
|