rufio 0.50.0 → 0.60.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 +26 -0
- data/README.md +32 -36
- data/examples/config.yml +8 -0
- data/lib/rufio/command_completion.rb +20 -5
- data/lib/rufio/command_mode.rb +98 -5
- data/lib/rufio/config_loader.rb +37 -0
- data/lib/rufio/job_manager.rb +128 -0
- data/lib/rufio/job_mode.rb +146 -0
- data/lib/rufio/keybind_handler.rb +243 -232
- data/lib/rufio/notification_manager.rb +77 -0
- data/lib/rufio/script_config_loader.rb +101 -0
- data/lib/rufio/script_path_manager.rb +386 -0
- data/lib/rufio/script_runner.rb +216 -0
- data/lib/rufio/task_status.rb +118 -0
- data/lib/rufio/terminal_ui.rb +181 -495
- data/lib/rufio/version.rb +1 -1
- data/lib/rufio.rb +8 -4
- data/scripts/test_jobs/build_simulation.sh +29 -0
- data/scripts/test_jobs/deploy_simulation.sh +37 -0
- data/scripts/test_jobs/quick.sh +11 -0
- data/scripts/test_jobs/random_result.sh +23 -0
- data/scripts/test_jobs/slow_fail.sh +10 -0
- data/scripts/test_jobs/slow_success.sh +10 -0
- data/scripts/test_jobs/very_slow.sh +19 -0
- metadata +17 -7
- data/docs/file-preview-optimization-analysis.md +0 -759
- data/docs/file-preview-performance-issue-FIXED.md +0 -547
- data/lib/rufio/project_command.rb +0 -147
- data/lib/rufio/project_log.rb +0 -68
- data/lib/rufio/project_mode.rb +0 -58
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a880862d559dd867d819889c3458abac662ae2a262a93d09c552aec45f850f06
|
|
4
|
+
data.tar.gz: 1ede13dbac6ef6b6d0af6f0c0d10254a0a6cfa79869b6eafa8636bbd31f2e773
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 198ef8a599e51a0bf51e9935e06329a81e4d68fa1fd065665e6f26d44013a3dc9e4419b385296f29dbfb92a44ab1e744d6a72d1352c9c2fd270f8c9f08640f95
|
|
7
|
+
data.tar.gz: 062a514877116a7ee3c30211628a68740911ee98844693e9caea4571da783622c59fc4a8a7fe26ee512fad62f3b296833be74d815aab4d5ff9635cccac5261f3
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.60.0] - 2026-01-24
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **⌨️ スクリプト補完機能**: コマンドモードでスクリプトのTab補完が可能に
|
|
14
|
+
- `@`プレフィックスでスクリプト専用補完(例: `@bu` + Tab → `@build.sh`)
|
|
15
|
+
- 通常補完時も登録済みスクリプトが候補に表示
|
|
16
|
+
- `CommandCompletion`が`CommandMode`と連携してスクリプト候補を取得
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
- **🗑️ プロジェクトモード廃止**: `P`キーで起動するプロジェクトモードを削除
|
|
20
|
+
- `lib/rufio/project_mode.rb` - ProjectModeクラス
|
|
21
|
+
- `lib/rufio/project_command.rb` - ProjectCommandクラス
|
|
22
|
+
- `lib/rufio/project_log.rb` - ProjectLogクラス
|
|
23
|
+
- 関連するUI描画メソッド(`draw_project_mode_screen`等)
|
|
24
|
+
- 関連するキーハンドリング(`handle_project_mode_key`等)
|
|
25
|
+
- 関連するテストファイル
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- **📋 ヘルプ表示更新**: ヘルプダイアログに`J`キー(Job mode)を追加
|
|
29
|
+
- **🧹 コードクリーンアップ**: プロジェクトモード関連の未使用コードを削除
|
|
30
|
+
|
|
31
|
+
### Technical Details
|
|
32
|
+
- **テストカバレッジ**: 684 tests, 2474 assertions (all passing)
|
|
33
|
+
- **削除ファイル**: 7ファイル(ライブラリ3、テスト4)
|
|
34
|
+
- **影響範囲**: `keybind_handler.rb`, `terminal_ui.rb`, `rufio.rb`
|
|
35
|
+
|
|
10
36
|
## [0.41.0] - 2026-01-13
|
|
11
37
|
|
|
12
38
|
### Changed
|
data/README.md
CHANGED
|
@@ -8,13 +8,13 @@ Ruby製のターミナルベースファイルマネージャー
|
|
|
8
8
|
|
|
9
9
|
rufioは、Yaziにインスパイアされたターミナル上で動作するファイルマネージャーです。Rubyで実装されており、DSLコマンドによる拡張機能を備えています。軽量で高速な操作性を提供し、ファイルの閲覧・管理・検索機能を備えています。
|
|
10
10
|
|
|
11
|
-
### 🚀 v0.
|
|
11
|
+
### 🚀 v0.60.0の重要な更新
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
13
|
+
- **⌨️ スクリプト補完機能**: コマンドモードでスクリプトのTab補完が可能に
|
|
14
|
+
- **🗑️ プロジェクトモード廃止**: `P`キーのプロジェクトモードを削除(ジョブモード`J`に機能統合予定)
|
|
15
|
+
- **🧹 コードクリーンアップ**: 未使用コードの削除でコードベースをスリム化
|
|
16
16
|
|
|
17
|
-
詳細は [CHANGELOG
|
|
17
|
+
詳細は [CHANGELOG](./CHANGELOG.md) を参照してください。
|
|
18
18
|
|
|
19
19
|
## 特徴
|
|
20
20
|
|
|
@@ -215,10 +215,16 @@ rufio --help # ヘルプメッセージを表示
|
|
|
215
215
|
| キー | 機能 |
|
|
216
216
|
| --------- | ------------------------------------ |
|
|
217
217
|
| `b` | 現在のディレクトリをブックマークに追加 |
|
|
218
|
-
| `
|
|
218
|
+
| `B` | ブックマークメニュー(スクリプトパス管理含む) |
|
|
219
219
|
| `0` | 起動したディレクトリに移動 |
|
|
220
220
|
| `1`-`9` | 対応する番号のブックマークに移動 |
|
|
221
221
|
|
|
222
|
+
#### ジョブモード
|
|
223
|
+
|
|
224
|
+
| キー | 機能 |
|
|
225
|
+
| --------- | ------------------------------------ |
|
|
226
|
+
| `J` | ジョブモードに入る |
|
|
227
|
+
|
|
222
228
|
#### zoxide連携機能
|
|
223
229
|
|
|
224
230
|
| キー | 機能 |
|
|
@@ -371,17 +377,11 @@ rufio --help # ヘルプメッセージを表示
|
|
|
371
377
|
- 重複パスは自動検出
|
|
372
378
|
- 緑色のダイアログで表示(作成操作)
|
|
373
379
|
|
|
374
|
-
|
|
375
|
-
- **`
|
|
376
|
-
|
|
377
|
-
- **`
|
|
378
|
-
|
|
379
|
-
- 前後のスペースは自動削除
|
|
380
|
-
- **`d`キー**: カーソル位置のブックマークを削除
|
|
381
|
-
- 赤色の確認ダイアログ表示
|
|
382
|
-
- **`l`キー**: ログモードに入る
|
|
383
|
-
- **`:`キー**: コマンドモード起動
|
|
384
|
-
- **`ESC`キー**: プロジェクトモードを終了
|
|
380
|
+
**ブックマークメニュー (`B`キー):**
|
|
381
|
+
- **`1`**: 現在のディレクトリをブックマークに追加
|
|
382
|
+
- **`2`**: 現在のディレクトリをスクリプトパスに追加
|
|
383
|
+
- **`3`**: スクリプトパスを管理(一覧表示、削除、ジャンプ)
|
|
384
|
+
- **`4`**: ブックマーク一覧を表示
|
|
385
385
|
|
|
386
386
|
#### 高速ナビゲーション (`1`-`9`)
|
|
387
387
|
|
|
@@ -396,32 +396,27 @@ rufio --help # ヘルプメッセージを表示
|
|
|
396
396
|
- 名前の前後のスペースは自動削除されて保存される
|
|
397
397
|
- JSONファイルを直接編集することも可能
|
|
398
398
|
|
|
399
|
-
###
|
|
399
|
+
### スクリプトパス機能の詳細
|
|
400
400
|
|
|
401
|
-
####
|
|
401
|
+
#### スクリプトパスとは
|
|
402
402
|
|
|
403
|
-
|
|
403
|
+
スクリプトパスは、スクリプトファイルを配置するディレクトリを登録する機能です。登録したディレクトリ内のスクリプトは、コマンドモードで`@`プレフィックスを使って実行できます。
|
|
404
404
|
|
|
405
|
-
####
|
|
405
|
+
#### スクリプトパスの管理 (`B` → `3`)
|
|
406
406
|
|
|
407
|
-
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
-
|
|
412
|
-
- `.rb`ファイルを一覧表示
|
|
413
|
-
- 選択したスクリプトを実行
|
|
414
|
-
- プロジェクトコンテキストで実行
|
|
407
|
+
- **一覧表示**: 登録済みのスクリプトパスを一覧表示
|
|
408
|
+
- **j/k**: カーソル移動
|
|
409
|
+
- **d**: 選択したパスを削除
|
|
410
|
+
- **Enter**: 選択したディレクトリにジャンプ
|
|
411
|
+
- **ESC**: メニューを閉じる
|
|
415
412
|
|
|
416
|
-
####
|
|
413
|
+
#### スクリプト補完機能
|
|
417
414
|
|
|
418
|
-
|
|
415
|
+
コマンドモード(`:`)でスクリプトをTab補完できます:
|
|
419
416
|
|
|
420
|
-
-
|
|
421
|
-
-
|
|
422
|
-
-
|
|
423
|
-
- **セパレータ**: 左右画面の間に`│`を表示
|
|
424
|
-
- **フッター**: 反転表示でキーバインドを表示
|
|
417
|
+
- **`@`プレフィックス**: スクリプト専用補完
|
|
418
|
+
- 例: `@bu` + Tab → `@build.sh`
|
|
419
|
+
- **通常補完**: 内部コマンドとスクリプト両方を候補に表示
|
|
425
420
|
|
|
426
421
|
#### 操作色の統一
|
|
427
422
|
|
|
@@ -429,6 +424,7 @@ rufio --help # ヘルプメッセージを表示
|
|
|
429
424
|
- **青色ダイアログ**: ディレクトリ作成
|
|
430
425
|
- **黄色ダイアログ**: リネーム操作
|
|
431
426
|
- **赤色ダイアログ**: 削除操作
|
|
427
|
+
- **マゼンタダイアログ**: スクリプトパス管理
|
|
432
428
|
|
|
433
429
|
### zoxide連携機能の詳細
|
|
434
430
|
|
data/examples/config.yml
ADDED
|
@@ -5,8 +5,9 @@ module Rufio
|
|
|
5
5
|
class CommandCompletion
|
|
6
6
|
# 初期化
|
|
7
7
|
# @param history [CommandHistory, nil] コマンド履歴(オプション)
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
# @param command_mode [CommandMode, nil] コマンドモード(スクリプト補完に使用)
|
|
9
|
+
def initialize(history = nil, command_mode = nil)
|
|
10
|
+
@command_mode = command_mode || CommandMode.new
|
|
10
11
|
@shell_completion = ShellCommandCompletion.new
|
|
11
12
|
@history = history
|
|
12
13
|
end
|
|
@@ -15,9 +16,9 @@ module Rufio
|
|
|
15
16
|
# @param input [String] 入力されたテキスト
|
|
16
17
|
# @return [Array<String>] 補完候補のリスト
|
|
17
18
|
def complete(input)
|
|
18
|
-
#
|
|
19
|
+
# 入力が空の場合は内部コマンドとスクリプトを返す
|
|
19
20
|
if input.nil? || input.strip.empty?
|
|
20
|
-
return @command_mode.available_commands.map(&:to_s)
|
|
21
|
+
return @command_mode.available_commands.map(&:to_s) + script_candidates('')
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# シェルコマンド補完(!で始まる場合)
|
|
@@ -25,7 +26,12 @@ module Rufio
|
|
|
25
26
|
return complete_shell_command(input.strip)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
|
-
#
|
|
29
|
+
# スクリプト補完(@で始まる場合)
|
|
30
|
+
if input.strip.start_with?('@')
|
|
31
|
+
return @command_mode.complete_script(input.strip)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# 通常のコマンド補完(内部コマンド + スクリプト)
|
|
29
35
|
available_commands = @command_mode.available_commands.map(&:to_s)
|
|
30
36
|
input_lower = input.downcase
|
|
31
37
|
candidates = available_commands.select do |command|
|
|
@@ -61,6 +67,15 @@ module Rufio
|
|
|
61
67
|
|
|
62
68
|
private
|
|
63
69
|
|
|
70
|
+
# スクリプト候補を取得
|
|
71
|
+
# @param prefix [String] 入力中の文字列
|
|
72
|
+
# @return [Array<String>] スクリプト候補(@付き)
|
|
73
|
+
def script_candidates(prefix)
|
|
74
|
+
return [] unless @command_mode&.script_runner
|
|
75
|
+
|
|
76
|
+
@command_mode.complete_script("@#{prefix}")
|
|
77
|
+
end
|
|
78
|
+
|
|
64
79
|
# シェルコマンドの補完
|
|
65
80
|
# @param input [String] ! で始まる入力
|
|
66
81
|
# @return [Array<String>] 補完候補のリスト
|
data/lib/rufio/command_mode.rb
CHANGED
|
@@ -7,19 +7,54 @@ module Rufio
|
|
|
7
7
|
# すべてのコマンドはDslCommandとして扱われる
|
|
8
8
|
class CommandMode
|
|
9
9
|
attr_accessor :background_executor
|
|
10
|
+
attr_reader :script_runner, :script_path_manager
|
|
10
11
|
|
|
11
12
|
def initialize(background_executor = nil)
|
|
12
13
|
@commands = {}
|
|
13
14
|
@background_executor = background_executor
|
|
15
|
+
@script_runner = nil
|
|
16
|
+
@script_path_manager = nil
|
|
17
|
+
@job_manager = nil
|
|
14
18
|
load_builtin_commands
|
|
15
19
|
load_dsl_commands
|
|
16
20
|
end
|
|
17
21
|
|
|
22
|
+
# ScriptRunnerを設定する
|
|
23
|
+
# @param script_paths [Array<String>] スクリプトパス
|
|
24
|
+
# @param job_manager [JobManager] ジョブマネージャー
|
|
25
|
+
def setup_script_runner(script_paths:, job_manager:)
|
|
26
|
+
@job_manager = job_manager
|
|
27
|
+
@script_runner = ScriptRunner.new(
|
|
28
|
+
script_paths: script_paths,
|
|
29
|
+
job_manager: job_manager
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# ScriptPathManagerを設定する(設定ファイルベース)
|
|
34
|
+
# @param config_file [String] 設定ファイルのパス
|
|
35
|
+
# @param job_manager [JobManager] ジョブマネージャー
|
|
36
|
+
def setup_script_path_manager(config_file:, job_manager:)
|
|
37
|
+
@job_manager = job_manager
|
|
38
|
+
@script_path_manager = ScriptPathManager.new(config_file)
|
|
39
|
+
# ScriptRunnerも設定(ScriptPathManagerのパスを使用)
|
|
40
|
+
@script_runner = ScriptRunner.new(
|
|
41
|
+
script_paths: @script_path_manager.paths,
|
|
42
|
+
job_manager: job_manager
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
18
46
|
# コマンドを実行する
|
|
19
|
-
|
|
47
|
+
# @param command_string [String] コマンド文字列
|
|
48
|
+
# @param working_dir [String, nil] 作業ディレクトリ(スクリプト実行時に使用)
|
|
49
|
+
def execute(command_string, working_dir: nil)
|
|
20
50
|
# 空のコマンドは無視
|
|
21
51
|
return nil if command_string.nil? || command_string.strip.empty?
|
|
22
52
|
|
|
53
|
+
# スクリプト実行 (@ で始まる場合)
|
|
54
|
+
if command_string.strip.start_with?('@')
|
|
55
|
+
return execute_script(command_string.strip[1..-1], working_dir)
|
|
56
|
+
end
|
|
57
|
+
|
|
23
58
|
# シェルコマンドの実行 (! で始まる場合)
|
|
24
59
|
if command_string.strip.start_with?('!')
|
|
25
60
|
shell_command = command_string.strip[1..-1]
|
|
@@ -42,12 +77,18 @@ module Rufio
|
|
|
42
77
|
|
|
43
78
|
# 統一されたコマンドストアから検索
|
|
44
79
|
command = @commands[command_name]
|
|
45
|
-
|
|
46
|
-
|
|
80
|
+
if command
|
|
81
|
+
# 内部コマンドを実行
|
|
82
|
+
return execute_unified_command(command_name, command)
|
|
47
83
|
end
|
|
48
84
|
|
|
49
|
-
#
|
|
50
|
-
|
|
85
|
+
# 内部コマンドが見つからない場合、スクリプトパスから検索
|
|
86
|
+
if @script_path_manager || @script_runner
|
|
87
|
+
script_result = try_execute_script_from_paths(command_string.strip, working_dir)
|
|
88
|
+
return script_result if script_result
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
"⚠️ コマンドが見つかりません: #{command_name}"
|
|
51
92
|
end
|
|
52
93
|
|
|
53
94
|
# 利用可能なコマンドのリストを取得
|
|
@@ -87,6 +128,17 @@ module Rufio
|
|
|
87
128
|
end
|
|
88
129
|
end
|
|
89
130
|
|
|
131
|
+
# スクリプト名を補完する
|
|
132
|
+
# @param prefix [String] 入力中の文字列(@を含む)
|
|
133
|
+
# @return [Array<String>] 補完候補(@付き)
|
|
134
|
+
def complete_script(prefix)
|
|
135
|
+
return [] unless @script_runner
|
|
136
|
+
|
|
137
|
+
# @を除去して検索
|
|
138
|
+
search_prefix = prefix.sub(/^@/, '')
|
|
139
|
+
@script_runner.complete(search_prefix).map { |name| "@#{name}" }
|
|
140
|
+
end
|
|
141
|
+
|
|
90
142
|
private
|
|
91
143
|
|
|
92
144
|
# 組み込みコマンドをロードする
|
|
@@ -150,5 +202,46 @@ module Rufio
|
|
|
150
202
|
{ success: false, error: "コマンド実行エラー: #{e.message}" }
|
|
151
203
|
end
|
|
152
204
|
end
|
|
205
|
+
|
|
206
|
+
# スクリプトを実行する(@プレフィックス用)
|
|
207
|
+
# @param script_name [String] スクリプト名
|
|
208
|
+
# @param working_dir [String, nil] 作業ディレクトリ
|
|
209
|
+
# @return [String] 実行結果メッセージ
|
|
210
|
+
def execute_script(script_name, working_dir)
|
|
211
|
+
unless @script_runner
|
|
212
|
+
return "⚠️ スクリプトランナーが設定されていません"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
working_dir ||= Dir.pwd
|
|
216
|
+
|
|
217
|
+
job = @script_runner.run(script_name, working_dir: working_dir)
|
|
218
|
+
|
|
219
|
+
if job
|
|
220
|
+
"🚀 ジョブを開始: #{script_name}"
|
|
221
|
+
else
|
|
222
|
+
"⚠️ スクリプトが見つかりません: #{script_name}"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# スクリプトパスからスクリプトを検索して実行を試みる
|
|
227
|
+
# @param command_name [String] コマンド名
|
|
228
|
+
# @param working_dir [String, nil] 作業ディレクトリ
|
|
229
|
+
# @return [String, nil] 実行結果メッセージ、見つからない場合nil
|
|
230
|
+
def try_execute_script_from_paths(command_name, working_dir)
|
|
231
|
+
return nil unless @script_runner
|
|
232
|
+
|
|
233
|
+
script = @script_runner.find_script(command_name)
|
|
234
|
+
return nil unless script
|
|
235
|
+
|
|
236
|
+
working_dir ||= Dir.pwd
|
|
237
|
+
|
|
238
|
+
job = @script_runner.run(command_name, working_dir: working_dir)
|
|
239
|
+
|
|
240
|
+
if job
|
|
241
|
+
"🚀 ジョブを開始: #{script[:name]}"
|
|
242
|
+
else
|
|
243
|
+
nil
|
|
244
|
+
end
|
|
245
|
+
end
|
|
153
246
|
end
|
|
154
247
|
end
|
data/lib/rufio/config_loader.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'yaml'
|
|
3
4
|
require_relative 'config'
|
|
4
5
|
|
|
5
6
|
module Rufio
|
|
6
7
|
class ConfigLoader
|
|
7
8
|
CONFIG_PATH = File.expand_path('~/.config/rufio/config.rb').freeze
|
|
9
|
+
YAML_CONFIG_PATH = File.expand_path('~/.config/rufio/config.yml').freeze
|
|
8
10
|
|
|
9
11
|
class << self
|
|
10
12
|
def load_config
|
|
@@ -60,6 +62,41 @@ module Rufio
|
|
|
60
62
|
load_config[:command_history_size] || 1000
|
|
61
63
|
end
|
|
62
64
|
|
|
65
|
+
# スクリプトパスの配列を取得
|
|
66
|
+
# @return [Array<String>] 展開済みのスクリプトパス
|
|
67
|
+
def script_paths
|
|
68
|
+
yaml_config = load_yaml_config
|
|
69
|
+
paths = yaml_config[:script_paths] || default_script_paths
|
|
70
|
+
expand_script_paths(paths)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# デフォルトのスクリプトパス
|
|
74
|
+
# @return [Array<String>] デフォルトパス
|
|
75
|
+
def default_script_paths
|
|
76
|
+
[File.expand_path('~/.config/rufio/scripts')]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# スクリプトパスを展開
|
|
80
|
+
# @param paths [Array<String>] パスの配列
|
|
81
|
+
# @return [Array<String>] 展開済みのパス
|
|
82
|
+
def expand_script_paths(paths)
|
|
83
|
+
paths.map { |p| File.expand_path(p) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# YAML設定ファイルを読み込む
|
|
87
|
+
# @param path [String, nil] 設定ファイルのパス(nilの場合はデフォルト)
|
|
88
|
+
# @return [Hash] 設定内容
|
|
89
|
+
def load_yaml_config(path = nil)
|
|
90
|
+
config_path = path || YAML_CONFIG_PATH
|
|
91
|
+
return {} unless File.exist?(config_path)
|
|
92
|
+
|
|
93
|
+
yaml = YAML.safe_load(File.read(config_path), symbolize_names: true)
|
|
94
|
+
yaml || {}
|
|
95
|
+
rescue StandardError => e
|
|
96
|
+
warn "Failed to load YAML config: #{e.message}"
|
|
97
|
+
{}
|
|
98
|
+
end
|
|
99
|
+
|
|
63
100
|
private
|
|
64
101
|
|
|
65
102
|
def load_config_file
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'task_status'
|
|
4
|
+
|
|
5
|
+
module Rufio
|
|
6
|
+
# 複数のジョブを管理するクラス
|
|
7
|
+
# ジョブの追加、状態追跡、通知連携を行う
|
|
8
|
+
class JobManager
|
|
9
|
+
attr_reader :jobs
|
|
10
|
+
|
|
11
|
+
def initialize(notification_manager: nil)
|
|
12
|
+
@jobs = []
|
|
13
|
+
@notification_manager = notification_manager
|
|
14
|
+
@next_id = 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# ジョブを追加
|
|
18
|
+
# @param name [String] ジョブ名
|
|
19
|
+
# @param path [String] 実行ディレクトリ
|
|
20
|
+
# @param command [String] 実行コマンド
|
|
21
|
+
# @return [TaskStatus] 作成されたジョブ
|
|
22
|
+
def add_job(name:, path:, command:)
|
|
23
|
+
job = TaskStatus.new(
|
|
24
|
+
id: @next_id,
|
|
25
|
+
name: name,
|
|
26
|
+
path: path
|
|
27
|
+
)
|
|
28
|
+
# commandを保存(TaskStatusに追加)
|
|
29
|
+
job.instance_variable_set(:@command, command)
|
|
30
|
+
job.define_singleton_method(:command) { @command }
|
|
31
|
+
|
|
32
|
+
@jobs << job
|
|
33
|
+
@next_id += 1
|
|
34
|
+
job
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# IDでジョブを検索
|
|
38
|
+
# @param id [Integer] ジョブID
|
|
39
|
+
# @return [TaskStatus, nil]
|
|
40
|
+
def find_job(id)
|
|
41
|
+
@jobs.find { |job| job.id == id }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# ジョブ数を取得
|
|
45
|
+
# @return [Integer]
|
|
46
|
+
def job_count
|
|
47
|
+
@jobs.size
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# 実行中のジョブ数
|
|
51
|
+
# @return [Integer]
|
|
52
|
+
def running_count
|
|
53
|
+
@jobs.count(&:running?)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 完了したジョブ数
|
|
57
|
+
# @return [Integer]
|
|
58
|
+
def completed_count
|
|
59
|
+
@jobs.count(&:completed?)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 失敗したジョブ数
|
|
63
|
+
# @return [Integer]
|
|
64
|
+
def failed_count
|
|
65
|
+
@jobs.count(&:failed?)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# ステータスサマリーを取得
|
|
69
|
+
# @return [Hash] { total:, running:, done:, failed: }
|
|
70
|
+
def status_summary
|
|
71
|
+
{
|
|
72
|
+
total: @jobs.size,
|
|
73
|
+
running: running_count,
|
|
74
|
+
done: completed_count,
|
|
75
|
+
failed: failed_count
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# ジョブをキャンセル
|
|
80
|
+
# @param id [Integer] ジョブID
|
|
81
|
+
# @return [Boolean] キャンセル成功かどうか
|
|
82
|
+
def cancel_job(id)
|
|
83
|
+
job = find_job(id)
|
|
84
|
+
return false unless job
|
|
85
|
+
|
|
86
|
+
job.cancel
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# 完了したジョブをクリア
|
|
91
|
+
def clear_completed
|
|
92
|
+
@jobs.reject! { |job| job.completed? || job.cancelled? }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# ステータスバー用のテキストを生成
|
|
96
|
+
# @return [String] "3 jobs: 2 running, 1 done" のような形式
|
|
97
|
+
def status_bar_text
|
|
98
|
+
summary = status_summary
|
|
99
|
+
"#{summary[:total]} jobs: #{summary[:running]} running, #{summary[:done]} done"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# ジョブがあるかどうか
|
|
103
|
+
# @return [Boolean]
|
|
104
|
+
def has_jobs?
|
|
105
|
+
!@jobs.empty?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# 実行中のジョブがあるかどうか
|
|
109
|
+
# @return [Boolean]
|
|
110
|
+
def any_running?
|
|
111
|
+
@jobs.any?(&:running?)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# ジョブ完了時の通知を送信(NotificationManagerと連携)
|
|
115
|
+
# @param job [TaskStatus] 完了したジョブ
|
|
116
|
+
def notify_completion(job)
|
|
117
|
+
return unless @notification_manager
|
|
118
|
+
|
|
119
|
+
type = job.completed? ? :success : :error
|
|
120
|
+
@notification_manager.add(
|
|
121
|
+
job.name,
|
|
122
|
+
type,
|
|
123
|
+
duration: job.duration || 0,
|
|
124
|
+
exit_code: job.exit_code
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rufio
|
|
4
|
+
# ジョブモードのUI管理クラス
|
|
5
|
+
# ジョブ一覧の表示、選択、操作を行う
|
|
6
|
+
class JobMode
|
|
7
|
+
attr_reader :selected_index
|
|
8
|
+
|
|
9
|
+
def initialize(job_manager:)
|
|
10
|
+
@job_manager = job_manager
|
|
11
|
+
@active = false
|
|
12
|
+
@selected_index = 0
|
|
13
|
+
@log_mode = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# ジョブモードを有効化
|
|
17
|
+
def activate
|
|
18
|
+
@active = true
|
|
19
|
+
@selected_index = 0
|
|
20
|
+
@log_mode = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# ジョブモードを無効化
|
|
24
|
+
def deactivate
|
|
25
|
+
@active = false
|
|
26
|
+
@log_mode = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# ジョブモードが有効かどうか
|
|
30
|
+
# @return [Boolean]
|
|
31
|
+
def active?
|
|
32
|
+
@active
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# ログモードが有効かどうか
|
|
36
|
+
# @return [Boolean]
|
|
37
|
+
def log_mode?
|
|
38
|
+
@log_mode
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# ログモードに入る
|
|
42
|
+
def enter_log_mode
|
|
43
|
+
@log_mode = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# ログモードを終了
|
|
47
|
+
def exit_log_mode
|
|
48
|
+
@log_mode = false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# 下に移動
|
|
52
|
+
def move_down
|
|
53
|
+
max_index = [@job_manager.job_count - 1, 0].max
|
|
54
|
+
@selected_index = [@selected_index + 1, max_index].min
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 上に移動
|
|
58
|
+
def move_up
|
|
59
|
+
@selected_index = [@selected_index - 1, 0].max
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 先頭に移動
|
|
63
|
+
def move_to_top
|
|
64
|
+
@selected_index = 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# 末尾に移動
|
|
68
|
+
def move_to_bottom
|
|
69
|
+
@selected_index = [@job_manager.job_count - 1, 0].max
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# 選択中のジョブを取得
|
|
73
|
+
# @return [TaskStatus, nil]
|
|
74
|
+
def selected_job
|
|
75
|
+
return nil if @job_manager.jobs.empty?
|
|
76
|
+
|
|
77
|
+
@job_manager.jobs[@selected_index]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# 選択中のジョブをキャンセル
|
|
81
|
+
# @return [Boolean]
|
|
82
|
+
def cancel_selected_job
|
|
83
|
+
job = selected_job
|
|
84
|
+
return false unless job
|
|
85
|
+
|
|
86
|
+
@job_manager.cancel_job(job.id)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# キー入力を処理
|
|
90
|
+
# @param key [String] 入力されたキー
|
|
91
|
+
# @return [Boolean, Symbol] 処理結果
|
|
92
|
+
def handle_key(key)
|
|
93
|
+
# ログモード中の処理
|
|
94
|
+
if @log_mode
|
|
95
|
+
return handle_log_mode_key(key)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
case key
|
|
99
|
+
when 'j'
|
|
100
|
+
move_down
|
|
101
|
+
true
|
|
102
|
+
when 'k'
|
|
103
|
+
move_up
|
|
104
|
+
true
|
|
105
|
+
when 'g'
|
|
106
|
+
move_to_top
|
|
107
|
+
true
|
|
108
|
+
when 'G'
|
|
109
|
+
move_to_bottom
|
|
110
|
+
true
|
|
111
|
+
when 'x'
|
|
112
|
+
cancel_selected_job
|
|
113
|
+
true
|
|
114
|
+
when ' ' # Space
|
|
115
|
+
enter_log_mode if selected_job
|
|
116
|
+
:show_log
|
|
117
|
+
when "\e" # Escape
|
|
118
|
+
deactivate
|
|
119
|
+
:exit
|
|
120
|
+
else
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
# ログモード中のキー処理
|
|
128
|
+
# @param key [String] 入力されたキー
|
|
129
|
+
# @return [Boolean, Symbol]
|
|
130
|
+
def handle_log_mode_key(key)
|
|
131
|
+
case key
|
|
132
|
+
when "\e" # Escape
|
|
133
|
+
exit_log_mode
|
|
134
|
+
true
|
|
135
|
+
when 'j'
|
|
136
|
+
# ログのスクロール(将来実装)
|
|
137
|
+
true
|
|
138
|
+
when 'k'
|
|
139
|
+
# ログのスクロール(将来実装)
|
|
140
|
+
true
|
|
141
|
+
else
|
|
142
|
+
false
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|