tui-td 0.2.10 → 0.2.11

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: 3eb2dc4db4af2ddbcbe377f8a3d6bddf8e1a45f3c78c85a25d8480d1e740e9f7
4
- data.tar.gz: 31a65006ceb299ba4487446932bf74305ecb92d4241288f82d7d11a0283e6bf6
3
+ metadata.gz: 07ef26981840d9171eb2082a6c638c6f43869c05ffdb11c7fff857b058ce87e7
4
+ data.tar.gz: 47d8dd453c1a393ad2f838b67226f92d50b97d5647fb775e36e95f9e381105e9
5
5
  SHA512:
6
- metadata.gz: 706d849d8c35380fabf149bcf6cee5441d92a71b228d7ac4bc0bbba3852be4443496d9d6ae79afd15daa5217b9ede798d6ce2d15dee2cbeb5f6a0901b5269722
7
- data.tar.gz: 9595ce52cd78959dfb49f314d3272d6cf2c065dd215467e204c862b48da2a88525ab8a9dad0ab98ccdd7b7fc142e700eab4ca65ea6a8c2b90d53aefbe8eca922
6
+ metadata.gz: 94ce617f9370428126bf1b565ca67787a455f6d64cd6273fc69c2cecc83ca293b07272cf3f227787ba4e56ae4bf914447752689115979d1d19863fd5d1e17545
7
+ data.tar.gz: fdc72eaff712400c026469730cce5fe88daf65b2cca7fa25a071d79a1a2d53b648f5cd80dc8f2b11fe952ce1c9b4aeed9dde5e372715c22d877487d1db25cb52
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # CHANGELOG
2
2
 
3
+ # CHANGELOG
4
+
5
+ ## 0.2.11
6
+
7
+ - Add RuboCop (rubocop-rake, rubocop-rspec), Reek, and Bundler-Audit linters
8
+ - Pre-commit hook runs all three checks automatically
9
+ - Fix dead code after `raise` in test_runner.rb
10
+ - Rename `is_cursor?` to `cursor_at?` in html_renderer.rb
11
+ - Merge duplicate `describe "#find_text"` blocks in state_spec.rb
12
+ - Fix ANSI parser `FormatStringToken` warnings
13
+
3
14
  ## 0.2.10
4
15
 
5
16
  - Three new MCP tools: `tui_wait_for_exit` (wait for process to end), `tui_exit_status` (get exit code), `tui_find_text` (search terminal state for text/regex matches)
@@ -12,14 +12,15 @@ module TUITD
12
12
  #
13
13
  # Output: {rows: [[{char, fg, bg, bold, italic, underline}]], cursor: {row, col}, size: {rows, cols}}
14
14
  #
15
+ # rubocop:disable Metrics/ModuleLength
15
16
  module ANSIParser
16
17
  SGR_COLORS = {
17
- 0 => :reset,
18
- 1 => :bold,
19
- 3 => :italic,
20
- 4 => :underline,
21
- 5 => :blink,
22
- 7 => :reverse,
18
+ 0 => :reset,
19
+ 1 => :bold,
20
+ 3 => :italic,
21
+ 4 => :underline,
22
+ 5 => :blink,
23
+ 7 => :reverse,
23
24
  22 => :normal,
24
25
  23 => :no_italic,
25
26
  24 => :no_underline,
@@ -62,16 +63,16 @@ module TUITD
62
63
  }.freeze
63
64
 
64
65
  SGR_16_TO_NAME = {
65
- 0 => "black",
66
- 1 => "red",
67
- 2 => "green",
68
- 3 => "yellow",
69
- 4 => "blue",
70
- 5 => "magenta",
71
- 6 => "cyan",
72
- 7 => "white",
73
- 8 => "bright_black",
74
- 9 => "bright_red",
66
+ 0 => "black",
67
+ 1 => "red",
68
+ 2 => "green",
69
+ 3 => "yellow",
70
+ 4 => "blue",
71
+ 5 => "magenta",
72
+ 6 => "cyan",
73
+ 7 => "white",
74
+ 8 => "bright_black",
75
+ 9 => "bright_red",
75
76
  10 => "bright_green",
76
77
  11 => "bright_yellow",
77
78
  12 => "bright_blue",
@@ -81,40 +82,41 @@ module TUITD
81
82
  }.freeze
82
83
 
83
84
  DEC_MAP = {
84
- '`' => '',
85
- 'a' => '',
86
- 'b' => "\u2409",
87
- 'c' => "\u240C",
88
- 'd' => "\u240D",
89
- 'e' => "\u240A",
90
- 'f' => '°',
91
- 'g' => '±',
92
- 'h' => "\u2424",
93
- 'i' => "\u240B",
94
- 'j' => '',
95
- 'k' => '',
96
- 'l' => '',
97
- 'm' => '',
98
- 'n' => '',
99
- 'o' => '',
100
- 'p' => '',
101
- 'q' => '',
102
- 'r' => '',
103
- 's' => '',
104
- 't' => '',
105
- 'u' => '',
106
- 'v' => '',
107
- 'w' => '',
108
- 'x' => '',
109
- 'y' => '',
110
- 'z' => '',
111
- '{' => 'π',
112
- '|' => '',
113
- '}' => '£',
114
- '~' => '·'
85
+ "`" => "",
86
+ "a" => "",
87
+ "b" => "\u2409",
88
+ "c" => "\u240C",
89
+ "d" => "\u240D",
90
+ "e" => "\u240A",
91
+ "f" => "°",
92
+ "g" => "±",
93
+ "h" => "\u2424",
94
+ "i" => "\u240B",
95
+ "j" => "",
96
+ "k" => "",
97
+ "l" => "",
98
+ "m" => "",
99
+ "n" => "",
100
+ "o" => "",
101
+ "p" => "",
102
+ "q" => "",
103
+ "r" => "",
104
+ "s" => "",
105
+ "t" => "",
106
+ "u" => "",
107
+ "v" => "",
108
+ "w" => "",
109
+ "x" => "",
110
+ "y" => "",
111
+ "z" => "",
112
+ "{" => "π",
113
+ "|" => "",
114
+ "}" => "£",
115
+ "~" => "·",
115
116
  }.freeze
116
117
 
117
118
  # Parse raw terminal output into a structured state Hash
119
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockNesting
118
120
  def self.parse(raw, rows = 40, cols = 120)
119
121
  grid = Array.new(rows) do
120
122
  Array.new(cols) { default_cell.dup }
@@ -156,7 +158,7 @@ module TUITD
156
158
  if processed[i] == "\e" && processed[i + 1] == "["
157
159
  # Find end of CSI sequence
158
160
  j = i + 2
159
- j += 1 while j < processed.length && !processed[j].match?(/[A-HJ-KP-SX@\`fhlmnRrsuq]/)
161
+ j += 1 while j < processed.length && !processed[j].match?(/[A-HJ-KP-SX@`fhlmnRrsuq]/)
160
162
  seq = processed[i..j]
161
163
 
162
164
  dsr, new_saved, action = _apply_csi(seq, cursor, attrs, grid, rows, cols, saved_cursor, scroll_region)
@@ -211,27 +213,19 @@ module TUITD
211
213
  end
212
214
  end
213
215
 
214
- if action.key?(:cursor_visible)
215
- cursor_visible = action[:cursor_visible]
216
- end
216
+ cursor_visible = action[:cursor_visible] if action.key?(:cursor_visible)
217
217
 
218
- if action.key?(:cursor_style)
219
- cursor_style = action[:cursor_style]
220
- end
218
+ cursor_style = action[:cursor_style] if action.key?(:cursor_style)
221
219
 
222
- if action.key?(:mouse_mode)
223
- mouse_mode = action[:mouse_mode]
224
- end
220
+ mouse_mode = action[:mouse_mode] if action.key?(:mouse_mode)
225
221
 
226
- if action.key?(:mouse_format)
227
- mouse_format = action[:mouse_format]
228
- end
222
+ mouse_format = action[:mouse_format] if action.key?(:mouse_format)
229
223
 
230
224
  i = j + 1
231
- elsif processed[i] == "\n" || processed[i] == "\r\n"
225
+ elsif ["\n", "\r\n"].include?(processed[i])
232
226
  cursor[:row] += 1
233
227
  cursor[:col] = 0
234
- i += processed[i..i + 1] == "\r\n" ? 2 : 1
228
+ i += processed[i..(i + 1)] == "\r\n" ? 2 : 1
235
229
  elsif processed[i] == "\r"
236
230
  cursor[:col] = 0
237
231
  i += 1
@@ -240,7 +234,7 @@ module TUITD
240
234
  cursor[:col] = cols - 1 if cursor[:col] >= cols
241
235
  i += 1
242
236
  elsif processed[i] == "\b"
243
- cursor[:col] -= 1 if cursor[:col] > 0
237
+ cursor[:col] -= 1 if cursor[:col].positive?
244
238
  i += 1
245
239
  elsif processed[i] == "\a"
246
240
  # Bell — ignore
@@ -270,13 +264,13 @@ module TUITD
270
264
  cursor[:col] = saved_cursor[:col]
271
265
  end
272
266
  i += 2
273
- elsif processed[i + 1] == "(" && (processed[i + 2] == "0" || processed[i + 2] == "B")
267
+ elsif processed[i + 1] == "(" && %w[0 B].include?(processed[i + 2])
274
268
  g0_charset = (processed[i + 2] == "0" ? :dec : :ascii)
275
269
  i += 3
276
- elsif processed[i + 1] == ")" && (processed[i + 2] == "0" || processed[i + 2] == "B")
270
+ elsif processed[i + 1] == ")" && %w[0 B].include?(processed[i + 2])
277
271
  g1_charset = (processed[i + 2] == "0" ? :dec : :ascii)
278
272
  i += 3
279
- elsif processed[i + 1] && processed[i + 1].match?(/[()*+\-.\/]/)
273
+ elsif processed[i + 1]&.match?(%r{[()*+\-./]})
280
274
  # Other ISO 2022 charset sequences (e.g. G2/G3 or other charsets)
281
275
  i += 3
282
276
  else
@@ -288,16 +282,14 @@ module TUITD
288
282
  cell = grid[cursor[:row]][cursor[:col]]
289
283
  current_charset = (active_charset == :g1 ? g1_charset : g0_charset)
290
284
  mapped_char = char
291
- if current_charset == :dec && DEC_MAP.key?(char)
292
- mapped_char = DEC_MAP[char]
293
- end
285
+ mapped_char = DEC_MAP[char] if current_charset == :dec && DEC_MAP.key?(char)
294
286
  cell[:char] = mapped_char
295
287
  cell.merge!(attrs)
296
288
  cursor[:col] += 1
297
289
  cursor[:col] = cols - 1 if cursor[:col] >= cols
298
290
  end
299
291
  i += char_len
300
- else
292
+ else # rubocop:disable Lint/DuplicateBranch
301
293
  i += 1
302
294
  end
303
295
 
@@ -305,19 +297,19 @@ module TUITD
305
297
  region_top = scroll_region[:top]
306
298
  region_bottom = scroll_region[:bottom]
307
299
 
308
- if cursor[:row] > region_bottom
309
- scroll_lines = [cursor[:row] - region_bottom, rows].min
310
- # Shift lines within the scroll region up
311
- (region_top..(region_bottom - scroll_lines)).each do |ri|
312
- src = ri + scroll_lines
313
- grid[ri] = src <= region_bottom ? grid[src] : Array.new(cols) { default_cell.dup }
314
- end
315
- # Fill bottom of scroll region with blank lines
316
- ((region_bottom - scroll_lines + 1)..region_bottom).each do |ri|
317
- grid[ri] = Array.new(cols) { default_cell.dup }
318
- end
319
- cursor[:row] = region_bottom
300
+ next unless cursor[:row] > region_bottom
301
+
302
+ scroll_lines = [cursor[:row] - region_bottom, rows].min
303
+ # Shift lines within the scroll region up
304
+ (region_top..(region_bottom - scroll_lines)).each do |ri|
305
+ src = ri + scroll_lines
306
+ grid[ri] = src <= region_bottom ? grid[src] : Array.new(cols) { default_cell.dup }
320
307
  end
308
+ # Fill bottom of scroll region with blank lines
309
+ ((region_bottom - scroll_lines + 1)..region_bottom).each do |ri|
310
+ grid[ri] = Array.new(cols) { default_cell.dup }
311
+ end
312
+ cursor[:row] = region_bottom
321
313
  end
322
314
 
323
315
  {
@@ -326,21 +318,23 @@ module TUITD
326
318
  row: cursor[:row],
327
319
  col: cursor[:col],
328
320
  visible: cursor_visible,
329
- style: cursor_style
321
+ style: cursor_style,
330
322
  },
331
323
  rows: grid,
332
324
  pending_dsr: pending_dsr,
333
325
  cursor_visible: cursor_visible,
334
326
  cursor_style: cursor_style,
335
327
  mouse_mode: mouse_mode,
336
- mouse_format: mouse_format
328
+ mouse_format: mouse_format,
337
329
  }
338
330
  end
331
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockNesting
339
332
 
340
333
  # Rebuild ANSI output from a state hash (for rendering/screenshot)
334
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
341
335
  def self.build_frame(state)
342
336
  rows = state.dig(:size, :rows) || state["size"]["rows"]
343
- cols = state.dig(:size, :cols) || state["size"]["cols"]
337
+ state.dig(:size, :cols) || state["size"]["cols"]
344
338
  grid = state[:rows] || state["rows"]
345
339
  cursor = state[:cursor] || state["cursor"]
346
340
  mouse_mode = state[:mouse_mode] || state["mouse_mode"] || :none
@@ -351,7 +345,7 @@ module TUITD
351
345
  out << "\e[2J\e[H"
352
346
 
353
347
  grid.each_with_index do |row, ri|
354
- row.each_with_index do |cell, ci|
348
+ row.each_with_index do |cell, _ci|
355
349
  char = cell[:char] || cell["char"] || " "
356
350
  fg = cell[:fg] || cell["fg"] || "default"
357
351
  bg = cell[:bg] || cell["bg"] || "default"
@@ -380,9 +374,7 @@ module TUITD
380
374
 
381
375
  # Reconstruct cursor visibility
382
376
  cursor_vis = true
383
- if cursor.is_a?(Hash)
384
- cursor_vis = cursor[:visible] != false && cursor["visible"] != false
385
- end
377
+ cursor_vis = cursor[:visible] != false && cursor["visible"] != false if cursor.is_a?(Hash)
386
378
  out << (cursor_vis ? "\e[?25h" : "\e[?25l")
387
379
 
388
380
  # Reconstruct cursor style
@@ -392,30 +384,33 @@ module TUITD
392
384
  end
393
385
 
394
386
  # Reconstruct mouse mode and format
395
- if mouse_mode == :normal
396
- out << "\e[?1000h"
397
- elsif mouse_mode == :drag
398
- out << "\e[?1002h"
399
- elsif mouse_mode == :all
400
- out << "\e[?1003h"
401
- else
402
- out << "\e[?1000l\e[?1002l\e[?1003l"
403
- end
404
-
405
- if mouse_format == :sgr
406
- out << "\e[?1006h"
407
- else
408
- out << "\e[?1006l"
409
- end
387
+ out << case mouse_mode
388
+ when :normal
389
+ "\e[?1000h"
390
+ when :drag
391
+ "\e[?1002h"
392
+ when :all
393
+ "\e[?1003h"
394
+ else
395
+ "\e[?1000l\e[?1002l\e[?1003l"
396
+ end
397
+
398
+ out << if mouse_format == :sgr
399
+ "\e[?1006h"
400
+ else
401
+ "\e[?1006l"
402
+ end
410
403
 
411
404
  out << "\e[0m"
412
405
  out
413
406
  end
407
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
414
408
 
409
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/ParameterLists
415
410
  def self._apply_csi(seq, cursor, attrs, grid, rows, cols, saved_cursor, scroll_region)
416
411
  # Strip leading escape char if present
417
412
  cleaned = seq.sub(/^\e/, "")
418
- match = cleaned.match(/^\[(\??)([\d;]*)( ?)([A-HJ-KP-SX@\`fhlmnRrsuq])$/)
413
+ match = cleaned.match(/^\[(\??)([\d;]*)( ?)([A-HJ-KP-SX@`fhlmnRrsuq])$/)
419
414
  return [false, nil, {}] unless match
420
415
 
421
416
  is_private = (match[1] == "?")
@@ -431,19 +426,19 @@ module TUITD
431
426
  _apply_sgr(params, attrs)
432
427
  when "A" # CUU — Cursor Up
433
428
  n = params[0] || 1
434
- n = 1 if n == 0
429
+ n = 1 if n.zero?
435
430
  cursor[:row] = [cursor[:row] - n, 0].max
436
431
  when "B" # CUD — Cursor Down
437
432
  n = params[0] || 1
438
- n = 1 if n == 0
433
+ n = 1 if n.zero?
439
434
  cursor[:row] = [cursor[:row] + n, rows - 1].min
440
435
  when "C" # CUF — Cursor Forward
441
436
  n = params[0] || 1
442
- n = 1 if n == 0
437
+ n = 1 if n.zero?
443
438
  cursor[:col] = [cursor[:col] + n, cols - 1].min
444
439
  when "D" # CUB — Cursor Back
445
440
  n = params[0] || 1
446
- n = 1 if n == 0
441
+ n = 1 if n.zero?
447
442
  cursor[:col] = [cursor[:col] - n, 0].max
448
443
  when "H", "f" # CUP — Cursor Position
449
444
  r = (params[0] || 1) - 1
@@ -474,6 +469,7 @@ module TUITD
474
469
  n = params[0] || 1
475
470
  n.times do |i|
476
471
  next unless cursor[:row] < rows && cursor[:col] + i < cols
472
+
477
473
  grid[cursor[:row]][cursor[:col] + i][:char] = " "
478
474
  end
479
475
  when "s" # DECSC — Save Cursor (CSI variant)
@@ -546,9 +542,14 @@ module TUITD
546
542
 
547
543
  [false, new_saved, action]
548
544
  end
545
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/ParameterLists
549
546
 
547
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
550
548
  def self._apply_sgr(params, attrs)
551
- return attrs.merge!(fg: "default", bg: "default", bold: false, italic: false, underline: false, blink: false) if params.empty? || params == [0]
549
+ if params.empty? || params == [0]
550
+ return attrs.merge!(fg: "default", bg: "default", bold: false, italic: false, underline: false,
551
+ blink: false,)
552
+ end
552
553
 
553
554
  i = 0
554
555
  while i < params.length
@@ -572,11 +573,9 @@ module TUITD
572
573
  attrs[:underline] = false
573
574
  when 25
574
575
  attrs[:blink] = false
575
- when 7
576
+ when 7, 27
576
577
  # Reverse — swap fg and bg
577
578
  attrs[:fg], attrs[:bg] = attrs[:bg], attrs[:fg]
578
- when 27
579
- attrs[:fg], attrs[:bg] = attrs[:bg], attrs[:fg]
580
579
  when 30..37
581
580
  attrs[:fg] = SGR_16_TO_NAME[p - 30] || "color#{p - 30}"
582
581
  when 38
@@ -586,8 +585,10 @@ module TUITD
586
585
  attrs[:fg] = "color#{color}"
587
586
  i += 2
588
587
  elsif params[i + 1] == 2
589
- r, g, b = params[i + 2], params[i + 3], params[i + 4]
590
- attrs[:fg] = format("#%02x%02x%02x", r, g, b)
588
+ r = params[i + 2]
589
+ g = params[i + 3]
590
+ b = params[i + 4]
591
+ attrs[:fg] = format("#%<r>02x%<g>02x%<b>02x", r: r, g: g, b: b)
591
592
  i += 4
592
593
  end
593
594
  when 39
@@ -601,8 +602,10 @@ module TUITD
601
602
  attrs[:bg] = "color#{color}"
602
603
  i += 2
603
604
  elsif params[i + 1] == 2
604
- r, g, b = params[i + 2], params[i + 3], params[i + 4]
605
- attrs[:bg] = format("#%02x%02x%02x", r, g, b)
605
+ r = params[i + 2]
606
+ g = params[i + 3]
607
+ b = params[i + 4]
608
+ attrs[:bg] = format("#%<r>02x%<g>02x%<b>02x", r: r, g: g, b: b)
606
609
  i += 4
607
610
  end
608
611
  when 49
@@ -615,6 +618,7 @@ module TUITD
615
618
  i += 1
616
619
  end
617
620
  end
621
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
618
622
 
619
623
  def self._erase_down(cursor, grid, rows, cols)
620
624
  r = cursor[:row]
@@ -654,7 +658,7 @@ module TUITD
654
658
  (c...cols).each { |ci| grid[r][ci][:char] = " " if r < grid.length }
655
659
  end
656
660
 
657
- def self._erase_line_left(cursor, grid, cols)
661
+ def self._erase_line_left(cursor, grid, _cols)
658
662
  r = cursor[:row]
659
663
  c = cursor[:col]
660
664
  (0..c).each { |ci| grid[r][ci][:char] = " " if r < grid.length }
@@ -665,23 +669,23 @@ module TUITD
665
669
  cols.times { |ci| grid[r][ci][:char] = " " if r < grid.length }
666
670
  end
667
671
 
672
+ # rubocop:disable Metrics/CyclomaticComplexity
668
673
  def self._color_code(name, prefix)
669
674
  case name
670
675
  when "default" then nil
671
676
  when /^#([0-9a-fA-F]{6})$/
672
- r = $1[0..1].to_i(16)
673
- g = $1[2..3].to_i(16)
674
- b = $1[4..5].to_i(16)
677
+ r = ::Regexp.last_match(1)[0..1].to_i(16)
678
+ g = ::Regexp.last_match(1)[2..3].to_i(16)
679
+ b = ::Regexp.last_match(1)[4..5].to_i(16)
675
680
  "#{prefix};2;#{r};#{g};#{b}"
676
681
  when /^(bright_)?(.+)$/
677
- base_name = $2
682
+ base_name = ::Regexp.last_match(2)
678
683
  index = SGR_16_TO_NAME.key(base_name)
679
- index += 8 if $1 && index && index < 8
684
+ index += 8 if ::Regexp.last_match(1) && index && index < 8
680
685
  index ? "#{prefix};5;#{index}" : nil
681
- else
682
- nil
683
686
  end
684
687
  end
688
+ # rubocop:enable Metrics/CyclomaticComplexity
685
689
 
686
690
  def self.default_cell
687
691
  { char: " ", fg: "default", bg: "default", bold: false, italic: false, underline: false, blink: false }
@@ -689,26 +693,28 @@ module TUITD
689
693
 
690
694
  # Extract a single UTF-8 character at position i in a binary string.
691
695
  # Returns [char_string, byte_length] or nil if the byte is not printable/valid.
696
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Naming/MethodParameterName
692
697
  def self._utf8_char_at(str, i)
693
698
  byte = str.getbyte(i)
694
699
  return nil unless byte
695
700
 
696
701
  if byte < 0x80
697
702
  # Single-byte ASCII
698
- return nil unless byte >= 0x20 # only printable, skip control chars
703
+ return nil unless byte >= 0x20 # only printable, skip control chars
704
+
699
705
  return [byte.chr, 1]
700
706
  end
701
707
 
702
708
  # Multi-byte UTF-8
703
709
  len = if byte & 0xE0 == 0xC0
704
710
  2
705
- elsif byte & 0xF0 == 0xE0
711
+ elsif byte & 0xF0 == 0xE0
706
712
  3
707
- elsif byte & 0xF8 == 0xF0
713
+ elsif byte & 0xF8 == 0xF0
708
714
  4
709
- else
710
- return nil # continuation byte or invalid — let main loop advance
711
- end
715
+ else
716
+ return nil # continuation byte or invalid — let main loop advance
717
+ end
712
718
  return nil if i + len > str.bytesize
713
719
 
714
720
  bytes = str.byteslice(i, len)
@@ -719,5 +725,7 @@ module TUITD
719
725
  rescue StandardError
720
726
  nil
721
727
  end
728
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Naming/MethodParameterName
722
729
  end
730
+ # rubocop:enable Metrics/ModuleLength
723
731
  end
@@ -5,22 +5,22 @@ module TUITD
5
5
  # Used by Screenshot, HtmlRenderer, and other color-aware renderers.
6
6
  module ANSIUtils
7
7
  ANSI_RGB = {
8
- "black" => [0x00, 0x00, 0x00],
9
- "red" => [0xAA, 0x00, 0x00],
10
- "green" => [0x00, 0xAA, 0x00],
11
- "yellow" => [0xAA, 0x55, 0x00],
12
- "blue" => [0x00, 0x00, 0xAA],
13
- "magenta" => [0xAA, 0x00, 0xAA],
14
- "cyan" => [0x00, 0xAA, 0xAA],
15
- "white" => [0xAA, 0xAA, 0xAA],
16
- "bright_black" => [0x55, 0x55, 0x55],
17
- "bright_red" => [0xFF, 0x55, 0x55],
18
- "bright_green" => [0x55, 0xFF, 0x55],
8
+ "black" => [0x00, 0x00, 0x00],
9
+ "red" => [0xAA, 0x00, 0x00],
10
+ "green" => [0x00, 0xAA, 0x00],
11
+ "yellow" => [0xAA, 0x55, 0x00],
12
+ "blue" => [0x00, 0x00, 0xAA],
13
+ "magenta" => [0xAA, 0x00, 0xAA],
14
+ "cyan" => [0x00, 0xAA, 0xAA],
15
+ "white" => [0xAA, 0xAA, 0xAA],
16
+ "bright_black" => [0x55, 0x55, 0x55],
17
+ "bright_red" => [0xFF, 0x55, 0x55],
18
+ "bright_green" => [0x55, 0xFF, 0x55],
19
19
  "bright_yellow" => [0xFF, 0xFF, 0x55],
20
- "bright_blue" => [0x55, 0x55, 0xFF],
21
- "bright_magenta"=> [0xFF, 0x55, 0xFF],
22
- "bright_cyan" => [0x55, 0xFF, 0xFF],
23
- "bright_white" => [0xFF, 0xFF, 0xFF],
20
+ "bright_blue" => [0x55, 0x55, 0xFF],
21
+ "bright_magenta" => [0xFF, 0x55, 0xFF],
22
+ "bright_cyan" => [0x55, 0xFF, 0xFF],
23
+ "bright_white" => [0xFF, 0xFF, 0xFF],
24
24
  }.freeze
25
25
 
26
26
  CUBE = [0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF].freeze
@@ -39,17 +39,18 @@ module TUITD
39
39
  when "default"
40
40
  fallback
41
41
  when /^#([0-9a-fA-F]{6})$/
42
- [$1[0..1].to_i(16), $1[2..3].to_i(16), $1[4..5].to_i(16)]
42
+ [::Regexp.last_match(1)[0..1].to_i(16), ::Regexp.last_match(1)[2..3].to_i(16),
43
+ ::Regexp.last_match(1)[4..5].to_i(16),]
43
44
  when /\Acolor(\d+)\z/
44
- xterm_256($1.to_i)
45
+ xterm_256(::Regexp.last_match(1).to_i)
45
46
  when /\Abright_(.+)\z/
46
47
  ANSI_RGB[name] || fallback
47
- else
48
+ else # rubocop:disable Lint/DuplicateBranch
48
49
  ANSI_RGB[name] || fallback
49
50
  end
50
51
  end
51
52
 
52
- def xterm_256(index)
53
+ def xterm_256(index) # rubocop:disable Naming/VariableNumber
53
54
  if index < 16
54
55
  name = ANSI_INDEX[index]
55
56
  ANSI_RGB[name] || DEFAULT_FG
@@ -59,7 +60,7 @@ module TUITD
59
60
  b = CUBE[(index - 16) % 6]
60
61
  [r, g, b]
61
62
  else
62
- v = 8 + (index - 232) * 10
63
+ v = 8 + ((index - 232) * 10)
63
64
  [v, v, v]
64
65
  end
65
66
  end
@@ -67,6 +68,7 @@ module TUITD
67
68
  def _dig(hash, *keys)
68
69
  keys.each do |k|
69
70
  return nil unless hash
71
+
70
72
  hash = hash[k] || hash[k.to_s]
71
73
  end
72
74
  hash
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/ParameterLists
4
+
3
5
  module TUITD
4
6
  module CairoRenderer
5
7
  CELL_W = 8
@@ -76,9 +78,9 @@ module TUITD
76
78
  CELL_W.times do |dx|
77
79
  sum = 0
78
80
  scale.times do |sy|
79
- row_off = (dy * scale + sy) * stride
81
+ row_off = ((dy * scale) + sy) * stride
80
82
  scale.times do |sx|
81
- sum += data.getbyte(row_off + (dx * scale + sx) * 4 + 3)
83
+ sum += data.getbyte(row_off + (((dx * scale) + sx) * 4) + 3)
82
84
  end
83
85
  end
84
86
  alpha_grid[dy][dx] = sum / scale_sq
@@ -107,3 +109,4 @@ module TUITD
107
109
  end
108
110
  end
109
111
  end
112
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/ParameterLists