rfmt 1.7.0-x64-mingw-ucrt → 2.0.0.beta1-x64-mingw-ucrt

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: bbf5dd124b8d65e24ce3371db17fa058b96f71b78dcd50a440d6bd50381d9393
4
- data.tar.gz: 5876e48f846366909c290b4526551955689bb660a91a54ac15820644f4c93322
3
+ metadata.gz: f178ee5b87f21a8d84684e1b399ea6b313f2f18582f479ed4517061e78df9a8b
4
+ data.tar.gz: 1382ae902e56542d501347eafa11725c4d41aeae4c81efd65bdb96c1ee99e692
5
5
  SHA512:
6
- metadata.gz: e49b4feafa30d8ca9e0fb0bc4a65170481ceaaa6ba18b70511c82412c5410b2589ff194073688d13cfde4108a4da61812db31988ade0c3c9bfe4fc8ead9b757d
7
- data.tar.gz: 964dd2718d504b0611a273613c7b90aafd343d33e5e0685b5ba56d242b76f9c7170520ac363737c5ba419754441334912bf2b49bce7bc0ec19dbd973f406af05
6
+ metadata.gz: 1474c6ce0e1d5b96de5f7dc2f7c878d8674d74431c0a2927a995940129a6cdb5331a4f6a61ce255b6b082b211dc26c25afa6f4caea4329e2defbd97b8bd7b6d2
7
+ data.tar.gz: 3d8f14dcd151add6679cf2db154f2a861cd5bb3252f768902aec489b9d3c42c2f0a718f09a118d6e75b12ec183da226e96c47060159e5e4c4b0eb745e6c88531
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.0.0.beta1] - 2026-07-22
4
+
5
+ Parsing now happens natively in Rust. The Ruby-side Prism parse and JSON handoff have been replaced by the ruby-prism crate with prism statically linked into the extension; Ruby remains the CLI/LSP shell.
6
+
7
+ ### Changed
8
+
9
+ - Native parsing via the ruby-prism crate: in-process formatting is about 22x faster: 0.19 ms/file measured with `scripts/bench_format.rb`, against a historical pre-migration baseline of 4.28 ms/file
10
+ - `--config` is now honored when formatting
11
+ - File writes are atomic (write to a temp file, then rename)
12
+
13
+ ### Added
14
+
15
+ - Output-validation guard: formatted output is re-parsed and rejected if it fails to parse
16
+ - Corpus check (`scripts/corpus_check.rb`): reformats every Ruby file in the repository and verifies AST equivalence with the prism gem; runs in CI alongside the parity fixtures
17
+
18
+ ### Fixed
19
+
20
+ - The `__END__` data section is no longer dropped from formatted output; everything from `__END__` to EOF is preserved byte-for-byte
21
+ - Method chain continuation indentation is now computed from the statement's output position instead of its input column, so misindented input converges in a single pass; heredoc bodies inside a reformatted chain keep their content byte-identical
22
+ - A trailing comment on an `else` line stays on that line instead of moving below it
23
+
24
+ ### Removed
25
+
26
+ - prism gem runtime dependency; it remains a development-only dependency for the corpus check and parity fixtures
27
+
3
28
  ## [1.7.0] - 2026-06-04
4
29
 
5
30
  rfmt now ships a standalone LSP server. Point any LSP-capable editor at `rfmt-lsp` and you get format-on-save — no Ruby LSP, no Gemfile, and no editor-specific plugin required. This makes rfmt usable from Helix, Neovim, Emacs, and any other LSP client, including in projects that don't bundle rfmt.
data/README.md CHANGED
@@ -30,7 +30,7 @@ A Ruby code formatter written in Rust
30
30
  - **Opinionated**: Minimal configuration with consistent output
31
31
  - **Idempotent**: Running multiple times produces identical results
32
32
  - **Comment preservation**: Maintains existing comment placement
33
- - **Rust implementation**: Core formatter implemented in Rust
33
+ - **Rust implementation**: Parsing and formatting both run natively in Rust (via the [ruby-prism](https://crates.io/crates/ruby-prism) crate); Ruby provides the CLI and LSP shell
34
34
 
35
35
  ## Features
36
36
 
@@ -49,35 +49,28 @@ Enforces code style rules:
49
49
 
50
50
  ## Performance
51
51
 
52
- rfmt delivers consistent, fast formatting across projects of any size:
52
+ Parsing and formatting both run natively in Rust (the [ruby-prism](https://crates.io/crates/ruby-prism) crate, with prism statically linked), so the per-file cost is well under a millisecond:
53
53
 
54
- | Project Size | Files | Execution Time | Throughput |
55
- |-------------|-------|----------------|------------|
56
- | Small | 9 files | ~105ms | 85 files/sec |
57
- | Medium | 35 files | ~110ms | 315 files/sec |
58
- | Large | 151 files | ~100ms | 1,560 files/sec |
54
+ | Pipeline | In-process format time |
55
+ |----------|------------------------|
56
+ | Before native parsing (Ruby Prism parse + JSON handoff to Rust; historical, not reproducible from this checkout) | 4.28 ms/file |
57
+ | Now (parsing and formatting in Rust) | 0.19 ms/file |
59
58
 
60
- **Key Performance Characteristics:**
59
+ Measured with `scripts/bench_format.rb` over rfmt's own `lib/` corpus on arm64 macOS, Ruby 3.4. Reproduce with:
61
60
 
62
- - **Constant Time**: Execution time stays around 100ms regardless of project size
63
- - **Parallel Processing**: Automatic scaling with available CPU cores
64
- - **High Throughput**: Up to 1,500+ files per second on large projects
65
- - **Low Overhead**: Minimal startup time and memory usage
66
-
67
- **Test Environment:**
68
- - CPU: Apple Silicon (arm64)
69
- - Ruby: 3.4.8
70
- - Average of 5 runs per test
61
+ ```bash
62
+ bundle exec ruby scripts/bench_format.rb
63
+ ```
71
64
 
72
- *Built with Rust for optimal performance and memory efficiency.*
65
+ 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.
73
66
 
74
- For detailed performance comparisons and benchmarks, see [Performance Benchmarks](docs/benchmark.md).
67
+ For more detail and a historical comparison against RuboCop, see [Performance Benchmarks](docs/benchmark.md).
75
68
 
76
69
  ## Installation
77
70
 
78
71
  ### Requirements
79
72
 
80
- - Ruby 3.0 or higher
73
+ - Ruby 3.3 or higher
81
74
  - Rust 1.70 or higher (for building from source)
82
75
 
83
76
  ### From RubyGems
@@ -379,18 +372,29 @@ gem install rfmt
379
372
  rfmt-lsp
380
373
  ```
381
374
 
382
- Example Helix configuration:
375
+ Example Neovim configuration (with `nvim-lspconfig`):
383
376
 
384
- ```toml
385
- [language-server.rfmt]
386
- command = "rfmt-lsp"
377
+ ```lua
378
+ local configs = require("lspconfig.configs")
379
+ local lspconfig = require("lspconfig")
380
+
381
+ if not configs.rfmt then
382
+ configs.rfmt = {
383
+ default_config = {
384
+ cmd = { "rfmt-lsp" },
385
+ filetypes = { "ruby" },
386
+ root_dir = lspconfig.util.root_pattern(".rfmt.yml", ".git"),
387
+ single_file_support = true,
388
+ },
389
+ }
390
+ end
387
391
 
388
- [[language]]
389
- name = "ruby"
390
- language-servers = ["rfmt"]
391
- auto-format = true
392
+ lspconfig.rfmt.setup({})
392
393
  ```
393
394
 
395
+ Helix, Emacs, and Zed configurations are covered in the
396
+ [Editor Integration Guide](docs/editors.md).
397
+
394
398
  ### VSCode (Quick Start)
395
399
 
396
400
  1. Install [Ruby LSP extension](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
@@ -0,0 +1,9 @@
1
+ # leading comment
2
+ x = 1 # trailing comment
3
+
4
+ =begin
5
+ embdoc line one
6
+ line two
7
+ =end
8
+ y = 2
9
+ # final comment
@@ -0,0 +1,50 @@
1
+ expected = :ready
2
+
3
+ case payload
4
+ in [Integer => first, *rest]
5
+ first + rest.sum
6
+ in { status: String => status, code: 200 | 201 }
7
+ status
8
+ in ^expected
9
+ :pinned
10
+ else
11
+ :unmatched
12
+ end
13
+
14
+ begin
15
+ risky!
16
+ rescue KeyError => e
17
+ recover(e)
18
+ rescue StandardError
19
+ retry_count += 1
20
+ retry if retry_count < 3
21
+ else
22
+ celebrate
23
+ ensure
24
+ cleanup
25
+ end
26
+
27
+ counters = Hash.new(0)
28
+ counters[:hits] ||= 0
29
+ counters[:hits] += 1
30
+ @cache &&= @cache.compact
31
+ $verbose ||= false
32
+ CONFIG = { a: 3r, b: 3.14r, c: 2i, d: 2ri }
33
+
34
+ class << self
35
+ def singleton_helper = :ok
36
+ end
37
+
38
+ alias aka singleton_helper
39
+ alias $err $stderr
40
+ undef :aka
41
+
42
+ flip = items.select { |l| (l == first)..(l == last) }
43
+
44
+ lines = ->(input) { input.each_line.to_a }
45
+ x, *middle, z = lines.call(<<~TXT)
46
+ alpha
47
+ beta
48
+ gamma
49
+ TXT
50
+ puts x, middle, z
@@ -0,0 +1,12 @@
1
+ =begin
2
+ Block documentation spanning
3
+ multiple lines.
4
+ =end
5
+ value = 42
6
+
7
+ =begin
8
+ Another embedded document right before a method.
9
+ =end
10
+ def answer
11
+ value
12
+ end
@@ -0,0 +1,15 @@
1
+ sql = <<~SQL
2
+ SELECT *
3
+ FROM users
4
+ WHERE active = true
5
+ SQL
6
+
7
+ body = <<-TEXT
8
+ indented terminator keeps its column
9
+ TEXT
10
+
11
+ plain = <<HTML
12
+ <p>no squiggly</p>
13
+ HTML
14
+
15
+ after = sql.length + body.length
@@ -0,0 +1,17 @@
1
+ result = execute(<<~SQL, timeout: 5)
2
+ SELECT id
3
+ FROM events
4
+ SQL
5
+
6
+ logger.info(prefix, <<~MSG)
7
+ first line
8
+ second line
9
+ MSG
10
+
11
+ wrap(inner(<<~ONE), <<~TWO)
12
+ one body
13
+ ONE
14
+ two body
15
+ TWO
16
+
17
+ puts result
@@ -0,0 +1,30 @@
1
+ class Foo::Bar < Baz::Qux
2
+ def change
3
+ x = compute(1)
4
+ @count = x
5
+ end
6
+ end
7
+
8
+ class AddUsers < ActiveRecord::Migration[8.1]
9
+ def change
10
+ create_table :users
11
+ end
12
+ end
13
+
14
+ class Simple < Base
15
+ end
16
+
17
+ class Plain
18
+ end
19
+
20
+ module Alpha::Beta
21
+ end
22
+
23
+ module Gamma
24
+ end
25
+
26
+ class ::TopScoped < ::Deep::Nested::Base
27
+ end
28
+
29
+ module Alpha::Beta::Gamma
30
+ end
@@ -0,0 +1,14 @@
1
+ result = flag ? 1 : 2
2
+
3
+ if flag
4
+ a = 1
5
+ else
6
+ a = 2
7
+ end
8
+
9
+ unless flag
10
+ b = 3
11
+ end
12
+
13
+ c = 4 if flag
14
+ d = 5 unless flag
@@ -0,0 +1,33 @@
1
+ def !@
2
+ true
3
+ end
4
+
5
+ def +@
6
+ self
7
+ end
8
+
9
+ def []=(key, value)
10
+ store(key, value)
11
+ end
12
+
13
+ def self.build(name)
14
+ new(name)
15
+ end
16
+
17
+ obj = Object.new
18
+
19
+ def obj.run
20
+ 1
21
+ end
22
+
23
+ def bare arg
24
+ arg
25
+ end
26
+
27
+ def none
28
+ 0
29
+ end
30
+
31
+ def empty_parens()
32
+ nil
33
+ end
@@ -0,0 +1,14 @@
1
+ greeting = "こんにちは、世界"
2
+ farewell = 'さようなら'; short = "あ"
3
+
4
+ def label(polite: false)
5
+ polite ? "です" : "だ"
6
+ end
7
+
8
+ mixed = "emoji 🎉 と 日本語"
9
+ message = <<~TEXT
10
+ 一行目の本文
11
+ 二行目の本文
12
+ TEXT
13
+
14
+ combined = "#{greeting} #{mixed}".freeze
@@ -0,0 +1,6 @@
1
+ a = 3r
2
+ b = 3.14r
3
+ c = 2i
4
+ d = 2ri
5
+ e = 6.02e2i
6
+ f = a + b * c - d / e
@@ -0,0 +1,31 @@
1
+ class Greeter
2
+ attr_reader :name
3
+
4
+ def initialize(name)
5
+ @name = name
6
+ end
7
+
8
+ def greet(loud: false)
9
+ message = "hello, #{name}"
10
+ if loud
11
+ message.upcase
12
+ else
13
+ message
14
+ end
15
+ end
16
+ end
17
+
18
+ module Registry
19
+ DEFAULT = Greeter.new("world")
20
+
21
+ def self.lookup(key)
22
+ entries.fetch(key) { DEFAULT }
23
+ end
24
+ end
25
+
26
+ items = [1, 2.5, :three, nil, true]
27
+ totals = { "sum" => items.compact.size, count: items.length }
28
+ items.each_with_index do |item, index|
29
+ puts "#{index}: #{item}" unless item.nil?
30
+ end
31
+ result = Registry.lookup(:default)&.greet(loud: totals[:count] > 3)
data/lib/rfmt/3.3/rfmt.so CHANGED
Binary file
data/lib/rfmt/3.4/rfmt.so CHANGED
Binary file
data/lib/rfmt/cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
3
4
  require 'thor'
4
5
 
5
6
  # Check for verbose flag before loading rfmt to set debug mode early
@@ -44,11 +45,15 @@ module Rfmt
44
45
 
45
46
  # Command Line Interface for rfmt
46
47
  class CLI < Thor
48
+ def self.exit_on_failure?
49
+ true
50
+ end
51
+
47
52
  # Constants
48
53
  PROGRESS_THRESHOLD = 20 # Show progress for file counts >= this
49
54
  PROGRESS_INTERVAL = 10 # Update progress every N files
50
55
 
51
- class_option :config, type: :string, desc: 'Path to configuration file'
56
+ class_option :config, type: :string, desc: 'Path to configuration file (formatting options and file selection)'
52
57
  class_option :verbose, type: :boolean, desc: 'Verbose output'
53
58
 
54
59
  default_command :format
@@ -120,11 +125,11 @@ module Rfmt
120
125
  say "Rust extension: #{Rfmt.rust_version}"
121
126
  end
122
127
 
123
- desc 'config', 'Show current configuration'
128
+ desc 'config', 'Show the effective configuration the formatter will use'
124
129
  def config_cmd
125
- config = load_config
126
- require 'json'
127
- say JSON.pretty_generate(config.config)
130
+ say Rfmt.resolved_config(config_path: options[:config])
131
+ rescue Rfmt::Error => e
132
+ raise Thor::Error, e.message
128
133
  end
129
134
 
130
135
  desc 'cache SUBCOMMAND', 'Manage cache'
@@ -183,12 +188,24 @@ module Rfmt
183
188
 
184
189
  def load_config
185
190
  if options[:config]
191
+ validate_explicit_config!(options[:config])
186
192
  Configuration.new(file: options[:config])
187
193
  else
188
194
  Configuration.discover
189
195
  end
190
196
  end
191
197
 
198
+ # Fail fast once instead of repeating the same load error per file
199
+ def validate_explicit_config!(path)
200
+ raise Thor::Error, "Configuration file not found: #{path}" unless File.exist?(path)
201
+
202
+ begin
203
+ Rfmt.resolved_config(config_path: path)
204
+ rescue Rfmt::Error => e
205
+ raise Thor::Error, e.message
206
+ end
207
+ end
208
+
192
209
  def initialize_cache_if_enabled
193
210
  return nil unless options[:cache]
194
211
 
@@ -255,7 +272,7 @@ module Rfmt
255
272
  start_time = Time.now
256
273
  source = File.read(file)
257
274
 
258
- formatted = Rfmt.format(source)
275
+ formatted = Rfmt.format(source, config_path: options[:config])
259
276
  changed = source != formatted
260
277
 
261
278
  {
@@ -325,11 +342,24 @@ module Rfmt
325
342
  end
326
343
 
327
344
  def write_formatted_file(result, cache)
328
- File.write(result[:file], result[:formatted])
345
+ atomic_write(result[:file], result[:formatted])
329
346
  say "✓ Formatted #{result[:file]}", :green unless options[:quiet]
330
347
  cache&.mark_formatted(result[:file])
331
348
  end
332
349
 
350
+ # Temp file must live in the same directory: rename across filesystems is not atomic.
351
+ def atomic_write(path, content)
352
+ # rename would replace a symlink itself; write to its target instead
353
+ path = File.realpath(path) if File.symlink?(path)
354
+ tmp_path = "#{path}.rfmt-#{Process.pid}.tmp"
355
+ mode = File.stat(path).mode
356
+ File.write(tmp_path, content)
357
+ File.chmod(mode, tmp_path)
358
+ File.rename(tmp_path, path)
359
+ ensure
360
+ FileUtils.rm_f(tmp_path)
361
+ end
362
+
333
363
  def display_summary(stats, total_files)
334
364
  @last_stats = stats # Store for verbose details
335
365
  unchanged_count = total_files - stats[:changed] - stats[:errors]
@@ -3,17 +3,13 @@
3
3
  require 'yaml'
4
4
 
5
5
  module Rfmt
6
- # Configuration management for rfmt
6
+ # File selection and cache concerns only: formatter settings (indent_width
7
+ # etc.) live in the Rust Config, reached via Rfmt.format(config_path:).
7
8
  class Configuration
8
9
  class ConfigError < StandardError; end
9
10
 
10
11
  DEFAULT_CONFIG = {
11
12
  'version' => '1.0',
12
- 'formatting' => {
13
- 'line_length' => 100,
14
- 'indent_width' => 2,
15
- 'indent_style' => 'spaces'
16
- },
17
13
  'include' => ['**/*.rb'],
18
14
  'exclude' => ['vendor/**/*', 'tmp/**/*', 'node_modules/**/*']
19
15
  }.freeze
@@ -43,11 +39,6 @@ module Rfmt
43
39
  (included_files - excluded_files).select { |f| File.file?(f) }
44
40
  end
45
41
 
46
- # Get formatting configuration
47
- def formatting_config
48
- @config['formatting']
49
- end
50
-
51
42
  private
52
43
 
53
44
  def load_configuration(options)
@@ -64,18 +55,9 @@ module Rfmt
64
55
  options.delete('file')
65
56
  config = deep_merge(config, options) unless options.empty?
66
57
 
67
- validate_config!(config)
68
58
  config
69
59
  end
70
60
 
71
- def validate_config!(config)
72
- line_length = config.dig('formatting', 'line_length')
73
- raise ConfigError, 'line_length must be positive' if line_length && line_length <= 0
74
-
75
- indent_width = config.dig('formatting', 'indent_width')
76
- raise ConfigError, 'indent_width must be positive' if indent_width && indent_width <= 0
77
- end
78
-
79
61
  def deep_merge(hash1, hash2)
80
62
  hash1.merge(hash2) do |_key, old_val, new_val|
81
63
  if old_val.is_a?(Hash) && new_val.is_a?(Hash)
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.7.0'
4
+ VERSION = '2.0.0.beta1'
5
5
  end
data/lib/rfmt.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative 'rfmt/version'
4
4
  require_relative 'rfmt/native_extension_loader'
5
- require_relative 'rfmt/prism_bridge'
6
5
 
7
6
  # Load native extension with version-aware loader
8
7
  Rfmt::NativeExtensionLoader.load_extension
@@ -14,25 +13,43 @@ module Rfmt
14
13
  # AST validation errors
15
14
  class ValidationError < RfmtError; end
16
15
 
16
+ # Rust reports errors as plain StandardError with a [Rfmt::<kind>] prefix;
17
+ # these two kinds map onto the public exception classes.
18
+ NATIVE_PARSE_ERROR_PREFIX = '[Rfmt::ParseError] '
19
+ NATIVE_VALIDATION_ERROR_PREFIX = '[Rfmt::ValidationError] '
20
+ NATIVE_CONFIG_ERROR_PREFIX = '[Rfmt::ConfigError] '
21
+ private_constant :NATIVE_PARSE_ERROR_PREFIX, :NATIVE_VALIDATION_ERROR_PREFIX,
22
+ :NATIVE_CONFIG_ERROR_PREFIX
23
+
17
24
  # Format Ruby source code
25
+ # Parsing, config resolution, and output validation all happen natively in Rust
18
26
  # @param source [String] Ruby source code to format
27
+ # @param config_path [String, nil] Explicit config file path; nil discovers
28
+ # rfmt.yml/.rfmt.yml from the current directory upward (cached per process)
19
29
  # @return [String] Formatted Ruby code
20
- def self.format(source)
21
- # Step 1: Parse with Prism (Ruby side)
22
- prism_json = PrismBridge.parse(source)
23
-
24
- # Step 2: Format in Rust
25
- # Pass both source and AST to enable source extraction fallback
26
- format_code(source, prism_json)
27
- rescue PrismBridge::ParseError => e
28
- # Re-raise with more context
29
- raise Error, "Failed to parse Ruby code: #{e.message}"
30
- rescue RfmtError
31
- # Rust side errors are re-raised as-is to preserve error details
32
- raise
30
+ def self.format(source, config_path: nil)
31
+ if config_path
32
+ format_code_with_config(source, config_path.to_s)
33
+ else
34
+ format_code(source)
35
+ end
33
36
  rescue StandardError => e
34
- raise Error, "Unexpected error during formatting: #{e.class}: #{e.message}"
37
+ raise wrap_native_error(e)
38
+ end
39
+
40
+ def self.wrap_native_error(error)
41
+ message = error.message
42
+ if message.start_with?(NATIVE_PARSE_ERROR_PREFIX)
43
+ Error.new("Failed to parse Ruby code: #{message.delete_prefix(NATIVE_PARSE_ERROR_PREFIX)}")
44
+ elsif message.start_with?(NATIVE_VALIDATION_ERROR_PREFIX)
45
+ ValidationError.new(message.delete_prefix(NATIVE_VALIDATION_ERROR_PREFIX))
46
+ elsif message.start_with?(NATIVE_CONFIG_ERROR_PREFIX)
47
+ Error.new("Configuration error: #{message.delete_prefix(NATIVE_CONFIG_ERROR_PREFIX)}")
48
+ else
49
+ Error.new("Unexpected error during formatting: #{error.class}: #{message}")
50
+ end
35
51
  end
52
+ private_class_method :wrap_native_error
36
53
 
37
54
  # Format a Ruby file
38
55
  # @param path [String] Path to Ruby file
@@ -44,6 +61,15 @@ module Rfmt
44
61
  raise Error, "File not found: #{path}"
45
62
  end
46
63
 
64
+ # Effective configuration as the Rust formatter resolves it
65
+ # @param config_path [String, nil] Explicit config file path; nil discovers
66
+ # @return [String] YAML dump of the resolved configuration
67
+ def self.resolved_config(config_path: nil)
68
+ resolved_config_yaml(config_path&.to_s)
69
+ rescue StandardError => e
70
+ raise wrap_native_error(e)
71
+ end
72
+
47
73
  # Get version information
48
74
  # @return [String] Version string including Ruby and Rust versions
49
75
  def self.version_info
@@ -54,8 +80,9 @@ module Rfmt
54
80
  # @param source [String] Ruby source code
55
81
  # @return [String] AST representation
56
82
  def self.parse(source)
57
- prism_json = PrismBridge.parse(source)
58
- parse_to_json(prism_json)
83
+ parse_to_json(source)
84
+ rescue StandardError => e
85
+ raise wrap_native_error(e)
59
86
  end
60
87
 
61
88
  # Configuration management
@@ -114,7 +141,8 @@ module Rfmt
114
141
  current_dir = Dir.pwd
115
142
 
116
143
  loop do
117
- ['.rfmt.yml', '.rfmt.yaml', 'rfmt.yml', 'rfmt.yaml'].each do |filename|
144
+ # Same search order as the Rust side (config/mod.rs CONFIG_FILE_NAMES)
145
+ ['rfmt.yml', 'rfmt.yaml', '.rfmt.yml', '.rfmt.yaml'].each do |filename|
118
146
  config_path = File.join(current_dir, filename)
119
147
  return config_path if File.exist?(config_path)
120
148
  end
@@ -132,7 +160,7 @@ module Rfmt
132
160
  nil
133
161
  end
134
162
  if home_dir
135
- ['.rfmt.yml', '.rfmt.yaml', 'rfmt.yml', 'rfmt.yaml'].each do |filename|
163
+ ['rfmt.yml', 'rfmt.yaml', '.rfmt.yml', '.rfmt.yaml'].each do |filename|
136
164
  config_path = File.join(home_dir, filename)
137
165
  return config_path if File.exist?(config_path)
138
166
  end