Proposal: public hook for external documentation providers in show_doc and the completion doc dialog
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 478
- Forks
- 158
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 12
Description
Summary
show_doc and the Alt+d documentation dialog shown by autocompletion are hard-wired to RDoc::RI::Driver. This proposes a small public extension point, a list of "document providers", so that other documentation backends (RI-compatible or not) can plug into the same UI without monkey-patching irb internals.
Motivation
irb currently has exactly one way to influence where documentation comes from: IRB.conf[:EXTRA_DOC_DIRS], which only adds more RI data directories. There is no way to serve documentation from a different format or source (a different language's manual, YARD-generated docs, RBS-embedded comments, a project-local doc store, etc.) through show_doc or the completion dialog.
Rurema (the Japanese Ruby reference manual) ships bitclust-irb, added in rurema/bitclust#326 (merged 2026-08-20, released in bitclust 1.7.0). Because there was no hook into show_doc, it had to register an entirely separate refe command through the public IRB::Command.register API instead of extending show_doc itself. A follow-up PR (rurema/bitclust#332) adds a fallback that searches docs.ruby-lang.org (its search index plus the Markdown pages) when no local database is present. Users have to remember two commands (show_doc for RI, refe for the Japanese manual) and only one of them gets the Alt+d dialog treatment.
A provider hook would let show_doc NAME and the completion dialog consult multiple backends in order, with RI as the default, so bitclust-irb (and similarly YARD's yri, or other translated manuals) could integrate directly instead of bolting on a parallel command.
Current implementation
lib/irb/command/show_doc.rb (ShowDoc#execute) always does:
require 'rdoc/ri/driver'
opts = RDoc::RI::Driver.process_args([])
ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
...
Ri.display_name(name) # or Ri.interactive when name is nil
and warns "Can't display document because rdoc is not installed." when rdoc can't be required.
lib/irb/input-method.rb (RelineInputMethod) independently drives the same backend for the Alt+d dialog:
rdoc_ri_driverbuilds anRDoc::RI::Driver.new(options), honoringIRB.conf[:EXTRA_DOC_DIRS].retrieve_rdoc_document(name)callsdriver.expand_name(name)thendriver.add_method/driver.class_documentto build anRDoc::Markup::Document.rdoc_dialog_contents(name, width)renders that document withRDoc::Markup::ToAnsifor the popup.display_document(matched)handles the Alt+d full-screen view:CommandDocumenttargets go throughIRB::Command.load_command,MethodDocumenttargets go through the RI driver'sdisplay_names(oradd_method+displaywhen there are several candidate names, e.g. ambiguous receivers like{}.any?).- The whole dialog proc is only installed when
require 'rdoc'succeeds (startinRelineInputMethod).
lib/irb/completion.rb supplies the names passed to the above. DocumentTarget, CommandDocument, and MethodDocument (MethodDocument#names can hold more than one name for an ambiguous receiver) came from #1180 ("Display command description in doc dialog on tab completion", merged 2026-03-13); rdoc_error_document for failed lookups was added by #1229 ("Keep completion alive when RDoc document retrieval fails", 2026-07-16). The names themselves are RI-style: the regexp completor emits things like "String.gsub" (a dot even for instance methods: RI's expand_name resolves it), "Array.new", or ["Hash.any?", "Proc.any?"] for an ambiguous {}.any?; the type-based completor gets the same shape from ReplTypeCompletor#doc_namespace. show_doc itself accepts anything RI accepts (Array, Array#each, Array.new, Array::new).
Proposal
Add a small ordered registry of document providers, defaulting to just the existing RDoc/RI behavior so nothing changes out of the box:
IRB.doc_providers # => [IRB::RDocDocumentProvider.new]
IRB.doc_providers.unshift(MyProvider.new) # e.g. in ~/.irbrc; earlier providers win
(An alternative shape would be IRB.conf[:DOC_PROVIDERS], consistent with EXTRA_DOC_DIRS; either works, but a plain array with push/unshift seems simpler to use and to reason about ordering with.)
A provider is a duck type, no base class required:
class MyProvider
# name is whatever show_doc / the completor already produce today (RI-style
# names such as "String#gsub", "String.gsub", "Array.new", "Array"). Return
# a String to be shown via IRB::Pager, or nil if this provider has nothing
# for the name, so the next provider gets a chance.
def document(name) end
# Optional. A short preview for the completion dialog: an array of lines
# that fit within `width` columns (ANSI escapes allowed). Return nil to
# skip the dialog for this name. Providers may omit this method entirely.
def dialog_contents(name, width) end
end
Resolution: providers are asked in order, and the first non-nil document/dialog_contents result wins. show_doc with no argument would keep starting RI's interactive session directly (providers are not consulted for that case, since "interactive" is RI-specific). When no provider returns anything, today's "not found" / "rdoc not installed" messages are kept.
The built-in IRB::RDocDocumentProvider would just be the existing code moved behind this interface: document wrapping Ri.display_name (captured instead of printed directly), dialog_contents wrapping retrieve_rdoc_document + RDoc::Markup::ToAnsi. So most of this is a refactor, not new behavior. A MethodDocument with multiple candidate names (ambiguous receivers) can be handled by calling document/dialog_contents once per name and combining, same as display_document does today with driver.add_method in a loop.
Sketch of ShowDoc#execute after the change, just to illustrate the shape (not final):
def execute(arg)
name = unwrap_string_literal(arg)
if name.nil?
# unchanged: still delegates straight to RI's interactive session
IRB::RDocDocumentProvider.new.interactive
return
end
IRB.doc_providers.each do |provider|
if (doc = provider.document(name))
Pager.page_content(doc)
return
end
end
# not found: keep today's messages (RI's "Nothing known about ...")
end
With this in place, bitclust-irb could register a provider instead of a separate refe command, and show_doc String#gsub would show the Japanese manual page when available, falling back to RI.
Notes / open questions
- Naming:
IRB.doc_providersvs.IRB.conf[:DOC_PROVIDERS], and the provider method names (document/dialog_contentsvs. something else), are open to bikeshedding. - Whether
show_docwith no argument (RI's interactive mode) should also become pluggable, or stay RI-only as sketched above. - This is related to, but does not by itself solve, #1242 ("Consider letting help fall back to RI documentation"): a provider abstraction would let
helpfall back to "documentation from some provider" rather than specifically RI, but that's a separate change to thehelpcommand. - Other plausible providers besides
bitclust-irb: YARD'syri, RBS-embedded documentation, project-local documentation, or manuals translated into other languages. - I'm happy to send a PR implementing this if the direction looks acceptable to maintainers;
bitclust-irbwould be the first external consumer.
Related
- #1180: introduced
DocumentTarget/CommandDocument/MethodDocumentinlib/irb/completion.rb. - #1229: added
rdoc_error_documentfor failed RDoc lookups. - #1242: asks for
helpto fall back to RI docs; a provider hook is related but doesn't itself implement that fallback. - rurema/bitclust#326: added the
bitclust-irbgem, which today registers a separaterefecommand because there is noshow_docextension point.
日本語要約
show_doc コマンドおよび自動補完の Alt+d ドキュメントダイアログは RDoc::RI::Driver に直結しており、他のドキュメント源(翻訳マニュアルや YARD など)を差し込む手段がありません。本 issue は、RI をデフォルトとしつつ外部の「ドキュメントプロバイダ」を登録できる小さな公開フック(IRB.doc_providers 案)を提案します。rurema 側では bitclust-irb(rurema/bitclust#326)が該当フックの不在によりやむを得ず別コマンド refe を登録している経緯があり、本提案が採用されれば show_doc から直接日本語マニュアルを引けるようになります。実装の方向性が受け入れられるなら PR を送る用意があります。
🤖 Generated with Claude Code
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading lib/irb/command/show_doc.rb, lib/irb/input-method.rb, and lib/irb/completion.rb, focusing on ShowDoc#execute, RelineInputMethod, and the document target classes. Trace the existing RDoc::RI::Driver paths and related error handling before deciding the registry shape. Done means external providers can serve show_doc and the completion dialog while RI remains the default and existing not-found behavior is preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- documentation, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100