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
|
@@ -3,10 +3,18 @@
|
|
|
3
3
|
module Rufio
|
|
4
4
|
# コマンド補完機能を提供するクラス
|
|
5
5
|
class CommandCompletion
|
|
6
|
+
# CommandMode が直接処理する(DSL コマンドではない)コマンド
|
|
7
|
+
NAVIGATION_COMMANDS = %w[cd].freeze
|
|
8
|
+
|
|
6
9
|
# 初期化
|
|
7
|
-
# @param history [CommandHistory, nil]
|
|
10
|
+
# @param history [CommandHistory, CommandMode, nil] 履歴、または単独で渡すコマンドモード
|
|
8
11
|
# @param command_mode [CommandMode, nil] コマンドモード(スクリプト補完に使用)
|
|
9
12
|
def initialize(history = nil, command_mode = nil)
|
|
13
|
+
# CommandMode だけを渡す従来の呼び方も受け付ける。
|
|
14
|
+
if history.is_a?(CommandMode) && command_mode.nil?
|
|
15
|
+
command_mode = history
|
|
16
|
+
history = nil
|
|
17
|
+
end
|
|
10
18
|
@command_mode = command_mode || CommandMode.new
|
|
11
19
|
@shell_completion = ShellCommandCompletion.new
|
|
12
20
|
@history = history
|
|
@@ -23,6 +31,11 @@ module Rufio
|
|
|
23
31
|
rake_candidates('')
|
|
24
32
|
end
|
|
25
33
|
|
|
34
|
+
# ディレクトリ補完(cd で始まる場合)
|
|
35
|
+
if (match = input.match(/\A\s*cd\s+(.*)\z/))
|
|
36
|
+
return complete_cd(match[1])
|
|
37
|
+
end
|
|
38
|
+
|
|
26
39
|
# シェルコマンド補完(!で始まる場合)
|
|
27
40
|
if input.strip.start_with?('!')
|
|
28
41
|
return complete_shell_command(input.strip)
|
|
@@ -40,7 +53,7 @@ module Rufio
|
|
|
40
53
|
end
|
|
41
54
|
|
|
42
55
|
# 通常のコマンド補完(内部コマンド + rakeタスク)
|
|
43
|
-
available_commands = @command_mode.available_commands.map(&:to_s)
|
|
56
|
+
available_commands = @command_mode.available_commands.map(&:to_s) + NAVIGATION_COMMANDS
|
|
44
57
|
input_lower = input.downcase
|
|
45
58
|
candidates = available_commands.select do |command|
|
|
46
59
|
command.downcase.start_with?(input_lower)
|
|
@@ -82,6 +95,19 @@ module Rufio
|
|
|
82
95
|
# スクリプト候補を取得(ScriptRunner + ローカルスクリプト)
|
|
83
96
|
# @param prefix [String] 入力中の文字列
|
|
84
97
|
# @return [Array<String>] スクリプト候補(@付き)
|
|
98
|
+
# :cd のディレクトリ補完。相対パスは閲覧中ディレクトリを基準にする
|
|
99
|
+
def complete_cd(argument)
|
|
100
|
+
if argument.start_with?('~', '/') || File.absolute_path?(argument)
|
|
101
|
+
candidates = @shell_completion.complete_path(argument, directories_only: true)
|
|
102
|
+
else
|
|
103
|
+
base = @command_mode.browsing_directory || Dir.pwd
|
|
104
|
+
prefix = File.join(base, '')
|
|
105
|
+
candidates = @shell_completion.complete_path(File.join(base, argument), directories_only: true)
|
|
106
|
+
.map { |path| path.delete_prefix(prefix) }
|
|
107
|
+
end
|
|
108
|
+
candidates.map { |path| "cd #{path}/" }
|
|
109
|
+
end
|
|
110
|
+
|
|
85
111
|
def script_candidates(prefix)
|
|
86
112
|
@command_mode.complete_script("@#{prefix}")
|
|
87
113
|
end
|
|
@@ -5,8 +5,6 @@ module Rufio
|
|
|
5
5
|
class CommandHistory
|
|
6
6
|
DEFAULT_MAX_SIZE = 1000
|
|
7
7
|
|
|
8
|
-
attr_reader :size
|
|
9
|
-
|
|
10
8
|
# 初期化
|
|
11
9
|
# @param history_file [String] 履歴ファイルのパス
|
|
12
10
|
# @param max_size [Integer] 履歴の最大保存数
|
|
@@ -37,6 +35,11 @@ module Rufio
|
|
|
37
35
|
reset_position
|
|
38
36
|
end
|
|
39
37
|
|
|
38
|
+
# 呼び出し側の変更が履歴本体や上下移動の位置に影響しないスナップショット。
|
|
39
|
+
def entries
|
|
40
|
+
@history.map(&:dup)
|
|
41
|
+
end
|
|
42
|
+
|
|
40
43
|
# 前のコマンドを取得(上矢印キー相当)
|
|
41
44
|
# @return [String, nil] 前のコマンド、存在しない場合はnil
|
|
42
45
|
def previous
|
data/lib/rufio/command_mode.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'open3'
|
|
4
|
+
require_relative 'script_runner'
|
|
4
5
|
|
|
5
6
|
module Rufio
|
|
6
7
|
# コマンドモード - DSLコマンドを実行するための統一インターフェース
|
|
@@ -37,19 +38,14 @@ module Rufio
|
|
|
37
38
|
# @param config_file [String] 設定ファイルのパス
|
|
38
39
|
# @param job_manager [JobManager] ジョブマネージャー
|
|
39
40
|
def setup_script_path_manager(config_file:, job_manager:)
|
|
40
|
-
@job_manager = job_manager
|
|
41
41
|
@script_path_manager = ScriptPathManager.new(config_file)
|
|
42
|
-
|
|
43
|
-
@script_runner = ScriptRunner.new(
|
|
44
|
-
script_paths: @script_path_manager.paths,
|
|
45
|
-
job_manager: job_manager,
|
|
46
|
-
command_logger: @background_executor&.command_logger
|
|
47
|
-
)
|
|
42
|
+
setup_script_runner(script_paths: @script_path_manager.paths, job_manager: job_manager)
|
|
48
43
|
end
|
|
49
44
|
|
|
50
45
|
# 閲覧中ディレクトリを更新
|
|
51
46
|
# @param directory [String] 現在の閲覧ディレクトリ
|
|
52
47
|
def update_browsing_directory(directory)
|
|
48
|
+
@browsing_directory = directory
|
|
53
49
|
@local_script_scanner.update_directory(directory)
|
|
54
50
|
@rakefile_parser.update_directory(directory)
|
|
55
51
|
end
|
|
@@ -61,6 +57,11 @@ module Rufio
|
|
|
61
57
|
# 空のコマンドは無視
|
|
62
58
|
return nil if command_string.nil? || command_string.strip.empty?
|
|
63
59
|
|
|
60
|
+
# ディレクトリ移動 (cd <path>)。実際の移動は TerminalUI が行う
|
|
61
|
+
if (match = command_string.strip.match(/\Acd(?:\s+(.*))?\z/m))
|
|
62
|
+
return resolve_cd(match[1].to_s, working_dir)
|
|
63
|
+
end
|
|
64
|
+
|
|
64
65
|
# スクリプト実行 (@ で始まる場合)
|
|
65
66
|
if command_string.strip.start_with?('@')
|
|
66
67
|
return execute_script(command_string.strip[1..-1], working_dir)
|
|
@@ -108,6 +109,9 @@ module Rufio
|
|
|
108
109
|
"⚠️ コマンドが見つかりません: #{command_name}"
|
|
109
110
|
end
|
|
110
111
|
|
|
112
|
+
# 閲覧中ディレクトリ(:cd の補完基準)
|
|
113
|
+
attr_reader :browsing_directory
|
|
114
|
+
|
|
111
115
|
# 利用可能なコマンドのリストを取得
|
|
112
116
|
def available_commands
|
|
113
117
|
@commands.keys
|
|
@@ -190,6 +194,17 @@ module Rufio
|
|
|
190
194
|
# @param command_name [Symbol] コマンド名
|
|
191
195
|
# @param command [Hash] コマンド情報 { command: DslCommand, source: String }
|
|
192
196
|
# @return [Hash] 実行結果
|
|
197
|
+
# :cd の引数を移動先に解決する
|
|
198
|
+
# @return [Hash] { cd: { directory:, file: } } / { cd: :previous }、失敗時はエラーメッセージ
|
|
199
|
+
def resolve_cd(argument, working_dir)
|
|
200
|
+
return { cd: :previous } if argument.strip == '-'
|
|
201
|
+
|
|
202
|
+
result = PathJump.resolve(argument, working_dir || @browsing_directory || Dir.pwd)
|
|
203
|
+
return "⚠️ #{result[:error]}" unless result[:directory]
|
|
204
|
+
|
|
205
|
+
{ cd: { directory: result[:directory], file: result[:file] } }
|
|
206
|
+
end
|
|
207
|
+
|
|
193
208
|
def execute_unified_command(command_name, command)
|
|
194
209
|
dsl_cmd = command[:command]
|
|
195
210
|
|
|
@@ -214,27 +229,28 @@ module Rufio
|
|
|
214
229
|
# コマンドが空の場合
|
|
215
230
|
return { success: false, error: "コマンドが指定されていません" } if shell_command.strip.empty?
|
|
216
231
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
stdout, stderr, status = Open3.capture3(shell_command)
|
|
220
|
-
|
|
221
|
-
result = {
|
|
222
|
-
success: status.success?,
|
|
223
|
-
output: stdout.strip,
|
|
224
|
-
stderr: stderr.strip
|
|
225
|
-
}
|
|
232
|
+
run_shell_capture(shell_command)
|
|
233
|
+
end
|
|
226
234
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
235
|
+
# Open3.capture3 によるコマンド実行と結果ハッシュの組み立て
|
|
236
|
+
def run_shell_capture(command, chdir: nil, not_found_label: "コマンド", error_label: "コマンド実行")
|
|
237
|
+
stdout, stderr, status = if chdir
|
|
238
|
+
Open3.capture3(command, chdir: chdir)
|
|
239
|
+
else
|
|
240
|
+
Open3.capture3(command)
|
|
241
|
+
end
|
|
231
242
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
243
|
+
result = {
|
|
244
|
+
success: status.success?,
|
|
245
|
+
output: stdout.strip,
|
|
246
|
+
stderr: stderr.strip
|
|
247
|
+
}
|
|
248
|
+
result[:error] = "コマンドが失敗しました (終了コード: #{status.exitstatus})" unless status.success?
|
|
249
|
+
result
|
|
250
|
+
rescue Errno::ENOENT => e
|
|
251
|
+
{ success: false, error: "#{not_found_label}が見つかりません: #{e.message}" }
|
|
252
|
+
rescue StandardError => e
|
|
253
|
+
{ success: false, error: "#{error_label}エラー: #{e.message}" }
|
|
238
254
|
end
|
|
239
255
|
|
|
240
256
|
# スクリプトを実行する(@プレフィックス用)
|
|
@@ -276,36 +292,17 @@ module Rufio
|
|
|
276
292
|
# @param args [String, nil] スクリプトに渡す引数
|
|
277
293
|
# @return [String, Hash] 実行結果メッセージ
|
|
278
294
|
def execute_local_script(script, working_dir, args = nil)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
path: working_dir,
|
|
286
|
-
command: command
|
|
287
|
-
)
|
|
288
|
-
job.start
|
|
289
|
-
|
|
290
|
-
Thread.new do
|
|
291
|
-
execute_script_in_background(job, script, working_dir, command)
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
"🚀 ジョブを開始: #{script[:name]}"
|
|
295
|
-
else
|
|
296
|
-
# 同期実行
|
|
297
|
-
stdout, stderr, status = Open3.capture3(command, chdir: working_dir)
|
|
298
|
-
result = {
|
|
299
|
-
success: status.success?,
|
|
300
|
-
output: stdout.strip,
|
|
301
|
-
stderr: stderr.strip
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
# Logsに記録
|
|
305
|
-
log_execution("@#{script[:name]}", result)
|
|
295
|
+
runner = ScriptRunner.new(
|
|
296
|
+
script_paths: [], job_manager: @job_manager,
|
|
297
|
+
command_logger: @background_executor&.command_logger
|
|
298
|
+
)
|
|
299
|
+
result = runner.execute(script, working_dir: working_dir, args: args)
|
|
300
|
+
return "🚀 ジョブを開始: #{script[:name]}" if @job_manager
|
|
306
301
|
|
|
307
|
-
|
|
308
|
-
|
|
302
|
+
# CommandMode の表示用の戻り値は維持する。
|
|
303
|
+
result = { success: result[:success], output: result[:output].strip, stderr: result[:error].strip }
|
|
304
|
+
log_execution("@#{script[:name]}", result)
|
|
305
|
+
result
|
|
309
306
|
end
|
|
310
307
|
|
|
311
308
|
# 実行結果をCommandLoggerに記録
|
|
@@ -324,59 +321,6 @@ module Rufio
|
|
|
324
321
|
)
|
|
325
322
|
end
|
|
326
323
|
|
|
327
|
-
# スクリプトの実行コマンドを構築
|
|
328
|
-
# @param script [Hash] スクリプト情報
|
|
329
|
-
# @return [String] 実行コマンド
|
|
330
|
-
def build_script_command(script)
|
|
331
|
-
path = script[:path]
|
|
332
|
-
ext = File.extname(path).downcase
|
|
333
|
-
|
|
334
|
-
case ext
|
|
335
|
-
when '.rb'
|
|
336
|
-
"ruby #{path.shellescape}"
|
|
337
|
-
when '.py'
|
|
338
|
-
"python3 #{path.shellescape}"
|
|
339
|
-
when '.js'
|
|
340
|
-
"node #{path.shellescape}"
|
|
341
|
-
when '.ts'
|
|
342
|
-
"ts-node #{path.shellescape}"
|
|
343
|
-
when '.pl'
|
|
344
|
-
"perl #{path.shellescape}"
|
|
345
|
-
when '.ps1'
|
|
346
|
-
"pwsh #{path.shellescape}"
|
|
347
|
-
else
|
|
348
|
-
path.shellescape
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
# ローカルスクリプトをバックグラウンドで実行
|
|
353
|
-
def execute_script_in_background(job, script, working_dir, command)
|
|
354
|
-
stdout, stderr, status = Open3.capture3(command, chdir: working_dir)
|
|
355
|
-
|
|
356
|
-
job.append_log(stdout) unless stdout.empty?
|
|
357
|
-
job.append_log(stderr) unless stderr.empty?
|
|
358
|
-
|
|
359
|
-
if status.success?
|
|
360
|
-
job.complete(exit_code: status.exitstatus)
|
|
361
|
-
else
|
|
362
|
-
job.fail(exit_code: status.exitstatus)
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
# Logsに記録
|
|
366
|
-
log_execution("@#{script[:name]}", {
|
|
367
|
-
success: status.success?,
|
|
368
|
-
output: stdout.strip,
|
|
369
|
-
stderr: stderr.strip
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
@job_manager&.notify_completion(job)
|
|
373
|
-
rescue StandardError => e
|
|
374
|
-
job.append_log("Error: #{e.message}")
|
|
375
|
-
job.fail(exit_code: -1)
|
|
376
|
-
log_execution("@#{script[:name]}", { success: false, output: '', stderr: e.message })
|
|
377
|
-
@job_manager&.notify_completion(job)
|
|
378
|
-
end
|
|
379
|
-
|
|
380
324
|
# rakeタスクを実行する
|
|
381
325
|
# @param task_name [String] タスク名
|
|
382
326
|
# @param working_dir [String, nil] 作業ディレクトリ
|
|
@@ -393,32 +337,14 @@ module Rufio
|
|
|
393
337
|
working_dir ||= Dir.pwd
|
|
394
338
|
shell_command = "rake #{task_name.shellescape}"
|
|
395
339
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
unless status.success?
|
|
406
|
-
result[:error] = "コマンドが失敗しました (終了コード: #{status.exitstatus})"
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
# Logsに記録
|
|
410
|
-
log_execution("rake:#{task_name}", result)
|
|
411
|
-
|
|
412
|
-
result
|
|
413
|
-
rescue Errno::ENOENT => e
|
|
414
|
-
result = { success: false, error: "rakeが見つかりません: #{e.message}" }
|
|
415
|
-
log_execution("rake:#{task_name}", result)
|
|
416
|
-
result
|
|
417
|
-
rescue StandardError => e
|
|
418
|
-
result = { success: false, error: "rake実行エラー: #{e.message}" }
|
|
419
|
-
log_execution("rake:#{task_name}", result)
|
|
420
|
-
result
|
|
421
|
-
end
|
|
340
|
+
result = run_shell_capture(
|
|
341
|
+
shell_command,
|
|
342
|
+
chdir: working_dir,
|
|
343
|
+
not_found_label: "rake",
|
|
344
|
+
error_label: "rake実行"
|
|
345
|
+
)
|
|
346
|
+
log_execution("rake:#{task_name}", result)
|
|
347
|
+
result
|
|
422
348
|
end
|
|
423
349
|
|
|
424
350
|
# スクリプトパスからスクリプトを検索して実行を試みる
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'dialog_support'
|
|
4
|
+
|
|
3
5
|
require 'io/console'
|
|
4
6
|
|
|
5
7
|
module Rufio
|
|
6
8
|
# コマンドモードのUI - Tab補完とフローティングウィンドウでの結果表示
|
|
7
9
|
class CommandModeUI
|
|
10
|
+
include DialogSupport
|
|
11
|
+
|
|
8
12
|
def initialize(command_mode, dialog_renderer)
|
|
9
13
|
@command_mode = command_mode
|
|
10
14
|
@dialog_renderer = dialog_renderer
|
|
@@ -155,48 +159,6 @@ module Rufio
|
|
|
155
159
|
|
|
156
160
|
private
|
|
157
161
|
|
|
158
|
-
# オーバーレイダイアログを表示してキー入力を待つヘルパーメソッド
|
|
159
|
-
def show_overlay_dialog(title, content_lines, options = {}, &block)
|
|
160
|
-
# terminal_ui が利用可能で、screen と renderer が存在する場合のみオーバーレイを使用
|
|
161
|
-
use_overlay = @terminal_ui &&
|
|
162
|
-
@terminal_ui.respond_to?(:screen) &&
|
|
163
|
-
@terminal_ui.respond_to?(:renderer) &&
|
|
164
|
-
@terminal_ui.screen &&
|
|
165
|
-
@terminal_ui.renderer
|
|
166
|
-
|
|
167
|
-
if use_overlay
|
|
168
|
-
# オーバーレイを使用
|
|
169
|
-
@terminal_ui.show_overlay_dialog(title, content_lines, options, &block)
|
|
170
|
-
else
|
|
171
|
-
# フォールバック: 従来の方法
|
|
172
|
-
width = options[:width]
|
|
173
|
-
height = options[:height]
|
|
174
|
-
|
|
175
|
-
unless width && height
|
|
176
|
-
width, height = @dialog_renderer.calculate_dimensions(content_lines, {
|
|
177
|
-
title: title,
|
|
178
|
-
min_width: options[:min_width] || 40,
|
|
179
|
-
max_width: options[:max_width] || 80
|
|
180
|
-
})
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
x, y = @dialog_renderer.calculate_center(width, height)
|
|
184
|
-
|
|
185
|
-
@dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
|
|
186
|
-
border_color: options[:border_color] || "\e[37m",
|
|
187
|
-
title_color: options[:title_color] || "\e[1;33m",
|
|
188
|
-
content_color: options[:content_color] || "\e[37m"
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
key = block_given? ? yield : STDIN.getch
|
|
192
|
-
|
|
193
|
-
@dialog_renderer.clear_area(x, y, width, height)
|
|
194
|
-
@terminal_ui&.refresh_display
|
|
195
|
-
|
|
196
|
-
key
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
162
|
# 文字列配列の共通プレフィックスを見つける
|
|
201
163
|
# @param strings [Array<String>] 文字列配列
|
|
202
164
|
# @return [String] 共通プレフィックス
|
data/lib/rufio/config.rb
CHANGED
|
@@ -19,147 +19,92 @@ module Rufio
|
|
|
19
19
|
YAML_CONFIG_PATH = File.join(CONFIG_DIR, 'config.yml').freeze
|
|
20
20
|
LOCAL_YAML_PATH = './rufio.yml'
|
|
21
21
|
|
|
22
|
+
# Base English message definitions
|
|
23
|
+
BASE_MESSAGES = {
|
|
24
|
+
# Application messages
|
|
25
|
+
'app.interrupted' => 'rufio interrupted',
|
|
26
|
+
'app.error_occurred' => 'Error occurred',
|
|
27
|
+
'app.terminated' => 'rufio terminated',
|
|
28
|
+
|
|
29
|
+
# File operations
|
|
30
|
+
'file.not_found' => 'File not found',
|
|
31
|
+
'file.not_readable' => 'File not readable',
|
|
32
|
+
'file.read_error' => 'File read error',
|
|
33
|
+
'file.binary_file' => 'Binary file',
|
|
34
|
+
'file.cannot_preview' => 'Cannot preview',
|
|
35
|
+
'file.encoding_error' => 'Character encoding error - cannot read file',
|
|
36
|
+
'file.invalid_archive' => 'Invalid ZIP archive',
|
|
37
|
+
'file.preview_error' => 'Preview error',
|
|
38
|
+
'file.error_prefix' => 'Error',
|
|
39
|
+
|
|
40
|
+
# Keybind messages
|
|
41
|
+
'keybind.invalid_key' => 'invalid key',
|
|
42
|
+
'keybind.search_text' => 'Search text: ',
|
|
43
|
+
'keybind.no_matches' => 'No matches found.',
|
|
44
|
+
'keybind.press_any_key' => 'Press any key to continue...',
|
|
45
|
+
'keybind.input_filename' => 'Enter filename: ',
|
|
46
|
+
'keybind.input_dirname' => 'Enter directory name: ',
|
|
47
|
+
'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
|
|
48
|
+
'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
|
|
49
|
+
'keybind.file_exists' => 'File already exists',
|
|
50
|
+
'keybind.directory_exists' => 'Directory already exists',
|
|
51
|
+
'keybind.file_created' => 'File created',
|
|
52
|
+
'keybind.directory_created' => 'Directory created',
|
|
53
|
+
'keybind.creation_error' => 'Creation error',
|
|
54
|
+
|
|
55
|
+
# UI messages
|
|
56
|
+
'ui.operation_prompt' => 'Operation: ',
|
|
57
|
+
|
|
58
|
+
# Help text
|
|
59
|
+
'help.full' => 'j/k:move h:back l:enter o:open g/}:top/bottom G:global-search r:refresh f:filter s:search F:content a/A:create m/p/x:ops b:bookmark z:zoxide 1-9:goto q:quit',
|
|
60
|
+
'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:goto q:quit',
|
|
61
|
+
|
|
62
|
+
# Health check messages
|
|
63
|
+
'health.title' => 'rufio Health Check',
|
|
64
|
+
'health.ruby_version' => 'Ruby version',
|
|
65
|
+
'health.required_gems' => 'Required gems',
|
|
66
|
+
'health.fzf' => 'fzf (file search)',
|
|
67
|
+
'health.rga' => 'rga (content search)',
|
|
68
|
+
'health.zoxide' => 'zoxide (directory history)',
|
|
69
|
+
'health.bat' => 'bat (syntax highlight)',
|
|
70
|
+
'health.file_opener' => 'System file opener',
|
|
71
|
+
'health.summary' => 'Summary:',
|
|
72
|
+
'health.ok' => 'OK',
|
|
73
|
+
'health.warnings' => 'Warnings',
|
|
74
|
+
'health.errors' => 'Errors',
|
|
75
|
+
'health.all_passed' => 'All checks passed! rufio is ready to use.',
|
|
76
|
+
'health.critical_missing' => 'Some critical components are missing. rufio may not work properly.',
|
|
77
|
+
'health.optional_missing' => 'Some optional features are unavailable. Basic functionality will work.',
|
|
78
|
+
'health.all_gems_installed' => 'All required gems installed',
|
|
79
|
+
'health.missing_gems' => 'Missing gems',
|
|
80
|
+
'health.gem_install_instruction' => 'Run: gem install',
|
|
81
|
+
'health.tool_not_found' => 'not found',
|
|
82
|
+
'health.unknown_platform' => 'Unknown platform',
|
|
83
|
+
'health.file_open_may_not_work' => 'File opening may not work properly',
|
|
84
|
+
'health.macos_opener' => 'macOS file opener',
|
|
85
|
+
'health.linux_opener' => 'Linux file opener',
|
|
86
|
+
'health.windows_opener' => 'Windows file opener',
|
|
87
|
+
'health.install_brew' => 'Install: brew install',
|
|
88
|
+
'health.install_apt' => 'Install: apt install',
|
|
89
|
+
'health.install_guide' => 'Install: Check the installation guide for your platform',
|
|
90
|
+
'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
|
|
91
|
+
'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
|
|
92
|
+
}.freeze
|
|
93
|
+
|
|
22
94
|
# Multi-language message definitions
|
|
23
95
|
MESSAGES = {
|
|
24
|
-
'en' =>
|
|
25
|
-
|
|
26
|
-
'app.interrupted' => 'rufio interrupted',
|
|
27
|
-
'app.error_occurred' => 'Error occurred',
|
|
28
|
-
'app.terminated' => 'rufio terminated',
|
|
29
|
-
|
|
30
|
-
# File operations
|
|
31
|
-
'file.not_found' => 'File not found',
|
|
32
|
-
'file.not_readable' => 'File not readable',
|
|
33
|
-
'file.read_error' => 'File read error',
|
|
34
|
-
'file.binary_file' => 'Binary file',
|
|
35
|
-
'file.cannot_preview' => 'Cannot preview',
|
|
36
|
-
'file.encoding_error' => 'Character encoding error - cannot read file',
|
|
37
|
-
'file.preview_error' => 'Preview error',
|
|
38
|
-
'file.error_prefix' => 'Error',
|
|
39
|
-
|
|
40
|
-
# Keybind messages
|
|
41
|
-
'keybind.invalid_key' => 'invalid key',
|
|
42
|
-
'keybind.search_text' => 'Search text: ',
|
|
43
|
-
'keybind.no_matches' => 'No matches found.',
|
|
44
|
-
'keybind.press_any_key' => 'Press any key to continue...',
|
|
45
|
-
'keybind.input_filename' => 'Enter filename: ',
|
|
46
|
-
'keybind.input_dirname' => 'Enter directory name: ',
|
|
47
|
-
'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
|
|
48
|
-
'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
|
|
49
|
-
'keybind.file_exists' => 'File already exists',
|
|
50
|
-
'keybind.directory_exists' => 'Directory already exists',
|
|
51
|
-
'keybind.file_created' => 'File created',
|
|
52
|
-
'keybind.directory_created' => 'Directory created',
|
|
53
|
-
'keybind.creation_error' => 'Creation error',
|
|
54
|
-
|
|
55
|
-
# UI messages
|
|
56
|
-
'ui.operation_prompt' => 'Operation: ',
|
|
57
|
-
|
|
58
|
-
# Help text
|
|
59
|
-
'help.full' => 'j/k:move h:back l:enter o:open g/}:top/bottom G:global-search r:refresh f:filter s:search F:content a/A:create m/p/x:ops b:bookmark z:zoxide 1-9:goto q:quit',
|
|
60
|
-
'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:goto q:quit',
|
|
61
|
-
|
|
62
|
-
# Health check messages
|
|
63
|
-
'health.title' => 'rufio Health Check',
|
|
64
|
-
'health.ruby_version' => 'Ruby version',
|
|
65
|
-
'health.required_gems' => 'Required gems',
|
|
66
|
-
'health.fzf' => 'fzf (file search)',
|
|
67
|
-
'health.rga' => 'rga (content search)',
|
|
68
|
-
'health.zoxide' => 'zoxide (directory history)',
|
|
69
|
-
'health.bat' => 'bat (syntax highlight)',
|
|
70
|
-
'health.file_opener' => 'System file opener',
|
|
71
|
-
'health.summary' => 'Summary:',
|
|
72
|
-
'health.ok' => 'OK',
|
|
73
|
-
'health.warnings' => 'Warnings',
|
|
74
|
-
'health.errors' => 'Errors',
|
|
75
|
-
'health.all_passed' => 'All checks passed! rufio is ready to use.',
|
|
76
|
-
'health.critical_missing' => 'Some critical components are missing. rufio may not work properly.',
|
|
77
|
-
'health.optional_missing' => 'Some optional features are unavailable. Basic functionality will work.',
|
|
78
|
-
'health.all_gems_installed' => 'All required gems installed',
|
|
79
|
-
'health.missing_gems' => 'Missing gems',
|
|
80
|
-
'health.gem_install_instruction' => 'Run: gem install',
|
|
81
|
-
'health.tool_not_found' => 'not found',
|
|
82
|
-
'health.unknown_platform' => 'Unknown platform',
|
|
83
|
-
'health.file_open_may_not_work' => 'File opening may not work properly',
|
|
84
|
-
'health.macos_opener' => 'macOS file opener',
|
|
85
|
-
'health.linux_opener' => 'Linux file opener',
|
|
86
|
-
'health.windows_opener' => 'Windows file opener',
|
|
87
|
-
'health.install_brew' => 'Install: brew install',
|
|
88
|
-
'health.install_apt' => 'Install: apt install',
|
|
89
|
-
'health.install_guide' => 'Install: Check the installation guide for your platform',
|
|
90
|
-
'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
|
|
91
|
-
'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
'ja' => {
|
|
95
|
-
# Application messages
|
|
96
|
-
'app.interrupted' => 'rufio interrupted',
|
|
97
|
-
'app.error_occurred' => 'Error occurred',
|
|
98
|
-
'app.terminated' => 'rufio terminated',
|
|
99
|
-
|
|
100
|
-
# File operations
|
|
101
|
-
'file.not_found' => 'File not found',
|
|
102
|
-
'file.not_readable' => 'File not readable',
|
|
103
|
-
'file.read_error' => 'File read error',
|
|
104
|
-
'file.binary_file' => 'Binary file',
|
|
105
|
-
'file.cannot_preview' => 'Cannot preview',
|
|
96
|
+
'en' => BASE_MESSAGES,
|
|
97
|
+
'ja' => BASE_MESSAGES.merge(
|
|
106
98
|
'file.encoding_error' => 'Encoding error - cannot read file',
|
|
107
|
-
'file.preview_error' => 'Preview error',
|
|
108
|
-
'file.error_prefix' => 'Error',
|
|
109
|
-
|
|
110
|
-
# Keybind messages
|
|
111
99
|
'keybind.invalid_key' => 'Invalid key',
|
|
112
100
|
'keybind.search_text' => 'Search: ',
|
|
113
|
-
'keybind.no_matches' => 'No matches found.',
|
|
114
|
-
'keybind.press_any_key' => 'Press any key to continue...',
|
|
115
|
-
'keybind.input_filename' => 'Enter filename: ',
|
|
116
|
-
'keybind.input_dirname' => 'Enter directory name: ',
|
|
117
|
-
'keybind.invalid_filename' => 'Invalid filename (cannot contain / or \\)',
|
|
118
|
-
'keybind.invalid_dirname' => 'Invalid directory name (cannot contain / or \\)',
|
|
119
|
-
'keybind.file_exists' => 'File already exists',
|
|
120
|
-
'keybind.directory_exists' => 'Directory already exists',
|
|
121
|
-
'keybind.file_created' => 'File created',
|
|
122
|
-
'keybind.directory_created' => 'Directory created',
|
|
123
|
-
'keybind.creation_error' => 'Creation error',
|
|
124
|
-
|
|
125
|
-
# UI messages
|
|
126
|
-
'ui.operation_prompt' => 'Operation: ',
|
|
127
|
-
|
|
128
|
-
# Help text
|
|
129
101
|
'help.full' => 'j/k:move h:back l:enter o:open g/}:top/end G:global-search r:refresh f:filter s:search F:content a/A:create m/c/x:ops b:bookmark z:zoxide 1-9:jump q:quit',
|
|
130
102
|
'help.short' => 'j/k:move h:back l:enter o:open f:filter s:search b:bookmark z:zoxide 1-9:jump q:quit',
|
|
131
|
-
|
|
132
|
-
# Health check messages
|
|
133
|
-
'health.title' => 'rufio Health Check',
|
|
134
|
-
'health.ruby_version' => 'Ruby version',
|
|
135
|
-
'health.required_gems' => 'Required gems',
|
|
136
|
-
'health.fzf' => 'fzf (file search)',
|
|
137
|
-
'health.rga' => 'rga (content search)',
|
|
138
|
-
'health.zoxide' => 'zoxide (directory history)',
|
|
139
|
-
'health.bat' => 'bat (syntax highlight)',
|
|
140
|
-
'health.file_opener' => 'System file opener',
|
|
141
|
-
'health.summary' => 'Summary:',
|
|
142
|
-
'health.ok' => 'OK',
|
|
143
|
-
'health.warnings' => 'Warnings',
|
|
144
|
-
'health.errors' => 'Errors',
|
|
145
|
-
'health.all_passed' => 'All checks passed! rufio is ready to use.',
|
|
146
103
|
'health.critical_missing' => 'Critical components missing. rufio may not work properly.',
|
|
147
104
|
'health.optional_missing' => 'Optional features unavailable. Basic features will work.',
|
|
148
105
|
'health.all_gems_installed' => 'All required gems are installed',
|
|
149
|
-
'health.
|
|
150
|
-
|
|
151
|
-
'health.tool_not_found' => 'not found',
|
|
152
|
-
'health.unknown_platform' => 'Unknown platform',
|
|
153
|
-
'health.file_open_may_not_work' => 'File open may not work properly',
|
|
154
|
-
'health.macos_opener' => 'macOS file opener',
|
|
155
|
-
'health.linux_opener' => 'Linux file opener',
|
|
156
|
-
'health.windows_opener' => 'Windows file opener',
|
|
157
|
-
'health.install_brew' => 'Install: brew install',
|
|
158
|
-
'health.install_apt' => 'Install: apt install',
|
|
159
|
-
'health.install_guide' => 'Install: Check the installation guide for your platform',
|
|
160
|
-
'health.rga_releases' => 'Install: https://github.com/phiresky/ripgrep-all/releases',
|
|
161
|
-
'health.ruby_upgrade_needed' => 'Please upgrade Ruby to version 2.7.0 or higher'
|
|
162
|
-
}
|
|
106
|
+
'health.file_open_may_not_work' => 'File open may not work properly'
|
|
107
|
+
).freeze
|
|
163
108
|
}.freeze
|
|
164
109
|
|
|
165
110
|
class << self
|
|
@@ -272,14 +217,21 @@ module Rufio
|
|
|
272
217
|
return [] unless File.exist?(path)
|
|
273
218
|
|
|
274
219
|
yaml = YAML.safe_load(File.read(path), symbolize_names: true)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
yaml.select { |b| b.is_a?(Hash) && b[:path] && b[:name] }
|
|
220
|
+
filter_valid_bookmarks(yaml)
|
|
278
221
|
rescue StandardError => e
|
|
279
222
|
warn "Failed to load bookmarks: #{e.message}"
|
|
280
223
|
[]
|
|
281
224
|
end
|
|
282
225
|
|
|
226
|
+
# 有効なブックマーク要素のみを抽出
|
|
227
|
+
# @param bookmarks [Array<Hash>] ブックマーク配列
|
|
228
|
+
# @return [Array<Hash>] フィルタ済みブックマーク配列
|
|
229
|
+
def filter_valid_bookmarks(bookmarks)
|
|
230
|
+
return [] unless bookmarks.is_a?(Array)
|
|
231
|
+
|
|
232
|
+
bookmarks.select { |b| b.is_a?(Hash) && b[:path] && b[:name] }
|
|
233
|
+
end
|
|
234
|
+
|
|
283
235
|
# ブックマークを保存
|
|
284
236
|
# @param path [String] YAMLファイルのパス
|
|
285
237
|
# @param bookmarks [Array<Hash>] ブックマーク配列
|
|
@@ -398,9 +350,9 @@ module Rufio
|
|
|
398
350
|
end
|
|
399
351
|
|
|
400
352
|
def detect_language
|
|
401
|
-
#
|
|
353
|
+
# RUFIO_LANG or BENIYA_LANG (legacy) environment variable takes precedence
|
|
402
354
|
# This ensures English is default unless explicitly requested
|
|
403
|
-
env_lang = ENV['BENIYA_LANG']
|
|
355
|
+
env_lang = ENV['RUFIO_LANG'] || ENV['BENIYA_LANG']
|
|
404
356
|
|
|
405
357
|
if env_lang && !env_lang.empty?
|
|
406
358
|
# Extract language code (e.g., 'ja_JP.UTF-8' -> 'ja')
|