rufio 1.1.0 → 1.4.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 +50 -0
- data/README.md +26 -297
- data/README_ja.md +24 -261
- data/Rakefile +14 -1
- data/bin/rufio +31 -2
- data/docs/fast_zip_design_ja.md +167 -0
- data/docs/guide.md +410 -0
- data/docs/guide_ja.md +410 -0
- data/info/help.md +54 -17
- data/info/keybindings.md +50 -22
- data/info/welcome.md +14 -5
- data/lib/rufio/application.rb +12 -1
- data/lib/rufio/archive_controller.rb +211 -0
- data/lib/rufio/background_command_executor.rb +9 -0
- data/lib/rufio/background_status.rb +49 -0
- data/lib/rufio/bookmark_controller.rb +38 -121
- data/lib/rufio/bookmark_manager.rb +77 -331
- data/lib/rufio/command_completion.rb +28 -2
- data/lib/rufio/command_history.rb +5 -2
- data/lib/rufio/command_mode.rb +60 -134
- data/lib/rufio/command_mode_ui.rb +4 -42
- data/lib/rufio/config.rb +88 -136
- data/lib/rufio/config_loader.rb +4 -11
- data/lib/rufio/dialog_renderer.rb +90 -152
- data/lib/rufio/dialog_support.rb +69 -0
- data/lib/rufio/document_text_extractor.rb +167 -0
- data/lib/rufio/file_operation_controller.rb +49 -222
- data/lib/rufio/file_operations.rb +28 -6
- data/lib/rufio/file_preview.rb +46 -2
- data/lib/rufio/global_search.rb +125 -13
- data/lib/rufio/help_menu.rb +19 -7
- data/lib/rufio/interpreter_resolver.rb +6 -11
- data/lib/rufio/job_manager.rb +2 -5
- data/lib/rufio/key_input.rb +77 -0
- data/lib/rufio/keybind_handler.rb +83 -116
- data/lib/rufio/local_script_scanner.rb +6 -18
- data/lib/rufio/native/aarch64-linux-gnu/librufio_zig.so +0 -0
- data/lib/rufio/native/aarch64-linux-musl/librufio_zig.so +0 -0
- data/lib/rufio/native/aarch64-macos/librufio_zig.dylib +0 -0
- data/lib/rufio/native/aarch64-windows/rufio_zig.dll +0 -0
- data/lib/rufio/native/x86_64-linux-gnu/librufio_zig.so +0 -0
- data/lib/rufio/native/x86_64-linux-musl/librufio_zig.so +0 -0
- data/lib/rufio/native/x86_64-macos/librufio_zig.dylib +0 -0
- data/lib/rufio/native/x86_64-windows/rufio_zig.dll +0 -0
- data/lib/rufio/native_content_search.rb +153 -0
- data/lib/rufio/native_scanner.rb +13 -68
- data/lib/rufio/native_scanner_zig.rb +106 -56
- data/lib/rufio/native_zip.rb +121 -0
- data/lib/rufio/navigation_controller.rb +20 -26
- data/lib/rufio/path_jump.rb +55 -0
- data/lib/rufio/screen.rb +2 -12
- data/lib/rufio/script_executor.rb +6 -73
- data/lib/rufio/script_file.rb +44 -0
- data/lib/rufio/script_path_manager.rb +9 -43
- data/lib/rufio/script_runner.rb +13 -50
- data/lib/rufio/search_controller.rb +1 -1
- data/lib/rufio/shell_command_completion.rb +1 -5
- data/lib/rufio/shell_init.rb +90 -0
- data/lib/rufio/task_status.rb +5 -1
- data/lib/rufio/terminal_ui.rb +84 -298
- data/lib/rufio/text_utils.rb +26 -3
- data/lib/rufio/ui_renderer.rb +73 -45
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio/zip_archive.rb +314 -0
- data/lib/rufio/zip_extractor.rb +346 -0
- data/lib/rufio/zip_writer.rb +268 -0
- data/lib/rufio/zoxide_integration.rb +5 -43
- data/lib/rufio.rb +6 -0
- data/lib_zig/rufio_native/Makefile +54 -17
- data/lib_zig/rufio_native/bench.sh +36 -0
- data/lib_zig/rufio_native/build.zig +32 -35
- data/lib_zig/rufio_native/src/cli.zig +99 -0
- data/lib_zig/rufio_native/src/main.zig +187 -83
- data/lib_zig/rufio_native/src/os.zig +889 -0
- data/lib_zig/rufio_native/src/search.zig +919 -0
- data/lib_zig/rufio_native/src/search_test.zig +88 -0
- data/lib_zig/rufio_native/src/zip.zig +482 -0
- metadata +128 -3
- data/lib/rufio/native/rufio_zig.bundle +0 -0
data/README_ja.md
CHANGED
|
@@ -32,8 +32,10 @@ rufio は、ファイルを探すところからスクリプトの実行、結
|
|
|
32
32
|
### ファイルマネージャーとして
|
|
33
33
|
|
|
34
34
|
- **Vimライクなキーバインド**: 直感的なナビゲーション
|
|
35
|
-
- **リアルタイムプレビュー**: ファイル内容を即座に表示(`bat`
|
|
36
|
-
- **高速検索**: fzf/rga
|
|
35
|
+
- **リアルタイムプレビュー**: ファイル内容を即座に表示(`bat` によるシンタックスハイライト対応、`.zip` はエントリ一覧)
|
|
36
|
+
- **高速検索**: fzf/rgaとの連携に加え、PDF・Excel・PowerPointも読める全ブックマーク横断検索を内蔵
|
|
37
|
+
- **ZIPの展開・圧縮**: 選択したアイテムをバックグラウンドで展開・圧縮
|
|
38
|
+
- **パスへ移動**: コピーしたパスを `:cd` に貼り付けて移動
|
|
37
39
|
- **ブックマーク**: よく使うディレクトリに素早くアクセス
|
|
38
40
|
- **zoxide連携**: スマートなディレクトリ履歴
|
|
39
41
|
|
|
@@ -78,277 +80,38 @@ rufio /path/to # 指定したディレクトリで起動
|
|
|
78
80
|
|
|
79
81
|
## キーバインド
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
よく使うキーです。すべてのキーとモードは **[操作ガイド](./docs/guide_ja.md)** を参照するか、rufio の中で `?` を押してください。
|
|
82
84
|
|
|
83
|
-
| キー |
|
|
85
|
+
| キー | 操作 |
|
|
84
86
|
|------|------|
|
|
85
|
-
| `j/k` | 上下移動 |
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
|
94
|
-
|
|
95
|
-
|
|
|
96
|
-
| `
|
|
97
|
-
| `a/A` | ファイル/ディレクトリ作成 |
|
|
98
|
-
| `r` | リネーム |
|
|
99
|
-
| `d` | 削除 |
|
|
100
|
-
| `m/c/x` | 移動/コピー/削除(選択済み) |
|
|
101
|
-
|
|
102
|
-
### 検索・フィルター
|
|
103
|
-
|
|
104
|
-
| キー | 機能 |
|
|
105
|
-
|------|------|
|
|
106
|
-
| `f` | フィルターモード |
|
|
107
|
-
| `s` | fzfでファイル検索 |
|
|
108
|
-
| `F` | rgaでファイル内容検索 |
|
|
109
|
-
|
|
110
|
-
### ナビゲーション
|
|
111
|
-
|
|
112
|
-
| キー | 機能 |
|
|
113
|
-
|------|------|
|
|
114
|
-
| `b` | ブックマーク追加 |
|
|
115
|
-
| `B` | ブックマークメニュー |
|
|
116
|
-
| `0` | 起動ディレクトリに戻る |
|
|
117
|
-
| `1-9` | ブックマークにジャンプ |
|
|
118
|
-
| `Tab` | 次のブックマークへ循環移動(Filesモード限定) |
|
|
119
|
-
| `z` | zoxide履歴 |
|
|
120
|
-
|
|
121
|
-
### ツールランタイム
|
|
122
|
-
|
|
123
|
-
| キー | 機能 |
|
|
124
|
-
|------|------|
|
|
125
|
-
| `:` | コマンドモード |
|
|
126
|
-
| `J` | ジョブモード |
|
|
127
|
-
| `L` | 実行ログ表示 |
|
|
87
|
+
| `j/k`、`h/l` | 上下移動 / 親ディレクトリ / ディレクトリに入る |
|
|
88
|
+
| `g/}` | 先頭 / 末尾 |
|
|
89
|
+
| `P` | 全画面プレビュー |
|
|
90
|
+
| `Space` | 選択 |
|
|
91
|
+
| `m/c/x` | 選択したアイテムの移動 / コピー / 削除 |
|
|
92
|
+
| `z` | 選択したアイテムの展開・圧縮(`.zip`)。未選択時は zoxide 履歴 |
|
|
93
|
+
| `a/A`、`r`、`d` | ファイル / ディレクトリ作成、リネーム、削除 |
|
|
94
|
+
| `f`、`s`、`F` | フィルター、fzf でファイル名検索、rga で内容検索 |
|
|
95
|
+
| `G` | 全ブックマーク配下のファイル名・内容(PDF・Excel・PowerPoint も)を横断検索 |
|
|
96
|
+
| `b`、`B`、`1-9`、`Tab` | ブックマーク追加、メニュー、ジャンプ、次のブックマーク |
|
|
97
|
+
| `:` | コマンドモード(`@スクリプト`、`rake:タスク`、`!シェル`、`cd <パス>`) |
|
|
98
|
+
| `J`、`L`、`C` | ジョブ、ログ、設定ディレクトリ |
|
|
128
99
|
| `?` | ヘルプ |
|
|
129
|
-
| `
|
|
130
|
-
| `C` | ~/.config/rufio/を開く(Configモード、操作はFilesと共通) |
|
|
131
|
-
| `Shift+Tab` | モード切り替え(逆順) |
|
|
132
|
-
|
|
133
|
-
### Gモード(横断検索)
|
|
134
|
-
|
|
135
|
-
`G` を押すと、ブックマークした全ディレクトリの配下を再帰的に検索します。
|
|
136
|
-
Tabで **File names(ファイル名)** と **Content(ファイル内容)** を切り替えます。
|
|
137
|
-
大文字・小文字を区別しない文字列の部分一致で検索し、内容検索ではファイル名・行番号・一致した行を表示します。
|
|
138
|
-
テキストはUTF-8として読み込み、先頭8 KBにNULバイトがあるファイルはバイナリとしてスキップします。
|
|
139
|
-
上下矢印または `Ctrl+N/P` で選択、
|
|
140
|
-
Enterで選んだファイルの場所へ移動します。`Ctrl+U` で検索語をクリアし、
|
|
141
|
-
Escで元の表示に戻ります。検索はバックグラウンドで進みます。
|
|
142
|
-
検索対象は `b` で登録したブックマークです。外部検索ツールは不要です。
|
|
143
|
-
アクセスできないディレクトリはスキップし、ディレクトリのシンボリックリンクは辿りません。
|
|
144
|
-
検索中は黄色の `Searching...` と動くスピナーを表示します。
|
|
145
|
-
結果がまだ見つかっていない間も表示が動き、完了すると緑の `Search complete` に切り替わります。
|
|
146
|
-
|
|
147
|
-
ファイル名検索・内容検索から特定の拡張子を除外するには、
|
|
148
|
-
`~/.config/rufio/config.rb` に以下を追加してRufioを再起動します。
|
|
149
|
-
|
|
150
|
-
```ruby
|
|
151
|
-
GLOBAL_SEARCH_EXCLUDED_EXTENSIONS = %w[.png .jpg .zip .tar.gz .log].freeze
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
大文字・小文字は区別せず、先頭のドットは省略できます。デフォルトは `[]`(拡張子の除外なし)です。
|
|
155
|
-
ディレクトリ名には適用しません。
|
|
156
|
-
|
|
157
|
-
### Configモード(C)
|
|
158
|
-
|
|
159
|
-
`C` で `~/.config/rufio/` を開きます。ディレクトリがなければ作成します。
|
|
160
|
-
移動・プレビュー・ファイル操作はFilesモードと共通で、設定やスクリプトを直接編集できます。
|
|
161
|
-
設定ディレクトリの外へ移動すると、モード表示がFilesに戻ります。
|
|
162
|
-
|
|
163
|
-
## コマンドモード
|
|
164
|
-
|
|
165
|
-
`:` でコマンドモードを起動し、様々なコマンドを実行できます。
|
|
166
|
-
|
|
167
|
-
### スクリプト実行
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
:@build # @で始まるとスクリプト補完
|
|
171
|
-
:@deploy.rb # 登録済みスクリプトを実行
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### シェルコマンド
|
|
175
|
-
|
|
176
|
-
```
|
|
177
|
-
:!git status # !で始まるとシェルコマンド
|
|
178
|
-
:!ls -la # バックグラウンドで実行
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### 組み込みコマンド
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
:hello # 挨拶メッセージ
|
|
185
|
-
:stop # rufioを終了
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## スクリプトパス
|
|
189
|
-
|
|
190
|
-
### スクリプトパスとは
|
|
191
|
-
|
|
192
|
-
スクリプトファイルを配置するディレクトリを登録する機能です。登録したディレクトリ内のスクリプトは、コマンドモードで `@` プレフィックスを使って実行できます。
|
|
193
|
-
|
|
194
|
-
### 管理方法
|
|
195
|
-
|
|
196
|
-
`B` → `3` でスクリプトパス管理メニューを開きます:
|
|
197
|
-
|
|
198
|
-
- 登録済みパスの一覧表示
|
|
199
|
-
- `d`: パスを削除
|
|
200
|
-
- `Enter`: ディレクトリにジャンプ
|
|
201
|
-
- `ESC`: メニューを閉じる
|
|
202
|
-
|
|
203
|
-
### 対応スクリプト
|
|
204
|
-
|
|
205
|
-
| 拡張子 | 言語 |
|
|
206
|
-
|--------|------|
|
|
207
|
-
| `.rb` | Ruby |
|
|
208
|
-
| `.py` | Python |
|
|
209
|
-
| `.ps1` | PowerShell |
|
|
210
|
-
| `.sh` | Shell (bash/zsh) |
|
|
211
|
-
|
|
212
|
-
## DSLコマンド
|
|
213
|
-
|
|
214
|
-
`~/.config/rufio/commands.rb` でカスタムコマンドを定義できます:
|
|
215
|
-
|
|
216
|
-
```ruby
|
|
217
|
-
command "hello" do
|
|
218
|
-
ruby { "Hello from rufio!" }
|
|
219
|
-
description "挨拶コマンド"
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
command "status" do
|
|
223
|
-
shell "git status"
|
|
224
|
-
description "Gitステータス"
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
command "build" do
|
|
228
|
-
script "~/.config/rufio/scripts/build.rb"
|
|
229
|
-
description "ビルド実行"
|
|
230
|
-
end
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## 設定
|
|
234
|
-
|
|
235
|
-
### 設定ファイル構成
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
~/.config/rufio/
|
|
239
|
-
├── config.rb # メイン設定(スクリプトパス、カラー等)
|
|
240
|
-
├── commands.rb # DSLコマンド定義
|
|
241
|
-
├── bookmarks.json # ブックマーク
|
|
242
|
-
├── scripts/ # スクリプトファイル
|
|
243
|
-
└── logs/ # 実行ログ
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### 設定例
|
|
247
|
-
|
|
248
|
-
```ruby
|
|
249
|
-
# ~/.config/rufio/config.rb
|
|
250
|
-
|
|
251
|
-
# スクリプトパス - スクリプトを配置するディレクトリ
|
|
252
|
-
SCRIPT_PATHS = [
|
|
253
|
-
'~/.config/rufio/scripts',
|
|
254
|
-
'~/my-scripts',
|
|
255
|
-
'./scripts'
|
|
256
|
-
].freeze
|
|
257
|
-
|
|
258
|
-
# カラー設定(HSL形式推奨)
|
|
259
|
-
COLORS = {
|
|
260
|
-
directory: { hsl: [220, 80, 60] },
|
|
261
|
-
file: { hsl: [0, 0, 90] },
|
|
262
|
-
executable: { hsl: [120, 70, 50] },
|
|
263
|
-
selected: { hsl: [50, 90, 70] },
|
|
264
|
-
preview: { hsl: [180, 60, 65] }
|
|
265
|
-
}.freeze
|
|
266
|
-
```
|
|
100
|
+
| `q`、`T` | 終了、終了してシェルを現在のディレクトリへ移動 |
|
|
267
101
|
|
|
268
|
-
|
|
102
|
+
Gモードでは `Ctrl+J` / `Ctrl+K` で次/前の検索結果を選択し、`Enter` で開きます。
|
|
269
103
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
```ruby
|
|
273
|
-
# ./rufio.rb(プロジェクトルート)
|
|
274
|
-
SCRIPT_PATHS = [
|
|
275
|
-
'./scripts',
|
|
276
|
-
'./bin'
|
|
277
|
-
].freeze
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## 外部ツール連携
|
|
281
|
-
|
|
282
|
-
rufioは以下の外部ツールと連携して機能を拡張します。
|
|
283
|
-
すべて**オプション**です。インストールなしでも rufio は正常動作します。
|
|
284
|
-
|
|
285
|
-
| ツール | 用途 | キー | 必須 |
|
|
286
|
-
|--------|------|------|------|
|
|
287
|
-
| fzf | ファイル名検索 | `s` | オプション |
|
|
288
|
-
| rga | ファイル内容検索 | `F` | オプション |
|
|
289
|
-
| zoxide | ディレクトリ履歴 | `z` | オプション |
|
|
290
|
-
| bat | プレビューのシンタックスハイライト | — | オプション |
|
|
291
|
-
|
|
292
|
-
### bat — シンタックスハイライト
|
|
293
|
-
|
|
294
|
-
`bat` をインストールすると、プレビューペインでコードファイルを開いた際に
|
|
295
|
-
シンタックスハイライトが表示されます(Ruby、Python、Go、Rust、TypeScript など15言語以上対応)。
|
|
296
|
-
|
|
297
|
-
ハイライトはバックグラウンドで非同期に読み込まれるため、大きなソースツリーでも
|
|
298
|
-
カーソル移動が重くなりません。
|
|
299
|
-
|
|
300
|
-
```bash
|
|
301
|
-
# macOS
|
|
302
|
-
brew install bat
|
|
104
|
+
## ドキュメント
|
|
303
105
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`rufio -c` を実行すると bat が正しく認識されているか確認できます。
|
|
309
|
-
|
|
310
|
-
### インストール(全ツール)
|
|
311
|
-
|
|
312
|
-
```bash
|
|
313
|
-
# macOS
|
|
314
|
-
brew install fzf bat rga zoxide
|
|
315
|
-
|
|
316
|
-
# Ubuntu/Debian
|
|
317
|
-
apt install fzf bat zoxide
|
|
318
|
-
# rgaは別途インストール: https://github.com/phiresky/ripgrep-all
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
## 高度な機能
|
|
322
|
-
|
|
323
|
-
### ネイティブスキャナー(実験的)
|
|
324
|
-
|
|
325
|
-
高速なディレクトリスキャンのためのネイティブ実装をサポート:
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
rufio --native # 自動検出
|
|
329
|
-
rufio --native=zig # Zig実装
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### JITコンパイラ
|
|
333
|
-
|
|
334
|
-
```bash
|
|
335
|
-
rufio --yjit # Ruby 3.1+ YJIT
|
|
336
|
-
rufio --zjit # Ruby 3.4+ ZJIT
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### ヘルスチェック
|
|
340
|
-
|
|
341
|
-
```bash
|
|
342
|
-
rufio -c # システム依存関係をチェック
|
|
343
|
-
rufio --check-health # 同上
|
|
344
|
-
```
|
|
106
|
+
- **[操作ガイド](./docs/guide_ja.md)**: キー・モード・コマンドモード・スクリプト・DSL コマンド・設定・シェル連携・ネイティブエンジン
|
|
107
|
+
- **[CHANGELOG](./CHANGELOG.md)**: リリースノート
|
|
108
|
+
- **[SECURITY.md](./SECURITY.md)**: セキュリティ設計
|
|
345
109
|
|
|
346
110
|
## 開発
|
|
347
111
|
|
|
348
112
|
### 必要な環境
|
|
349
113
|
|
|
350
114
|
- Ruby 2.7.0以上
|
|
351
|
-
- io-console, pastel, tty-cursor, tty-screen gems
|
|
352
115
|
|
|
353
116
|
### 開発版の実行
|
|
354
117
|
|
data/Rakefile
CHANGED
|
@@ -14,10 +14,23 @@ end
|
|
|
14
14
|
# Default task
|
|
15
15
|
task default: :test
|
|
16
16
|
|
|
17
|
+
# Zig native library tasks
|
|
18
|
+
namespace :native do
|
|
19
|
+
desc 'Cross-compile the Zig library for every bundled platform (requires zig)'
|
|
20
|
+
task :build do
|
|
21
|
+
sh 'make -C lib_zig/rufio_native dist'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'Verify bundled Zig binaries for macOS, Linux and Windows'
|
|
25
|
+
task :check do
|
|
26
|
+
ruby '-I test -I lib test/test_native_platform_binaries.rb'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
17
30
|
# Gem build and push tasks
|
|
18
31
|
namespace :gem do
|
|
19
32
|
desc 'Build the gem file'
|
|
20
|
-
task :build do
|
|
33
|
+
task :build => 'native:check' do
|
|
21
34
|
puts "Building rufio v#{Rufio::VERSION}..."
|
|
22
35
|
|
|
23
36
|
# Clean old gem files
|
data/bin/rufio
CHANGED
|
@@ -19,6 +19,8 @@ start_directory = nil
|
|
|
19
19
|
test_mode = false
|
|
20
20
|
yjit_mode = false
|
|
21
21
|
zjit_mode = false
|
|
22
|
+
choosedir = nil
|
|
23
|
+
shell_init = nil
|
|
22
24
|
skip_next = false
|
|
23
25
|
|
|
24
26
|
ARGV.each_with_index do |arg, idx|
|
|
@@ -49,6 +51,21 @@ ARGV.each_with_index do |arg, idx|
|
|
|
49
51
|
end
|
|
50
52
|
when /^--native=(rust|go|auto|ruby)$/
|
|
51
53
|
native_mode = $1
|
|
54
|
+
when '--choosedir'
|
|
55
|
+
choosedir = ARGV[idx + 1]
|
|
56
|
+
skip_next = true
|
|
57
|
+
when /^--choosedir=(.+)$/
|
|
58
|
+
choosedir = $1
|
|
59
|
+
when '--shell-init'
|
|
60
|
+
# 次の引数がシェル名なら使い、なければ OS から判定する
|
|
61
|
+
if Rufio::ShellInit::SHELLS.include?(ARGV[idx + 1]) || ARGV[idx + 1] == 'pwsh'
|
|
62
|
+
shell_init = ARGV[idx + 1]
|
|
63
|
+
skip_next = true
|
|
64
|
+
else
|
|
65
|
+
shell_init = Rufio::ShellInit.detect
|
|
66
|
+
end
|
|
67
|
+
when /^--shell-init=(.+)$/
|
|
68
|
+
shell_init = $1
|
|
52
69
|
when '-c', '--check-health', '--help', '-h', '--yjit', '--zjit'
|
|
53
70
|
# これらは後で処理
|
|
54
71
|
when /^--/
|
|
@@ -70,7 +87,15 @@ if ENV['RUFIO_NATIVE'] && defined?(Rufio::NativeScanner)
|
|
|
70
87
|
end
|
|
71
88
|
|
|
72
89
|
# コマンド処理
|
|
73
|
-
if
|
|
90
|
+
if shell_init
|
|
91
|
+
begin
|
|
92
|
+
print Rufio::ShellInit.script(shell_init)
|
|
93
|
+
exit(0)
|
|
94
|
+
rescue ArgumentError => e
|
|
95
|
+
warn e.message
|
|
96
|
+
exit(1)
|
|
97
|
+
end
|
|
98
|
+
elsif ARGV.include?('--check-health') || ARGV.include?('-c')
|
|
74
99
|
# ヘルスチェック実行
|
|
75
100
|
health_checker = Rufio::HealthChecker.new
|
|
76
101
|
success = health_checker.run_check
|
|
@@ -87,6 +112,9 @@ elsif ARGV.include?('--help') || ARGV.include?('-h')
|
|
|
87
112
|
puts " --test Show FPS counter in footer (for performance testing)"
|
|
88
113
|
puts " --yjit Enable YJIT JIT compiler (Ruby 3.1+)"
|
|
89
114
|
puts " --zjit Enable ZJIT JIT compiler (Ruby 3.4+)"
|
|
115
|
+
puts " --shell-init [SHELL] Print the rf shell function (cd on T quit)"
|
|
116
|
+
puts " SHELL: bash|zsh|fish|powershell (default: by OS)"
|
|
117
|
+
puts " --choosedir FILE Write the directory to FILE when quitting with T"
|
|
90
118
|
if defined?(Rufio::NativeScanner)
|
|
91
119
|
puts " --native[=MODE] Enable native scanner (experimental)"
|
|
92
120
|
puts " MODE: auto|rust|go (default: auto)"
|
|
@@ -117,11 +145,12 @@ elsif ARGV.include?('--help') || ARGV.include?('-h')
|
|
|
117
145
|
puts " RUFIO_NATIVE=rust rufio # Use Rust via environment variable"
|
|
118
146
|
end
|
|
119
147
|
puts " rufio -c # Check dependencies"
|
|
148
|
+
puts " eval \"$(rufio --shell-init)\" # Add rf to bash/zsh (T quits and cd)"
|
|
120
149
|
exit(0)
|
|
121
150
|
else
|
|
122
151
|
start_directory ||= Dir.pwd
|
|
123
152
|
|
|
124
153
|
# アプリケーション開始
|
|
125
|
-
app = Rufio::Application.new(start_directory)
|
|
154
|
+
app = Rufio::Application.new(start_directory, choosedir: choosedir)
|
|
126
155
|
app.run(test_mode: test_mode)
|
|
127
156
|
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# FastZip 実装検討(rufio 1.3)
|
|
2
|
+
|
|
3
|
+
ブランチ: `v1.3.0`
|
|
4
|
+
ステータス: Phase 1〜5 実装済み(Pure Ruby の一覧・展開・作成、`z` キー、Zig エンジンの並列展開・圧縮、8 プラットフォームのバイナリ同梱)
|
|
5
|
+
|
|
6
|
+
「FastZip 開発構想メモ(Ruby × Zig ネイティブ拡張アーカイバ)」を rufio 1.3 の機能として取り込む前提で、既存コードとの整合と実装方針を整理する。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. 結論
|
|
11
|
+
|
|
12
|
+
| 項目 | 構想メモ | 本検討での方針 |
|
|
13
|
+
|------|----------|----------------|
|
|
14
|
+
| 置き場所 | 独立Gem `fast_zip` | まず rufio 内の機能として実装。ABIが Ruby 非依存なので、後から Gem に切り出せる |
|
|
15
|
+
| Ruby との接続 | C拡張(`Init_fast_zip` + `rb_thread_call_without_gvl`) | **既存の流儀に合わせて Fiddle + Ruby ABI 非依存の C 関数** |
|
|
16
|
+
| GVL 解放 | 自前で `rb_thread_call_without_gvl` | Fiddle が既定で解放する(下記 2.3)。長時間処理は既存スキャナと同じ「ハンドル+ポーリング」型にしてキャンセル可能にする |
|
|
17
|
+
| ネイティブ非搭載環境 | 記載なし | **Pure Ruby 実装を必須のフォールバック兼リファレンス**とする(Phase 1 で実装済み) |
|
|
18
|
+
| 最初のユーザー価値 | — | `.zip` をプレビューペインで一覧表示(`unzip -l` 相当)※実装済み |
|
|
19
|
+
|
|
20
|
+
### 1.1 決定事項(2026-10-31)
|
|
21
|
+
- C 拡張にはしない。既存の Zig 連携(Fiddle + Ruby ABI 非依存の C 関数)で実装する
|
|
22
|
+
- Pure Ruby 版は残す(ネイティブ非搭載環境のフォールバック兼、互換性の基準)
|
|
23
|
+
- 展開時の安全対策を入れる(4.2)
|
|
24
|
+
- ネイティブバイナリはビルド済みのものを Gem に同梱して配る(1.2 の `make dist` の 8 プラットフォーム)
|
|
25
|
+
- 操作は Files モードで選択後に `z`(すべて .zip なら展開、それ以外は圧縮)。暗号化エントリはスキップのまま
|
|
26
|
+
- 展開上限は現在の値(合計 16GiB / 10 万エントリ / 1MiB 超で 1000:1)で確定。対応方式は Stored と Deflate のみ
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. 現状調査(事実)
|
|
31
|
+
|
|
32
|
+
### 2.1 rufio のアーカイブ対応
|
|
33
|
+
- `FilePreview#determine_file_type` は `.zip` 等を `archive` と判定するが、実際はバイナリ判定が先に走り「Binary file / Cannot preview」と表示されていた。
|
|
34
|
+
- 圧縮・展開機能はない。
|
|
35
|
+
|
|
36
|
+
### 2.2 既存の Zig ネイティブ拡張(1.2.0 時点)
|
|
37
|
+
- ソース: `lib_zig/rufio_native/src/`(`main.zig` がエクスポート、`search.zig` がコンテンツ検索、`os.zig` が OS 差分の吸収)。Ruby ヘッダは include せず、`core_async_*` / `core_search_*` / `core_cache_*` という **Ruby ABI 非依存の C 関数**をエクスポートしている。
|
|
38
|
+
- Ruby 側: `lib/rufio/native_scanner_zig.rb` が `Fiddle::Importer` でロード。関数グループごとに `search_available?` のような判定があり、古いバイナリでも Ruby 実装に切り替わる。
|
|
39
|
+
- ビルド: Zig 0.17。`make -C lib_zig/rufio_native dist` で macOS(arm64/x86_64)・Linux(x86_64/aarch64、glibc 2.17+ と musl)・Windows(x64/arm64)の 8 種類をクロスコンパイルし、`lib/rufio/native/<platform>/` に同梱する。
|
|
40
|
+
- `test/test_native_platform_binaries.rb` が各バイナリの形式・CPU・必須エクスポート(`REQUIRED_EXPORTS`)を検証している。
|
|
41
|
+
- 1.2.0 で `rubyzip`(`>= 2.4, < 3.0`)が G モードの `.pptx` 読み取り用に依存関係に入った。ただし rubyzip 2.4 はファイル名を ASCII-8BIT で保持し、UTF-8 文字列で引けない(このブランチのテストで確認)。プレビュー・展開・作成は本実装を使い、rubyzip は相互運用テストの相手として使う。
|
|
42
|
+
|
|
43
|
+
### 2.3 Fiddle と GVL
|
|
44
|
+
- 本環境の fiddle 1.1.8 の `ext/fiddle/function.c` では、`need_gvl` の既定値が `Qfalse` で、その場合 `rb_thread_call_without_gvl` 経由で関数を呼ぶ。`Fiddle::Importer#extern` は `need_gvl` を指定しないので、**既存の Zig 関数呼び出しは GVL を解放して実行されている**。
|
|
45
|
+
- したがって構想メモの「GVL 解放のための C 拡張化」は、Fiddle 経由でも同じ効果が得られる。C 拡張にすると Ruby のマイナーバージョンごとにバイナリが必要になるデメリットの方が大きい。
|
|
46
|
+
- ただし Fiddle の GVL 解放ではキャンセル(unblock 関数)が指定できない。TUI から中断できるようにするため、展開・圧縮は既存スキャナと同じ非同期ハンドル方式を採る。
|
|
47
|
+
|
|
48
|
+
### 2.4 Ruby 標準 zlib でできること
|
|
49
|
+
- 構想メモの「zlib は ZIP 構造を扱えない」は正しいが、ZIP のコンテナ構造(EOCD / Central Directory / Local Header)は数百行の Ruby で扱える(Phase 1 で実装)。
|
|
50
|
+
- `Zlib::Inflate.new(-Zlib::MAX_WBITS)` / `Zlib::Deflate.new(level, -Zlib::MAX_WBITS)` で raw DEFLATE を扱え、`Zlib.crc32` もある。つまり **Pure Ruby + zlib だけで Store / Deflate の展開・作成まで実装できる**。ネイティブ実装は「速度のための加速器」という位置づけになる。
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. 実装内容(このブランチ)
|
|
55
|
+
|
|
56
|
+
### 3.1 `Rufio::ZipArchive`(`lib/rufio/zip_archive.rb`)
|
|
57
|
+
読み取り専用。ファイル本体は読まず、末尾と Central Directory だけを読む。
|
|
58
|
+
|
|
59
|
+
- EOCD の後方探索(末尾 22 + 65535 バイト)。コメント長が整合し、かつ指す先に Central Directory シグネチャがある候補のみ採用(コメント内の偽シグネチャ対策)
|
|
60
|
+
- ZIP64(ZIP64 EOCD Locator / ZIP64 EOCD / ZIP64 拡張フィールド 0x0001)
|
|
61
|
+
- ファイル名のエンコーディング: 汎用フラグ bit 11 → UTF-8、Info-ZIP Unicode Path 拡張(0x7075、CRC 一致時のみ)、UTF-8 として不正なら CP932(Windows 日本語環境で作られた ZIP)として解釈、それも失敗したら置換文字
|
|
62
|
+
- DOS 日時の変換、ディレクトリ・暗号化フラグの判定
|
|
63
|
+
- `limit:` で先頭 N 件だけ読む(プレビュー用。総数は EOCD から得る)
|
|
64
|
+
- 範囲外オフセット・切り詰められたファイルは `ZipArchive::FormatError`
|
|
65
|
+
- マルチディスク(分割)ZIP は非対応
|
|
66
|
+
|
|
67
|
+
### 3.2 プレビュー統合
|
|
68
|
+
- `FilePreview#preview_file` で拡張子 `.zip` または先頭が `PK\x03\x04` / `PK\x05\x06` のファイルを `type: "archive"` として一覧表示(最大 200 件、超過分は件数のみ)
|
|
69
|
+
- 壊れた ZIP は `Invalid ZIP archive: <理由>` をエラー表示
|
|
70
|
+
- `UIRenderer#extract_preview_lines` が `archive` を表示対象に追加
|
|
71
|
+
|
|
72
|
+
### 3.3 テスト(`test/test_zip_archive.rb`)
|
|
73
|
+
フィクスチャはテスト内の最小 ZIP ライタで生成し、外部コマンドに依存しない。加えて `zip` / `unzip` がある環境では `unzip -Z1` の一覧と完全一致することを確認する(ない環境では skip)。
|
|
74
|
+
|
|
75
|
+
### 3.4 計測(参考値)
|
|
76
|
+
環境: このクラウドコンテナ(Linux x86_64, Ruby 3.3.6)、`zip -qr` で作成した 20,001 エントリ / 3.2MB の ZIP。各 5 回平均。
|
|
77
|
+
|
|
78
|
+
| 処理 | 時間 |
|
|
79
|
+
|------|------|
|
|
80
|
+
| `ZipArchive.list`(全 20,001 件) | 176 ms |
|
|
81
|
+
| `ZipArchive.list(limit: 200)` | 1.9 ms |
|
|
82
|
+
| `FilePreview#preview_file`(200 件表示) | 1.9 ms |
|
|
83
|
+
| 参考: `unzip -l`(全件を標準出力へ、`time` の real) | 55 ms |
|
|
84
|
+
|
|
85
|
+
プレビュー用途では Pure Ruby で十分速い。ネイティブ化の効果が出るのは「全件走査」と「展開・圧縮(データ本体の処理)」である。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### 3.5 Phase 2: 展開・作成(Pure Ruby)
|
|
90
|
+
- `Rufio::ZipExtractor`(`lib/rufio/zip_extractor.rb`): Stored / Deflate の展開。安全対策は 4.2 をすべて実装。書き込み前に全エントリを検証し、危険なエントリが 1 件でもあれば何も書かない。進捗コールバックとキャンセルに対応。
|
|
91
|
+
- `Rufio::ZipWriter`(`lib/rufio/zip_writer.rb`): ファイル・ディレクトリから ZIP を作成。UTF-8 名、縮まないファイルは Stored、ZIP64(4GiB 超・65535 件超)、シンボリックリンクと出力先自身はスキップ、一時ファイル経由で失敗時に残骸を残さない。
|
|
92
|
+
- テスト: `test/test_zip_extractor.rb` / `test/test_zip_writer.rb`。`zip` コマンド製(データディスクリプタ付きを含む)の展開、作成物の `unzip -t` 通過、rubyzip での読み取り、70,000 件での ZIP64 を確認。
|
|
93
|
+
|
|
94
|
+
## 4. 計画
|
|
95
|
+
|
|
96
|
+
| Phase | 内容 | 実装 | 備考 |
|
|
97
|
+
|-------|------|------|------|
|
|
98
|
+
| 2 | Store / Deflate の展開・作成 | Pure Ruby + zlib | **実装済み**。ネイティブ版のテストオラクルにする |
|
|
99
|
+
| 3 | Zig コア(展開・圧縮のデータ処理) | Zig + Fiddle | **実装済み**。同じテストを両実装で回す |
|
|
100
|
+
| 4 | 並列展開・並列圧縮 | Zig(ワーカースレッド) | **実装済み**。最大 16 スレッド、既定は CPU 数 |
|
|
101
|
+
| 5 | 配布・ベンチマーク | — | **実装済み**。`make dist` で 8 プラットフォーム分を同梱、`REQUIRED_EXPORTS` に `core_zip_*` を追加 |
|
|
102
|
+
|
|
103
|
+
### 4.1 TUI への組み込み(実装済み)
|
|
104
|
+
- Files モードで `Space` で選択してから `z`。選択がすべて `.zip` なら展開、それ以外は圧縮(`lib/rufio/archive_controller.rb`)
|
|
105
|
+
- 展開: 確認ダイアログの後、各 ZIP を現在のディレクトリの `<名前>/` に展開(既にあれば `<名前> 2/` …)。失敗・キャンセル時は作った展開先を削除する
|
|
106
|
+
- 圧縮: アーカイブ名を入力(空 Enter で 1 件なら `<名前>.zip`、複数なら `Archive.zip`)。同名ファイルがあればエラー通知
|
|
107
|
+
- `..` は対象外
|
|
108
|
+
- 選択がなければ `z` は従来どおり zoxide(キー設定 `zoxide` を共用)
|
|
109
|
+
- 処理は `JobManager` のジョブとしてバックグラウンドスレッドで実行。ジョブ名に 5% 刻みで進捗を表示し、Jobs モードのキャンセルで中断できる。完了時に通知し、表示中のディレクトリなら一覧を再読み込みする
|
|
110
|
+
- 暗号化エントリはスキップ(決定事項)
|
|
111
|
+
|
|
112
|
+
### 4.2 安全性要件(実装済み。Ruby と Zig で共通)
|
|
113
|
+
- **Zip Slip**: `..`、絶対パス、Windows のドライブレター・`\` 区切りを含むエントリを拒否し、展開先ディレクトリ外に書かない
|
|
114
|
+
- **シンボリックリンク**: 外部属性で symlink を示すエントリは展開しない。展開先に既にあるシンボリックリンクも辿らない
|
|
115
|
+
- **ZIP 爆弾**: 展開後合計サイズ・圧縮率・エントリ数の上限。Central Directory 上のサイズを信用せず、実際に書いたバイト数で打ち切る。Local Header の範囲が重なるエントリ(重複参照型の爆弾)を拒否
|
|
116
|
+
- **CRC-32 検証**: 不一致なら展開したファイルを削除してエラー
|
|
117
|
+
- **既存ファイルの上書き**: 既定は拒否(`overwrite: true` で上書き)。UI では確認ダイアログを出す想定
|
|
118
|
+
- **ファイルモード**: 実行権限だけを引き継ぎ、setuid / setgid / sticky と他者書き込み権限は付けない
|
|
119
|
+
- 暗号化エントリ(ZipCrypto / AES)は初期は非対応としてスキップ
|
|
120
|
+
|
|
121
|
+
### 4.3 Zig エンジン(実装済み)
|
|
122
|
+
`lib_zig/rufio_native/src/zip.zig`。役割分担は次のとおりで、安全性の検証は Ruby 実装と共通にした。
|
|
123
|
+
|
|
124
|
+
| 処理 | 担当 |
|
|
125
|
+
|------|------|
|
|
126
|
+
| Central Directory の解析、パス正規化、上限・重複範囲・シンボリックリンクの検証、ディレクトリ作成 | Ruby |
|
|
127
|
+
| ZIP 内のデータ範囲を読み、伸長し、宣言サイズ超過で打ち切り、CRC-32 を検証して一時ファイルに書く | Zig(エントリ単位で並列) |
|
|
128
|
+
| 一時ファイルの rename、権限・日時の設定、失敗時の一時ファイル削除 | Ruby |
|
|
129
|
+
| ファイルを raw DEFLATE で一時ファイルへ圧縮し、CRC-32 とサイズを返す(1MiB 以上のファイルのみ) | Zig(ファイル単位で並列) |
|
|
130
|
+
| ローカルヘッダ・Central Directory・ZIP64 を書いてアーカイブを組み立てる | Ruby |
|
|
131
|
+
|
|
132
|
+
- エクスポート: `core_zip_extract_create` / `core_zip_deflate_create` / `core_zip_state` / `core_zip_progress` / `core_zip_status` / `core_zip_deflate_result` / `core_zip_cancel` / `core_zip_destroy`
|
|
133
|
+
- Ruby 側は `NativeScannerZigFFI.zip_available?` で関数グループの有無を判定し、ない場合(古いバイナリ)や `RUFIO_NATIVE_ZIP=0` では Pure Ruby を使う
|
|
134
|
+
- DEFLATE は Zig 標準ライブラリの `std.compress.flate`、CRC-32 は `std.hash.Crc32`。libdeflate は同梱していない
|
|
135
|
+
- ファイル操作は `os.zig` に追加(Linux は直接システムコール、macOS は libc、Windows は Win32。位置指定読み込み・排他作成・書き込み)
|
|
136
|
+
- `flate.Compress` は約 230KB あり初期化時にスタックへ載るため、ワーカースレッドのスタックは 4MB
|
|
137
|
+
- 1MiB 未満のファイルの圧縮は Ruby の zlib で行う。小さいファイルを Zig に渡すと、一時ファイルの作成・コピー・削除の分だけ遅くなった(2 万ファイルで 0.98 秒 → 5.26 秒)ため
|
|
138
|
+
- テスト: `test/test_zip_extractor.rb` / `test/test_zip_writer.rb` の全テストを `TestZipExtractorNative` / `TestZipWriterNative` で Zig 実装でも実行。Debug ビルド(安全チェック有効)でも全件通過を確認
|
|
139
|
+
|
|
140
|
+
### 4.4 計測(このクラウドコンテナ、4 CPU、Linux x86_64、ReleaseFast、各 3 回の最小値)
|
|
141
|
+
|
|
142
|
+
| データ | 処理 | Ruby | Zig | zip / unzip |
|
|
143
|
+
|--------|------|------|-----|-------------|
|
|
144
|
+
| 8 ファイル × 64MB のテキスト | 圧縮 | 15.36 秒 | 3.34 秒 | 20.20 秒 |
|
|
145
|
+
| 同上 | 展開 | 3.03 秒 | 0.94 秒 | 4.11 秒 |
|
|
146
|
+
| 2 万ファイル・計 79MB | 圧縮 | 0.93 秒 | 0.90 秒 | 0.30 秒 |
|
|
147
|
+
| 同上 | 展開 | 5.82 秒 | 5.00 秒 | 4.62 秒 |
|
|
148
|
+
|
|
149
|
+
- 圧縮後サイズは Zig の方がやや大きい(512MB → Ruby/zlib 67MB、Zig 71MB)。同じレベル指定でも実装が異なるため
|
|
150
|
+
- 小さいファイル大量の展開はファイル作成が律速で、計測ごとのばらつきが大きい(`sync` を挟まない計測では unzip 1.4〜1.8 秒、Ruby/Zig 3〜6 秒)。ファイルごとの Ruby 側処理(rename・chmod・utime)が残るため、unzip との差は縮め切れていない
|
|
151
|
+
|
|
152
|
+
## 5. 技術的な注意点(構想メモへのコメント)
|
|
153
|
+
|
|
154
|
+
- **libdeflate はストリーミング API を持たない**(バッファ全体を一度に処理する設計)。巨大エントリを扱うにはエントリ全体をメモリに載せるか mmap が必要。メモリ上限の設計とセットで検討する。ライセンスは MIT。
|
|
155
|
+
- **CRC-32**: 自前の SIMD 実装より、libdeflate の `libdeflate_crc32`(CPU 機能に応じた高速版を内部で選択)を使う方が保守コストが低い。
|
|
156
|
+
- **mmap**: POSIX と Windows で API が異なる。Windows 対応を続けるなら抽象化が必要(rufio は Windows 対応の修正を継続的に入れている)。
|
|
157
|
+
- **Zig のバージョン固定**: 既存 `build.zig` は `addExecutable(.{ .root_source_file = ... })` の書式。Zig はリリースごとにビルド API・標準ライブラリ(`std.compress.flate` 含む)が変わっているため、採用前に対象バージョンを決めて動作確認する必要がある(本検討では未確認)。
|
|
158
|
+
- **圧縮率**: 構想メモの通り DEFLATE は 32KB 窓・エントリ単位圧縮なので 7z には勝てない。比較軸は速度と OS 標準互換性に絞る。
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 6. 要判断事項
|
|
163
|
+
1. **独立 Gem 化のタイミング**: rufio 内で安定させてからか、`fast_zip` として切り出すか
|
|
164
|
+
|
|
165
|
+
## 7. 未確認事項
|
|
166
|
+
- Zig 版は Linux x86_64(このコンテナ)でのみ実行・計測した。macOS / Windows / aarch64 向けはクロスコンパイルとバイナリ形式・エクスポートの検証(`rake native:check`)までで、実機では動かしていない。特に Windows のファイル操作(`CreateFileW` / 位置指定 `ReadFile`)は未実行
|
|
167
|
+
- 計測は 1 環境・1 種類のデータのみ。CPU 数やストレージによって倍率は変わる
|