codemirror-lang-ruby
v0.4.4
Published
Ruby language support for CodeMirror 6, built on a Lezer grammar
Maintainers
Readme
codemirror-lang-ruby
Ruby language support for CodeMirror 6, built on a Lezer grammar.
Targets Ruby 3.0+ syntax (including endless methods and basic pattern matching).
Install
npm install codemirror-lang-rubyUsage
import {EditorView, basicSetup} from "codemirror"
import {ruby} from "codemirror-lang-ruby"
new EditorView({
doc: 'puts "hello"',
extensions: [basicSetup, ruby()],
parent: document.getElementById("editor")!,
})Real-world parse accuracy
Tested against popular open source Ruby projects (large, representative files):
| Project | File | Lines | Accuracy | |---------|------|-------|----------| | Faker | internet.rb | 579 | 98.4% | | Devise | devise.rb | 534 | 98.3% | | Jekyll | site.rb | 577 | 97.6% | | Fastlane | runner.rb | 379 | 96.3% | | Rails | query_methods.rb | 2291 | 96.0% | | Grape | api.rb | 166 | 95.8% | | Sidekiq | config.rb | 321 | 95.6% |
What's supported
- Definitions: methods (with params, endless
def f(x) = expr), classes (with inheritance), modules - Control flow: if/elsif/else, unless, while, until, for/in, case/when, case/in (pattern matching with pin operators, hash patterns, find patterns)
- Error handling: begin/rescue/ensure/raise, rescue with scoped constants (
rescue Foo::Bar => e) - Strings: single-quoted, double-quoted with
#{interpolation}, heredocs (<<~DELIM),%-literals with any delimiter - Literals: integers, floats, symbols, character literals (
?a), arrays, hashes, regex (/pattern/flags), nil, true, false - Expressions: assignment (including
||=,&&=), multiple assignment (a, b = 1, 2), method calls (with receiver and keyword args likeUser.where(active: true), splat*args/**kwargs/&block), chained calls, binary/unary/ternary operators, lambdas, ranges, conditional modifiers - Blocks: brace blocks and do/end blocks attached to method calls (
items.each { |x| x }) - Operators: proper precedence (
**>*//>+/-> comparison > logic), safe navigation (&.), scope resolution (::including leading::TopLevel) - Bare method calls:
puts "hello",require "json",attr_reader :name,include Comparable,validates_presence_of :name,rescue_from,helper_method(49 common Ruby/Rails methods) - Variables: local,
@instance,@@class,$global,Constants - Comments: line
#and block=begin/=end - Editor features: smart indentation, code folding, bracket closing, keyword autocompletion (31 keywords)
Known limitations
- Heredoc body highlighting with trailing code —
foo(<<~SQL)and<<~HEREDOC.stripparse correctly (no error nodes), but the heredoc body is not highlighted as a string in these cases. Simple heredocs (x = <<~SQL) highlight the full body as a string. This is a Lezer architectural limitation: inline tokenizers always run before external tokenizers, preventing the body tokenizer from claiming the content. - Guard clauses in pattern matching —
in x if x > 0is not supported. Theif/unlesskeyword conflicts withIfStatement/ConditionalModifierin the LR parser and cannot be resolved without an external tokenizer. - Heredoc and
%-literal bodies are opaque tokens (no interpolation highlighting inside). - Inline rescue as a standalone expression —
value = foo rescue nilworks (rescue in assignments), butfoo rescue baras a standalone expression outside of assignment context is not supported. - Newline as statement separator — Ruby uses newlines to separate statements, but the grammar is whitespace-insensitive. An expression followed by
ifon the next line may be parsed as a conditional modifier (e.g.,x = 1\nif condparses asx = (1 if cond)). - Setter assignment with conditional modifier —
self.x = 1 if conditionis not fully supported. - Bare method calls only work for a curated list of 49 common Ruby/Rails methods. Other methods need parentheses (e.g.,
assert_includes(errors, "msg")instead ofassert_includes errors, "msg"). - Line-based indentation — The indent engine uses regex line scanning rather than tree-based analysis. Most patterns work well, but complex multi-line expressions (e.g., multi-line method args followed by a body, trailing commas inside nested delimiters) may not indent perfectly.
Development
npm install # Install dependencies
npm run build # Build grammar + bundle to dist/
npm test # Run 105 grammar tests (721 total across all suites)
npm run lint # TypeScript type check
npm run demo:build # Build the demo pageBuilt with Claude Code
This entire project — grammar, external tokenizers, tests, editor integration, and demo — was written by Claude Code, guided by @jeanpaulsio.
License
MIT
