ruby-lsp 0.20.1 → 0.21.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/README.md +2 -2
 - data/VERSION +1 -1
 - data/exe/ruby-lsp +18 -3
 - data/exe/ruby-lsp-launcher +127 -0
 - data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +56 -2
 - data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +21 -6
 - data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
 - data/lib/ruby_indexer/lib/ruby_indexer/index.rb +5 -5
 - data/lib/ruby_indexer/test/classes_and_modules_test.rb +2 -2
 - data/lib/ruby_indexer/test/enhancements_test.rb +51 -19
 - data/lib/ruby_indexer/test/index_test.rb +2 -2
 - data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
 - data/lib/ruby_indexer/test/method_test.rb +26 -0
 - data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
 - data/lib/ruby_lsp/addon.rb +9 -2
 - data/lib/ruby_lsp/base_server.rb +14 -5
 - data/lib/ruby_lsp/client_capabilities.rb +60 -0
 - data/lib/ruby_lsp/document.rb +1 -1
 - data/lib/ruby_lsp/global_state.rb +25 -19
 - data/lib/ruby_lsp/internal.rb +2 -0
 - data/lib/ruby_lsp/listeners/completion.rb +62 -0
 - data/lib/ruby_lsp/listeners/definition.rb +48 -13
 - data/lib/ruby_lsp/listeners/hover.rb +52 -0
 - data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
 - data/lib/ruby_lsp/requests/completion.rb +7 -1
 - data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
 - data/lib/ruby_lsp/requests/definition.rb +26 -11
 - data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
 - data/lib/ruby_lsp/requests/hover.rb +24 -6
 - data/lib/ruby_lsp/requests/rename.rb +1 -1
 - data/lib/ruby_lsp/requests/request.rb +1 -1
 - data/lib/ruby_lsp/requests/support/rubocop_runner.rb +11 -1
 - data/lib/ruby_lsp/scripts/compose_bundle.rb +20 -0
 - data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +8 -0
 - data/lib/ruby_lsp/server.rb +54 -16
 - data/lib/ruby_lsp/setup_bundler.rb +111 -22
 - data/lib/ruby_lsp/utils.rb +8 -0
 - metadata +8 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: c270ba4b6a7348ccb821e1bae776f4bd9ac704973b251791d2104d79861878c1
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5d27d76eca727dca7e439aa177db04fb3ec17e1169c19a267ba2cffc8f9e19a3
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8502d09966be4e7ec79c276b4d078edc0f4e150c86ff24bb6e6f5b9c28e4f89393760983b7344c108e356a5bc79daa314806c69b81441338465b00f8bb693cc1
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 0d84c86826022c8e71311bef8070f8b5364cd45f10f242fcb5b8cf96f9dd391a9a45a034b264ee8b2cec236ce69257a1a102d9e769bbdf3b8c0dc9baff8650e9
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -29,8 +29,8 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Shopif 
     | 
|
| 
       29 
29 
     | 
    
         
             
            be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor
         
     | 
| 
       30 
30 
     | 
    
         
             
            Covenant](CODE_OF_CONDUCT.md) code of conduct.
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            If you wish to contribute, see [ 
     | 
| 
       33 
     | 
    
         
            -
            [roadmap 
     | 
| 
      
 32 
     | 
    
         
            +
            If you wish to contribute, see [Contributing](https://shopify.github.io/ruby-lsp/contributing.html) for development instructions and check out our
         
     | 
| 
      
 33 
     | 
    
         
            +
            [Design and roadmap](https://shopify.github.io/ruby-lsp/design-and-roadmap.html) for a list of tasks to get started.
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
            ## License
         
     | 
| 
       36 
36 
     | 
    
         | 
    
        data/VERSION
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            0. 
     | 
| 
      
 1 
     | 
    
         
            +
            0.21.1
         
     | 
    
        data/exe/ruby-lsp
    CHANGED
    
    | 
         @@ -33,6 +33,10 @@ parser = OptionParser.new do |opts| 
     | 
|
| 
       33 
33 
     | 
    
         
             
                options[:doctor] = true
         
     | 
| 
       34 
34 
     | 
    
         
             
              end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
              opts.on("--use-launcher", "[EXPERIMENTAL] Use launcher mechanism to handle missing dependencies gracefully") do
         
     | 
| 
      
 37 
     | 
    
         
            +
                options[:launcher] = true
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       36 
40 
     | 
    
         
             
              opts.on("-h", "--help", "Print this help") do
         
     | 
| 
       37 
41 
     | 
    
         
             
                puts opts.help
         
     | 
| 
       38 
42 
     | 
    
         
             
                puts
         
     | 
| 
         @@ -54,6 +58,17 @@ end 
     | 
|
| 
       54 
58 
     | 
    
         
             
            # using `BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp` so that we have access to the gems that are a part of
         
     | 
| 
       55 
59 
     | 
    
         
             
            # the application's bundle
         
     | 
| 
       56 
60 
     | 
    
         
             
            if ENV["BUNDLE_GEMFILE"].nil?
         
     | 
| 
      
 61 
     | 
    
         
            +
              # Substitute the current process by the launcher. RubyGems activates all dependencies of a gem's executable eagerly,
         
     | 
| 
      
 62 
     | 
    
         
            +
              # but we can't have that happen because we want to invoke Bundler.setup ourselves with the composed bundle and avoid
         
     | 
| 
      
 63 
     | 
    
         
            +
              # duplicate spec activation errors. Replacing the process with the launcher executable will clear the activated specs,
         
     | 
| 
      
 64 
     | 
    
         
            +
              # which gives us the opportunity to control which specs are activated and enter degraded mode if any gems failed to
         
     | 
| 
      
 65 
     | 
    
         
            +
              # install rather than failing to boot the server completely
         
     | 
| 
      
 66 
     | 
    
         
            +
              if options[:launcher]
         
     | 
| 
      
 67 
     | 
    
         
            +
                command = +File.expand_path("ruby-lsp-launcher", __dir__)
         
     | 
| 
      
 68 
     | 
    
         
            +
                command << " --debug" if options[:debug]
         
     | 
| 
      
 69 
     | 
    
         
            +
                exit exec(command)
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
       57 
72 
     | 
    
         
             
              require_relative "../lib/ruby_lsp/setup_bundler"
         
     | 
| 
       58 
73 
     | 
    
         | 
| 
       59 
74 
     | 
    
         
             
              begin
         
     | 
| 
         @@ -69,12 +84,12 @@ if ENV["BUNDLE_GEMFILE"].nil? 
     | 
|
| 
       69 
84 
     | 
    
         
             
                "bundle"
         
     | 
| 
       70 
85 
     | 
    
         
             
              end
         
     | 
| 
       71 
86 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
              exit exec(env, "#{base_bundle} exec ruby-lsp #{original_args.join(" ")}")
         
     | 
| 
      
 87 
     | 
    
         
            +
              exit exec(env, "#{base_bundle} exec ruby-lsp #{original_args.join(" ")}".strip)
         
     | 
| 
       73 
88 
     | 
    
         
             
            end
         
     | 
| 
       74 
89 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
            require "ruby_lsp/load_sorbet"
         
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
90 
     | 
    
         
             
            $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
            require "ruby_lsp/load_sorbet"
         
     | 
| 
       78 
93 
     | 
    
         
             
            require "ruby_lsp/internal"
         
     | 
| 
       79 
94 
     | 
    
         | 
| 
       80 
95 
     | 
    
         
             
            T::Utils.run_all_sig_blocks
         
     | 
| 
         @@ -0,0 +1,127 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # !!!!!!!
         
     | 
| 
      
 5 
     | 
    
         
            +
            # No gems can be required in this file until we invoke bundler setup except inside the forked process that sets up the
         
     | 
| 
      
 6 
     | 
    
         
            +
            # composed bundle
         
     | 
| 
      
 7 
     | 
    
         
            +
            # !!!!!!!
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            setup_error = nil
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            # Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
         
     | 
| 
      
 12 
     | 
    
         
            +
            # Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
         
     | 
| 
      
 13 
     | 
    
         
            +
            # to ensure that we're setting up the bundle in the right place
         
     | 
| 
      
 14 
     | 
    
         
            +
            $stdin.binmode
         
     | 
| 
      
 15 
     | 
    
         
            +
            headers = $stdin.gets("\r\n\r\n")
         
     | 
| 
      
 16 
     | 
    
         
            +
            content_length = headers[/Content-Length: (\d+)/i, 1].to_i
         
     | 
| 
      
 17 
     | 
    
         
            +
            raw_initialize = $stdin.read(content_length)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            # Compose the Ruby LSP bundle in a forked process so that we can require gems without polluting the main process
         
     | 
| 
      
 20 
     | 
    
         
            +
            # `$LOAD_PATH` and `Gem.loaded_specs`. Windows doesn't support forking, so we need a separate path to support it
         
     | 
| 
      
 21 
     | 
    
         
            +
            pid = if Gem.win_platform?
         
     | 
| 
      
 22 
     | 
    
         
            +
              # Since we can't fork on Windows and spawn won't carry over the existing load paths, we need to explicitly pass that
         
     | 
| 
      
 23 
     | 
    
         
            +
              # down to the child process or else requiring gems during composing the bundle will fail
         
     | 
| 
      
 24 
     | 
    
         
            +
              load_path = $LOAD_PATH.flat_map do |path|
         
     | 
| 
      
 25 
     | 
    
         
            +
                ["-I", File.expand_path(path)]
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              Process.spawn(
         
     | 
| 
      
 29 
     | 
    
         
            +
                Gem.ruby,
         
     | 
| 
      
 30 
     | 
    
         
            +
                *load_path,
         
     | 
| 
      
 31 
     | 
    
         
            +
                File.expand_path("../lib/ruby_lsp/scripts/compose_bundle_windows.rb", __dir__),
         
     | 
| 
      
 32 
     | 
    
         
            +
                raw_initialize,
         
     | 
| 
      
 33 
     | 
    
         
            +
              )
         
     | 
| 
      
 34 
     | 
    
         
            +
            else
         
     | 
| 
      
 35 
     | 
    
         
            +
              fork do
         
     | 
| 
      
 36 
     | 
    
         
            +
                require_relative "../lib/ruby_lsp/scripts/compose_bundle"
         
     | 
| 
      
 37 
     | 
    
         
            +
                compose(raw_initialize)
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            begin
         
     | 
| 
      
 42 
     | 
    
         
            +
              # Wait until the composed Bundle is finished
         
     | 
| 
      
 43 
     | 
    
         
            +
              Process.wait(pid)
         
     | 
| 
      
 44 
     | 
    
         
            +
            rescue Errno::ECHILD
         
     | 
| 
      
 45 
     | 
    
         
            +
              # In theory, the child process can finish before we even get to the wait call, but that is not an error
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            begin
         
     | 
| 
      
 49 
     | 
    
         
            +
              bundle_env_path = File.join(".ruby-lsp", "bundle_env")
         
     | 
| 
      
 50 
     | 
    
         
            +
              # We can't require `bundler/setup` because that file prematurely exits the process if setup fails. However, we can't
         
     | 
| 
      
 51 
     | 
    
         
            +
              # simply require bundler either because the version required might conflict with the one locked in the composed
         
     | 
| 
      
 52 
     | 
    
         
            +
              # bundle. We need the composed bundle sub-process to inform us of the locked Bundler version, so that we can then
         
     | 
| 
      
 53 
     | 
    
         
            +
              # activate the right spec and require the exact Bundler version required by the app
         
     | 
| 
      
 54 
     | 
    
         
            +
              if File.exist?(bundle_env_path)
         
     | 
| 
      
 55 
     | 
    
         
            +
                env = File.readlines(bundle_env_path).to_h { |line| line.chomp.split("=", 2) }
         
     | 
| 
      
 56 
     | 
    
         
            +
                ENV.merge!(env)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                if env["BUNDLER_VERSION"]
         
     | 
| 
      
 59 
     | 
    
         
            +
                  Gem::Specification.find_by_name("bundler", env["BUNDLER_VERSION"]).activate
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                require "bundler"
         
     | 
| 
      
 63 
     | 
    
         
            +
                Bundler.ui.level = :silent
         
     | 
| 
      
 64 
     | 
    
         
            +
                Bundler.setup
         
     | 
| 
      
 65 
     | 
    
         
            +
                $stderr.puts("Composed Bundle set up successfully")
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
            rescue StandardError => e
         
     | 
| 
      
 68 
     | 
    
         
            +
              # If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most
         
     | 
| 
      
 69 
     | 
    
         
            +
              # features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be
         
     | 
| 
      
 70 
     | 
    
         
            +
              # missing, but we respect the LSP life cycle
         
     | 
| 
      
 71 
     | 
    
         
            +
              setup_error = e
         
     | 
| 
      
 72 
     | 
    
         
            +
              $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
              # If Bundler.setup fails, we need to restore the original $LOAD_PATH so that we can still require the Ruby LSP server
         
     | 
| 
      
 75 
     | 
    
         
            +
              # in degraded mode
         
     | 
| 
      
 76 
     | 
    
         
            +
              $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
         
     | 
| 
      
 77 
     | 
    
         
            +
            ensure
         
     | 
| 
      
 78 
     | 
    
         
            +
              require "fileutils"
         
     | 
| 
      
 79 
     | 
    
         
            +
              FileUtils.rm(bundle_env_path) if File.exist?(bundle_env_path)
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
            error_path = File.join(".ruby-lsp", "install_error")
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
            install_error = if File.exist?(error_path)
         
     | 
| 
      
 85 
     | 
    
         
            +
              Marshal.load(File.read(error_path))
         
     | 
| 
      
 86 
     | 
    
         
            +
            end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            # Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
         
     | 
| 
      
 89 
     | 
    
         
            +
            # configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
         
     | 
| 
      
 90 
     | 
    
         
            +
            # paths into the load path manually or we may end up requiring the wrong version of the gem
         
     | 
| 
      
 91 
     | 
    
         
            +
            require "ruby_lsp/load_sorbet"
         
     | 
| 
      
 92 
     | 
    
         
            +
            require "ruby_lsp/internal"
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            T::Utils.run_all_sig_blocks
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            if ARGV.include?("--debug")
         
     | 
| 
      
 97 
     | 
    
         
            +
              if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
         
     | 
| 
      
 98 
     | 
    
         
            +
                $stderr.puts "Debugging is not supported on Windows"
         
     | 
| 
      
 99 
     | 
    
         
            +
              else
         
     | 
| 
      
 100 
     | 
    
         
            +
                begin
         
     | 
| 
      
 101 
     | 
    
         
            +
                  ENV.delete("RUBY_DEBUG_IRB_CONSOLE")
         
     | 
| 
      
 102 
     | 
    
         
            +
                  require "debug/open_nonstop"
         
     | 
| 
      
 103 
     | 
    
         
            +
                rescue LoadError
         
     | 
| 
      
 104 
     | 
    
         
            +
                  $stderr.puts("You need to install the debug gem to use the --debug flag")
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
      
 107 
     | 
    
         
            +
            end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            # Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
         
     | 
| 
      
 110 
     | 
    
         
            +
            $> = $stderr
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            initialize_request = JSON.parse(raw_initialize, symbolize_names: true) if raw_initialize
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            begin
         
     | 
| 
      
 115 
     | 
    
         
            +
              RubyLsp::Server.new(
         
     | 
| 
      
 116 
     | 
    
         
            +
                install_error: install_error,
         
     | 
| 
      
 117 
     | 
    
         
            +
                setup_error: setup_error,
         
     | 
| 
      
 118 
     | 
    
         
            +
                initialize_request: initialize_request,
         
     | 
| 
      
 119 
     | 
    
         
            +
              ).start
         
     | 
| 
      
 120 
     | 
    
         
            +
            rescue ArgumentError
         
     | 
| 
      
 121 
     | 
    
         
            +
              # If the launcher is booting an outdated version of the server, then the initializer doesn't accept a keyword splat
         
     | 
| 
      
 122 
     | 
    
         
            +
              # and we already read the initialize request from the stdin pipe. In this case, we need to process the initialize
         
     | 
| 
      
 123 
     | 
    
         
            +
              # request manually and then start the main loop
         
     | 
| 
      
 124 
     | 
    
         
            +
              server = RubyLsp::Server.new
         
     | 
| 
      
 125 
     | 
    
         
            +
              server.process_message(initialize_request)
         
     | 
| 
      
 126 
     | 
    
         
            +
              server.start
         
     | 
| 
      
 127 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -312,12 +312,16 @@ module RubyIndexer 
     | 
|
| 
       312 
312 
     | 
    
         
             
                    @visibility_stack.push(Entry::Visibility::PROTECTED)
         
     | 
| 
       313 
313 
     | 
    
         
             
                  when :private
         
     | 
| 
       314 
314 
     | 
    
         
             
                    @visibility_stack.push(Entry::Visibility::PRIVATE)
         
     | 
| 
      
 315 
     | 
    
         
            +
                  when :module_function
         
     | 
| 
      
 316 
     | 
    
         
            +
                    handle_module_function(node)
         
     | 
| 
       315 
317 
     | 
    
         
             
                  end
         
     | 
| 
       316 
318 
     | 
    
         | 
| 
       317 
319 
     | 
    
         
             
                  @enhancements.each do |enhancement|
         
     | 
| 
       318 
     | 
    
         
            -
                    enhancement. 
     | 
| 
      
 320 
     | 
    
         
            +
                    enhancement.on_call_node_enter(@owner_stack.last, node, @file_path, @code_units_cache)
         
     | 
| 
       319 
321 
     | 
    
         
             
                  rescue StandardError => e
         
     | 
| 
       320 
     | 
    
         
            -
                    @indexing_errors <<  
     | 
| 
      
 322 
     | 
    
         
            +
                    @indexing_errors << <<~MSG
         
     | 
| 
      
 323 
     | 
    
         
            +
                      Indexing error in #{@file_path} with '#{enhancement.class.name}' on call node enter enhancement: #{e.message}
         
     | 
| 
      
 324 
     | 
    
         
            +
                    MSG
         
     | 
| 
       321 
325 
     | 
    
         
             
                  end
         
     | 
| 
       322 
326 
     | 
    
         
             
                end
         
     | 
| 
       323 
327 
     | 
    
         | 
| 
         @@ -332,6 +336,14 @@ module RubyIndexer 
     | 
|
| 
       332 
336 
     | 
    
         
             
                      @visibility_stack.pop
         
     | 
| 
       333 
337 
     | 
    
         
             
                    end
         
     | 
| 
       334 
338 
     | 
    
         
             
                  end
         
     | 
| 
      
 339 
     | 
    
         
            +
             
     | 
| 
      
 340 
     | 
    
         
            +
                  @enhancements.each do |enhancement|
         
     | 
| 
      
 341 
     | 
    
         
            +
                    enhancement.on_call_node_leave(@owner_stack.last, node, @file_path, @code_units_cache)
         
     | 
| 
      
 342 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
      
 343 
     | 
    
         
            +
                    @indexing_errors << <<~MSG
         
     | 
| 
      
 344 
     | 
    
         
            +
                      Indexing error in #{@file_path} with '#{enhancement.class.name}' on call node leave enhancement: #{e.message}
         
     | 
| 
      
 345 
     | 
    
         
            +
                    MSG
         
     | 
| 
      
 346 
     | 
    
         
            +
                  end
         
     | 
| 
       335 
347 
     | 
    
         
             
                end
         
     | 
| 
       336 
348 
     | 
    
         | 
| 
       337 
349 
     | 
    
         
             
                sig { params(node: Prism::DefNode).void }
         
     | 
| 
         @@ -751,6 +763,48 @@ module RubyIndexer 
     | 
|
| 
       751 
763 
     | 
    
         
             
                  end
         
     | 
| 
       752 
764 
     | 
    
         
             
                end
         
     | 
| 
       753 
765 
     | 
    
         | 
| 
      
 766 
     | 
    
         
            +
                sig { params(node: Prism::CallNode).void }
         
     | 
| 
      
 767 
     | 
    
         
            +
                def handle_module_function(node)
         
     | 
| 
      
 768 
     | 
    
         
            +
                  arguments_node = node.arguments
         
     | 
| 
      
 769 
     | 
    
         
            +
                  return unless arguments_node
         
     | 
| 
      
 770 
     | 
    
         
            +
             
     | 
| 
      
 771 
     | 
    
         
            +
                  owner_name = @owner_stack.last&.name
         
     | 
| 
      
 772 
     | 
    
         
            +
                  return unless owner_name
         
     | 
| 
      
 773 
     | 
    
         
            +
             
     | 
| 
      
 774 
     | 
    
         
            +
                  arguments_node.arguments.each do |argument|
         
     | 
| 
      
 775 
     | 
    
         
            +
                    method_name = case argument
         
     | 
| 
      
 776 
     | 
    
         
            +
                    when Prism::StringNode
         
     | 
| 
      
 777 
     | 
    
         
            +
                      argument.content
         
     | 
| 
      
 778 
     | 
    
         
            +
                    when Prism::SymbolNode
         
     | 
| 
      
 779 
     | 
    
         
            +
                      argument.value
         
     | 
| 
      
 780 
     | 
    
         
            +
                    end
         
     | 
| 
      
 781 
     | 
    
         
            +
                    next unless method_name
         
     | 
| 
      
 782 
     | 
    
         
            +
             
     | 
| 
      
 783 
     | 
    
         
            +
                    entries = @index.resolve_method(method_name, owner_name)
         
     | 
| 
      
 784 
     | 
    
         
            +
                    next unless entries
         
     | 
| 
      
 785 
     | 
    
         
            +
             
     | 
| 
      
 786 
     | 
    
         
            +
                    entries.each do |entry|
         
     | 
| 
      
 787 
     | 
    
         
            +
                      entry_owner_name = entry.owner&.name
         
     | 
| 
      
 788 
     | 
    
         
            +
                      next unless entry_owner_name
         
     | 
| 
      
 789 
     | 
    
         
            +
             
     | 
| 
      
 790 
     | 
    
         
            +
                      entry.visibility = Entry::Visibility::PRIVATE
         
     | 
| 
      
 791 
     | 
    
         
            +
             
     | 
| 
      
 792 
     | 
    
         
            +
                      singleton = @index.existing_or_new_singleton_class(entry_owner_name)
         
     | 
| 
      
 793 
     | 
    
         
            +
                      location = Location.from_prism_location(argument.location, @code_units_cache)
         
     | 
| 
      
 794 
     | 
    
         
            +
                      @index.add(Entry::Method.new(
         
     | 
| 
      
 795 
     | 
    
         
            +
                        method_name,
         
     | 
| 
      
 796 
     | 
    
         
            +
                        @file_path,
         
     | 
| 
      
 797 
     | 
    
         
            +
                        location,
         
     | 
| 
      
 798 
     | 
    
         
            +
                        location,
         
     | 
| 
      
 799 
     | 
    
         
            +
                        collect_comments(node)&.concat(entry.comments),
         
     | 
| 
      
 800 
     | 
    
         
            +
                        entry.signatures,
         
     | 
| 
      
 801 
     | 
    
         
            +
                        Entry::Visibility::PUBLIC,
         
     | 
| 
      
 802 
     | 
    
         
            +
                        singleton,
         
     | 
| 
      
 803 
     | 
    
         
            +
                      ))
         
     | 
| 
      
 804 
     | 
    
         
            +
                    end
         
     | 
| 
      
 805 
     | 
    
         
            +
                  end
         
     | 
| 
      
 806 
     | 
    
         
            +
                end
         
     | 
| 
      
 807 
     | 
    
         
            +
             
     | 
| 
       754 
808 
     | 
    
         
             
                sig { returns(Entry::Visibility) }
         
     | 
| 
       755 
809 
     | 
    
         
             
                def current_visibility
         
     | 
| 
       756 
810 
     | 
    
         
             
                  T.must(@visibility_stack.last)
         
     | 
| 
         @@ -2,20 +2,35 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module RubyIndexer
         
     | 
| 
       5 
     | 
    
         
            -
               
     | 
| 
      
 5 
     | 
    
         
            +
              class Enhancement
         
     | 
| 
       6 
6 
     | 
    
         
             
                extend T::Sig
         
     | 
| 
       7 
7 
     | 
    
         
             
                extend T::Helpers
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
      
 9 
     | 
    
         
            +
                abstract!
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                 
     | 
| 
      
 11 
     | 
    
         
            +
                sig { params(index: Index).void }
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(index)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @index = index
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
       12 
15 
     | 
    
         | 
| 
       13 
16 
     | 
    
         
             
                # The `on_extend` indexing enhancement is invoked whenever an extend is encountered in the code. It can be used to
         
     | 
| 
       14 
17 
     | 
    
         
             
                # register for an included callback, similar to what `ActiveSupport::Concern` does in order to auto-extend the
         
     | 
| 
       15 
18 
     | 
    
         
             
                # `ClassMethods` modules
         
     | 
| 
       16 
19 
     | 
    
         
             
                sig do
         
     | 
| 
       17 
     | 
    
         
            -
                   
     | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
      
 20 
     | 
    
         
            +
                  overridable.params(
         
     | 
| 
      
 21 
     | 
    
         
            +
                    owner: T.nilable(Entry::Namespace),
         
     | 
| 
      
 22 
     | 
    
         
            +
                    node: Prism::CallNode,
         
     | 
| 
      
 23 
     | 
    
         
            +
                    file_path: String,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    code_units_cache: T.any(
         
     | 
| 
      
 25 
     | 
    
         
            +
                      T.proc.params(arg0: Integer).returns(Integer),
         
     | 
| 
      
 26 
     | 
    
         
            +
                      Prism::CodeUnitsCache,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 28 
     | 
    
         
            +
                  ).void
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                def on_call_node_enter(owner, node, file_path, code_units_cache); end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                sig do
         
     | 
| 
      
 33 
     | 
    
         
            +
                  overridable.params(
         
     | 
| 
       19 
34 
     | 
    
         
             
                    owner: T.nilable(Entry::Namespace),
         
     | 
| 
       20 
35 
     | 
    
         
             
                    node: Prism::CallNode,
         
     | 
| 
       21 
36 
     | 
    
         
             
                    file_path: String,
         
     | 
| 
         @@ -25,6 +40,6 @@ module RubyIndexer 
     | 
|
| 
       25 
40 
     | 
    
         
             
                    ),
         
     | 
| 
       26 
41 
     | 
    
         
             
                  ).void
         
     | 
| 
       27 
42 
     | 
    
         
             
                end
         
     | 
| 
       28 
     | 
    
         
            -
                def  
     | 
| 
      
 43 
     | 
    
         
            +
                def on_call_node_leave(owner, node, file_path, code_units_cache); end
         
     | 
| 
       29 
44 
     | 
    
         
             
              end
         
     | 
| 
       30 
45 
     | 
    
         
             
            end
         
     | 
| 
         @@ -646,7 +646,7 @@ module RubyIndexer 
     | 
|
| 
       646 
646 
     | 
    
         
             
                    (positionals.empty? && forwarding_arguments.any?) ||
         
     | 
| 
       647 
647 
     | 
    
         
             
                      (
         
     | 
| 
       648 
648 
     | 
    
         
             
                        # Check if positional arguments match. This includes required, optional, rest arguments. We also need to
         
     | 
| 
       649 
     | 
    
         
            -
                        # verify if there's a trailing  
     | 
| 
      
 649 
     | 
    
         
            +
                        # verify if there's a trailing forwarding argument, like `def foo(a, ...); end`
         
     | 
| 
       650 
650 
     | 
    
         
             
                        positional_arguments_match?(positionals, forwarding_arguments, keyword_args, min_pos, max_pos) &&
         
     | 
| 
       651 
651 
     | 
    
         
             
                        # If the positional arguments match, we move on to checking keyword, optional keyword and keyword rest
         
     | 
| 
       652 
652 
     | 
    
         
             
                        # arguments. If there's a forward argument, then it will always match. If the method accepts a keyword rest
         
     | 
| 
         @@ -784,7 +784,7 @@ module RubyIndexer 
     | 
|
| 
       784 
784 
     | 
    
         
             
                  singleton_levels
         
     | 
| 
       785 
785 
     | 
    
         
             
                )
         
     | 
| 
       786 
786 
     | 
    
         
             
                  # Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
         
     | 
| 
       787 
     | 
    
         
            -
                  # from two  
     | 
| 
      
 787 
     | 
    
         
            +
                  # from two different classes in different files, we simply ignore it
         
     | 
| 
       788 
788 
     | 
    
         
             
                  superclass = T.cast(
         
     | 
| 
       789 
789 
     | 
    
         
             
                    if singleton_levels > 0
         
     | 
| 
       790 
790 
     | 
    
         
             
                      self[attached_class_name]&.find { |n| n.is_a?(Entry::Class) && n.parent_class }
         
     | 
| 
         @@ -974,10 +974,10 @@ module RubyIndexer 
     | 
|
| 
       974 
974 
     | 
    
         
             
                  []
         
     | 
| 
       975 
975 
     | 
    
         
             
                end
         
     | 
| 
       976 
976 
     | 
    
         | 
| 
       977 
     | 
    
         
            -
                # Removes  
     | 
| 
       978 
     | 
    
         
            -
                # of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up 
     | 
| 
       979 
     | 
    
         
            -
                # `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and 
     | 
| 
       980 
     | 
    
         
            -
                # nesting
         
     | 
| 
      
 977 
     | 
    
         
            +
                # Removes redundancy from a constant reference's full name. For example, if we find a reference to `A::B::Foo`
         
     | 
| 
      
 978 
     | 
    
         
            +
                # inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
         
     | 
| 
      
 979 
     | 
    
         
            +
                # with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
         
     | 
| 
      
 980 
     | 
    
         
            +
                # the nesting
         
     | 
| 
       981 
981 
     | 
    
         
             
                sig { params(name: String, nesting: T::Array[String]).returns(String) }
         
     | 
| 
       982 
982 
     | 
    
         
             
                def build_non_redundant_full_name(name, nesting)
         
     | 
| 
       983 
983 
     | 
    
         
             
                  # If there's no nesting, then we can just return the name as is
         
     | 
| 
         @@ -72,7 +72,7 @@ module RubyIndexer 
     | 
|
| 
       72 
72 
     | 
    
         
             
                  assert_entry("self::Bar", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
         
     | 
| 
       73 
73 
     | 
    
         
             
                end
         
     | 
| 
       74 
74 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                def  
     | 
| 
      
 75 
     | 
    
         
            +
                def test_dynamically_namespaced_class_does_not_affect_other_classes
         
     | 
| 
       76 
76 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       77 
77 
     | 
    
         
             
                    class Foo
         
     | 
| 
       78 
78 
     | 
    
         
             
                      class self::Bar
         
     | 
| 
         @@ -143,7 +143,7 @@ module RubyIndexer 
     | 
|
| 
       143 
143 
     | 
    
         
             
                  assert_entry("self::Bar", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
         
     | 
| 
       144 
144 
     | 
    
         
             
                end
         
     | 
| 
       145 
145 
     | 
    
         | 
| 
       146 
     | 
    
         
            -
                def  
     | 
| 
      
 146 
     | 
    
         
            +
                def test_dynamically_namespaced_module_does_not_affect_other_modules
         
     | 
| 
       147 
147 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       148 
148 
     | 
    
         
             
                    module Foo
         
     | 
| 
       149 
149 
     | 
    
         
             
                      class self::Bar
         
     | 
| 
         @@ -6,10 +6,8 @@ require_relative "test_case" 
     | 
|
| 
       6 
6 
     | 
    
         
             
            module RubyIndexer
         
     | 
| 
       7 
7 
     | 
    
         
             
              class EnhancementTest < TestCase
         
     | 
| 
       8 
8 
     | 
    
         
             
                def test_enhancing_indexing_included_hook
         
     | 
| 
       9 
     | 
    
         
            -
                  enhancement_class = Class.new do
         
     | 
| 
       10 
     | 
    
         
            -
                     
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                    def on_call_node(index, owner, node, file_path, code_units_cache)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  enhancement_class = Class.new(Enhancement) do
         
     | 
| 
      
 10 
     | 
    
         
            +
                    def on_call_node_enter(owner, node, file_path, code_units_cache)
         
     | 
| 
       13 
11 
     | 
    
         
             
                      return unless owner
         
     | 
| 
       14 
12 
     | 
    
         
             
                      return unless node.name == :extend
         
     | 
| 
       15 
13 
     | 
    
         | 
| 
         @@ -24,7 +22,7 @@ module RubyIndexer 
     | 
|
| 
       24 
22 
     | 
    
         
             
                        module_name = node.full_name
         
     | 
| 
       25 
23 
     | 
    
         
             
                        next unless module_name == "ActiveSupport::Concern"
         
     | 
| 
       26 
24 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                        index.register_included_hook(owner.name) do |index, base|
         
     | 
| 
      
 25 
     | 
    
         
            +
                        @index.register_included_hook(owner.name) do |index, base|
         
     | 
| 
       28 
26 
     | 
    
         
             
                          class_methods_name = "#{owner.name}::ClassMethods"
         
     | 
| 
       29 
27 
     | 
    
         | 
| 
       30 
28 
     | 
    
         
             
                          if index.indexed?(class_methods_name)
         
     | 
| 
         @@ -33,7 +31,7 @@ module RubyIndexer 
     | 
|
| 
       33 
31 
     | 
    
         
             
                          end
         
     | 
| 
       34 
32 
     | 
    
         
             
                        end
         
     | 
| 
       35 
33 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                        index.add(Entry::Method.new(
         
     | 
| 
      
 34 
     | 
    
         
            +
                        @index.add(Entry::Method.new(
         
     | 
| 
       37 
35 
     | 
    
         
             
                          "new_method",
         
     | 
| 
       38 
36 
     | 
    
         
             
                          file_path,
         
     | 
| 
       39 
37 
     | 
    
         
             
                          location,
         
     | 
| 
         @@ -50,7 +48,7 @@ module RubyIndexer 
     | 
|
| 
       50 
48 
     | 
    
         
             
                    end
         
     | 
| 
       51 
49 
     | 
    
         
             
                  end
         
     | 
| 
       52 
50 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                  @index.register_enhancement(enhancement_class.new)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @index.register_enhancement(enhancement_class.new(@index))
         
     | 
| 
       54 
52 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       55 
53 
     | 
    
         
             
                    module ActiveSupport
         
     | 
| 
       56 
54 
     | 
    
         
             
                      module Concern
         
     | 
| 
         @@ -98,10 +96,8 @@ module RubyIndexer 
     | 
|
| 
       98 
96 
     | 
    
         
             
                end
         
     | 
| 
       99 
97 
     | 
    
         | 
| 
       100 
98 
     | 
    
         
             
                def test_enhancing_indexing_configuration_dsl
         
     | 
| 
       101 
     | 
    
         
            -
                  enhancement_class = Class.new do
         
     | 
| 
       102 
     | 
    
         
            -
                     
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                    def on_call_node(index, owner, node, file_path, code_units_cache)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  enhancement_class = Class.new(Enhancement) do
         
     | 
| 
      
 100 
     | 
    
         
            +
                    def on_call_node_enter(owner, node, file_path, code_units_cache)
         
     | 
| 
       105 
101 
     | 
    
         
             
                      return unless owner
         
     | 
| 
       106 
102 
     | 
    
         | 
| 
       107 
103 
     | 
    
         
             
                      name = node.name
         
     | 
| 
         @@ -115,7 +111,7 @@ module RubyIndexer 
     | 
|
| 
       115 
111 
     | 
    
         | 
| 
       116 
112 
     | 
    
         
             
                      location = Location.from_prism_location(association_name.location, code_units_cache)
         
     | 
| 
       117 
113 
     | 
    
         | 
| 
       118 
     | 
    
         
            -
                      index.add(Entry::Method.new(
         
     | 
| 
      
 114 
     | 
    
         
            +
                      @index.add(Entry::Method.new(
         
     | 
| 
       119 
115 
     | 
    
         
             
                        T.must(association_name.value),
         
     | 
| 
       120 
116 
     | 
    
         
             
                        file_path,
         
     | 
| 
       121 
117 
     | 
    
         
             
                        location,
         
     | 
| 
         @@ -128,7 +124,7 @@ module RubyIndexer 
     | 
|
| 
       128 
124 
     | 
    
         
             
                    end
         
     | 
| 
       129 
125 
     | 
    
         
             
                  end
         
     | 
| 
       130 
126 
     | 
    
         | 
| 
       131 
     | 
    
         
            -
                  @index.register_enhancement(enhancement_class.new)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  @index.register_enhancement(enhancement_class.new(@index))
         
     | 
| 
       132 
128 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       133 
129 
     | 
    
         
             
                    module ActiveSupport
         
     | 
| 
       134 
130 
     | 
    
         
             
                      module Concern
         
     | 
| 
         @@ -160,11 +156,44 @@ module RubyIndexer 
     | 
|
| 
       160 
156 
     | 
    
         
             
                  assert_entry("posts", Entry::Method, "/fake/path/foo.rb:23-11:23-17")
         
     | 
| 
       161 
157 
     | 
    
         
             
                end
         
     | 
| 
       162 
158 
     | 
    
         | 
| 
       163 
     | 
    
         
            -
                def  
     | 
| 
       164 
     | 
    
         
            -
                  enhancement_class = Class.new do
         
     | 
| 
       165 
     | 
    
         
            -
                     
     | 
| 
      
 159 
     | 
    
         
            +
                def test_error_handling_in_on_call_node_enter_enhancement
         
     | 
| 
      
 160 
     | 
    
         
            +
                  enhancement_class = Class.new(Enhancement) do
         
     | 
| 
      
 161 
     | 
    
         
            +
                    def on_call_node_enter(owner, node, file_path, code_units_cache)
         
     | 
| 
      
 162 
     | 
    
         
            +
                      raise "Error"
         
     | 
| 
      
 163 
     | 
    
         
            +
                    end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                    class << self
         
     | 
| 
      
 166 
     | 
    
         
            +
                      def name
         
     | 
| 
      
 167 
     | 
    
         
            +
                        "TestEnhancement"
         
     | 
| 
      
 168 
     | 
    
         
            +
                      end
         
     | 
| 
      
 169 
     | 
    
         
            +
                    end
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                  @index.register_enhancement(enhancement_class.new(@index))
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                  _stdout, stderr = capture_io do
         
     | 
| 
      
 175 
     | 
    
         
            +
                    index(<<~RUBY)
         
     | 
| 
      
 176 
     | 
    
         
            +
                      module ActiveSupport
         
     | 
| 
      
 177 
     | 
    
         
            +
                        module Concern
         
     | 
| 
      
 178 
     | 
    
         
            +
                          def self.extended(base)
         
     | 
| 
      
 179 
     | 
    
         
            +
                            base.class_eval("def new_method(a); end")
         
     | 
| 
      
 180 
     | 
    
         
            +
                          end
         
     | 
| 
      
 181 
     | 
    
         
            +
                        end
         
     | 
| 
      
 182 
     | 
    
         
            +
                      end
         
     | 
| 
      
 183 
     | 
    
         
            +
                    RUBY
         
     | 
| 
      
 184 
     | 
    
         
            +
                  end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                  assert_match(
         
     | 
| 
      
 187 
     | 
    
         
            +
                    %r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' on call node enter enhancement},
         
     | 
| 
      
 188 
     | 
    
         
            +
                    stderr,
         
     | 
| 
      
 189 
     | 
    
         
            +
                  )
         
     | 
| 
      
 190 
     | 
    
         
            +
                  # The module should still be indexed
         
     | 
| 
      
 191 
     | 
    
         
            +
                  assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
         
     | 
| 
      
 192 
     | 
    
         
            +
                end
         
     | 
| 
       166 
193 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
      
 194 
     | 
    
         
            +
                def test_error_handling_in_on_call_node_leave_enhancement
         
     | 
| 
      
 195 
     | 
    
         
            +
                  enhancement_class = Class.new(Enhancement) do
         
     | 
| 
      
 196 
     | 
    
         
            +
                    def on_call_node_leave(owner, node, file_path, code_units_cache)
         
     | 
| 
       168 
197 
     | 
    
         
             
                      raise "Error"
         
     | 
| 
       169 
198 
     | 
    
         
             
                    end
         
     | 
| 
       170 
199 
     | 
    
         | 
| 
         @@ -175,7 +204,7 @@ module RubyIndexer 
     | 
|
| 
       175 
204 
     | 
    
         
             
                    end
         
     | 
| 
       176 
205 
     | 
    
         
             
                  end
         
     | 
| 
       177 
206 
     | 
    
         | 
| 
       178 
     | 
    
         
            -
                  @index.register_enhancement(enhancement_class.new)
         
     | 
| 
      
 207 
     | 
    
         
            +
                  @index.register_enhancement(enhancement_class.new(@index))
         
     | 
| 
       179 
208 
     | 
    
         | 
| 
       180 
209 
     | 
    
         
             
                  _stdout, stderr = capture_io do
         
     | 
| 
       181 
210 
     | 
    
         
             
                    index(<<~RUBY)
         
     | 
| 
         @@ -189,7 +218,10 @@ module RubyIndexer 
     | 
|
| 
       189 
218 
     | 
    
         
             
                    RUBY
         
     | 
| 
       190 
219 
     | 
    
         
             
                  end
         
     | 
| 
       191 
220 
     | 
    
         | 
| 
       192 
     | 
    
         
            -
                  assert_match( 
     | 
| 
      
 221 
     | 
    
         
            +
                  assert_match(
         
     | 
| 
      
 222 
     | 
    
         
            +
                    %r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' on call node leave enhancement},
         
     | 
| 
      
 223 
     | 
    
         
            +
                    stderr,
         
     | 
| 
      
 224 
     | 
    
         
            +
                  )
         
     | 
| 
       193 
225 
     | 
    
         
             
                  # The module should still be indexed
         
     | 
| 
       194 
226 
     | 
    
         
             
                  assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
         
     | 
| 
       195 
227 
     | 
    
         
             
                end
         
     | 
| 
         @@ -904,7 +904,7 @@ module RubyIndexer 
     | 
|
| 
       904 
904 
     | 
    
         
             
                  assert_equal(14, entry.location.start_line)
         
     | 
| 
       905 
905 
     | 
    
         
             
                end
         
     | 
| 
       906 
906 
     | 
    
         | 
| 
       907 
     | 
    
         
            -
                def  
     | 
| 
      
 907 
     | 
    
         
            +
                def test_resolving_inherited_aliased_namespace
         
     | 
| 
       908 
908 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       909 
909 
     | 
    
         
             
                    module Bar
         
     | 
| 
       910 
910 
     | 
    
         
             
                      TARGET = 123
         
     | 
| 
         @@ -1490,7 +1490,7 @@ module RubyIndexer 
     | 
|
| 
       1490 
1490 
     | 
    
         
             
                  assert_kind_of(Entry::UnresolvedMethodAlias, entry)
         
     | 
| 
       1491 
1491 
     | 
    
         
             
                end
         
     | 
| 
       1492 
1492 
     | 
    
         | 
| 
       1493 
     | 
    
         
            -
                def  
     | 
| 
      
 1493 
     | 
    
         
            +
                def test_unresolvable_method_aliases
         
     | 
| 
       1494 
1494 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       1495 
1495 
     | 
    
         
             
                    class Foo
         
     | 
| 
       1496 
1496 
     | 
    
         
             
                      alias bar baz
         
     | 
| 
         @@ -209,7 +209,7 @@ module RubyIndexer 
     | 
|
| 
       209 
209 
     | 
    
         
             
                    end
         
     | 
| 
       210 
210 
     | 
    
         
             
                  RUBY
         
     | 
| 
       211 
211 
     | 
    
         | 
| 
       212 
     | 
    
         
            -
                  # If the surrounding method is  
     | 
| 
      
 212 
     | 
    
         
            +
                  # If the surrounding method is being defined on any dynamic value that isn't `self`, then we attribute the
         
     | 
| 
       213 
213 
     | 
    
         
             
                  # instance variable to the wrong owner since there's no way to understand that statically
         
     | 
| 
       214 
214 
     | 
    
         
             
                  entry = T.must(@index["@a"]&.first)
         
     | 
| 
       215 
215 
     | 
    
         
             
                  owner = T.must(entry.owner)
         
     | 
| 
         @@ -123,6 +123,32 @@ module RubyIndexer 
     | 
|
| 
       123 
123 
     | 
    
         
             
                  assert_entry("baz", Entry::Method, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE)
         
     | 
| 
       124 
124 
     | 
    
         
             
                end
         
     | 
| 
       125 
125 
     | 
    
         | 
| 
      
 126 
     | 
    
         
            +
                def test_visibility_tracking_with_module_function
         
     | 
| 
      
 127 
     | 
    
         
            +
                  index(<<~RUBY)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    module Test
         
     | 
| 
      
 129 
     | 
    
         
            +
                      def foo; end
         
     | 
| 
      
 130 
     | 
    
         
            +
                      def bar; end
         
     | 
| 
      
 131 
     | 
    
         
            +
                      module_function :foo, "bar"
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
                  RUBY
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  ["foo", "bar"].each do |keyword|
         
     | 
| 
      
 136 
     | 
    
         
            +
                    entries = T.must(@index[keyword])
         
     | 
| 
      
 137 
     | 
    
         
            +
                    # should receive two entries because module_function creates a singleton method
         
     | 
| 
      
 138 
     | 
    
         
            +
                    # for the Test module and a private method for classes include the Test module
         
     | 
| 
      
 139 
     | 
    
         
            +
                    assert_equal(entries.size, 2)
         
     | 
| 
      
 140 
     | 
    
         
            +
                    first_entry, second_entry = *entries
         
     | 
| 
      
 141 
     | 
    
         
            +
                    # The first entry points to the location of the module_function call
         
     | 
| 
      
 142 
     | 
    
         
            +
                    assert_equal("Test", first_entry.owner.name)
         
     | 
| 
      
 143 
     | 
    
         
            +
                    assert_instance_of(Entry::Module, first_entry.owner)
         
     | 
| 
      
 144 
     | 
    
         
            +
                    assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility)
         
     | 
| 
      
 145 
     | 
    
         
            +
                    # The second entry points to the public singleton method
         
     | 
| 
      
 146 
     | 
    
         
            +
                    assert_equal("Test::<Class:Test>", second_entry.owner.name)
         
     | 
| 
      
 147 
     | 
    
         
            +
                    assert_instance_of(Entry::SingletonClass, second_entry.owner)
         
     | 
| 
      
 148 
     | 
    
         
            +
                    assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
                end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
       126 
152 
     | 
    
         
             
                def test_method_with_parameters
         
     | 
| 
       127 
153 
     | 
    
         
             
                  index(<<~RUBY)
         
     | 
| 
       128 
154 
     | 
    
         
             
                    class Foo
         
     | 
| 
         @@ -100,7 +100,7 @@ module RubyIndexer 
     | 
|
| 
       100 
100 
     | 
    
         
             
                end
         
     | 
| 
       101 
101 
     | 
    
         | 
| 
       102 
102 
     | 
    
         
             
                def test_location_and_name_location_are_the_same
         
     | 
| 
       103 
     | 
    
         
            -
                  # NOTE: RBS does not store the name location for classes, modules or methods. This  
     | 
| 
      
 103 
     | 
    
         
            +
                  # NOTE: RBS does not store the name location for classes, modules or methods. This behavior is not exactly what
         
     | 
| 
       104 
104 
     | 
    
         
             
                  # we would like, but for now we assign the same location to both
         
     | 
| 
       105 
105 
     | 
    
         | 
| 
       106 
106 
     | 
    
         
             
                  entries = @index["Array"]
         
     | 
    
        data/lib/ruby_lsp/addon.rb
    CHANGED
    
    | 
         @@ -46,7 +46,7 @@ module RubyLsp 
     | 
|
| 
       46 
46 
     | 
    
         
             
                  sig { returns(T::Array[T.class_of(Addon)]) }
         
     | 
| 
       47 
47 
     | 
    
         
             
                  attr_reader :addon_classes
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                  # Automatically track and instantiate  
     | 
| 
      
 49 
     | 
    
         
            +
                  # Automatically track and instantiate add-on classes
         
     | 
| 
       50 
50 
     | 
    
         
             
                  sig { params(child_class: T.class_of(Addon)).void }
         
     | 
| 
       51 
51 
     | 
    
         
             
                  def inherited(child_class)
         
     | 
| 
       52 
52 
     | 
    
         
             
                    addon_classes << child_class
         
     | 
| 
         @@ -82,7 +82,7 @@ module RubyLsp 
     | 
|
| 
       82 
82 
     | 
    
         
             
                      e
         
     | 
| 
       83 
83 
     | 
    
         
             
                    end
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                    # Instantiate all discovered  
     | 
| 
      
 85 
     | 
    
         
            +
                    # Instantiate all discovered add-on classes
         
     | 
| 
       86 
86 
     | 
    
         
             
                    self.addons = addon_classes.map(&:new)
         
     | 
| 
       87 
87 
     | 
    
         
             
                    self.file_watcher_addons = addons.select { |addon| addon.respond_to?(:workspace_did_change_watched_files) }
         
     | 
| 
       88 
88 
     | 
    
         | 
| 
         @@ -194,6 +194,13 @@ module RubyLsp 
     | 
|
| 
       194 
194 
     | 
    
         
             
                sig { abstract.returns(String) }
         
     | 
| 
       195 
195 
     | 
    
         
             
                def version; end
         
     | 
| 
       196 
196 
     | 
    
         | 
| 
      
 197 
     | 
    
         
            +
                # Handle a response from a window/showMessageRequest request. Add-ons must include the addon_name as part of the
         
     | 
| 
      
 198 
     | 
    
         
            +
                # original request so that the response is delegated to the correct add-on and must override this method to handle
         
     | 
| 
      
 199 
     | 
    
         
            +
                # the response
         
     | 
| 
      
 200 
     | 
    
         
            +
                # https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest
         
     | 
| 
      
 201 
     | 
    
         
            +
                sig { overridable.params(title: String).void }
         
     | 
| 
      
 202 
     | 
    
         
            +
                def handle_window_show_message_response(title); end
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
       197 
204 
     | 
    
         
             
                # Creates a new CodeLens listener. This method is invoked on every CodeLens request
         
     | 
| 
       198 
205 
     | 
    
         
             
                sig do
         
     | 
| 
       199 
206 
     | 
    
         
             
                  overridable.params(
         
     |