rfmt

A Ruby code formatter written in Rust

Gem Version License: MIT

InstallationUsageFeaturesEditor IntegrationDocumentationContributing


What is rfmt?

RubyGems reference DeepWiki 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: Parsing and formatting both run natively in Rust (via the ruby-prism crate); Ruby provides the CLI and LSP shell

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

Parsing and formatting both run natively in Rust (the ruby-prism crate, with prism statically linked), so the per-file cost is well under a millisecond:

Pipeline In-process format time
Before native parsing (Ruby Prism parse + JSON handoff to Rust; historical, not reproducible from this checkout) 4.28 ms/file
Now (parsing and formatting in Rust) 0.19 ms/file

Measured with scripts/bench_format.rb over rfmt's own lib/ corpus on arm64 macOS, Ruby 3.4. Reproduce with:

bundle exec ruby scripts/bench_format.rb

A cold CLI invocation (rfmt --check FILE) takes roughly 0.1-0.25 s of wall-clock time; that is Ruby VM startup, not formatting.

For more detail and a historical comparison against RuboCop, see Performance Benchmarks.

Installation

Requirements

  • Ruby 3.3 or higher
  • Rust 1.70 or higher (for building from source)

From RubyGems

gem install rfmt

In Your Gemfile

gem 'rfmt'

Then run:

bundle install

From Source

git clone https://github.com/fs0414/rfmt.git
cd rfmt
bundle install
bundle exec rake compile

Usage

Initialize Configuration

First, create a configuration file with default settings:

rfmt init

This 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 --force

Command Line

Format a single file:

rfmt lib/user.rb

Format multiple files:

rfmt lib/**/*.rb

Format all files in your project:

rfmt .

Check if files need formatting (CI/CD):

rfmt check .

Show diff without modifying files:

rfmt lib/user.rb --diff

Quiet mode (minimal output):

rfmt --quiet lib/**/*.rb

Enable verbose output for debugging:

rfmt --verbose lib/user.rb

Common Options

Option Description
--check Check formatting without writing files
--diff Show diff of changes
--quiet Minimal output
--verbose Detailed output with timing

Output Modes

Normal mode (default):

$ rfmt app/
Processing 25 file(s)...
✓ Formatted app/controllers/users_controller.rb
✓ Formatted app/models/user.rb

✓ Processed 25 files
  (3 formatted, 22 unchanged)

Quiet mode (--quiet or -q):

$ rfmt --quiet app/
✓ 3 files formatted

Verbose mode (--verbose or -v):

$ rfmt --verbose app/
Processing 25 file(s)...
Using sequential processing for 25 files
✓ Formatted app/controllers/users_controller.rb  
✓ app/models/application_record.rb already formatted
...

✓ Processed 25 files
  (3 formatted, 22 unchanged)

Details:
  Total files: 25
  Total time: 0.45s
  Files/sec: 55.6

Parallel Processing

rfmt automatically chooses the optimal processing mode:

  • < 20 files: Sequential processing (fastest for small batches)
  • 20-49 files: Automatic based on average file size
  • ≥ 50 files: Parallel processing (utilizes multiple cores)

You can override this behavior:

# Force parallel processing
rfmt --parallel app/

# Force sequential processing  
rfmt --no-parallel app/

Cache Management

rfmt uses caching to improve performance on large codebases:

# Clear cache if needed
rfmt cache clear

# View cache statistics  
rfmt cache stats

Ruby API

Input (unformatted code):

require 'rfmt'

source = <<~RUBY
  class User
  def initialize(name)
  @name=name
  end
  end
RUBY

formatted = Rfmt.format(source)
puts formatted

Output (formatted code):

class User
  def initialize(name)
    @name=name
  end
end

Configuration

Configuration File Discovery

rfmt automatically searches for configuration files in this order:

  1. Current directory (.rfmt.yml, .rfmt.yaml, rfmt.yml, or rfmt.yaml)
  2. Parent directories (up to root)
  3. User home directory (.rfmt.yml, .rfmt.yaml, rfmt.yml, or rfmt.yaml)
  4. 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
end

After Formatting

class User < ApplicationRecord
  has_many :posts
  validates :email, presence: true

  def full_name
    "#{first_name} #{last_name}"
  end
end

Editor Integration

rfmt can integrate with editors in two ways:

  • Standalone LSP: run rfmt-lsp directly from your editor. This works well for single Ruby scripts or projects without a Gemfile.
  • Ruby LSP add-on: use rfmt as the formatter inside Ruby LSP.

For detailed setup instructions, see Editor Integration Guide.

Standalone LSP

After installing rfmt, configure your editor's Ruby language server command to rfmt-lsp.

gem install rfmt
rfmt-lsp

Example Neovim configuration (with nvim-lspconfig):

local configs = require("lspconfig.configs")
local lspconfig = require("lspconfig")

if not configs.rfmt then
  configs.rfmt = {
    default_config = {
      cmd = { "rfmt-lsp" },
      filetypes = { "ruby" },
      root_dir = lspconfig.util.root_pattern(".rfmt.yml", ".git"),
      single_file_support = true,
    },
  }
end

lspconfig.rfmt.setup({})

Helix, Emacs, and Zed configurations are covered in the Editor Integration Guide.

VSCode (Quick Start)

  1. Install Ruby LSP extension
  2. Add to your settings.json:
{
  "rubyLsp.formatter": "rfmt",
  "editor.formatOnSave": true,
  "[ruby]": {
    "editor.defaultFormatter": "Shopify.ruby-lsp"
  }
}

Neovim

require("lspconfig").ruby_lsp.setup({
  init_options = {
    formatter = "rfmt"
  }
})

See Editor Integration Guide for Helix, Emacs, Sublime Text, and more.

Development

Setup

After cloning the repository:

bundle install
bundle exec lefthook install

Git 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 push

Running Tests

# Ruby tests
bundle exec rspec

# Rust tests
cargo test --manifest-path ext/rfmt/Cargo.toml

# All tests
bundle exec rake dev:test_all

Documentation

Documentation is available in the docs directory:

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

Acknowledgments

  • Built with Prism - Modern Ruby parser
  • Powered by Rust - Performance and safety
  • FFI via Magnus - Ruby-Rust bridge

Created by Fujitani Sora