rufio 0.40.0 → 0.41.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.
@@ -3,7 +3,7 @@
3
3
  require 'open3'
4
4
 
5
5
  module Rufio
6
- # バックグラウンドでシェルコマンドを実行するクラス
6
+ # バックグラウンドでシェルコマンドまたはRubyコードを実行するクラス
7
7
  class BackgroundCommandExecutor
8
8
  attr_reader :command_logger
9
9
 
@@ -13,11 +13,12 @@ module Rufio
13
13
  @command_logger = command_logger
14
14
  @thread = nil
15
15
  @command = nil
16
+ @command_type = nil # :shell または :ruby
16
17
  @completed = false
17
18
  @completion_message = nil
18
19
  end
19
20
 
20
- # コマンドを非同期で実行
21
+ # シェルコマンドを非同期で実行
21
22
  # @param command [String] 実行するコマンド
22
23
  # @return [Boolean] 実行を開始した場合はtrue、既に実行中の場合はfalse
23
24
  def execute_async(command)
@@ -25,6 +26,7 @@ module Rufio
25
26
  return false if running?
26
27
 
27
28
  @command = command
29
+ @command_type = :shell
28
30
  @completed = false
29
31
  @completion_message = nil
30
32
 
@@ -73,6 +75,54 @@ module Rufio
73
75
  true
74
76
  end
75
77
 
78
+ # Rubyコード(プラグインコマンド)を非同期で実行
79
+ # @param command_name [String] コマンド名(表示用)
80
+ # @param block [Proc] 実行するコードブロック
81
+ # @return [Boolean] 実行を開始した場合はtrue、既に実行中の場合はfalse
82
+ def execute_ruby_async(command_name, &block)
83
+ # 既に実行中の場合は新しいコマンドを開始しない
84
+ return false if running?
85
+
86
+ @command = command_name
87
+ @command_type = :ruby
88
+ @completed = false
89
+ @completion_message = nil
90
+
91
+ @thread = Thread.new do
92
+ begin
93
+ # Rubyコードを実行
94
+ result = block.call
95
+
96
+ # 結果をログに保存
97
+ output = result.to_s
98
+
99
+ @command_logger.log(
100
+ command_name,
101
+ output,
102
+ success: true,
103
+ error: nil
104
+ )
105
+
106
+ # 完了メッセージを生成
107
+ @completion_message = "✓ #{command_name} 完了"
108
+ @completed = true
109
+ rescue StandardError => e
110
+ # エラーが発生した場合もログに記録
111
+ @command_logger.log(
112
+ command_name,
113
+ "",
114
+ success: false,
115
+ error: e.message
116
+ )
117
+
118
+ @completion_message = "✗ #{command_name} エラー: #{e.message}"
119
+ @completed = true
120
+ end
121
+ end
122
+
123
+ true
124
+ end
125
+
76
126
  # コマンドが実行中かどうか
77
127
  # @return [Boolean] 実行中の場合はtrue
78
128
  def running?
@@ -85,6 +135,18 @@ module Rufio
85
135
  @completion_message
86
136
  end
87
137
 
138
+ # 現在実行中のコマンド名を取得
139
+ # @return [String, nil] コマンド名(実行中でない場合はnil)
140
+ def current_command
141
+ running? ? @command : nil
142
+ end
143
+
144
+ # コマンドタイプを取得
145
+ # @return [Symbol, nil] :shell または :ruby(実行中でない場合はnil)
146
+ def command_type
147
+ running? ? @command_type : nil
148
+ end
149
+
88
150
  private
89
151
 
90
152
  # コマンド文字列からコマンド名を抽出
@@ -43,7 +43,21 @@ module Rufio
43
43
  return "⚠️ コマンドが見つかりません: #{command_name}"
44
44
  end
45
45
 
46
- # コマンドを実行
46
+ # バックグラウンドエグゼキュータが利用可能な場合は非同期実行
47
+ if @background_executor
48
+ command_method = @commands[command_name][:method]
49
+ command_display_name = command_name.to_s
50
+
51
+ if @background_executor.execute_ruby_async(command_display_name) do
52
+ command_method.call
53
+ end
54
+ return "🔄 バックグラウンドで実行中: #{command_display_name}"
55
+ else
56
+ return "⚠️ 既にコマンドが実行中です"
57
+ end
58
+ end
59
+
60
+ # バックグラウンドエグゼキュータがない場合は同期実行
47
61
  begin
48
62
  command_method = @commands[command_name][:method]
49
63
  command_method.call
@@ -62,18 +62,27 @@ module Rufio
62
62
  truncated = false
63
63
  encoding = "UTF-8"
64
64
 
65
- File.open(file_path, "r:UTF-8") do |file|
65
+ # UTF-8で読み込み、不正なバイト列は置換文字に変換
66
+ File.open(file_path, "r:UTF-8:UTF-8", invalid: :replace, undef: :replace, replace: '�') do |file|
66
67
  file.each_line.with_index do |line, index|
67
68
  break if index >= max_lines
68
-
69
- # truncate too long lines
70
- if line.length > MAX_LINE_LENGTH
71
- line = line[0...MAX_LINE_LENGTH] + "..."
69
+
70
+ begin
71
+ # Ensure line is properly encoded as UTF-8
72
+ line = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
73
+
74
+ # truncate too long lines
75
+ if line.length > MAX_LINE_LENGTH
76
+ line = line[0...MAX_LINE_LENGTH] + "..."
77
+ end
78
+
79
+ lines << line.chomp
80
+ rescue EncodingError, ArgumentError => e
81
+ # If encoding fails, add placeholder
82
+ lines << "[encoding error in line #{index + 1}]"
72
83
  end
73
-
74
- lines << line.chomp
75
84
  end
76
-
85
+
77
86
  # check if there are more lines to read
78
87
  truncated = !file.eof?
79
88
  end
@@ -83,14 +92,22 @@ module Rufio
83
92
  truncated: truncated,
84
93
  encoding: encoding
85
94
  }
86
- rescue Encoding::InvalidByteSequenceError
95
+ rescue Encoding::InvalidByteSequenceError, Encoding::CompatibilityError
87
96
  # try Shift_JIS if UTF-8 fails
88
97
  begin
89
98
  lines = []
90
- File.open(file_path, "r:Shift_JIS:UTF-8") do |file|
99
+ File.open(file_path, "r:Shift_JIS:UTF-8", invalid: :replace, undef: :replace, replace: '�') do |file|
91
100
  file.each_line.with_index do |line, index|
92
101
  break if index >= max_lines
93
- lines << line.chomp
102
+
103
+ begin
104
+ # Ensure line is properly encoded as UTF-8
105
+ line = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
106
+ lines << line.chomp
107
+ rescue EncodingError, ArgumentError => e
108
+ # If encoding fails, add placeholder
109
+ lines << "[encoding error in line #{index + 1}]"
110
+ end
94
111
  end
95
112
  truncated = !file.eof?
96
113
  end
@@ -22,20 +22,33 @@ module Rufio
22
22
  # Render the screen with differential updates
23
23
  #
24
24
  # @param screen [Screen] The back buffer to render
25
+ # @return [Boolean] true if rendering was performed, false if skipped
25
26
  def render(screen)
27
+ # CPU最適化: Dirty rowsが空の場合は完全にスキップ
28
+ dirty = screen.dirty_rows
29
+ if dirty.empty?
30
+ return false
31
+ end
32
+
26
33
  # Phase1: Only process dirty rows (rows that have changed)
27
- screen.dirty_rows.each do |y|
34
+ rendered_count = 0
35
+ dirty.each do |y|
28
36
  line = screen.row(y)
29
37
  next if line == @front[y] # Skip if content is actually the same
30
38
 
31
39
  # Move cursor to line y (1-indexed) and output the line
32
40
  @output.print "\e[#{y + 1};1H#{line}"
33
41
  @front[y] = line
42
+ rendered_count += 1
34
43
  end
35
44
 
36
45
  # Phase1: Clear dirty tracking after rendering
37
46
  screen.clear_dirty
38
- @output.flush
47
+
48
+ # Only flush if we actually rendered something
49
+ @output.flush if rendered_count > 0
50
+
51
+ true
39
52
  end
40
53
 
41
54
  # Resize the front buffer
@@ -71,6 +71,10 @@ module Rufio
71
71
  @cached_bookmarks = nil
72
72
  @cached_bookmark_time = nil
73
73
  @bookmark_cache_ttl = 1.0 # 1秒間キャッシュ
74
+
75
+ # Command execution lamp (footer indicator)
76
+ @completion_lamp_message = nil
77
+ @completion_lamp_time = nil
74
78
  end
75
79
 
76
80
  def start(directory_listing, keybind_handler, file_preview, background_executor = nil)
@@ -159,75 +163,143 @@ module Rufio
159
163
  puts ConfigLoader.message('app.terminated')
160
164
  end
161
165
 
162
- # ゲームループパターンのmain_loop
166
+ # ゲームループパターンのmain_loop(CPU最適化版:フレームスキップ対応)
163
167
  # UPDATE → DRAW → RENDER → SLEEP のサイクル
168
+ # 変更がない場合は描画をスキップしてCPU使用率を削減
164
169
  def main_loop
165
- fps = 60
166
- interval = 1.0 / fps
170
+ # CPU最適化: 固定FPSをやめて、イベントドリブンに変更
171
+ # 最小スリープ時間(入力チェック間隔)
172
+ min_sleep_interval = 0.0333 # 30FPS(約33.33ms/フレーム)
173
+ check_interval = 0.1 # バックグラウンドタスクのチェック間隔
167
174
 
168
175
  # Phase 3: Screen/Rendererを初期化
169
176
  @screen = Screen.new(@screen_width, @screen_height)
170
177
  @renderer = Renderer.new(@screen_width, @screen_height)
171
178
 
179
+ # 初回描画
180
+ @screen.clear
181
+ draw_screen_to_buffer(@screen, nil, nil)
182
+ @renderer.render(@screen)
183
+
172
184
  last_notification_check = Time.now
185
+ last_lamp_check = Time.now
173
186
  notification_message = nil
174
187
  notification_time = nil
188
+ previous_notification = nil
189
+ previous_lamp_message = @completion_lamp_message
175
190
 
176
191
  # FPS計測用
177
192
  frame_times = []
178
193
  last_frame_time = Time.now
179
194
  current_fps = 0.0
195
+ last_fps_update = Time.now
196
+
197
+ # 再描画フラグ
198
+ needs_redraw = false
180
199
 
181
200
  while @running
182
201
  start = Time.now
183
202
 
203
+ # FPS計算(毎フレームで記録)- ループの最初で計測してsleep時間を含める
204
+ if @test_mode
205
+ frame_time = start - last_frame_time
206
+ last_frame_time = start
207
+ frame_times << frame_time
208
+ frame_times.shift if frame_times.size > 60 # 直近60フレームで平均
209
+
210
+ # FPS表示の更新は1秒ごと
211
+ if (start - last_fps_update) > 1.0
212
+ avg_frame_time = frame_times.sum / frame_times.size
213
+ current_fps = 1.0 / avg_frame_time if avg_frame_time > 0
214
+ last_fps_update = start
215
+ end
216
+
217
+ # test_modeでは毎フレーム描画してFPS計測の精度を上げる
218
+ needs_redraw = true
219
+ end
220
+
184
221
  # UPDATE phase - ノンブロッキング入力処理
185
- handle_input_nonblocking
222
+ # 入力があった場合は再描画が必要
223
+ had_input = handle_input_nonblocking
224
+ needs_redraw = true if had_input
186
225
 
187
- # バックグラウンドコマンドの完了チェック(0.5秒ごと)
188
- if @background_executor && (Time.now - last_notification_check) > 0.5
226
+ # バックグラウンドコマンドの完了チェック(0.1秒ごと)
227
+ if @background_executor && (start - last_notification_check) > check_interval
189
228
  if !@background_executor.running? && @background_executor.get_completion_message
190
- notification_message = @background_executor.get_completion_message
191
- notification_time = Time.now
229
+ completion_msg = @background_executor.get_completion_message
230
+ # 通知メッセージとして表示
231
+ notification_message = completion_msg
232
+ notification_time = start
233
+ # フッターのランプ表示用にも設定
234
+ @completion_lamp_message = completion_msg
235
+ @completion_lamp_time = start
192
236
  @background_executor.instance_variable_set(:@completion_message, nil) # メッセージをクリア
237
+ needs_redraw = true
193
238
  end
194
- last_notification_check = Time.now
239
+ last_notification_check = start
195
240
  end
196
241
 
197
- # FPS計算(移動平均)
198
- if @test_mode
199
- frame_time = Time.now - last_frame_time
200
- frame_times << frame_time
201
- frame_times.shift if frame_times.size > 60 # 直近60フレームで平均
202
- avg_frame_time = frame_times.sum / frame_times.size
203
- current_fps = 1.0 / avg_frame_time if avg_frame_time > 0
204
- last_frame_time = Time.now
242
+ # バックグラウンドコマンドの実行状態が変わった場合も再描画
243
+ if @background_executor
244
+ current_running = @background_executor.running?
245
+ if @last_bg_running != current_running
246
+ @last_bg_running = current_running
247
+ needs_redraw = true
248
+ end
205
249
  end
206
250
 
207
- # DRAW phase - Screenバッファに描画
208
- @screen.clear
209
- if notification_message && (Time.now - notification_time) < 3.0
210
- draw_screen_to_buffer(@screen, notification_message, current_fps)
211
- else
212
- notification_message = nil if notification_message
213
- draw_screen_to_buffer(@screen, nil, current_fps)
251
+ # 完了ランプの表示状態をチェック(0.5秒ごと)
252
+ if (start - last_lamp_check) > 0.5
253
+ current_lamp = @completion_lamp_message
254
+ if current_lamp != previous_lamp_message
255
+ previous_lamp_message = current_lamp
256
+ needs_redraw = true
257
+ end
258
+ # 完了ランプのタイムアウトチェック
259
+ if @completion_lamp_message && @completion_lamp_time && (start - @completion_lamp_time) >= 3.0
260
+ @completion_lamp_message = nil
261
+ needs_redraw = true
262
+ end
263
+ last_lamp_check = start
214
264
  end
215
265
 
216
- # RENDER phase - 差分レンダリング
217
- @renderer.render(@screen)
266
+ # 通知メッセージの変化をチェック
267
+ current_notification = notification_message && (start - notification_time) < 3.0 ? notification_message : nil
268
+ if current_notification != previous_notification
269
+ previous_notification = current_notification
270
+ notification_message = nil if current_notification.nil?
271
+ needs_redraw = true
272
+ end
273
+
274
+ # DRAW & RENDER phase - 変更があった場合のみ描画
275
+ if needs_redraw
276
+ # Screenバッファに描画(clearは呼ばない。必要な部分だけ更新)
277
+ if notification_message && (start - notification_time) < 3.0
278
+ draw_screen_to_buffer(@screen, notification_message, current_fps)
279
+ else
280
+ draw_screen_to_buffer(@screen, nil, current_fps)
281
+ end
282
+
283
+ # 差分レンダリング(dirty rowsのみ)
284
+ @renderer.render(@screen)
285
+
286
+ # 描画後にカーソルを画面外に移動
287
+ if !@command_mode_active
288
+ print "\e[#{@screen_height};#{@screen_width}H"
289
+ end
290
+
291
+ needs_redraw = false
292
+ end
218
293
 
219
294
  # コマンドモードがアクティブな場合はフローティングウィンドウを表示
220
295
  # Phase 4: 暫定的に直接描画(Screenバッファ外)
221
296
  if @command_mode_active
222
297
  @command_mode_ui.show_input_prompt(@command_input)
223
- else
224
- # カーソルを画面外に移動
225
- print "\e[#{@screen_height};#{@screen_width}H"
226
298
  end
227
299
 
228
- # SLEEP phase - FPS制御
300
+ # SLEEP phase - CPU使用率削減のため適切にスリープ
229
301
  elapsed = Time.now - start
230
- sleep_time = [interval - elapsed, 0].max
302
+ sleep_time = [min_sleep_interval - elapsed, 0].max
231
303
  sleep sleep_time if sleep_time > 0
232
304
  end
233
305
  end
@@ -829,13 +901,36 @@ module Rufio
829
901
  end
830
902
  bookmark_text = bookmark_parts.join(" ")
831
903
 
832
- # 右側の情報: FPS(test modeの時のみ)| ?:help
904
+ # 右側の情報: コマンド実行ランプ | FPS(test modeの時のみ)| ?:help
905
+ right_parts = []
906
+
907
+ # バックグラウンドコマンドの実行状態をランプで表示
908
+ if @background_executor
909
+ if @background_executor.running?
910
+ # 実行中ランプ(緑色の回転矢印)
911
+ command_name = @background_executor.current_command || "処理中"
912
+ right_parts << "\e[32m🔄\e[0m #{command_name}"
913
+ elsif @completion_lamp_message && @completion_lamp_time
914
+ # 完了ランプ(3秒間表示)
915
+ if (Time.now - @completion_lamp_time) < 3.0
916
+ right_parts << @completion_lamp_message
917
+ else
918
+ @completion_lamp_message = nil
919
+ @completion_lamp_time = nil
920
+ end
921
+ end
922
+ end
923
+
924
+ # FPS表示(test modeの時のみ)
833
925
  if @test_mode && fps
834
- right_info = "#{fps.round(1)} FPS | ?:help"
835
- else
836
- right_info = "?:help"
926
+ right_parts << "#{fps.round(1)} FPS"
837
927
  end
838
928
 
929
+ # ヘルプ表示
930
+ right_parts << "?:help"
931
+
932
+ right_info = right_parts.join(" | ")
933
+
839
934
  # ブックマーク一覧を利用可能な幅に収める
840
935
  available_width = @screen_width - right_info.length - 3
841
936
  if bookmark_text.length > available_width && available_width > 3
@@ -918,26 +1013,26 @@ module Rufio
918
1013
  # ノンブロッキング入力処理(ゲームループ用)
919
1014
  # IO.selectでタイムアウト付きで入力をチェック
920
1015
  def handle_input_nonblocking
921
- # 1msタイムアウトで入力待ち(60FPS = 16.67ms/frame)
922
- ready = IO.select([STDIN], nil, nil, 0.001)
923
- return unless ready
1016
+ # 0msタイムアウトで即座にチェック(30FPS = 33.33ms/frame)
1017
+ ready = IO.select([STDIN], nil, nil, 0)
1018
+ return false unless ready
924
1019
 
925
1020
  begin
926
1021
  # read_nonblockを使ってノンブロッキングで1文字読み取る
927
1022
  input = STDIN.read_nonblock(1)
928
1023
  rescue IO::WaitReadable, IO::EAGAINWaitReadable
929
1024
  # 入力が利用できない
930
- return
1025
+ return false
931
1026
  rescue Errno::ENOTTY, Errno::ENODEV
932
1027
  # ターミナルでない環境
933
- return
1028
+ return false
934
1029
  end
935
1030
 
936
1031
  # コマンドモードがアクティブな場合は、エスケープシーケンス処理をスキップ
937
1032
  # ESCキーをそのまま handle_command_input に渡す
938
1033
  if @command_mode_active
939
1034
  handle_command_input(input)
940
- return
1035
+ return true
941
1036
  end
942
1037
 
943
1038
  # 特殊キーの処理(エスケープシーケンス)(コマンドモード外のみ)
@@ -967,12 +1062,15 @@ module Rufio
967
1062
  end
968
1063
 
969
1064
  # キーバインドハンドラーに処理を委譲
970
- @keybind_handler.handle_key(input) if input
1065
+ result = @keybind_handler.handle_key(input) if input
971
1066
 
972
- # 終了処理(qキーのみ)
973
- if input == 'q'
1067
+ # 終了処理(qキーのみ、確認ダイアログの結果を確認)
1068
+ if input == 'q' && result == true
974
1069
  @running = false
975
1070
  end
1071
+
1072
+ # 入力があったことを返す
1073
+ true
976
1074
  end
977
1075
 
978
1076
  def handle_input
@@ -1028,10 +1126,10 @@ module Rufio
1028
1126
  end
1029
1127
 
1030
1128
  # キーバインドハンドラーに処理を委譲
1031
- _result = @keybind_handler.handle_key(input)
1129
+ result = @keybind_handler.handle_key(input)
1032
1130
 
1033
- # 終了処理(qキーのみ)
1034
- if input == 'q'
1131
+ # 終了処理(qキーのみ、確認ダイアログの結果を確認)
1132
+ if input == 'q' && result == true
1035
1133
  @running = false
1036
1134
  end
1037
1135
  end
@@ -126,8 +126,17 @@ module Rufio
126
126
 
127
127
  wrapped = []
128
128
  lines.each do |line|
129
- # Remove trailing whitespace
130
- line = line.rstrip
129
+ # Handle encoding errors: scrub invalid UTF-8 sequences
130
+ begin
131
+ # Force UTF-8 encoding and replace invalid bytes
132
+ line = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
133
+ # Remove trailing whitespace
134
+ line = line.rstrip
135
+ rescue EncodingError, ArgumentError => e
136
+ # If encoding fails completely, skip this line
137
+ wrapped << '[encoding error]'
138
+ next
139
+ end
131
140
 
132
141
  # If line is empty, keep it
133
142
  if line.empty?
@@ -136,31 +145,45 @@ module Rufio
136
145
  end
137
146
 
138
147
  # If line fits within max_width, keep it as is
139
- if display_width(line) <= max_width
140
- wrapped << line
141
- next
148
+ begin
149
+ if display_width(line) <= max_width
150
+ wrapped << line
151
+ next
152
+ end
153
+ rescue ArgumentError => e
154
+ # If display_width fails, just truncate by byte length
155
+ if line.bytesize <= max_width
156
+ wrapped << line
157
+ next
158
+ end
142
159
  end
143
160
 
144
161
  # Split long lines
145
162
  current_line = []
146
163
  current_width = 0
147
164
 
148
- line.each_char do |char|
149
- cw = char_width(char)
150
-
151
- if current_width + cw > max_width
152
- # Start a new line
153
- wrapped << current_line.join
154
- current_line = [char]
155
- current_width = cw
156
- else
157
- current_line << char
158
- current_width += cw
165
+ begin
166
+ line.each_char do |char|
167
+ cw = char_width(char)
168
+
169
+ if current_width + cw > max_width
170
+ # Start a new line
171
+ wrapped << current_line.join
172
+ current_line = [char]
173
+ current_width = cw
174
+ else
175
+ current_line << char
176
+ current_width += cw
177
+ end
159
178
  end
160
- end
161
179
 
162
- # Add remaining characters
163
- wrapped << current_line.join unless current_line.empty?
180
+ # Add remaining characters
181
+ wrapped << current_line.join unless current_line.empty?
182
+ rescue ArgumentError, EncodingError => e
183
+ # If character iteration fails, just add the line truncated
184
+ truncated = line.byteslice(0, [max_width, line.bytesize].min)
185
+ wrapped << (truncated || line)
186
+ end
164
187
  end
165
188
 
166
189
  wrapped
data/lib/rufio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rufio
4
- VERSION = '0.40.0'
4
+ VERSION = '0.41.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rufio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.40.0
4
+ version: 0.41.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - masisz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-11 00:00:00.000000000 Z
11
+ date: 2026-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -132,6 +132,7 @@ files:
132
132
  - docs/CHANGELOG_v0.33.0.md
133
133
  - docs/CHANGELOG_v0.4.0.md
134
134
  - docs/CHANGELOG_v0.40.0.md
135
+ - docs/CHANGELOG_v0.41.0.md
135
136
  - docs/CHANGELOG_v0.5.0.md
136
137
  - docs/CHANGELOG_v0.6.0.md
137
138
  - docs/CHANGELOG_v0.7.0.md
@@ -192,7 +193,6 @@ files:
192
193
  - lib_zig/rufio_native/build.zig
193
194
  - lib_zig/rufio_native/src/main.zig
194
195
  - lib_zig/rufio_native/src/main.zig.sync
195
- - publish_gem.zsh
196
196
  - retag.sh
197
197
  - test_delete/test1.txt
198
198
  - test_delete/test2.txt