ruby-lsp 0.23.14 → 0.23.16
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-launcher +9 -1
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +6 -3
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +4 -2
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +60 -30
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +5 -4
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +5 -1
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +6 -4
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +139 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
- data/lib/ruby_indexer/test/method_test.rb +118 -118
- data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +64 -70
- data/lib/ruby_indexer/test/test_case.rb +2 -2
- data/lib/ruby_lsp/document.rb +6 -1
- data/lib/ruby_lsp/erb_document.rb +12 -4
- data/lib/ruby_lsp/global_state.rb +1 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +3 -3
- data/lib/ruby_lsp/listeners/completion.rb +24 -11
- data/lib/ruby_lsp/listeners/definition.rb +1 -1
- data/lib/ruby_lsp/listeners/document_link.rb +3 -1
- data/lib/ruby_lsp/listeners/document_symbol.rb +3 -3
- data/lib/ruby_lsp/listeners/folding_ranges.rb +8 -4
- data/lib/ruby_lsp/listeners/hover.rb +2 -2
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +12 -5
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -1
- data/lib/ruby_lsp/listeners/spec_style.rb +1 -1
- data/lib/ruby_lsp/listeners/test_style.rb +8 -8
- data/lib/ruby_lsp/requests/code_action_resolve.rb +14 -15
- data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/hover.rb +2 -2
- data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -2
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +1 -2
- data/lib/ruby_lsp/requests/references.rb +2 -1
- data/lib/ruby_lsp/requests/rename.rb +8 -5
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +4 -4
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +2 -2
- data/lib/ruby_lsp/requests/support/common.rb +3 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
- data/lib/ruby_lsp/requests/support/source_uri.rb +5 -3
- data/lib/ruby_lsp/response_builders/document_symbol.rb +3 -2
- data/lib/ruby_lsp/response_builders/hover.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/ruby_document.rb +2 -2
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +14 -5
- data/lib/ruby_lsp/setup_bundler.rb +7 -3
- data/lib/ruby_lsp/static_docs.rb +8 -1
- data/lib/ruby_lsp/store.rb +3 -2
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +164 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
- data/lib/ruby_lsp/type_inferrer.rb +4 -1
- data/lib/ruby_lsp/utils.rb +12 -2
- metadata +6 -6
- data/lib/ruby_lsp/ruby_lsp_reporter_plugin.rb +0 -109
- data/lib/ruby_lsp/test_reporter.rb +0 -207
- data/lib/ruby_lsp/test_unit_test_runner.rb +0 -98
| @@ -299,7 +299,7 @@ module RubyLsp | |
| 299 299 | 
             
                    methods = @index.resolve_method(message, type.name, inherited_only: inherited_only)
         | 
| 300 300 | 
             
                    return unless methods
         | 
| 301 301 |  | 
| 302 | 
            -
                    first_method =  | 
| 302 | 
            +
                    first_method = methods.first #: as !nil
         | 
| 303 303 |  | 
| 304 304 | 
             
                    title = "#{message}#{first_method.decorated_parameters}"
         | 
| 305 305 | 
             
                    title << first_method.formatted_signatures
         | 
| @@ -365,7 +365,7 @@ module RubyLsp | |
| 365 365 |  | 
| 366 366 | 
             
                    # We should only show hover for private constants if the constant is defined in the same namespace as the
         | 
| 367 367 | 
             
                    # reference
         | 
| 368 | 
            -
                    first_entry =  | 
| 368 | 
            +
                    first_entry = entries.first #: as !nil
         | 
| 369 369 | 
             
                    return if first_entry.private? && first_entry.name != "#{@node_context.fully_qualified_name}::#{name}"
         | 
| 370 370 |  | 
| 371 371 | 
             
                    categorized_markdown_from_index_entries(name, entries).each do |category, content|
         | 
| @@ -67,12 +67,18 @@ module RubyLsp | |
| 67 67 | 
             
                    return if special_method?(message)
         | 
| 68 68 |  | 
| 69 69 | 
             
                    if Requests::Support::Sorbet.annotation?(node)
         | 
| 70 | 
            -
                      @response_builder.add_token( | 
| 70 | 
            +
                      @response_builder.add_token(
         | 
| 71 | 
            +
                        node.message_loc, #: as !nil
         | 
| 72 | 
            +
                        :type,
         | 
| 73 | 
            +
                      )
         | 
| 71 74 | 
             
                    elsif !node.receiver && !node.opening_loc
         | 
| 72 75 | 
             
                      # If the node has a receiver, then the syntax is not ambiguous and semantic highlighting is not necessary to
         | 
| 73 76 | 
             
                      # determine that the token is a method call. The only ambiguous case is method calls with implicit self
         | 
| 74 77 | 
             
                      # receiver and no parenthesis, which may be confused with local variables
         | 
| 75 | 
            -
                      @response_builder.add_token( | 
| 78 | 
            +
                      @response_builder.add_token(
         | 
| 79 | 
            +
                        node.message_loc, #: as !nil
         | 
| 80 | 
            +
                        :method,
         | 
| 81 | 
            +
                      )
         | 
| 76 82 | 
             
                    end
         | 
| 77 83 | 
             
                  end
         | 
| 78 84 |  | 
| @@ -98,7 +104,7 @@ module RubyLsp | |
| 98 104 |  | 
| 99 105 | 
             
                  #: (Prism::DefNode node) -> void
         | 
| 100 106 | 
             
                  def on_def_node_leave(node)
         | 
| 101 | 
            -
                    @current_scope =  | 
| 107 | 
            +
                    @current_scope = @current_scope.parent #: as !nil
         | 
| 102 108 | 
             
                  end
         | 
| 103 109 |  | 
| 104 110 | 
             
                  #: (Prism::BlockNode node) -> void
         | 
| @@ -108,7 +114,7 @@ module RubyLsp | |
| 108 114 |  | 
| 109 115 | 
             
                  #: (Prism::BlockNode node) -> void
         | 
| 110 116 | 
             
                  def on_block_node_leave(node)
         | 
| 111 | 
            -
                    @current_scope =  | 
| 117 | 
            +
                    @current_scope = @current_scope.parent #: as !nil
         | 
| 112 118 | 
             
                  end
         | 
| 113 119 |  | 
| 114 120 | 
             
                  #: (Prism::BlockLocalVariableNode node) -> void
         | 
| @@ -301,7 +307,8 @@ module RubyLsp | |
| 301 307 | 
             
                    # For each capture name we find in the regexp, look for a local in the current_scope
         | 
| 302 308 | 
             
                    Regexp.new(content, Regexp::FIXEDENCODING).names.each do |name|
         | 
| 303 309 | 
             
                      # The +3 is to compensate for the "(?<" part of the capture name
         | 
| 304 | 
            -
                       | 
| 310 | 
            +
                      capture_name_index = content.index("(?<#{name}>") #: as !nil
         | 
| 311 | 
            +
                      capture_name_offset = capture_name_index + 3
         | 
| 305 312 | 
             
                      local_var_loc = loc.copy(start_offset: loc.start_offset + capture_name_offset, length: name.length)
         | 
| 306 313 |  | 
| 307 314 | 
             
                      local = @current_scope.lookup(name)
         | 
| @@ -69,7 +69,11 @@ module RubyLsp | |
| 69 69 | 
             
                      signature.matches?(arguments)
         | 
| 70 70 | 
             
                    end || 0
         | 
| 71 71 |  | 
| 72 | 
            -
                    parameter_length = [ | 
| 72 | 
            +
                    parameter_length = [
         | 
| 73 | 
            +
                      signatures[active_sig_index] #: as !nil
         | 
| 74 | 
            +
                        .parameters.length - 1,
         | 
| 75 | 
            +
                      0,
         | 
| 76 | 
            +
                    ].max
         | 
| 73 77 | 
             
                    active_parameter = (arguments.length - 1).clamp(0, parameter_length)
         | 
| 74 78 |  | 
| 75 79 | 
             
                    # If there are arguments, then we need to check if there's a trailing comma after the end of the last argument
         | 
| @@ -22,7 +22,7 @@ module RubyLsp | |
| 22 22 | 
             
                      queue = items.dup
         | 
| 23 23 |  | 
| 24 24 | 
             
                      until queue.empty?
         | 
| 25 | 
            -
                        item =  | 
| 25 | 
            +
                        item = queue.shift #: as !nil
         | 
| 26 26 | 
             
                        tags = Set.new(item[:tags])
         | 
| 27 27 | 
             
                        next unless tags.include?("framework:minitest") || tags.include?("framework:test_unit")
         | 
| 28 28 |  | 
| @@ -93,9 +93,9 @@ module RubyLsp | |
| 93 93 | 
             
                        if examples.empty?
         | 
| 94 94 | 
             
                          "^#{group_regex}(#|::)"
         | 
| 95 95 | 
             
                        elsif examples.length == 1
         | 
| 96 | 
            -
                          "^#{group_regex}##{examples[0]} | 
| 96 | 
            +
                          "^#{group_regex}##{examples[0]}\\$"
         | 
| 97 97 | 
             
                        else
         | 
| 98 | 
            -
                          "^#{group_regex}#(#{examples.join("|")}) | 
| 98 | 
            +
                          "^#{group_regex}#(#{examples.join("|")})\\$"
         | 
| 99 99 | 
             
                        end
         | 
| 100 100 | 
             
                      end
         | 
| 101 101 |  | 
| @@ -116,13 +116,13 @@ module RubyLsp | |
| 116 116 | 
             
                          Shellwords.escape(TestDiscovery::DYNAMIC_REFERENCE_MARKER),
         | 
| 117 117 | 
             
                          ".*",
         | 
| 118 118 | 
             
                        )
         | 
| 119 | 
            -
                        command = +"#{BASE_COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex} | 
| 119 | 
            +
                        command = +"#{BASE_COMMAND} -Itest #{file_path} --testcase \"/^#{group_regex}\\$/\""
         | 
| 120 120 |  | 
| 121 121 | 
             
                        unless examples.empty?
         | 
| 122 122 | 
             
                          command << if examples.length == 1
         | 
| 123 | 
            -
                            " --name \"/#{examples[0]} | 
| 123 | 
            +
                            " --name \"/#{examples[0]}\\$/\""
         | 
| 124 124 | 
             
                          else
         | 
| 125 | 
            -
                            " --name \"/(#{examples.join("|")}) | 
| 125 | 
            +
                            " --name \"/(#{examples.join("|")})\\$/\""
         | 
| 126 126 | 
             
                          end
         | 
| 127 127 | 
             
                        end
         | 
| 128 128 |  | 
| @@ -133,8 +133,8 @@ module RubyLsp | |
| 133 133 |  | 
| 134 134 | 
             
                  include Requests::Support::Common
         | 
| 135 135 |  | 
| 136 | 
            -
                  MINITEST_REPORTER_PATH = File.expand_path("../ | 
| 137 | 
            -
                  TEST_UNIT_REPORTER_PATH = File.expand_path("../ | 
| 136 | 
            +
                  MINITEST_REPORTER_PATH = File.expand_path("../test_reporters/minitest_reporter.rb", __dir__) #: String
         | 
| 137 | 
            +
                  TEST_UNIT_REPORTER_PATH = File.expand_path("../test_reporters/test_unit_reporter.rb", __dir__) #: String
         | 
| 138 138 | 
             
                  ACCESS_MODIFIERS = [:public, :private, :protected].freeze
         | 
| 139 139 | 
             
                  BASE_COMMAND = begin
         | 
| 140 140 | 
             
                    Bundler.with_original_env { Bundler.default_lockfile }
         | 
| @@ -97,7 +97,7 @@ module RubyLsp | |
| 97 97 | 
             
                    return Error::EmptySelection if source_range[:start] == source_range[:end]
         | 
| 98 98 |  | 
| 99 99 | 
             
                    start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
         | 
| 100 | 
            -
                    extracted_source =  | 
| 100 | 
            +
                    extracted_source = @document.source[start_index...end_index] #: as !nil
         | 
| 101 101 |  | 
| 102 102 | 
             
                    # Find the closest statements node, so that we place the refactor in a valid position
         | 
| 103 103 | 
             
                    node_context = RubyDocument
         | 
| @@ -115,10 +115,10 @@ module RubyLsp | |
| 115 115 |  | 
| 116 116 | 
             
                    # Find the node with the end line closest to the requested position, so that we can place the refactor
         | 
| 117 117 | 
             
                    # immediately after that closest node
         | 
| 118 | 
            -
                    closest_node =  | 
| 118 | 
            +
                    closest_node = closest_statements.child_nodes.compact.min_by do |node|
         | 
| 119 119 | 
             
                      distance = source_range.dig(:start, :line) - (node.location.end_line - 1)
         | 
| 120 120 | 
             
                      distance <= 0 ? Float::INFINITY : distance
         | 
| 121 | 
            -
                    end | 
| 121 | 
            +
                    end #: as !nil
         | 
| 122 122 |  | 
| 123 123 | 
             
                    return Error::InvalidTargetRange if closest_node.is_a?(Prism::MissingNode)
         | 
| 124 124 |  | 
| @@ -153,7 +153,8 @@ module RubyLsp | |
| 153 153 | 
             
                      indentation_line = lines[indentation_line_number]
         | 
| 154 154 | 
             
                      return Error::InvalidTargetRange unless indentation_line
         | 
| 155 155 |  | 
| 156 | 
            -
                      indentation =  | 
| 156 | 
            +
                      indentation = indentation_line[/\A */] #: as !nil
         | 
| 157 | 
            +
                        .size
         | 
| 157 158 |  | 
| 158 159 | 
             
                      target_range = {
         | 
| 159 160 | 
             
                        start: { line: target_line, character: indentation },
         | 
| @@ -195,7 +196,7 @@ module RubyLsp | |
| 195 196 | 
             
                    return Error::EmptySelection if source_range[:start] == source_range[:end]
         | 
| 196 197 |  | 
| 197 198 | 
             
                    start_index, end_index = @document.find_index_by_position(source_range[:start], source_range[:end])
         | 
| 198 | 
            -
                    extracted_source =  | 
| 199 | 
            +
                    extracted_source = @document.source[start_index...end_index] #: as !nil
         | 
| 199 200 |  | 
| 200 201 | 
             
                    # Find the closest method declaration node, so that we place the refactor in a valid position
         | 
| 201 202 | 
             
                    node_context = RubyDocument.locate(
         | 
| @@ -379,16 +380,14 @@ module RubyLsp | |
| 379 380 |  | 
| 380 381 | 
             
                    attribute_name = node.name[1..]
         | 
| 381 382 | 
             
                    indentation = " " * (closest_node.location.start_column + 2)
         | 
| 382 | 
            -
                    attribute_accessor_source =  | 
| 383 | 
            -
             | 
| 384 | 
            -
                       | 
| 385 | 
            -
             | 
| 386 | 
            -
                       | 
| 387 | 
            -
             | 
| 388 | 
            -
                       | 
| 389 | 
            -
             | 
| 390 | 
            -
                      end,
         | 
| 391 | 
            -
                    )
         | 
| 383 | 
            +
                    attribute_accessor_source = case @code_action[:title]
         | 
| 384 | 
            +
                    when CodeActions::CREATE_ATTRIBUTE_READER
         | 
| 385 | 
            +
                      "#{indentation}attr_reader :#{attribute_name}\n\n"
         | 
| 386 | 
            +
                    when CodeActions::CREATE_ATTRIBUTE_WRITER
         | 
| 387 | 
            +
                      "#{indentation}attr_writer :#{attribute_name}\n\n"
         | 
| 388 | 
            +
                    when CodeActions::CREATE_ATTRIBUTE_ACCESSOR
         | 
| 389 | 
            +
                      "#{indentation}attr_accessor :#{attribute_name}\n\n"
         | 
| 390 | 
            +
                    end #: as !nil
         | 
| 392 391 |  | 
| 393 392 | 
             
                    target_start_line = closest_node.location.start_line
         | 
| 394 393 | 
             
                    target_range = {
         | 
| @@ -51,7 +51,9 @@ module RubyLsp | |
| 51 51 | 
             
                      # But if it's a RBS signature starting with `#:`, we'll ignore it
         | 
| 52 52 | 
             
                      # so users can immediately continue typing the method definition
         | 
| 53 53 | 
             
                      if (comment_match = @previous_line.match(/^#(?!:)(\s*)/))
         | 
| 54 | 
            -
                        handle_comment_line( | 
| 54 | 
            +
                        handle_comment_line(
         | 
| 55 | 
            +
                          comment_match[1], #: as !nil
         | 
| 56 | 
            +
                        )
         | 
| 55 57 | 
             
                      elsif @document.syntax_error?
         | 
| 56 58 | 
             
                        match = /(<<((-|~)?))(?<quote>['"`]?)(?<delimiter>\w+)\k<quote>/.match(@previous_line)
         | 
| 57 59 | 
             
                        heredoc_delimiter = match && match.named_captures["delimiter"]
         | 
| @@ -76,7 +78,7 @@ module RubyLsp | |
| 76 78 | 
             
                    current_line = @lines[@position[:line]]
         | 
| 77 79 | 
             
                    return unless /((?<=do)|(?<={))\s+\|/.match?(current_line)
         | 
| 78 80 |  | 
| 79 | 
            -
                    line =  | 
| 81 | 
            +
                    line = current_line #: as !nil
         | 
| 80 82 |  | 
| 81 83 | 
             
                    # If the user inserts the closing pipe manually to the end of the block argument, we need to avoid adding
         | 
| 82 84 | 
             
                    # an additional one and remove the previous one.  This also helps to remove the user who accidentally
         | 
| @@ -49,8 +49,7 @@ module RubyLsp | |
| 49 49 |  | 
| 50 50 | 
             
                    # While the spec allows for multiple entries, VSCode seems to only support one
         | 
| 51 51 | 
             
                    # We'll just return the first one for now
         | 
| 52 | 
            -
                    first_entry =  | 
| 53 | 
            -
             | 
| 52 | 
            +
                    first_entry = entries.first #: as !nil
         | 
| 54 53 | 
             
                    range = range_from_location(first_entry.location)
         | 
| 55 54 |  | 
| 56 55 | 
             
                    [
         | 
| @@ -106,7 +106,8 @@ module RubyLsp | |
| 106 106 | 
             
                      entries = @global_state.index.resolve(name, node_context.nesting)
         | 
| 107 107 | 
             
                      return unless entries
         | 
| 108 108 |  | 
| 109 | 
            -
                      fully_qualified_name =  | 
| 109 | 
            +
                      fully_qualified_name = entries.first #: as !nil
         | 
| 110 | 
            +
                        .name
         | 
| 110 111 | 
             
                      RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
         | 
| 111 112 | 
             
                    when
         | 
| 112 113 | 
             
                      Prism::InstanceVariableAndWriteNode,
         | 
| @@ -63,10 +63,11 @@ module RubyLsp | |
| 63 63 | 
             
                    return unless entries
         | 
| 64 64 |  | 
| 65 65 | 
             
                    if (conflict_entries = @global_state.index.resolve(@new_name, node_context.nesting))
         | 
| 66 | 
            -
                      raise InvalidNameError, "The new name is already in use by #{ | 
| 66 | 
            +
                      raise InvalidNameError, "The new name is already in use by #{conflict_entries.first&.name}"
         | 
| 67 67 | 
             
                    end
         | 
| 68 68 |  | 
| 69 | 
            -
                    fully_qualified_name =  | 
| 69 | 
            +
                    fully_qualified_name = entries.first #: as !nil
         | 
| 70 | 
            +
                      .name
         | 
| 70 71 | 
             
                    reference_target = RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
         | 
| 71 72 | 
             
                    changes = collect_text_edits(reference_target, name)
         | 
| 72 73 |  | 
| @@ -97,9 +98,9 @@ module RubyLsp | |
| 97 98 | 
             
                    # rename the files for the user.
         | 
| 98 99 | 
             
                    #
         | 
| 99 100 | 
             
                    # We also look for an associated test file and rename it too
         | 
| 100 | 
            -
                    short_name =  | 
| 101 | 
            +
                    short_name = fully_qualified_name.split("::").last #: as !nil
         | 
| 101 102 |  | 
| 102 | 
            -
                     | 
| 103 | 
            +
                    @global_state.index[fully_qualified_name]&.each do |entry|
         | 
| 103 104 | 
             
                      # Do not rename files that are not part of the workspace
         | 
| 104 105 | 
             
                      uri = entry.uri
         | 
| 105 106 | 
             
                      file_path = uri.full_path
         | 
| @@ -112,7 +113,9 @@ module RubyLsp | |
| 112 113 | 
             
                        file_name = file_from_constant_name(short_name)
         | 
| 113 114 |  | 
| 114 115 | 
             
                        if "#{file_name}.rb" == entry.file_name
         | 
| 115 | 
            -
                          new_file_name = file_from_constant_name( | 
| 116 | 
            +
                          new_file_name = file_from_constant_name(
         | 
| 117 | 
            +
                            @new_name.split("::").last, #: as !nil
         | 
| 118 | 
            +
                          )
         | 
| 116 119 |  | 
| 117 120 | 
             
                          new_uri = URI::Generic.from_path(path: File.join(
         | 
| 118 121 | 
             
                            File.dirname(file_path),
         | 
| @@ -32,7 +32,7 @@ module RubyLsp | |
| 32 32 | 
             
                      next unless node
         | 
| 33 33 |  | 
| 34 34 | 
             
                      range = Support::SelectionRange.new(range: range_from_location(node.location), parent: parent)
         | 
| 35 | 
            -
                       | 
| 35 | 
            +
                      queue.unshift(*node.child_nodes.map { |child| [child, range] })
         | 
| 36 36 | 
             
                      @ranges.unshift(range)
         | 
| 37 37 | 
             
                    end
         | 
| 38 38 |  | 
| @@ -43,8 +43,8 @@ module RubyLsp | |
| 43 43 | 
             
                      # Filter the tokens based on the first different position. This must happen at this stage, before we try to
         | 
| 44 44 | 
             
                      # find the next position from the end or else we risk confusing sets of token that may have different lengths,
         | 
| 45 45 | 
             
                      # but end with the exact same token
         | 
| 46 | 
            -
                      old_tokens =  | 
| 47 | 
            -
                      new_tokens =  | 
| 46 | 
            +
                      old_tokens = previous_tokens[first_different_position...] #: as !nil
         | 
| 47 | 
            +
                      new_tokens = current_tokens[first_different_position...] #: as !nil
         | 
| 48 48 |  | 
| 49 49 | 
             
                      # Then search from the end to find the first token that doesn't match. Since the user is normally editing the
         | 
| 50 50 | 
             
                      # middle of the file, this will minimize the number of edits since the end of the token array has not changed
         | 
| @@ -53,8 +53,8 @@ module RubyLsp | |
| 53 53 | 
             
                      end || 0
         | 
| 54 54 |  | 
| 55 55 | 
             
                      # Filter the old and new tokens to only the section that will be replaced/inserted/deleted
         | 
| 56 | 
            -
                      old_tokens =  | 
| 57 | 
            -
                      new_tokens =  | 
| 56 | 
            +
                      old_tokens = old_tokens[...old_tokens.length - first_different_token_from_end] #: as !nil
         | 
| 57 | 
            +
                      new_tokens = new_tokens[...new_tokens.length - first_different_token_from_end] #: as !nil
         | 
| 58 58 |  | 
| 59 59 | 
             
                      # And we send back a single edit, replacing an entire section for the new tokens
         | 
| 60 60 | 
             
                      Interface::SemanticTokensDelta.new(
         | 
| @@ -29,7 +29,7 @@ module RubyLsp | |
| 29 29 |  | 
| 30 30 | 
             
                  #: -> String
         | 
| 31 31 | 
             
                  def ast_for_range
         | 
| 32 | 
            -
                    range =  | 
| 32 | 
            +
                    range = @range #: as !nil
         | 
| 33 33 | 
             
                    start_char, end_char = @document.find_index_by_position(range[:start], range[:end])
         | 
| 34 34 |  | 
| 35 35 | 
             
                    queue = @tree.statements.body.dup
         | 
| @@ -46,7 +46,7 @@ module RubyLsp | |
| 46 46 | 
             
                      if (start_char..end_char).cover?(loc.start_offset..loc.end_offset)
         | 
| 47 47 | 
             
                        found_nodes << node
         | 
| 48 48 | 
             
                      else
         | 
| 49 | 
            -
                         | 
| 49 | 
            +
                        queue.unshift(*node.child_nodes)
         | 
| 50 50 | 
             
                      end
         | 
| 51 51 | 
             
                    end
         | 
| 52 52 |  | 
| @@ -54,7 +54,9 @@ module RubyLsp | |
| 54 54 | 
             
                    #: (String file_path) -> bool?
         | 
| 55 55 | 
             
                    def not_in_dependencies?(file_path)
         | 
| 56 56 | 
             
                      BUNDLE_PATH &&
         | 
| 57 | 
            -
                        !file_path.start_with?( | 
| 57 | 
            +
                        !file_path.start_with?(
         | 
| 58 | 
            +
                          BUNDLE_PATH, #: as !nil
         | 
| 59 | 
            +
                        ) &&
         | 
| 58 60 | 
             
                        !file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
         | 
| 59 61 | 
             
                    end
         | 
| 60 62 |  | 
| @@ -21,7 +21,7 @@ module RubyLsp | |
| 21 21 | 
             
                    # @override
         | 
| 22 22 | 
             
                    #: (URI::Generic uri, RubyDocument document) -> String?
         | 
| 23 23 | 
             
                    def run_formatting(uri, document)
         | 
| 24 | 
            -
                      filename =  | 
| 24 | 
            +
                      filename = uri.to_standardized_path || uri.opaque #: as !nil
         | 
| 25 25 |  | 
| 26 26 | 
             
                      # Invoke RuboCop with just this file in `paths`
         | 
| 27 27 | 
             
                      @format_runner.run(filename, document.source)
         | 
| @@ -38,7 +38,7 @@ module RubyLsp | |
| 38 38 | 
             
                    # @override
         | 
| 39 39 | 
             
                    #: (URI::Generic uri, RubyDocument document) -> Array[Interface::Diagnostic]?
         | 
| 40 40 | 
             
                    def run_diagnostic(uri, document)
         | 
| 41 | 
            -
                      filename =  | 
| 41 | 
            +
                      filename = uri.to_standardized_path || uri.opaque #: as !nil
         | 
| 42 42 | 
             
                      # Invoke RuboCop with just this file in `paths`
         | 
| 43 43 | 
             
                      @diagnostic_runner.run(filename, document.source)
         | 
| 44 44 |  | 
| @@ -21,8 +21,10 @@ module URI | |
| 21 21 | 
             
                # have the uri gem in their own bundle and thus not use a compatible version.
         | 
| 22 22 | 
             
                PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER #: RFC2396_Parser
         | 
| 23 23 |  | 
| 24 | 
            -
                 | 
| 25 | 
            -
             | 
| 24 | 
            +
                self #: as untyped # rubocop:disable Style/RedundantSelf
         | 
| 25 | 
            +
                  .alias_method(:gem_name, :host)
         | 
| 26 | 
            +
                self #: as untyped # rubocop:disable Style/RedundantSelf
         | 
| 27 | 
            +
                  .alias_method(:line_number, :fragment)
         | 
| 26 28 |  | 
| 27 29 | 
             
                #: String?
         | 
| 28 30 | 
             
                attr_reader :gem_version
         | 
| @@ -71,7 +73,7 @@ module URI | |
| 71 73 | 
             
                if URI.respond_to?(:register_scheme)
         | 
| 72 74 | 
             
                  URI.register_scheme("SOURCE", self)
         | 
| 73 75 | 
             
                else
         | 
| 74 | 
            -
                  @@schemes = @@schemes # rubocop:disable Style/ClassVars | 
| 76 | 
            +
                  @@schemes = @@schemes #: Hash[String, untyped] # rubocop:disable Style/ClassVars
         | 
| 75 77 | 
             
                  @@schemes["SOURCE"] = self
         | 
| 76 78 | 
             
                end
         | 
| 77 79 | 
             
              end
         | 
| @@ -38,13 +38,14 @@ module RubyLsp | |
| 38 38 |  | 
| 39 39 | 
             
                  #: -> (SymbolHierarchyRoot | Interface::DocumentSymbol)
         | 
| 40 40 | 
             
                  def last
         | 
| 41 | 
            -
                     | 
| 41 | 
            +
                    @stack.last #: as !nil
         | 
| 42 42 | 
             
                  end
         | 
| 43 43 |  | 
| 44 44 | 
             
                  # @override
         | 
| 45 45 | 
             
                  #: -> Array[Interface::DocumentSymbol]
         | 
| 46 46 | 
             
                  def response
         | 
| 47 | 
            -
                     | 
| 47 | 
            +
                    @stack.first #: as !nil
         | 
| 48 | 
            +
                      .children
         | 
| 48 49 | 
             
                  end
         | 
| 49 50 | 
             
                end
         | 
| 50 51 | 
             
              end
         | 
| @@ -35,7 +35,7 @@ module RubyLsp | |
| 35 35 | 
             
                  # @override
         | 
| 36 36 | 
             
                  #: -> ResponseType
         | 
| 37 37 | 
             
                  def response
         | 
| 38 | 
            -
                    result =  | 
| 38 | 
            +
                    result = @response[:title] #: as !nil
         | 
| 39 39 | 
             
                    result << "\n" << @response[:links] if @response[:links]
         | 
| 40 40 | 
             
                    result << "\n" << @response[:documentation] if @response[:documentation]
         | 
| 41 41 |  | 
| @@ -64,7 +64,7 @@ module RubyLsp | |
| 64 64 | 
             
                        start_line: location.start_line,
         | 
| 65 65 | 
             
                        start_code_unit_column: location.cached_start_code_units_column(@code_units_cache),
         | 
| 66 66 | 
             
                        length: length,
         | 
| 67 | 
            -
                        type:  | 
| 67 | 
            +
                        type: TOKEN_TYPES[type], #: as !nil
         | 
| 68 68 | 
             
                        modifier: modifiers_indices,
         | 
| 69 69 | 
             
                      ),
         | 
| 70 70 | 
             
                    )
         | 
| @@ -53,7 +53,7 @@ module RubyLsp | |
| 53 53 | 
             
                      # Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
         | 
| 54 54 | 
             
                      # same order as the visiting mechanism, which means searching the child nodes before moving on to the next
         | 
| 55 55 | 
             
                      # sibling
         | 
| 56 | 
            -
                       | 
| 56 | 
            +
                      queue.unshift(*candidate.child_nodes)
         | 
| 57 57 |  | 
| 58 58 | 
             
                      # Skip if the current node doesn't cover the desired position
         | 
| 59 59 | 
             
                      loc = candidate.location
         | 
| @@ -195,7 +195,7 @@ module RubyLsp | |
| 195 195 | 
             
                    # Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
         | 
| 196 196 | 
             
                    # same order as the visiting mechanism, which means searching the child nodes before moving on to the next
         | 
| 197 197 | 
             
                    # sibling
         | 
| 198 | 
            -
                     | 
| 198 | 
            +
                    queue.unshift(*candidate.child_nodes)
         | 
| 199 199 |  | 
| 200 200 | 
             
                    # Skip if the current node doesn't cover the desired position
         | 
| 201 201 | 
             
                    loc = candidate.location
         | 
| @@ -13,8 +13,10 @@ def compose(raw_initialize) | |
| 13 13 | 
             
              workspace_path ||= Dir.pwd
         | 
| 14 14 |  | 
| 15 15 | 
             
              env = RubyLsp::SetupBundler.new(workspace_path, launcher: true).setup!
         | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
                 | 
| 19 | 
            -
             | 
| 16 | 
            +
             | 
| 17 | 
            +
              File.open(File.join(".ruby-lsp", "bundle_env"), "w") do |f|
         | 
| 18 | 
            +
                f.flock(File::LOCK_EX)
         | 
| 19 | 
            +
                f.write(env.map { |k, v| "#{k}=#{v}" }.join("\n"))
         | 
| 20 | 
            +
                f.flush
         | 
| 21 | 
            +
              end
         | 
| 20 22 | 
             
            end
         | 
    
        data/lib/ruby_lsp/server.rb
    CHANGED
    
    | @@ -223,7 +223,8 @@ module RubyLsp | |
| 223 223 | 
             
                  configured_features = options.dig(:initializationOptions, :enabledFeatures)
         | 
| 224 224 |  | 
| 225 225 | 
             
                  configured_hints = options.dig(:initializationOptions, :featuresConfiguration, :inlayHint)
         | 
| 226 | 
            -
                   | 
| 226 | 
            +
                  @store.features_configuration.dig(:inlayHint) #: as !nil
         | 
| 227 | 
            +
                    .configuration.merge!(configured_hints) if configured_hints
         | 
| 227 228 |  | 
| 228 229 | 
             
                  enabled_features = case configured_features
         | 
| 229 230 | 
             
                  when Array
         | 
| @@ -294,6 +295,7 @@ module RubyLsp | |
| 294 295 | 
             
                        addon_detection: true,
         | 
| 295 296 | 
             
                        compose_bundle: true,
         | 
| 296 297 | 
             
                        go_to_relevant_file: true,
         | 
| 298 | 
            +
                        full_test_discovery: true,
         | 
| 297 299 | 
             
                      },
         | 
| 298 300 | 
             
                    ),
         | 
| 299 301 | 
             
                    serverInfo: {
         | 
| @@ -488,7 +490,11 @@ module RubyLsp | |
| 488 490 | 
             
                  document_symbol = Requests::DocumentSymbol.new(uri, dispatcher)
         | 
| 489 491 | 
             
                  document_link = Requests::DocumentLink.new(uri, parse_result.comments, dispatcher)
         | 
| 490 492 | 
             
                  code_lens = Requests::CodeLens.new(@global_state, uri, dispatcher)
         | 
| 491 | 
            -
                  inlay_hint = Requests::InlayHints.new( | 
| 493 | 
            +
                  inlay_hint = Requests::InlayHints.new(
         | 
| 494 | 
            +
                    document,
         | 
| 495 | 
            +
                    @store.features_configuration.dig(:inlayHint), #: as !nil
         | 
| 496 | 
            +
                    dispatcher,
         | 
| 497 | 
            +
                  )
         | 
| 492 498 |  | 
| 493 499 | 
             
                  if document.is_a?(RubyDocument) && document.should_index?
         | 
| 494 500 | 
             
                    # Re-index the file as it is modified. This mode of indexing updates entries only. Require path trees are only
         | 
| @@ -815,7 +821,7 @@ module RubyLsp | |
| 815 821 | 
             
                    return
         | 
| 816 822 | 
             
                  end
         | 
| 817 823 |  | 
| 818 | 
            -
                  hints_configurations =  | 
| 824 | 
            +
                  hints_configurations = @store.features_configuration.dig(:inlayHint) #: as !nil
         | 
| 819 825 | 
             
                  dispatcher = Prism::Dispatcher.new
         | 
| 820 826 |  | 
| 821 827 | 
             
                  unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
         | 
| @@ -1056,7 +1062,8 @@ module RubyLsp | |
| 1056 1062 | 
             
                  end
         | 
| 1057 1063 |  | 
| 1058 1064 | 
             
                  Addon.file_watcher_addons.each do |addon|
         | 
| 1059 | 
            -
                     | 
| 1065 | 
            +
                    addon #: as untyped
         | 
| 1066 | 
            +
                      .workspace_did_change_watched_files(changes)
         | 
| 1060 1067 | 
             
                  rescue => e
         | 
| 1061 1068 | 
             
                    send_log_message(
         | 
| 1062 1069 | 
             
                      "Error in #{addon.name} add-on while processing watched file notifications: #{e.full_message}",
         | 
| @@ -1393,7 +1400,9 @@ module RubyLsp | |
| 1393 1400 | 
             
                      Open3.capture3(
         | 
| 1394 1401 | 
             
                        Gem.ruby,
         | 
| 1395 1402 | 
             
                        "-I",
         | 
| 1396 | 
            -
                        File.dirname( | 
| 1403 | 
            +
                        File.dirname(
         | 
| 1404 | 
            +
                          __dir__, #: as !nil
         | 
| 1405 | 
            +
                        ),
         | 
| 1397 1406 | 
             
                        File.expand_path("../../exe/ruby-lsp-launcher", __dir__),
         | 
| 1398 1407 | 
             
                        @global_state.workspace_uri.to_s,
         | 
| 1399 1408 | 
             
                        chdir: @global_state.workspace_path,
         | 
| @@ -260,7 +260,8 @@ module RubyLsp | |
| 260 260 | 
             
                  # The ENV can only be merged after checking if an update is required because we depend on the original value of
         | 
| 261 261 | 
             
                  # ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
         | 
| 262 262 | 
             
                  should_update = should_bundle_update?
         | 
| 263 | 
            -
                   | 
| 263 | 
            +
                  ENV #: as untyped
         | 
| 264 | 
            +
                    .merge!(env)
         | 
| 264 265 |  | 
| 265 266 | 
             
                  unless should_update && !force_install
         | 
| 266 267 | 
             
                    Bundler::CLI::Install.new({ "no-cache" => true }).run
         | 
| @@ -405,12 +406,15 @@ module RubyLsp | |
| 405 406 | 
             
                def correct_relative_remote_paths
         | 
| 406 407 | 
             
                  content = @custom_lockfile.read
         | 
| 407 408 | 
             
                  content.gsub!(/remote: (.*)/) do |match|
         | 
| 408 | 
            -
                     | 
| 409 | 
            +
                    last_match = Regexp.last_match #: as !nil
         | 
| 410 | 
            +
                    path = last_match[1]
         | 
| 409 411 |  | 
| 410 412 | 
             
                    # We should only apply the correction if the remote is a relative path. It might also be a URI, like
         | 
| 411 413 | 
             
                    # `https://rubygems.org` or an absolute path, in which case we shouldn't do anything
         | 
| 412 414 | 
             
                    if path && !URI(path).scheme
         | 
| 413 | 
            -
                       | 
| 415 | 
            +
                      bundle_dir = @gemfile #: as !nil
         | 
| 416 | 
            +
                        .dirname
         | 
| 417 | 
            +
                      "remote: #{File.expand_path(path, bundle_dir)}"
         | 
| 414 418 | 
             
                    else
         | 
| 415 419 | 
             
                      match
         | 
| 416 420 | 
             
                    end
         | 
    
        data/lib/ruby_lsp/static_docs.rb
    CHANGED
    
    | @@ -3,7 +3,14 @@ | |
| 3 3 |  | 
| 4 4 | 
             
            module RubyLsp
         | 
| 5 5 | 
             
              # The path to the `static_docs` directory, where we keep long-form static documentation
         | 
| 6 | 
            -
              STATIC_DOCS_PATH = File.join( | 
| 6 | 
            +
              STATIC_DOCS_PATH = File.join(
         | 
| 7 | 
            +
                File.dirname(
         | 
| 8 | 
            +
                  File.dirname(
         | 
| 9 | 
            +
                    __dir__, #: as !nil
         | 
| 10 | 
            +
                  ),
         | 
| 11 | 
            +
                ),
         | 
| 12 | 
            +
                "static_docs",
         | 
| 13 | 
            +
              ) #: String
         | 
| 7 14 |  | 
| 8 15 | 
             
              # A map of keyword => short documentation to be displayed on hover or completion
         | 
| 9 16 | 
             
              KEYWORD_DOCS = {
         | 
    
        data/lib/ruby_lsp/store.rb
    CHANGED
    
    | @@ -46,7 +46,7 @@ module RubyLsp | |
| 46 46 | 
             
                  end
         | 
| 47 47 |  | 
| 48 48 | 
             
                  set(uri: uri, source: File.binread(path), version: 0, language_id: language_id)
         | 
| 49 | 
            -
                   | 
| 49 | 
            +
                  @state[uri.to_s] #: as !nil
         | 
| 50 50 | 
             
                rescue Errno::ENOENT
         | 
| 51 51 | 
             
                  raise NonExistingDocumentError, uri.to_s
         | 
| 52 52 | 
             
                end
         | 
| @@ -65,7 +65,8 @@ module RubyLsp | |
| 65 65 |  | 
| 66 66 | 
             
                #: (uri: URI::Generic, edits: Array[Hash[Symbol, untyped]], version: Integer) -> void
         | 
| 67 67 | 
             
                def push_edits(uri:, edits:, version:)
         | 
| 68 | 
            -
                   | 
| 68 | 
            +
                  @state[uri.to_s] #: as !nil
         | 
| 69 | 
            +
                    .push_edits(edits, version: version)
         | 
| 69 70 | 
             
                end
         | 
| 70 71 |  | 
| 71 72 | 
             
                #: -> void
         |