rfmt
A Ruby code formatter written in Rust
Installation • Usage • Features • Editor Integration • Documentation • Contributing
What is rfmt?
rfmt is a Ruby code formatter that enforces consistent style across your codebase. Key characteristics:
- Opinionated: Minimal configuration with consistent output
- Idempotent: Running multiple times produces identical results
- Comment preservation: Maintains existing comment placement
- Rust implementation: Core formatter implemented in Rust
Features
Performance
Built with Rust for improved execution speed. See Performance Benchmarks section for details.
Consistent Style
Enforces code style rules:
- Automatic indentation
- Spacing and alignment normalization
- Quote style standardization
- Method definition formatting
Performance Benchmarks
Execution time comparison on a Rails project (111 files, 3,241 lines):
| Test Type | Files | rfmt | RuboCop | Ratio |
|---|---|---|---|---|
| Single File | 1 | 191ms | 1.38s | 7.2x |
| Directory | 14 | 176ms | 1.68s | 9.6x |
| Full Project (check) | 111 | 172ms | 4.36s | 25.4x |
About this comparison:
- RuboCop times include startup overhead and loading all cops (linting rules)
- RuboCop was run with default configuration (all cops enabled)
- rfmt is a formatting-only tool with minimal overhead
- Both tools were measured in check mode (no file modifications)
- Results are averages from 10 runs per test
Observations:
- rfmt execution time remains constant (172-191ms) regardless of file count
- Low variance across runs (standard deviation: 8-23ms)
Test Environment:
- CPU: Apple Silicon (arm64)
- Ruby: 3.4.5
- rfmt: 0.3.0, RuboCop: 1.81.7
See detailed benchmark report for complete data.
Installation
Requirements
- Ruby 3.0 or higher
- Rust 1.70 or higher (for building from source)
From RubyGems
gem install rfmtIn Your Gemfile
gem 'rfmt'Then run:
bundle installFrom Source
git clone https://github.com/fujitanisora/rfmt.git
cd rfmt
bundle install
bundle exec rake compileUsage
Initialize Configuration
First, create a configuration file with default settings:
rfmt initThis creates a .rfmt.yml file with default settings:
version: "1.0"
formatting:
line_length: 100 # Maximum line length (40-500)
indent_width: 2 # Spaces/tabs per indent (1-8)
indent_style: "spaces" # "spaces" or "tabs"
quote_style: "double" # "double", "single", or "consistent"
include:
- "**/*.rb"
- "**/*.rake"
- "**/Rakefile"
- "**/Gemfile"
exclude:
- "vendor/**/*"
- "tmp/**/*"
- "node_modules/**/*"
- "db/schema.rb"Options:
# Specify custom path
rfmt init --path config/.rfmt.yml
# Overwrite existing configuration
rfmt init --forceCommand Line
Format a single file:
rfmt lib/user.rbFormat multiple files:
rfmt lib/**/*.rbCheck if files need formatting (CI/CD):
rfmt check .Show diff without modifying files:
rfmt lib/user.rb --diffEnable verbose output for debugging:
rfmt lib/user.rb --verbose
# or use environment variable
DEBUG=1 rfmt lib/user.rbRuby API
Input (unformatted code):
require 'rfmt'
source = <<~RUBY
class User
def initialize(name)
@name=name
end
end
RUBY
formatted = Rfmt.format(source)
puts formattedOutput (formatted code):
class User
def initialize(name)
@name=name
end
endConfiguration
Configuration File Discovery
rfmt automatically searches for configuration files in this order:
- Current directory (
.rfmt.yml,.rfmt.yaml,rfmt.yml, orrfmt.yaml) - Parent directories (up to root)
- User home directory (
.rfmt.yml,.rfmt.yaml,rfmt.yml, orrfmt.yaml) - Default settings (if no file found)
Ruby API for Configuration
require 'rfmt'
# Generate configuration file
Rfmt::Config.init('.rfmt.yml', force: false)
# Find configuration file
config_path = Rfmt::Config.find
# => "/Users/username/project/.rfmt.yml"
# Check if configuration exists
Rfmt::Config.exists?
# => true
# Load configuration
config = Rfmt::Config.load
# => {"version"=>"1.0", "formatting"=>{"line_length"=>100, ...}, ...}Examples
Before Formatting
class User<ApplicationRecord
has_many :posts
validates :email,presence: true
def full_name
"#{first_name} #{last_name}"
end
endAfter Formatting
class User < ApplicationRecord
has_many :posts
validates :email, presence: true
def full_name
"#{first_name} #{last_name}"
end
endEditor Integration
Neovim
Format Ruby files on save using autocmd:
-- ~/.config/nvim/init.lua
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = { "*.rb", "*.rake", "Gemfile", "Rakefile" },
callback = function()
local filepath = vim.fn.expand("%:p")
local result = vim.fn.system({ "rfmt", filepath })
if vim.v.shell_error == 0 then
vim.cmd("edit!")
end
end,
})Coming Soon
- VS Code - Extension in development
- RubyMine - Plugin in development
- Zed - Extension in development
Development
Setup
After cloning the repository:
bundle install
bundle exec lefthook installGit Hooks
This project uses lefthook for automated validation before push:
Pre-push checks:
- RuboCop (Ruby linting)
- cargo fmt --check (Rust formatting)
- cargo clippy (Rust linting)
Skip hooks temporarily:
# Skip all hooks for this push
LEFTHOOK=0 git push
# Skip specific hook
LEFTHOOK_EXCLUDE=rubocop git pushRunning Tests
# Ruby tests
bundle exec rspec
# Rust tests
cargo test --manifest-path ext/rfmt/Cargo.toml
# All tests
bundle exec rake dev:test_allDocumentation
Documentation is available in the docs directory:
- User Guide - Comprehensive usage guide
- Error Reference - Error codes and troubleshooting
- Contributing Guide - How to contribute
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Comparison with Other Tools
rfmt vs RuboCop
| Feature | rfmt | RuboCop |
|---|---|---|
| Primary Purpose | Code formatting | Linting + formatting |
| Configuration | Minimal | Extensive |
| Code Quality Checks | No | Yes |
| Bug Detection | No | Yes |
Note: rfmt focuses on code formatting, while RuboCop provides additional code quality analysis. They can be used together.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the rfmt project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
Support
- 📖 Documentation
- 🐛 Issues
- 📧 Email: fujitanisora0414@gmail.com
Acknowledgments
- Built with Prism - Modern Ruby parser
- Powered by Rust - Performance and safety
- FFI via Magnus - Ruby-Rust bridge
Created by Fujitani Sora