rfmt 1.6.3-aarch64-linux → 2.0.0.beta1-aarch64-linux
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 +38 -0
- data/README.md +53 -21
- data/exe/rfmt-lsp +8 -0
- data/exe/rfmt_fast +12 -0
- data/ext/rfmt/tests/fixtures/parity/comments_mixed.rb +9 -0
- data/ext/rfmt/tests/fixtures/parity/constructs.rb +50 -0
- data/ext/rfmt/tests/fixtures/parity/embdoc.rb +12 -0
- data/ext/rfmt/tests/fixtures/parity/heredoc_assign.rb +15 -0
- data/ext/rfmt/tests/fixtures/parity/heredoc_call_args.rb +17 -0
- data/ext/rfmt/tests/fixtures/parity/metadata_classes.rb +30 -0
- data/ext/rfmt/tests/fixtures/parity/metadata_conditionals.rb +14 -0
- data/ext/rfmt/tests/fixtures/parity/metadata_defs.rb +33 -0
- data/ext/rfmt/tests/fixtures/parity/multibyte.rb +14 -0
- data/ext/rfmt/tests/fixtures/parity/numeric.rb +6 -0
- data/ext/rfmt/tests/fixtures/parity/plain.rb +31 -0
- data/lib/rfmt/3.3/rfmt.so +0 -0
- data/lib/rfmt/3.4/rfmt.so +0 -0
- data/lib/rfmt/cli.rb +37 -7
- data/lib/rfmt/configuration.rb +2 -20
- data/lib/rfmt/lsp/document_store.rb +27 -0
- data/lib/rfmt/lsp/formatter.rb +37 -0
- data/lib/rfmt/lsp/message_io.rb +55 -0
- data/lib/rfmt/lsp/server.rb +168 -0
- data/lib/rfmt/lsp/uri.rb +35 -0
- data/lib/rfmt/lsp/workspace.rb +62 -0
- data/lib/rfmt/lsp.rb +8 -0
- data/lib/rfmt/version.rb +1 -1
- data/lib/rfmt.rb +47 -19
- metadata +51 -4
- data/lib/rfmt/prism_bridge.rb +0 -529
- data/lib/rfmt/prism_node_extractor.rb +0 -115
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f0c6681074bc422a604388e60cc5dee756651e2a645363f8226b81f5dd96992b
|
|
4
|
+
data.tar.gz: 0bddc889fcf62ba16e2868e4ea47254430954aea4755267e83e38e515d321c93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 87fe10e37abd3c2b7f9c63ec96899201aa7cb3c320baf427a7e9501b0dbe8541761e43438a7e5cc4b9c97e1137d6b052ceb3017602dad3891cd4ce560ba36d61
|
|
7
|
+
data.tar.gz: fdf482ae1350e6aa4ad44c70e68c9246eaf8a22e35a8f39bfb821a4f5ebc11756b8245c4af69b86b54a950ee88dcb97b4fadb89282f19e817624eac4803c53f8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
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
|
+
|
|
28
|
+
## [1.7.0] - 2026-06-04
|
|
29
|
+
|
|
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.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Standalone LSP server `rfmt-lsp` (#108). Provides format-on-save in any LSP-capable editor without requiring Ruby LSP or a Gemfile:
|
|
35
|
+
- `textDocument/formatting` with full document sync; unsaved buffer contents are formatted via `didOpen`/`didChange` tracking
|
|
36
|
+
- `.rfmt.yml` is resolved relative to the workspace root (`rootUri`/`workspaceFolders`)
|
|
37
|
+
- Graceful handling of syntax errors (returns no edits instead of crashing), empty files, and unsupported LSP methods
|
|
38
|
+
- New `exe/rfmt-lsp` executable shipped with the gem
|
|
39
|
+
- Editor setup guides for the standalone server (Neovim, Helix, Emacs eglot) in `docs/editors.md`
|
|
40
|
+
|
|
3
41
|
## [1.6.3] - 2026-04-24
|
|
4
42
|
|
|
5
43
|
Minor stability refinements on top of the 1.6.x architecture release. See the 1.6.1 notes below for the feature set this series delivers.
|
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**:
|
|
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
|
-
|
|
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
|
-
|
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
|
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
|
-
|
|
59
|
+
Measured with `scripts/bench_format.rb` over rfmt's own `lib/` corpus on arm64 macOS, Ruby 3.4. Reproduce with:
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
73
|
+
- Ruby 3.3 or higher
|
|
81
74
|
- Rust 1.70 or higher (for building from source)
|
|
82
75
|
|
|
83
76
|
### From RubyGems
|
|
@@ -361,7 +354,46 @@ end
|
|
|
361
354
|
|
|
362
355
|
## Editor Integration
|
|
363
356
|
|
|
364
|
-
rfmt
|
|
357
|
+
rfmt can integrate with editors in two ways:
|
|
358
|
+
|
|
359
|
+
- Standalone LSP: run `rfmt-lsp` directly from your editor. This works well for single
|
|
360
|
+
Ruby scripts or projects without a Gemfile.
|
|
361
|
+
- Ruby LSP add-on: use rfmt as the formatter inside
|
|
362
|
+
[Ruby LSP](https://shopify.github.io/ruby-lsp/).
|
|
363
|
+
|
|
364
|
+
For detailed setup instructions, see [Editor Integration Guide](docs/editors.md).
|
|
365
|
+
|
|
366
|
+
### Standalone LSP
|
|
367
|
+
|
|
368
|
+
After installing rfmt, configure your editor's Ruby language server command to `rfmt-lsp`.
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
gem install rfmt
|
|
372
|
+
rfmt-lsp
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Example Neovim configuration (with `nvim-lspconfig`):
|
|
376
|
+
|
|
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
|
|
391
|
+
|
|
392
|
+
lspconfig.rfmt.setup({})
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Helix, Emacs, and Zed configurations are covered in the
|
|
396
|
+
[Editor Integration Guide](docs/editors.md).
|
|
365
397
|
|
|
366
398
|
### VSCode (Quick Start)
|
|
367
399
|
|
data/exe/rfmt-lsp
ADDED
data/exe/rfmt_fast
ADDED
|
@@ -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,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,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,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
|
|
128
|
+
desc 'config', 'Show the effective configuration the formatter will use'
|
|
124
129
|
def config_cmd
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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]
|
data/lib/rfmt/configuration.rb
CHANGED
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
require 'yaml'
|
|
4
4
|
|
|
5
5
|
module Rfmt
|
|
6
|
-
#
|
|
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)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rfmt
|
|
4
|
+
module LSP
|
|
5
|
+
class DocumentStore
|
|
6
|
+
def initialize
|
|
7
|
+
@documents = {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def open(uri, text)
|
|
11
|
+
@documents[uri] = text
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def change(uri, text)
|
|
15
|
+
@documents[uri] = text
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def close(uri)
|
|
19
|
+
@documents.delete(uri)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def source_for(uri)
|
|
23
|
+
@documents[uri]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rfmt'
|
|
4
|
+
|
|
5
|
+
module Rfmt
|
|
6
|
+
module LSP
|
|
7
|
+
class Formatter
|
|
8
|
+
def self.format_edits(source)
|
|
9
|
+
formatted = source.empty? ? "\n" : Rfmt.format(source)
|
|
10
|
+
return [] if formatted == source
|
|
11
|
+
|
|
12
|
+
[
|
|
13
|
+
{
|
|
14
|
+
range: full_document_range(source),
|
|
15
|
+
newText: formatted
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
rescue Rfmt::Error
|
|
19
|
+
[]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.full_document_range(source)
|
|
23
|
+
return { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } } if source.empty?
|
|
24
|
+
|
|
25
|
+
lines = source.split("\n", -1)
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
start: { line: 0, character: 0 },
|
|
29
|
+
end: {
|
|
30
|
+
line: lines.length - 1,
|
|
31
|
+
character: lines.last.length
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|