ruby-lsp 0.8.0 → 0.9.0
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 +41 -33
 - data/lib/core_ext/uri.rb +9 -14
 - data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +166 -0
 - data/lib/ruby_indexer/lib/ruby_indexer/index.rb +147 -0
 - data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +123 -0
 - data/lib/ruby_indexer/ruby_indexer.rb +20 -0
 - data/lib/ruby_indexer/test/classes_and_modules_test.rb +220 -0
 - data/lib/ruby_indexer/test/configuration_test.rb +114 -0
 - data/lib/ruby_indexer/test/constant_test.rb +108 -0
 - data/lib/ruby_indexer/test/index_test.rb +129 -0
 - data/lib/ruby_indexer/test/test_case.rb +42 -0
 - data/lib/ruby_lsp/executor.rb +144 -10
 - data/lib/ruby_lsp/internal.rb +2 -0
 - data/lib/ruby_lsp/requests/definition.rb +60 -5
 - data/lib/ruby_lsp/requests/hover.rb +53 -30
 - data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -6
 - data/lib/ruby_lsp/requests/support/dependency_detector.rb +7 -0
 - data/lib/ruby_lsp/requests/workspace_symbol.rb +86 -0
 - data/lib/ruby_lsp/requests.rb +1 -0
 - data/lib/ruby_lsp/setup_bundler.rb +24 -8
 - data/lib/ruby_lsp/store.rb +15 -9
 - metadata +33 -4
 - data/lib/ruby_lsp/requests/support/rails_document_client.rb +0 -122
 
    
        data/lib/ruby_lsp/executor.rb
    CHANGED
    
    | 
         @@ -15,6 +15,7 @@ module RubyLsp 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  @store = store
         
     | 
| 
       16 
16 
     | 
    
         
             
                  @test_library = T.let(DependencyDetector.detected_test_library, String)
         
     | 
| 
       17 
17 
     | 
    
         
             
                  @message_queue = message_queue
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
         
     | 
| 
       18 
19 
     | 
    
         
             
                end
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
21 
     | 
    
         
             
                sig { params(request: T::Hash[Symbol, T.untyped]).returns(Result) }
         
     | 
| 
         @@ -57,6 +58,7 @@ module RubyLsp 
     | 
|
| 
       57 
58 
     | 
    
         
             
                      warn(errored_extensions.map(&:backtraces).join("\n\n"))
         
     | 
| 
       58 
59 
     | 
    
         
             
                    end
         
     | 
| 
       59 
60 
     | 
    
         | 
| 
      
 61 
     | 
    
         
            +
                    perform_initial_indexing
         
     | 
| 
       60 
62 
     | 
    
         
             
                    check_formatter_is_available
         
     | 
| 
       61 
63 
     | 
    
         | 
| 
       62 
64 
     | 
    
         
             
                    warn("Ruby LSP is ready")
         
     | 
| 
         @@ -169,25 +171,91 @@ module RubyLsp 
     | 
|
| 
       169 
171 
     | 
    
         
             
                    completion(uri, request.dig(:params, :position))
         
     | 
| 
       170 
172 
     | 
    
         
             
                  when "textDocument/definition"
         
     | 
| 
       171 
173 
     | 
    
         
             
                    definition(uri, request.dig(:params, :position))
         
     | 
| 
      
 174 
     | 
    
         
            +
                  when "workspace/didChangeWatchedFiles"
         
     | 
| 
      
 175 
     | 
    
         
            +
                    did_change_watched_files(request.dig(:params, :changes))
         
     | 
| 
      
 176 
     | 
    
         
            +
                  when "workspace/symbol"
         
     | 
| 
      
 177 
     | 
    
         
            +
                    workspace_symbol(request.dig(:params, :query))
         
     | 
| 
       172 
178 
     | 
    
         
             
                  when "rubyLsp/textDocument/showSyntaxTree"
         
     | 
| 
       173 
179 
     | 
    
         
             
                    show_syntax_tree(uri, request.dig(:params, :range))
         
     | 
| 
       174 
180 
     | 
    
         
             
                  end
         
     | 
| 
       175 
181 
     | 
    
         
             
                end
         
     | 
| 
       176 
182 
     | 
    
         | 
| 
      
 183 
     | 
    
         
            +
                sig { params(changes: T::Array[{ uri: String, type: Integer }]).returns(Object) }
         
     | 
| 
      
 184 
     | 
    
         
            +
                def did_change_watched_files(changes)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  changes.each do |change|
         
     | 
| 
      
 186 
     | 
    
         
            +
                    # File change events include folders, but we're only interested in files
         
     | 
| 
      
 187 
     | 
    
         
            +
                    uri = URI(change[:uri])
         
     | 
| 
      
 188 
     | 
    
         
            +
                    file_path = uri.to_standardized_path
         
     | 
| 
      
 189 
     | 
    
         
            +
                    next if file_path.nil? || File.directory?(file_path)
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                    case change[:type]
         
     | 
| 
      
 192 
     | 
    
         
            +
                    when Constant::FileChangeType::CREATED
         
     | 
| 
      
 193 
     | 
    
         
            +
                      @index.index_single(file_path)
         
     | 
| 
      
 194 
     | 
    
         
            +
                    when Constant::FileChangeType::CHANGED
         
     | 
| 
      
 195 
     | 
    
         
            +
                      @index.delete(file_path)
         
     | 
| 
      
 196 
     | 
    
         
            +
                      @index.index_single(file_path)
         
     | 
| 
      
 197 
     | 
    
         
            +
                    when Constant::FileChangeType::DELETED
         
     | 
| 
      
 198 
     | 
    
         
            +
                      @index.delete(file_path)
         
     | 
| 
      
 199 
     | 
    
         
            +
                    end
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                  VOID
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                sig { void }
         
     | 
| 
      
 206 
     | 
    
         
            +
                def perform_initial_indexing
         
     | 
| 
      
 207 
     | 
    
         
            +
                  return unless @store.experimental_features
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
         
     | 
| 
      
 210 
     | 
    
         
            +
                  # stuck indexing files
         
     | 
| 
      
 211 
     | 
    
         
            +
                  RubyIndexer.configuration.load_config
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 214 
     | 
    
         
            +
                    @index.index_all
         
     | 
| 
      
 215 
     | 
    
         
            +
                  rescue StandardError => error
         
     | 
| 
      
 216 
     | 
    
         
            +
                    @message_queue << Notification.new(
         
     | 
| 
      
 217 
     | 
    
         
            +
                      message: "window/showMessage",
         
     | 
| 
      
 218 
     | 
    
         
            +
                      params: Interface::ShowMessageParams.new(
         
     | 
| 
      
 219 
     | 
    
         
            +
                        type: Constant::MessageType::ERROR,
         
     | 
| 
      
 220 
     | 
    
         
            +
                        message: "Error while indexing: #{error.message}",
         
     | 
| 
      
 221 
     | 
    
         
            +
                      ),
         
     | 
| 
      
 222 
     | 
    
         
            +
                    )
         
     | 
| 
      
 223 
     | 
    
         
            +
                  end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                  # Always end the progress notification even if indexing failed or else it never goes away and the user has no way
         
     | 
| 
      
 226 
     | 
    
         
            +
                  # of dismissing it
         
     | 
| 
      
 227 
     | 
    
         
            +
                  end_progress("indexing-progress")
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                sig { params(query: T.nilable(String)).returns(T::Array[Interface::WorkspaceSymbol]) }
         
     | 
| 
      
 231 
     | 
    
         
            +
                def workspace_symbol(query)
         
     | 
| 
      
 232 
     | 
    
         
            +
                  Requests::WorkspaceSymbol.new(query, @index).run
         
     | 
| 
      
 233 
     | 
    
         
            +
                end
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
       177 
235 
     | 
    
         
             
                sig { params(uri: URI::Generic, range: T.nilable(Document::RangeShape)).returns({ ast: String }) }
         
     | 
| 
       178 
236 
     | 
    
         
             
                def show_syntax_tree(uri, range)
         
     | 
| 
       179 
237 
     | 
    
         
             
                  { ast: Requests::ShowSyntaxTree.new(@store.get(uri), range).run }
         
     | 
| 
       180 
238 
     | 
    
         
             
                end
         
     | 
| 
       181 
239 
     | 
    
         | 
| 
       182 
     | 
    
         
            -
                sig  
     | 
| 
      
 240 
     | 
    
         
            +
                sig do
         
     | 
| 
      
 241 
     | 
    
         
            +
                  params(
         
     | 
| 
      
 242 
     | 
    
         
            +
                    uri: URI::Generic,
         
     | 
| 
      
 243 
     | 
    
         
            +
                    position: Document::PositionShape,
         
     | 
| 
      
 244 
     | 
    
         
            +
                  ).returns(T.nilable(T.any(T::Array[Interface::Location], Interface::Location)))
         
     | 
| 
      
 245 
     | 
    
         
            +
                end
         
     | 
| 
       183 
246 
     | 
    
         
             
                def definition(uri, position)
         
     | 
| 
       184 
247 
     | 
    
         
             
                  document = @store.get(uri)
         
     | 
| 
       185 
248 
     | 
    
         
             
                  return if document.syntax_error?
         
     | 
| 
       186 
249 
     | 
    
         | 
| 
       187 
     | 
    
         
            -
                  target,  
     | 
| 
      
 250 
     | 
    
         
            +
                  target, parent, nesting = document.locate_node(
         
     | 
| 
      
 251 
     | 
    
         
            +
                    position,
         
     | 
| 
      
 252 
     | 
    
         
            +
                    node_types: [SyntaxTree::Command, SyntaxTree::Const, SyntaxTree::ConstPathRef],
         
     | 
| 
      
 253 
     | 
    
         
            +
                  )
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                  target = parent if target.is_a?(SyntaxTree::Const) && parent.is_a?(SyntaxTree::ConstPathRef)
         
     | 
| 
       188 
256 
     | 
    
         | 
| 
       189 
257 
     | 
    
         
             
                  emitter = EventEmitter.new
         
     | 
| 
       190 
     | 
    
         
            -
                  base_listener = Requests::Definition.new(uri, emitter, @message_queue)
         
     | 
| 
      
 258 
     | 
    
         
            +
                  base_listener = Requests::Definition.new(uri, nesting, @index, emitter, @message_queue)
         
     | 
| 
       191 
259 
     | 
    
         
             
                  emitter.emit_for_target(target)
         
     | 
| 
       192 
260 
     | 
    
         
             
                  base_listener.response
         
     | 
| 
       193 
261 
     | 
    
         
             
                end
         
     | 
| 
         @@ -209,16 +277,20 @@ module RubyLsp 
     | 
|
| 
       209 
277 
     | 
    
         
             
                  document = @store.get(uri)
         
     | 
| 
       210 
278 
     | 
    
         
             
                  return if document.syntax_error?
         
     | 
| 
       211 
279 
     | 
    
         | 
| 
       212 
     | 
    
         
            -
                  target, parent = document.locate_node( 
     | 
| 
      
 280 
     | 
    
         
            +
                  target, parent, nesting = document.locate_node(
         
     | 
| 
      
 281 
     | 
    
         
            +
                    position,
         
     | 
| 
      
 282 
     | 
    
         
            +
                    node_types: Requests::Hover::ALLOWED_TARGETS,
         
     | 
| 
      
 283 
     | 
    
         
            +
                  )
         
     | 
| 
       213 
284 
     | 
    
         | 
| 
       214 
     | 
    
         
            -
                  if  
     | 
| 
       215 
     | 
    
         
            -
                      Requests::Hover::ALLOWED_TARGETS.include?( 
     | 
| 
      
 285 
     | 
    
         
            +
                  if (Requests::Hover::ALLOWED_TARGETS.include?(parent.class) &&
         
     | 
| 
      
 286 
     | 
    
         
            +
                      !Requests::Hover::ALLOWED_TARGETS.include?(target.class)) ||
         
     | 
| 
      
 287 
     | 
    
         
            +
                      (parent.is_a?(SyntaxTree::ConstPathRef) && target.is_a?(SyntaxTree::Const))
         
     | 
| 
       216 
288 
     | 
    
         
             
                    target = parent
         
     | 
| 
       217 
289 
     | 
    
         
             
                  end
         
     | 
| 
       218 
290 
     | 
    
         | 
| 
       219 
291 
     | 
    
         
             
                  # Instantiate all listeners
         
     | 
| 
       220 
292 
     | 
    
         
             
                  emitter = EventEmitter.new
         
     | 
| 
       221 
     | 
    
         
            -
                  hover = Requests::Hover.new(emitter, @message_queue)
         
     | 
| 
      
 293 
     | 
    
         
            +
                  hover = Requests::Hover.new(@index, nesting, emitter, @message_queue)
         
     | 
| 
       222 
294 
     | 
    
         | 
| 
       223 
295 
     | 
    
         
             
                  # Emit events for all listeners
         
     | 
| 
       224 
296 
     | 
    
         
             
                  emitter.emit_for_target(target)
         
     | 
| 
         @@ -436,6 +508,37 @@ module RubyLsp 
     | 
|
| 
       436 
508 
     | 
    
         
             
                  listener.response
         
     | 
| 
       437 
509 
     | 
    
         
             
                end
         
     | 
| 
       438 
510 
     | 
    
         | 
| 
      
 511 
     | 
    
         
            +
                sig { params(id: String, title: String).void }
         
     | 
| 
      
 512 
     | 
    
         
            +
                def begin_progress(id, title)
         
     | 
| 
      
 513 
     | 
    
         
            +
                  return unless @store.supports_progress
         
     | 
| 
      
 514 
     | 
    
         
            +
             
     | 
| 
      
 515 
     | 
    
         
            +
                  @message_queue << Request.new(
         
     | 
| 
      
 516 
     | 
    
         
            +
                    message: "window/workDoneProgress/create",
         
     | 
| 
      
 517 
     | 
    
         
            +
                    params: Interface::WorkDoneProgressCreateParams.new(token: id),
         
     | 
| 
      
 518 
     | 
    
         
            +
                  )
         
     | 
| 
      
 519 
     | 
    
         
            +
             
     | 
| 
      
 520 
     | 
    
         
            +
                  @message_queue << Notification.new(
         
     | 
| 
      
 521 
     | 
    
         
            +
                    message: "$/progress",
         
     | 
| 
      
 522 
     | 
    
         
            +
                    params: Interface::ProgressParams.new(
         
     | 
| 
      
 523 
     | 
    
         
            +
                      token: id,
         
     | 
| 
      
 524 
     | 
    
         
            +
                      value: Interface::WorkDoneProgressBegin.new(kind: "begin", title: title),
         
     | 
| 
      
 525 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 526 
     | 
    
         
            +
                  )
         
     | 
| 
      
 527 
     | 
    
         
            +
                end
         
     | 
| 
      
 528 
     | 
    
         
            +
             
     | 
| 
      
 529 
     | 
    
         
            +
                sig { params(id: String).void }
         
     | 
| 
      
 530 
     | 
    
         
            +
                def end_progress(id)
         
     | 
| 
      
 531 
     | 
    
         
            +
                  return unless @store.supports_progress
         
     | 
| 
      
 532 
     | 
    
         
            +
             
     | 
| 
      
 533 
     | 
    
         
            +
                  @message_queue << Notification.new(
         
     | 
| 
      
 534 
     | 
    
         
            +
                    message: "$/progress",
         
     | 
| 
      
 535 
     | 
    
         
            +
                    params: Interface::ProgressParams.new(
         
     | 
| 
      
 536 
     | 
    
         
            +
                      token: id,
         
     | 
| 
      
 537 
     | 
    
         
            +
                      value: Interface::WorkDoneProgressEnd.new(kind: "end"),
         
     | 
| 
      
 538 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 539 
     | 
    
         
            +
                  )
         
     | 
| 
      
 540 
     | 
    
         
            +
                end
         
     | 
| 
      
 541 
     | 
    
         
            +
             
     | 
| 
       439 
542 
     | 
    
         
             
                sig { params(options: T::Hash[Symbol, T.untyped]).returns(Interface::InitializeResult) }
         
     | 
| 
       440 
543 
     | 
    
         
             
                def initialize_request(options)
         
     | 
| 
       441 
544 
     | 
    
         
             
                  @store.clear
         
     | 
| 
         @@ -449,6 +552,7 @@ module RubyLsp 
     | 
|
| 
       449 
552 
     | 
    
         
             
                    encodings.first
         
     | 
| 
       450 
553 
     | 
    
         
             
                  end
         
     | 
| 
       451 
554 
     | 
    
         | 
| 
      
 555 
     | 
    
         
            +
                  @store.supports_progress = options.dig(:capabilities, :window, :workDoneProgress) || true
         
     | 
| 
       452 
556 
     | 
    
         
             
                  formatter = options.dig(:initializationOptions, :formatter) || "auto"
         
     | 
| 
       453 
557 
     | 
    
         
             
                  @store.formatter = if formatter == "auto"
         
     | 
| 
       454 
558 
     | 
    
         
             
                    DependencyDetector.detected_formatter
         
     | 
| 
         @@ -457,9 +561,7 @@ module RubyLsp 
     | 
|
| 
       457 
561 
     | 
    
         
             
                  end
         
     | 
| 
       458 
562 
     | 
    
         | 
| 
       459 
563 
     | 
    
         
             
                  configured_features = options.dig(:initializationOptions, :enabledFeatures)
         
     | 
| 
       460 
     | 
    
         
            -
             
     | 
| 
       461 
     | 
    
         
            -
                  # Uncomment the line below and use the variable to gate features behind the experimental flag
         
     | 
| 
       462 
     | 
    
         
            -
                  # experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled)
         
     | 
| 
      
 564 
     | 
    
         
            +
                  @store.experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
         
     | 
| 
       463 
565 
     | 
    
         | 
| 
       464 
566 
     | 
    
         
             
                  enabled_features = case configured_features
         
     | 
| 
       465 
567 
     | 
    
         
             
                  when Array
         
     | 
| 
         @@ -541,6 +643,37 @@ module RubyLsp 
     | 
|
| 
       541 
643 
     | 
    
         
             
                    )
         
     | 
| 
       542 
644 
     | 
    
         
             
                  end
         
     | 
| 
       543 
645 
     | 
    
         | 
| 
      
 646 
     | 
    
         
            +
                  if @store.experimental_features
         
     | 
| 
      
 647 
     | 
    
         
            +
                    # Dynamically registered capabilities
         
     | 
| 
      
 648 
     | 
    
         
            +
                    file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
         
     | 
| 
      
 649 
     | 
    
         
            +
             
     | 
| 
      
 650 
     | 
    
         
            +
                    # Not every client supports dynamic registration or file watching
         
     | 
| 
      
 651 
     | 
    
         
            +
                    if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
         
     | 
| 
      
 652 
     | 
    
         
            +
                      @message_queue << Request.new(
         
     | 
| 
      
 653 
     | 
    
         
            +
                        message: "client/registerCapability",
         
     | 
| 
      
 654 
     | 
    
         
            +
                        params: Interface::RegistrationParams.new(
         
     | 
| 
      
 655 
     | 
    
         
            +
                          registrations: [
         
     | 
| 
      
 656 
     | 
    
         
            +
                            # Register watching Ruby files
         
     | 
| 
      
 657 
     | 
    
         
            +
                            Interface::Registration.new(
         
     | 
| 
      
 658 
     | 
    
         
            +
                              id: "workspace/didChangeWatchedFiles",
         
     | 
| 
      
 659 
     | 
    
         
            +
                              method: "workspace/didChangeWatchedFiles",
         
     | 
| 
      
 660 
     | 
    
         
            +
                              register_options: Interface::DidChangeWatchedFilesRegistrationOptions.new(
         
     | 
| 
      
 661 
     | 
    
         
            +
                                watchers: [
         
     | 
| 
      
 662 
     | 
    
         
            +
                                  Interface::FileSystemWatcher.new(
         
     | 
| 
      
 663 
     | 
    
         
            +
                                    glob_pattern: "**/*.rb",
         
     | 
| 
      
 664 
     | 
    
         
            +
                                    kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE | Constant::WatchKind::DELETE,
         
     | 
| 
      
 665 
     | 
    
         
            +
                                  ),
         
     | 
| 
      
 666 
     | 
    
         
            +
                                ],
         
     | 
| 
      
 667 
     | 
    
         
            +
                              ),
         
     | 
| 
      
 668 
     | 
    
         
            +
                            ),
         
     | 
| 
      
 669 
     | 
    
         
            +
                          ],
         
     | 
| 
      
 670 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 671 
     | 
    
         
            +
                      )
         
     | 
| 
      
 672 
     | 
    
         
            +
                    end
         
     | 
| 
      
 673 
     | 
    
         
            +
             
     | 
| 
      
 674 
     | 
    
         
            +
                    begin_progress("indexing-progress", "Ruby LSP: indexing files")
         
     | 
| 
      
 675 
     | 
    
         
            +
                  end
         
     | 
| 
      
 676 
     | 
    
         
            +
             
     | 
| 
       544 
677 
     | 
    
         
             
                  Interface::InitializeResult.new(
         
     | 
| 
       545 
678 
     | 
    
         
             
                    capabilities: Interface::ServerCapabilities.new(
         
     | 
| 
       546 
679 
     | 
    
         
             
                      text_document_sync: Interface::TextDocumentSyncOptions.new(
         
     | 
| 
         @@ -563,6 +696,7 @@ module RubyLsp 
     | 
|
| 
       563 
696 
     | 
    
         
             
                      completion_provider: completion_provider,
         
     | 
| 
       564 
697 
     | 
    
         
             
                      code_lens_provider: code_lens_provider,
         
     | 
| 
       565 
698 
     | 
    
         
             
                      definition_provider: enabled_features["definition"],
         
     | 
| 
      
 699 
     | 
    
         
            +
                      workspace_symbol_provider: enabled_features["workspaceSymbol"],
         
     | 
| 
       566 
700 
     | 
    
         
             
                    ),
         
     | 
| 
       567 
701 
     | 
    
         
             
                  )
         
     | 
| 
       568 
702 
     | 
    
         
             
                end
         
     | 
    
        data/lib/ruby_lsp/internal.rb
    CHANGED
    
    | 
         @@ -3,6 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            require "sorbet-runtime"
         
     | 
| 
       5 
5 
     | 
    
         
             
            require "syntax_tree"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "yarp"
         
     | 
| 
       6 
7 
     | 
    
         
             
            require "language_server-protocol"
         
     | 
| 
       7 
8 
     | 
    
         
             
            require "benchmark"
         
     | 
| 
       8 
9 
     | 
    
         
             
            require "bundler"
         
     | 
| 
         @@ -10,6 +11,7 @@ require "uri" 
     | 
|
| 
       10 
11 
     | 
    
         
             
            require "cgi"
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
            require "ruby-lsp"
         
     | 
| 
      
 14 
     | 
    
         
            +
            require "ruby_indexer/ruby_indexer"
         
     | 
| 
       13 
15 
     | 
    
         
             
            require "core_ext/uri"
         
     | 
| 
       14 
16 
     | 
    
         
             
            require "ruby_lsp/utils"
         
     | 
| 
       15 
17 
     | 
    
         
             
            require "ruby_lsp/server"
         
     | 
| 
         @@ -9,29 +9,51 @@ module RubyLsp 
     | 
|
| 
       9 
9 
     | 
    
         
             
                # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
         
     | 
| 
       10 
10 
     | 
    
         
             
                # definition of the symbol under the cursor.
         
     | 
| 
       11 
11 
     | 
    
         
             
                #
         
     | 
| 
       12 
     | 
    
         
            -
                # Currently, only jumping to required files is supported.
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Currently, only jumping to classes, modules and required files is supported.
         
     | 
| 
       13 
13 
     | 
    
         
             
                #
         
     | 
| 
       14 
14 
     | 
    
         
             
                # # Example
         
     | 
| 
       15 
15 
     | 
    
         
             
                #
         
     | 
| 
       16 
16 
     | 
    
         
             
                # ```ruby
         
     | 
| 
       17 
17 
     | 
    
         
             
                # require "some_gem/file" # <- Request go to definition on this string will take you to the file
         
     | 
| 
      
 18 
     | 
    
         
            +
                # Product.new # <- Request go to definition on this class name will take you to its declaration.
         
     | 
| 
       18 
19 
     | 
    
         
             
                # ```
         
     | 
| 
       19 
20 
     | 
    
         
             
                class Definition < Listener
         
     | 
| 
       20 
21 
     | 
    
         
             
                  extend T::Sig
         
     | 
| 
       21 
22 
     | 
    
         
             
                  extend T::Generic
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                  ResponseType = type_member { { fixed: T.nilable(Interface::Location) } }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ResponseType = type_member { { fixed: T.nilable(T.any(T::Array[Interface::Location], Interface::Location)) } }
         
     | 
| 
       24 
25 
     | 
    
         | 
| 
       25 
26 
     | 
    
         
             
                  sig { override.returns(ResponseType) }
         
     | 
| 
       26 
27 
     | 
    
         
             
                  attr_reader :response
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                  sig  
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 29 
     | 
    
         
            +
                  sig do
         
     | 
| 
      
 30 
     | 
    
         
            +
                    params(
         
     | 
| 
      
 31 
     | 
    
         
            +
                      uri: URI::Generic,
         
     | 
| 
      
 32 
     | 
    
         
            +
                      nesting: T::Array[String],
         
     | 
| 
      
 33 
     | 
    
         
            +
                      index: RubyIndexer::Index,
         
     | 
| 
      
 34 
     | 
    
         
            +
                      emitter: EventEmitter,
         
     | 
| 
      
 35 
     | 
    
         
            +
                      message_queue: Thread::Queue,
         
     | 
| 
      
 36 
     | 
    
         
            +
                    ).void
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def initialize(uri, nesting, index, emitter, message_queue)
         
     | 
| 
       30 
39 
     | 
    
         
             
                    super(emitter, message_queue)
         
     | 
| 
       31 
40 
     | 
    
         | 
| 
       32 
41 
     | 
    
         
             
                    @uri = uri
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @nesting = nesting
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @index = index
         
     | 
| 
       33 
44 
     | 
    
         
             
                    @response = T.let(nil, ResponseType)
         
     | 
| 
       34 
     | 
    
         
            -
                    emitter.register(self, :on_command)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    emitter.register(self, :on_command, :on_const, :on_const_path_ref)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  sig { params(node: SyntaxTree::ConstPathRef).void }
         
     | 
| 
      
 49 
     | 
    
         
            +
                  def on_const_path_ref(node)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    name = full_constant_name(node)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    find_in_index(name)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  sig { params(node: SyntaxTree::Const).void }
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def on_const(node)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    find_in_index(node.value)
         
     | 
| 
       35 
57 
     | 
    
         
             
                  end
         
     | 
| 
       36 
58 
     | 
    
         | 
| 
       37 
59 
     | 
    
         
             
                  sig { params(node: SyntaxTree::Command).void }
         
     | 
| 
         @@ -79,6 +101,39 @@ module RubyLsp 
     | 
|
| 
       79 
101 
     | 
    
         | 
| 
       80 
102 
     | 
    
         
             
                  private
         
     | 
| 
       81 
103 
     | 
    
         | 
| 
      
 104 
     | 
    
         
            +
                  sig { params(value: String).void }
         
     | 
| 
      
 105 
     | 
    
         
            +
                  def find_in_index(value)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    entries = @index.resolve(value, @nesting)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    return unless entries
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                    bundle_path = begin
         
     | 
| 
      
 110 
     | 
    
         
            +
                      Bundler.bundle_path.to_s
         
     | 
| 
      
 111 
     | 
    
         
            +
                    rescue Bundler::GemfileNotFound
         
     | 
| 
      
 112 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    @response = entries.filter_map do |entry|
         
     | 
| 
      
 116 
     | 
    
         
            +
                      location = entry.location
         
     | 
| 
      
 117 
     | 
    
         
            +
                      # If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
         
     | 
| 
      
 118 
     | 
    
         
            +
                      # additional behavior on top of jumping to RBIs. Sorbet can already handle go to definition for all constants
         
     | 
| 
      
 119 
     | 
    
         
            +
                      # in the project, even if the files are typed false
         
     | 
| 
      
 120 
     | 
    
         
            +
                      file_path = entry.file_path
         
     | 
| 
      
 121 
     | 
    
         
            +
                      if DependencyDetector::HAS_TYPECHECKER && bundle_path && !file_path.start_with?(bundle_path) &&
         
     | 
| 
      
 122 
     | 
    
         
            +
                          !file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                        next
         
     | 
| 
      
 125 
     | 
    
         
            +
                      end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                      Interface::Location.new(
         
     | 
| 
      
 128 
     | 
    
         
            +
                        uri: URI::Generic.from_path(path: file_path).to_s,
         
     | 
| 
      
 129 
     | 
    
         
            +
                        range: Interface::Range.new(
         
     | 
| 
      
 130 
     | 
    
         
            +
                          start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
         
     | 
| 
      
 131 
     | 
    
         
            +
                          end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
         
     | 
| 
      
 132 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 133 
     | 
    
         
            +
                      )
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
       82 
137 
     | 
    
         
             
                  sig { params(file: String).returns(T.nilable(String)) }
         
     | 
| 
       83 
138 
     | 
    
         
             
                  def find_file_in_load_path(file)
         
     | 
| 
       84 
139 
     | 
    
         
             
                    return unless file.include?("/")
         
     | 
| 
         @@ -3,19 +3,15 @@ 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module RubyLsp
         
     | 
| 
       5 
5 
     | 
    
         
             
              module Requests
         
     | 
| 
       6 
     | 
    
         
            -
                # 
         
     | 
| 
       7 
7 
     | 
    
         
             
                #
         
     | 
| 
       8 
8 
     | 
    
         
             
                # The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
         
     | 
| 
       9 
     | 
    
         
            -
                #  
     | 
| 
       10 
     | 
    
         
            -
                # It currently only supports Rails' documentation: when hovering over Rails DSLs/constants under certain paths,
         
     | 
| 
       11 
     | 
    
         
            -
                # like `before_save :callback` in `models/post.rb`, it generates a link to `before_save`'s API documentation.
         
     | 
| 
      
 9 
     | 
    
         
            +
                # displays the documentation for the symbol currently under the cursor.
         
     | 
| 
       12 
10 
     | 
    
         
             
                #
         
     | 
| 
       13 
11 
     | 
    
         
             
                # # Example
         
     | 
| 
       14 
12 
     | 
    
         
             
                #
         
     | 
| 
       15 
13 
     | 
    
         
             
                # ```ruby
         
     | 
| 
       16 
     | 
    
         
            -
                # class  
     | 
| 
       17 
     | 
    
         
            -
                #   before_save :do_something # when hovering on before_save, the link will be rendered
         
     | 
| 
       18 
     | 
    
         
            -
                # end
         
     | 
| 
      
 14 
     | 
    
         
            +
                # String # -> Hovering over the class reference will show all declaration locations and the documentation
         
     | 
| 
       19 
15 
     | 
    
         
             
                # ```
         
     | 
| 
       20 
16 
     | 
    
         
             
                class Hover < Listener
         
     | 
| 
       21 
17 
     | 
    
         
             
                  extend T::Sig
         
     | 
| 
         @@ -25,6 +21,7 @@ module RubyLsp 
     | 
|
| 
       25 
21 
     | 
    
         | 
| 
       26 
22 
     | 
    
         
             
                  ALLOWED_TARGETS = T.let(
         
     | 
| 
       27 
23 
     | 
    
         
             
                    [
         
     | 
| 
      
 24 
     | 
    
         
            +
                      SyntaxTree::Const,
         
     | 
| 
       28 
25 
     | 
    
         
             
                      SyntaxTree::Command,
         
     | 
| 
       29 
26 
     | 
    
         
             
                      SyntaxTree::CallNode,
         
     | 
| 
       30 
27 
     | 
    
         
             
                      SyntaxTree::ConstPathRef,
         
     | 
| 
         @@ -35,15 +32,24 @@ module RubyLsp 
     | 
|
| 
       35 
32 
     | 
    
         
             
                  sig { override.returns(ResponseType) }
         
     | 
| 
       36 
33 
     | 
    
         
             
                  attr_reader :response
         
     | 
| 
       37 
34 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                  sig  
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
                  sig do
         
     | 
| 
      
 36 
     | 
    
         
            +
                    params(
         
     | 
| 
      
 37 
     | 
    
         
            +
                      index: RubyIndexer::Index,
         
     | 
| 
      
 38 
     | 
    
         
            +
                      nesting: T::Array[String],
         
     | 
| 
      
 39 
     | 
    
         
            +
                      emitter: EventEmitter,
         
     | 
| 
      
 40 
     | 
    
         
            +
                      message_queue: Thread::Queue,
         
     | 
| 
      
 41 
     | 
    
         
            +
                    ).void
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def initialize(index, nesting, emitter, message_queue)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    super(emitter, message_queue)
         
     | 
| 
       41 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                    @nesting = nesting
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @index = index
         
     | 
| 
       42 
48 
     | 
    
         
             
                    @external_listeners.concat(
         
     | 
| 
       43 
49 
     | 
    
         
             
                      Extension.extensions.filter_map { |ext| ext.create_hover_listener(emitter, message_queue) },
         
     | 
| 
       44 
50 
     | 
    
         
             
                    )
         
     | 
| 
       45 
51 
     | 
    
         
             
                    @response = T.let(nil, ResponseType)
         
     | 
| 
       46 
     | 
    
         
            -
                    emitter.register(self, : 
     | 
| 
      
 52 
     | 
    
         
            +
                    emitter.register(self, :on_const_path_ref, :on_const)
         
     | 
| 
       47 
53 
     | 
    
         
             
                  end
         
     | 
| 
       48 
54 
     | 
    
         | 
| 
       49 
55 
     | 
    
         
             
                  # Merges responses from other hover listeners
         
     | 
| 
         @@ -55,40 +61,57 @@ module RubyLsp 
     | 
|
| 
       55 
61 
     | 
    
         
             
                    if @response.nil?
         
     | 
| 
       56 
62 
     | 
    
         
             
                      @response = other.response
         
     | 
| 
       57 
63 
     | 
    
         
             
                    else
         
     | 
| 
       58 
     | 
    
         
            -
                      @response.contents.value <<  
     | 
| 
      
 64 
     | 
    
         
            +
                      @response.contents.value << "\n\n" << other_response.contents.value
         
     | 
| 
       59 
65 
     | 
    
         
             
                    end
         
     | 
| 
       60 
66 
     | 
    
         | 
| 
       61 
67 
     | 
    
         
             
                    self
         
     | 
| 
       62 
68 
     | 
    
         
             
                  end
         
     | 
| 
       63 
69 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                  sig { params(node: SyntaxTree::Command).void }
         
     | 
| 
       65 
     | 
    
         
            -
                  def on_command(node)
         
     | 
| 
       66 
     | 
    
         
            -
                    message = node.message
         
     | 
| 
       67 
     | 
    
         
            -
                    @response = generate_rails_document_link_hover(message.value, message)
         
     | 
| 
       68 
     | 
    
         
            -
                  end
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
70 
     | 
    
         
             
                  sig { params(node: SyntaxTree::ConstPathRef).void }
         
     | 
| 
       71 
71 
     | 
    
         
             
                  def on_const_path_ref(node)
         
     | 
| 
       72 
     | 
    
         
            -
                     
     | 
| 
      
 72 
     | 
    
         
            +
                    return if DependencyDetector::HAS_TYPECHECKER
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    name = full_constant_name(node)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    generate_hover(name, node)
         
     | 
| 
       73 
76 
     | 
    
         
             
                  end
         
     | 
| 
       74 
77 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                  sig { params(node: SyntaxTree:: 
     | 
| 
       76 
     | 
    
         
            -
                  def  
     | 
| 
       77 
     | 
    
         
            -
                     
     | 
| 
       78 
     | 
    
         
            -
                    return if message.is_a?(Symbol)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  sig { params(node: SyntaxTree::Const).void }
         
     | 
| 
      
 79 
     | 
    
         
            +
                  def on_const(node)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    return if DependencyDetector::HAS_TYPECHECKER
         
     | 
| 
       79 
81 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
                     
     | 
| 
      
 82 
     | 
    
         
            +
                    generate_hover(node.value, node)
         
     | 
| 
       81 
83 
     | 
    
         
             
                  end
         
     | 
| 
       82 
84 
     | 
    
         | 
| 
       83 
85 
     | 
    
         
             
                  private
         
     | 
| 
       84 
86 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                  sig { params(name: String, node: SyntaxTree::Node). 
     | 
| 
       86 
     | 
    
         
            -
                  def  
     | 
| 
       87 
     | 
    
         
            -
                     
     | 
| 
       88 
     | 
    
         
            -
                    return  
     | 
| 
      
 87 
     | 
    
         
            +
                  sig { params(name: String, node: SyntaxTree::Node).void }
         
     | 
| 
      
 88 
     | 
    
         
            +
                  def generate_hover(name, node)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    entries = @index.resolve(name, @nesting)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    return unless entries
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    title = +"```ruby\n#{name}\n```"
         
     | 
| 
      
 93 
     | 
    
         
            +
                    definitions = []
         
     | 
| 
      
 94 
     | 
    
         
            +
                    content = +""
         
     | 
| 
      
 95 
     | 
    
         
            +
                    entries.each do |entry|
         
     | 
| 
      
 96 
     | 
    
         
            +
                      loc = entry.location
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                      # We always handle locations as zero based. However, for file links in Markdown we need them to be one based,
         
     | 
| 
      
 99 
     | 
    
         
            +
                      # which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to columns. The
         
     | 
| 
      
 100 
     | 
    
         
            +
                      # format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
         
     | 
| 
      
 101 
     | 
    
         
            +
                      uri = URI::Generic.from_path(
         
     | 
| 
      
 102 
     | 
    
         
            +
                        path: entry.file_path,
         
     | 
| 
      
 103 
     | 
    
         
            +
                        fragment: "L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}",
         
     | 
| 
      
 104 
     | 
    
         
            +
                      )
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                      definitions << "[#{entry.file_name}](#{uri})"
         
     | 
| 
      
 107 
     | 
    
         
            +
                      content << "\n\n#{entry.comments.join("\n")}" unless entry.comments.empty?
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
       89 
109 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
                    contents = Interface::MarkupContent.new( 
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
      
 110 
     | 
    
         
            +
                    contents = Interface::MarkupContent.new(
         
     | 
| 
      
 111 
     | 
    
         
            +
                      kind: "markdown",
         
     | 
| 
      
 112 
     | 
    
         
            +
                      value: "#{title}\n\n**Definitions**: #{definitions.join(" | ")}\n\n#{content}",
         
     | 
| 
      
 113 
     | 
    
         
            +
                    )
         
     | 
| 
      
 114 
     | 
    
         
            +
                    @response = Interface::Hover.new(range: range_from_syntax_tree_node(node), contents: contents)
         
     | 
| 
       92 
115 
     | 
    
         
             
                  end
         
     | 
| 
       93 
116 
     | 
    
         
             
                end
         
     | 
| 
       94 
117 
     | 
    
         
             
              end
         
     | 
| 
         @@ -67,12 +67,10 @@ module RubyLsp 
     | 
|
| 
       67 
67 
     | 
    
         | 
| 
       68 
68 
     | 
    
         
             
                    line = T.must(current_line)
         
     | 
| 
       69 
69 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
                    # If the  
     | 
| 
       71 
     | 
    
         
            -
                    #  
     | 
| 
       72 
     | 
    
         
            -
                     
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                        line[@position[:character]] == "|"
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
      
 70 
     | 
    
         
            +
                    # If the user inserts the closing pipe manually to the end of the block argument, we need to avoid adding
         
     | 
| 
      
 71 
     | 
    
         
            +
                    # an additional one and remove the previous one.  This also helps to remove the user who accidentally
         
     | 
| 
      
 72 
     | 
    
         
            +
                    # inserts another pipe after the autocompleted one.
         
     | 
| 
      
 73 
     | 
    
         
            +
                    if line[..@position[:character]] =~ /(do|{)\s+\|[^|]*\|\|$/
         
     | 
| 
       76 
74 
     | 
    
         
             
                      @edits << Interface::TextEdit.new(
         
     | 
| 
       77 
75 
     | 
    
         
             
                        range: Interface::Range.new(
         
     | 
| 
       78 
76 
     | 
    
         
             
                          start: Interface::Position.new(
         
     | 
| 
         @@ -36,6 +36,11 @@ module RubyLsp 
     | 
|
| 
       36 
36 
     | 
    
         
             
                    end
         
     | 
| 
       37 
37 
     | 
    
         
             
                  end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                  sig { returns(T::Boolean) }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def typechecker?
         
     | 
| 
      
 41 
     | 
    
         
            +
                    direct_dependency?(/^sorbet/) || direct_dependency?(/^sorbet-static-and-runtime/)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
       39 
44 
     | 
    
         
             
                  sig { params(gem_pattern: Regexp).returns(T::Boolean) }
         
     | 
| 
       40 
45 
     | 
    
         
             
                  def direct_dependency?(gem_pattern)
         
     | 
| 
       41 
46 
     | 
    
         
             
                    Bundler.with_original_env { Bundler.default_gemfile } &&
         
     | 
| 
         @@ -44,5 +49,7 @@ module RubyLsp 
     | 
|
| 
       44 
49 
     | 
    
         
             
                    false
         
     | 
| 
       45 
50 
     | 
    
         
             
                  end
         
     | 
| 
       46 
51 
     | 
    
         
             
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                HAS_TYPECHECKER = T.let(typechecker?, T::Boolean)
         
     | 
| 
       47 
54 
     | 
    
         
             
              end
         
     | 
| 
       48 
55 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,86 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module RubyLsp
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Requests
         
     | 
| 
      
 6 
     | 
    
         
            +
                # 
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # The [workspace symbol](https://microsoft.github.io/language-server-protocol/specification#workspace_symbol)
         
     | 
| 
      
 9 
     | 
    
         
            +
                # request allows fuzzy searching declarations in the entire project. On VS Code, use CTRL/CMD + T to search for
         
     | 
| 
      
 10 
     | 
    
         
            +
                # symbols.
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # # Example
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # ```ruby
         
     | 
| 
      
 15 
     | 
    
         
            +
                # # Searching for `Floo` will fuzzy match and return all declarations according to the query, including this `Foo`
         
     | 
| 
      
 16 
     | 
    
         
            +
                # class
         
     | 
| 
      
 17 
     | 
    
         
            +
                # class Foo
         
     | 
| 
      
 18 
     | 
    
         
            +
                # end
         
     | 
| 
      
 19 
     | 
    
         
            +
                # ```
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                class WorkspaceSymbol
         
     | 
| 
      
 22 
     | 
    
         
            +
                  extend T::Sig
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  sig { params(query: T.nilable(String), index: RubyIndexer::Index).void }
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def initialize(query, index)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @query = query
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @index = index
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  sig { returns(T::Array[Interface::WorkspaceSymbol]) }
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def run
         
     | 
| 
      
 32 
     | 
    
         
            +
                    bundle_path = begin
         
     | 
| 
      
 33 
     | 
    
         
            +
                      Bundler.bundle_path.to_s
         
     | 
| 
      
 34 
     | 
    
         
            +
                    rescue Bundler::GemfileNotFound
         
     | 
| 
      
 35 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    @index.fuzzy_search(@query).filter_map do |entry|
         
     | 
| 
      
 39 
     | 
    
         
            +
                      # If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
         
     | 
| 
      
 40 
     | 
    
         
            +
                      # we still return entries defined in gems to allow developers to jump directly to the source
         
     | 
| 
      
 41 
     | 
    
         
            +
                      file_path = entry.file_path
         
     | 
| 
      
 42 
     | 
    
         
            +
                      if DependencyDetector::HAS_TYPECHECKER && bundle_path && !file_path.start_with?(bundle_path) &&
         
     | 
| 
      
 43 
     | 
    
         
            +
                          !file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                        next
         
     | 
| 
      
 46 
     | 
    
         
            +
                      end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                      kind = kind_for_entry(entry)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      loc = entry.location
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                      # We use the namespace as the container name, but we also use the full name as the regular name. The reason we
         
     | 
| 
      
 52 
     | 
    
         
            +
                      # do this is to allow people to search for fully qualified names (e.g.: `Foo::Bar`). If we only included the
         
     | 
| 
      
 53 
     | 
    
         
            +
                      # short name `Bar`, then searching for `Foo::Bar` would not return any results
         
     | 
| 
      
 54 
     | 
    
         
            +
                      *container, _short_name = entry.name.split("::")
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      Interface::WorkspaceSymbol.new(
         
     | 
| 
      
 57 
     | 
    
         
            +
                        name: entry.name,
         
     | 
| 
      
 58 
     | 
    
         
            +
                        container_name: T.must(container).join("::"),
         
     | 
| 
      
 59 
     | 
    
         
            +
                        kind: kind,
         
     | 
| 
      
 60 
     | 
    
         
            +
                        location: Interface::Location.new(
         
     | 
| 
      
 61 
     | 
    
         
            +
                          uri: URI::Generic.from_path(path: file_path).to_s,
         
     | 
| 
      
 62 
     | 
    
         
            +
                          range:  Interface::Range.new(
         
     | 
| 
      
 63 
     | 
    
         
            +
                            start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
         
     | 
| 
      
 64 
     | 
    
         
            +
                            end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
         
     | 
| 
      
 65 
     | 
    
         
            +
                          ),
         
     | 
| 
      
 66 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 67 
     | 
    
         
            +
                      )
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  private
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  sig { params(entry: RubyIndexer::Index::Entry).returns(T.nilable(Integer)) }
         
     | 
| 
      
 74 
     | 
    
         
            +
                  def kind_for_entry(entry)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    case entry
         
     | 
| 
      
 76 
     | 
    
         
            +
                    when RubyIndexer::Index::Entry::Class
         
     | 
| 
      
 77 
     | 
    
         
            +
                      Constant::SymbolKind::CLASS
         
     | 
| 
      
 78 
     | 
    
         
            +
                    when RubyIndexer::Index::Entry::Module
         
     | 
| 
      
 79 
     | 
    
         
            +
                      Constant::SymbolKind::NAMESPACE
         
     | 
| 
      
 80 
     | 
    
         
            +
                    when RubyIndexer::Index::Entry::Constant
         
     | 
| 
      
 81 
     | 
    
         
            +
                      Constant::SymbolKind::CONSTANT
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ruby_lsp/requests.rb
    CHANGED
    
    | 
         @@ -41,6 +41,7 @@ module RubyLsp 
     | 
|
| 
       41 
41 
     | 
    
         
             
                autoload :CodeLens, "ruby_lsp/requests/code_lens"
         
     | 
| 
       42 
42 
     | 
    
         
             
                autoload :Definition, "ruby_lsp/requests/definition"
         
     | 
| 
       43 
43 
     | 
    
         
             
                autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
         
     | 
| 
      
 44 
     | 
    
         
            +
                autoload :WorkspaceSymbol, "ruby_lsp/requests/workspace_symbol"
         
     | 
| 
       44 
45 
     | 
    
         | 
| 
       45 
46 
     | 
    
         
             
                # :nodoc:
         
     | 
| 
       46 
47 
     | 
    
         
             
                module Support
         
     |