Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

jeanpaulsio/codemirror-lang-ruby

Open more actions menu

Repository files navigation

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).

Live Demo

Install

npm install codemirror-lang-ruby

Usage

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 like User.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 codefoo(<<~SQL) and <<~HEREDOC.strip parse 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 matchingin x if x > 0 is not supported. The if/unless keyword conflicts with IfStatement/ConditionalModifier in 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 expressionvalue = foo rescue nil works (rescue in assignments), but foo rescue bar as 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 if on the next line may be parsed as a conditional modifier (e.g., x = 1\nif cond parses as x = (1 if cond)).
  • Setter assignment with conditional modifierself.x = 1 if condition is 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 of assert_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 page

Built with Claude Code

This entire project — grammar, external tokenizers, tests, editor integration, and demo — was written by Claude Code, guided by @jeanpaulsio.

License

MIT

Morty Proxy This is a proxified and sanitized view of the page, visit original site.