ruby-lsp 0.22.1 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -9
- data/exe/ruby-lsp-check +5 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +26 -20
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +88 -22
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +60 -30
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +73 -55
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +16 -14
- data/lib/{core_ext → ruby_indexer/lib/ruby_indexer}/uri.rb +29 -3
- data/lib/ruby_indexer/ruby_indexer.rb +1 -1
- data/lib/ruby_indexer/test/class_variables_test.rb +140 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -6
- data/lib/ruby_indexer/test/configuration_test.rb +116 -51
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -2
- data/lib/ruby_indexer/test/index_test.rb +72 -43
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_indexer/test/reference_finder_test.rb +1 -1
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_indexer/test/uri_test.rb +72 -0
- data/lib/ruby_lsp/addon.rb +9 -0
- data/lib/ruby_lsp/base_server.rb +15 -6
- data/lib/ruby_lsp/document.rb +10 -1
- data/lib/ruby_lsp/internal.rb +1 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +8 -4
- data/lib/ruby_lsp/listeners/completion.rb +73 -4
- data/lib/ruby_lsp/listeners/definition.rb +73 -17
- data/lib/ruby_lsp/listeners/document_symbol.rb +12 -1
- data/lib/ruby_lsp/listeners/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/listeners/hover.rb +57 -0
- data/lib/ruby_lsp/requests/completion.rb +6 -0
- data/lib/ruby_lsp/requests/completion_resolve.rb +2 -1
- data/lib/ruby_lsp/requests/definition.rb +6 -0
- data/lib/ruby_lsp/requests/prepare_rename.rb +51 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -1
- data/lib/ruby_lsp/requests/rename.rb +14 -4
- data/lib/ruby_lsp/requests/support/common.rb +1 -5
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +1 -1
- data/lib/ruby_lsp/requests/workspace_symbol.rb +3 -2
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +42 -7
- data/lib/ruby_lsp/setup_bundler.rb +31 -41
- data/lib/ruby_lsp/test_helper.rb +45 -11
- data/lib/ruby_lsp/type_inferrer.rb +22 -0
- data/lib/ruby_lsp/utils.rb +3 -0
- metadata +7 -8
- data/lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb +0 -29
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module RubyLsp
         | 
| 5 | 
            +
              module Requests
         | 
| 6 | 
            +
                # The
         | 
| 7 | 
            +
                # [prepare_rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename)
         | 
| 8 | 
            +
                # # request checks the validity of a rename operation at a given location.
         | 
| 9 | 
            +
                class PrepareRename < Request
         | 
| 10 | 
            +
                  extend T::Sig
         | 
| 11 | 
            +
                  include Support::Common
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  sig do
         | 
| 14 | 
            +
                    params(
         | 
| 15 | 
            +
                      document: RubyDocument,
         | 
| 16 | 
            +
                      position: T::Hash[Symbol, T.untyped],
         | 
| 17 | 
            +
                    ).void
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                  def initialize(document, position)
         | 
| 20 | 
            +
                    super()
         | 
| 21 | 
            +
                    @document = document
         | 
| 22 | 
            +
                    @position = T.let(position, T::Hash[Symbol, Integer])
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  sig { override.returns(T.nilable(Interface::Range)) }
         | 
| 26 | 
            +
                  def perform
         | 
| 27 | 
            +
                    char_position = @document.create_scanner.find_char_position(@position)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    node_context = RubyDocument.locate(
         | 
| 30 | 
            +
                      @document.parse_result.value,
         | 
| 31 | 
            +
                      char_position,
         | 
| 32 | 
            +
                      node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
         | 
| 33 | 
            +
                      code_units_cache: @document.code_units_cache,
         | 
| 34 | 
            +
                    )
         | 
| 35 | 
            +
                    target = node_context.node
         | 
| 36 | 
            +
                    parent = node_context.parent
         | 
| 37 | 
            +
                    return if !target || target.is_a?(Prism::ProgramNode)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
         | 
| 40 | 
            +
                      target = determine_target(
         | 
| 41 | 
            +
                        target,
         | 
| 42 | 
            +
                        parent,
         | 
| 43 | 
            +
                        @position,
         | 
| 44 | 
            +
                      )
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    range_from_location(target.location)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
            end
         | 
| @@ -66,7 +66,7 @@ module RubyLsp | |
| 66 66 | 
             
                      Interface::TypeHierarchyItem.new(
         | 
| 67 67 | 
             
                        name: first_entry.name,
         | 
| 68 68 | 
             
                        kind: kind_for_entry(first_entry),
         | 
| 69 | 
            -
                        uri:  | 
| 69 | 
            +
                        uri: first_entry.uri.to_s,
         | 
| 70 70 | 
             
                        range: range,
         | 
| 71 71 | 
             
                        selection_range: range,
         | 
| 72 72 | 
             
                      ),
         | 
| @@ -12,6 +12,15 @@ module RubyLsp | |
| 12 12 |  | 
| 13 13 | 
             
                  class InvalidNameError < StandardError; end
         | 
| 14 14 |  | 
| 15 | 
            +
                  class << self
         | 
| 16 | 
            +
                    extend T::Sig
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    sig { returns(Interface::RenameOptions) }
         | 
| 19 | 
            +
                    def provider
         | 
| 20 | 
            +
                      Interface::RenameOptions.new(prepare_provider: true)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 15 24 | 
             
                  sig do
         | 
| 16 25 | 
             
                    params(
         | 
| 17 26 | 
             
                      global_state: GlobalState,
         | 
| @@ -106,7 +115,9 @@ module RubyLsp | |
| 106 115 |  | 
| 107 116 | 
             
                    T.must(@global_state.index[fully_qualified_name]).each do |entry|
         | 
| 108 117 | 
             
                      # Do not rename files that are not part of the workspace
         | 
| 109 | 
            -
                       | 
| 118 | 
            +
                      uri = entry.uri
         | 
| 119 | 
            +
                      file_path = T.must(uri.full_path)
         | 
| 120 | 
            +
                      next unless file_path.start_with?(@global_state.workspace_path)
         | 
| 110 121 |  | 
| 111 122 | 
             
                      case entry
         | 
| 112 123 | 
             
                      when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
         | 
| @@ -117,13 +128,12 @@ module RubyLsp | |
| 117 128 | 
             
                        if "#{file_name}.rb" == entry.file_name
         | 
| 118 129 | 
             
                          new_file_name = file_from_constant_name(T.must(@new_name.split("::").last))
         | 
| 119 130 |  | 
| 120 | 
            -
                          old_uri = URI::Generic.from_path(path: entry.file_path).to_s
         | 
| 121 131 | 
             
                          new_uri = URI::Generic.from_path(path: File.join(
         | 
| 122 | 
            -
                            File.dirname( | 
| 132 | 
            +
                            File.dirname(file_path),
         | 
| 123 133 | 
             
                            "#{new_file_name}.rb",
         | 
| 124 134 | 
             
                          )).to_s
         | 
| 125 135 |  | 
| 126 | 
            -
                          document_changes << Interface::RenameFile.new(kind: "rename", old_uri:  | 
| 136 | 
            +
                          document_changes << Interface::RenameFile.new(kind: "rename", old_uri: uri.to_s, new_uri: new_uri)
         | 
| 127 137 | 
             
                        end
         | 
| 128 138 | 
             
                      end
         | 
| 129 139 | 
             
                    end
         | 
| @@ -93,11 +93,7 @@ module RubyLsp | |
| 93 93 | 
             
                        # based, which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to
         | 
| 94 94 | 
             
                        # columns. The format for VS Code file URIs is
         | 
| 95 95 | 
             
                        # `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
         | 
| 96 | 
            -
                        uri =  | 
| 97 | 
            -
                          path: entry.file_path,
         | 
| 98 | 
            -
                          fragment: "L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}",
         | 
| 99 | 
            -
                        )
         | 
| 100 | 
            -
             | 
| 96 | 
            +
                        uri = "#{entry.uri}#L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}"
         | 
| 101 97 | 
             
                        definitions << "[#{entry.file_name}](#{uri})"
         | 
| 102 98 | 
             
                        content << "\n\n#{entry.comments}" unless entry.comments.empty?
         | 
| 103 99 | 
             
                      end
         | 
| @@ -65,7 +65,7 @@ module RubyLsp | |
| 65 65 | 
             
                    Interface::TypeHierarchyItem.new(
         | 
| 66 66 | 
             
                      name: entry.name,
         | 
| 67 67 | 
             
                      kind: kind_for_entry(entry),
         | 
| 68 | 
            -
                      uri:  | 
| 68 | 
            +
                      uri: entry.uri.to_s,
         | 
| 69 69 | 
             
                      range: range_from_location(entry.location),
         | 
| 70 70 | 
             
                      selection_range: range_from_location(entry.name_location),
         | 
| 71 71 | 
             
                      detail: entry.file_name,
         | 
| @@ -21,7 +21,8 @@ module RubyLsp | |
| 21 21 | 
             
                  sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
         | 
| 22 22 | 
             
                  def perform
         | 
| 23 23 | 
             
                    @index.fuzzy_search(@query).filter_map do |entry|
         | 
| 24 | 
            -
                       | 
| 24 | 
            +
                      uri = entry.uri
         | 
| 25 | 
            +
                      file_path = T.must(uri.full_path)
         | 
| 25 26 |  | 
| 26 27 | 
             
                      # We only show symbols declared in the workspace
         | 
| 27 28 | 
             
                      in_dependencies = !not_in_dependencies?(file_path)
         | 
| @@ -43,7 +44,7 @@ module RubyLsp | |
| 43 44 | 
             
                        container_name: container.join("::"),
         | 
| 44 45 | 
             
                        kind: kind,
         | 
| 45 46 | 
             
                        location: Interface::Location.new(
         | 
| 46 | 
            -
                          uri:  | 
| 47 | 
            +
                          uri: uri.to_s,
         | 
| 47 48 | 
             
                          range:  Interface::Range.new(
         | 
| 48 49 | 
             
                            start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
         | 
| 49 50 | 
             
                            end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
         | 
| @@ -5,7 +5,7 @@ def compose(raw_initialize) | |
| 5 5 | 
             
              require_relative "../setup_bundler"
         | 
| 6 6 | 
             
              require "json"
         | 
| 7 7 | 
             
              require "uri"
         | 
| 8 | 
            -
               | 
| 8 | 
            +
              require "ruby_indexer/lib/ruby_indexer/uri"
         | 
| 9 9 |  | 
| 10 10 | 
             
              initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
         | 
| 11 11 | 
             
              workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)
         | 
    
        data/lib/ruby_lsp/server.rb
    CHANGED
    
    | @@ -71,6 +71,8 @@ module RubyLsp | |
| 71 71 | 
             
                    text_document_prepare_type_hierarchy(message)
         | 
| 72 72 | 
             
                  when "textDocument/rename"
         | 
| 73 73 | 
             
                    text_document_rename(message)
         | 
| 74 | 
            +
                  when "textDocument/prepareRename"
         | 
| 75 | 
            +
                    text_document_prepare_rename(message)
         | 
| 74 76 | 
             
                  when "textDocument/references"
         | 
| 75 77 | 
             
                    text_document_references(message)
         | 
| 76 78 | 
             
                  when "typeHierarchy/supertypes"
         | 
| @@ -117,12 +119,24 @@ module RubyLsp | |
| 117 119 | 
             
                    # If a document is deleted before we are able to process all of its enqueued requests, we will try to read it
         | 
| 118 120 | 
             
                    # from disk and it raise this error. This is expected, so we don't include the `data` attribute to avoid
         | 
| 119 121 | 
             
                    # reporting these to our telemetry
         | 
| 120 | 
            -
                     | 
| 122 | 
            +
                    case e
         | 
| 123 | 
            +
                    when Store::NonExistingDocumentError
         | 
| 121 124 | 
             
                      send_message(Error.new(
         | 
| 122 125 | 
             
                        id: message[:id],
         | 
| 123 126 | 
             
                        code: Constant::ErrorCodes::INVALID_PARAMS,
         | 
| 124 127 | 
             
                        message: e.full_message,
         | 
| 125 128 | 
             
                      ))
         | 
| 129 | 
            +
                    when Document::LocationNotFoundError
         | 
| 130 | 
            +
                      send_message(Error.new(
         | 
| 131 | 
            +
                        id: message[:id],
         | 
| 132 | 
            +
                        code: Constant::ErrorCodes::REQUEST_FAILED,
         | 
| 133 | 
            +
                        message: <<~MESSAGE,
         | 
| 134 | 
            +
                          Request #{message[:method]} failed to find the target position.
         | 
| 135 | 
            +
                          The file might have been modified while the server was in the middle of searching for the target.
         | 
| 136 | 
            +
                          If you experience this regularly, please report any findings and extra information on
         | 
| 137 | 
            +
                          https://github.com/Shopify/ruby-lsp/issues/2446
         | 
| 138 | 
            +
                        MESSAGE
         | 
| 139 | 
            +
                      ))
         | 
| 126 140 | 
             
                    else
         | 
| 127 141 | 
             
                      send_message(Error.new(
         | 
| 128 142 | 
             
                        id: message[:id],
         | 
| @@ -237,6 +251,7 @@ module RubyLsp | |
| 237 251 | 
             
                  completion_provider = Requests::Completion.provider if enabled_features["completion"]
         | 
| 238 252 | 
             
                  signature_help_provider = Requests::SignatureHelp.provider if enabled_features["signatureHelp"]
         | 
| 239 253 | 
             
                  type_hierarchy_provider = Requests::PrepareTypeHierarchy.provider if enabled_features["typeHierarchy"]
         | 
| 254 | 
            +
                  rename_provider = Requests::Rename.provider unless @global_state.has_type_checker
         | 
| 240 255 |  | 
| 241 256 | 
             
                  response = {
         | 
| 242 257 | 
             
                    capabilities: Interface::ServerCapabilities.new(
         | 
| @@ -263,7 +278,7 @@ module RubyLsp | |
| 263 278 | 
             
                      workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.has_type_checker,
         | 
| 264 279 | 
             
                      signature_help_provider: signature_help_provider,
         | 
| 265 280 | 
             
                      type_hierarchy_provider: type_hierarchy_provider,
         | 
| 266 | 
            -
                      rename_provider:  | 
| 281 | 
            +
                      rename_provider: rename_provider,
         | 
| 267 282 | 
             
                      references_provider: !@global_state.has_type_checker,
         | 
| 268 283 | 
             
                      document_range_formatting_provider: true,
         | 
| 269 284 | 
             
                      experimental: {
         | 
| @@ -727,6 +742,24 @@ module RubyLsp | |
| 727 742 | 
             
                  send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
         | 
| 728 743 | 
             
                end
         | 
| 729 744 |  | 
| 745 | 
            +
                sig { params(message: T::Hash[Symbol, T.untyped]).void }
         | 
| 746 | 
            +
                def text_document_prepare_rename(message)
         | 
| 747 | 
            +
                  params = message[:params]
         | 
| 748 | 
            +
                  document = @store.get(params.dig(:textDocument, :uri))
         | 
| 749 | 
            +
             | 
| 750 | 
            +
                  unless document.is_a?(RubyDocument)
         | 
| 751 | 
            +
                    send_empty_response(message[:id])
         | 
| 752 | 
            +
                    return
         | 
| 753 | 
            +
                  end
         | 
| 754 | 
            +
             | 
| 755 | 
            +
                  send_message(
         | 
| 756 | 
            +
                    Result.new(
         | 
| 757 | 
            +
                      id: message[:id],
         | 
| 758 | 
            +
                      response: Requests::PrepareRename.new(document, params[:position]).perform,
         | 
| 759 | 
            +
                    ),
         | 
| 760 | 
            +
                  )
         | 
| 761 | 
            +
                end
         | 
| 762 | 
            +
             | 
| 730 763 | 
             
                sig { params(message: T::Hash[Symbol, T.untyped]).void }
         | 
| 731 764 | 
             
                def text_document_references(message)
         | 
| 732 765 | 
             
                  params = message[:params]
         | 
| @@ -981,15 +1014,17 @@ module RubyLsp | |
| 981 1014 | 
             
                    next unless file_path.end_with?(".rb")
         | 
| 982 1015 |  | 
| 983 1016 | 
             
                    load_path_entry = $LOAD_PATH.find { |load_path| file_path.start_with?(load_path) }
         | 
| 984 | 
            -
                     | 
| 1017 | 
            +
                    uri.add_require_path_from_load_entry(load_path_entry) if load_path_entry
         | 
| 1018 | 
            +
             | 
| 1019 | 
            +
                    content = File.read(file_path)
         | 
| 985 1020 |  | 
| 986 1021 | 
             
                    case change[:type]
         | 
| 987 1022 | 
             
                    when Constant::FileChangeType::CREATED
         | 
| 988 | 
            -
                      index.index_single( | 
| 1023 | 
            +
                      index.index_single(uri, content)
         | 
| 989 1024 | 
             
                    when Constant::FileChangeType::CHANGED
         | 
| 990 | 
            -
                      index.handle_change( | 
| 1025 | 
            +
                      index.handle_change(uri, content)
         | 
| 991 1026 | 
             
                    when Constant::FileChangeType::DELETED
         | 
| 992 | 
            -
                      index.delete( | 
| 1027 | 
            +
                      index.delete(uri)
         | 
| 993 1028 | 
             
                    end
         | 
| 994 1029 | 
             
                  end
         | 
| 995 1030 |  | 
| @@ -1088,7 +1123,7 @@ module RubyLsp | |
| 1088 1123 |  | 
| 1089 1124 | 
             
                sig { override.void }
         | 
| 1090 1125 | 
             
                def shutdown
         | 
| 1091 | 
            -
                  Addon. | 
| 1126 | 
            +
                  Addon.unload_addons
         | 
| 1092 1127 | 
             
                end
         | 
| 1093 1128 |  | 
| 1094 1129 | 
             
                sig { void }
         | 
| @@ -152,7 +152,13 @@ module RubyLsp | |
| 152 152 | 
             
                  end
         | 
| 153 153 |  | 
| 154 154 | 
             
                  unless @dependencies["debug"]
         | 
| 155 | 
            -
                     | 
| 155 | 
            +
                    # The `mri` platform excludes Windows. We want to install the debug gem only on MRI for any operating system,
         | 
| 156 | 
            +
                    # but that constraint doesn't yet exist in Bundler. On Windows, we are manually checking if the engine is MRI
         | 
| 157 | 
            +
                    parts << if Gem.win_platform?
         | 
| 158 | 
            +
                      'gem "debug", require: false, group: :development, install_if: -> { RUBY_ENGINE == "ruby" }'
         | 
| 159 | 
            +
                    else
         | 
| 160 | 
            +
                      'gem "debug", require: false, group: :development, platforms: :mri'
         | 
| 161 | 
            +
                    end
         | 
| 156 162 | 
             
                  end
         | 
| 157 163 |  | 
| 158 164 | 
             
                  if @rails_app && !@dependencies["ruby-lsp-rails"]
         | 
| @@ -196,15 +202,15 @@ module RubyLsp | |
| 196 202 | 
             
                    env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
         | 
| 197 203 | 
             
                  end
         | 
| 198 204 |  | 
| 199 | 
            -
                   | 
| 200 | 
            -
             | 
| 201 | 
            -
                  # This same check happens conditionally when running through the command. For invoking the CLI directly, it's
         | 
| 202 | 
            -
                  # important that we ensure the Bundler version is set to avoid restarts
         | 
| 205 | 
            +
                  # Set the specific Bundler version used by the main app. This avoids issues with Bundler restarts, which clean the
         | 
| 206 | 
            +
                  # environment and lead to the `ruby-lsp` executable not being found
         | 
| 203 207 | 
             
                  if @bundler_version
         | 
| 204 208 | 
             
                    env["BUNDLER_VERSION"] = @bundler_version.to_s
         | 
| 205 209 | 
             
                    install_bundler_if_needed
         | 
| 206 210 | 
             
                  end
         | 
| 207 211 |  | 
| 212 | 
            +
                  return run_bundle_install_through_command(env) unless @launcher
         | 
| 213 | 
            +
             | 
| 208 214 | 
             
                  begin
         | 
| 209 215 | 
             
                    run_bundle_install_directly(env)
         | 
| 210 216 | 
             
                    # If no error occurred, then clear previous errors
         | 
| @@ -235,12 +241,16 @@ module RubyLsp | |
| 235 241 | 
             
                  env
         | 
| 236 242 | 
             
                end
         | 
| 237 243 |  | 
| 238 | 
            -
                sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
         | 
| 239 | 
            -
                def run_bundle_install_directly(env)
         | 
| 244 | 
            +
                sig { params(env: T::Hash[String, String], force_install: T::Boolean).returns(T::Hash[String, String]) }
         | 
| 245 | 
            +
                def run_bundle_install_directly(env, force_install: false)
         | 
| 240 246 | 
             
                  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                  # The ENV can only be merged after checking if an update is required because we depend on the original value of
         | 
| 249 | 
            +
                  # ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
         | 
| 250 | 
            +
                  should_update = should_bundle_update?
         | 
| 241 251 | 
             
                  T.unsafe(ENV).merge!(env)
         | 
| 242 252 |  | 
| 243 | 
            -
                  unless  | 
| 253 | 
            +
                  unless should_update && !force_install
         | 
| 244 254 | 
             
                    Bundler::CLI::Install.new({}).run
         | 
| 245 255 | 
             
                    correct_relative_remote_paths if @custom_lockfile.exist?
         | 
| 246 256 | 
             
                    return env
         | 
| @@ -255,12 +265,13 @@ module RubyLsp | |
| 255 265 | 
             
                  correct_relative_remote_paths if @custom_lockfile.exist?
         | 
| 256 266 | 
             
                  @last_updated_path.write(Time.now.iso8601)
         | 
| 257 267 | 
             
                  env
         | 
| 268 | 
            +
                rescue Bundler::GemNotFound, Bundler::GitError
         | 
| 269 | 
            +
                  # If a gem is not installed, skip the upgrade and try to install it with a single retry
         | 
| 270 | 
            +
                  @retry ? env : run_bundle_install_directly(env, force_install: true)
         | 
| 258 271 | 
             
                end
         | 
| 259 272 |  | 
| 260 273 | 
             
                sig { params(env: T::Hash[String, String]).returns(T::Hash[String, String]) }
         | 
| 261 274 | 
             
                def run_bundle_install_through_command(env)
         | 
| 262 | 
            -
                  base_bundle = base_bundle_command(env)
         | 
| 263 | 
            -
             | 
| 264 275 | 
             
                  # If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
         | 
| 265 276 | 
             
                  # to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
         | 
| 266 277 | 
             
                  # and any of `ruby-lsp`, `ruby-lsp-rails` or `debug` weren't a part of the Gemfile, then we need to run `bundle
         | 
| @@ -269,13 +280,20 @@ module RubyLsp | |
| 269 280 |  | 
| 270 281 | 
             
                  # When not updating, we run `(bundle check || bundle install)`
         | 
| 271 282 | 
             
                  # When updating, we run `((bundle check && bundle update ruby-lsp debug) || bundle install)`
         | 
| 272 | 
            -
                   | 
| 283 | 
            +
                  bundler_path = File.join(Gem.default_bindir, "bundle")
         | 
| 284 | 
            +
                  base_command = (!Gem.win_platform? && File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                  if env["BUNDLER_VERSION"]
         | 
| 287 | 
            +
                    base_command << " _#{env["BUNDLER_VERSION"]}_"
         | 
| 288 | 
            +
                  end
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                  command = +"(#{base_command} check"
         | 
| 273 291 |  | 
| 274 292 | 
             
                  if should_bundle_update?
         | 
| 275 293 | 
             
                    # If any of `ruby-lsp`, `ruby-lsp-rails` or `debug` are not in the Gemfile, try to update them to the latest
         | 
| 276 294 | 
             
                    # version
         | 
| 277 295 | 
             
                    command.prepend("(")
         | 
| 278 | 
            -
                    command << " && #{ | 
| 296 | 
            +
                    command << " && #{base_command} update "
         | 
| 279 297 | 
             
                    command << "ruby-lsp " unless @dependencies["ruby-lsp"]
         | 
| 280 298 | 
             
                    command << "debug " unless @dependencies["debug"]
         | 
| 281 299 | 
             
                    command << "ruby-lsp-rails " if @rails_app && !@dependencies["ruby-lsp-rails"]
         | 
| @@ -285,7 +303,7 @@ module RubyLsp | |
| 285 303 | 
             
                    @last_updated_path.write(Time.now.iso8601)
         | 
| 286 304 | 
             
                  end
         | 
| 287 305 |  | 
| 288 | 
            -
                  command << " || #{ | 
| 306 | 
            +
                  command << " || #{base_command} install) "
         | 
| 289 307 |  | 
| 290 308 | 
             
                  # Redirect stdout to stderr to prevent going into an infinite loop. The extension might confuse stdout output with
         | 
| 291 309 | 
             
                  # responses
         | 
| @@ -395,34 +413,6 @@ module RubyLsp | |
| 395 413 | 
             
                  /class .* < (::)?Rails::Application/.match?(application_contents)
         | 
| 396 414 | 
             
                end
         | 
| 397 415 |  | 
| 398 | 
            -
                # Returns the base bundle command we should use for this project, which will be:
         | 
| 399 | 
            -
                # - `bundle` if there's no locked Bundler version and no `bin/bundle` binstub in the $PATH
         | 
| 400 | 
            -
                # - `bundle _<version>_` if there's a locked Bundler version
         | 
| 401 | 
            -
                # - `bin/bundle` if there's a `bin/bundle` binstub in the $PATH
         | 
| 402 | 
            -
                sig { params(env: T::Hash[String, String]).returns(String) }
         | 
| 403 | 
            -
                def base_bundle_command(env)
         | 
| 404 | 
            -
                  path_parts = if Gem.win_platform?
         | 
| 405 | 
            -
                    ENV["Path"] || ENV["PATH"] || ENV["path"] || ""
         | 
| 406 | 
            -
                  else
         | 
| 407 | 
            -
                    ENV["PATH"] || ""
         | 
| 408 | 
            -
                  end.split(File::PATH_SEPARATOR)
         | 
| 409 | 
            -
             | 
| 410 | 
            -
                  bin_dir = File.expand_path("bin", @project_path)
         | 
| 411 | 
            -
                  bundle_binstub = File.join(@project_path, "bin", "bundle")
         | 
| 412 | 
            -
             | 
| 413 | 
            -
                  if File.exist?(bundle_binstub) && path_parts.any? { |path| File.expand_path(path, @project_path) == bin_dir }
         | 
| 414 | 
            -
                    return bundle_binstub
         | 
| 415 | 
            -
                  end
         | 
| 416 | 
            -
             | 
| 417 | 
            -
                  if @bundler_version
         | 
| 418 | 
            -
                    env["BUNDLER_VERSION"] = @bundler_version.to_s
         | 
| 419 | 
            -
                    install_bundler_if_needed
         | 
| 420 | 
            -
                    return "bundle _#{@bundler_version}_"
         | 
| 421 | 
            -
                  end
         | 
| 422 | 
            -
             | 
| 423 | 
            -
                  "bundle"
         | 
| 424 | 
            -
                end
         | 
| 425 | 
            -
             | 
| 426 416 | 
             
                sig { void }
         | 
| 427 417 | 
             
                def patch_thor_to_print_progress_to_stderr!
         | 
| 428 418 | 
             
                  return unless defined?(Bundler::Thor::Shell::Basic)
         | 
    
        data/lib/ruby_lsp/test_helper.rb
    CHANGED
    
    | @@ -1,11 +1,16 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: true
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 4 | 
             
            # NOTE: This module is intended to be used by addons for writing their own tests, so keep that in mind if changing.
         | 
| 5 5 |  | 
| 6 6 | 
             
            module RubyLsp
         | 
| 7 7 | 
             
              module TestHelper
         | 
| 8 | 
            +
                class TestError < StandardError; end
         | 
| 9 | 
            +
             | 
| 8 10 | 
             
                extend T::Sig
         | 
| 11 | 
            +
                extend T::Helpers
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                requires_ancestor { Kernel }
         | 
| 9 14 |  | 
| 10 15 | 
             
                sig do
         | 
| 11 16 | 
             
                  type_parameters(:T)
         | 
| @@ -36,20 +41,49 @@ module RubyLsp | |
| 36 41 | 
             
                        },
         | 
| 37 42 | 
             
                      },
         | 
| 38 43 | 
             
                    })
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    server.global_state.index.index_single(uri, source)
         | 
| 39 46 | 
             
                  end
         | 
| 40 47 |  | 
| 41 | 
            -
                  server.global_state.index.index_single(
         | 
| 42 | 
            -
                    RubyIndexer::IndexablePath.new(nil, T.must(uri.to_standardized_path)),
         | 
| 43 | 
            -
                    source,
         | 
| 44 | 
            -
                  )
         | 
| 45 48 | 
             
                  server.load_addons(include_project_addons: false) if load_addons
         | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
                     | 
| 49 | 
            +
             | 
| 50 | 
            +
                  begin
         | 
| 51 | 
            +
                    block.call(server, uri)
         | 
| 52 | 
            +
                  ensure
         | 
| 53 | 
            +
                    if load_addons
         | 
| 54 | 
            +
                      RubyLsp::Addon.addons.each(&:deactivate)
         | 
| 55 | 
            +
                      RubyLsp::Addon.addons.clear
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                    server.run_shutdown
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                sig { params(server: RubyLsp::Server).returns(RubyLsp::Result) }
         | 
| 62 | 
            +
                def pop_result(server)
         | 
| 63 | 
            +
                  result = server.pop_response
         | 
| 64 | 
            +
                  result = server.pop_response until result.is_a?(RubyLsp::Result) || result.is_a?(RubyLsp::Error)
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  if result.is_a?(RubyLsp::Error)
         | 
| 67 | 
            +
                    raise TestError, "Failed to execute request #{result.message}"
         | 
| 68 | 
            +
                  else
         | 
| 69 | 
            +
                    result
         | 
| 51 70 | 
             
                  end
         | 
| 52 | 
            -
             | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def pop_log_notification(message_queue, type)
         | 
| 74 | 
            +
                  log = message_queue.pop
         | 
| 75 | 
            +
                  return log if log.params.type == type
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  log = message_queue.pop until log.params.type == type
         | 
| 78 | 
            +
                  log
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def pop_message(outgoing_queue, &block)
         | 
| 82 | 
            +
                  message = outgoing_queue.pop
         | 
| 83 | 
            +
                  return message if block.call(message)
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  message = outgoing_queue.pop until block.call(message)
         | 
| 86 | 
            +
                  message
         | 
| 53 87 | 
             
                end
         | 
| 54 88 | 
             
              end
         | 
| 55 89 | 
             
            end
         | 
| @@ -23,6 +23,9 @@ module RubyLsp | |
| 23 23 | 
             
                    Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode,
         | 
| 24 24 | 
             
                    Prism::SuperNode, Prism::ForwardingSuperNode
         | 
| 25 25 | 
             
                    self_receiver_handling(node_context)
         | 
| 26 | 
            +
                  when Prism::ClassVariableAndWriteNode, Prism::ClassVariableWriteNode, Prism::ClassVariableOperatorWriteNode,
         | 
| 27 | 
            +
                    Prism::ClassVariableOrWriteNode, Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
         | 
| 28 | 
            +
                    infer_receiver_for_class_variables(node_context)
         | 
| 26 29 | 
             
                  end
         | 
| 27 30 | 
             
                end
         | 
| 28 31 |  | 
| @@ -143,6 +146,25 @@ module RubyLsp | |
| 143 146 | 
             
                  nil
         | 
| 144 147 | 
             
                end
         | 
| 145 148 |  | 
| 149 | 
            +
                sig { params(node_context: NodeContext).returns(T.nilable(Type)) }
         | 
| 150 | 
            +
                def infer_receiver_for_class_variables(node_context)
         | 
| 151 | 
            +
                  nesting_parts = node_context.nesting.dup
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                  return Type.new("Object") if nesting_parts.empty?
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  nesting_parts.reverse_each do |part|
         | 
| 156 | 
            +
                    break unless part.include?("<Class:")
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    nesting_parts.pop
         | 
| 159 | 
            +
                  end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                  receiver_name = nesting_parts.join("::")
         | 
| 162 | 
            +
                  resolved_receiver = @index.resolve(receiver_name, node_context.nesting)&.first
         | 
| 163 | 
            +
                  return unless resolved_receiver&.name
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  Type.new(resolved_receiver.name)
         | 
| 166 | 
            +
                end
         | 
| 167 | 
            +
             | 
| 146 168 | 
             
                # A known type
         | 
| 147 169 | 
             
                class Type
         | 
| 148 170 | 
             
                  extend T::Sig
         | 
    
        data/lib/ruby_lsp/utils.rb
    CHANGED
    
    | @@ -173,6 +173,9 @@ module RubyLsp | |
| 173 173 | 
             
                sig { returns(String) }
         | 
| 174 174 | 
             
                attr_reader :message
         | 
| 175 175 |  | 
| 176 | 
            +
                sig { returns(Integer) }
         | 
| 177 | 
            +
                attr_reader :code
         | 
| 178 | 
            +
             | 
| 176 179 | 
             
                sig { params(id: Integer, code: Integer, message: String, data: T.nilable(T::Hash[Symbol, T.untyped])).void }
         | 
| 177 180 | 
             
                def initialize(id:, code:, message:, data: nil)
         | 
| 178 181 | 
             
                  @id = id
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ruby-lsp
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.23.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Shopify
         | 
| 8 | 
            -
            autorequire:
         | 
| 9 8 | 
             
            bindir: exe
         | 
| 10 9 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 10 | 
            +
            date: 2025-01-06 00:00:00.000000000 Z
         | 
| 12 11 | 
             
            dependencies:
         | 
| 13 12 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 13 | 
             
              name: language_server-protocol
         | 
| @@ -94,7 +93,6 @@ files: | |
| 94 93 | 
             
            - exe/ruby-lsp
         | 
| 95 94 | 
             
            - exe/ruby-lsp-check
         | 
| 96 95 | 
             
            - exe/ruby-lsp-launcher
         | 
| 97 | 
            -
            - lib/core_ext/uri.rb
         | 
| 98 96 | 
             
            - lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
         | 
| 99 97 | 
             
            - lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
         | 
| 100 98 | 
             
            - lib/ruby-lsp.rb
         | 
| @@ -103,12 +101,13 @@ files: | |
| 103 101 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/enhancement.rb
         | 
| 104 102 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/entry.rb
         | 
| 105 103 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/index.rb
         | 
| 106 | 
            -
            - lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb
         | 
| 107 104 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/location.rb
         | 
| 108 105 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
         | 
| 109 106 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
         | 
| 110 107 | 
             
            - lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
         | 
| 108 | 
            +
            - lib/ruby_indexer/lib/ruby_indexer/uri.rb
         | 
| 111 109 | 
             
            - lib/ruby_indexer/ruby_indexer.rb
         | 
| 110 | 
            +
            - lib/ruby_indexer/test/class_variables_test.rb
         | 
| 112 111 | 
             
            - lib/ruby_indexer/test/classes_and_modules_test.rb
         | 
| 113 112 | 
             
            - lib/ruby_indexer/test/configuration_test.rb
         | 
| 114 113 | 
             
            - lib/ruby_indexer/test/constant_test.rb
         | 
| @@ -121,6 +120,7 @@ files: | |
| 121 120 | 
             
            - lib/ruby_indexer/test/rbs_indexer_test.rb
         | 
| 122 121 | 
             
            - lib/ruby_indexer/test/reference_finder_test.rb
         | 
| 123 122 | 
             
            - lib/ruby_indexer/test/test_case.rb
         | 
| 123 | 
            +
            - lib/ruby_indexer/test/uri_test.rb
         | 
| 124 124 | 
             
            - lib/ruby_lsp/addon.rb
         | 
| 125 125 | 
             
            - lib/ruby_lsp/base_server.rb
         | 
| 126 126 | 
             
            - lib/ruby_lsp/client_capabilities.rb
         | 
| @@ -157,6 +157,7 @@ files: | |
| 157 157 | 
             
            - lib/ruby_lsp/requests/hover.rb
         | 
| 158 158 | 
             
            - lib/ruby_lsp/requests/inlay_hints.rb
         | 
| 159 159 | 
             
            - lib/ruby_lsp/requests/on_type_formatting.rb
         | 
| 160 | 
            +
            - lib/ruby_lsp/requests/prepare_rename.rb
         | 
| 160 161 | 
             
            - lib/ruby_lsp/requests/prepare_type_hierarchy.rb
         | 
| 161 162 | 
             
            - lib/ruby_lsp/requests/range_formatting.rb
         | 
| 162 163 | 
             
            - lib/ruby_lsp/requests/references.rb
         | 
| @@ -202,7 +203,6 @@ licenses: | |
| 202 203 | 
             
            metadata:
         | 
| 203 204 | 
             
              allowed_push_host: https://rubygems.org
         | 
| 204 205 | 
             
              documentation_uri: https://shopify.github.io/ruby-lsp/
         | 
| 205 | 
            -
            post_install_message:
         | 
| 206 206 | 
             
            rdoc_options: []
         | 
| 207 207 | 
             
            require_paths:
         | 
| 208 208 | 
             
            - lib
         | 
| @@ -217,8 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 217 217 | 
             
                - !ruby/object:Gem::Version
         | 
| 218 218 | 
             
                  version: '0'
         | 
| 219 219 | 
             
            requirements: []
         | 
| 220 | 
            -
            rubygems_version: 3. | 
| 221 | 
            -
            signing_key:
         | 
| 220 | 
            +
            rubygems_version: 3.6.2
         | 
| 222 221 | 
             
            specification_version: 4
         | 
| 223 222 | 
             
            summary: An opinionated language server for Ruby
         | 
| 224 223 | 
             
            test_files: []
         | 
| @@ -1,29 +0,0 @@ | |
| 1 | 
            -
            # typed: strict
         | 
| 2 | 
            -
            # frozen_string_literal: true
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            module RubyIndexer
         | 
| 5 | 
            -
              class IndexablePath
         | 
| 6 | 
            -
                extend T::Sig
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                sig { returns(T.nilable(String)) }
         | 
| 9 | 
            -
                attr_reader :require_path
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                sig { returns(String) }
         | 
| 12 | 
            -
                attr_reader :full_path
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                # An IndexablePath is instantiated with a load_path_entry and a full_path. The load_path_entry is where the file can
         | 
| 15 | 
            -
                # be found in the $LOAD_PATH, which we use to determine the require_path. The load_path_entry may be `nil` if the
         | 
| 16 | 
            -
                # indexer is configured to go through files that do not belong in the $LOAD_PATH. For example,
         | 
| 17 | 
            -
                # `sorbet/tapioca/require.rb` ends up being a part of the paths to be indexed because it's a Ruby file inside the
         | 
| 18 | 
            -
                # project, but the `sorbet` folder is not a part of the $LOAD_PATH. That means that both its load_path_entry and
         | 
| 19 | 
            -
                # require_path will be `nil`, since it cannot be required by the project
         | 
| 20 | 
            -
                sig { params(load_path_entry: T.nilable(String), full_path: String).void }
         | 
| 21 | 
            -
                def initialize(load_path_entry, full_path)
         | 
| 22 | 
            -
                  @full_path = full_path
         | 
| 23 | 
            -
                  @require_path = T.let(
         | 
| 24 | 
            -
                    load_path_entry ? full_path.delete_prefix("#{load_path_entry}/").delete_suffix(".rb") : nil,
         | 
| 25 | 
            -
                    T.nilable(String),
         | 
| 26 | 
            -
                  )
         | 
| 27 | 
            -
                end
         | 
| 28 | 
            -
              end
         | 
| 29 | 
            -
            end
         |