terminal_rb 0.14.0 → 0.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 107b7b65dc5f58b1178e01fc86adad127df49fd175e56f7e36d2bfb873fc17a9
4
- data.tar.gz: dfbf4797df22ac2803034a85a00fce2c480e4530840abd0d2a900bfb98993db5
3
+ metadata.gz: 05eda4c596ad7060b576cb01f6fe7dafd38e114274b44c14c4c90e1b3a2b322a
4
+ data.tar.gz: '0479860569041d6fb874d38de0d183a2cc778a6803d3eed302425cd795133e06'
5
5
  SHA512:
6
- metadata.gz: 115235b382d02c4c5242925e1bf1ff3b9928702b30682dd4a2964282b11df6339118da582e0eebc096806005f781a537688741eab2e94984db391abaf6b4ec72
7
- data.tar.gz: 2718bf0147fcb7c3d84374f59c4f8ea4862c58dfd6982057196a78bd1f4308eb9c319a55ba3c41339a45a6fa994b1449b2cedb2e00ce6de5548dd3eaa5b98fa0
6
+ metadata.gz: ab038b9b4cc9ed41ba09673be9a2e2d25aa2edc2ffbf1fa0cad826ca7f55c8fef3aab734648f13f63750ea7799bce638479839e5f609e597eeb0bf4508b0f0e7
7
+ data.tar.gz: b501721dfd7d6d79ceb8821fa5dd9d877e764e235435f3e6441e9b02b424a5873aba1318365a4b064a9430c135882be39470361d0042b56726be3dd2505e9db5
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Terminal.rb ![version](https://img.shields.io/gem/v/terminal_rb?label=)
2
2
 
3
- Terminal.rb supports you with input and output on your terminal. Simple [BBCode](https://en.wikipedia.org/wiki/BBCode)-like markup for attributes and coloring, word-wise line breaks, and correct special key recognition enable you to implement your CLI app quickly and easily.
3
+ Terminal.rb supports you with input and output on your terminal. Simple [BBCode](https://en.wikipedia.org/wiki/BBCode)-like markup for attributes and coloring, word-wise line breaks, correct special key recognition and mouse event reporting enable you to implement your CLI app quickly and easily.
4
4
 
5
5
  - Gem: [rubygems.org](https://rubygems.org/gems/terminal_rb)
6
6
  - Source: [codeberg.org](https://codeberg.org/mblumtritt/Terminal.rb)
@@ -16,6 +16,7 @@ Terminal.rb supports you with input and output on your terminal. Simple [BBCode]
16
16
  - calculation for correct display width of strings containing Unicdode chars inclusive emojis
17
17
  - word-wise line break generator
18
18
  - supports [CSIu protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol)
19
+ - mouse events support
19
20
 
20
21
  ## Examples
21
22
 
data/examples/info.rb CHANGED
@@ -12,5 +12,6 @@ Terminal.puts <<~TEXT
12
12
  Terminal Size: [b]#{Terminal.size.join(' x ')}[/]
13
13
  Cursor Position: [b]#{Terminal.pos&.join(', ')}[/]
14
14
  Input Mode: [b]#{Terminal.input_mode}[/]
15
+ Link Support: [b]#{Terminal::Ansi.link('https://codeberg.org/mblumtritt/Terminal.rb', 'Link')}[/]
15
16
 
16
17
  TEXT
@@ -13,10 +13,13 @@ TEXT
13
13
  Terminal.hide_cursor
14
14
  at_exit { Terminal.show_cursor } # required for some terminals :/
15
15
 
16
- while true
17
- event = Terminal.read_key_event or break puts
16
+ # if you like to have mouse position changes reported then use
17
+ # 'mouse_move: true' in next line
18
+ Terminal.on_key_event(mouse: true, focus: true, mouse_move: false) do |event|
18
19
  str = "[blue]: [yellow]#{event.raw.inspect}"
19
20
  str << " [bold bright_green]#{event.name}[/]" unless event.simple?
21
+ str << " [dim]#{event.position.inspect}[/]" if event.position
20
22
  Terminal.puts(str)
21
- break puts if event.name == 'Esc'
23
+ event.name != 'Esc'
22
24
  end
25
+ puts
data/examples/text.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/terminal'
4
+
5
+ LOREM = <<~IPSUM
6
+ This example outputs [i bright_white]Lorem ipsum[/] text so that it takes up 75% of the screen width and fits the height of your screen.
7
+
8
+ [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet.
9
+
10
+ Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat.
11
+
12
+ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
13
+
14
+ Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
15
+
16
+ Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg].
17
+
18
+ At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd [b]magna[/b] no rebum. [yellow]sanctus[/fg] sea sed takimata ut vero voluptua. est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat.
19
+
20
+ Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg].
21
+
22
+ [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet.
23
+
24
+ Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat.
25
+
26
+ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
27
+
28
+ Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
29
+ IPSUM
30
+
31
+ rows, cols = Terminal.size
32
+ puts Terminal::Text.each(LOREM, limit: cols * 0.75).take(rows - 1)
data/lib/terminal/ansi.rb CHANGED
@@ -34,8 +34,8 @@ module Terminal
34
34
  # @!group ANSI control code generator functions
35
35
  #
36
36
 
37
- # Combine given ANSI {attributes}, {colors}, {named_colors} and color
38
- # codes.
37
+ # Combine given {attributes}, {colors}, {named_colors} and color codes to
38
+ # an ANSI control code sequence.
39
39
  #
40
40
  # Colors can specified by their name for ANSI 3-bit and 4-bit colors.
41
41
  # For 8-bit ANSI colors use 2-digit hexadecimal values `00`...`ff`.
@@ -96,7 +96,7 @@ module Terminal
96
96
  return +'' if attributes.empty?
97
97
  "\e[#{
98
98
  attributes
99
- .map do |arg|
99
+ .map! do |arg|
100
100
  case arg
101
101
  when String
102
102
  @attr_map[arg] || _invalid(arg)
@@ -154,8 +154,7 @@ module Terminal
154
154
  # @param str [#to_s] string to be modified
155
155
  # @return [String] string without ANSI attributes
156
156
  def undecorate(str)
157
- str = str.to_s
158
- str.index("\e") ? str.gsub(@re_test, '') : str.dup
157
+ (str = str.to_s).index("\e") ? str.gsub(@re_test, '') : str.dup
159
158
  end
160
159
 
161
160
  # Try to combine given ANSI attributes and colors.
@@ -263,8 +262,7 @@ module Terminal
263
262
  # @param str [#to_s] string to be modified
264
263
  # @return [String] string without BBCode and ANSI control codes.
265
264
  def plain(str)
266
- str = str.to_s
267
- unless str.index('[')
265
+ unless (str = str.to_s).index('[')
268
266
  return str.index("\e") ? str.gsub(@re_test, '') : str.dup
269
267
  end
270
268
  str =
@@ -289,16 +287,14 @@ module Terminal
289
287
  pos = -1
290
288
  @pi2_third ||= 2.0 * Math::PI / 3.0
291
289
  @pi4_third ||= 4.0 * Math::PI / 3.0
292
- str
293
- .to_s
294
- .chars
295
- .map! do |char|
290
+ (
291
+ str.to_s.chars.map! do |char|
296
292
  i = (seed + ((pos += 1) / spread)) * frequency
297
293
  "\e[38;2;#{(Math.sin(i) * 255).to_i.abs};" \
298
294
  "#{(Math.sin(i + @pi2_third) * 255).to_i.abs};" \
299
295
  "#{(Math.sin(i + @pi4_third) * 255).to_i.abs}m#{char}"
300
- end
301
- .join << RESET
296
+ end << RESET
297
+ ).join
302
298
  end
303
299
 
304
300
  #
@@ -349,6 +345,21 @@ module Terminal
349
345
  # @return (see cursor_up)
350
346
  def cursor_column(column = 1) = "\e[#{column}G"
351
347
 
348
+ # Move cursor to given column in the current row relative to the current
349
+ # position.
350
+ # (Skip some columns.)
351
+ #
352
+ # @param (see cursor_column)
353
+ # @return (see cursor_up)
354
+ def cursor_column_rel(column = 1) = "\e[#{column}a"
355
+
356
+ # Move cursor to given row relative to the current position.
357
+ # (Skip some rows.)
358
+ #
359
+ # @param row [Integer] row index
360
+ # @return (see cursor_up)
361
+ def cursor_row_rel(row = 1) = "\e[#{row}e"
362
+
352
363
  # Move to given row and column.
353
364
  #
354
365
  # @param row [Integer] row index
@@ -389,20 +400,7 @@ module Terminal
389
400
  #
390
401
  # @param part [:below, :above, :all, :scrollback] screen part to erase
391
402
  # @return (see cursor_up)
392
- def screen_erase(part = :all)
393
- "\e[#{
394
- case part
395
- when :below
396
- # nop
397
- when :above
398
- '1'
399
- when :scrollback
400
- '3'
401
- else # all
402
- '2'
403
- end
404
- }J"
405
- end
403
+ def screen_erase(part = :all) = "\e[#{@screen_erase[part]}J"
406
404
 
407
405
  # Safe current screen.
408
406
  #
@@ -442,22 +440,17 @@ module Terminal
442
440
  # @!group Other ANSI control functions
443
441
  #
444
442
 
443
+ # Repeat last char.
444
+ #
445
+ # @param count [Integer] repeat count
446
+ # @return (see cursor_up)
447
+ def char_repeat(count = 1) = "\e[#{count}b"
448
+
445
449
  # Erase part of line.
446
450
  #
447
451
  # @param part [:to_end, :to_start, :all] line part to erase
448
452
  # @return (see cursor_up)
449
- def line_erase(part = :all)
450
- "\e[#{
451
- case part
452
- when :to_end
453
- # nop
454
- when :to_start
455
- '1'
456
- else # :all
457
- '2'
458
- end
459
- }K"
460
- end
453
+ def line_erase(part = :all) = "\e[#{@line_erase[part]}K"
461
454
 
462
455
  # Set (tab) title.
463
456
  # This is not widely supported; works for
@@ -486,6 +479,17 @@ module Terminal
486
479
  # @return (see cursor_up)
487
480
  def link(url, text) = "\e]8;;#{url}\a#{text}\e]8;;\a"
488
481
 
482
+ # Show a simple notification.
483
+ # This is not widely supported; works for
484
+ # Ghosty,
485
+ # iTerm2,
486
+ # Kitty,
487
+ # WezTerm.
488
+ #
489
+ # @param text [#to_s] text to display
490
+ # @return (see cursor_up)
491
+ def notify(text) = "\e]9;#{text}\a"
492
+
489
493
  # Create scaled text.
490
494
  # It uses the
491
495
  # [text sizing protocol](https://sw.kovidgoyal.net/kitty/text-sizing-protocol).
@@ -559,27 +563,6 @@ module Terminal
559
563
  caller(1)
560
564
  )
561
565
  end
562
-
563
- def _color(str)
564
- b, v = /\A(fg|bg|on|ul)?_?#?([[:xdigit:]]{1,6})\z/.match(str)&.captures
565
- if v
566
- return(
567
- case v.size
568
- when 1, 2
569
- "#{@cbase[b]};5;#{v.hex}"
570
- when 3
571
- "#{@cbase[b]};2;#{(v[0] * 2).hex};#{
572
- (v[1] * 2).hex
573
- };#{(v[2] * 2).hex}"
574
- when 6
575
- "#{@cbase[b]};2;#{v[0, 2].hex};#{v[2, 2].hex};#{v[4, 2].hex}"
576
- end
577
- )
578
- end
579
- b, v = /\A(fg|bg|on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(str)&.captures
580
- return unless v
581
- name = NAMED_COLORS[v] and return "#{@cbase[b]};#{name}"
582
- end
583
566
  end
584
567
 
585
568
  @cbase = { 'bg' => '48', 'on' => '48', 'ul' => '58' }
@@ -588,63 +571,13 @@ module Terminal
588
571
 
589
572
  @re_test =
590
573
  /
591
- (?:\e\[[\d;:\?]*[ABCDEFGHJKSTfminsuhl])
574
+ (?:\e\[[\d;:?]*[ABCDEFGHJKSTfminsuhl])
592
575
  |
593
576
  (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
594
577
  /x
595
578
 
596
579
  @re_bbcode = /(?:\[((?~[\[\]]))\])/
597
580
 
598
- attr_map = {
599
- '' => 'reset',
600
- '1' => 'bold',
601
- '2' => 'faint',
602
- '3' => 'italic',
603
- '4' => 'underline',
604
- '5' => 'blink',
605
- '6' => 'rapid_blink',
606
- '7' => 'invert',
607
- '8' => 'hide',
608
- '9' => 'strike',
609
- '10' => 'primary_font',
610
- '11' => 'font1',
611
- '12' => 'font2',
612
- '13' => 'font3',
613
- '14' => 'font4',
614
- '15' => 'font5',
615
- '16' => 'font6',
616
- '17' => 'font7',
617
- '18' => 'font8',
618
- '19' => 'font9',
619
- '20' => 'fraktur',
620
- '21' => 'double_underline',
621
- '22' => 'bold_off', # faint_off
622
- '23' => 'italic_off', # fraktur_off
623
- '24' => 'underline_off', # double_underline_off
624
- '25' => 'blink_off', # rapid_blink_off
625
- '26' => 'proportional',
626
- '27' => 'invert_off',
627
- '28' => 'hide_off',
628
- '29' => 'strike_off',
629
- # colors ...
630
- '50' => 'proportional_off',
631
- '51' => 'framed',
632
- '52' => 'encircled',
633
- '53' => 'overlined',
634
- '54' => 'framed_off', # encircled_off
635
- '55' => 'overlined_off',
636
- # ...
637
- '73' => 'superscript',
638
- '74' => 'subscript',
639
- '75' => 'superscript_off', # subscript_off
640
- # special underline
641
- '4:3' => 'curly_underline',
642
- '4:4' => 'dotted_underline',
643
- '4:5' => 'dashed_underline',
644
- '4:0' => 'curly_underline_off' # dotted_underline_off, dashed_underline_off
645
- }.invert
646
- attr_alias = ->(t, s) { attr_map[t] = attr_map[s] }
647
-
648
581
  clr_map = {
649
582
  # foreground
650
583
  '30' => 'black',
@@ -714,6 +647,56 @@ module Terminal
714
647
  clr_alias['fg_default', 'default']
715
648
  clr_alias['bg_default', 'on_default']
716
649
 
650
+ attr_map = {
651
+ '' => 'reset',
652
+ '1' => 'bold',
653
+ '2' => 'faint',
654
+ '3' => 'italic',
655
+ '4' => 'underline',
656
+ '5' => 'blink',
657
+ '6' => 'rapid_blink',
658
+ '7' => 'invert',
659
+ '8' => 'hide',
660
+ '9' => 'strike',
661
+ '10' => 'primary_font',
662
+ '11' => 'font1',
663
+ '12' => 'font2',
664
+ '13' => 'font3',
665
+ '14' => 'font4',
666
+ '15' => 'font5',
667
+ '16' => 'font6',
668
+ '17' => 'font7',
669
+ '18' => 'font8',
670
+ '19' => 'font9',
671
+ '20' => 'fraktur',
672
+ '21' => 'double_underline',
673
+ '22' => 'bold_off', # faint_off
674
+ '23' => 'italic_off', # fraktur_off
675
+ '24' => 'underline_off', # double_underline_off
676
+ '25' => 'blink_off', # rapid_blink_off
677
+ '26' => 'proportional',
678
+ '27' => 'invert_off',
679
+ '28' => 'hide_off',
680
+ '29' => 'strike_off',
681
+ # colors ...
682
+ '50' => 'proportional_off',
683
+ '51' => 'framed',
684
+ '52' => 'encircled',
685
+ '53' => 'overlined',
686
+ '54' => 'framed_off', # encircled_off
687
+ '55' => 'overlined_off',
688
+ # ...
689
+ '73' => 'superscript',
690
+ '74' => 'subscript',
691
+ '75' => 'superscript_off', # subscript_off
692
+ # special underline
693
+ '4:3' => 'curly_underline',
694
+ '4:4' => 'dotted_underline',
695
+ '4:5' => 'dashed_underline',
696
+ '4:0' => 'curly_underline_off' # dotted_underline_off, dashed_underline_off
697
+ }.invert
698
+ attr_alias = ->(t, s) { attr_map[t] = attr_map[s] }
699
+
717
700
  attr_alias['faint_off', 'bold_off']
718
701
  attr_alias['fraktur_off', 'italic_off']
719
702
  attr_alias['double_underline_off', 'underline_off']
@@ -777,7 +760,23 @@ module Terminal
777
760
  attr_map['/bg'] = clr_map['on_default']
778
761
  attr_map['/ul'] = clr_map['ul_default']
779
762
 
780
- @attr_map = Hash.new { _color(_2) }
763
+ @attr_map =
764
+ Hash.new do |_, str|
765
+ b, v = /\A(fg|bg|on|ul)?_?#?([[:xdigit:]]{1,6})\z/.match(str)&.captures
766
+ unless v
767
+ b = /\A(fg|bg|on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(str) or next
768
+ name = NAMED_COLORS[b[2]] or next
769
+ next "#{@cbase[b[1]]};#{name}"
770
+ end
771
+ case v.size
772
+ when 1, 2
773
+ "#{@cbase[b]};5;#{v.hex}"
774
+ when 3
775
+ "#{@cbase[b]};2;#{(v[0] * 2).hex};#{(v[1] * 2).hex};#{(v[2] * 2).hex}"
776
+ when 6
777
+ "#{@cbase[b]};2;#{v[0, 2].hex};#{v[2, 2].hex};#{v[4, 2].hex}"
778
+ end
779
+ end
781
780
  attr_map.merge!(clr_map).keys.sort!.each { @attr_map[_1] = attr_map[_1] }
782
781
  @attr_map.freeze
783
782
 
@@ -785,21 +784,29 @@ module Terminal
785
784
  @attrs_map.default_proc = @attr_map.default_proc
786
785
  @attrs_map.compare_by_identity.freeze
787
786
 
787
+ @screen_erase = { below: nil, above: '1', scrollback: '3' }
788
+ @screen_erase.default = '2'
789
+ @screen_erase.compare_by_identity.freeze
790
+
791
+ @line_erase = { to_end: nil, to_start: '1' }
792
+ @line_erase.default = '2'
793
+ @line_erase.compare_by_identity.freeze
794
+
788
795
  autoload :NAMED_COLORS, "#{__dir__}/ansi/named_colors.rb"
789
796
  private_constant :NAMED_COLORS
790
797
 
791
798
  # @private
792
- RESET = self[:reset].freeze
799
+ RESET = -self[:reset]
793
800
 
794
801
  # @private
795
802
  FULL_RESET = "\ec"
796
803
 
797
804
  # @private
798
- CURSOR_HOME = cursor_pos(nil, nil).freeze
805
+ CURSOR_HOME = -cursor_pos(nil, nil)
799
806
  # @private
800
- CURSOR_FIRST_ROW = cursor_pos(1).freeze
807
+ CURSOR_FIRST_ROW = -cursor_pos(1)
801
808
  # @private
802
- CURSOR_FIRST_COLUMN = cursor_column(1).freeze
809
+ CURSOR_FIRST_COLUMN = -cursor_column(1)
803
810
 
804
811
  # @private
805
812
  CURSOR_SHOW = "\e[?25h"
@@ -817,13 +824,13 @@ module Terminal
817
824
  CURSOR_POS_RESTORE = "\e8"
818
825
 
819
826
  # @private
820
- SCREEN_ERASE = screen_erase.freeze
827
+ SCREEN_ERASE = -screen_erase
821
828
  # @private
822
- SCREEN_ERASE_BELOW = screen_erase(:below).freeze
829
+ SCREEN_ERASE_BELOW = -screen_erase(:below)
823
830
  # @private
824
- SCREEN_ERASE_ABOVE = screen_erase(:above).freeze
831
+ SCREEN_ERASE_ABOVE = -screen_erase(:above)
825
832
  # @private
826
- SCREEN_ERASE_SCROLLBACK = screen_erase(:scrollback).freeze
833
+ SCREEN_ERASE_SCROLLBACK = -screen_erase(:scrollback)
827
834
 
828
835
  # @private
829
836
  SCREEN_SAVE = "\e[?47h"
@@ -832,7 +839,7 @@ module Terminal
832
839
 
833
840
  # @private
834
841
  # @comment at least Kitty requires CURSOR_HOME too
835
- SCREEN_ALTERNATE = "\e[?1049h#{CURSOR_HOME}".freeze
842
+ SCREEN_ALTERNATE = -"\e[?1049h#{CURSOR_HOME}"
836
843
  # @private
837
844
  SCREEN_ALTERNATE_OFF = "\e[?1049l"
838
845
 
@@ -842,21 +849,19 @@ module Terminal
842
849
  SCREEN_REVERSE_MODE_OFF = "\e[?5l"
843
850
 
844
851
  # @private
845
- LINE_ERASE = line_erase.freeze
852
+ LINE_ERASE = -line_erase
846
853
  # @private
847
- LINE_ERASE_TO_END = line_erase(:to_end).freeze
854
+ LINE_ERASE_TO_END = -line_erase(:to_end)
848
855
  # @private
849
- LINE_ERASE_TO_START = line_erase(:to_start).freeze
856
+ LINE_ERASE_TO_START = -line_erase(:to_start)
850
857
  # @private
851
- LINE_ERASE_PREV = "#{cursor_prev_line(nil)}#{LINE_ERASE}".freeze
858
+ LINE_ERASE_PREV = -"#{cursor_prev_line(nil)}#{LINE_ERASE}"
852
859
 
853
860
  # @comment seems not widely supported:
854
861
  # doubled: def cursor_column(column = 1) = "\e[#{column}`"
855
862
  # doubled: def cursor_row(row = 1) = "\e[#{row}d"
856
863
  # doubled: def cursor_pos(row, col) = "\e[#{row};#{col}f"
857
864
  #
858
- # def cursor_column_rel(columns = 1) = "\e[#{columns}a"
859
- # def cursor_row_rel(rows = 1) = "\e[#{rows}e"
860
865
  # def cursor_tab(count = 1) = "\e[#{column}I"
861
866
  # def cursor_reverse_tab(count = 1) = "\e[#{count}Z"
862
867
  #
@@ -867,20 +872,14 @@ module Terminal
867
872
  # def chars_delete(count = 1) = "\e[#{count}P"
868
873
  # def chars_erase(count = 1) = "\e[#{count}X"
869
874
  #
870
- # def chars_repeat_last(count = 1) = "\e[#{count}b"
871
- #
872
- # def notify(title) = "\e]9;#{title}\a"
873
- #
874
875
  # def set_scroll_region(top = nil, bottom = nil) = "\e[#{top};#{bottom}r"
875
876
 
876
877
  # @comment other:
877
878
  # "\eE" same as "\r\n"
878
- # "\eD" same as "\n" but preserves X coord
879
+ # "\eD" same as "\n" but preserves column
879
880
  # "\eM" reverse "\n"
880
881
  # "\e[6n" report Cursor Position as "ESC \[ row ; col R"
881
882
  # "\e[21t report window’s title as ESC ] l title ESC \"
882
- # "\e[?1004h" report focus lost "\e[O" and focus get "\e[I"
883
- # (disable by "\e[?1004l")
884
883
 
885
884
  # @comment TODO:
886
885
  # https://sw.kovidgoyal.net/kitty/desktop-notifications