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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d2092d9d53fa47024fcb8a13fb635a6da7eba5d4c51d3a8df854d551380037b
4
- data.tar.gz: 59bb09f50cb7ee5a033328300a581e973879aaad56067d267b041c2070cac8bf
3
+ metadata.gz: cb4f41a4ba34f1bc106746712bf144fe6f5ee8cf5a80cf26efaecc8450645d89
4
+ data.tar.gz: 94f896c28015d9592d959ef3a42b4db80e309ad664ef97d5f28b0e8479605892
5
5
  SHA512:
6
- metadata.gz: f7043e22d625b0b6a689f01950a6ed0fae7ab3fa1cfa2dcd51af544d72aadf8b0c3aa65a3465c9f9baabf2f1a000c37bddb2f2f57e2defd11344f6194ad3b68b
7
- data.tar.gz: 407b70b3614da3ed19c74bd43d9491ee9bff1ce6cb1ef7f1e1d2d0b71068afb937bd5409668ae0c7454a6f2d12cb4764fa6df0273bef149980be30e0f9f1d7b6
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
@@ -1214,7 +1214,7 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
1214
1214
 
1215
1215
  [[package]]
1216
1216
  name = "rfmt"
1217
- version = "1.3.3"
1217
+ version = "1.4.0"
1218
1218
  dependencies = [
1219
1219
  "anyhow",
1220
1220
  "clap",
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 Benchmarks
49
+ ## Performance
50
50
 
51
- Execution time comparison on a Rails project (111 files, 3,241 lines):
51
+ rfmt delivers consistent, fast formatting across projects of any size:
52
52
 
53
- | Test Type | Files | rfmt | RuboCop | Ratio |
54
- |-----------|-------|------|---------|-------|
55
- | Single File | 1 | 191ms | 1.38s | 7.2x |
56
- | Directory | 14 | 176ms | 1.68s | 9.6x |
57
- | Full Project (check) | 111 | 172ms | 4.36s | 25.4x |
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
- **About this comparison:**
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
- **Observations:**
67
- - rfmt execution time remains constant (172-191ms) regardless of file count
68
- - Low variance across runs (standard deviation: 8-23ms)
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.5
73
- - rfmt: 0.3.0, RuboCop: 1.81.7
68
+ - Ruby: 3.4.8
69
+ - Average of 5 runs per test
74
70
 
75
- See [detailed benchmark report](docs/benchmark.md) for complete data.
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 --verbose
185
- # or use environment variable
186
- DEBUG=1 rfmt lib/user.rb
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
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ # Suppress gem warnings for performance (saves ~2 seconds)
5
+ $VERBOSE = nil unless ENV['RFMT_VERBOSE']
6
+
4
7
  require 'rfmt/cli'
5
8
 
6
9
  # Known subcommands - if first arg is not one of these, prepend 'format'
data/ext/rfmt/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rfmt"
3
- version = "1.3.3"
3
+ version = "1.4.0"
4
4
  edition = "2021"
5
5
  authors = ["fujitani sora <fujitanisora0414@gmail.com>"]
6
6
  license = "MIT"
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?('-v') || ARGV.include?('--verbose')
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, aliases: '-v', desc: 'Verbose output'
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, default: true, desc: 'Process files in parallel'
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 = if options[:cache]
72
- cache_opts = options[:cache_dir] ? { cache_dir: options[:cache_dir] } : {}
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)', :green
84
+ say '✓ All files are already formatted (cached)', :cyan
86
85
  return
87
86
  end
88
87
 
89
- # Show progress message
90
- if files.size == 1
91
- say "Processing #{files.first}...", :blue
92
- else
93
- say "Processing #{files.size} file(s)...", :blue
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 options[:parallel] && files.size > 1
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
- files.map do |file|
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
- # Determine number of processes to use
161
- process_count = options[:jobs] || Parallel.processor_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
- failed_count = 0
195
- changed_count = 0
196
- error_count = 0
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
- say "Error in #{result[:file]}: #{result[:error]}", :red
201
- error_count += 1
202
- next
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
- if result[:changed]
206
- changed_count += 1
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
- # Update cache even if no changes (file was checked)
229
- cache&.mark_formatted(result[:file])
230
- end
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
- # Save cache to disk
234
- cache&.save
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
- # Summary - always show a summary message
237
- if error_count.positive?
238
- say "\n✗ Failed: #{error_count} error(s) occurred", :red
239
- elsif options[:check] && failed_count.positive?
240
- say "\n✗ Check failed: #{failed_count} file(s) need formatting", :yellow
241
- elsif changed_count.positive?
242
- # Success message with appropriate details
243
- say "\n✓ Success! Formatted #{changed_count} file(s)", :green
244
- elsif results.size == 1
245
- say "\n✓ Success! File is already formatted", :green
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
- say "\n✓ Success! All #{results.size} files are already formatted", :green
344
+ display_normal_summary(stats[:changed], unchanged_count, total_files)
248
345
  end
249
346
 
250
- # Detailed summary in verbose mode
251
- if options[:verbose]
252
- say "Total: #{results.size} file(s) processed", :blue
253
- say "Changed: #{changed_count} file(s)", :yellow if changed_count.positive?
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
- exit(1) if (options[:check] && failed_count.positive?) || error_count.positive?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rfmt
4
- VERSION = '1.3.3'
4
+ VERSION = '1.4.0'
5
5
  end
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.3.3
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-16 00:00:00.000000000 Z
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