22# frozen_string_literal: true
33
44require "ruby_lsp/requests/support/source_uri"
5+ require "ruby_lsp/requests/support/package_url"
56
67module RubyLsp
78 module Listeners
@@ -102,19 +103,58 @@ def extract_document_link(node)
102103 comment = @lines_to_comments [ node . location . start_line - 1 ]
103104 return unless comment
104105
105- match = comment . location . slice . match ( %r{source://.*#\d +$} )
106+ match = comment . location . slice . match ( %r{( source://.*#\d +|pkg:gem/.*#.*) $} )
106107 return unless match
107108
108- uri = begin
109- URI (
110- match [ 0 ] , #: as !nil
111- )
109+ uri_string = match [ 0 ] #: as !nil
110+
111+ file_path , line_number = if uri_string . start_with? ( "pkg:gem/" )
112+ parse_package_url ( uri_string )
113+ else
114+ parse_source_uri ( uri_string )
115+ end
116+
117+ return unless file_path
118+
119+ @response_builder << Interface ::DocumentLink . new (
120+ range : range_from_location ( comment . location ) ,
121+ target : "file://#{ file_path } ##{ line_number } " ,
122+ tooltip : "Jump to #{ file_path } ##{ line_number } " ,
123+ )
124+ end
125+
126+ #: (String uri_string) -> [String, String]?
127+ def parse_package_url ( uri_string )
128+ purl = PackageURL . parse ( uri_string ) #: as PackageURL?
129+ return unless purl
130+
131+ gem_version = resolve_version ( purl . version , purl . name )
132+ return if gem_version . nil?
133+
134+ path , line_number = purl . subpath . split ( ":" , 2 )
135+ return unless path
136+
137+ gem_name = purl . name
138+ return unless gem_name
139+
140+ file_path = self . class . gem_paths . dig ( gem_name , gem_version , CGI . unescape ( path ) )
141+ return if file_path . nil?
142+
143+ [ file_path , line_number ]
144+ rescue PackageURL ::InvalidPackageURL
145+ nil
146+ end
147+
148+ #: (String uri_string) -> [String, String]?
149+ def parse_source_uri ( uri_string )
150+ uri = begin
151+ URI ( uri_string )
112152 rescue URI ::Error
113153 nil
114154 end #: as URI::Source?
115155 return unless uri
116156
117- gem_version = resolve_version ( uri )
157+ gem_version = resolve_version ( uri . gem_version , uri . gem_name )
118158 return if gem_version . nil?
119159
120160 path = uri . path
@@ -126,28 +166,20 @@ def extract_document_link(node)
126166 file_path = self . class . gem_paths . dig ( gem_name , gem_version , CGI . unescape ( path ) )
127167 return if file_path . nil?
128168
129- @response_builder << Interface ::DocumentLink . new (
130- range : range_from_location ( comment . location ) ,
131- target : "file://#{ file_path } ##{ uri . line_number } " ,
132- tooltip : "Jump to #{ file_path } ##{ uri . line_number } " ,
133- )
169+ [ file_path , uri . line_number || "0" ]
134170 end
135171
136172 # Try to figure out the gem version for a source:// link. The order of precedence is:
137173 # 1. The version in the URI
138174 # 2. The version in the RBI file name
139175 # 3. The version from the gemspec
140- #: (URI::Source uri) -> String?
141- def resolve_version ( uri )
142- version = uri . gem_version
176+ #: (String? version, String? gem_name) -> String?
177+ def resolve_version ( version , gem_name )
143178 return version unless version . nil? || version . empty?
144179
145180 return @gem_version unless @gem_version . nil? || @gem_version . empty?
146181
147- gem_name = uri . gem_name
148- return unless gem_name
149-
150- GEM_TO_VERSION_MAP [ gem_name ]
182+ GEM_TO_VERSION_MAP [ gem_name . to_s ]
151183 end
152184 end
153185 end
0 commit comments