Default gems: skip the gem-cache subprocess, parse RBS immediately - #87
Draft
apiology wants to merge 2 commits into
Draft
Default gems: skip the gem-cache subprocess, parse RBS immediately#87apiology wants to merge 2 commits into
apiology wants to merge 2 commits into
Conversation
DocMap splits requires by whether a gemspec exists: no gemspec goes to deserialize_stdlib_rbs_map, which builds pins on the spot, and a gemspec goes to deserialize_combined_pin_cache, which waits for `solargraph gems` to write a cache entry. Ruby has promoted most of the standard library to default gems, and each promotion silently moved that library from the first path to the second. A default gem carries no documentation of its own, so a combined cache entry could never hold more than the stdlib RBS already does. Measured on Ruby 3.2.6, abbrev, cgi, csv, delegate, digest, etc, fileutils, find, forwardable, ipaddr, mutex_m and nkf all yield zero YARD pins. Send those straight down the stdlib path. Of 65 stdlib requires probed, 29 qualify, and they also drop out of the caching queue since nothing is left for `solargraph gems` to build. Gems that merely look like stdlib are unaffected. logger 1.7.0 is not a default gem, and its YARD pins add 69 source locations and 39 doc strings over the RBS alone, so it stays on the cache path.
The pending review noted a default gem could ship its own RBS, and that "the stdlib RBS" implies one atomic source when it is not. State the actual, checkable reason instead: RbsMap.from_gemspec already tries RbsMap::StdlibMap first and returns it whenever resolved, so this method returns identical pins to that path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was written by Claude Code on behalf of @apiology.
Problem: A default gem waits on a cache that can never hold more than the RBS it already has.
DocMappicks the pin-building path by whether a gemspec exists, not by whether the gem ships any Ruby source. Every stdlib package Ruby promotes to a default gem inherits a gemspec and silently starts waiting onsolargraph gemsto cache it — of 65 stdlib requires probed on Ruby 3.2.6, 43 now take that path, versus 5 that still resolve straight from RBS. That wait exists to isolate a cost this gem never has.Solution: route default gems to the stdlib RBS path, keyed on
Gem::Specification#default_gem?.Why this is correct, not just faster
Gem-caching runs in a subprocess so LSP requests stay responsive: a project's gem set is unbounded, and an arbitrary gem's YARD source has no bound on parse time, so that work is deferred to a background thread while the editor answers immediately. Core and stdlib skip that detour because neither risk applies to them —
RbsMap::CoreMapbuilds all of Ruby core synchronously at boot, before any request is served, andRbsMap::StdlibMapdoes the same per library, live, mid-session, for any barerequirewith no gemspec (require 'set'), because there's no arbitrary source to parse, only RBS Ruby itself maintains.A default gem has that same property, and it's cheap enough to measure: digesting
forwardablecold (never touched in this process) took 9.3ms;pathname, digested right after, took 2.39ms. Both drop under 1ms oncePinCachehas them — for scale, even a warm read of the already-cached core pins (6,849 of them) costs 306ms, a price every boot already pays. Opening a file that introduces one never-before-seen default gem adds single-digit milliseconds, once; every default gem after that is instant:loggershows the boundary holds — real source, so it correctly stays on the subprocess path.