Skip to content

Latest commit

 

History

History
151 lines (122 loc) · 7.46 KB

File metadata and controls

151 lines (122 loc) · 7.46 KB

Contributing to mruby-lsp

Thanks for helping out. This is a standalone LSP server for mruby whose source of truth is the live, compiled mruby VM — not RBS, not CRuby conventions, not static file indexing. Keep that premise in mind; it drives most design decisions.

Layout

  • lib/mruby_lsp/ — the server (Ruby): LSP transport, the VM-reflection index, and every feature (completion, hover, definition, …). The buffer overlay (buffer_harvester.rb) parses open/unsaved files with Prism so edits count before a rebuild.
  • ext/mruby_reflect/ + vendor/value_bridge/ — the C bridge that reflects a built libmruby into the host Ruby process. It is a dumb FFI conduit: all logic lives in Ruby; the VM decides, C never dispatches.
  • editors/vscode/ — the VS Code / VSCodium extension (language client + the mruby debug adapter over mrdb).
  • test/overlay/ — fast, prism-only unit tests (no VM needed). test/conformance/ — replays ruby-lsp's own expectation vectors against our server; test/parity/ — drives the real ruby-lsp as an oracle.

Build & run from a checkout

# run the server (host Ruby) from the checkout, via the CLI dispatcher:
ruby -Ilib -r mruby_lsp/cli -e 'MrubyLsp::CLI.run(ARGV.shift, ARGV)' -- server /path/to/project
cc -O2 -o /tmp/mruby-lsp ext/mruby_lsp_launcher/launcher.c   # build the sandbox launcher
cd editors/vscode && npm install && npm run compile     # build the extension

Full editor install and per-project setup are in the README (rake vscode:install / rake install + mruby-lsp-setup).

Build layout and versioning

Everything transient builds under build/ — nothing lands in the source tree:

  • build/gems/ — the two gems we author (mruby-lsp, value_bridge), built once by rake gem:build at the current version.
  • build/stage/ — the .vsix assembly area (staged extension source + the fetched external-gem closure).
  • build/mruby-lsp-<v>.vsix — the packaged extension.

rake clobber removes build/ (and mruby/build/) entirely — the single cleanup command.

Versioning is deliberate. lib/mruby_lsp/version.rb is the single, hand-set SemVer source of truth. No build, install, or package task bumps it; a same-version rake install reinstalls cleanly (gem install --force) so you can iterate without bumping. To cut a release, run an explicit bump (each writes version.rb + the extension package.json and pins value_bridge in lockstep):

rake bump:patch   # 0.1.120 -> 0.1.121
rake bump:minor   # 0.1.120 -> 0.2.0
rake bump:major   # 0.1.120 -> 1.0.0

External runtime deps (prism, rbs, language_server-protocol, …) are not kept in the repo: rake vscode:package fetches the full source-gem closure into the .vsix stage at package time (needs network), so the shipped .vsix is self-contained but the repo stays source-only.

Packaging the extension (rake vscode:package / vscode:install) stages the sources with rsync when present (it ships by default on most Linux/macOS but is NOT in the FreeBSD base system — pkg install rsync there); without rsync it falls back to an equivalent in-Rakefile copy that preserves mtimes.

Tests

Setup first — the suite does not run on a bare Ruby. The overlay tests need the runtime gems mruby-lsp itself uses (current prism, rbs, …), which a distro/default Ruby is usually missing or ships too old (a default prism predates node classes we depend on and fails with uninitialized constant). Install them the same way a user would, and put the gem bindir on PATH:

rake install                                   # builds + installs the gems (fetches deps)
# rake install is a --user-install: binstubs land in Gem.user_dir/bin
# (Gem.bindir on setups where user and system dirs coincide) — put both on PATH:
export PATH="$(ruby -e 'print File.join(Gem.user_dir, "bin")'):$(ruby -e 'print Gem.bindir'):$PATH"

test/overlay/mruby_semantics_test.rb additionally needs mruby head built somewhere (git clone https://github.com/mruby/mruby && cd mruby && rake); point the test at the binary with MRUBY=/path/to/mruby/build/host/bin/mruby (default: /tmp/mruby/build/host/bin/mruby).

cd test/overlay && for t in *_test.rb; do ruby "$t"; done   # unit (prism-only)
cd editors/vscode && npm test                    # extension, in a real VS Code host
cd editors/vscode && npm run test:debug-adapter  # DAP session logic (plain node)

npm test uses VS Code's official extension-test runner (@vscode/test-cli, config in .vscode-test.mjs): it downloads a VS Code build into .vscode-test/ on first run and drives the compiled extension in a live extension host against the fixture workspaces under test/fixtures/ — nothing about the vscode API is mocked. On a machine without a display, run it under xvfb: xvfb-run -a npm test.

The conformance + parity suites (test/conformance/README.md, test/parity/README.md) need a built reflection VM and, for parity, ruby-lsp built from source; both READMEs document the procedure.

CI

One workflow (.github/workflows/ci.yml), and it runs the WHOLE suite against the real server on every pull request and every push to main (plus manual dispatch) — there is no reduced/fast variant:

  • server job: installs the gems from the checkout (rake install), builds mruby HEAD plus the reflection VM via mruby-lsp-setup, then runs all of test/overlay (including the live-VM pin mruby_semantics_test.rb), the conformance replays over real LSP stdio (each script exits non-zero on any FAIL — see the scorecard in test/conformance/README.md), and the test/consistency suite driven by a real LSP client (headless Neovim + clangd).
  • editor job: the extension suite in a real VS Code extension host (xvfb-run -a npm test) and the debug-adapter protocol tests.

An upstream mruby behavior change, a conformance regression, or an endpoint-consistency drift turns the run red.

test/consistency/ asserts that the SAME question answered through different endpoints (completion / hover / signatureHelp / definition / references / documentHighlight / rename) yields the SAME logical fact — formatting differs by design, the facts must not. It is driven by a REAL LSP client (Neovim's built-in vim.lsp) against the real server, so it needs a built reflection VM + clangd; see test/consistency/README.md.

House rules (non-negotiable)

  • Verify by running. Drive the server over real LSP stdio; static inspection doesn't count as done.
  • No mrb_load_string / eval in any mruby context. Reading the already-loaded image is fine; executing buffer code is the security line.
  • No regex on structured input (Ruby, JSON, HTML, compiler command lines, structured machine output). Ruby → Prism; C locations → addr2line/nm; reflection → consumed structurally. Keep regex out of hot paths.
  • C is a dumb bridge, and it contains nothing fuzzable — all parsing and logic is host Ruby.
  • Degrade, don't crash. Optional subsystems (e.g. clangd) are BYO and may be absent or die mid-session — switch the feature off and keep the server alive.

For the working conventions (commit author/trailers, patch delivery) see AGENTS.md; for hard-won pitfalls before touching native/build code read docs/GOTCHAS.md; the ruby-lsp conformance contract is in docs/CONFORMANCE.md. Project state, open work, and the roadmap live in the GitHub issue tracker.