rfmt 0.3.0 → 0.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 +17 -0
- data/Cargo.lock +1 -1
- data/README.md +65 -7
- data/ext/rfmt/Cargo.toml +1 -1
- data/ext/rfmt/src/ast/mod.rs +2 -0
- data/ext/rfmt/src/config/mod.rs +1 -0
- data/ext/rfmt/src/emitter/mod.rs +10 -3
- data/ext/rfmt/src/lib.rs +4 -1
- data/ext/rfmt/src/logging/logger.rs +11 -2
- data/lib/rfmt/cli.rb +7 -3
- data/lib/rfmt/rfmt.so +0 -0
- data/lib/rfmt/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3077996bae0d4959192fb7885a197916fc85679858ebc10ea00ea8696bf6d320
|
|
4
|
+
data.tar.gz: c3adf00614cc4ed60df41858c482e77b9618f06f7dadc7e9bc016c7b4d419f99
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8c1a042bcbcd8e74ac67918428fee08f8f4cf85e4f5f67a0cd9196bf7305489808176c442e8d388ae50c32c7d3f329706788c02a62ef69708be9126e600556bf
|
|
7
|
+
data.tar.gz: 2f13782fff7bb4eaea95a78bc8e3d55c4f00395074b9e5befb6d93e66b803673f5bfb1f285ae74facb009d65dbdf2ae8322f405232277994c010d88135d62746
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2025-11-26
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Verbose mode option (`--verbose` flag) for detailed output during formatting
|
|
7
|
+
- Git commit hook configuration with Lefthook integration for automatic formatting
|
|
8
|
+
- RubyGems badge and installation instructions in README
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Improved documentation structure and readability in user guides (English and Japanese)
|
|
12
|
+
- Enhanced logging system with verbose output support
|
|
13
|
+
- Updated benchmark documentation in README
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Command formatting to execution conversion issues
|
|
17
|
+
- Documentation version command display
|
|
18
|
+
- Various code quality improvements based on Clippy suggestions
|
|
19
|
+
|
|
3
20
|
## [0.3.0] - 2025-11-25
|
|
4
21
|
|
|
5
22
|
### Changed
|
data/Cargo.lock
CHANGED
data/README.md
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
A Ruby code formatter written in Rust
|
|
6
6
|
|
|
7
|
-
[](https://
|
|
8
|
-
[](https://github.com/fujitanisora/rfmt/actions)
|
|
7
|
+
[](https://rubygems.org/gems/rfmt)
|
|
9
8
|
[](https://opensource.org/licenses/MIT)
|
|
10
9
|
|
|
11
10
|
[Installation](#installation) •
|
|
@@ -20,6 +19,8 @@ A Ruby code formatter written in Rust
|
|
|
20
19
|
|
|
21
20
|
## What is rfmt?
|
|
22
21
|
|
|
22
|
+
[RubyGems reference](https://rubygems.org/gems/rfmt)
|
|
23
|
+
|
|
23
24
|
**rfmt** is a Ruby code formatter that enforces consistent style across your codebase. Key characteristics:
|
|
24
25
|
|
|
25
26
|
- **Opinionated**: Minimal configuration with consistent output
|
|
@@ -44,7 +45,7 @@ Enforces code style rules:
|
|
|
44
45
|
|
|
45
46
|
## Performance Benchmarks
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
Execution time comparison on a Rails project (111 files, 3,241 lines):
|
|
48
49
|
|
|
49
50
|
| Test Type | Files | rfmt | RuboCop | Ratio |
|
|
50
51
|
|-----------|-------|------|---------|-------|
|
|
@@ -52,6 +53,13 @@ Performance comparison with RuboCop on a Rails project (111 files, 3,241 lines):
|
|
|
52
53
|
| Directory | 14 | 176ms | 1.68s | 9.6x |
|
|
53
54
|
| Full Project (check) | 111 | 172ms | 4.36s | 25.4x |
|
|
54
55
|
|
|
56
|
+
**About this comparison:**
|
|
57
|
+
- RuboCop times include startup overhead and loading all cops (linting rules)
|
|
58
|
+
- RuboCop was run with default configuration (all cops enabled)
|
|
59
|
+
- rfmt is a formatting-only tool with minimal overhead
|
|
60
|
+
- Both tools were measured in check mode (no file modifications)
|
|
61
|
+
- Results are averages from 10 runs per test
|
|
62
|
+
|
|
55
63
|
**Observations:**
|
|
56
64
|
- rfmt execution time remains constant (172-191ms) regardless of file count
|
|
57
65
|
- Low variance across runs (standard deviation: 8-23ms)
|
|
@@ -59,7 +67,7 @@ Performance comparison with RuboCop on a Rails project (111 files, 3,241 lines):
|
|
|
59
67
|
**Test Environment:**
|
|
60
68
|
- CPU: Apple Silicon (arm64)
|
|
61
69
|
- Ruby: 3.4.5
|
|
62
|
-
- rfmt: 0.
|
|
70
|
+
- rfmt: 0.3.0, RuboCop: 1.81.7
|
|
63
71
|
|
|
64
72
|
See [detailed benchmark report](docs/benchmark.md) for complete data.
|
|
65
73
|
|
|
@@ -146,13 +154,13 @@ rfmt init --force
|
|
|
146
154
|
Format a single file:
|
|
147
155
|
|
|
148
156
|
```bash
|
|
149
|
-
rfmt
|
|
157
|
+
rfmt exec lib/user.rb
|
|
150
158
|
```
|
|
151
159
|
|
|
152
160
|
Format multiple files:
|
|
153
161
|
|
|
154
162
|
```bash
|
|
155
|
-
rfmt
|
|
163
|
+
rfmt exec lib/**/*.rb
|
|
156
164
|
```
|
|
157
165
|
|
|
158
166
|
Check if files need formatting (CI/CD):
|
|
@@ -164,7 +172,15 @@ rfmt check .
|
|
|
164
172
|
Show diff without modifying files:
|
|
165
173
|
|
|
166
174
|
```bash
|
|
167
|
-
rfmt
|
|
175
|
+
rfmt exec lib/user.rb --diff
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Enable verbose output for debugging:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
rfmt exec lib/user.rb --verbose
|
|
182
|
+
# or use environment variable
|
|
183
|
+
DEBUG=1 rfmt exec lib/user.rb
|
|
168
184
|
```
|
|
169
185
|
|
|
170
186
|
### Ruby API
|
|
@@ -255,6 +271,48 @@ class User < ApplicationRecord
|
|
|
255
271
|
end
|
|
256
272
|
```
|
|
257
273
|
|
|
274
|
+
## Development
|
|
275
|
+
|
|
276
|
+
### Setup
|
|
277
|
+
|
|
278
|
+
After cloning the repository:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
bundle install
|
|
282
|
+
bundle exec lefthook install
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Git Hooks
|
|
286
|
+
|
|
287
|
+
This project uses [lefthook](https://github.com/evilmartians/lefthook) for automated validation before push:
|
|
288
|
+
|
|
289
|
+
**Pre-push checks:**
|
|
290
|
+
- RuboCop (Ruby linting)
|
|
291
|
+
- cargo fmt --check (Rust formatting)
|
|
292
|
+
- cargo clippy (Rust linting)
|
|
293
|
+
|
|
294
|
+
**Skip hooks temporarily:**
|
|
295
|
+
```bash
|
|
296
|
+
# Skip all hooks for this push
|
|
297
|
+
LEFTHOOK=0 git push
|
|
298
|
+
|
|
299
|
+
# Skip specific hook
|
|
300
|
+
LEFTHOOK_EXCLUDE=rubocop git push
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Running Tests
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Ruby tests
|
|
307
|
+
bundle exec rspec
|
|
308
|
+
|
|
309
|
+
# Rust tests
|
|
310
|
+
cargo test --manifest-path ext/rfmt/Cargo.toml
|
|
311
|
+
|
|
312
|
+
# All tests
|
|
313
|
+
bundle exec rake dev:test_all
|
|
314
|
+
```
|
|
315
|
+
|
|
258
316
|
## Documentation
|
|
259
317
|
|
|
260
318
|
Documentation is available in the [docs](docs/) directory:
|
data/ext/rfmt/Cargo.toml
CHANGED
data/ext/rfmt/src/ast/mod.rs
CHANGED
|
@@ -174,6 +174,7 @@ impl Node {
|
|
|
174
174
|
|
|
175
175
|
/// Add metadata to the node
|
|
176
176
|
#[cfg(test)]
|
|
177
|
+
#[allow(dead_code)]
|
|
177
178
|
pub fn with_metadata(mut self, metadata: HashMap<String, String>) -> Self {
|
|
178
179
|
self.metadata = metadata;
|
|
179
180
|
self
|
|
@@ -181,6 +182,7 @@ impl Node {
|
|
|
181
182
|
|
|
182
183
|
/// Add comments to the node
|
|
183
184
|
#[cfg(test)]
|
|
185
|
+
#[allow(dead_code)]
|
|
184
186
|
pub fn with_comments(mut self, comments: Vec<Comment>) -> Self {
|
|
185
187
|
self.comments = comments;
|
|
186
188
|
self
|
data/ext/rfmt/src/config/mod.rs
CHANGED
data/ext/rfmt/src/emitter/mod.rs
CHANGED
|
@@ -309,11 +309,18 @@ impl Emitter {
|
|
|
309
309
|
/// Emit if/unless/elsif/else node
|
|
310
310
|
/// is_elsif: true if this is an elsif clause (don't emit 'end')
|
|
311
311
|
/// keyword: "if" or "unless"
|
|
312
|
-
fn emit_if_unless(
|
|
312
|
+
fn emit_if_unless(
|
|
313
|
+
&mut self,
|
|
314
|
+
node: &Node,
|
|
315
|
+
indent_level: usize,
|
|
316
|
+
is_elsif: bool,
|
|
317
|
+
keyword: &str,
|
|
318
|
+
) -> Result<()> {
|
|
313
319
|
// Check if this is a postfix if (modifier form)
|
|
314
320
|
// In postfix if, the statements come before the if keyword in source
|
|
315
321
|
let is_postfix = if let (Some(predicate), Some(statements)) =
|
|
316
|
-
(node.children.first(), node.children.get(1))
|
|
322
|
+
(node.children.first(), node.children.get(1))
|
|
323
|
+
{
|
|
317
324
|
statements.location.start_offset < predicate.location.start_offset
|
|
318
325
|
} else {
|
|
319
326
|
false
|
|
@@ -399,7 +406,7 @@ impl Emitter {
|
|
|
399
406
|
NodeType::ElseNode => {
|
|
400
407
|
// This is an else clause
|
|
401
408
|
self.emit_indent(indent_level)?;
|
|
402
|
-
|
|
409
|
+
writeln!(self.buffer, "else")?;
|
|
403
410
|
|
|
404
411
|
// Emit else body (first child of ElseNode)
|
|
405
412
|
if let Some(else_statements) = consequent.children.first() {
|
data/ext/rfmt/src/lib.rs
CHANGED
|
@@ -29,7 +29,10 @@ fn format_ruby_code(ruby: &Ruby, source: String, json: String) -> Result<String,
|
|
|
29
29
|
// Load configuration from file or use defaults
|
|
30
30
|
log::info!("Attempting to discover config file...");
|
|
31
31
|
let config = Config::discover().map_err(|e| e.to_magnus_error(ruby))?;
|
|
32
|
-
log::info!(
|
|
32
|
+
log::info!(
|
|
33
|
+
"Config loaded successfully, line_length: {}",
|
|
34
|
+
config.formatting.line_length
|
|
35
|
+
);
|
|
33
36
|
let mut emitter = Emitter::with_source(config, source);
|
|
34
37
|
|
|
35
38
|
let formatted = emitter.emit(&ast).map_err(|e| e.to_magnus_error(ruby))?;
|
|
@@ -22,10 +22,20 @@ impl RfmtLogger {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
pub fn init() {
|
|
25
|
+
// Check for debug mode via environment variables
|
|
26
|
+
let debug_mode = std::env::var("DEBUG").is_ok()
|
|
27
|
+
|| std::env::var("RFMT_DEBUG").is_ok()
|
|
28
|
+
|| std::env::var("RUST_LOG").is_ok();
|
|
29
|
+
|
|
25
30
|
let level = std::env::var("RFMT_LOG")
|
|
26
31
|
.ok()
|
|
27
32
|
.and_then(|s| s.parse().ok())
|
|
28
|
-
.unwrap_or(
|
|
33
|
+
.unwrap_or(if debug_mode {
|
|
34
|
+
LevelFilter::Info
|
|
35
|
+
} else {
|
|
36
|
+
// In normal mode, only show warnings and errors
|
|
37
|
+
LevelFilter::Warn
|
|
38
|
+
});
|
|
29
39
|
let logger = Self::new(level);
|
|
30
40
|
log::set_boxed_logger(Box::new(logger)).expect("Failed to initialize logger");
|
|
31
41
|
log::set_max_level(LevelFilter::Trace);
|
|
@@ -63,7 +73,6 @@ impl Log for RfmtLogger {
|
|
|
63
73
|
#[cfg(test)]
|
|
64
74
|
mod tests {
|
|
65
75
|
use super::*;
|
|
66
|
-
use log::{debug, error, info, trace, warn};
|
|
67
76
|
use std::sync::{Arc, Mutex};
|
|
68
77
|
|
|
69
78
|
struct TestWriter {
|
data/lib/rfmt/cli.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
|
+
|
|
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')
|
|
7
|
+
|
|
4
8
|
require 'rfmt'
|
|
5
9
|
require 'rfmt/configuration'
|
|
6
10
|
require 'rfmt/cache'
|
|
@@ -43,7 +47,7 @@ module Rfmt
|
|
|
43
47
|
class_option :config, type: :string, desc: 'Path to configuration file'
|
|
44
48
|
class_option :verbose, type: :boolean, aliases: '-v', desc: 'Verbose output'
|
|
45
49
|
|
|
46
|
-
desc '
|
|
50
|
+
desc 'exec [FILES]', 'Format Ruby files'
|
|
47
51
|
option :write, type: :boolean, default: true, desc: 'Write formatted output'
|
|
48
52
|
option :check, type: :boolean, desc: "Check if files are formatted (don't write)"
|
|
49
53
|
option :diff, type: :boolean, desc: 'Show diff of changes'
|
|
@@ -52,7 +56,7 @@ module Rfmt
|
|
|
52
56
|
option :jobs, type: :numeric, desc: 'Number of parallel jobs (default: CPU count)'
|
|
53
57
|
option :cache, type: :boolean, default: true, desc: 'Use cache to skip unchanged files'
|
|
54
58
|
option :cache_dir, type: :string, desc: 'Cache directory (default: ~/.cache/rfmt)'
|
|
55
|
-
def
|
|
59
|
+
def exec(*files)
|
|
56
60
|
config = load_config
|
|
57
61
|
files = files.empty? ? config.files_to_format : files.flatten
|
|
58
62
|
|
|
@@ -92,7 +96,7 @@ module Rfmt
|
|
|
92
96
|
|
|
93
97
|
desc 'check [FILES]', 'Check if files need formatting'
|
|
94
98
|
def check(*files)
|
|
95
|
-
invoke :
|
|
99
|
+
invoke :exec, files, check: true, write: false
|
|
96
100
|
end
|
|
97
101
|
|
|
98
102
|
desc 'version', 'Show version'
|
data/lib/rfmt/rfmt.so
CHANGED
|
Binary file
|
data/lib/rfmt/version.rb
CHANGED