rufio 0.31.0 → 0.33.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 +132 -12
- data/README.md +84 -10
- data/README_EN.md +55 -2
- data/docs/CHANGELOG_v0.32.0.md +288 -0
- data/docs/CHANGELOG_v0.33.0.md +444 -0
- data/{CHANGELOG_v0.4.0.md → docs/CHANGELOG_v0.4.0.md} +2 -2
- data/{CHANGELOG_v0.5.0.md → docs/CHANGELOG_v0.5.0.md} +3 -0
- data/{CHANGELOG_v0.7.0.md → docs/CHANGELOG_v0.7.0.md} +1 -1
- data/{CHANGELOG_v0.8.0.md → docs/CHANGELOG_v0.8.0.md} +1 -1
- data/{CHANGELOG_v0.9.0.md → docs/CHANGELOG_v0.9.0.md} +1 -1
- data/docs/file-preview-optimization-analysis.md +759 -0
- data/docs/file-preview-performance-issue-FIXED.md +547 -0
- data/lib/rufio/application.rb +9 -1
- data/lib/rufio/background_command_executor.rb +98 -0
- data/lib/rufio/command_completion.rb +101 -0
- data/lib/rufio/command_history.rb +109 -0
- data/lib/rufio/command_logger.rb +118 -0
- data/lib/rufio/command_mode.rb +51 -1
- data/lib/rufio/command_mode_ui.rb +48 -15
- data/lib/rufio/config_loader.rb +9 -0
- data/lib/rufio/directory_listing.rb +60 -12
- data/lib/rufio/keybind_handler.rb +73 -2
- data/lib/rufio/native/rufio_native.bundle +0 -0
- data/lib/rufio/native/rufio_zig.bundle +0 -0
- data/lib/rufio/native_scanner.rb +306 -0
- data/lib/rufio/native_scanner_magnus.rb +194 -0
- data/lib/rufio/native_scanner_zig.rb +221 -0
- data/lib/rufio/plugins/hello.rb +30 -0
- data/lib/rufio/shell_command_completion.rb +120 -0
- data/lib/rufio/terminal_ui.rb +155 -20
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +11 -0
- data/lib_rust/rufio_native/.cargo/config.toml +2 -0
- data/lib_rust/rufio_native/Cargo.lock +346 -0
- data/lib_rust/rufio_native/Cargo.toml +18 -0
- data/lib_rust/rufio_native/build.rs +46 -0
- data/lib_rust/rufio_native/src/lib.rs +197 -0
- data/lib_zig/rufio_native/Makefile +33 -0
- data/lib_zig/rufio_native/build.zig +45 -0
- data/lib_zig/rufio_native/src/main.zig +167 -0
- metadata +36 -13
- /data/{CHANGELOG_v0.10.0.md → docs/CHANGELOG_v0.10.0.md} +0 -0
- /data/{CHANGELOG_v0.20.0.md → docs/CHANGELOG_v0.20.0.md} +0 -0
- /data/{CHANGELOG_v0.21.0.md → docs/CHANGELOG_v0.21.0.md} +0 -0
- /data/{CHANGELOG_v0.30.0.md → docs/CHANGELOG_v0.30.0.md} +0 -0
- /data/{CHANGELOG_v0.31.0.md → docs/CHANGELOG_v0.31.0.md} +0 -0
- /data/{CHANGELOG_v0.6.0.md → docs/CHANGELOG_v0.6.0.md} +0 -0
|
@@ -56,6 +56,11 @@ module Rufio
|
|
|
56
56
|
@in_help_mode = false
|
|
57
57
|
@pre_help_directory = nil
|
|
58
58
|
|
|
59
|
+
# Log viewer mode
|
|
60
|
+
@in_log_viewer_mode = false
|
|
61
|
+
@pre_log_viewer_directory = nil
|
|
62
|
+
@log_dir = File.join(Dir.home, '.config', 'rufio', 'log')
|
|
63
|
+
|
|
59
64
|
# Preview pane focus and scroll
|
|
60
65
|
@preview_focused = false
|
|
61
66
|
@preview_scroll_offset = 0
|
|
@@ -103,6 +108,11 @@ module Rufio
|
|
|
103
108
|
return exit_help_mode
|
|
104
109
|
end
|
|
105
110
|
|
|
111
|
+
# ログビューワモード中のESCキー特別処理
|
|
112
|
+
if @in_log_viewer_mode && key == "\e"
|
|
113
|
+
return exit_log_viewer_mode
|
|
114
|
+
end
|
|
115
|
+
|
|
106
116
|
# フィルターモード中は他のキーバインドを無効化
|
|
107
117
|
return handle_filter_input(key) if @filter_manager.filter_mode
|
|
108
118
|
|
|
@@ -167,7 +177,7 @@ module Rufio
|
|
|
167
177
|
copy_selected_to_current
|
|
168
178
|
when 'x' # x - delete selected files
|
|
169
179
|
delete_selected_files
|
|
170
|
-
when '
|
|
180
|
+
when 'P' # P - project mode
|
|
171
181
|
enter_project_mode
|
|
172
182
|
when 'b' # b - add bookmark
|
|
173
183
|
add_bookmark
|
|
@@ -179,6 +189,8 @@ module Rufio
|
|
|
179
189
|
goto_bookmark(key.to_i)
|
|
180
190
|
when '?' # ? - enter help mode
|
|
181
191
|
enter_help_mode
|
|
192
|
+
when 'L' # L - enter log viewer mode
|
|
193
|
+
enter_log_viewer_mode
|
|
182
194
|
when ':' # : - command mode
|
|
183
195
|
activate_command_mode
|
|
184
196
|
else
|
|
@@ -255,6 +267,49 @@ module Rufio
|
|
|
255
267
|
true
|
|
256
268
|
end
|
|
257
269
|
|
|
270
|
+
# ログビューワモード関連メソッド
|
|
271
|
+
|
|
272
|
+
# ログビューワモード中かどうか
|
|
273
|
+
def log_viewer_mode?
|
|
274
|
+
@in_log_viewer_mode
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# ログビューワモードに入る
|
|
278
|
+
def enter_log_viewer_mode
|
|
279
|
+
return false unless @directory_listing
|
|
280
|
+
|
|
281
|
+
# 現在のディレクトリを保存
|
|
282
|
+
@pre_log_viewer_directory = @directory_listing.current_path
|
|
283
|
+
|
|
284
|
+
# log ディレクトリを作成(存在しない場合)
|
|
285
|
+
FileUtils.mkdir_p(@log_dir) unless Dir.exist?(@log_dir)
|
|
286
|
+
|
|
287
|
+
# log ディレクトリに移動
|
|
288
|
+
navigate_to_directory(@log_dir)
|
|
289
|
+
|
|
290
|
+
# ログビューワモードを有効化
|
|
291
|
+
@in_log_viewer_mode = true
|
|
292
|
+
|
|
293
|
+
true
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# ログビューワモードを終了
|
|
297
|
+
def exit_log_viewer_mode
|
|
298
|
+
return false unless @in_log_viewer_mode
|
|
299
|
+
return false unless @pre_log_viewer_directory
|
|
300
|
+
|
|
301
|
+
# ログビューワモードを無効化
|
|
302
|
+
@in_log_viewer_mode = false
|
|
303
|
+
|
|
304
|
+
# 元のディレクトリに戻る
|
|
305
|
+
navigate_to_directory(@pre_log_viewer_directory)
|
|
306
|
+
|
|
307
|
+
# 保存したディレクトリをクリア
|
|
308
|
+
@pre_log_viewer_directory = nil
|
|
309
|
+
|
|
310
|
+
true
|
|
311
|
+
end
|
|
312
|
+
|
|
258
313
|
# ヘルプモード時の制限付き親ディレクトリナビゲーション
|
|
259
314
|
def navigate_parent_with_restriction
|
|
260
315
|
if @in_help_mode
|
|
@@ -276,8 +331,24 @@ module Rufio
|
|
|
276
331
|
|
|
277
332
|
# info ディレクトリ配下であれば、通常のナビゲーションを実行
|
|
278
333
|
navigate_parent
|
|
334
|
+
elsif @in_log_viewer_mode
|
|
335
|
+
# log ディレクトリより上には移動できない
|
|
336
|
+
current_path = @directory_listing.current_path
|
|
337
|
+
|
|
338
|
+
# 現在のパスが log ディレクトリ以下でない場合は移動を許可しない
|
|
339
|
+
unless current_path.start_with?(@log_dir)
|
|
340
|
+
return false
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# 現在のパスが log ディレクトリそのものの場合は移動を許可しない
|
|
344
|
+
if current_path == @log_dir
|
|
345
|
+
return false
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# log ディレクトリ配下であれば、通常のナビゲーションを実行
|
|
349
|
+
navigate_parent
|
|
279
350
|
else
|
|
280
|
-
#
|
|
351
|
+
# ヘルプモード・ログビューワモード外では通常のナビゲーション
|
|
281
352
|
navigate_parent
|
|
282
353
|
end
|
|
283
354
|
end
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ffi'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Rufio
|
|
7
|
+
# NativeScanner - Rust/Goのネイティブライブラリを使った高速ディレクトリスキャナー
|
|
8
|
+
class NativeScanner
|
|
9
|
+
# ライブラリパス
|
|
10
|
+
LIB_DIR = File.expand_path('native', __dir__)
|
|
11
|
+
RUST_LIB = File.join(LIB_DIR, 'librufio_scanner.dylib')
|
|
12
|
+
GO_LIB = File.join(LIB_DIR, 'libscanner.dylib')
|
|
13
|
+
|
|
14
|
+
@mode = nil
|
|
15
|
+
@current_library = nil
|
|
16
|
+
|
|
17
|
+
# Rustライブラリ用のFFIモジュール
|
|
18
|
+
module RustLib
|
|
19
|
+
extend FFI::Library
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
ffi_lib RUST_LIB
|
|
23
|
+
attach_function :scan_directory, [:string], :pointer
|
|
24
|
+
attach_function :scan_directory_fast, [:string, :int], :pointer
|
|
25
|
+
attach_function :get_version, [], :pointer
|
|
26
|
+
@available = true
|
|
27
|
+
rescue LoadError, FFI::NotFoundError
|
|
28
|
+
@available = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.available?
|
|
32
|
+
@available
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Goライブラリ用のFFIモジュール
|
|
37
|
+
module GoLib
|
|
38
|
+
extend FFI::Library
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
ffi_lib GO_LIB
|
|
42
|
+
attach_function :ScanDirectory, [:string], :pointer
|
|
43
|
+
attach_function :ScanDirectoryFast, [:string, :int], :pointer
|
|
44
|
+
attach_function :GetVersion, [], :pointer
|
|
45
|
+
attach_function :FreeCString, [:pointer], :void
|
|
46
|
+
@available = true
|
|
47
|
+
rescue LoadError, FFI::NotFoundError
|
|
48
|
+
@available = false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.available?
|
|
52
|
+
@available
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class << self
|
|
57
|
+
# モード設定
|
|
58
|
+
def mode=(value)
|
|
59
|
+
case value
|
|
60
|
+
when 'rust'
|
|
61
|
+
if RustLib.available?
|
|
62
|
+
@mode = 'rust'
|
|
63
|
+
@current_library = RustLib
|
|
64
|
+
else
|
|
65
|
+
@mode = 'ruby'
|
|
66
|
+
@current_library = nil
|
|
67
|
+
end
|
|
68
|
+
when 'go'
|
|
69
|
+
if GoLib.available?
|
|
70
|
+
@mode = 'go'
|
|
71
|
+
@current_library = GoLib
|
|
72
|
+
else
|
|
73
|
+
@mode = 'ruby'
|
|
74
|
+
@current_library = nil
|
|
75
|
+
end
|
|
76
|
+
when 'auto'
|
|
77
|
+
# 優先順位: Rust > Go > Ruby
|
|
78
|
+
if RustLib.available?
|
|
79
|
+
@mode = 'rust'
|
|
80
|
+
@current_library = RustLib
|
|
81
|
+
elsif GoLib.available?
|
|
82
|
+
@mode = 'go'
|
|
83
|
+
@current_library = GoLib
|
|
84
|
+
else
|
|
85
|
+
@mode = 'ruby'
|
|
86
|
+
@current_library = nil
|
|
87
|
+
end
|
|
88
|
+
when 'ruby'
|
|
89
|
+
@mode = 'ruby'
|
|
90
|
+
@current_library = nil
|
|
91
|
+
else
|
|
92
|
+
# 無効なモードはrubyにフォールバック
|
|
93
|
+
@mode = 'ruby'
|
|
94
|
+
@current_library = nil
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# 現在のモード取得
|
|
99
|
+
def mode
|
|
100
|
+
# 初回アクセス時はautoモードに設定
|
|
101
|
+
self.mode = 'auto' if @mode.nil?
|
|
102
|
+
@mode
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# 利用可能なライブラリをチェック
|
|
106
|
+
def available_libraries
|
|
107
|
+
{
|
|
108
|
+
rust: RustLib.available?,
|
|
109
|
+
go: GoLib.available?
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# ディレクトリをスキャン
|
|
114
|
+
def scan_directory(path)
|
|
115
|
+
# モードが未設定の場合は自動設定
|
|
116
|
+
mode if @mode.nil?
|
|
117
|
+
|
|
118
|
+
case @mode
|
|
119
|
+
when 'rust'
|
|
120
|
+
scan_with_rust(path)
|
|
121
|
+
when 'go'
|
|
122
|
+
scan_with_go(path)
|
|
123
|
+
else
|
|
124
|
+
scan_with_ruby(path)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# 高速スキャン(エントリ数制限付き)
|
|
129
|
+
def scan_directory_fast(path, max_entries = 1000)
|
|
130
|
+
# モードが未設定の場合は自動設定
|
|
131
|
+
mode if @mode.nil?
|
|
132
|
+
|
|
133
|
+
case @mode
|
|
134
|
+
when 'rust'
|
|
135
|
+
scan_fast_with_rust(path, max_entries)
|
|
136
|
+
when 'go'
|
|
137
|
+
scan_fast_with_go(path, max_entries)
|
|
138
|
+
else
|
|
139
|
+
scan_fast_with_ruby(path, max_entries)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# バージョン情報取得
|
|
144
|
+
def version
|
|
145
|
+
# モードが未設定の場合は自動設定
|
|
146
|
+
mode if @mode.nil?
|
|
147
|
+
|
|
148
|
+
case @mode
|
|
149
|
+
when 'rust'
|
|
150
|
+
ptr = RustLib.get_version
|
|
151
|
+
ptr.read_string
|
|
152
|
+
when 'go'
|
|
153
|
+
ptr = GoLib.GetVersion
|
|
154
|
+
result = ptr.read_string
|
|
155
|
+
GoLib.FreeCString(ptr)
|
|
156
|
+
result
|
|
157
|
+
else
|
|
158
|
+
"Ruby #{RUBY_VERSION}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
# Rustライブラリでスキャン
|
|
165
|
+
def scan_with_rust(path)
|
|
166
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
167
|
+
|
|
168
|
+
ptr = RustLib.scan_directory(path)
|
|
169
|
+
json_str = ptr.read_string
|
|
170
|
+
parse_scan_result(json_str)
|
|
171
|
+
rescue StandardError => e
|
|
172
|
+
raise StandardError, "Rust scan failed: #{e.message}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Rustライブラリで高速スキャン
|
|
176
|
+
def scan_fast_with_rust(path, max_entries)
|
|
177
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
178
|
+
|
|
179
|
+
ptr = RustLib.scan_directory_fast(path, max_entries)
|
|
180
|
+
json_str = ptr.read_string
|
|
181
|
+
parse_scan_result(json_str)
|
|
182
|
+
rescue StandardError => e
|
|
183
|
+
raise StandardError, "Rust fast scan failed: #{e.message}"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Goライブラリでスキャン
|
|
187
|
+
def scan_with_go(path)
|
|
188
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
189
|
+
|
|
190
|
+
ptr = GoLib.ScanDirectory(path)
|
|
191
|
+
json_str = ptr.read_string
|
|
192
|
+
GoLib.FreeCString(ptr)
|
|
193
|
+
parse_scan_result(json_str)
|
|
194
|
+
rescue StandardError => e
|
|
195
|
+
raise StandardError, "Go scan failed: #{e.message}"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Goライブラリで高速スキャン
|
|
199
|
+
def scan_fast_with_go(path, max_entries)
|
|
200
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
201
|
+
|
|
202
|
+
ptr = GoLib.ScanDirectoryFast(path, max_entries)
|
|
203
|
+
json_str = ptr.read_string
|
|
204
|
+
GoLib.FreeCString(ptr)
|
|
205
|
+
parse_scan_result(json_str)
|
|
206
|
+
rescue StandardError => e
|
|
207
|
+
raise StandardError, "Go fast scan failed: #{e.message}"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Rubyでスキャン(フォールバック実装)
|
|
211
|
+
def scan_with_ruby(path)
|
|
212
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
213
|
+
|
|
214
|
+
entries = []
|
|
215
|
+
Dir.foreach(path) do |entry|
|
|
216
|
+
next if entry == '.' || entry == '..'
|
|
217
|
+
|
|
218
|
+
full_path = File.join(path, entry)
|
|
219
|
+
stat = File.lstat(full_path)
|
|
220
|
+
|
|
221
|
+
entries << {
|
|
222
|
+
name: entry,
|
|
223
|
+
type: file_type(stat),
|
|
224
|
+
size: stat.size,
|
|
225
|
+
mtime: stat.mtime.to_i,
|
|
226
|
+
mode: stat.mode
|
|
227
|
+
}
|
|
228
|
+
end
|
|
229
|
+
entries
|
|
230
|
+
rescue StandardError => e
|
|
231
|
+
raise StandardError, "Ruby scan failed: #{e.message}"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Ruby高速スキャン(エントリ数制限付き)
|
|
235
|
+
def scan_fast_with_ruby(path, max_entries)
|
|
236
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
237
|
+
|
|
238
|
+
entries = []
|
|
239
|
+
count = 0
|
|
240
|
+
|
|
241
|
+
Dir.foreach(path) do |entry|
|
|
242
|
+
next if entry == '.' || entry == '..'
|
|
243
|
+
break if count >= max_entries
|
|
244
|
+
|
|
245
|
+
full_path = File.join(path, entry)
|
|
246
|
+
stat = File.lstat(full_path)
|
|
247
|
+
|
|
248
|
+
entries << {
|
|
249
|
+
name: entry,
|
|
250
|
+
type: file_type(stat),
|
|
251
|
+
size: stat.size,
|
|
252
|
+
mtime: stat.mtime.to_i,
|
|
253
|
+
mode: stat.mode
|
|
254
|
+
}
|
|
255
|
+
count += 1
|
|
256
|
+
end
|
|
257
|
+
entries
|
|
258
|
+
rescue StandardError => e
|
|
259
|
+
raise StandardError, "Ruby fast scan failed: #{e.message}"
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# ファイルタイプを判定
|
|
263
|
+
def file_type(stat)
|
|
264
|
+
if stat.directory?
|
|
265
|
+
'directory'
|
|
266
|
+
elsif stat.symlink?
|
|
267
|
+
'symlink'
|
|
268
|
+
elsif stat.file?
|
|
269
|
+
'file'
|
|
270
|
+
else
|
|
271
|
+
'other'
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# JSONレスポンスをパース
|
|
276
|
+
def parse_scan_result(json_str)
|
|
277
|
+
entries = JSON.parse(json_str, symbolize_names: true)
|
|
278
|
+
|
|
279
|
+
# エラーチェック(配列ではなくハッシュが返された場合)
|
|
280
|
+
if entries.is_a?(Hash) && entries[:error]
|
|
281
|
+
raise StandardError, entries[:error]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# 配列が返された場合は各エントリを変換
|
|
285
|
+
if entries.is_a?(Array)
|
|
286
|
+
return entries.map do |entry|
|
|
287
|
+
{
|
|
288
|
+
name: entry[:name],
|
|
289
|
+
type: entry[:is_dir] ? 'directory' : 'file',
|
|
290
|
+
size: entry[:size],
|
|
291
|
+
mtime: entry[:mtime],
|
|
292
|
+
mode: 0, # Rustライブラリはmodeを返さない
|
|
293
|
+
executable: entry[:executable],
|
|
294
|
+
hidden: entry[:hidden]
|
|
295
|
+
}
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# それ以外の場合は空配列
|
|
300
|
+
[]
|
|
301
|
+
rescue JSON::ParserError => e
|
|
302
|
+
raise StandardError, "Failed to parse scan result: #{e.message}"
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rufio
|
|
4
|
+
# Magnus拡張のラッパー
|
|
5
|
+
# FFIを使わずに直接Rubyオブジェクトとして扱える
|
|
6
|
+
module NativeScannerMagnusLoader
|
|
7
|
+
LIB_PATH = File.expand_path('native/rufio_native.bundle', __dir__)
|
|
8
|
+
|
|
9
|
+
@loaded = false
|
|
10
|
+
@available = false
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def load!
|
|
14
|
+
return @available if @loaded
|
|
15
|
+
|
|
16
|
+
@loaded = true
|
|
17
|
+
|
|
18
|
+
if File.exist?(LIB_PATH)
|
|
19
|
+
begin
|
|
20
|
+
# .bundleファイルを直接ロード
|
|
21
|
+
# 拡張子を外してrequireする
|
|
22
|
+
lib_path_without_ext = LIB_PATH.sub(/\.bundle$/, '')
|
|
23
|
+
require lib_path_without_ext
|
|
24
|
+
@available = defined?(Rufio::NativeScannerMagnus)
|
|
25
|
+
rescue LoadError => e
|
|
26
|
+
warn "Failed to load magnus extension: #{e.message}" if ENV['RUFIO_DEBUG']
|
|
27
|
+
@available = false
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
@available = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@available
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def available?
|
|
37
|
+
load! unless @loaded
|
|
38
|
+
@available
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Magnus拡張が利用可能な場合のみロード
|
|
44
|
+
if NativeScannerMagnusLoader.load!
|
|
45
|
+
# NativeScannerにmagnusモードを追加
|
|
46
|
+
class NativeScanner
|
|
47
|
+
class << self
|
|
48
|
+
# magnusモードを追加
|
|
49
|
+
alias_method :original_mode=, :mode=
|
|
50
|
+
|
|
51
|
+
def mode=(value)
|
|
52
|
+
case value
|
|
53
|
+
when 'magnus'
|
|
54
|
+
if NativeScannerMagnusLoader.available?
|
|
55
|
+
@mode = 'magnus'
|
|
56
|
+
@current_library = nil # magnus は FFI を使わない
|
|
57
|
+
else
|
|
58
|
+
@mode = 'ruby'
|
|
59
|
+
@current_library = nil
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
send(:original_mode=, value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# magnusスキャン
|
|
67
|
+
def scan_directory_with_magnus(path)
|
|
68
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
69
|
+
|
|
70
|
+
entries = NativeScannerMagnus.scan_directory(path)
|
|
71
|
+
|
|
72
|
+
# 結果の形式を統一(すでに正しい形式だが念のため)
|
|
73
|
+
entries.map do |entry|
|
|
74
|
+
{
|
|
75
|
+
name: entry[:name],
|
|
76
|
+
type: entry[:is_dir] ? 'directory' : 'file',
|
|
77
|
+
size: entry[:size],
|
|
78
|
+
mtime: entry[:mtime],
|
|
79
|
+
mode: 0,
|
|
80
|
+
executable: entry[:executable],
|
|
81
|
+
hidden: entry[:hidden]
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
raise StandardError, "Magnus scan failed: #{e.message}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# magnusで高速スキャン
|
|
89
|
+
def scan_directory_fast_with_magnus(path, max_entries)
|
|
90
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
91
|
+
|
|
92
|
+
entries = NativeScannerMagnus.scan_directory_fast(path, max_entries)
|
|
93
|
+
|
|
94
|
+
entries.map do |entry|
|
|
95
|
+
{
|
|
96
|
+
name: entry[:name],
|
|
97
|
+
type: entry[:is_dir] ? 'directory' : 'file',
|
|
98
|
+
size: entry[:size],
|
|
99
|
+
mtime: entry[:mtime],
|
|
100
|
+
mode: 0,
|
|
101
|
+
executable: entry[:executable],
|
|
102
|
+
hidden: entry[:hidden]
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
raise StandardError, "Magnus fast scan failed: #{e.message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# scan_directoryメソッドを拡張
|
|
110
|
+
alias_method :original_scan_directory, :scan_directory
|
|
111
|
+
|
|
112
|
+
def scan_directory(path)
|
|
113
|
+
mode if @mode.nil?
|
|
114
|
+
|
|
115
|
+
case @mode
|
|
116
|
+
when 'magnus'
|
|
117
|
+
scan_directory_with_magnus(path)
|
|
118
|
+
else
|
|
119
|
+
original_scan_directory(path)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# scan_directory_fastメソッドを拡張
|
|
124
|
+
alias_method :original_scan_directory_fast, :scan_directory_fast
|
|
125
|
+
|
|
126
|
+
def scan_directory_fast(path, max_entries = 1000)
|
|
127
|
+
mode if @mode.nil?
|
|
128
|
+
|
|
129
|
+
case @mode
|
|
130
|
+
when 'magnus'
|
|
131
|
+
scan_directory_fast_with_magnus(path, max_entries)
|
|
132
|
+
else
|
|
133
|
+
original_scan_directory_fast(path, max_entries)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# versionメソッドを拡張
|
|
138
|
+
alias_method :original_version, :version
|
|
139
|
+
|
|
140
|
+
def version
|
|
141
|
+
mode if @mode.nil?
|
|
142
|
+
|
|
143
|
+
case @mode
|
|
144
|
+
when 'magnus'
|
|
145
|
+
NativeScannerMagnus.version
|
|
146
|
+
else
|
|
147
|
+
original_version
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# available_librariesを更新
|
|
152
|
+
alias_method :original_available_libraries, :available_libraries
|
|
153
|
+
|
|
154
|
+
def available_libraries
|
|
155
|
+
original = original_available_libraries
|
|
156
|
+
original.merge(magnus: NativeScannerMagnusLoader.available?)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# autoモードの優先順位を更新(magnus > rust > go > ruby)
|
|
160
|
+
alias_method :original_auto_mode, :mode=
|
|
161
|
+
|
|
162
|
+
def mode=(value)
|
|
163
|
+
case value
|
|
164
|
+
when 'auto'
|
|
165
|
+
# 優先順位: Magnus > Rust > Go > Ruby
|
|
166
|
+
if NativeScannerMagnusLoader.available?
|
|
167
|
+
@mode = 'magnus'
|
|
168
|
+
@current_library = nil
|
|
169
|
+
elsif RustLib.available?
|
|
170
|
+
@mode = 'rust'
|
|
171
|
+
@current_library = RustLib
|
|
172
|
+
elsif GoLib.available?
|
|
173
|
+
@mode = 'go'
|
|
174
|
+
@current_library = GoLib
|
|
175
|
+
else
|
|
176
|
+
@mode = 'ruby'
|
|
177
|
+
@current_library = nil
|
|
178
|
+
end
|
|
179
|
+
when 'magnus'
|
|
180
|
+
if NativeScannerMagnusLoader.available?
|
|
181
|
+
@mode = 'magnus'
|
|
182
|
+
@current_library = nil
|
|
183
|
+
else
|
|
184
|
+
@mode = 'ruby'
|
|
185
|
+
@current_library = nil
|
|
186
|
+
end
|
|
187
|
+
else
|
|
188
|
+
send(:original_auto_mode, value)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|