An open API service indexing awesome lists of open source software.

https://github.com/jeanpaulsio/codemirror-lang-ruby

Ruby language support for CodeMirror 6, built on a Lezer grammar
https://github.com/jeanpaulsio/codemirror-lang-ruby

code-editor codemirror codemirror-6 editor lezer lezer-grammar ruby syntax-highlighting

Last synced: 2 months ago
JSON representation

Ruby language support for CodeMirror 6, built on a Lezer grammar

Awesome Lists containing this project

README

          

# codemirror-lang-ruby

Ruby language support for [CodeMirror 6](https://codemirror.net/), built on a [Lezer](https://lezer.codemirror.net/) grammar.

Targets **Ruby 3.0+** syntax (including endless methods and basic pattern matching).

[**Live Demo**](https://jeanpaulsio.github.io/codemirror-lang-ruby/)

## Install

```bash
npm install codemirror-lang-ruby
```

## Usage

```typescript
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](https://github.com/faker-ruby/faker) | internet.rb | 579 | **98.4%** |
| [Devise](https://github.com/heartcombo/devise) | devise.rb | 534 | **98.3%** |
| [Jekyll](https://github.com/jekyll/jekyll) | site.rb | 577 | **97.6%** |
| [Fastlane](https://github.com/fastlane/fastlane) | runner.rb | 379 | **96.3%** |
| [Rails](https://github.com/rails/rails) | query_methods.rb | 2291 | **96.0%** |
| [Grape](https://github.com/ruby-grape/grape) | api.rb | 166 | **95.8%** |
| [Sidekiq](https://github.com/sidekiq/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 code** — `foo(<<~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 matching** — `in 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 expression** — `value = 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 modifier** — `self.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

```bash
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](https://claude.ai/code), guided by [@jeanpaulsio](https://github.com/jeanpaulsio).

## License

MIT