theme-check 1.6.2 → 1.7.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/CHANGELOG.md +9 -0
- data/lib/theme_check/analyzer.rb +29 -5
- data/lib/theme_check/language_server/handler.rb +88 -6
- data/lib/theme_check/language_server/messenger.rb +57 -0
- data/lib/theme_check/language_server/server.rb +105 -40
- data/lib/theme_check/language_server.rb +1 -0
- data/lib/theme_check/version.rb +1 -1
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 943f8cb722f6c19c1c41e494622922e608a99b50e4ad67f2288a2ed99f406ff3
         | 
| 4 | 
            +
              data.tar.gz: a8fd83f054895fd366bba2512ff6431d8c470bb3e013ff6c6bf9f6be5366bf38
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fb37aed715f2b9f2dd0a8c17f2abf69110d583280d88508ef3f6ee439989c63fe9819025d870cfc1ed64a8deaac19586ac48ab261495e7a371575d9971754980
         | 
| 7 | 
            +
              data.tar.gz: 64727b7c9684fa094ce498c09a2a3f1dc06ce10fa99aed066af2aa327bd4de70fcc728f76ad127fd08d7b1b6e28b42b06acf96d83b26193814e7eed1b19a2008
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,4 +1,13 @@ | |
| 1 1 |  | 
| 2 | 
            +
            v1.7.0 / 2021-09-20
         | 
| 3 | 
            +
            ===================
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ### Features
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              * Handle LSP messages concurrently in the Language Server ([#459](https://github.com/shopify/theme-check/issues/459))
         | 
| 8 | 
            +
                * Adds progress reporting while checking (:eyes: VS Code status bar)
         | 
| 9 | 
            +
                * Makes completions work while checking (more noticeable on Windows since ruby is 3x slower on Windows)
         | 
| 10 | 
            +
             | 
| 2 11 | 
             
            v1.6.2 / 2021-09-16
         | 
| 3 12 | 
             
            ===================
         | 
| 4 13 |  | 
    
        data/lib/theme_check/analyzer.rb
    CHANGED
    
    | @@ -29,19 +29,36 @@ module ThemeCheck | |
| 29 29 | 
             
                    @html_checks.flat_map(&:offenses)
         | 
| 30 30 | 
             
                end
         | 
| 31 31 |  | 
| 32 | 
            +
                def json_file_count
         | 
| 33 | 
            +
                  @json_file_count ||= @theme.json.size
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def liquid_file_count
         | 
| 37 | 
            +
                  @liquid_file_count ||= @theme.liquid.size
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def total_file_count
         | 
| 41 | 
            +
                  json_file_count + liquid_file_count
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 32 44 | 
             
                def analyze_theme
         | 
| 33 45 | 
             
                  reset
         | 
| 34 46 |  | 
| 35 47 | 
             
                  liquid_visitor = LiquidVisitor.new(@liquid_checks, @disabled_checks)
         | 
| 36 48 | 
             
                  html_visitor = HtmlVisitor.new(@html_checks)
         | 
| 49 | 
            +
             | 
| 37 50 | 
             
                  ThemeCheck.with_liquid_c_disabled do
         | 
| 38 | 
            -
                    @theme.liquid. | 
| 51 | 
            +
                    @theme.liquid.each_with_index do |liquid_file, i|
         | 
| 52 | 
            +
                      yield(liquid_file.relative_path.to_s, i, total_file_count) if block_given?
         | 
| 39 53 | 
             
                      liquid_visitor.visit_liquid_file(liquid_file)
         | 
| 40 54 | 
             
                      html_visitor.visit_liquid_file(liquid_file)
         | 
| 41 55 | 
             
                    end
         | 
| 42 56 | 
             
                  end
         | 
| 43 57 |  | 
| 44 | 
            -
                  @theme.json. | 
| 58 | 
            +
                  @theme.json.each_with_index do |json_file, i|
         | 
| 59 | 
            +
                    yield(json_file.relative_path.to_s, liquid_file_count + i, total_file_count) if block_given?
         | 
| 60 | 
            +
                    @json_checks.call(:on_file, json_file)
         | 
| 61 | 
            +
                  end
         | 
| 45 62 |  | 
| 46 63 | 
             
                  finish
         | 
| 47 64 | 
             
                end
         | 
| @@ -53,16 +70,23 @@ module ThemeCheck | |
| 53 70 | 
             
                    # Call all checks that run on the whole theme
         | 
| 54 71 | 
             
                    liquid_visitor = LiquidVisitor.new(@liquid_checks.whole_theme, @disabled_checks)
         | 
| 55 72 | 
             
                    html_visitor = HtmlVisitor.new(@html_checks.whole_theme)
         | 
| 56 | 
            -
                     | 
| 73 | 
            +
                    total = total_file_count + files.size
         | 
| 74 | 
            +
                    @theme.liquid.each_with_index do |liquid_file, i|
         | 
| 75 | 
            +
                      yield(liquid_file.relative_path.to_s, i, total) if block_given?
         | 
| 57 76 | 
             
                      liquid_visitor.visit_liquid_file(liquid_file)
         | 
| 58 77 | 
             
                      html_visitor.visit_liquid_file(liquid_file)
         | 
| 59 78 | 
             
                    end
         | 
| 60 | 
            -
             | 
| 79 | 
            +
             | 
| 80 | 
            +
                    @theme.json.each_with_index do |json_file, i|
         | 
| 81 | 
            +
                      yield(json_file.relative_path.to_s, liquid_file_count + i, total) if block_given?
         | 
| 82 | 
            +
                      @json_checks.whole_theme.call(:on_file, json_file)
         | 
| 83 | 
            +
                    end
         | 
| 61 84 |  | 
| 62 85 | 
             
                    # Call checks that run on a single files, only on specified file
         | 
| 63 86 | 
             
                    liquid_visitor = LiquidVisitor.new(@liquid_checks.single_file, @disabled_checks)
         | 
| 64 87 | 
             
                    html_visitor = HtmlVisitor.new(@html_checks.single_file)
         | 
| 65 | 
            -
                    files. | 
| 88 | 
            +
                    files.each_with_index do |theme_file, i|
         | 
| 89 | 
            +
                      yield(theme_file.relative_path.to_s, total_file_count + i, total) if block_given?
         | 
| 66 90 | 
             
                      if theme_file.liquid?
         | 
| 67 91 | 
             
                        liquid_visitor.visit_liquid_file(theme_file)
         | 
| 68 92 | 
             
                        html_visitor.visit_liquid_file(theme_file)
         | 
| @@ -7,6 +7,11 @@ module ThemeCheck | |
| 7 7 | 
             
                class Handler
         | 
| 8 8 | 
             
                  include URIHelper
         | 
| 9 9 |  | 
| 10 | 
            +
                  SERVER_INFO = {
         | 
| 11 | 
            +
                    name: $PROGRAM_NAME,
         | 
| 12 | 
            +
                    version: ThemeCheck::VERSION,
         | 
| 13 | 
            +
                  }
         | 
| 14 | 
            +
             | 
| 10 15 | 
             
                  CAPABILITIES = {
         | 
| 11 16 | 
             
                    completionProvider: {
         | 
| 12 17 | 
             
                      triggerCharacters: ['.', '{{ ', '{% '],
         | 
| @@ -24,10 +29,17 @@ module ThemeCheck | |
| 24 29 | 
             
                  def initialize(server)
         | 
| 25 30 | 
             
                    @server = server
         | 
| 26 31 | 
             
                    @diagnostics_tracker = DiagnosticsTracker.new
         | 
| 32 | 
            +
                    @diagnostics_lock = Mutex.new
         | 
| 33 | 
            +
                    @supports_progress = false
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def supports_progress_notifications?
         | 
| 37 | 
            +
                    @supports_progress
         | 
| 27 38 | 
             
                  end
         | 
| 28 39 |  | 
| 29 40 | 
             
                  def on_initialize(id, params)
         | 
| 30 41 | 
             
                    @root_path = root_path_from_params(params)
         | 
| 42 | 
            +
                    @supports_progress = params.dig('capabilities', 'window', 'workDoneProgress')
         | 
| 31 43 |  | 
| 32 44 | 
             
                    # Tell the client we don't support anything if there's no rootPath
         | 
| 33 45 | 
             
                    return send_response(id, { capabilities: {} }) if @root_path.nil?
         | 
| @@ -37,6 +49,7 @@ module ThemeCheck | |
| 37 49 | 
             
                    # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#responseMessage
         | 
| 38 50 | 
             
                    send_response(id, {
         | 
| 39 51 | 
             
                      capabilities: CAPABILITIES,
         | 
| 52 | 
            +
                      serverInfo: SERVER_INFO,
         | 
| 40 53 | 
             
                    })
         | 
| 41 54 | 
             
                  end
         | 
| 42 55 |  | 
| @@ -128,6 +141,8 @@ module ThemeCheck | |
| 128 141 | 
             
                  end
         | 
| 129 142 |  | 
| 130 143 | 
             
                  def analyze_and_send_offenses(absolute_path)
         | 
| 144 | 
            +
                    return unless @diagnostics_lock.try_lock
         | 
| 145 | 
            +
                    token = send_create_work_done_progress_request
         | 
| 131 146 | 
             
                    config = config_for_path(absolute_path)
         | 
| 132 147 | 
             
                    storage = ThemeCheck::FileSystemStorage.new(
         | 
| 133 148 | 
             
                      config.root,
         | 
| @@ -137,13 +152,17 @@ module ThemeCheck | |
| 137 152 | 
             
                    analyzer = ThemeCheck::Analyzer.new(theme, config.enabled_checks)
         | 
| 138 153 |  | 
| 139 154 | 
             
                    if @diagnostics_tracker.first_run?
         | 
| 140 | 
            -
                       | 
| 155 | 
            +
                      send_work_done_progress_begin(token, "Full theme check")
         | 
| 141 156 | 
             
                      log("Checking #{config.root}")
         | 
| 142 157 | 
             
                      offenses = nil
         | 
| 143 158 | 
             
                      time = Benchmark.measure do
         | 
| 144 | 
            -
                        offenses = analyzer.analyze_theme
         | 
| 159 | 
            +
                        offenses = analyzer.analyze_theme do |path, i, total|
         | 
| 160 | 
            +
                          send_work_done_progress_report(token, "#{i}/#{total} #{path}", (i.to_f / total * 100.0).to_i)
         | 
| 161 | 
            +
                        end
         | 
| 145 162 | 
             
                      end
         | 
| 146 | 
            -
                       | 
| 163 | 
            +
                      end_message = "Found #{offenses.size} offenses in #{format("%0.2f", time.real)}s"
         | 
| 164 | 
            +
                      log(end_message)
         | 
| 165 | 
            +
                      send_work_done_progress_end(token, end_message)
         | 
| 147 166 | 
             
                      send_diagnostics(offenses)
         | 
| 148 167 | 
             
                    else
         | 
| 149 168 | 
             
                      # Analyze selected files
         | 
| @@ -152,14 +171,20 @@ module ThemeCheck | |
| 152 171 | 
             
                      # Skip if not a theme file
         | 
| 153 172 | 
             
                      if file
         | 
| 154 173 | 
             
                        log("Checking #{relative_path}")
         | 
| 174 | 
            +
                        send_work_done_progress_begin(token, "Partial theme check")
         | 
| 155 175 | 
             
                        offenses = nil
         | 
| 156 176 | 
             
                        time = Benchmark.measure do
         | 
| 157 | 
            -
                          offenses = analyzer.analyze_files([file])
         | 
| 177 | 
            +
                          offenses = analyzer.analyze_files([file]) do |path, i, total|
         | 
| 178 | 
            +
                            send_work_done_progress_report(token, "#{i}/#{total} #{path}", (i.to_f / total * 100.0).to_i)
         | 
| 179 | 
            +
                          end
         | 
| 158 180 | 
             
                        end
         | 
| 159 | 
            -
                         | 
| 181 | 
            +
                        end_message = "Found #{offenses.size} new offenses in #{format("%0.2f", time.real)}s"
         | 
| 182 | 
            +
                        send_work_done_progress_end(token, end_message)
         | 
| 183 | 
            +
                        log(end_message)
         | 
| 160 184 | 
             
                        send_diagnostics(offenses, [absolute_path])
         | 
| 161 185 | 
             
                      end
         | 
| 162 186 | 
             
                    end
         | 
| 187 | 
            +
                    @diagnostics_lock.unlock
         | 
| 163 188 | 
             
                  end
         | 
| 164 189 |  | 
| 165 190 | 
             
                  def completions(relative_path, line, col)
         | 
| @@ -228,9 +253,57 @@ module ThemeCheck | |
| 228 253 | 
             
                    }
         | 
| 229 254 | 
             
                  end
         | 
| 230 255 |  | 
| 256 | 
            +
                  def send_create_work_done_progress_request
         | 
| 257 | 
            +
                    return unless supports_progress_notifications?
         | 
| 258 | 
            +
                    token = nil
         | 
| 259 | 
            +
                    @server.request do |id|
         | 
| 260 | 
            +
                      token = id # we'll reuse the RQID as token
         | 
| 261 | 
            +
                      send_message({
         | 
| 262 | 
            +
                        id: id,
         | 
| 263 | 
            +
                        method: "window/workDoneProgress/create",
         | 
| 264 | 
            +
                        params: {
         | 
| 265 | 
            +
                          token: id,
         | 
| 266 | 
            +
                        },
         | 
| 267 | 
            +
                      })
         | 
| 268 | 
            +
                    end
         | 
| 269 | 
            +
                    token
         | 
| 270 | 
            +
                  end
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                  def send_work_done_progress_begin(token, title)
         | 
| 273 | 
            +
                    return unless supports_progress_notifications?
         | 
| 274 | 
            +
                    send_progress(token, {
         | 
| 275 | 
            +
                      kind: 'begin',
         | 
| 276 | 
            +
                      title: title,
         | 
| 277 | 
            +
                      cancellable: false,
         | 
| 278 | 
            +
                      percentage: 0,
         | 
| 279 | 
            +
                    })
         | 
| 280 | 
            +
                  end
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                  def send_work_done_progress_report(token, message, percentage)
         | 
| 283 | 
            +
                    return unless supports_progress_notifications?
         | 
| 284 | 
            +
                    send_progress(token, {
         | 
| 285 | 
            +
                      kind: 'report',
         | 
| 286 | 
            +
                      message: message,
         | 
| 287 | 
            +
                      cancellable: false,
         | 
| 288 | 
            +
                      percentage: percentage,
         | 
| 289 | 
            +
                    })
         | 
| 290 | 
            +
                  end
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                  def send_work_done_progress_end(token, message)
         | 
| 293 | 
            +
                    return unless supports_progress_notifications?
         | 
| 294 | 
            +
                    send_progress(token, {
         | 
| 295 | 
            +
                      kind: 'end',
         | 
| 296 | 
            +
                      message: message,
         | 
| 297 | 
            +
                    })
         | 
| 298 | 
            +
                  end
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                  def send_progress(token, value)
         | 
| 301 | 
            +
                    send_notification("$/progress", token: token, value: value)
         | 
| 302 | 
            +
                  end
         | 
| 303 | 
            +
             | 
| 231 304 | 
             
                  def send_message(message)
         | 
| 232 305 | 
             
                    message[:jsonrpc] = '2.0'
         | 
| 233 | 
            -
                    @server. | 
| 306 | 
            +
                    @server.send_message(message)
         | 
| 234 307 | 
             
                  end
         | 
| 235 308 |  | 
| 236 309 | 
             
                  def send_response(id, result = nil, error = nil)
         | 
| @@ -240,6 +313,15 @@ module ThemeCheck | |
| 240 313 | 
             
                    send_message(message)
         | 
| 241 314 | 
             
                  end
         | 
| 242 315 |  | 
| 316 | 
            +
                  def send_request(method, params = nil)
         | 
| 317 | 
            +
                    @server.request do |id|
         | 
| 318 | 
            +
                      message = { id: id }
         | 
| 319 | 
            +
                      message[:method] = method
         | 
| 320 | 
            +
                      message[:params] = params if params
         | 
| 321 | 
            +
                      send_message(message)
         | 
| 322 | 
            +
                    end
         | 
| 323 | 
            +
                  end
         | 
| 324 | 
            +
             | 
| 243 325 | 
             
                  def send_notification(method, params)
         | 
| 244 326 | 
             
                    message = { method: method }
         | 
| 245 327 | 
             
                    message[:params] = params
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ThemeCheck
         | 
| 4 | 
            +
              module LanguageServer
         | 
| 5 | 
            +
                class Messenger
         | 
| 6 | 
            +
                  def initialize
         | 
| 7 | 
            +
                    @responses = {}
         | 
| 8 | 
            +
                    @mutex = Mutex.new
         | 
| 9 | 
            +
                    @id = 0
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Here's how you'd use this:
         | 
| 13 | 
            +
                  #
         | 
| 14 | 
            +
                  # def some_method_that_communicates_both_ways
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  #   # this will block until the JSON rpc loop has an answer
         | 
| 17 | 
            +
                  #   token = @server.request do |id|
         | 
| 18 | 
            +
                  #     send_create_work_done_progress_request(id, ...)
         | 
| 19 | 
            +
                  #   end
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  #   send_create_work_done_begin_notification(token, "...")
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  #   do_stuff do |file, i, total|
         | 
| 24 | 
            +
                  #     send_create_work_done_progress_notification(token, "...")
         | 
| 25 | 
            +
                  #   end
         | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  #   send_create_work_done_end_notification(token, "...")
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # end
         | 
| 30 | 
            +
                  def request(&block)
         | 
| 31 | 
            +
                    id = @mutex.synchronize { @id += 1 }
         | 
| 32 | 
            +
                    @responses[id] = SizedQueue.new(1)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    # Execute the block in the parent thread with an ID
         | 
| 35 | 
            +
                    # So that we're able to relinquish control in the right
         | 
| 36 | 
            +
                    # place when we have a response.
         | 
| 37 | 
            +
                    block.call(id)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    # this call is blocking until we get a response from somewhere
         | 
| 40 | 
            +
                    result = @responses[id].pop
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    # cleanup when done
         | 
| 43 | 
            +
                    @responses.delete(id)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    # return the response
         | 
| 46 | 
            +
                    result
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  # In the JSONRPC loop, when we find the response to the
         | 
| 50 | 
            +
                  # request, we unblock the thread that made the request with the
         | 
| 51 | 
            +
                  # response.
         | 
| 52 | 
            +
                  def respond(id, value)
         | 
| 53 | 
            +
                    @responses[id] << value
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -16,7 +16,8 @@ module ThemeCheck | |
| 16 16 | 
             
                    in_stream: STDIN,
         | 
| 17 17 | 
             
                    out_stream: STDOUT,
         | 
| 18 18 | 
             
                    err_stream: STDERR,
         | 
| 19 | 
            -
                    should_raise_errors: false
         | 
| 19 | 
            +
                    should_raise_errors: false,
         | 
| 20 | 
            +
                    number_of_threads: 2
         | 
| 20 21 | 
             
                  )
         | 
| 21 22 | 
             
                    validate!([in_stream, out_stream, err_stream])
         | 
| 22 23 |  | 
| @@ -37,33 +38,90 @@ module ThemeCheck | |
| 37 38 | 
             
                    @out.sync = true # do not buffer
         | 
| 38 39 | 
             
                    @err.sync = true # do not buffer
         | 
| 39 40 |  | 
| 41 | 
            +
                    # The queue holds the JSON RPC messages
         | 
| 42 | 
            +
                    @queue = Queue.new
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    # The JSON RPC thread pushes messages onto the queue
         | 
| 45 | 
            +
                    @json_rpc_thread = nil
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    # The handler threads read messages from the queue
         | 
| 48 | 
            +
                    @number_of_threads = number_of_threads
         | 
| 49 | 
            +
                    @handlers = []
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    # The messenger permits requests to be made from the handler
         | 
| 52 | 
            +
                    # to the language client and for those messages to be resolved in place.
         | 
| 53 | 
            +
                    @messenger = Messenger.new
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    # The error queue holds blocks the main thread. When filled, we exit the program.
         | 
| 56 | 
            +
                    @error = SizedQueue.new(1)
         | 
| 57 | 
            +
             | 
| 40 58 | 
             
                    @should_raise_errors = should_raise_errors
         | 
| 41 59 | 
             
                  end
         | 
| 42 60 |  | 
| 43 61 | 
             
                  def listen
         | 
| 44 | 
            -
                     | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
                     | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
                       | 
| 56 | 
            -
             | 
| 62 | 
            +
                    start_handler_threads
         | 
| 63 | 
            +
                    start_json_rpc_thread
         | 
| 64 | 
            +
                    status_code_from_error(@error.pop)
         | 
| 65 | 
            +
                  rescue SignalException
         | 
| 66 | 
            +
                    0
         | 
| 67 | 
            +
                  ensure
         | 
| 68 | 
            +
                    cleanup
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def start_json_rpc_thread
         | 
| 72 | 
            +
                    @json_rpc_thread = Thread.new do
         | 
| 73 | 
            +
                      loop do
         | 
| 74 | 
            +
                        message = read_json_rpc_message
         | 
| 75 | 
            +
                        if message['method'] == 'initialize'
         | 
| 76 | 
            +
                          handle_message(message)
         | 
| 77 | 
            +
                        else
         | 
| 78 | 
            +
                          @queue << message
         | 
| 79 | 
            +
                        end
         | 
| 80 | 
            +
                      rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 81 | 
            +
                        break @error << e
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def start_handler_threads
         | 
| 87 | 
            +
                    @number_of_threads.times do
         | 
| 88 | 
            +
                      @handlers << Thread.new do
         | 
| 89 | 
            +
                        loop do
         | 
| 90 | 
            +
                          message = @queue.pop
         | 
| 91 | 
            +
                          break if @queue.closed? && @queue.empty?
         | 
| 92 | 
            +
                          handle_message(message)
         | 
| 93 | 
            +
                        rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 94 | 
            +
                          break @error << e
         | 
| 95 | 
            +
                        end
         | 
| 96 | 
            +
                      end
         | 
| 57 97 | 
             
                    end
         | 
| 58 98 | 
             
                  end
         | 
| 59 99 |  | 
| 60 | 
            -
                  def  | 
| 61 | 
            -
                     | 
| 62 | 
            -
             | 
| 100 | 
            +
                  def status_code_from_error(e)
         | 
| 101 | 
            +
                    raise e
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  # support ctrl+c and stuff
         | 
| 104 | 
            +
                  rescue SignalException, DoneStreaming
         | 
| 105 | 
            +
                    0
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 108 | 
            +
                    raise e if should_raise_errors
         | 
| 109 | 
            +
                    log(e)
         | 
| 110 | 
            +
                    log(e.backtrace)
         | 
| 111 | 
            +
                    2
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  def request(&block)
         | 
| 115 | 
            +
                    @messenger.request(&block)
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def send_message(message)
         | 
| 119 | 
            +
                    message_body = JSON.dump(message)
         | 
| 120 | 
            +
                    log(JSON.pretty_generate(message)) if $DEBUG
         | 
| 63 121 |  | 
| 64 | 
            -
                    @out.write("Content-Length: #{ | 
| 122 | 
            +
                    @out.write("Content-Length: #{message_body.bytesize}\r\n")
         | 
| 65 123 | 
             
                    @out.write("\r\n")
         | 
| 66 | 
            -
                    @out.write( | 
| 124 | 
            +
                    @out.write(message_body)
         | 
| 67 125 | 
             
                    @out.flush
         | 
| 68 126 | 
             
                  end
         | 
| 69 127 |  | 
| @@ -91,17 +149,23 @@ module ThemeCheck | |
| 91 149 | 
             
                    "one of the following: #{supported_io_classes.join(', ')}"
         | 
| 92 150 | 
             
                  end
         | 
| 93 151 |  | 
| 94 | 
            -
                  def  | 
| 95 | 
            -
                     | 
| 96 | 
            -
                     | 
| 97 | 
            -
                    log(JSON.pretty_generate( | 
| 152 | 
            +
                  def read_json_rpc_message
         | 
| 153 | 
            +
                    message_body = read_new_content
         | 
| 154 | 
            +
                    message_json = JSON.parse(message_body)
         | 
| 155 | 
            +
                    log(JSON.pretty_generate(message_json)) if $DEBUG
         | 
| 156 | 
            +
                    message_json
         | 
| 157 | 
            +
                  end
         | 
| 98 158 |  | 
| 99 | 
            -
             | 
| 100 | 
            -
                     | 
| 101 | 
            -
                     | 
| 102 | 
            -
                    method_name  | 
| 159 | 
            +
                  def handle_message(message)
         | 
| 160 | 
            +
                    id = message['id']
         | 
| 161 | 
            +
                    method_name = message['method']
         | 
| 162 | 
            +
                    method_name &&= "on_#{to_snake_case(method_name)}"
         | 
| 163 | 
            +
                    params = message['params']
         | 
| 164 | 
            +
                    result = message['result']
         | 
| 103 165 |  | 
| 104 | 
            -
                    if  | 
| 166 | 
            +
                    if message.key?('result')
         | 
| 167 | 
            +
                      @messenger.respond(id, result)
         | 
| 168 | 
            +
                    elsif @handler.respond_to?(method_name)
         | 
| 105 169 | 
             
                      @handler.send(method_name, id, params)
         | 
| 106 170 | 
             
                    end
         | 
| 107 171 | 
             
                  end
         | 
| @@ -128,26 +192,27 @@ module ThemeCheck | |
| 128 192 | 
             
                    length = initial_line.match(/Content-Length: (\d+)/)[1].to_i
         | 
| 129 193 | 
             
                    content = ''
         | 
| 130 194 | 
             
                    while content.length < length + 2
         | 
| 131 | 
            -
                       | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
                      rescue => e
         | 
| 135 | 
            -
                        log(e)
         | 
| 136 | 
            -
                        log(e.backtrace)
         | 
| 137 | 
            -
                        # We have almost certainly been disconnected from the server
         | 
| 138 | 
            -
                        cleanup
         | 
| 139 | 
            -
                        raise DoneStreaming
         | 
| 140 | 
            -
                      end
         | 
| 195 | 
            +
                      # Why + 2? Because \r\n
         | 
| 196 | 
            +
                      content += @in.read(length + 2)
         | 
| 197 | 
            +
                      raise DoneStreaming if @in.closed?
         | 
| 141 198 | 
             
                    end
         | 
| 142 199 |  | 
| 143 200 | 
             
                    content
         | 
| 144 201 | 
             
                  end
         | 
| 145 202 |  | 
| 146 203 | 
             
                  def cleanup
         | 
| 204 | 
            +
                    # Stop listenting to RPC calls
         | 
| 205 | 
            +
                    @in.close unless @in.closed?
         | 
| 206 | 
            +
                    # Wait for rpc loop to close
         | 
| 207 | 
            +
                    @json_rpc_thread&.join if @json_rpc_thread&.alive?
         | 
| 208 | 
            +
                    # Close the queue
         | 
| 209 | 
            +
                    @queue.close unless @queue.closed?
         | 
| 210 | 
            +
                    # Give 10 seconds for the handlers to wrap up what they were
         | 
| 211 | 
            +
                    # doing/emptying the queue. 👀 unit tests.
         | 
| 212 | 
            +
                    @handlers.each { |thread| thread.join(10) if thread.alive? }
         | 
| 213 | 
            +
                  ensure
         | 
| 147 214 | 
             
                    @err.close
         | 
| 148 215 | 
             
                    @out.close
         | 
| 149 | 
            -
                  rescue
         | 
| 150 | 
            -
                    # I did my best
         | 
| 151 216 | 
             
                  end
         | 
| 152 217 | 
             
                end
         | 
| 153 218 | 
             
              end
         | 
    
        data/lib/theme_check/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: theme-check
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Marc-André Cournoyer
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2021-09- | 
| 11 | 
            +
            date: 2021-09-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: liquid
         | 
| @@ -217,6 +217,7 @@ files: | |
| 217 217 | 
             
            - lib/theme_check/language_server/document_link_providers/render_document_link_provider.rb
         | 
| 218 218 | 
             
            - lib/theme_check/language_server/document_link_providers/section_document_link_provider.rb
         | 
| 219 219 | 
             
            - lib/theme_check/language_server/handler.rb
         | 
| 220 | 
            +
            - lib/theme_check/language_server/messenger.rb
         | 
| 220 221 | 
             
            - lib/theme_check/language_server/protocol.rb
         | 
| 221 222 | 
             
            - lib/theme_check/language_server/server.rb
         | 
| 222 223 | 
             
            - lib/theme_check/language_server/tokens.rb
         |