rufio 1.1.0 → 1.3.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +74 -3
  4. data/README_ja.md +71 -2
  5. data/Rakefile +14 -1
  6. data/bin/rufio +31 -2
  7. data/docs/fast_zip_design_ja.md +167 -0
  8. data/info/help.md +2 -0
  9. data/info/keybindings.md +3 -1
  10. data/info/welcome.md +1 -1
  11. data/lib/rufio/application.rb +12 -1
  12. data/lib/rufio/archive_controller.rb +211 -0
  13. data/lib/rufio/background_command_executor.rb +9 -0
  14. data/lib/rufio/background_status.rb +49 -0
  15. data/lib/rufio/bookmark_controller.rb +38 -121
  16. data/lib/rufio/bookmark_manager.rb +77 -331
  17. data/lib/rufio/command_completion.rb +6 -1
  18. data/lib/rufio/command_history.rb +5 -2
  19. data/lib/rufio/command_mode.rb +40 -134
  20. data/lib/rufio/command_mode_ui.rb +4 -42
  21. data/lib/rufio/config.rb +88 -136
  22. data/lib/rufio/config_loader.rb +3 -11
  23. data/lib/rufio/dialog_renderer.rb +64 -152
  24. data/lib/rufio/dialog_support.rb +69 -0
  25. data/lib/rufio/document_text_extractor.rb +167 -0
  26. data/lib/rufio/file_operation_controller.rb +49 -222
  27. data/lib/rufio/file_preview.rb +46 -2
  28. data/lib/rufio/global_search.rb +121 -9
  29. data/lib/rufio/help_menu.rb +8 -2
  30. data/lib/rufio/interpreter_resolver.rb +6 -11
  31. data/lib/rufio/job_manager.rb +2 -5
  32. data/lib/rufio/key_input.rb +77 -0
  33. data/lib/rufio/keybind_handler.rb +81 -115
  34. data/lib/rufio/local_script_scanner.rb +6 -18
  35. data/lib/rufio/native/aarch64-linux-gnu/librufio_zig.so +0 -0
  36. data/lib/rufio/native/aarch64-linux-musl/librufio_zig.so +0 -0
  37. data/lib/rufio/native/aarch64-macos/librufio_zig.dylib +0 -0
  38. data/lib/rufio/native/aarch64-windows/rufio_zig.dll +0 -0
  39. data/lib/rufio/native/x86_64-linux-gnu/librufio_zig.so +0 -0
  40. data/lib/rufio/native/x86_64-linux-musl/librufio_zig.so +0 -0
  41. data/lib/rufio/native/x86_64-macos/librufio_zig.dylib +0 -0
  42. data/lib/rufio/native/x86_64-windows/rufio_zig.dll +0 -0
  43. data/lib/rufio/native_content_search.rb +153 -0
  44. data/lib/rufio/native_scanner.rb +13 -68
  45. data/lib/rufio/native_scanner_zig.rb +106 -56
  46. data/lib/rufio/native_zip.rb +121 -0
  47. data/lib/rufio/navigation_controller.rb +18 -20
  48. data/lib/rufio/screen.rb +2 -12
  49. data/lib/rufio/script_executor.rb +6 -73
  50. data/lib/rufio/script_file.rb +44 -0
  51. data/lib/rufio/script_path_manager.rb +9 -43
  52. data/lib/rufio/script_runner.rb +13 -50
  53. data/lib/rufio/search_controller.rb +1 -1
  54. data/lib/rufio/shell_command_completion.rb +1 -5
  55. data/lib/rufio/shell_init.rb +90 -0
  56. data/lib/rufio/task_status.rb +5 -1
  57. data/lib/rufio/terminal_ui.rb +55 -294
  58. data/lib/rufio/text_utils.rb +26 -3
  59. data/lib/rufio/ui_renderer.rb +63 -40
  60. data/lib/rufio/version.rb +1 -1
  61. data/lib/rufio/zip_archive.rb +314 -0
  62. data/lib/rufio/zip_extractor.rb +346 -0
  63. data/lib/rufio/zip_writer.rb +268 -0
  64. data/lib/rufio/zoxide_integration.rb +5 -43
  65. data/lib/rufio.rb +5 -0
  66. data/lib_zig/rufio_native/Makefile +54 -17
  67. data/lib_zig/rufio_native/bench.sh +36 -0
  68. data/lib_zig/rufio_native/build.zig +32 -35
  69. data/lib_zig/rufio_native/src/cli.zig +99 -0
  70. data/lib_zig/rufio_native/src/main.zig +187 -83
  71. data/lib_zig/rufio_native/src/os.zig +889 -0
  72. data/lib_zig/rufio_native/src/search.zig +919 -0
  73. data/lib_zig/rufio_native/src/search_test.zig +88 -0
  74. data/lib_zig/rufio_native/src/zip.zig +482 -0
  75. metadata +125 -3
  76. data/lib/rufio/native/rufio_zig.bundle +0 -0
@@ -1,20 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'script_file'
4
+
3
5
  module Rufio
4
6
  # 拡張子からインタープリタを解決するクラス
5
7
  class InterpreterResolver
6
8
  # デフォルトの拡張子とインタープリタのマッピング
7
- DEFAULT_EXTENSIONS = {
8
- ".rb" => "ruby",
9
- ".py" => "python3",
10
- ".sh" => "bash",
11
- ".js" => "node",
12
- ".pl" => "perl",
13
- ".lua" => "lua",
14
- ".ts" => "ts-node",
15
- ".php" => "php",
16
- ".ps1" => nil # プラットフォーム依存
17
- }.freeze
9
+ # DSLでは .sh にインタープリタを指定し、WindowsのPowerShellを別途解決する。
10
+ DEFAULT_EXTENSIONS = ScriptFile::INTERPRETERS.merge(
11
+ ".sh" => "bash", ".lua" => "lua", ".php" => "php", ".ps1" => nil
12
+ ).freeze
18
13
 
19
14
  class << self
20
15
  # 拡張子からインタープリタを解決する
@@ -23,12 +23,9 @@ module Rufio
23
23
  job = TaskStatus.new(
24
24
  id: @next_id,
25
25
  name: name,
26
- path: path
26
+ path: path,
27
+ command: command
27
28
  )
28
- # commandを保存(TaskStatusに追加)
29
- job.instance_variable_set(:@command, command)
30
- job.define_singleton_method(:command) { @command }
31
-
32
29
  @jobs << job
33
30
  @next_id += 1
34
31
  job
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+
5
+ module Rufio
6
+ # ダイアログなどのブロッキングなキー入力の読み取り口。
7
+ # Windows ではメインループ用の入力スレッドが常に STDIN.getch で待機しているため、
8
+ # ダイアログが STDIN.getch を直接呼ぶと 1 キーずつ入力を奪い合い、2 回押さないと入力できない。
9
+ # 入力キューが接続されている間はキューから読み、それ以外は STDIN.getch を使う。
10
+ module KeyInput
11
+ # Ruby 3.2+ は Queue#pop(timeout:) で正確に待機する(sleep ポーリングは
12
+ # Windows のタイマー分解能 約15ms により遅延する)。それ以前はポーリング。
13
+ QUEUE_POP_TIMEOUT_SUPPORTED = Queue.instance_method(:pop).parameters.any? { |_, n| n == :timeout }
14
+ MULTIBYTE_TIMEOUT = 0.005
15
+
16
+ class << self
17
+ attr_reader :queue
18
+
19
+ def attach(queue)
20
+ @queue = queue
21
+ end
22
+
23
+ def detach
24
+ @queue = nil
25
+ end
26
+
27
+ # 1 文字(マルチバイト対応)を読むまでブロックする
28
+ def getch
29
+ queue = @queue
30
+ return STDIN.getch unless queue
31
+
32
+ loop do
33
+ char = build_char(queue.pop, queue)
34
+ return char if char
35
+ end
36
+ end
37
+
38
+ # キューからタイムアウト付きで Integer バイトを取り出す
39
+ def pop_with_timeout(queue, timeout_sec)
40
+ return queue.pop(timeout: timeout_sec) if QUEUE_POP_TIMEOUT_SUPPORTED
41
+
42
+ deadline = Time.now + timeout_sec
43
+ loop do
44
+ begin
45
+ return queue.pop(true)
46
+ rescue ThreadError
47
+ return nil if Time.now >= deadline
48
+
49
+ sleep 0.001
50
+ end
51
+ end
52
+ end
53
+
54
+ # first_byte_int に続く UTF-8 の後続バイトをキューから読み、1 文字に組み立てる
55
+ def build_char(first_byte_int, queue)
56
+ remaining = case first_byte_int
57
+ when 0x00..0x7F then 0
58
+ when 0xC0..0xDF then 1
59
+ when 0xE0..0xEF then 2
60
+ when 0xF0..0xF7 then 3
61
+ else 0
62
+ end
63
+ return first_byte_int.chr(Encoding::UTF_8) if remaining == 0
64
+
65
+ buf = first_byte_int.chr(Encoding::BINARY)
66
+ remaining.times do
67
+ b = pop_with_timeout(queue, MULTIBYTE_TIMEOUT)
68
+ return nil if b.nil?
69
+
70
+ buf << b.chr(Encoding::BINARY)
71
+ end
72
+ result = buf.force_encoding(Encoding::UTF_8)
73
+ result.valid_encoding? ? result : nil
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'dialog_support'
4
+
3
5
  require 'shellwords'
4
6
  require_relative 'file_opener'
5
7
  require_relative 'filter_manager'
@@ -11,11 +13,14 @@ require_relative 'selection_manager'
11
13
  require_relative 'file_operations'
12
14
  require_relative 'bookmark_manager'
13
15
  require_relative 'zoxide_integration'
16
+ require_relative 'archive_controller'
14
17
  require_relative 'dialog_renderer'
15
18
  require_relative 'logger'
16
19
 
17
20
  module Rufio
18
21
  class KeybindHandler
22
+ include DialogSupport
23
+
19
24
  # NavigationController への委譲
20
25
  def current_index
21
26
  @nav_controller.current_index
@@ -49,9 +54,7 @@ module Rufio
49
54
  return false unless @directory_listing
50
55
 
51
56
  FileUtils.mkdir_p(config_directory)
52
- exit_log_viewer_mode if @in_log_viewer_mode
53
- exit_help_mode if @in_help_mode
54
- exit_job_mode if @job_mode.active?
57
+ leave_other_modes(:config)
55
58
  @nav_controller.unfocus_preview_pane
56
59
  navigate_to_directory(config_directory)
57
60
  rescue SystemCallError => e
@@ -59,6 +62,23 @@ module Rufio
59
62
  false
60
63
  end
61
64
 
65
+ # モード遷移の入口。ヘルプ・検索のオーバーレイは元のモードを保持する。
66
+ def switch_mode(mode)
67
+ case mode
68
+ when :files then leave_other_modes(:files)
69
+ when :logs then enter_log_viewer_mode
70
+ when :jobs then enter_job_mode
71
+ when :config then enter_config_mode
72
+ when :global then start_global_search
73
+ when :help then show_help_dialog
74
+ else false
75
+ end
76
+ end
77
+
78
+ def navigate_to_directory(path)
79
+ @nav_controller.navigate_to_directory(path)
80
+ end
81
+
62
82
  def preview_scroll_offset
63
83
  @nav_controller.preview_scroll_offset
64
84
  end
@@ -112,6 +132,11 @@ module Rufio
112
132
  @script_path_manager, @notification_manager, @zoxide_integration
113
133
  )
114
134
 
135
+ # ArchiveController(選択アイテムの ZIP 圧縮・展開を担当)
136
+ @archive_controller = ArchiveController.new(
137
+ nil, @dialog_renderer, @selection_manager, @job_manager, @notification_manager
138
+ )
139
+
115
140
  # SearchController(fzf・rga検索を担当)
116
141
  @search_controller = SearchController.new(nil, @file_opener)
117
142
 
@@ -134,12 +159,14 @@ module Rufio
134
159
  @file_op_controller.set_directory_listing(directory_listing)
135
160
  @bookmark_controller.set_directory_listing(directory_listing)
136
161
  @search_controller.set_directory_listing(directory_listing)
162
+ @archive_controller.set_directory_listing(directory_listing)
137
163
  end
138
164
 
139
165
  def set_terminal_ui(terminal_ui)
140
166
  @terminal_ui = terminal_ui
141
167
  @nav_controller.set_terminal_ui(terminal_ui)
142
168
  @file_op_controller.set_terminal_ui(terminal_ui)
169
+ @archive_controller.set_terminal_ui(terminal_ui)
143
170
  @bookmark_controller.set_terminal_ui(terminal_ui)
144
171
  # terminal_ui が設定されたら、bookmark_manager と zoxide_integration にも渡す
145
172
  @bookmark_manager.set_terminal_ui(terminal_ui)
@@ -236,6 +263,10 @@ module Rufio
236
263
  entries[@nav_controller.current_index]
237
264
  end
238
265
 
266
+ def filter_mode?
267
+ @filter_manager.filter_mode
268
+ end
269
+
239
270
  def filter_active?
240
271
  @filter_manager.filter_active?
241
272
  end
@@ -265,11 +296,6 @@ module Rufio
265
296
  return false unless @directory_listing
266
297
  return true if @in_help_mode
267
298
 
268
- exit_log_viewer_mode if @in_log_viewer_mode
269
-
270
- # 現在のディレクトリを保存
271
- @pre_help_directory = @directory_listing.current_path
272
-
273
299
  # info ディレクトリに移動
274
300
  rufio_root = File.expand_path('../..', __dir__)
275
301
  info_dir = File.join(rufio_root, 'info')
@@ -277,6 +303,9 @@ module Rufio
277
303
  # info ディレクトリが存在することを確認
278
304
  return false unless Dir.exist?(info_dir)
279
305
 
306
+ leave_other_modes(:help_directory)
307
+ @pre_help_directory = @directory_listing.current_path
308
+
280
309
  # ヘルプモードを有効化
281
310
  @in_help_mode = true
282
311
 
@@ -315,14 +344,10 @@ module Rufio
315
344
  return false unless @directory_listing
316
345
  return true if @in_log_viewer_mode
317
346
 
318
- exit_help_mode if @in_help_mode
319
-
320
- # 現在のディレクトリを保存
347
+ FileUtils.mkdir_p(@log_dir)
348
+ leave_other_modes(:logs)
321
349
  @pre_log_viewer_directory = @directory_listing.current_path
322
350
 
323
- # log ディレクトリを作成(存在しない場合)
324
- FileUtils.mkdir_p(@log_dir) unless Dir.exist?(@log_dir)
325
-
326
351
  # log ディレクトリに移動
327
352
  navigate_to_directory(@log_dir)
328
353
 
@@ -351,45 +376,19 @@ module Rufio
351
376
 
352
377
  # ヘルプモード時の制限付き親ディレクトリナビゲーション
353
378
  def navigate_parent_with_restriction
354
- if @in_help_mode
355
- # info ディレクトリより上には移動できない
356
- rufio_root = File.expand_path('../..', __dir__)
357
- info_dir = File.join(rufio_root, 'info')
379
+ restricted_root = if @in_help_mode
380
+ File.join(File.expand_path('../..', __dir__), 'info')
381
+ elsif @in_log_viewer_mode
382
+ @log_dir
383
+ end
358
384
 
385
+ if restricted_root
359
386
  current_path = @directory_listing.current_path
360
-
361
- # 現在のパスが info ディレクトリ以下でない場合は移動を許可しない
362
- unless current_path.start_with?(info_dir)
363
- return false
364
- end
365
-
366
- # 現在のパスが info ディレクトリそのものの場合は移動を許可しない
367
- if current_path == info_dir
368
- return false
369
- end
370
-
371
- # info ディレクトリ配下であれば、通常のナビゲーションを実行
372
- navigate_parent
373
- elsif @in_log_viewer_mode
374
- # log ディレクトリより上には移動できない
375
- current_path = @directory_listing.current_path
376
-
377
- # 現在のパスが log ディレクトリ以下でない場合は移動を許可しない
378
- unless current_path.start_with?(@log_dir)
379
- return false
380
- end
381
-
382
- # 現在のパスが log ディレクトリそのものの場合は移動を許可しない
383
- if current_path == @log_dir
384
- return false
385
- end
386
-
387
- # log ディレクトリ配下であれば、通常のナビゲーションを実行
388
- navigate_parent
389
- else
390
- # ヘルプモード・ログビューワモード外では通常のナビゲーション
391
- navigate_parent
387
+ return false unless current_path.start_with?(restricted_root)
388
+ return false if current_path == restricted_root
392
389
  end
390
+
391
+ navigate_parent
393
392
  end
394
393
 
395
394
  # プレビューペイン関連メソッド(NavigationController に委譲)
@@ -444,52 +443,12 @@ module Rufio
444
443
 
445
444
  private
446
445
 
447
- # オーバーレイダイアログを表示してキー入力を待つヘルパーメソッド
448
- # terminal_ui が利用可能な場合はオーバーレイを使用、そうでなければ従来の方法を使用
449
- # @param title [String] ダイアログタイトル
450
- # @param content_lines [Array<String>] コンテンツ行
451
- # @param options [Hash] オプション
452
- # @yield キー入力処理(ブロックが与えられた場合)
453
- # @return [String] 入力されたキー
454
- def show_overlay_dialog(title, content_lines, options = {}, &block)
455
- # terminal_ui が利用可能で、screen と renderer が存在する場合のみオーバーレイを使用
456
- use_overlay = @terminal_ui &&
457
- @terminal_ui.respond_to?(:screen) &&
458
- @terminal_ui.respond_to?(:renderer) &&
459
- @terminal_ui.screen &&
460
- @terminal_ui.renderer
461
-
462
- if use_overlay
463
- # オーバーレイを使用
464
- @terminal_ui.show_overlay_dialog(title, content_lines, options, &block)
465
- else
466
- # フォールバック: 従来の方法
467
- width = options[:width]
468
- height = options[:height]
469
-
470
- unless width && height
471
- width, height = @dialog_renderer.calculate_dimensions(content_lines, {
472
- title: title,
473
- min_width: options[:min_width] || 40,
474
- max_width: options[:max_width] || 80
475
- })
476
- end
477
-
478
- x, y = @dialog_renderer.calculate_center(width, height)
479
-
480
- @dialog_renderer.draw_floating_window(x, y, width, height, title, content_lines, {
481
- border_color: options[:border_color] || "\e[37m",
482
- title_color: options[:title_color] || "\e[1;33m",
483
- content_color: options[:content_color] || "\e[37m"
484
- })
485
-
486
- key = block_given? ? yield : STDIN.getch
487
-
488
- @dialog_renderer.clear_area(x, y, width, height)
489
- @terminal_ui&.refresh_display
490
-
491
- key
492
- end
446
+ # 別モードへ入る際の終了順序はここだけで管理する。
447
+ def leave_other_modes(target)
448
+ exit_help_mode if target != :help_directory && help_mode?
449
+ exit_log_viewer_mode if target != :logs && log_viewer_mode?
450
+ exit_job_mode if target != :jobs && in_job_mode?
451
+ true
493
452
  end
494
453
 
495
454
  def navigate_parent
@@ -537,13 +496,14 @@ module Rufio
537
496
  when :rga_search then rga_search
538
497
  when :add_bookmark then add_bookmark
539
498
  when :bookmark_menu then show_bookmark_menu
540
- when :zoxide then show_zoxide_menu
499
+ when :zoxide then archive_or_zoxide
541
500
  when :start_dir then goto_start_directory
542
501
  when :job_mode then enter_job_mode
543
502
  when :help then show_help_dialog
544
503
  when :log_viewer then enter_log_viewer_mode
545
504
  when :command_mode then activate_command_mode
546
505
  when :quit then exit_request
506
+ when :quit_cd then quit_to_current_directory
547
507
  else
548
508
  false
549
509
  end
@@ -553,6 +513,14 @@ module Rufio
553
513
  @file_op_controller.show_exit_confirmation
554
514
  end
555
515
 
516
+ # 確認なしで終了し、シェル関数(rufio --shell-init)が移動する先として現在のディレクトリを伝える。
517
+ # ヘルプ・ログビューワは rufio 内部のディレクトリを表示しているため対象外。
518
+ def quit_to_current_directory
519
+ return false if @in_help_mode || @in_log_viewer_mode
520
+
521
+ :quit_cd
522
+ end
523
+
556
524
  def fzf_search
557
525
  @search_controller.fzf_search
558
526
  end
@@ -590,8 +558,6 @@ module Rufio
590
558
  true
591
559
  end
592
560
 
593
-
594
-
595
561
  def goto_start_directory
596
562
  @bookmark_controller.goto_start_directory
597
563
  end
@@ -615,17 +581,11 @@ module Rufio
615
581
  @bookmark_controller.show_script_paths_manager
616
582
  end
617
583
 
618
- def navigate_to_directory(path)
619
- return false unless @directory_listing
584
+ # 選択中のアイテムがあれば ZIP 圧縮・展開、なければ zoxide
585
+ def archive_or_zoxide
586
+ return @archive_controller.archive_selected if @selection_manager.any? && !@in_help_mode && !@in_log_viewer_mode
620
587
 
621
- result = @directory_listing.navigate_to_path(path)
622
- if result
623
- @nav_controller.select_index(0)
624
- @nav_controller.clear_filter_mode
625
- true
626
- else
627
- false
628
- end
588
+ show_zoxide_menu
629
589
  end
630
590
 
631
591
  # zoxide 機能
@@ -641,11 +601,9 @@ module Rufio
641
601
 
642
602
  # カーソルを画面下部の入力行に移動
643
603
  def move_to_input_line
644
- # 画面の最終行にカーソルを移動
645
- # terminal_uiから画面の高さを取得できない場合は、24行目(デフォルト)を使用
646
- screen_height = @terminal_ui&.instance_variable_get(:@screen_height) || 24
647
- print "\e[#{screen_height};1H" # 最終行の先頭にカーソル移動
648
- print "\e[2K" # 行全体をクリア
604
+ screen_height = @terminal_ui&.screen_height || 24
605
+ print "\e[#{screen_height};1H"
606
+ print "\e[2K"
649
607
  end
650
608
 
651
609
  # Escキーでキャンセル可能な入力処理
@@ -655,7 +613,7 @@ module Rufio
655
613
  input = []
656
614
 
657
615
  loop do
658
- char = STDIN.getch
616
+ char = KeyInput.getch
659
617
 
660
618
  case char
661
619
  when "\e" # Escape
@@ -699,6 +657,9 @@ module Rufio
699
657
 
700
658
  # ジョブモードに入る
701
659
  def enter_job_mode
660
+ return true if @job_mode.active?
661
+
662
+ leave_other_modes(:jobs)
702
663
  @job_mode.activate
703
664
  @terminal_ui&.set_job_mode(@job_mode, @job_manager, @notification_manager) if @terminal_ui
704
665
  true
@@ -737,6 +698,11 @@ module Rufio
737
698
  end
738
699
 
739
700
  # ジョブがあるかどうか
701
+ # バックグラウンドの圧縮・展開ジョブの進捗・完了を反映する(メインループから呼ぶ)
702
+ def poll_archive_jobs
703
+ @archive_controller.poll
704
+ end
705
+
740
706
  def has_jobs?
741
707
  @job_manager.has_jobs?
742
708
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'script_file'
4
+
3
5
  module Rufio
4
6
  # 閲覧中ディレクトリのスクリプトファイルを検出するスキャナー
5
7
  # カレントディレクトリ直下のスクリプトファイル(.sh, .rb, .py等)を検出し、
6
8
  # コマンドモードから @script.sh 形式で実行可能にする
7
9
  class LocalScriptScanner
8
10
  # サポートするスクリプト拡張子
9
- SUPPORTED_EXTENSIONS = %w[.sh .rb .py .pl .js .ts .ps1].freeze
11
+ SUPPORTED_EXTENSIONS = ScriptFile::EXTENSIONS
10
12
 
11
13
  # @param directory [String, nil] スキャン対象ディレクトリ
12
14
  def initialize(directory = nil)
@@ -33,27 +35,14 @@ module Rufio
33
35
  # @param name [String] スクリプト名(拡張子あり/なし)
34
36
  # @return [Hash, nil] スクリプト情報 { name:, path:, dir: } または nil
35
37
  def find_script(name)
36
- # 完全一致を優先
37
- script = available_scripts.find { |s| s[:name] == name }
38
- return script if script
39
-
40
- # 拡張子なしで検索
41
- SUPPORTED_EXTENSIONS.each do |ext|
42
- script = available_scripts.find { |s| s[:name] == "#{name}#{ext}" }
43
- return script if script
44
- end
45
-
46
- nil
38
+ ScriptFile.find(available_scripts, name)
47
39
  end
48
40
 
49
41
  # Tab補完候補を取得
50
42
  # @param prefix [String] 入力中の文字列
51
43
  # @return [Array<String>] 補完候補のスクリプト名
52
44
  def complete(prefix)
53
- available_scripts
54
- .map { |s| s[:name] }
55
- .select { |name| name.start_with?(prefix) }
56
- .sort
45
+ ScriptFile.complete(available_scripts, prefix)
57
46
  end
58
47
 
59
48
  private
@@ -85,8 +74,7 @@ module Rufio
85
74
  # @param filename [String] ファイル名
86
75
  # @return [Boolean]
87
76
  def script_file?(filename)
88
- ext = File.extname(filename).downcase
89
- SUPPORTED_EXTENSIONS.include?(ext)
77
+ ScriptFile.supported?(filename)
90
78
  end
91
79
  end
92
80
  end