diff --git a/lib/core_ext/uri.rb b/lib/core_ext/uri.rb index 43997393f..42ddb1ef8 100644 --- a/lib/core_ext/uri.rb +++ b/lib/core_ext/uri.rb @@ -3,6 +3,11 @@ module URI class Generic + # Avoid a deprecation warning with Ruby 3.4 where the default parser was changed to RFC3986. + # This condition must remain even after support for 3.4 has been dropped for users that have + # `uri` in their lockfile, decoupling it from the ruby version. + PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser) + class << self extend T::Sig @@ -10,12 +15,12 @@ class << self def from_path(path:, fragment: nil, scheme: "file") # On Windows, if the path begins with the disk name, we need to add a leading slash to make it a valid URI escaped_path = if /^[A-Z]:/i.match?(path) - DEFAULT_PARSER.escape("/#{path}") + PARSER.escape("/#{path}") elsif path.start_with?("//?/") # Some paths on Windows start with "//?/". This is a special prefix that allows for long file paths - DEFAULT_PARSER.escape(path.delete_prefix("//?")) + PARSER.escape(path.delete_prefix("//?")) else - DEFAULT_PARSER.escape(path) + PARSER.escape(path) end build(scheme: scheme, path: escaped_path, fragment: fragment) @@ -29,7 +34,7 @@ def to_standardized_path parsed_path = path return unless parsed_path - unescaped_path = DEFAULT_PARSER.unescape(parsed_path) + unescaped_path = PARSER.unescape(parsed_path) # On Windows, when we're getting the file system path back from the URI, we need to remove the leading forward # slash diff --git a/lib/ruby_lsp/requests/support/source_uri.rb b/lib/ruby_lsp/requests/support/source_uri.rb index e41e8a5d3..7df47b2ce 100644 --- a/lib/ruby_lsp/requests/support/source_uri.rb +++ b/lib/ruby_lsp/requests/support/source_uri.rb @@ -19,6 +19,13 @@ class Source < URI::File T::Array[Symbol], ) + # `uri` for Ruby 3.4 switched the default parser from RFC2396 to RFC3986. The new parser emits a deprecation + # warning on a few methods and delegates them to RFC2396, namely `extract`/`make_regexp`/`escape`/`unescape`. + # On earlier versions of the uri gem, the RFC2396_PARSER constant doesn't exist, so it needs some special + # handling to select a parser that doesn't emit deprecations. While it was backported to Ruby 3.1, users may + # have the uri gem in their own bundle and thus not use a compatible version. + PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser) + T.unsafe(self).alias_method(:gem_name, :host) T.unsafe(self).alias_method(:line_number, :fragment) @@ -41,7 +48,7 @@ def build(gem_name:, gem_version:, path:, line_number:) { scheme: "source", host: gem_name, - path: DEFAULT_PARSER.escape("/#{gem_version}/#{path}"), + path: PARSER.escape("/#{gem_version}/#{path}"), fragment: line_number, } )