rufio 0.32.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 +23 -0
- data/README.md +63 -7
- data/README_EN.md +25 -0
- data/docs/CHANGELOG_v0.33.0.md +444 -0
- 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_logger.rb +118 -0
- data/lib/rufio/command_mode.rb +17 -2
- 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/terminal_ui.rb +66 -16
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +5 -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 +20 -2
|
@@ -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
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rufio
|
|
4
|
+
# Zig拡張のラッパー
|
|
5
|
+
# FFIを使わずに直接Rubyオブジェクトとして扱える
|
|
6
|
+
module NativeScannerZigLoader
|
|
7
|
+
LIB_PATH = File.expand_path('native/rufio_zig.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::NativeScannerZig)
|
|
25
|
+
rescue LoadError => e
|
|
26
|
+
warn "Failed to load zig 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
|
+
# Zig拡張が利用可能な場合のみロード
|
|
44
|
+
if NativeScannerZigLoader.load!
|
|
45
|
+
# NativeScannerにzigモードを追加
|
|
46
|
+
class NativeScanner
|
|
47
|
+
class << self
|
|
48
|
+
# zigモードを追加
|
|
49
|
+
alias_method :original_mode=, :mode= unless method_defined?(:original_mode=)
|
|
50
|
+
|
|
51
|
+
def mode=(value)
|
|
52
|
+
case value
|
|
53
|
+
when 'zig'
|
|
54
|
+
if NativeScannerZigLoader.available?
|
|
55
|
+
@mode = 'zig'
|
|
56
|
+
@current_library = nil # zig は FFI を使わない
|
|
57
|
+
else
|
|
58
|
+
@mode = 'ruby'
|
|
59
|
+
@current_library = nil
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
original_mode=(value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# zigスキャン
|
|
67
|
+
def scan_directory_with_zig(path)
|
|
68
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
69
|
+
|
|
70
|
+
entries = NativeScannerZig.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, "Zig scan failed: #{e.message}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# zigで高速スキャン
|
|
89
|
+
def scan_directory_fast_with_zig(path, max_entries)
|
|
90
|
+
raise StandardError, "Directory does not exist: #{path}" unless Dir.exist?(path)
|
|
91
|
+
|
|
92
|
+
entries = NativeScannerZig.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, "Zig fast scan failed: #{e.message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# scan_directoryメソッドを拡張
|
|
110
|
+
alias_method :original_scan_directory, :scan_directory unless method_defined?(:original_scan_directory)
|
|
111
|
+
|
|
112
|
+
def scan_directory(path)
|
|
113
|
+
mode if @mode.nil?
|
|
114
|
+
|
|
115
|
+
case @mode
|
|
116
|
+
when 'zig'
|
|
117
|
+
scan_directory_with_zig(path)
|
|
118
|
+
when 'magnus'
|
|
119
|
+
# magnusモードの場合は、native_scanner_magnus.rbで定義されたメソッドを呼ぶ
|
|
120
|
+
if defined?(NativeScannerMagnusLoader) && respond_to?(:scan_directory_with_magnus, true)
|
|
121
|
+
scan_directory_with_magnus(path)
|
|
122
|
+
else
|
|
123
|
+
original_scan_directory(path)
|
|
124
|
+
end
|
|
125
|
+
else
|
|
126
|
+
original_scan_directory(path)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# scan_directory_fastメソッドを拡張
|
|
131
|
+
alias_method :original_scan_directory_fast, :scan_directory_fast unless method_defined?(:original_scan_directory_fast)
|
|
132
|
+
|
|
133
|
+
def scan_directory_fast(path, max_entries = 1000)
|
|
134
|
+
mode if @mode.nil?
|
|
135
|
+
|
|
136
|
+
case @mode
|
|
137
|
+
when 'zig'
|
|
138
|
+
scan_directory_fast_with_zig(path, max_entries)
|
|
139
|
+
when 'magnus'
|
|
140
|
+
# magnusモードの場合は、native_scanner_magnus.rbで定義されたメソッドを呼ぶ
|
|
141
|
+
if defined?(NativeScannerMagnusLoader) && respond_to?(:scan_directory_fast_with_magnus, true)
|
|
142
|
+
scan_directory_fast_with_magnus(path, max_entries)
|
|
143
|
+
else
|
|
144
|
+
original_scan_directory_fast(path, max_entries)
|
|
145
|
+
end
|
|
146
|
+
else
|
|
147
|
+
original_scan_directory_fast(path, max_entries)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# versionメソッドを拡張
|
|
152
|
+
alias_method :original_version, :version unless method_defined?(:original_version)
|
|
153
|
+
|
|
154
|
+
def version
|
|
155
|
+
mode if @mode.nil?
|
|
156
|
+
|
|
157
|
+
case @mode
|
|
158
|
+
when 'zig'
|
|
159
|
+
NativeScannerZig.version
|
|
160
|
+
else
|
|
161
|
+
original_version
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# available_librariesを更新
|
|
166
|
+
alias_method :original_available_libraries, :available_libraries unless method_defined?(:original_available_libraries)
|
|
167
|
+
|
|
168
|
+
def available_libraries
|
|
169
|
+
original = original_available_libraries
|
|
170
|
+
result = original.merge(zig: NativeScannerZigLoader.available?)
|
|
171
|
+
# magnusが既に追加されていなければ追加
|
|
172
|
+
if defined?(NativeScannerMagnusLoader) && !result.key?(:magnus)
|
|
173
|
+
result = result.merge(magnus: NativeScannerMagnusLoader.available?)
|
|
174
|
+
end
|
|
175
|
+
result
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# autoモードの優先順位を更新(magnus > zig > rust > go > ruby)
|
|
179
|
+
def mode=(value)
|
|
180
|
+
case value
|
|
181
|
+
when 'auto'
|
|
182
|
+
# 優先順位: Magnus > Zig > Rust > Go > Ruby
|
|
183
|
+
if defined?(NativeScannerMagnusLoader) && NativeScannerMagnusLoader.available?
|
|
184
|
+
@mode = 'magnus'
|
|
185
|
+
@current_library = nil
|
|
186
|
+
elsif NativeScannerZigLoader.available?
|
|
187
|
+
@mode = 'zig'
|
|
188
|
+
@current_library = nil
|
|
189
|
+
elsif defined?(RustLib) && RustLib.available?
|
|
190
|
+
@mode = 'rust'
|
|
191
|
+
@current_library = RustLib
|
|
192
|
+
elsif defined?(GoLib) && GoLib.available?
|
|
193
|
+
@mode = 'go'
|
|
194
|
+
@current_library = GoLib
|
|
195
|
+
else
|
|
196
|
+
@mode = 'ruby'
|
|
197
|
+
@current_library = nil
|
|
198
|
+
end
|
|
199
|
+
when 'zig'
|
|
200
|
+
if NativeScannerZigLoader.available?
|
|
201
|
+
@mode = 'zig'
|
|
202
|
+
@current_library = nil
|
|
203
|
+
else
|
|
204
|
+
@mode = 'ruby'
|
|
205
|
+
@current_library = nil
|
|
206
|
+
end
|
|
207
|
+
when 'magnus'
|
|
208
|
+
if defined?(NativeScannerMagnusLoader) && NativeScannerMagnusLoader.available?
|
|
209
|
+
@mode = 'magnus'
|
|
210
|
+
@current_library = nil
|
|
211
|
+
else
|
|
212
|
+
send(:original_mode=, value)
|
|
213
|
+
end
|
|
214
|
+
else
|
|
215
|
+
send(:original_mode=, value)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
data/lib/rufio/terminal_ui.rb
CHANGED
|
@@ -62,13 +62,17 @@ module Rufio
|
|
|
62
62
|
@in_log_mode = false
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def start(directory_listing, keybind_handler, file_preview)
|
|
65
|
+
def start(directory_listing, keybind_handler, file_preview, background_executor = nil)
|
|
66
66
|
@directory_listing = directory_listing
|
|
67
67
|
@keybind_handler = keybind_handler
|
|
68
68
|
@file_preview = file_preview
|
|
69
|
+
@background_executor = background_executor
|
|
69
70
|
@keybind_handler.set_directory_listing(@directory_listing)
|
|
70
71
|
@keybind_handler.set_terminal_ui(self)
|
|
71
72
|
|
|
73
|
+
# コマンドモードにバックグラウンドエグゼキュータを設定
|
|
74
|
+
@command_mode.background_executor = @background_executor if @background_executor
|
|
75
|
+
|
|
72
76
|
@running = true
|
|
73
77
|
setup_terminal
|
|
74
78
|
|
|
@@ -114,8 +118,29 @@ module Rufio
|
|
|
114
118
|
end
|
|
115
119
|
|
|
116
120
|
def main_loop
|
|
121
|
+
last_notification_check = Time.now
|
|
122
|
+
notification_message = nil
|
|
123
|
+
notification_time = nil
|
|
124
|
+
|
|
117
125
|
while @running
|
|
118
|
-
|
|
126
|
+
# バックグラウンドコマンドの完了チェック(0.5秒ごと)
|
|
127
|
+
if @background_executor && (Time.now - last_notification_check) > 0.5
|
|
128
|
+
if !@background_executor.running? && @background_executor.get_completion_message
|
|
129
|
+
notification_message = @background_executor.get_completion_message
|
|
130
|
+
notification_time = Time.now
|
|
131
|
+
@background_executor.instance_variable_set(:@completion_message, nil) # メッセージをクリア
|
|
132
|
+
end
|
|
133
|
+
last_notification_check = Time.now
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# 通知メッセージを表示(3秒間)
|
|
137
|
+
if notification_message && (Time.now - notification_time) < 3.0
|
|
138
|
+
draw_screen_with_notification(notification_message)
|
|
139
|
+
else
|
|
140
|
+
notification_message = nil if notification_message
|
|
141
|
+
draw_screen
|
|
142
|
+
end
|
|
143
|
+
|
|
119
144
|
handle_input
|
|
120
145
|
end
|
|
121
146
|
end
|
|
@@ -165,6 +190,23 @@ module Rufio
|
|
|
165
190
|
end
|
|
166
191
|
end
|
|
167
192
|
|
|
193
|
+
def draw_screen_with_notification(notification_message)
|
|
194
|
+
# 通常の画面を描画
|
|
195
|
+
draw_screen
|
|
196
|
+
|
|
197
|
+
# 通知メッセージを画面下部に表示
|
|
198
|
+
notification_line = @screen_height - 1
|
|
199
|
+
print "\e[#{notification_line};1H" # カーソルを画面下部に移動
|
|
200
|
+
|
|
201
|
+
# 通知メッセージを反転表示で目立たせる
|
|
202
|
+
message_display = " #{notification_message} "
|
|
203
|
+
if message_display.length > @screen_width
|
|
204
|
+
message_display = message_display[0...(@screen_width - 3)] + "..."
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
print "\e[7m#{message_display.ljust(@screen_width)}\e[0m"
|
|
208
|
+
end
|
|
209
|
+
|
|
168
210
|
def draw_header
|
|
169
211
|
current_path = @directory_listing.current_path
|
|
170
212
|
header = "📁 rufio - #{current_path}"
|
|
@@ -310,15 +352,22 @@ module Rufio
|
|
|
310
352
|
end
|
|
311
353
|
|
|
312
354
|
def draw_file_preview(selected_entry, width, height, left_offset)
|
|
355
|
+
# 事前計算(ループの外で一度だけ)
|
|
356
|
+
cursor_position = left_offset + CURSOR_OFFSET
|
|
357
|
+
max_chars_from_cursor = @screen_width - cursor_position
|
|
358
|
+
safe_width = [max_chars_from_cursor - 2, width - 2, 0].max
|
|
359
|
+
|
|
360
|
+
# プレビューコンテンツとWrapped linesを一度だけ計算
|
|
361
|
+
preview_content = nil
|
|
362
|
+
wrapped_lines = nil
|
|
363
|
+
|
|
364
|
+
if selected_entry && selected_entry[:type] == 'file'
|
|
365
|
+
preview_content = get_preview_content(selected_entry)
|
|
366
|
+
wrapped_lines = TextUtils.wrap_preview_lines(preview_content, safe_width - 1) if safe_width > 0
|
|
367
|
+
end
|
|
368
|
+
|
|
313
369
|
(0...height).each do |i|
|
|
314
370
|
line_num = i + CONTENT_START_LINE
|
|
315
|
-
# カーソル位置を左パネルの右端に設定
|
|
316
|
-
cursor_position = left_offset + CURSOR_OFFSET
|
|
317
|
-
|
|
318
|
-
# 画面の境界を厳密に計算
|
|
319
|
-
max_chars_from_cursor = @screen_width - cursor_position
|
|
320
|
-
# 区切り線(│)分を除いて、さらに安全マージンを取る
|
|
321
|
-
safe_width = [max_chars_from_cursor - 2, width - 2, 0].max
|
|
322
371
|
|
|
323
372
|
print "\e[#{line_num};#{cursor_position}H" # カーソル位置設定
|
|
324
373
|
print '│' # 区切り線
|
|
@@ -333,11 +382,8 @@ module Rufio
|
|
|
333
382
|
header += "[PREVIEW MODE]"
|
|
334
383
|
end
|
|
335
384
|
content_to_print = header
|
|
336
|
-
elsif
|
|
385
|
+
elsif wrapped_lines && i >= 2
|
|
337
386
|
# ファイルプレビュー(折り返し対応)
|
|
338
|
-
preview_content = get_preview_content(selected_entry)
|
|
339
|
-
wrapped_lines = TextUtils.wrap_preview_lines(preview_content, safe_width - 1) # スペース分を除く
|
|
340
|
-
|
|
341
387
|
# スクロールオフセットを適用
|
|
342
388
|
scroll_offset = @keybind_handler&.preview_scroll_offset || 0
|
|
343
389
|
display_line_index = i - 2 + scroll_offset
|
|
@@ -569,8 +615,12 @@ module Rufio
|
|
|
569
615
|
|
|
570
616
|
result = @command_mode.execute(command_string)
|
|
571
617
|
|
|
572
|
-
#
|
|
573
|
-
|
|
618
|
+
# バックグラウンドコマンドの場合は結果表示をスキップ
|
|
619
|
+
# (完了通知は別途メインループで表示される)
|
|
620
|
+
if result && !result.to_s.include?("🔄 バックグラウンドで実行中")
|
|
621
|
+
# コマンド実行結果をフローティングウィンドウで表示
|
|
622
|
+
@command_mode_ui.show_result(result)
|
|
623
|
+
end
|
|
574
624
|
|
|
575
625
|
# 画面を再描画
|
|
576
626
|
draw_screen
|
|
@@ -1028,7 +1078,7 @@ module Rufio
|
|
|
1028
1078
|
'z - Zoxide navigation',
|
|
1029
1079
|
'0 - Go to start directory',
|
|
1030
1080
|
'1-9 - Go to bookmark',
|
|
1031
|
-
'
|
|
1081
|
+
'P - Project mode',
|
|
1032
1082
|
': - Command mode',
|
|
1033
1083
|
'q - Quit',
|
|
1034
1084
|
''
|
data/lib/rufio/version.rb
CHANGED
data/lib/rufio.rb
CHANGED
|
@@ -30,6 +30,11 @@ require_relative "rufio/command_mode_ui"
|
|
|
30
30
|
require_relative "rufio/command_history"
|
|
31
31
|
require_relative "rufio/command_completion"
|
|
32
32
|
require_relative "rufio/shell_command_completion"
|
|
33
|
+
require_relative "rufio/command_logger"
|
|
34
|
+
require_relative "rufio/background_command_executor"
|
|
35
|
+
require_relative "rufio/native_scanner"
|
|
36
|
+
require_relative "rufio/native_scanner_magnus"
|
|
37
|
+
require_relative "rufio/native_scanner_zig"
|
|
33
38
|
|
|
34
39
|
# プロジェクトモード
|
|
35
40
|
require_relative "rufio/project_mode"
|