ruby-lsp 0.4.4 → 0.5.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/README.md +5 -86
 - data/VERSION +1 -1
 - data/exe/ruby-lsp +10 -1
 - data/lib/ruby_lsp/check_docs.rb +112 -0
 - data/lib/ruby_lsp/document.rb +13 -2
 - data/lib/ruby_lsp/event_emitter.rb +86 -18
 - data/lib/ruby_lsp/executor.rb +146 -45
 - data/lib/ruby_lsp/extension.rb +104 -0
 - data/lib/ruby_lsp/internal.rb +2 -0
 - data/lib/ruby_lsp/listener.rb +14 -11
 - data/lib/ruby_lsp/requests/base_request.rb +0 -5
 - data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
 - data/lib/ruby_lsp/requests/code_actions.rb +1 -1
 - data/lib/ruby_lsp/requests/code_lens.rb +82 -66
 - data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
 - data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
 - data/lib/ruby_lsp/requests/document_link.rb +17 -15
 - data/lib/ruby_lsp/requests/document_symbol.rb +51 -31
 - data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
 - data/lib/ruby_lsp/requests/formatting.rb +10 -11
 - data/lib/ruby_lsp/requests/hover.rb +34 -19
 - data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
 - data/lib/ruby_lsp/requests/on_type_formatting.rb +5 -1
 - data/lib/ruby_lsp/requests/path_completion.rb +21 -57
 - data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
 - data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
 - data/lib/ruby_lsp/requests/support/common.rb +36 -0
 - data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -1
 - data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
 - data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +5 -2
 - data/lib/ruby_lsp/requests.rb +15 -15
 - data/lib/ruby_lsp/server.rb +44 -11
 - data/lib/ruby_lsp/store.rb +1 -1
 - data/lib/ruby_lsp/utils.rb +9 -8
 - metadata +5 -3
 
    
        data/lib/ruby_lsp/executor.rb
    CHANGED
    
    | 
         @@ -6,12 +6,12 @@ module RubyLsp 
     | 
|
| 
       6 
6 
     | 
    
         
             
              class Executor
         
     | 
| 
       7 
7 
     | 
    
         
             
                extend T::Sig
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                sig { params(store: Store).void }
         
     | 
| 
       10 
     | 
    
         
            -
                def initialize(store)
         
     | 
| 
      
 9 
     | 
    
         
            +
                sig { params(store: Store, message_queue: Thread::Queue).void }
         
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(store, message_queue)
         
     | 
| 
       11 
11 
     | 
    
         
             
                  # Requests that mutate the store must be run sequentially! Parallel requests only receive a temporary copy of the
         
     | 
| 
       12 
12 
     | 
    
         
             
                  # store
         
     | 
| 
       13 
13 
     | 
    
         
             
                  @store = store
         
     | 
| 
       14 
     | 
    
         
            -
                  @ 
     | 
| 
      
 14 
     | 
    
         
            +
                  @message_queue = message_queue
         
     | 
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                sig { params(request: T::Hash[Symbol, T.untyped]).returns(Result) }
         
     | 
| 
         @@ -25,7 +25,7 @@ module RubyLsp 
     | 
|
| 
       25 
25 
     | 
    
         
             
                    error = e
         
     | 
| 
       26 
26 
     | 
    
         
             
                  end
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                  Result.new(response: response, error: error, request_time: request_time 
     | 
| 
      
 28 
     | 
    
         
            +
                  Result.new(response: response, error: error, request_time: request_time)
         
     | 
| 
       29 
29 
     | 
    
         
             
                end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                private
         
     | 
| 
         @@ -38,6 +38,24 @@ module RubyLsp 
     | 
|
| 
       38 
38 
     | 
    
         
             
                  when "initialize"
         
     | 
| 
       39 
39 
     | 
    
         
             
                    initialize_request(request.dig(:params))
         
     | 
| 
       40 
40 
     | 
    
         
             
                  when "initialized"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    Extension.load_extensions
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    errored_extensions = Extension.extensions.select(&:error?)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    if errored_extensions.any?
         
     | 
| 
      
 46 
     | 
    
         
            +
                      @message_queue << Notification.new(
         
     | 
| 
      
 47 
     | 
    
         
            +
                        message: "window/showMessage",
         
     | 
| 
      
 48 
     | 
    
         
            +
                        params: Interface::ShowMessageParams.new(
         
     | 
| 
      
 49 
     | 
    
         
            +
                          type: Constant::MessageType::WARNING,
         
     | 
| 
      
 50 
     | 
    
         
            +
                          message: "Error loading extensions:\n\n#{errored_extensions.map(&:formatted_errors).join("\n\n")}",
         
     | 
| 
      
 51 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 52 
     | 
    
         
            +
                      )
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      warn(errored_extensions.map(&:backtraces).join("\n\n"))
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    check_formatter_is_available
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
       41 
59 
     | 
    
         
             
                    warn("Ruby LSP is ready")
         
     | 
| 
       42 
60 
     | 
    
         
             
                    VOID
         
     | 
| 
       43 
61 
     | 
    
         
             
                  when "textDocument/didOpen"
         
     | 
| 
         @@ -47,7 +65,7 @@ module RubyLsp 
     | 
|
| 
       47 
65 
     | 
    
         
             
                      request.dig(:params, :textDocument, :version),
         
     | 
| 
       48 
66 
     | 
    
         
             
                    )
         
     | 
| 
       49 
67 
     | 
    
         
             
                  when "textDocument/didClose"
         
     | 
| 
       50 
     | 
    
         
            -
                    @ 
     | 
| 
      
 68 
     | 
    
         
            +
                    @message_queue << Notification.new(
         
     | 
| 
       51 
69 
     | 
    
         
             
                      message: "textDocument/publishDiagnostics",
         
     | 
| 
       52 
70 
     | 
    
         
             
                      params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: []),
         
     | 
| 
       53 
71 
     | 
    
         
             
                    )
         
     | 
| 
         @@ -61,12 +79,33 @@ module RubyLsp 
     | 
|
| 
       61 
79 
     | 
    
         
             
                    )
         
     | 
| 
       62 
80 
     | 
    
         
             
                  when "textDocument/foldingRange"
         
     | 
| 
       63 
81 
     | 
    
         
             
                    folding_range(uri)
         
     | 
| 
       64 
     | 
    
         
            -
                  when "textDocument/documentLink"
         
     | 
| 
       65 
     | 
    
         
            -
                    document_link(uri)
         
     | 
| 
       66 
82 
     | 
    
         
             
                  when "textDocument/selectionRange"
         
     | 
| 
       67 
83 
     | 
    
         
             
                    selection_range(uri, request.dig(:params, :positions))
         
     | 
| 
       68 
     | 
    
         
            -
                  when "textDocument/documentSymbol"
         
     | 
| 
       69 
     | 
    
         
            -
                     
     | 
| 
      
 84 
     | 
    
         
            +
                  when "textDocument/documentSymbol", "textDocument/documentLink", "textDocument/codeLens"
         
     | 
| 
      
 85 
     | 
    
         
            +
                    document = @store.get(uri)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    # If the response has already been cached by another request, return it
         
     | 
| 
      
 88 
     | 
    
         
            +
                    cached_response = document.cache_get(request[:method])
         
     | 
| 
      
 89 
     | 
    
         
            +
                    return cached_response if cached_response
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    # Run listeners for the document
         
     | 
| 
      
 92 
     | 
    
         
            +
                    emitter = EventEmitter.new
         
     | 
| 
      
 93 
     | 
    
         
            +
                    document_symbol = Requests::DocumentSymbol.new(emitter, @message_queue)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    document_link = Requests::DocumentLink.new(uri, emitter, @message_queue)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    code_lens = Requests::CodeLens.new(uri, emitter, @message_queue)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    code_lens_extensions_listeners = Requests::CodeLens.listeners.map do |l|
         
     | 
| 
      
 97 
     | 
    
         
            +
                      T.unsafe(l).new(document.uri, emitter, @message_queue)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
                    emitter.visit(document.tree) if document.parsed?
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    code_lens_extensions_listeners.each { |ext| code_lens.merge_response!(ext) }
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    # Store all responses retrieve in this round of visits in the cache and then return the response for the request
         
     | 
| 
      
 104 
     | 
    
         
            +
                    # we actually received
         
     | 
| 
      
 105 
     | 
    
         
            +
                    document.cache_set("textDocument/documentSymbol", document_symbol.response)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    document.cache_set("textDocument/documentLink", document_link.response)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    document.cache_set("textDocument/codeLens", code_lens.response)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    document.cache_get(request[:method])
         
     | 
| 
       70 
109 
     | 
    
         
             
                  when "textDocument/semanticTokens/full"
         
     | 
| 
       71 
110 
     | 
    
         
             
                    semantic_tokens_full(uri)
         
     | 
| 
       72 
111 
     | 
    
         
             
                  when "textDocument/semanticTokens/range"
         
     | 
| 
         @@ -75,7 +114,7 @@ module RubyLsp 
     | 
|
| 
       75 
114 
     | 
    
         
             
                    begin
         
     | 
| 
       76 
115 
     | 
    
         
             
                      formatting(uri)
         
     | 
| 
       77 
116 
     | 
    
         
             
                    rescue Requests::Formatting::InvalidFormatter => error
         
     | 
| 
       78 
     | 
    
         
            -
                      @ 
     | 
| 
      
 117 
     | 
    
         
            +
                      @message_queue << Notification.new(
         
     | 
| 
       79 
118 
     | 
    
         
             
                        message: "window/showMessage",
         
     | 
| 
       80 
119 
     | 
    
         
             
                        params: Interface::ShowMessageParams.new(
         
     | 
| 
       81 
120 
     | 
    
         
             
                          type: Constant::MessageType::ERROR,
         
     | 
| 
         @@ -85,7 +124,7 @@ module RubyLsp 
     | 
|
| 
       85 
124 
     | 
    
         | 
| 
       86 
125 
     | 
    
         
             
                      nil
         
     | 
| 
       87 
126 
     | 
    
         
             
                    rescue StandardError => error
         
     | 
| 
       88 
     | 
    
         
            -
                      @ 
     | 
| 
      
 127 
     | 
    
         
            +
                      @message_queue << Notification.new(
         
     | 
| 
       89 
128 
     | 
    
         
             
                        message: "window/showMessage",
         
     | 
| 
       90 
129 
     | 
    
         
             
                        params: Interface::ShowMessageParams.new(
         
     | 
| 
       91 
130 
     | 
    
         
             
                          type: Constant::MessageType::ERROR,
         
     | 
| 
         @@ -111,7 +150,7 @@ module RubyLsp 
     | 
|
| 
       111 
150 
     | 
    
         
             
                    begin
         
     | 
| 
       112 
151 
     | 
    
         
             
                      diagnostic(uri)
         
     | 
| 
       113 
152 
     | 
    
         
             
                    rescue StandardError => error
         
     | 
| 
       114 
     | 
    
         
            -
                      @ 
     | 
| 
      
 153 
     | 
    
         
            +
                      @message_queue << Notification.new(
         
     | 
| 
       115 
154 
     | 
    
         
             
                        message: "window/showMessage",
         
     | 
| 
       116 
155 
     | 
    
         
             
                        params: Interface::ShowMessageParams.new(
         
     | 
| 
       117 
156 
     | 
    
         
             
                          type: Constant::MessageType::ERROR,
         
     | 
| 
         @@ -123,25 +162,16 @@ module RubyLsp 
     | 
|
| 
       123 
162 
     | 
    
         
             
                    end
         
     | 
| 
       124 
163 
     | 
    
         
             
                  when "textDocument/completion"
         
     | 
| 
       125 
164 
     | 
    
         
             
                    completion(uri, request.dig(:params, :position))
         
     | 
| 
       126 
     | 
    
         
            -
                  when "textDocument/codeLens"
         
     | 
| 
       127 
     | 
    
         
            -
                    code_lens(uri)
         
     | 
| 
       128 
165 
     | 
    
         
             
                  end
         
     | 
| 
       129 
166 
     | 
    
         
             
                end
         
     | 
| 
       130 
167 
     | 
    
         | 
| 
       131 
168 
     | 
    
         
             
                sig { params(uri: String).returns(T::Array[Interface::FoldingRange]) }
         
     | 
| 
       132 
169 
     | 
    
         
             
                def folding_range(uri)
         
     | 
| 
       133 
     | 
    
         
            -
                  @store.cache_fetch(uri,  
     | 
| 
      
 170 
     | 
    
         
            +
                  @store.cache_fetch(uri, "textDocument/foldingRange") do |document|
         
     | 
| 
       134 
171 
     | 
    
         
             
                    Requests::FoldingRanges.new(document).run
         
     | 
| 
       135 
172 
     | 
    
         
             
                  end
         
     | 
| 
       136 
173 
     | 
    
         
             
                end
         
     | 
| 
       137 
174 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
                sig { params(uri: String).returns(T::Array[Interface::CodeLens]) }
         
     | 
| 
       139 
     | 
    
         
            -
                def code_lens(uri)
         
     | 
| 
       140 
     | 
    
         
            -
                  @store.cache_fetch(uri, :code_lens) do |document|
         
     | 
| 
       141 
     | 
    
         
            -
                    Requests::CodeLens.new(document).run
         
     | 
| 
       142 
     | 
    
         
            -
                  end
         
     | 
| 
       143 
     | 
    
         
            -
                end
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
175 
     | 
    
         
             
                sig do
         
     | 
| 
       146 
176 
     | 
    
         
             
                  params(
         
     | 
| 
       147 
177 
     | 
    
         
             
                    uri: String,
         
     | 
| 
         @@ -150,7 +180,6 @@ module RubyLsp 
     | 
|
| 
       150 
180 
     | 
    
         
             
                end
         
     | 
| 
       151 
181 
     | 
    
         
             
                def hover(uri, position)
         
     | 
| 
       152 
182 
     | 
    
         
             
                  document = @store.get(uri)
         
     | 
| 
       153 
     | 
    
         
            -
                  document.parse
         
     | 
| 
       154 
183 
     | 
    
         
             
                  return if document.syntax_error?
         
     | 
| 
       155 
184 
     | 
    
         | 
| 
       156 
185 
     | 
    
         
             
                  target, parent = document.locate_node(position)
         
     | 
| 
         @@ -160,23 +189,17 @@ module RubyLsp 
     | 
|
| 
       160 
189 
     | 
    
         
             
                    target = parent
         
     | 
| 
       161 
190 
     | 
    
         
             
                  end
         
     | 
| 
       162 
191 
     | 
    
         | 
| 
       163 
     | 
    
         
            -
                   
     | 
| 
       164 
     | 
    
         
            -
                  EventEmitter.new 
     | 
| 
       165 
     | 
    
         
            -
                   
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
      
 192 
     | 
    
         
            +
                  # Instantiate all listeners
         
     | 
| 
      
 193 
     | 
    
         
            +
                  emitter = EventEmitter.new
         
     | 
| 
      
 194 
     | 
    
         
            +
                  base_listener = Requests::Hover.new(emitter, @message_queue)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  listeners = Requests::Hover.listeners.map { |l| l.new(emitter, @message_queue) }
         
     | 
| 
       167 
196 
     | 
    
         | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
                  @store.cache_fetch(uri, :document_link) do |document|
         
     | 
| 
       171 
     | 
    
         
            -
                    RubyLsp::Requests::DocumentLink.new(document).run
         
     | 
| 
       172 
     | 
    
         
            -
                  end
         
     | 
| 
       173 
     | 
    
         
            -
                end
         
     | 
| 
      
 197 
     | 
    
         
            +
                  # Emit events for all listeners
         
     | 
| 
      
 198 
     | 
    
         
            +
                  emitter.emit_for_target(target)
         
     | 
| 
       174 
199 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
                   
     | 
| 
       178 
     | 
    
         
            -
                    Requests::DocumentSymbol.new(document).run
         
     | 
| 
       179 
     | 
    
         
            -
                  end
         
     | 
| 
      
 200 
     | 
    
         
            +
                  # Merge all responses into a single hover
         
     | 
| 
      
 201 
     | 
    
         
            +
                  listeners.each { |ext| base_listener.merge_response!(ext) }
         
     | 
| 
      
 202 
     | 
    
         
            +
                  base_listener.response
         
     | 
| 
       180 
203 
     | 
    
         
             
                end
         
     | 
| 
       181 
204 
     | 
    
         | 
| 
       182 
205 
     | 
    
         
             
                sig { params(uri: String, content_changes: T::Array[Document::EditShape], version: Integer).returns(Object) }
         
     | 
| 
         @@ -204,7 +227,7 @@ module RubyLsp 
     | 
|
| 
       204 
227 
     | 
    
         
             
                  ).returns(T.nilable(T::Array[T.nilable(Requests::Support::SelectionRange)]))
         
     | 
| 
       205 
228 
     | 
    
         
             
                end
         
     | 
| 
       206 
229 
     | 
    
         
             
                def selection_range(uri, positions)
         
     | 
| 
       207 
     | 
    
         
            -
                  ranges = @store.cache_fetch(uri,  
     | 
| 
      
 230 
     | 
    
         
            +
                  ranges = @store.cache_fetch(uri, "textDocument/selectionRange") do |document|
         
     | 
| 
       208 
231 
     | 
    
         
             
                    Requests::SelectionRanges.new(document).run
         
     | 
| 
       209 
232 
     | 
    
         
             
                  end
         
     | 
| 
       210 
233 
     | 
    
         | 
| 
         @@ -224,7 +247,7 @@ module RubyLsp 
     | 
|
| 
       224 
247 
     | 
    
         | 
| 
       225 
248 
     | 
    
         
             
                sig { params(uri: String).returns(Interface::SemanticTokens) }
         
     | 
| 
       226 
249 
     | 
    
         
             
                def semantic_tokens_full(uri)
         
     | 
| 
       227 
     | 
    
         
            -
                  @store.cache_fetch(uri,  
     | 
| 
      
 250 
     | 
    
         
            +
                  @store.cache_fetch(uri, "textDocument/semanticTokens/full") do |document|
         
     | 
| 
       228 
251 
     | 
    
         
             
                    T.cast(
         
     | 
| 
       229 
252 
     | 
    
         
             
                      Requests::SemanticHighlighting.new(
         
     | 
| 
       230 
253 
     | 
    
         
             
                        document,
         
     | 
| 
         @@ -237,6 +260,9 @@ module RubyLsp 
     | 
|
| 
       237 
260 
     | 
    
         | 
| 
       238 
261 
     | 
    
         
             
                sig { params(uri: String).returns(T.nilable(T::Array[Interface::TextEdit])) }
         
     | 
| 
       239 
262 
     | 
    
         
             
                def formatting(uri)
         
     | 
| 
      
 263 
     | 
    
         
            +
                  # If formatter is set to `auto` but no supported formatting gem is found, don't attempt to format
         
     | 
| 
      
 264 
     | 
    
         
            +
                  return if @store.formatter == "none"
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
       240 
266 
     | 
    
         
             
                  Requests::Formatting.new(@store.get(uri), formatter: @store.formatter).run
         
     | 
| 
       241 
267 
     | 
    
         
             
                end
         
     | 
| 
       242 
268 
     | 
    
         | 
| 
         @@ -291,7 +317,7 @@ module RubyLsp 
     | 
|
| 
       291 
317 
     | 
    
         | 
| 
       292 
318 
     | 
    
         
             
                  case result
         
     | 
| 
       293 
319 
     | 
    
         
             
                  when Requests::CodeActionResolve::Error::EmptySelection
         
     | 
| 
       294 
     | 
    
         
            -
                    @ 
     | 
| 
      
 320 
     | 
    
         
            +
                    @message_queue << Notification.new(
         
     | 
| 
       295 
321 
     | 
    
         
             
                      message: "window/showMessage",
         
     | 
| 
       296 
322 
     | 
    
         
             
                      params: Interface::ShowMessageParams.new(
         
     | 
| 
       297 
323 
     | 
    
         
             
                        type: Constant::MessageType::ERROR,
         
     | 
| 
         @@ -300,7 +326,7 @@ module RubyLsp 
     | 
|
| 
       300 
326 
     | 
    
         
             
                    )
         
     | 
| 
       301 
327 
     | 
    
         
             
                    raise Requests::CodeActionResolve::CodeActionError
         
     | 
| 
       302 
328 
     | 
    
         
             
                  when Requests::CodeActionResolve::Error::InvalidTargetRange
         
     | 
| 
       303 
     | 
    
         
            -
                    @ 
     | 
| 
      
 329 
     | 
    
         
            +
                    @message_queue << Notification.new(
         
     | 
| 
       304 
330 
     | 
    
         
             
                      message: "window/showMessage",
         
     | 
| 
       305 
331 
     | 
    
         
             
                      params: Interface::ShowMessageParams.new(
         
     | 
| 
       306 
332 
     | 
    
         
             
                        type: Constant::MessageType::ERROR,
         
     | 
| 
         @@ -315,7 +341,7 @@ module RubyLsp 
     | 
|
| 
       315 
341 
     | 
    
         | 
| 
       316 
342 
     | 
    
         
             
                sig { params(uri: String).returns(T.nilable(Interface::FullDocumentDiagnosticReport)) }
         
     | 
| 
       317 
343 
     | 
    
         
             
                def diagnostic(uri)
         
     | 
| 
       318 
     | 
    
         
            -
                  response = @store.cache_fetch(uri,  
     | 
| 
      
 344 
     | 
    
         
            +
                  response = @store.cache_fetch(uri, "textDocument/diagnostic") do |document|
         
     | 
| 
       319 
345 
     | 
    
         
             
                    Requests::Diagnostics.new(document).run
         
     | 
| 
       320 
346 
     | 
    
         
             
                  end
         
     | 
| 
       321 
347 
     | 
    
         | 
| 
         @@ -342,7 +368,44 @@ module RubyLsp 
     | 
|
| 
       342 
368 
     | 
    
         
             
                  params(uri: String, position: Document::PositionShape).returns(T.nilable(T::Array[Interface::CompletionItem]))
         
     | 
| 
       343 
369 
     | 
    
         
             
                end
         
     | 
| 
       344 
370 
     | 
    
         
             
                def completion(uri, position)
         
     | 
| 
       345 
     | 
    
         
            -
                   
     | 
| 
      
 371 
     | 
    
         
            +
                  document = @store.get(uri)
         
     | 
| 
      
 372 
     | 
    
         
            +
                  return unless document.parsed?
         
     | 
| 
      
 373 
     | 
    
         
            +
             
     | 
| 
      
 374 
     | 
    
         
            +
                  char_position = document.create_scanner.find_char_position(position)
         
     | 
| 
      
 375 
     | 
    
         
            +
                  matched, parent = document.locate(
         
     | 
| 
      
 376 
     | 
    
         
            +
                    T.must(document.tree),
         
     | 
| 
      
 377 
     | 
    
         
            +
                    char_position,
         
     | 
| 
      
 378 
     | 
    
         
            +
                    node_types: [SyntaxTree::Command, SyntaxTree::CommandCall, SyntaxTree::CallNode],
         
     | 
| 
      
 379 
     | 
    
         
            +
                  )
         
     | 
| 
      
 380 
     | 
    
         
            +
             
     | 
| 
      
 381 
     | 
    
         
            +
                  return unless matched && parent
         
     | 
| 
      
 382 
     | 
    
         
            +
             
     | 
| 
      
 383 
     | 
    
         
            +
                  target = case matched
         
     | 
| 
      
 384 
     | 
    
         
            +
                  when SyntaxTree::Command, SyntaxTree::CallNode, SyntaxTree::CommandCall
         
     | 
| 
      
 385 
     | 
    
         
            +
                    message = matched.message
         
     | 
| 
      
 386 
     | 
    
         
            +
                    return if message.is_a?(Symbol)
         
     | 
| 
      
 387 
     | 
    
         
            +
                    return unless message.value == "require"
         
     | 
| 
      
 388 
     | 
    
         
            +
             
     | 
| 
      
 389 
     | 
    
         
            +
                    args = matched.arguments
         
     | 
| 
      
 390 
     | 
    
         
            +
                    args = args.arguments if args.is_a?(SyntaxTree::ArgParen)
         
     | 
| 
      
 391 
     | 
    
         
            +
                    return if args.nil? || args.is_a?(SyntaxTree::ArgsForward)
         
     | 
| 
      
 392 
     | 
    
         
            +
             
     | 
| 
      
 393 
     | 
    
         
            +
                    argument = args.parts.first
         
     | 
| 
      
 394 
     | 
    
         
            +
                    return unless argument.is_a?(SyntaxTree::StringLiteral)
         
     | 
| 
      
 395 
     | 
    
         
            +
             
     | 
| 
      
 396 
     | 
    
         
            +
                    path_node = argument.parts.first
         
     | 
| 
      
 397 
     | 
    
         
            +
                    return unless path_node.is_a?(SyntaxTree::TStringContent)
         
     | 
| 
      
 398 
     | 
    
         
            +
                    return unless (path_node.location.start_char..path_node.location.end_char).cover?(char_position)
         
     | 
| 
      
 399 
     | 
    
         
            +
             
     | 
| 
      
 400 
     | 
    
         
            +
                    path_node
         
     | 
| 
      
 401 
     | 
    
         
            +
                  end
         
     | 
| 
      
 402 
     | 
    
         
            +
             
     | 
| 
      
 403 
     | 
    
         
            +
                  return unless target
         
     | 
| 
      
 404 
     | 
    
         
            +
             
     | 
| 
      
 405 
     | 
    
         
            +
                  emitter = EventEmitter.new
         
     | 
| 
      
 406 
     | 
    
         
            +
                  listener = Requests::PathCompletion.new(emitter, @message_queue)
         
     | 
| 
      
 407 
     | 
    
         
            +
                  emitter.emit_for_target(target)
         
     | 
| 
      
 408 
     | 
    
         
            +
                  listener.response
         
     | 
| 
       346 
409 
     | 
    
         
             
                end
         
     | 
| 
       347 
410 
     | 
    
         | 
| 
       348 
411 
     | 
    
         
             
                sig { params(options: T::Hash[Symbol, T.untyped]).returns(Interface::InitializeResult) }
         
     | 
| 
         @@ -359,7 +422,11 @@ module RubyLsp 
     | 
|
| 
       359 
422 
     | 
    
         
             
                  end
         
     | 
| 
       360 
423 
     | 
    
         | 
| 
       361 
424 
     | 
    
         
             
                  formatter = options.dig(:initializationOptions, :formatter)
         
     | 
| 
       362 
     | 
    
         
            -
                  @store.formatter = formatter  
     | 
| 
      
 425 
     | 
    
         
            +
                  @store.formatter = if formatter == "auto"
         
     | 
| 
      
 426 
     | 
    
         
            +
                    detected_formatter
         
     | 
| 
      
 427 
     | 
    
         
            +
                  else
         
     | 
| 
      
 428 
     | 
    
         
            +
                    formatter
         
     | 
| 
      
 429 
     | 
    
         
            +
                  end
         
     | 
| 
       363 
430 
     | 
    
         | 
| 
       364 
431 
     | 
    
         
             
                  configured_features = options.dig(:initializationOptions, :enabledFeatures)
         
     | 
| 
       365 
432 
     | 
    
         
             
                  experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled)
         
     | 
| 
         @@ -468,5 +535,39 @@ module RubyLsp 
     | 
|
| 
       468 
535 
     | 
    
         
             
                    ),
         
     | 
| 
       469 
536 
     | 
    
         
             
                  )
         
     | 
| 
       470 
537 
     | 
    
         
             
                end
         
     | 
| 
      
 538 
     | 
    
         
            +
             
     | 
| 
      
 539 
     | 
    
         
            +
                sig { returns(String) }
         
     | 
| 
      
 540 
     | 
    
         
            +
                def detected_formatter
         
     | 
| 
      
 541 
     | 
    
         
            +
                  # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
         
     | 
| 
      
 542 
     | 
    
         
            +
                  if direct_dependency?(/^rubocop/)
         
     | 
| 
      
 543 
     | 
    
         
            +
                    "rubocop"
         
     | 
| 
      
 544 
     | 
    
         
            +
                  elsif direct_dependency?(/^syntax_tree$/)
         
     | 
| 
      
 545 
     | 
    
         
            +
                    "syntax_tree"
         
     | 
| 
      
 546 
     | 
    
         
            +
                  else
         
     | 
| 
      
 547 
     | 
    
         
            +
                    "none"
         
     | 
| 
      
 548 
     | 
    
         
            +
                  end
         
     | 
| 
      
 549 
     | 
    
         
            +
                end
         
     | 
| 
      
 550 
     | 
    
         
            +
             
     | 
| 
      
 551 
     | 
    
         
            +
                sig { params(gem_pattern: Regexp).returns(T::Boolean) }
         
     | 
| 
      
 552 
     | 
    
         
            +
                def direct_dependency?(gem_pattern)
         
     | 
| 
      
 553 
     | 
    
         
            +
                  Bundler.locked_gems.dependencies.keys.grep(gem_pattern).any?
         
     | 
| 
      
 554 
     | 
    
         
            +
                end
         
     | 
| 
      
 555 
     | 
    
         
            +
             
     | 
| 
      
 556 
     | 
    
         
            +
                sig { void }
         
     | 
| 
      
 557 
     | 
    
         
            +
                def check_formatter_is_available
         
     | 
| 
      
 558 
     | 
    
         
            +
                  # Warn of an unavailable `formatter` setting, e.g. `rubocop` on a project which doesn't have RuboCop.
         
     | 
| 
      
 559 
     | 
    
         
            +
                  # Syntax Tree will always be available via Ruby LSP so we don't need to check for it.
         
     | 
| 
      
 560 
     | 
    
         
            +
                  return unless @store.formatter == "rubocop"
         
     | 
| 
      
 561 
     | 
    
         
            +
             
     | 
| 
      
 562 
     | 
    
         
            +
                  unless defined?(RubyLsp::Requests::Support::RuboCopRunner)
         
     | 
| 
      
 563 
     | 
    
         
            +
                    @message_queue << Notification.new(
         
     | 
| 
      
 564 
     | 
    
         
            +
                      message: "window/showMessage",
         
     | 
| 
      
 565 
     | 
    
         
            +
                      params: Interface::ShowMessageParams.new(
         
     | 
| 
      
 566 
     | 
    
         
            +
                        type: Constant::MessageType::ERROR,
         
     | 
| 
      
 567 
     | 
    
         
            +
                        message: "Ruby LSP formatter is set to `rubocop` but RuboCop was not found in the bundle.",
         
     | 
| 
      
 568 
     | 
    
         
            +
                      ),
         
     | 
| 
      
 569 
     | 
    
         
            +
                    )
         
     | 
| 
      
 570 
     | 
    
         
            +
                  end
         
     | 
| 
      
 571 
     | 
    
         
            +
                end
         
     | 
| 
       471 
572 
     | 
    
         
             
              end
         
     | 
| 
       472 
573 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module RubyLsp
         
     | 
| 
      
 5 
     | 
    
         
            +
              # To register an extension, inherit from this class and implement both `name` and `activate`
         
     | 
| 
      
 6 
     | 
    
         
            +
              #
         
     | 
| 
      
 7 
     | 
    
         
            +
              # # Example
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # ```ruby
         
     | 
| 
      
 10 
     | 
    
         
            +
              # module MyGem
         
     | 
| 
      
 11 
     | 
    
         
            +
              #   class MyExtension < Extension
         
     | 
| 
      
 12 
     | 
    
         
            +
              #     def activate
         
     | 
| 
      
 13 
     | 
    
         
            +
              #       # Perform any relevant initialization
         
     | 
| 
      
 14 
     | 
    
         
            +
              #     end
         
     | 
| 
      
 15 
     | 
    
         
            +
              #
         
     | 
| 
      
 16 
     | 
    
         
            +
              #     def name
         
     | 
| 
      
 17 
     | 
    
         
            +
              #       "My extension name"
         
     | 
| 
      
 18 
     | 
    
         
            +
              #     end
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   end
         
     | 
| 
      
 20 
     | 
    
         
            +
              # end
         
     | 
| 
      
 21 
     | 
    
         
            +
              # ```
         
     | 
| 
      
 22 
     | 
    
         
            +
              class Extension
         
     | 
| 
      
 23 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 24 
     | 
    
         
            +
                extend T::Helpers
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                abstract!
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 29 
     | 
    
         
            +
                  extend T::Sig
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Automatically track and instantiate extension classes
         
     | 
| 
      
 32 
     | 
    
         
            +
                  sig { params(child_class: T.class_of(Extension)).void }
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def inherited(child_class)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    extensions << child_class.new
         
     | 
| 
      
 35 
     | 
    
         
            +
                    super
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  sig { returns(T::Array[Extension]) }
         
     | 
| 
      
 39 
     | 
    
         
            +
                  def extensions
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @extensions ||= T.let([], T.nilable(T::Array[Extension]))
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  # Discovers and loads all extensions. Returns the list of activated extensions
         
     | 
| 
      
 44 
     | 
    
         
            +
                  sig { returns(T::Array[Extension]) }
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def load_extensions
         
     | 
| 
      
 46 
     | 
    
         
            +
                    # Require all extensions entry points, which should be placed under
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # `some_gem/lib/ruby_lsp/your_gem_name/extension.rb`
         
     | 
| 
      
 48 
     | 
    
         
            +
                    Gem.find_files("ruby_lsp/**/extension.rb").each do |extension|
         
     | 
| 
      
 49 
     | 
    
         
            +
                      require File.expand_path(extension)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    rescue => e
         
     | 
| 
      
 51 
     | 
    
         
            +
                      warn(e.message)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      warn(e.backtrace.to_s) # rubocop:disable Lint/RedundantStringCoercion
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    # Activate each one of the discovered extensions. If any problems occur in the extensions, we don't want to
         
     | 
| 
      
 56 
     | 
    
         
            +
                    # fail to boot the server
         
     | 
| 
      
 57 
     | 
    
         
            +
                    extensions.each do |extension|
         
     | 
| 
      
 58 
     | 
    
         
            +
                      extension.activate
         
     | 
| 
      
 59 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 60 
     | 
    
         
            +
                    rescue => e
         
     | 
| 
      
 61 
     | 
    
         
            +
                      extension.add_error(e)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                sig { void }
         
     | 
| 
      
 67 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @errors = T.let([], T::Array[StandardError])
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                sig { params(error: StandardError).returns(T.self_type) }
         
     | 
| 
      
 72 
     | 
    
         
            +
                def add_error(error)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @errors << error
         
     | 
| 
      
 74 
     | 
    
         
            +
                  self
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                sig { returns(T::Boolean) }
         
     | 
| 
      
 78 
     | 
    
         
            +
                def error?
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @errors.any?
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                sig { returns(String) }
         
     | 
| 
      
 83 
     | 
    
         
            +
                def formatted_errors
         
     | 
| 
      
 84 
     | 
    
         
            +
                  <<~ERRORS
         
     | 
| 
      
 85 
     | 
    
         
            +
                    #{name}:
         
     | 
| 
      
 86 
     | 
    
         
            +
                      #{@errors.map(&:message).join("\n")}
         
     | 
| 
      
 87 
     | 
    
         
            +
                  ERRORS
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                sig { returns(String) }
         
     | 
| 
      
 91 
     | 
    
         
            +
                def backtraces
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @errors.filter_map(&:backtrace).join("\n\n")
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                # Each extension should implement `MyExtension#activate` and use to perform any sort of initialization, such as
         
     | 
| 
      
 96 
     | 
    
         
            +
                # reading information into memory or even spawning a separate process
         
     | 
| 
      
 97 
     | 
    
         
            +
                sig { abstract.void }
         
     | 
| 
      
 98 
     | 
    
         
            +
                def activate; end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                # Extensions should override the `name` method to return the extension name
         
     | 
| 
      
 101 
     | 
    
         
            +
                sig { abstract.returns(String) }
         
     | 
| 
      
 102 
     | 
    
         
            +
                def name; end
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ruby_lsp/internal.rb
    CHANGED
    
    
    
        data/lib/ruby_lsp/listener.rb
    CHANGED
    
    | 
         @@ -14,20 +14,23 @@ module RubyLsp 
     | 
|
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                abstract!
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
                sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
         
     | 
| 
      
 18 
     | 
    
         
            +
                def initialize(emitter, message_queue)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @emitter = emitter
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @message_queue = message_queue
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       17 
23 
     | 
    
         
             
                class << self
         
     | 
| 
       18 
24 
     | 
    
         
             
                  extend T::Sig
         
     | 
| 
       19 
25 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                  sig { returns(T 
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                   
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                    current_methods = instance_methods
         
     | 
| 
       29 
     | 
    
         
            -
                    block.call
         
     | 
| 
       30 
     | 
    
         
            -
                    @events = T.let(instance_methods - current_methods, T.nilable(T::Array[Symbol]))
         
     | 
| 
      
 26 
     | 
    
         
            +
                  sig { returns(T::Array[T.class_of(Listener)]) }
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def listeners
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @listeners ||= T.let([], T.nilable(T::Array[T.class_of(Listener)]))
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  sig { params(listener: T.class_of(Listener)).void }
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def add_listener(listener)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    listeners << listener
         
     | 
| 
       31 
34 
     | 
    
         
             
                  end
         
     | 
| 
       32 
35 
     | 
    
         
             
                end
         
     | 
| 
       33 
36 
     | 
    
         | 
| 
         @@ -18,11 +18,6 @@ module RubyLsp 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  sig { params(document: Document, _kwargs: T.untyped).void }
         
     | 
| 
       19 
19 
     | 
    
         
             
                  def initialize(document, **_kwargs)
         
     | 
| 
       20 
20 
     | 
    
         
             
                    @document = document
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                    # Parsing the document here means we're taking a lazy approach by only doing it when the first feature request
         
     | 
| 
       23 
     | 
    
         
            -
                    # is received by the server. This happens because {Document#parse} remembers if there are new edits to be parsed
         
     | 
| 
       24 
     | 
    
         
            -
                    @document.parse
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
21 
     | 
    
         
             
                    super()
         
     | 
| 
       27 
22 
     | 
    
         
             
                  end
         
     | 
| 
       28 
23 
     | 
    
         | 
| 
         @@ -3,7 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module RubyLsp
         
     | 
| 
       5 
5 
     | 
    
         
             
              module Requests
         
     | 
| 
       6 
     | 
    
         
            -
                # 
         
     | 
| 
       7 
7 
     | 
    
         
             
                #
         
     | 
| 
       8 
8 
     | 
    
         
             
                # The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
         
     | 
| 
       9 
9 
     | 
    
         
             
                # request is used to to resolve the edit field for a given code action, if it is not already provided in the
         
     | 
| 
         @@ -3,7 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module RubyLsp
         
     | 
| 
       5 
5 
     | 
    
         
             
              module Requests
         
     | 
| 
       6 
     | 
    
         
            -
                # 
         
     | 
| 
       7 
7 
     | 
    
         
             
                #
         
     | 
| 
       8 
8 
     | 
    
         
             
                # The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
         
     | 
| 
       9 
9 
     | 
    
         
             
                # request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
         
     |