rufio 0.33.0 → 0.40.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/CHANGELOG.md +103 -7
- data/bin/rufio +17 -2
- data/docs/CHANGELOG_v0.33.0.md +2 -2
- data/docs/CHANGELOG_v0.40.0.md +416 -0
- data/lib/rufio/application.rb +2 -2
- data/lib/rufio/async_scanner_fiber.rb +154 -0
- data/lib/rufio/async_scanner_promise.rb +66 -0
- data/lib/rufio/color_helper.rb +59 -6
- data/lib/rufio/command_logger.rb +3 -0
- data/lib/rufio/command_mode_ui.rb +18 -0
- data/lib/rufio/dialog_renderer.rb +68 -0
- data/lib/rufio/keybind_handler.rb +53 -2
- data/lib/rufio/native/rufio_zig.bundle +0 -0
- data/lib/rufio/native_scanner.rb +252 -233
- data/lib/rufio/native_scanner_zig.rb +215 -82
- data/lib/rufio/parallel_scanner.rb +173 -0
- data/lib/rufio/plugins/stop.rb +32 -0
- data/lib/rufio/renderer.rb +64 -0
- data/lib/rufio/screen.rb +184 -0
- data/lib/rufio/terminal_ui.rb +557 -34
- data/lib/rufio/text_utils.rb +30 -18
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +5 -1
- data/lib_zig/rufio_native/Makefile +2 -1
- data/lib_zig/rufio_native/src/main.zig +328 -117
- data/lib_zig/rufio_native/src/main.zig.sync +205 -0
- metadata +10 -9
- data/lib/rufio/native/rufio_native.bundle +0 -0
- data/lib/rufio/native_scanner_magnus.rb +0 -194
- data/lib_rust/rufio_native/.cargo/config.toml +0 -2
- data/lib_rust/rufio_native/Cargo.lock +0 -346
- data/lib_rust/rufio_native/Cargo.toml +0 -18
- data/lib_rust/rufio_native/build.rs +0 -46
- data/lib_rust/rufio_native/src/lib.rs +0 -197
data/lib/rufio/screen.rb
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Rufio
|
|
6
|
+
# Screen class - Back buffer for double buffering
|
|
7
|
+
#
|
|
8
|
+
# Manages a virtual screen buffer where each cell contains:
|
|
9
|
+
# - Character
|
|
10
|
+
# - Foreground color (ANSI code)
|
|
11
|
+
# - Background color (ANSI code)
|
|
12
|
+
# - Display width (for multibyte characters)
|
|
13
|
+
#
|
|
14
|
+
# Supports:
|
|
15
|
+
# - ASCII characters (width = 1)
|
|
16
|
+
# - Full-width characters (width = 2, e.g., Japanese, Chinese)
|
|
17
|
+
# - Emoji (width = 2+)
|
|
18
|
+
#
|
|
19
|
+
# Phase1 Optimizations:
|
|
20
|
+
# - Width pre-calculation (computed once in put method)
|
|
21
|
+
# - Dirty row tracking (only render changed rows)
|
|
22
|
+
# - Optimized format_cell (String.new with capacity)
|
|
23
|
+
# - Optimized row generation (width accumulation, no ANSI strip)
|
|
24
|
+
# - Minimal ANSI stripping (only once in put_string)
|
|
25
|
+
#
|
|
26
|
+
class Screen
|
|
27
|
+
attr_reader :width, :height
|
|
28
|
+
|
|
29
|
+
def initialize(width, height)
|
|
30
|
+
@width = width
|
|
31
|
+
@height = height
|
|
32
|
+
@cells = Array.new(height) { Array.new(width) { default_cell } }
|
|
33
|
+
@dirty_rows = Set.new # Phase1: Dirty row tracking
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Put a single character at (x, y) with optional color
|
|
37
|
+
#
|
|
38
|
+
# @param x [Integer] X position (0-indexed)
|
|
39
|
+
# @param y [Integer] Y position (0-indexed)
|
|
40
|
+
# @param char [String] Character to put
|
|
41
|
+
# @param fg [String, nil] Foreground ANSI color code
|
|
42
|
+
# @param bg [String, nil] Background ANSI color code
|
|
43
|
+
# @param width [Integer, nil] Display width (auto-detected if not provided)
|
|
44
|
+
def put(x, y, char, fg: nil, bg: nil, width: nil)
|
|
45
|
+
return if out_of_bounds?(x, y)
|
|
46
|
+
|
|
47
|
+
# Phase1: Width is calculated once here (not in rendering loop)
|
|
48
|
+
char_width = width || TextUtils.display_width(char)
|
|
49
|
+
@cells[y][x] = {
|
|
50
|
+
char: char,
|
|
51
|
+
fg: fg,
|
|
52
|
+
bg: bg,
|
|
53
|
+
width: char_width
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Phase1: Mark row as dirty
|
|
57
|
+
@dirty_rows.add(y)
|
|
58
|
+
|
|
59
|
+
# For full-width characters, mark the next cell as occupied
|
|
60
|
+
if char_width >= 2 && x + 1 < @width
|
|
61
|
+
(char_width - 1).times do |offset|
|
|
62
|
+
next_x = x + 1 + offset
|
|
63
|
+
break if next_x >= @width
|
|
64
|
+
@cells[y][next_x] = {
|
|
65
|
+
char: '',
|
|
66
|
+
fg: nil,
|
|
67
|
+
bg: nil,
|
|
68
|
+
width: 0
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Put a string starting at (x, y)
|
|
75
|
+
#
|
|
76
|
+
# @param x [Integer] Starting X position
|
|
77
|
+
# @param y [Integer] Y position
|
|
78
|
+
# @param str [String] String to put (ANSI codes will be stripped)
|
|
79
|
+
# @param fg [String, nil] Foreground ANSI color code
|
|
80
|
+
# @param bg [String, nil] Background ANSI color code
|
|
81
|
+
def put_string(x, y, str, fg: nil, bg: nil)
|
|
82
|
+
return if out_of_bounds?(x, y)
|
|
83
|
+
|
|
84
|
+
# Phase1: ANSI stripping only once (minimal processing)
|
|
85
|
+
# Only strip if the string contains ANSI codes
|
|
86
|
+
clean_str = str.include?("\e") ? ColorHelper.strip_ansi(str) : str
|
|
87
|
+
|
|
88
|
+
current_x = x
|
|
89
|
+
clean_str.each_char do |char|
|
|
90
|
+
break if current_x >= @width
|
|
91
|
+
|
|
92
|
+
char_width = TextUtils.display_width(char)
|
|
93
|
+
put(current_x, y, char, fg: fg, bg: bg, width: char_width)
|
|
94
|
+
current_x += char_width
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Get the cell at (x, y)
|
|
99
|
+
#
|
|
100
|
+
# @param x [Integer] X position
|
|
101
|
+
# @param y [Integer] Y position
|
|
102
|
+
# @return [Hash] Cell data {char:, fg:, bg:, width:}
|
|
103
|
+
def get_cell(x, y)
|
|
104
|
+
return default_cell if out_of_bounds?(x, y)
|
|
105
|
+
@cells[y][x]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Get a row as a formatted string
|
|
109
|
+
#
|
|
110
|
+
# @param y [Integer] Row number
|
|
111
|
+
# @return [String] Formatted row with ANSI codes
|
|
112
|
+
def row(y)
|
|
113
|
+
return " " * @width if y < 0 || y >= @height
|
|
114
|
+
|
|
115
|
+
# Phase1: Pre-allocate string capacity for better performance
|
|
116
|
+
result = String.new(capacity: @width * 20)
|
|
117
|
+
current_width = 0 # Phase1: Accumulate width from cells (no recalculation)
|
|
118
|
+
|
|
119
|
+
@cells[y].each do |cell|
|
|
120
|
+
# Skip marker cells for full-width characters
|
|
121
|
+
next if cell[:width] == 0
|
|
122
|
+
|
|
123
|
+
result << format_cell(cell)
|
|
124
|
+
current_width += cell[:width] # Phase1: Use pre-calculated width
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Pad the row to full width
|
|
128
|
+
# Phase1: No ANSI stripping or width recalculation needed
|
|
129
|
+
if current_width < @width
|
|
130
|
+
result << (" " * (@width - current_width))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
result
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Clear the entire screen
|
|
137
|
+
def clear
|
|
138
|
+
@cells.each do |row|
|
|
139
|
+
row.fill { default_cell }
|
|
140
|
+
end
|
|
141
|
+
# Phase1: Clear dirty rows after full clear
|
|
142
|
+
@dirty_rows.clear
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Phase1: Get dirty rows (rows that have been modified since last clear)
|
|
146
|
+
#
|
|
147
|
+
# @return [Array<Integer>] Array of dirty row indices
|
|
148
|
+
def dirty_rows
|
|
149
|
+
@dirty_rows.to_a
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Phase1: Clear dirty row tracking
|
|
153
|
+
def clear_dirty
|
|
154
|
+
@dirty_rows.clear
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def default_cell
|
|
160
|
+
{ char: ' ', fg: nil, bg: nil, width: 1 }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def out_of_bounds?(x, y)
|
|
164
|
+
x < 0 || y < 0 || x >= @width || y >= @height
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def format_cell(cell)
|
|
168
|
+
char = cell[:char]
|
|
169
|
+
fg = cell[:fg]
|
|
170
|
+
bg = cell[:bg]
|
|
171
|
+
|
|
172
|
+
# Phase1: Fast path for cells without color
|
|
173
|
+
return char if fg.nil? && bg.nil?
|
|
174
|
+
|
|
175
|
+
# Phase1: String builder with pre-allocated capacity (no array generation)
|
|
176
|
+
result = String.new(capacity: 30)
|
|
177
|
+
result << fg if fg
|
|
178
|
+
result << bg if bg
|
|
179
|
+
result << char
|
|
180
|
+
result << "\e[0m"
|
|
181
|
+
result
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|