rfmt 1.3.3 → 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 +35 -0
- data/Cargo.lock +1 -1
- data/README.md +106 -22
- data/exe/rfmt +3 -0
- data/ext/rfmt/Cargo.toml +1 -1
- data/lib/rfmt/cli.rb +218 -77
- data/lib/rfmt/version.rb +1 -1
- data/lib/rfmt.rb +1 -6
- metadata +44 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb4f41a4ba34f1bc106746712bf144fe6f5ee8cf5a80cf26efaecc8450645d89
|
|
4
|
+
data.tar.gz: 94f896c28015d9592d959ef3a42b4db80e309ad664ef97d5f28b0e8479605892
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0aef4d71101fd621bcecf87b0160e437ec9f7836e12759dbb28cd56416d1428bb3b54aabae888566548dc11557d1179e519aa5c971e613c3c37c775590eef981
|
|
7
|
+
data.tar.gz: 3ee34d6c250fdd9263822a8b361dc3fdde5005c36570a5a6a0be346f717090c581822c90cd08bee43674495c97a15fb22677558bd5be5b85f9ccd4c1d4fd9e51
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.4.0] - 2026-01-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- New `rfmt_fast` executable for optimized performance
|
|
7
|
+
- Automatic parallel processing detection logic
|
|
8
|
+
- Enhanced logging and summary display functionality
|
|
9
|
+
- CLI option mapping for `-v/--version` commands
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- Fixed `-v` flag incorrectly triggering format instead of showing version
|
|
13
|
+
- Fixed `--diff` option dependency issues (added `diffy` and `diff-lcs` to gemspec)
|
|
14
|
+
- CLI option conflicts between verbose and version flags
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Updated performance benchmarks documentation
|
|
18
|
+
- Code formatting improvements with Rubocop compliance
|
|
19
|
+
- Dependencies alphabetically sorted in gemspec
|
|
20
|
+
|
|
21
|
+
## [1.3.4] - 2026-01-17
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- New `rfmt_fast` executable for optimized performance
|
|
25
|
+
- Automatic parallel processing detection logic
|
|
26
|
+
- Enhanced logging and summary display functionality
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- Optimize logging and summary display performance
|
|
30
|
+
- Improve parallel execution logic with automatic detection
|
|
31
|
+
- Update README with new features and usage examples
|
|
32
|
+
- Code formatting improvements with Rubocop compliance
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- Logger optimization to reduce overhead
|
|
36
|
+
- Parallel processing logic refinements
|
|
37
|
+
|
|
3
38
|
## [1.3.3] - 2026-01-17
|
|
4
39
|
|
|
5
40
|
### Fixed
|
data/Cargo.lock
CHANGED
data/README.md
CHANGED
|
@@ -46,33 +46,31 @@ Enforces code style rules:
|
|
|
46
46
|
- Quote style standardization
|
|
47
47
|
- Method definition formatting
|
|
48
48
|
|
|
49
|
-
## Performance
|
|
49
|
+
## Performance
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
rfmt delivers consistent, fast formatting across projects of any size:
|
|
52
52
|
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
53
|
+
| Project Size | Files | Execution Time | Throughput |
|
|
54
|
+
|-------------|-------|----------------|------------|
|
|
55
|
+
| Small | 9 files | ~105ms | 85 files/sec |
|
|
56
|
+
| Medium | 35 files | ~110ms | 315 files/sec |
|
|
57
|
+
| Large | 151 files | ~100ms | 1,560 files/sec |
|
|
58
58
|
|
|
59
|
-
**
|
|
60
|
-
- RuboCop times include startup overhead and loading all cops (linting rules)
|
|
61
|
-
- RuboCop was run with default configuration (all cops enabled)
|
|
62
|
-
- rfmt is a formatting-only tool with minimal overhead
|
|
63
|
-
- Both tools were measured in check mode (no file modifications)
|
|
64
|
-
- Results are averages from 10 runs per test
|
|
59
|
+
**Key Performance Characteristics:**
|
|
65
60
|
|
|
66
|
-
**
|
|
67
|
-
-
|
|
68
|
-
-
|
|
61
|
+
- **Constant Time**: Execution time stays around 100ms regardless of project size
|
|
62
|
+
- **Parallel Processing**: Automatic scaling with available CPU cores
|
|
63
|
+
- **High Throughput**: Up to 1,500+ files per second on large projects
|
|
64
|
+
- **Low Overhead**: Minimal startup time and memory usage
|
|
69
65
|
|
|
70
66
|
**Test Environment:**
|
|
71
67
|
- CPU: Apple Silicon (arm64)
|
|
72
|
-
- Ruby: 3.4.
|
|
73
|
-
-
|
|
68
|
+
- Ruby: 3.4.8
|
|
69
|
+
- Average of 5 runs per test
|
|
74
70
|
|
|
75
|
-
|
|
71
|
+
*Built with Rust for optimal performance and memory efficiency.*
|
|
72
|
+
|
|
73
|
+
For detailed performance comparisons and benchmarks, see [Performance Benchmarks](docs/benchmark.md).
|
|
76
74
|
|
|
77
75
|
## Installation
|
|
78
76
|
|
|
@@ -166,6 +164,12 @@ Format multiple files:
|
|
|
166
164
|
rfmt lib/**/*.rb
|
|
167
165
|
```
|
|
168
166
|
|
|
167
|
+
Format all files in your project:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
rfmt .
|
|
171
|
+
```
|
|
172
|
+
|
|
169
173
|
Check if files need formatting (CI/CD):
|
|
170
174
|
|
|
171
175
|
```bash
|
|
@@ -178,12 +182,92 @@ Show diff without modifying files:
|
|
|
178
182
|
rfmt lib/user.rb --diff
|
|
179
183
|
```
|
|
180
184
|
|
|
185
|
+
Quiet mode (minimal output):
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
rfmt --quiet lib/**/*.rb
|
|
189
|
+
```
|
|
190
|
+
|
|
181
191
|
Enable verbose output for debugging:
|
|
182
192
|
|
|
183
193
|
```bash
|
|
184
|
-
rfmt lib/user.rb
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
rfmt --verbose lib/user.rb
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### Common Options
|
|
198
|
+
|
|
199
|
+
| Option | Description |
|
|
200
|
+
|--------|-------------|
|
|
201
|
+
| `--check` | Check formatting without writing files |
|
|
202
|
+
| `--diff` | Show diff of changes |
|
|
203
|
+
| `--quiet` | Minimal output |
|
|
204
|
+
| `--verbose` | Detailed output with timing |
|
|
205
|
+
|
|
206
|
+
### Output Modes
|
|
207
|
+
|
|
208
|
+
**Normal mode** (default):
|
|
209
|
+
```bash
|
|
210
|
+
$ rfmt app/
|
|
211
|
+
Processing 25 file(s)...
|
|
212
|
+
✓ Formatted app/controllers/users_controller.rb
|
|
213
|
+
✓ Formatted app/models/user.rb
|
|
214
|
+
|
|
215
|
+
✓ Processed 25 files
|
|
216
|
+
(3 formatted, 22 unchanged)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Quiet mode** (`--quiet` or `-q`):
|
|
220
|
+
```bash
|
|
221
|
+
$ rfmt --quiet app/
|
|
222
|
+
✓ 3 files formatted
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Verbose mode** (`--verbose` or `-v`):
|
|
226
|
+
```bash
|
|
227
|
+
$ rfmt --verbose app/
|
|
228
|
+
Processing 25 file(s)...
|
|
229
|
+
Using sequential processing for 25 files
|
|
230
|
+
✓ Formatted app/controllers/users_controller.rb
|
|
231
|
+
✓ app/models/application_record.rb already formatted
|
|
232
|
+
...
|
|
233
|
+
|
|
234
|
+
✓ Processed 25 files
|
|
235
|
+
(3 formatted, 22 unchanged)
|
|
236
|
+
|
|
237
|
+
Details:
|
|
238
|
+
Total files: 25
|
|
239
|
+
Total time: 0.45s
|
|
240
|
+
Files/sec: 55.6
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Parallel Processing
|
|
244
|
+
|
|
245
|
+
rfmt automatically chooses the optimal processing mode:
|
|
246
|
+
|
|
247
|
+
- **< 20 files**: Sequential processing (fastest for small batches)
|
|
248
|
+
- **20-49 files**: Automatic based on average file size
|
|
249
|
+
- **≥ 50 files**: Parallel processing (utilizes multiple cores)
|
|
250
|
+
|
|
251
|
+
You can override this behavior:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Force parallel processing
|
|
255
|
+
rfmt --parallel app/
|
|
256
|
+
|
|
257
|
+
# Force sequential processing
|
|
258
|
+
rfmt --no-parallel app/
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Cache Management
|
|
262
|
+
|
|
263
|
+
rfmt uses caching to improve performance on large codebases:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Clear cache if needed
|
|
267
|
+
rfmt cache clear
|
|
268
|
+
|
|
269
|
+
# View cache statistics
|
|
270
|
+
rfmt cache stats
|
|
187
271
|
```
|
|
188
272
|
|
|
189
273
|
### Ruby API
|
data/exe/rfmt
CHANGED
data/ext/rfmt/Cargo.toml
CHANGED
data/lib/rfmt/cli.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require 'thor'
|
|
4
4
|
|
|
5
5
|
# Check for verbose flag before loading rfmt to set debug mode early
|
|
6
|
-
ENV['RFMT_DEBUG'] = '1' if ARGV.include?('
|
|
6
|
+
ENV['RFMT_DEBUG'] = '1' if ARGV.include?('--verbose')
|
|
7
7
|
|
|
8
8
|
require 'rfmt'
|
|
9
9
|
require 'rfmt/configuration'
|
|
@@ -44,20 +44,29 @@ module Rfmt
|
|
|
44
44
|
|
|
45
45
|
# Command Line Interface for rfmt
|
|
46
46
|
class CLI < Thor
|
|
47
|
+
# Constants
|
|
48
|
+
PROGRESS_THRESHOLD = 20 # Show progress for file counts >= this
|
|
49
|
+
PROGRESS_INTERVAL = 10 # Update progress every N files
|
|
50
|
+
|
|
47
51
|
class_option :config, type: :string, desc: 'Path to configuration file'
|
|
48
|
-
class_option :verbose, type: :boolean,
|
|
52
|
+
class_option :verbose, type: :boolean, desc: 'Verbose output'
|
|
49
53
|
|
|
50
54
|
default_command :format
|
|
51
55
|
|
|
56
|
+
# Map -v and --version to version command
|
|
57
|
+
map '-v' => 'version'
|
|
58
|
+
map '--version' => 'version'
|
|
59
|
+
|
|
52
60
|
desc 'format [FILES]', 'Format Ruby files (default command)'
|
|
53
61
|
option :write, type: :boolean, default: true, desc: 'Write formatted output'
|
|
54
62
|
option :check, type: :boolean, desc: "Check if files are formatted (don't write)"
|
|
55
63
|
option :diff, type: :boolean, desc: 'Show diff of changes'
|
|
56
64
|
option :diff_format, type: :string, default: 'unified', desc: 'Diff format: unified, side_by_side, or color'
|
|
57
|
-
option :parallel, type: :boolean,
|
|
65
|
+
option :parallel, type: :boolean, desc: 'Use parallel processing (auto-disabled for <20 files)'
|
|
58
66
|
option :jobs, type: :numeric, desc: 'Number of parallel jobs (default: CPU count)'
|
|
59
67
|
option :cache, type: :boolean, default: true, desc: 'Use cache to skip unchanged files'
|
|
60
68
|
option :cache_dir, type: :string, desc: 'Cache directory (default: ~/.cache/rfmt)'
|
|
69
|
+
option :quiet, type: :boolean, aliases: '-q', desc: 'Minimal output (errors and summary only)'
|
|
61
70
|
def format(*files)
|
|
62
71
|
config = load_config
|
|
63
72
|
files = files.empty? ? config.files_to_format : files.flatten
|
|
@@ -67,33 +76,32 @@ module Rfmt
|
|
|
67
76
|
return
|
|
68
77
|
end
|
|
69
78
|
|
|
70
|
-
# Initialize cache
|
|
71
|
-
cache =
|
|
72
|
-
|
|
73
|
-
Cache.new(**cache_opts)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Filter files using cache
|
|
77
|
-
if cache
|
|
78
|
-
original_count = files.size
|
|
79
|
-
files = files.select { |file| cache.needs_formatting?(file) }
|
|
80
|
-
skipped = original_count - files.size
|
|
81
|
-
say "ℹ Skipped #{skipped} unchanged file(s) (cached)", :cyan if skipped.positive? && options[:verbose]
|
|
82
|
-
end
|
|
79
|
+
# Initialize and use cache if enabled
|
|
80
|
+
cache = initialize_cache_if_enabled
|
|
81
|
+
files = filter_files_with_cache(files, cache)
|
|
83
82
|
|
|
84
83
|
if files.empty?
|
|
85
|
-
say '✓ All files are already formatted (cached)', :
|
|
84
|
+
say '✓ All files are already formatted (cached)', :cyan
|
|
86
85
|
return
|
|
87
86
|
end
|
|
88
87
|
|
|
89
|
-
# Show progress message
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
# Show progress message (unless in quiet mode)
|
|
89
|
+
unless options[:quiet]
|
|
90
|
+
if files.size == 1
|
|
91
|
+
say "Processing #{files.first}...", :blue
|
|
92
|
+
else
|
|
93
|
+
say "Processing #{files.size} file(s)...", :blue
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
use_parallel = should_use_parallel?(files)
|
|
98
|
+
|
|
99
|
+
if options[:verbose] && files.size > 1
|
|
100
|
+
mode = use_parallel ? "parallel (#{options[:jobs] || 'auto'} jobs)" : 'sequential'
|
|
101
|
+
say "Using #{mode} processing for #{files.size} files", :blue
|
|
94
102
|
end
|
|
95
103
|
|
|
96
|
-
results = if
|
|
104
|
+
results = if use_parallel
|
|
97
105
|
format_files_parallel(files)
|
|
98
106
|
else
|
|
99
107
|
format_files_sequential(files)
|
|
@@ -140,6 +148,39 @@ module Rfmt
|
|
|
140
148
|
|
|
141
149
|
private
|
|
142
150
|
|
|
151
|
+
# Intelligently decide whether to use parallel processing
|
|
152
|
+
def should_use_parallel?(files)
|
|
153
|
+
return false if files.size <= 1
|
|
154
|
+
|
|
155
|
+
# Check if parallel option was explicitly set via command line
|
|
156
|
+
# Thor sets options[:parallel] to true/false for --parallel/--no-parallel
|
|
157
|
+
# and nil when not specified
|
|
158
|
+
return options[:parallel] unless options[:parallel].nil?
|
|
159
|
+
|
|
160
|
+
# Auto decision based on workload characteristics
|
|
161
|
+
# Calculate total size for better decision
|
|
162
|
+
total_size = files.sum do |f|
|
|
163
|
+
File.size(f)
|
|
164
|
+
rescue StandardError
|
|
165
|
+
0
|
|
166
|
+
end
|
|
167
|
+
avg_size = total_size / files.size.to_f
|
|
168
|
+
|
|
169
|
+
# Decision matrix:
|
|
170
|
+
# - Less than 20 files: sequential (overhead > benefit)
|
|
171
|
+
# - 20-50 files with small size (<10KB avg): sequential
|
|
172
|
+
# - 20-50 files with large size (>10KB avg): parallel
|
|
173
|
+
# - More than 50 files: always parallel
|
|
174
|
+
|
|
175
|
+
if files.size < 20
|
|
176
|
+
false
|
|
177
|
+
elsif files.size < 50
|
|
178
|
+
avg_size > 10_000 # 10KB threshold
|
|
179
|
+
else
|
|
180
|
+
true
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
143
184
|
def load_config
|
|
144
185
|
if options[:config]
|
|
145
186
|
Configuration.new(file: options[:config])
|
|
@@ -148,25 +189,68 @@ module Rfmt
|
|
|
148
189
|
end
|
|
149
190
|
end
|
|
150
191
|
|
|
192
|
+
def initialize_cache_if_enabled
|
|
193
|
+
return nil unless options[:cache]
|
|
194
|
+
|
|
195
|
+
cache_opts = options[:cache_dir] ? { cache_dir: options[:cache_dir] } : {}
|
|
196
|
+
Cache.new(**cache_opts)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def filter_files_with_cache(files, cache)
|
|
200
|
+
return files unless cache
|
|
201
|
+
|
|
202
|
+
original_count = files.size
|
|
203
|
+
filtered = files.select { |file| cache.needs_formatting?(file) }
|
|
204
|
+
|
|
205
|
+
log_cache_skip(original_count - filtered.size)
|
|
206
|
+
filtered
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def log_cache_skip(skipped_count)
|
|
210
|
+
return unless skipped_count.positive? && options[:verbose]
|
|
211
|
+
|
|
212
|
+
say "ℹ Skipped #{skipped_count} unchanged file(s) (cached)", :cyan
|
|
213
|
+
end
|
|
214
|
+
|
|
151
215
|
def format_files_sequential(files)
|
|
152
|
-
|
|
216
|
+
show_progress = should_show_progress?(files)
|
|
217
|
+
|
|
218
|
+
files.map.with_index do |file, index|
|
|
219
|
+
display_progress(index, files.size) if show_progress && (index % PROGRESS_INTERVAL).zero?
|
|
153
220
|
format_single_file(file)
|
|
154
221
|
end
|
|
155
222
|
end
|
|
156
223
|
|
|
224
|
+
def should_show_progress?(files)
|
|
225
|
+
!options[:quiet] && files.size >= PROGRESS_THRESHOLD
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def display_progress(index, total)
|
|
229
|
+
percentage = ((index.to_f / total) * 100).round
|
|
230
|
+
say "[#{index}/#{total}] #{percentage}% complete...", :blue
|
|
231
|
+
end
|
|
232
|
+
|
|
157
233
|
def format_files_parallel(files)
|
|
158
234
|
require 'parallel'
|
|
159
235
|
|
|
160
|
-
|
|
161
|
-
process_count
|
|
162
|
-
|
|
163
|
-
say "Processing #{files.size} files with #{process_count} parallel jobs...", :blue if options[:verbose]
|
|
236
|
+
process_count = determine_process_count
|
|
237
|
+
log_parallel_processing(files.size, process_count)
|
|
164
238
|
|
|
165
239
|
Parallel.map(files, in_processes: process_count) do |file|
|
|
166
240
|
format_single_file(file)
|
|
167
241
|
end
|
|
168
242
|
end
|
|
169
243
|
|
|
244
|
+
def determine_process_count
|
|
245
|
+
options[:jobs] || Parallel.processor_count
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def log_parallel_processing(file_count, process_count)
|
|
249
|
+
return unless options[:verbose]
|
|
250
|
+
|
|
251
|
+
say "Processing #{file_count} files with #{process_count} parallel jobs...", :blue
|
|
252
|
+
end
|
|
253
|
+
|
|
170
254
|
def format_single_file(file)
|
|
171
255
|
start_time = Time.now
|
|
172
256
|
source = File.read(file)
|
|
@@ -191,69 +275,126 @@ module Rfmt
|
|
|
191
275
|
end
|
|
192
276
|
|
|
193
277
|
def handle_results(results, cache = nil)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
278
|
+
stats = process_results(results, cache)
|
|
279
|
+
stats[:total_duration] = results.sum { |r| r[:duration] || 0 }
|
|
280
|
+
cache&.save
|
|
281
|
+
display_summary(stats, results.size)
|
|
282
|
+
exit(1) if should_exit_with_error?(stats)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def process_results(results, cache)
|
|
286
|
+
stats = { changed: 0, errors: 0, failed: 0, duration: 0 }
|
|
197
287
|
|
|
198
288
|
results.each do |result|
|
|
199
289
|
if result[:error]
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
290
|
+
handle_error_result(result, stats)
|
|
291
|
+
elsif result[:changed]
|
|
292
|
+
handle_changed_result(result, stats, cache)
|
|
293
|
+
else
|
|
294
|
+
handle_unchanged_result(result, cache)
|
|
203
295
|
end
|
|
296
|
+
end
|
|
204
297
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if options[:check]
|
|
209
|
-
say "#{result[:file]} needs formatting", :yellow
|
|
210
|
-
failed_count += 1
|
|
211
|
-
show_diff(result[:file], result[:original], result[:formatted]) if options[:diff]
|
|
212
|
-
elsif options[:diff]
|
|
213
|
-
show_diff(result[:file], result[:original], result[:formatted])
|
|
214
|
-
elsif options[:write]
|
|
215
|
-
File.write(result[:file], result[:formatted])
|
|
216
|
-
# Always show formatted files (not just in verbose mode)
|
|
217
|
-
say "✓ Formatted #{result[:file]}", :green
|
|
218
|
-
|
|
219
|
-
# Update cache after successful write
|
|
220
|
-
cache&.mark_formatted(result[:file])
|
|
221
|
-
else
|
|
222
|
-
puts result[:formatted]
|
|
223
|
-
end
|
|
224
|
-
else
|
|
225
|
-
# Show already formatted files in non-check mode
|
|
226
|
-
say "✓ #{result[:file]} already formatted", :cyan unless options[:check]
|
|
298
|
+
stats
|
|
299
|
+
end
|
|
227
300
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
301
|
+
def handle_error_result(result, stats)
|
|
302
|
+
say "Error in #{result[:file]}: #{result[:error]}", :red
|
|
303
|
+
stats[:errors] += 1
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def handle_changed_result(result, stats, cache)
|
|
307
|
+
stats[:changed] += 1
|
|
308
|
+
|
|
309
|
+
if options[:check]
|
|
310
|
+
say "#{result[:file]} needs formatting", :yellow
|
|
311
|
+
stats[:failed] += 1
|
|
312
|
+
show_diff(result[:file], result[:original], result[:formatted]) if options[:diff]
|
|
313
|
+
elsif options[:diff]
|
|
314
|
+
show_diff(result[:file], result[:original], result[:formatted])
|
|
315
|
+
elsif options[:write]
|
|
316
|
+
write_formatted_file(result, cache)
|
|
317
|
+
else
|
|
318
|
+
puts result[:formatted]
|
|
231
319
|
end
|
|
320
|
+
end
|
|
232
321
|
|
|
233
|
-
|
|
234
|
-
|
|
322
|
+
def handle_unchanged_result(result, cache)
|
|
323
|
+
say "✓ #{result[:file]} already formatted", :white if options[:verbose] && !options[:check]
|
|
324
|
+
cache&.mark_formatted(result[:file])
|
|
325
|
+
end
|
|
235
326
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
327
|
+
def write_formatted_file(result, cache)
|
|
328
|
+
File.write(result[:file], result[:formatted])
|
|
329
|
+
say "✓ Formatted #{result[:file]}", :green unless options[:quiet]
|
|
330
|
+
cache&.mark_formatted(result[:file])
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def display_summary(stats, total_files)
|
|
334
|
+
@last_stats = stats # Store for verbose details
|
|
335
|
+
unchanged_count = total_files - stats[:changed] - stats[:errors]
|
|
336
|
+
|
|
337
|
+
if stats[:errors].positive?
|
|
338
|
+
display_error_summary(stats[:errors])
|
|
339
|
+
elsif options[:check] && stats[:failed].positive?
|
|
340
|
+
display_check_failed_summary(stats[:failed])
|
|
341
|
+
elsif options[:quiet]
|
|
342
|
+
display_quiet_summary(stats[:changed])
|
|
246
343
|
else
|
|
247
|
-
|
|
344
|
+
display_normal_summary(stats[:changed], unchanged_count, total_files)
|
|
248
345
|
end
|
|
249
346
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
347
|
+
display_verbose_details(total_files) if options[:verbose] && !options[:quiet]
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def display_error_summary(error_count)
|
|
351
|
+
say "\n✗ Failed: #{error_count} error(s) occurred", :red
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def display_check_failed_summary(failed_count)
|
|
355
|
+
say "\n✗ Check failed: #{failed_count} file(s) need formatting", :yellow
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def display_quiet_summary(changed_count)
|
|
359
|
+
say "✓ #{changed_count} files formatted", :cyan if changed_count.positive?
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def display_normal_summary(changed_count, unchanged_count, total_files)
|
|
363
|
+
if total_files == 1
|
|
364
|
+
if changed_count.positive?
|
|
365
|
+
say "\n✓ Formatted 1 file", :cyan
|
|
366
|
+
else
|
|
367
|
+
say "\n✓ File is already formatted", :cyan
|
|
368
|
+
end
|
|
369
|
+
else
|
|
370
|
+
say "\n✓ Processed #{total_files} files", :cyan
|
|
371
|
+
display_file_breakdown(changed_count, unchanged_count)
|
|
254
372
|
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def display_file_breakdown(changed_count, unchanged_count)
|
|
376
|
+
return unless changed_count.positive? || unchanged_count.positive?
|
|
377
|
+
|
|
378
|
+
parts = []
|
|
379
|
+
parts << "#{changed_count} formatted" if changed_count.positive?
|
|
380
|
+
parts << "#{unchanged_count} unchanged" if unchanged_count.positive?
|
|
381
|
+
say " (#{parts.join(', ')})", :white
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def display_verbose_details(total_files)
|
|
385
|
+
say "\nDetails:", :blue
|
|
386
|
+
say " Total files: #{total_files}", :blue
|
|
387
|
+
|
|
388
|
+
# Duration is collected if available
|
|
389
|
+
return unless defined?(@last_stats) && @last_stats[:total_duration]
|
|
390
|
+
|
|
391
|
+
duration = @last_stats[:total_duration].round(2)
|
|
392
|
+
say " Total time: #{duration}s", :blue
|
|
393
|
+
say " Files/sec: #{(total_files / duration).round(1)}", :blue if duration.positive?
|
|
394
|
+
end
|
|
255
395
|
|
|
256
|
-
|
|
396
|
+
def should_exit_with_error?(stats)
|
|
397
|
+
(options[:check] && stats[:failed].positive?) || stats[:errors].positive?
|
|
257
398
|
end
|
|
258
399
|
|
|
259
400
|
def show_diff(file, original, formatted)
|
data/lib/rfmt/version.rb
CHANGED
data/lib/rfmt.rb
CHANGED
|
@@ -102,14 +102,9 @@ module Rfmt
|
|
|
102
102
|
# @param force [Boolean] Overwrite existing file if true
|
|
103
103
|
# @return [Boolean] true if file was created, false if already exists
|
|
104
104
|
def self.init(path = '.rfmt.yml', force: false)
|
|
105
|
-
if File.exist?(path) && !force
|
|
106
|
-
warn "Configuration file already exists: #{path}"
|
|
107
|
-
warn 'Use force: true to overwrite'
|
|
108
|
-
return false
|
|
109
|
-
end
|
|
105
|
+
return false if File.exist?(path) && !force
|
|
110
106
|
|
|
111
107
|
File.write(path, DEFAULT_CONFIG)
|
|
112
|
-
puts "Created rfmt configuration file: #{path}"
|
|
113
108
|
true
|
|
114
109
|
end
|
|
115
110
|
|
metadata
CHANGED
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rfmt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- fujitani sora
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: diff-lcs
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.5'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.5'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: diffy
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.4'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.4'
|
|
13
41
|
- !ruby/object:Gem::Dependency
|
|
14
42
|
name: rb_sys
|
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -24,6 +52,20 @@ dependencies:
|
|
|
24
52
|
- - "~>"
|
|
25
53
|
- !ruby/object:Gem::Version
|
|
26
54
|
version: 0.9.120
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: thor
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.3'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.3'
|
|
27
69
|
description: Write a longer description or delete this line.
|
|
28
70
|
email:
|
|
29
71
|
- fujitanisora0414@gmail.com
|