Skip to content

Let Addons control what seeds the index #4132

@Asmod4n

Description

@Asmod4n

I have checked that this feature is not already implemented

  • This feature does not exist

Use case

Add-ons can currently contribute to Ruby LSP's features through the create_*_listener hooks, but they cannot influence what the index itself is seeded with. The index source is fixed: CRuby core via RBS, plus the host Ruby's default gems and the locked bundle.
That's exactly right for CRuby projects — but it makes a whole category of add-on impossible: language servers for Ruby runtimes whose API surface isn't CRuby's. mruby is the concrete case (each project compiles its own runtime, so the ground truth for what exists lives in the built binary), and the same applies to Artichoke, Opal, or any embedded build.
Today, an add-on that feeds the index from such a runtime still gets the CRuby seeding mixed in, producing actively wrong answers. Real example from an mruby project: JSON::ParserError resolves to CRuby's /usr/lib/ruby/3.4.0/json/common.rb — in a VM that has no JSON at all. Completions offer CRuby String methods the target runtime doesn't have.
Giving add-ons a say in index seeding would let the entire rest of Ruby LSP — wire protocol, document lifecycle, Prism listeners, completion/hover/definition — work unmodified for these runtimes. The add-on model is almost powerful enough for this today; it's one feature short.

Description

An official way for an add-on to opt out of the default index seeding and supply its own entries as the source of truth.
Currently two things are hardcoded with no public switch:

RubyIndexer::Index#index_all unconditionally runs RBSIndexer.new(self).index_ruby_core.
RubyIndexer::Configuration#indexable_uris unconditionally adds the host Ruby's default gems (from RbConfig::CONFIG["rubylibdir"]) and the locked bundle gems. excluded_gems/excluded_patterns can trim, but can't express "workspace files only — the runtime API comes from my add-on."

The feature: make those two seedings opt-out-able, so an add-on can populate the index itself in activate (it already can add entries — what's missing is preventing the defaults from being mixed in). Everything downstream — all request listeners — runs on whatever the index contains and needs no changes.
Without this, the only routes are monkey-patching internals from activate (fragile, breaks across releases, and clearly outside the intended model) or forking the server.

Implementation

An add-on API for index seeding: when an add-on declares it provides the index source (e.g. implements a hook like provide_index_source(index, global_state)), Ruby LSP skips its default seeding — RBSIndexer#index_ruby_core and the default-gem/bundle-gem globbing in indexable_uris — and the add-on populates the index in its place. Workspace file indexing and all request listeners stay exactly as they are; they just operate on whatever the index contains.
No add-on implementing the hook → nothing changes for anyone.
For context: I have a working proof of concept — an add-on that populates the index from a live mruby VM via an MRI C-extension reflecting the built runtime; completion/hover/definition all work on real mruby data. The reflection side is done; the only blocker is that the default CRuby/gem seeding can't be suppressed through the add-on API. (Read against ruby-lsp 0.26.9.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions