Skip to content

Latest commit

Β 

History

121 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

refract

A fast Ruby LSP server backed by SQLite. Single static binary, no Ruby runtime required. Powered by Prism 1.9.0.

   editor (VS Code, Neovim, Helix, Zed, Emacs, Sublime)
              β”‚  LSP over stdio
              β–Ό
   refract  ── parses Ruby with Prism ── persists to per-project SQLite ── serves LSP queries
              β”‚
              └─ also: refract --mcp  (30 tools for AI agents)

Why pick refract

  • Cold install in seconds β€” brew install, no gem install, no bundler, no YARD doc generation
  • Rails 5.2–8.0 DSL coverage β€” has_many, has_one_attached, delegated_type, composed_of, attribute :x, :integer, etc.
  • Type-aware β€” RBS + Sorbet sigs + literal narrowing power completion, hover, inlay hints
  • Light type checking β€” refract/nil-receiver and refract/wrong-arity ship in v0.1, confidence-gated
  • AI-native β€” refract --mcp exposes 30 tools your agent can call directly
  • Single binary β€” drop into Docker, CI, anywhere. No Ruby on the path.

See docs/BENCHMARK.md for the head-to-head numbers vs Solargraph, Ruby LSP, Sorbet, and Steep β€” perf, accuracy, DX, RAM β€” plus when to pick each.

Install

Platform support: Linux (x86_64 / aarch64, glibc and musl) and macOS (x86_64 / aarch64). Windows is not supported in 0.1.0. Ruby: 2.7 – 3.4 syntax (via Prism 1.9.0).

macOS (Homebrew):

brew install hrtsx/refract/refract

Linux / manual:

OS=$(uname -s); ARCH=$(uname -m)
case "$OS" in Darwin) OS=macos ;; *) OS=linux ;; esac
case "$ARCH" in arm64) ARCH=aarch64 ;; esac
curl -L "https://github.com/hrtsx/refract/releases/latest/download/refract-${ARCH}-${OS}" \
  -o ~/.local/bin/refract && chmod +x ~/.local/bin/refract

30-second quick start

# 1. Install (linux/macos)
brew install hrtsx/refract/refract              # or: curl from Releases (see above)

# 2. Verify
refract --version
refract --doctor                                 # paste-able diagnostic report

# 3. Index a Rails app (first run)
cd ~/code/my-rails-app
refract --check                                  # bootstraps the index, exits 0

# 4. Open your editor β€” it's already wired (see Editor Setup below)

Need to file an issue? refract --doctor | gh issue create --body-file - produces a clean report.

Editor Setup

VS Code

Install the extension from the Marketplace, or from source:

cd editors/vscode && npm install && npm run build

See editors/vscode/README.md for commands, settings, and troubleshooting.

Neovim

Via refract.vim (handles binary download automatically):

-- lazy.nvim
{
  "hrtsx/refract.vim",
  build = function() require("refract").install() end,
  ft = { "ruby", "eruby", "haml" },
  opts = {},
}

Or manually if the binary is already in PATH:

vim.api.nvim_create_autocmd("FileType", {
  pattern = { "ruby", "eruby", "haml" },
  callback = function()
    vim.lsp.start({
      name = "refract",
      cmd = { "refract" },
      root_dir = vim.fs.root(0, { "Gemfile", ".git" }),
      init_options = { logLevel = 2 },
    })
  end,
})

Helix

[language-server.refract]
command = "refract"

[[language]]
name = "ruby"
language-servers = ["refract"]

Zed

Install the extension from the Zed extension registry (search "refract"), or via zed-refract. For a manual no-extension setup, add to .zed/settings.json:

{
  "lsp": {
    "refract": { "binary": { "path": "refract" } }
  },
  "languages": {
    "Ruby": { "language_servers": ["refract"] }
  }
}

Emacs

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs '(ruby-mode . ("refract"))))

Sublime Text

Preferences > Package Settings > LSP > Settings:

{
  "clients": {
    "refract": {
      "enabled": true,
      "command": ["refract"],
      "selector": "source.ruby | text.html.ruby"
    }
  }
}

See docs/lsp_capabilities.md for the full per-editor capability matrix and initializationOptions examples.

Configuration

All options are passed via initializationOptions and hot-reload via workspace/didChangeConfiguration.

Option Type Default Description
disableGemIndex bool false Skip indexing gems in Gemfile
disableRubocop bool false Disable RuboCop entirely
maxFileSizeMb number 8 Max file size to index (MB)
rubocopTimeoutSecs number 30 RuboCop subprocess timeout
rubocopDebounceMs number 1500 Delay after save before running RuboCop
bundleExecTimeoutSecs number 15 bundle exec gem-scan timeout
maxWorkers number cpu count (max 8) Parallel indexing threads
extraExcludeDirs string[] [] Extra directories to skip during scan
logLevel number 2 1=error 2=warn 3=info 4=debug
typeCheckerConfidence object {"surface": 80} Minimum confidence (0–100) to surface a resolved type in completion/hover/inlay/navigation. Lower = more inferred guesses surfaced

CLI

refract                        # start LSP server (stdin/stdout)
refract --mcp                  # start MCP server
refract --version              # print version and exit
refract --help                 # print usage
refract --doctor               # paste-able diagnostic report (version, db state, last crash)
refract --last-crash           # print most recent crash log and exit
refract --log-file F           # write logs to file
refract --log-level N          # set verbosity (1–4)
refract --verbose              # shorthand for --log-level 3
refract --db-path PATH         # override database path
refract --print-db-path        # print database path and exit
refract --reset-db             # delete database and exit
refract --check                # verify database integrity (0=ok, 1=fail)
refract --repair               # repair recoverable index corruption
refract --stats                # print file and symbol counts
refract --workspace-info       # print workspace + symbol-kind breakdown

--stdio is accepted and ignored (pass-through for editors that always add it).

Diagnostics

Code What it catches Severity (default)
refract/nil-receiver Method call on a NilClass receiver (would raise NoMethodError) warning
refract/wrong-arity Call passes more positional args than the method accepts warning
RuboCop offenses Anything RuboCop flags (when available) per RuboCop config

To suppress a diagnostic for the workspace, append it to .refract/disabled.txt β€” or use the "Disable refract/X for this workspace" code action. Severity overrides via refract.typeCheckerSeverity (error / warning / info).

Features

Feature Status
textDocument/completion β€” type-aware, route helpers, i18n keys βœ“
textDocument/hover β€” types, YARD docs, constant values, Rails DSL βœ“
textDocument/definition, declaration, implementation, typeDefinition βœ“
textDocument/references βœ“
textDocument/rename β€” MRO-aware, cross-file βœ“
textDocument/prepareRename βœ“
textDocument/signatureHelp βœ“
textDocument/documentSymbol, workspace/symbol βœ“
textDocument/inlayHint β€” return types, block params βœ“
textDocument/semanticTokens (full, delta, range) βœ“
textDocument/documentHighlight βœ“
textDocument/documentLink β€” clickable requires βœ“
textDocument/formatting, rangeFormatting β€” via RuboCop βœ“
textDocument/codeAction β€” refactoring + RuboCop quickfix βœ“
textDocument/diagnostic (pull) + publishDiagnostics βœ“
textDocument/codeLens β€” reference counts βœ“
textDocument/foldingRange, selectionRange, linkedEditingRange βœ“
callHierarchy/incomingCalls + outgoingCalls βœ“
typeHierarchy/subtypes + supertypes βœ“
HAML / ERB template support βœ“
Background workspace indexing with live progress βœ“

MCP Server

Start with refract --mcp. Exposes 30 tools for AI agent integration.

Highest-value tools for Rails projects:

Tool What it gives Latency
workspace_symbols Ranked fuzzy symbol search (FTS5 trigram) ~2 ms
class_summary Full method roster with visibility and types ~3 ms
association_graph has_many/has_one/belongs_to with typed returns ~1 ms
explain_symbol Signature + callers + diagnostics in one call ~2 ms
find_references Call-site hits with surrounding context ~8 ms
workspace_health File counts, type coverage, schema version ~15 ms

See docs/mcp_tools.md for the full tool reference.

RuboCop

Auto-detected at startup: with a Gemfile.lock, refract probes bundle exec rubocop, then bare rubocop in PATH (project-local version wins). Re-run detection with the Refract: Re-check RuboCop command; disableRubocop: true turns it off.

Gem Indexing

Indexes installed gems from Gemfile.lock; rbs_collection.lock.yaml for RBS type discovery. Toggle at runtime with refract.toggleGemIndex.

Database

One SQLite database per workspace at ~/.local/share/refract/<hash>.db.

refract --reset-db   # delete and rebuild on next start
refract --check      # verify integrity

Schema mismatch after a downgrade triggers an automatic reset β€” this is expected. Upgrade with the install command above.

Build from source

Requires Zig 0.16.0+.

git clone --recurse-submodules https://github.com/hrtsx/refract
cd refract
zig build --release=safe
# binary at zig-out/bin/refract

How it works

On initialized, refract scans .rb, .rbs, .rbi, .erb, .rake, .gemspec, .ru plus Gemfile/Rakefile, and indexes classes, modules, methods, constants, routes, associations, and i18n keys into a per-project SQLite database. All LSP requests query that database; saves update it incrementally via workspace/didChangeWatchedFiles. Works with Rails, plain gems, Rack apps, scripts, and monorepos.

Reproduce benchmarks

Run ./scripts/bench/repro.sh to clone corpora, build refract in release mode, and run the full benchmark matrix. Results land under bench-results/realistic/<timestamp>/.

About

Ruby LSP and a bit more

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages