rfmt 0.2.1 → 0.2.2

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: f647dba194f9105f5f85937e8927d2f73e856fe009a2ba5eaa4b28aad90a3498
4
- data.tar.gz: 663ab8dd439b10f7f7cbc06c3675bc42d4af27b92baf3340e0f586f5867ea7ae
3
+ metadata.gz: a9981b614b859c7c49e597686b89c79279ba977911b9a62d691aa0b88408eb0f
4
+ data.tar.gz: 1461e9bcf3ff3b298855963fdf2e6cdd0aca1b3e29bf6f5b9037e62889903469
5
5
  SHA512:
6
- metadata.gz: 43721c2ec9d960ed6aaf7c5f18bf8d94accc7ed54b7470a58e91f9c00c7e85667b7f9034362326768db8453a4c2f6aaf8cf6ab5c9ffa1869f4d22e5b5f859743
7
- data.tar.gz: cf024a6618d7cf976479d3951d8f46a906a97f19cffaa2166a6fc23ff95451b4178f2c204bd073cab2d497d90032ccb4d34a68b18ef21af3a6adbd6299fe9d08
6
+ metadata.gz: 0b6aa80fa2df44397d1bdc81a12b860e1446a611f565e3283af8a821e691ca5122041f9622d48e2577740e878d740a1dba95df0a8c6acbea71c8301993f6fb46
7
+ data.tar.gz: a1f330f17114d738523adf1fdcc9ccc42590b028a754e32304d2d4724f9966d54e5a7467bd50c8884111bc3ff16780b9b9c7591a0a22d8cd1eb17ffa50a856ed
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.2] - 2025-01-25
4
+
5
+ ### Fixed
6
+ - Fixed blank line formatting to output single blank line instead of double blank lines
7
+
3
8
  ## [0.2.1] - 2025-01-25
4
9
 
5
10
  ### Fixed
data/Cargo.lock CHANGED
@@ -1219,7 +1219,7 @@ checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
1219
1219
 
1220
1220
  [[package]]
1221
1221
  name = "rfmt"
1222
- version = "0.2.1"
1222
+ version = "0.2.2"
1223
1223
  dependencies = [
1224
1224
  "anyhow",
1225
1225
  "clap",
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- A fast, opinionated Ruby code formatter written in Rust
5
+ A Ruby code formatter written in Rust
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/rfmt.svg)](https://badge.fury.io/rb/rfmt)
8
8
  [![Test Status](https://github.com/fujitanisora/rfmt/workflows/test/badge.svg)](https://github.com/fujitanisora/rfmt/actions)
@@ -20,54 +20,54 @@
20
20
 
21
21
  ## What is rfmt?
22
22
 
23
- **rfmt** is a lightning-fast Ruby code formatter that enforces consistent style across your codebase. It's designed to be:
23
+ **rfmt** is a Ruby code formatter that enforces consistent style across your codebase. Key characteristics:
24
24
 
25
- - **Fast**: Written in Rust for maximum performance
26
- - **Opinionated**: Minimal configuration, consistent results
27
- - **Idempotent**: Running it multiple times produces the same output
28
- - **Comment-preserving**: Your comments stay exactly where they should
29
- - **Production-ready**: Comprehensive error handling and logging
25
+ - **Rust implementation**: Provides faster execution than Ruby-based tools
26
+ - **Opinionated**: Minimal configuration with consistent output
27
+ - **Idempotent**: Running multiple times produces identical results
28
+ - **Comment preservation**: Maintains existing comment placement
29
+ - **Error handling**: Includes structured error messages and logging
30
30
 
31
31
  ## Features
32
32
 
33
- ### Performance
33
+ ### Performance
34
34
 
35
- rfmt is built with Rust, making it significantly faster than pure-Ruby formatters:
35
+ Built with Rust for improved execution speed:
36
36
 
37
- - Formats thousands of lines per second
38
- - Parallel processing support
39
- - Smart caching for unchanged files
37
+ - Benchmark shows 7-58x faster than RuboCop depending on project size (see Performance Benchmarks section)
38
+ - Processes 168 files/second in tested Rails project
39
+ - Supports parallel processing
40
40
 
41
- ### 🎨 Consistent Style
41
+ ### Consistent Style
42
42
 
43
- rfmt enforces a consistent code style across your entire project:
43
+ Enforces code style rules:
44
44
 
45
45
  - Automatic indentation
46
- - Consistent spacing and alignment
47
- - Quote style normalization
46
+ - Spacing and alignment normalization
47
+ - Quote style standardization
48
48
  - Method definition formatting
49
49
 
50
- ### 🔍 Smart Error Handling
50
+ ### Error Handling
51
51
 
52
- rfmt provides detailed, actionable error messages:
52
+ Provides structured error messages:
53
53
 
54
- - **Error codes** (E001-E999) for easy troubleshooting
55
- - **Code snippets** showing exactly where errors occur
56
- - **Help URLs** linking to detailed documentation
57
- - **Recovery strategies** to handle partial formatting
54
+ - Error codes (E001-E999) for categorization
55
+ - Code snippets showing error locations
56
+ - Help URLs linking to documentation
57
+ - Recovery strategies for handling errors
58
58
 
59
- ### 📊 Comprehensive Logging
59
+ ### Logging
60
60
 
61
- Built-in logging system for debugging and monitoring:
61
+ Built-in logging system:
62
62
 
63
- - Multiple log levels (ERROR, WARN, INFO, DEBUG, TRACE)
64
- - Structured output for easy parsing
65
- - Performance metrics and timing information
66
- - Debug context for complex operations
63
+ - 5 log levels (ERROR, WARN, INFO, DEBUG, TRACE)
64
+ - Structured output format
65
+ - Performance metrics and timing data
66
+ - Debug context information
67
67
 
68
- ### 🧩 Editor Integration
68
+ ### Editor Integration
69
69
 
70
- Works with your favorite editor:
70
+ Compatible editors:
71
71
 
72
72
  - Visual Studio Code
73
73
  - RubyMine / IntelliJ IDEA
@@ -168,7 +168,7 @@ Create a configuration file with default settings:
168
168
  rfmt init
169
169
  ```
170
170
 
171
- This creates a `.rfmt.yml` file with sensible defaults:
171
+ This creates a `.rfmt.yml` file with default settings:
172
172
 
173
173
  ```yaml
174
174
  version: "1.0"
@@ -234,7 +234,7 @@ config = Rfmt::Config.load
234
234
 
235
235
  ## Error Handling
236
236
 
237
- rfmt provides detailed error messages with actionable solutions:
237
+ rfmt provides structured error messages:
238
238
 
239
239
  ```
240
240
  [Rfmt::ParseError] Parse error in app/models/user.rb:15:10
@@ -269,7 +269,7 @@ See the [Error Reference](docs/error_reference.md) for detailed information.
269
269
 
270
270
  ## Logging
271
271
 
272
- rfmt includes a comprehensive logging system:
272
+ rfmt includes a logging system:
273
273
 
274
274
  ```ruby
275
275
  # Logs are automatically output during initialization
@@ -312,118 +312,45 @@ class User < ApplicationRecord
312
312
  end
313
313
  ```
314
314
 
315
- ## Documentation
316
-
317
- Comprehensive documentation is available:
318
-
319
- - 📖 [User Guide](docs/user_guide.md) - Complete usage guide
320
- - 🌐 [User Guide (日本語)](docs/user_guide.ja.md) - Japanese version
321
- - 🔍 [Error Reference](docs/error_reference.md) - All error codes and solutions
322
- - 🔍 [Error Reference (日本語)](docs/error_reference.ja.md) - Japanese version
323
- - 🤝 [Contributing Guide](CONTRIBUTING.md) - How to contribute
324
- - 📊 [Phase 4 Implementation](docs/phase4_implementation_summary.md) - Recent changes
325
-
326
- ## Development
327
-
328
- After checking out the repo:
315
+ ## Performance Benchmarks
329
316
 
330
- ```bash
331
- # Install dependencies
332
- bundle install
333
-
334
- # Compile Rust extension
335
- bundle exec rake compile
317
+ Performance comparison with RuboCop on a Rails project (111 files, 3,231 lines):
336
318
 
337
- # Run tests
338
- bundle exec rspec
339
-
340
- # Run Rust tests
341
- cd ext/rfmt && cargo test
342
- ```
343
-
344
- ### Running Tests
345
-
346
- ```bash
347
- # All tests
348
- bundle exec rspec
349
-
350
- # Specific test file
351
- bundle exec rspec spec/error_handling_spec.rb
352
-
353
- # With documentation format
354
- bundle exec rspec --format documentation
355
- ```
319
+ ### Benchmark Results (Rails Project)
356
320
 
357
- ### Test Results
321
+ | Scenario | rfmt | RuboCop | Speedup |
322
+ |----------|------|---------|---------|
323
+ | **Single File** | ~190ms | ~1.35s | **7.3x faster** |
324
+ | **Directory (14 files)** | 168ms | 1.67s | **10.0x faster** |
325
+ | **Full Project (111 files)** | 173ms | 10.09s | **58.5x faster** |
326
+ | **Check Mode (CI/CD)** | 172ms | 1.55s | **9.0x faster** |
358
327
 
359
- All 187 tests passing:
360
- - 172 existing tests
361
- - 15 new tests for error handling and logging
328
+ ### Key Metrics
362
329
 
363
- ## Performance
330
+ - Single file: Formats in ~190ms
331
+ - Scaling: 58x faster on full project (111 files)
332
+ - CI/CD: Check time reduced from 10.09s to 0.173s (98% reduction)
333
+ - Variance: Low standard deviation across runs
364
334
 
365
- rfmt is designed for speed:
335
+ ### Throughput Comparison
366
336
 
367
- | File Size | Format Time |
368
- |-----------|-------------|
369
- | 100 lines | < 10ms |
370
- | 1,000 lines | < 50ms |
371
- | 10,000 lines | < 500ms |
337
+ | Directory | rfmt | RuboCop | Difference |
338
+ |-----------|------|---------|------------|
339
+ | app/models (14 files) | 83.5 files/s | 8.4 files/s | **10x throughput** |
340
+ | test/ (30 files) | 168.1 files/s | 18.1 files/s | **9.3x throughput** |
372
341
 
373
- *Benchmarks run on M1 MacBook Pro*
342
+ *Benchmark environment: Apple Silicon (arm64), macOS Darwin 23.6.0, Ruby 3.4.5*
374
343
 
375
- ## Roadmap
344
+ See [detailed benchmark report](docspriv/benchmark_report.md) for full data.
376
345
 
377
- See [ROADMAP.md](ROADMAP.md) for planned features:
346
+ ## Documentation
378
347
 
379
- - [ ] Pattern matching support
380
- - [ ] Numbered parameters
381
- - [ ] Additional formatting rules
382
- - [ ] Plugin system
383
- - [ ] Language server protocol (LSP)
348
+ Documentation is available in the [docs](docs/) directory. See [User Guide](docs/user_guide.md) or [Contributing Guide](CONTRIBUTING.md) for details.
384
349
 
385
350
  ## Contributing
386
351
 
387
352
  We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
388
353
 
389
- **Quick Start:**
390
-
391
- 1. Fork the repository
392
- 2. Create your feature branch (`git checkout -b feature/my-feature`)
393
- 3. Make your changes and add tests
394
- 4. Run tests (`bundle exec rspec`)
395
- 5. Commit your changes (`git commit -m 'feat: add some feature'`)
396
- 6. Push to the branch (`git push origin feature/my-feature`)
397
- 7. Open a Pull Request
398
-
399
- ## Architecture
400
-
401
- rfmt is built with a hybrid Ruby-Rust architecture:
402
-
403
- ```
404
- ┌─────────────────┐
405
- │ Ruby Layer │ ← User API, Prism parser
406
- ├─────────────────┤
407
- │ FFI Interface │ ← Magnus (Ruby-Rust bridge)
408
- ├─────────────────┤
409
- │ Rust Layer │ ← Formatting engine
410
- │ │ - Parser (AST)
411
- │ │ - Formatter (Rules)
412
- │ │ - Emitter (Output)
413
- │ │ - Error Handler
414
- │ │ - Logger
415
- └─────────────────┘
416
- ```
417
-
418
- ## Technology Stack
419
-
420
- - **Ruby**: 3.0+ (Prism parser, FFI interface)
421
- - **Rust**: 1.70+ (Core formatting engine)
422
- - **Magnus**: Ruby-Rust FFI bridge
423
- - **Prism**: Modern Ruby parser
424
- - **RSpec**: Ruby testing
425
- - **Cargo**: Rust build system
426
-
427
354
  ## Comparison with Other Tools
428
355
 
429
356
  ### rfmt vs RuboCop
@@ -431,36 +358,12 @@ rfmt is built with a hybrid Ruby-Rust architecture:
431
358
  | Feature | rfmt | RuboCop |
432
359
  |---------|------|---------|
433
360
  | **Primary Purpose** | Code formatting | Linting + formatting |
434
- | **Speed** | Very fast (Rust) | Moderate (Ruby) |
361
+ | **Speed** | 58x faster (tested benchmark) | Ruby-based |
435
362
  | **Configuration** | Minimal | Extensive |
436
363
  | **Code Quality Checks** | No | Yes |
437
364
  | **Bug Detection** | No | Yes |
438
365
 
439
- **Recommendation**: Use rfmt for consistent formatting, RuboCop for code quality checks.
440
-
441
- ### rfmt vs Prettier (Ruby plugin)
442
-
443
- | Feature | rfmt | Prettier |
444
- |---------|------|----------|
445
- | **Native Ruby Support** | Yes | Via plugin |
446
- | **Speed** | Very fast | Fast |
447
- | **Ruby-specific Features** | Full support | Limited |
448
- | **Comment Preservation** | Excellent | Good |
449
-
450
- ## Project Status
451
-
452
- rfmt is under active development. Current phase:
453
-
454
- - ✅ Phase 1: Foundation (Complete)
455
- - ✅ Phase 2: Core Formatting (Complete)
456
- - ✅ Phase 3: Advanced Features (Complete)
457
- - ✅ Phase 4: Production Quality (Logging & Error Control Complete)
458
- - ✅ Error Handling System
459
- - ✅ Logging System
460
- - ⬜ Documentation (In Progress)
461
- - ⬜ Security
462
- - ⬜ Release Process
463
- - ⬜ Editor Integration
366
+ **Note**: rfmt focuses on formatting speed, while RuboCop provides additional code quality analysis. They can be used together.
464
367
 
465
368
  ## License
466
369
 
@@ -487,8 +390,6 @@ Everyone interacting in the rfmt project's codebases, issue trackers, chat rooms
487
390
 
488
391
  <div align="center">
489
392
 
490
- Made with ❤️ by [Fujitani Sora](https://github.com/fs0414)
491
-
492
- **⭐ Star us on GitHub — it motivates us a lot!**
393
+ Created by [Fujitani Sora](https://github.com/fs0414)
493
394
 
494
395
  </div>
data/ext/rfmt/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rfmt"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  edition = "2021"
5
5
  authors = ["fujitani sora <fujitanisora0414@gmail.com>"]
6
6
  license = "MIT"
@@ -123,9 +123,17 @@ impl Emitter {
123
123
  for (i, child) in node.children.iter().enumerate() {
124
124
  self.emit_node(child, indent_level)?;
125
125
 
126
- // Add newline between top-level statements
126
+ // Add newlines between top-level statements, normalizing to max 1 blank line
127
127
  if i < node.children.len() - 1 {
128
- self.buffer.push('\n');
128
+ let current_end_line = child.location.end_line;
129
+ let next_start_line = node.children[i + 1].location.start_line;
130
+ let line_diff = next_start_line.saturating_sub(current_end_line);
131
+
132
+ // Add 1 newline if consecutive, 2 newlines (1 blank line) if there was a gap
133
+ let newlines = if line_diff > 1 { 2 } else { 1 };
134
+ for _ in 0..newlines {
135
+ self.buffer.push('\n');
136
+ }
129
137
  }
130
138
  }
131
139
  Ok(())
@@ -136,9 +144,17 @@ impl Emitter {
136
144
  for (i, child) in node.children.iter().enumerate() {
137
145
  self.emit_node(child, indent_level)?;
138
146
 
139
- // Add newline between statements
147
+ // Add newlines between statements, normalizing to max 1 blank line
140
148
  if i < node.children.len() - 1 {
141
- self.buffer.push('\n');
149
+ let current_end_line = child.location.end_line;
150
+ let next_start_line = node.children[i + 1].location.start_line;
151
+ let line_diff = next_start_line.saturating_sub(current_end_line);
152
+
153
+ // Add 1 newline if consecutive, 2 newlines (1 blank line) if there was a gap
154
+ let newlines = if line_diff > 1 { 2 } else { 1 };
155
+ for _ in 0..newlines {
156
+ self.buffer.push('\n');
157
+ }
142
158
  }
143
159
  }
144
160
  Ok(())
data/lib/rfmt/rfmt.so CHANGED
Binary file
data/lib/rfmt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rfmt
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rfmt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - fujitani sora