rufio 0.71.0 → 0.81.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b269821deb0be4866afdc75e58a1369de3448324cee49e6c4f7e68febbae9fff
4
- data.tar.gz: 835570061c06d6a5adc439d2dfb67c62313fccf101cdb6fb068976f5d72d5607
3
+ metadata.gz: 533e0a5b32ced89d9d65bfd1abaafee73bada752cc24848538abc05e878a3f41
4
+ data.tar.gz: f614bb8ee10f320c3b8e3387f5374b5c1bc2c2b9299112f0640061dffcb47b05
5
5
  SHA512:
6
- metadata.gz: 8ba615e62db5874737085807f29825e92c3447a094aeb7349c77b8d0adab4e39fc2ce4bfe40fa53f354b23057e81a29077a7177a6f31379cf140ff876bd2bbc7
7
- data.tar.gz: b8ac577e995fb0a1cbc7776246f3443e2d137914a84fa94b02b013a3bca5f9124b30b42b03cb6fedbe8bb5d33fcadfbea4d9622023b446df5abb5288871bf978
6
+ metadata.gz: a0f9f950cfa19fc467fe220614f298ac35473aa8e9112e7776b683b9414e39436f4cce7bf5f5d3cf3a481dbf48545e2e971a9773e3e68bc11bfae53c6b63fc13
7
+ data.tar.gz: cff712ea8904a749d0d3f646bc0b9a016c085a043bf08bc6cf304d1f1bbbc5a4e94daf7e05464334f6da56c0bdcf32f6411e8d0787a7763b303428bde0993f65
data/CHANGELOG.md CHANGED
@@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+ - **Bookmark Jump Highlight**: When cycling bookmarks with `Tab`, the target bookmark in the top bar is highlighted in cyan for 500ms, then automatically fades back to gray
12
+
13
+ ### Changed
14
+ - **UI Layout**: Merged mode tabs and title bar into a single combined bottom row
15
+ - **Top bar (y=0)**: Bookmark list `0.start │ 1.name │ 2.name ...`, job status, FPS (test mode)
16
+ - **Bottom bar (y=h-1)**: Mode tabs + current path + version — all in one line
17
+ - Powerline-style separators: active tab gets a `\uE0B0` cyan exit arrow; version section uses `\uE0B2` cyan entry arrow with cyan background and black bold text
18
+ - **`Tab` key**: Changed from mode cycling to **bookmark cycling** (Files mode only)
19
+ - In Files mode: jump to next registered bookmark in sequence (wraps to first)
20
+ - In other modes (Logs / Jobs / Help): no action
21
+ - Mode switching is done via `J` / `L` / `?` keys and `Shift+Tab`
22
+
23
+ ### Fixed
24
+ - **zoxide 2-digit input**: Fixed a bug where pressing a single digit in the zoxide history dialog immediately navigated, making it impossible to enter 2-digit numbers (e.g., `12`)
25
+ - **Jobs mode tab highlight**: Fixed active tab not highlighting when entering Jobs mode from Logs or Help mode
26
+ - **Jobs mode navigation**: Fixed inability to switch away from Jobs mode using `L`, `?` keys
27
+
28
+ ## [0.80.0] - 2026-02-21
29
+
30
+ ### Added
31
+ - **Syntax Highlighting**: File preview now supports syntax highlighting via `bat`
32
+ - 19 languages supported: Ruby, Python, JS, TS, Go, Rust, Shell, C, C++, Java, SQL, TOML, YAML, JSON, HTML, CSS, Markdown, Dockerfile, Makefile
33
+ - Graceful fallback to plain text when `bat` is not installed
34
+ - mtime-based cache for instant re-display of previously viewed files
35
+ - Health check support (`rufio -c`) now reports `bat` availability
36
+ - **New classes**: `AnsiLineParser`, `SyntaxHighlighter`
37
+ - **New tests**: `test_ansi_line_parser.rb` (25 tests), `test_syntax_highlighter.rb` (+7 async tests)
38
+
39
+ ### Fixed
40
+ - **Cursor flickering** when navigating source directories: Changed `Renderer#render` from per-line `print` (immediate flush) to a single buffered `write` call — terminal updates are now atomic
41
+ - **Navigation lag** when moving between source files: `bat` is now executed asynchronously in a background thread; the current frame immediately falls back to plain text, then re-renders with highlighting on completion
42
+
43
+ ### Changed
44
+ - **`Renderer#render`**: Replaced row-by-row `print` with single `write(buf)` + `flush` for atomic output
45
+ - **`SyntaxHighlighter`**: Added `highlight_async` (Thread + Mutex + pending guard) alongside existing `highlight`
46
+ - **`TerminalUI`**: Preview caching extended with `highlighted` / `highlighted_wrapped` keys; `@highlight_updated` flag added for async re-render notification
47
+ - **`FilePreview#determine_file_type`**: Added Go, Rust, Shell, TOML, SQL, C, C++, Java, Dockerfile, Makefile language detection
48
+
49
+ ### Technical Details
50
+ - **New files**: `lib/rufio/ansi_line_parser.rb`, `lib/rufio/syntax_highlighter.rb`
51
+ - **Modified files**: `lib/rufio/renderer.rb`, `lib/rufio/terminal_ui.rb`, `lib/rufio/file_preview.rb`, `lib/rufio/health_checker.rb`, `lib/rufio/config.rb`, `lib/rufio.rb`
52
+ - **For details**: [CHANGELOG_v0.80.0.md](./docs/CHANGELOG_v0.80.0.md)
53
+
10
54
  ## [0.71.0] - 2026-02-16
11
55
 
12
56
  ### Added
@@ -611,6 +655,7 @@ For detailed information, see [CHANGELOG_v0.4.0.md](./docs/CHANGELOG_v0.4.0.md)
611
655
 
612
656
  ### Detailed Release Notes
613
657
 
658
+ - [v0.80.0](./docs/CHANGELOG_v0.80.0.md) - Syntax Highlighting & Rendering Fixes
614
659
  - [v0.31.0](./docs/CHANGELOG_v0.31.0.md) - Experimental Native Scanner Implementation
615
660
  - [v0.30.0](./docs/CHANGELOG_v0.30.0.md) - Help System Overhaul
616
661
  - [v0.21.0](./docs/CHANGELOG_v0.21.0.md) - Copy Feature & Code Refactoring
data/README.md CHANGED
@@ -40,7 +40,7 @@ rufio is not just a file manager. It's a **tool runtime execution environment**.
40
40
  ### As a File Manager
41
41
 
42
42
  - **Vim-like Key Bindings**: Intuitive navigation
43
- - **Real-time Preview**: Instantly display file contents
43
+ - **Real-time Preview**: Instantly display file contents with syntax highlighting (via `bat`)
44
44
  - **Fast Search**: Integration with fzf/rga
45
45
  - **Bookmarks**: Quick access to frequently used directories
46
46
  - **zoxide Integration**: Smart directory history
@@ -123,6 +123,7 @@ rufio /path/to # Launch in specified directory
123
123
  | `B` | Bookmark menu |
124
124
  | `0` | Return to startup directory |
125
125
  | `1-9` | Jump to bookmark |
126
+ | `Tab` | Cycle to next bookmark (Files mode only) |
126
127
  | `z` | zoxide history |
127
128
 
128
129
  ### Tool Runtime
@@ -133,7 +134,6 @@ rufio /path/to # Launch in specified directory
133
134
  | `J` | Job mode |
134
135
  | `L` | View execution logs |
135
136
  | `?` | Help |
136
- | `Tab` | Switch mode (Files → Logs → Jobs → Help) |
137
137
  | `Shift+Tab` | Switch mode (reverse) |
138
138
 
139
139
  ## Command Mode
@@ -290,22 +290,41 @@ script_paths:
290
290
 
291
291
  ## External Tool Integration
292
292
 
293
- rufio integrates with the following external tools to extend functionality:
293
+ rufio integrates with the following external tools to extend functionality.
294
+ All tools are **optional** — rufio works without them, falling back to built-in behavior.
294
295
 
295
- | Tool | Purpose | Key |
296
- |------|---------|-----|
297
- | fzf | File name search | `s` |
298
- | rga | File content search | `F` |
299
- | zoxide | Directory history | `z` |
296
+ | Tool | Purpose | Key | Required? |
297
+ |------|---------|-----|-----------|
298
+ | fzf | File name search | `s` | Optional |
299
+ | rga | File content search | `F` | Optional |
300
+ | zoxide | Directory history | `z` | Optional |
301
+ | bat | Syntax highlighting in preview | — | Optional |
300
302
 
301
- ### Installation
303
+ ### bat — Syntax Highlighting
304
+
305
+ When `bat` is installed, code files in the preview pane are displayed with full syntax
306
+ highlighting (Ruby, Python, Go, Rust, TypeScript, and 15+ more languages).
307
+
308
+ Highlighting is loaded asynchronously — navigation stays fast even on large source trees.
309
+
310
+ ```bash
311
+ # macOS
312
+ brew install bat
313
+
314
+ # Ubuntu/Debian
315
+ apt install bat
316
+ ```
317
+
318
+ Run `rufio -c` to verify bat is detected correctly.
319
+
320
+ ### Installation (all tools)
302
321
 
303
322
  ```bash
304
323
  # macOS
305
- brew install fzf rga zoxide
324
+ brew install fzf bat rga zoxide
306
325
 
307
326
  # Ubuntu/Debian
308
- apt install fzf zoxide
327
+ apt install fzf bat zoxide
309
328
  # rga requires separate installation: https://github.com/phiresky/ripgrep-all
310
329
  ```
311
330
 
data/README_ja.md CHANGED
@@ -42,7 +42,7 @@ rufioは単なるファイルマネージャーではありません。**ツー
42
42
  ### ファイルマネージャーとして
43
43
 
44
44
  - **Vimライクなキーバインド**: 直感的なナビゲーション
45
- - **リアルタイムプレビュー**: ファイル内容を即座に表示
45
+ - **リアルタイムプレビュー**: ファイル内容を即座に表示(`bat` によるシンタックスハイライト対応)
46
46
  - **高速検索**: fzf/rgaとの連携
47
47
  - **ブックマーク**: よく使うディレクトリに素早くアクセス
48
48
  - **zoxide連携**: スマートなディレクトリ履歴
@@ -125,6 +125,7 @@ rufio /path/to # 指定したディレクトリで起動
125
125
  | `B` | ブックマークメニュー |
126
126
  | `0` | 起動ディレクトリに戻る |
127
127
  | `1-9` | ブックマークにジャンプ |
128
+ | `Tab` | 次のブックマークへ循環移動(Filesモード限定) |
128
129
  | `z` | zoxide履歴 |
129
130
 
130
131
  ### ツールランタイム
@@ -135,6 +136,7 @@ rufio /path/to # 指定したディレクトリで起動
135
136
  | `J` | ジョブモード |
136
137
  | `L` | 実行ログ表示 |
137
138
  | `?` | ヘルプ |
139
+ | `Shift+Tab` | モード切り替え(逆順) |
138
140
 
139
141
  ## コマンドモード
140
142
 
@@ -255,22 +257,42 @@ SCRIPT_PATHS = [
255
257
 
256
258
  ## 外部ツール連携
257
259
 
258
- rufioは以下の外部ツールと連携して機能を拡張します:
260
+ rufioは以下の外部ツールと連携して機能を拡張します。
261
+ すべて**オプション**です。インストールなしでも rufio は正常動作します。
259
262
 
260
- | ツール | 用途 | キー |
261
- |--------|------|------|
262
- | fzf | ファイル名検索 | `s` |
263
- | rga | ファイル内容検索 | `F` |
264
- | zoxide | ディレクトリ履歴 | `z` |
263
+ | ツール | 用途 | キー | 必須 |
264
+ |--------|------|------|------|
265
+ | fzf | ファイル名検索 | `s` | オプション |
266
+ | rga | ファイル内容検索 | `F` | オプション |
267
+ | zoxide | ディレクトリ履歴 | `z` | オプション |
268
+ | bat | プレビューのシンタックスハイライト | — | オプション |
265
269
 
266
- ### インストール
270
+ ### bat — シンタックスハイライト
271
+
272
+ `bat` をインストールすると、プレビューペインでコードファイルを開いた際に
273
+ シンタックスハイライトが表示されます(Ruby、Python、Go、Rust、TypeScript など15言語以上対応)。
274
+
275
+ ハイライトはバックグラウンドで非同期に読み込まれるため、大きなソースツリーでも
276
+ カーソル移動が重くなりません。
277
+
278
+ ```bash
279
+ # macOS
280
+ brew install bat
281
+
282
+ # Ubuntu/Debian
283
+ apt install bat
284
+ ```
285
+
286
+ `rufio -c` を実行すると bat が正しく認識されているか確認できます。
287
+
288
+ ### インストール(全ツール)
267
289
 
268
290
  ```bash
269
291
  # macOS
270
- brew install fzf rga zoxide
292
+ brew install fzf bat rga zoxide
271
293
 
272
294
  # Ubuntu/Debian
273
- apt install fzf zoxide
295
+ apt install fzf bat zoxide
274
296
  # rgaは別途インストール: https://github.com/phiresky/ripgrep-all
275
297
  ```
276
298
 
@@ -0,0 +1,189 @@
1
+ # CHANGELOG v0.80.0
2
+
3
+ ## Overview
4
+
5
+ Adds syntax highlighting to the file preview pane. When `bat` is installed, code files
6
+ are displayed with full ANSI color highlighting. Environments without `bat` fall back
7
+ silently to plain text display.
8
+
9
+ Also fixes **cursor flickering** and **navigation lag** that occurred when browsing
10
+ source code directories with highlighting active.
11
+
12
+ ---
13
+
14
+ ## New Features
15
+
16
+ ### Syntax Highlighting (via `bat`)
17
+
18
+ The preview pane now renders code files with syntax highlighting using `bat`'s ANSI
19
+ color output.
20
+
21
+ **Supported languages:**
22
+
23
+ | Language | Extensions |
24
+ |----------|------------|
25
+ | Ruby | `.rb` |
26
+ | Python | `.py` |
27
+ | JavaScript | `.js`, `.mjs` |
28
+ | TypeScript | `.ts` |
29
+ | HTML | `.html`, `.htm` |
30
+ | CSS | `.css` |
31
+ | JSON | `.json` |
32
+ | YAML | `.yml`, `.yaml` |
33
+ | Markdown | `.md`, `.markdown` |
34
+ | Go | `.go` |
35
+ | Rust | `.rs` |
36
+ | Shell | `.sh`, `.bash`, `.zsh` |
37
+ | TOML | `.toml` |
38
+ | SQL | `.sql` |
39
+ | C | `.c`, `.h` |
40
+ | C++ | `.cpp`, `.cc`, `.cxx`, `.hpp` |
41
+ | Java | `.java` |
42
+ | Dockerfile | `Dockerfile`, `Dockerfile.*` |
43
+ | Makefile | `Makefile`, `GNUmakefile` |
44
+
45
+ **Behavior:**
46
+ - Graceful degradation — plain text display when `bat` is not installed
47
+ - Non-UTF-8 files (e.g. Shift_JIS) skip highlighting automatically
48
+ - mtime-based cache: second visit to the same file is instant (0 ms)
49
+ - Run `rufio -c` to verify `bat` is detected by the health checker
50
+
51
+ **Installation:**
52
+ ```bash
53
+ # macOS
54
+ brew install bat
55
+
56
+ # Ubuntu/Debian
57
+ apt install bat
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Bug Fixes
63
+
64
+ ### Fix 1: Cursor Flickering (Renderer Atomic Output)
65
+
66
+ **Symptom:** The preview pane flickered briefly when moving the cursor in source
67
+ code directories.
68
+
69
+ **Root cause:** `Renderer#render` called `print` once per dirty row. With
70
+ `STDOUT sync=true`, each `print` flushes immediately, so intermediate states
71
+ (highlighted color removed, new color not yet drawn) were visible in the terminal.
72
+
73
+ **Fix:** All dirty row output is now accumulated into a single string buffer, then
74
+ written with one `write` + `flush` call — guaranteeing an atomic terminal update.
75
+
76
+ ```
77
+ Before: print row0 → flush → print row1 → flush → ... (intermediate states visible)
78
+ After: buf = row0 + row1 + ... → write(buf) + flush (single atomic update)
79
+ ```
80
+
81
+ ### Fix 2: Navigation Lag (Async bat Execution)
82
+
83
+ **Symptom:** Moving the cursor to a new file in a source directory caused 10–30 ms
84
+ of lag, making navigation feel sluggish.
85
+
86
+ **Root cause:** `SyntaxHighlighter#highlight` called `IO.popen(['bat', ...])` synchronously
87
+ inside the main loop. The bat process startup cost blocked the frame on every new file visit.
88
+
89
+ **Fix:** Added `highlight_async` which runs `bat` in a background Thread.
90
+
91
+ - The frame immediately after moving to a new file displays plain text (instant fallback)
92
+ - When the background thread completes, it sets `@highlight_updated = true`
93
+ - The main loop detects the flag and triggers a re-render with highlighting
94
+ - A pending guard prevents duplicate threads for the same file path
95
+ - A `Mutex` protects all cache reads/writes for thread safety
96
+
97
+ ---
98
+
99
+ ## Technical Details
100
+
101
+ ### New Files
102
+
103
+ | File | Description |
104
+ |------|-------------|
105
+ | `lib/rufio/ansi_line_parser.rb` | Parses ANSI SGR escape sequences into token arrays. Full-width character-aware wrapping |
106
+ | `lib/rufio/syntax_highlighter.rb` | Wraps the `bat` command. mtime cache, async execution, Mutex protection |
107
+ | `test/test_ansi_line_parser.rb` | Unit tests for AnsiLineParser (25 tests) |
108
+ | `test/test_syntax_highlighter.rb` | Unit tests for SyntaxHighlighter (16 tests) |
109
+
110
+ ### Modified Files
111
+
112
+ | File | Change |
113
+ |------|--------|
114
+ | `lib/rufio/renderer.rb` | Per-line `print` → single `write(buf)` + `flush` |
115
+ | `lib/rufio/terminal_ui.rb` | Added `@syntax_highlighter`, highlighting branch in `draw_file_preview_to_buffer`, `@highlight_updated` check in main loop |
116
+ | `lib/rufio/file_preview.rb` | Extended `determine_file_type` with Go, Rust, Shell, TOML, SQL, C, C++, Java, Dockerfile, Makefile |
117
+ | `lib/rufio/health_checker.rb` | Added `check_bat` method |
118
+ | `lib/rufio/config.rb` | Added `health.bat` message key (EN + JA) |
119
+ | `lib/rufio.rb` | Added `require` for `ansi_line_parser` and `syntax_highlighter` |
120
+ | `test/test_renderer.rb` | Added `OutputSpy` helper, 2 new atomic output tests |
121
+
122
+ ### Architecture
123
+
124
+ ```
125
+ bat (external process)
126
+ ↓ IO.popen — background Thread
127
+ SyntaxHighlighter#highlight_async
128
+ ↓ callback on completion
129
+ @preview_cache[path][:highlighted] = lines # store ANSI line array
130
+ @highlight_updated = true # notify main loop
131
+ ↓ next frame
132
+ AnsiLineParser.parse(line) # ANSI → token array
133
+ AnsiLineParser.wrap(tokens, width) # full-width-aware wrapping
134
+ draw_highlighted_line_to_buffer(screen, ...) # per-char fg: color drawing
135
+ ```
136
+
137
+ ### Preview Cache Structure
138
+
139
+ ```ruby
140
+ @preview_cache[file_path] = {
141
+ content: Array<String>, # plain text lines
142
+ preview_data: Hash, # type, encoding, etc.
143
+ highlighted: nil | false | Array<String>,
144
+ # nil = not yet requested
145
+ # false = requested, awaiting background result
146
+ # Array = ANSI lines ready to render
147
+ wrapped: Hash, # width => wrapped plain lines
148
+ highlighted_wrapped: Hash # width => wrapped token arrays
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Tests
155
+
156
+ All tests pass (pre-existing TestUISnapshot / TestBufferParity snapshot mismatches excluded):
157
+
158
+ | Test file | Tests | Status |
159
+ |-----------|-------|--------|
160
+ | `test_ansi_line_parser.rb` | 25 | new |
161
+ | `test_syntax_highlighter.rb` | 16 (9 existing + 7 new) | pass |
162
+ | `test_renderer.rb` | 12 (10 existing + 2 new) | pass |
163
+
164
+ **34 new tests** added in this release.
165
+
166
+ ---
167
+
168
+ ## Dependencies
169
+
170
+ ### New Optional External Tool
171
+
172
+ | Tool | Purpose | Install |
173
+ |------|---------|---------|
174
+ | `bat` | Syntax highlighting | `brew install bat` / `apt install bat` |
175
+
176
+ rufio works normally without `bat` — plain text preview is always available as a fallback.
177
+
178
+ ---
179
+
180
+ ## Health Check
181
+
182
+ Use `rufio -c` to verify `bat` installation:
183
+
184
+ ```
185
+ rufio Health Check
186
+ ✓ bat (syntax highlight): bat 0.25.0 (2024-...)
187
+ ✗ bat (syntax highlight): not found
188
+ brew install bat # optional: enables syntax highlighting
189
+ ```
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rufio
4
+ # ANSI エスケープコード付きの行をトークン列に分解するモジュール。
5
+ # bat --color=always の出力をシンタックスハイライト表示するために使用する。
6
+ module AnsiLineParser
7
+ # SGR (Select Graphic Rendition) ANSI エスケープシーケンスにマッチするパターン
8
+ ANSI_SGR_PATTERN = /\e\[[0-9;]*m/
9
+
10
+ # ANSI 付き行を {text: String, fg: String|nil} のトークン配列に分解する。
11
+ #
12
+ # @param line [String] ANSI コードを含む可能性がある行
13
+ # @return [Array<Hash>] {text:, fg:} のトークン配列
14
+ def self.parse(line)
15
+ tokens = []
16
+ current_fg = nil
17
+
18
+ # ANSI SGR シーケンス、非エスケープ文字列、孤立エスケープの順にスキャン
19
+ line.scan(/#{ANSI_SGR_PATTERN}|[^\e]+|\e(?!\[)/) do |part|
20
+ if part.start_with?("\e[")
21
+ current_fg = apply_ansi_sequence(current_fg, part)
22
+ else
23
+ tokens << { text: part, fg: current_fg }
24
+ end
25
+ end
26
+
27
+ tokens
28
+ end
29
+
30
+ # トークン配列の表示幅(ANSI コードを除く)を計算する。
31
+ #
32
+ # @param tokens [Array<Hash>] parse が返したトークン配列
33
+ # @return [Integer] 表示幅
34
+ def self.display_width(tokens)
35
+ tokens.sum { |t| TextUtils.display_width(t[:text]) }
36
+ end
37
+
38
+ # トークン配列を max_width で折り返し、行ごとのトークン配列の配列を返す。
39
+ # 全角文字(日本語等)は幅2として扱う。
40
+ #
41
+ # @param tokens [Array<Hash>] parse が返したトークン配列
42
+ # @param max_width [Integer] 折り返し幅(表示幅基準)
43
+ # @return [Array<Array<Hash>>] 行ごとのトークン配列
44
+ def self.wrap(tokens, max_width)
45
+ return [] if tokens.empty? || max_width <= 0
46
+
47
+ lines = []
48
+ current_line = []
49
+ current_width = 0
50
+
51
+ tokens.each do |token|
52
+ fg = token[:fg]
53
+ current_text = String.new
54
+
55
+ token[:text].each_char do |char|
56
+ char_w = TextUtils.char_width(char)
57
+
58
+ if current_width + char_w > max_width
59
+ # 折り返し: 現在のテキストをトークンとして確定
60
+ current_line << { text: current_text, fg: fg } unless current_text.empty?
61
+ lines << current_line
62
+ current_line = []
63
+ current_text = String.new
64
+ current_width = 0
65
+ end
66
+
67
+ current_text << char
68
+ current_width += char_w
69
+ end
70
+
71
+ current_line << { text: current_text, fg: fg } unless current_text.empty?
72
+ end
73
+
74
+ lines << current_line unless current_line.empty?
75
+ lines
76
+ end
77
+
78
+ # private helper: ANSI シーケンスを適用して新しい fg 状態を返す。
79
+ # リセットシーケンスは nil を返す。
80
+ # 複合シーケンス(例: \e[0;32m)はリセット後の色だけを返す。
81
+ def self.apply_ansi_sequence(current_fg, seq)
82
+ # \e[ と m の間のコード文字列を取り出す
83
+ codes_str = seq[2..-2]
84
+ codes = codes_str.split(';')
85
+
86
+ # すべてのコードがリセット("0" or "")ならリセット
87
+ if codes.empty? || codes.all? { |c| c.empty? || c == '0' }
88
+ return nil
89
+ end
90
+
91
+ # 先頭がリセットコードで後続に色指定がある場合(例: \e[0;32m)
92
+ if codes.first.empty? || codes.first == '0'
93
+ remaining = codes.drop_while { |c| c.empty? || c == '0' }
94
+ return remaining.empty? ? nil : "\e[#{remaining.join(';')}m"
95
+ end
96
+
97
+ # 通常の色/属性コード → そのまま使用
98
+ seq
99
+ end
100
+
101
+ private_class_method :apply_ansi_sequence
102
+ end
103
+ end
data/lib/rufio/config.rb CHANGED
@@ -66,6 +66,7 @@ module Rufio
66
66
  'health.fzf' => 'fzf (file search)',
67
67
  'health.rga' => 'rga (content search)',
68
68
  'health.zoxide' => 'zoxide (directory history)',
69
+ 'health.bat' => 'bat (syntax highlight)',
69
70
  'health.file_opener' => 'System file opener',
70
71
  'health.summary' => 'Summary:',
71
72
  'health.ok' => 'OK',
@@ -135,6 +136,7 @@ module Rufio
135
136
  'health.fzf' => 'fzf (file search)',
136
137
  'health.rga' => 'rga (content search)',
137
138
  'health.zoxide' => 'zoxide (directory history)',
139
+ 'health.bat' => 'bat (syntax highlight)',
138
140
  'health.file_opener' => 'System file opener',
139
141
  'health.summary' => 'Summary:',
140
142
  'health.ok' => 'OK',
@@ -127,7 +127,16 @@ module Rufio
127
127
 
128
128
  def determine_file_type(file_path)
129
129
  extension = File.extname(file_path).downcase
130
-
130
+ basename = File.basename(file_path)
131
+
132
+ # 拡張子なしファイル名での判定(Dockerfile 等)
133
+ case basename
134
+ when "Dockerfile", /\ADockerfile\./
135
+ return { type: "code", language: "dockerfile" }
136
+ when "Makefile", "GNUmakefile"
137
+ return { type: "code", language: "makefile" }
138
+ end
139
+
131
140
  case extension
132
141
  when ".rb"
133
142
  { type: "code", language: "ruby" }
@@ -147,6 +156,22 @@ module Rufio
147
156
  { type: "code", language: "yaml" }
148
157
  when ".md", ".markdown"
149
158
  { type: "code", language: "markdown" }
159
+ when ".go"
160
+ { type: "code", language: "go" }
161
+ when ".rs"
162
+ { type: "code", language: "rust" }
163
+ when ".sh", ".bash", ".zsh"
164
+ { type: "code", language: "shell" }
165
+ when ".toml"
166
+ { type: "code", language: "toml" }
167
+ when ".sql"
168
+ { type: "code", language: "sql" }
169
+ when ".c", ".h"
170
+ { type: "code", language: "c" }
171
+ when ".cpp", ".cc", ".cxx", ".hpp"
172
+ { type: "code", language: "cpp" }
173
+ when ".java"
174
+ { type: "code", language: "java" }
150
175
  when ".txt", ".log"
151
176
  { type: "text", language: nil }
152
177
  when ".zip", ".tar", ".gz", ".bz2", ".xz", ".7z"
@@ -21,6 +21,7 @@ module Rufio
21
21
  { name: ConfigLoader.message('health.fzf'), method: :check_fzf },
22
22
  { name: ConfigLoader.message('health.rga'), method: :check_rga },
23
23
  { name: ConfigLoader.message('health.zoxide'), method: :check_zoxide },
24
+ { name: ConfigLoader.message('health.bat'), method: :check_bat },
24
25
  { name: ConfigLoader.message('health.file_opener'), method: :check_file_opener }
25
26
  ]
26
27
 
@@ -136,6 +137,23 @@ module Rufio
136
137
  end
137
138
  end
138
139
 
140
+ def check_bat
141
+ if system("which bat > /dev/null 2>&1")
142
+ version = `bat --version 2>/dev/null`.strip
143
+ {
144
+ status: :ok,
145
+ message: version,
146
+ details: nil
147
+ }
148
+ else
149
+ {
150
+ status: :warning,
151
+ message: "bat #{ConfigLoader.message('health.tool_not_found')}",
152
+ details: install_instruction_for('bat')
153
+ }
154
+ end
155
+ end
156
+
139
157
  def check_file_opener
140
158
  case RUBY_PLATFORM
141
159
  when /darwin/
@@ -180,6 +198,8 @@ module Rufio
180
198
  "#{ConfigLoader.message('health.install_brew')} rga"
181
199
  when 'zoxide'
182
200
  "#{ConfigLoader.message('health.install_brew')} zoxide"
201
+ when 'bat'
202
+ "#{ConfigLoader.message('health.install_brew')} bat # optional: syntax highlight"
183
203
  end
184
204
  when /linux/
185
205
  case tool
@@ -189,6 +209,8 @@ module Rufio
189
209
  ConfigLoader.message('health.rga_releases')
190
210
  when 'zoxide'
191
211
  "#{ConfigLoader.message('health.install_apt')} zoxide (Ubuntu/Debian) or check your package manager"
212
+ when 'bat'
213
+ "#{ConfigLoader.message('health.install_apt')} bat (Ubuntu/Debian) or check your package manager # optional: syntax highlight"
192
214
  end
193
215
  else
194
216
  ConfigLoader.message('health.install_guide')
@@ -436,6 +436,20 @@ module Rufio
436
436
  @preview_scroll_offset = 0
437
437
  end
438
438
 
439
+ # Tabキー: 次のブックマークへ循環移動
440
+ def goto_next_bookmark
441
+ bookmarks = @bookmark_manager.list
442
+ return nil unless bookmarks&.any?
443
+
444
+ current_path = @directory_listing.current_path
445
+ current_idx = bookmarks.find_index { |bm| bm[:path] == current_path }
446
+
447
+ next_idx = current_idx ? (current_idx + 1) % bookmarks.length : 0
448
+ next_bookmark = bookmarks[next_idx]
449
+ navigate_to_directory(next_bookmark[:path])
450
+ next_idx
451
+ end
452
+
439
453
  private
440
454
 
441
455
  # オーバーレイダイアログを表示してキー入力を待つヘルパーメソッド
@@ -1388,6 +1402,7 @@ module Rufio
1388
1402
  navigate_to_directory(bookmark[:path])
1389
1403
  end
1390
1404
 
1405
+
1391
1406
  def add_bookmark
1392
1407
  current_path = @directory_listing&.current_path || Dir.pwd
1393
1408