ruby-lsp 0.0.2 → 0.0.3
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/.github/workflows/ci.yml +3 -1
- data/.github/workflows/publish_docs.yml +32 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +15 -9
- data/README.md +18 -1
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp +1 -0
- data/lib/ruby-lsp.rb +1 -0
- data/lib/ruby_lsp/cli.rb +12 -3
- data/lib/ruby_lsp/document.rb +67 -4
- data/lib/ruby_lsp/handler.rb +51 -3
- data/lib/ruby_lsp/requests/base_request.rb +1 -0
- data/lib/ruby_lsp/requests/code_actions.rb +12 -1
- data/lib/ruby_lsp/requests/diagnostics.rb +17 -78
- data/lib/ruby_lsp/requests/document_symbol.rb +21 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +9 -0
- data/lib/ruby_lsp/requests/formatting.rb +11 -0
- data/lib/ruby_lsp/requests/rubocop_request.rb +2 -0
- data/lib/ruby_lsp/requests/selection_ranges.rb +102 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +23 -41
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +87 -0
- data/lib/ruby_lsp/requests/support/selection_range.rb +16 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +49 -0
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +26 -0
- data/lib/ruby_lsp/requests.rb +8 -0
- data/lib/ruby_lsp/store.rb +4 -0
- data/rakelib/check_docs.rake +53 -0
- metadata +9 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 58ed87185da0a5d1da74b10f1525f1ca1341f1001f3a4cbdf2b086dd93750ad9
         | 
| 4 | 
            +
              data.tar.gz: 1210f736e4cd8450cbe1e1d796dbd22a889c244c6ea5902bc6082f3791064ddc
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a1ed47a3e4d53dbf5250beaa98b3095c89099e67829f2ecac3149b34ecc87ae579c67ed6cd67035ee9930c0fffa0db6c0d4fdc3c3398520196005b180f28ea6f
         | 
| 7 | 
            +
              data.tar.gz: 18d07a7c5426fa8cfad11cfab44dbabf05fd3a1352f1bf9272b04fb35c78f0bb0b4860dd4b7bbfda89efb9cfa99fc3ef77fbb679ff2b21bbd4cffb3c53a14bd5
         | 
    
        data/.github/workflows/ci.yml
    CHANGED
    
    | @@ -4,7 +4,7 @@ on: [push, pull_request] | |
| 4 4 |  | 
| 5 5 | 
             
            jobs:
         | 
| 6 6 | 
             
              build:
         | 
| 7 | 
            -
                runs-on:  | 
| 7 | 
            +
                runs-on: ubuntu-latest
         | 
| 8 8 | 
             
                strategy:
         | 
| 9 9 | 
             
                  fail-fast: false
         | 
| 10 10 | 
             
                  matrix:
         | 
| @@ -17,6 +17,8 @@ jobs: | |
| 17 17 | 
             
                    with:
         | 
| 18 18 | 
             
                      ruby-version: ${{ matrix.ruby }}
         | 
| 19 19 | 
             
                      bundler-cache: true
         | 
| 20 | 
            +
                  - name: Check if documentation is up to date
         | 
| 21 | 
            +
                    run: bundle exec rake check_docs
         | 
| 20 22 | 
             
                  - name: Lint Ruby files
         | 
| 21 23 | 
             
                    run: bin/rubocop
         | 
| 22 24 | 
             
                  - name: Run tests
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            name: Publish docs
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            on:
         | 
| 4 | 
            +
              push:
         | 
| 5 | 
            +
                branches: [main]
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            jobs:
         | 
| 8 | 
            +
              build:
         | 
| 9 | 
            +
                runs-on: ubuntu-latest
         | 
| 10 | 
            +
                name: Publish documentation website
         | 
| 11 | 
            +
                steps:
         | 
| 12 | 
            +
                  - uses: actions/checkout@v3
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  - name: Set up Ruby
         | 
| 15 | 
            +
                    uses: ruby/setup-ruby@v1
         | 
| 16 | 
            +
                    with:
         | 
| 17 | 
            +
                      ruby-version: 3.1.1
         | 
| 18 | 
            +
                      bundler-cache: true
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  - name: Configure git
         | 
| 21 | 
            +
                    run: |
         | 
| 22 | 
            +
                      git config user.name github-actions
         | 
| 23 | 
            +
                      git config user.email github-actions@github.com
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  - name: Generate documentation
         | 
| 26 | 
            +
                    run: bundle exec rake yard
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  - name: Commit to gh-pages
         | 
| 29 | 
            +
                    run: |
         | 
| 30 | 
            +
                      git add docs
         | 
| 31 | 
            +
                      git commit -m "Publish website $(git log --format=format:%h -1)"
         | 
| 32 | 
            +
                      git push --force origin main:gh-pages
         | 
    
        data/Gemfile
    CHANGED
    
    | @@ -8,6 +8,7 @@ gem "debug", "~> 1.5" | |
| 8 8 | 
             
            gem "minitest", "~> 5.15"
         | 
| 9 9 | 
             
            gem "minitest-reporters", "~> 1.5"
         | 
| 10 10 | 
             
            gem "rake", "~> 13.0"
         | 
| 11 | 
            -
            gem "rubocop-shopify", "~> 2. | 
| 11 | 
            +
            gem "rubocop-shopify", "~> 2.6"
         | 
| 12 12 | 
             
            gem "rubocop-minitest", "~> 0.19.1"
         | 
| 13 13 | 
             
            gem "rubocop-rake", "~> 0.6.0"
         | 
| 14 | 
            +
            gem "yard", "~> 0.9"
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                ruby-lsp (0.0. | 
| 4 | 
            +
                ruby-lsp (0.0.3)
         | 
| 5 5 | 
             
                  language_server-protocol
         | 
| 6 6 | 
             
                  rubocop (>= 1.0)
         | 
| 7 7 | 
             
                  syntax_tree (>= 2.3)
         | 
| @@ -28,32 +28,37 @@ GEM | |
| 28 28 | 
             
                parallel (1.22.1)
         | 
| 29 29 | 
             
                parser (3.1.2.0)
         | 
| 30 30 | 
             
                  ast (~> 2.4.1)
         | 
| 31 | 
            +
                prettier_print (0.1.0)
         | 
| 31 32 | 
             
                rainbow (3.1.1)
         | 
| 32 33 | 
             
                rake (13.0.6)
         | 
| 33 | 
            -
                regexp_parser (2. | 
| 34 | 
            +
                regexp_parser (2.4.0)
         | 
| 34 35 | 
             
                reline (0.3.1)
         | 
| 35 36 | 
             
                  io-console (~> 0.5)
         | 
| 36 37 | 
             
                rexml (3.2.5)
         | 
| 37 | 
            -
                rubocop (1. | 
| 38 | 
            +
                rubocop (1.29.1)
         | 
| 38 39 | 
             
                  parallel (~> 1.10)
         | 
| 39 40 | 
             
                  parser (>= 3.1.0.0)
         | 
| 40 41 | 
             
                  rainbow (>= 2.2.2, < 4.0)
         | 
| 41 42 | 
             
                  regexp_parser (>= 1.8, < 3.0)
         | 
| 42 | 
            -
                  rexml
         | 
| 43 | 
            +
                  rexml (>= 3.2.5, < 4.0)
         | 
| 43 44 | 
             
                  rubocop-ast (>= 1.17.0, < 2.0)
         | 
| 44 45 | 
             
                  ruby-progressbar (~> 1.7)
         | 
| 45 46 | 
             
                  unicode-display_width (>= 1.4.0, < 3.0)
         | 
| 46 | 
            -
                rubocop-ast (1. | 
| 47 | 
            +
                rubocop-ast (1.18.0)
         | 
| 47 48 | 
             
                  parser (>= 3.1.1.0)
         | 
| 48 49 | 
             
                rubocop-minitest (0.19.1)
         | 
| 49 50 | 
             
                  rubocop (>= 0.90, < 2.0)
         | 
| 50 51 | 
             
                rubocop-rake (0.6.0)
         | 
| 51 52 | 
             
                  rubocop (~> 1.0)
         | 
| 52 | 
            -
                rubocop-shopify (2. | 
| 53 | 
            -
                  rubocop (~> 1. | 
| 53 | 
            +
                rubocop-shopify (2.6.0)
         | 
| 54 | 
            +
                  rubocop (~> 1.29)
         | 
| 54 55 | 
             
                ruby-progressbar (1.11.0)
         | 
| 55 | 
            -
                syntax_tree (2. | 
| 56 | 
            +
                syntax_tree (2.7.1)
         | 
| 57 | 
            +
                  prettier_print
         | 
| 56 58 | 
             
                unicode-display_width (2.1.0)
         | 
| 59 | 
            +
                webrick (1.7.0)
         | 
| 60 | 
            +
                yard (0.9.27)
         | 
| 61 | 
            +
                  webrick (~> 1.7.0)
         | 
| 57 62 |  | 
| 58 63 | 
             
            PLATFORMS
         | 
| 59 64 | 
             
              arm64-darwin-21
         | 
| @@ -66,8 +71,9 @@ DEPENDENCIES | |
| 66 71 | 
             
              rake (~> 13.0)
         | 
| 67 72 | 
             
              rubocop-minitest (~> 0.19.1)
         | 
| 68 73 | 
             
              rubocop-rake (~> 0.6.0)
         | 
| 69 | 
            -
              rubocop-shopify (~> 2. | 
| 74 | 
            +
              rubocop-shopify (~> 2.6)
         | 
| 70 75 | 
             
              ruby-lsp!
         | 
| 76 | 
            +
              yard (~> 0.9)
         | 
| 71 77 |  | 
| 72 78 | 
             
            BUNDLED WITH
         | 
| 73 79 | 
             
               2.3.7
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,4 +1,21 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Ruby LSP
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            This gem is an implementation of the language server protocol specification for Ruby, used to improve editor features.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ## Usage
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Install the gem. There's no need to require it, since the server is used as a standalone executable.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ```ruby
         | 
| 12 | 
            +
            group :development do
         | 
| 13 | 
            +
              gem "ruby-lsp", require: false
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
            ```
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            If using VS Code, install the [Ruby LSP plugin](https://github.com/Shopify/vscode-ruby-lsp) to get the extra features in
         | 
| 18 | 
            +
            the editor.
         | 
| 2 19 |  | 
| 3 20 | 
             
            ## Contributing
         | 
| 4 21 |  | 
    
        data/Rakefile
    CHANGED
    
    | @@ -2,6 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require "bundler/gem_tasks"
         | 
| 4 4 | 
             
            require "rake/testtask"
         | 
| 5 | 
            +
            require "yard"
         | 
| 5 6 |  | 
| 6 7 | 
             
            Rake::TestTask.new(:test) do |t|
         | 
| 7 8 | 
             
              t.libs << "test"
         | 
| @@ -9,6 +10,10 @@ Rake::TestTask.new(:test) do |t| | |
| 9 10 | 
             
              t.test_files = FileList["test/**/*_test.rb"]
         | 
| 10 11 | 
             
            end
         | 
| 11 12 |  | 
| 13 | 
            +
            YARD::Rake::YardocTask.new do |t|
         | 
| 14 | 
            +
              t.options = ["--markup", "markdown", "--output-dir", "docs"]
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 12 17 | 
             
            require "rubocop/rake_task"
         | 
| 13 18 |  | 
| 14 19 | 
             
            RuboCop::RakeTask.new
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.0. | 
| 1 | 
            +
            0.0.3
         | 
    
        data/exe/ruby-lsp
    CHANGED
    
    
    
        data/lib/ruby-lsp.rb
    CHANGED
    
    
    
        data/lib/ruby_lsp/cli.rb
    CHANGED
    
    | @@ -12,13 +12,15 @@ module RubyLsp | |
| 12 12 | 
             
                  handler.config do
         | 
| 13 13 | 
             
                    on("initialize") do |request|
         | 
| 14 14 | 
             
                      store.clear
         | 
| 15 | 
            -
                       | 
| 15 | 
            +
                      initialization_options = request.dig(:params, :initializationOptions)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      configure_options(initialization_options)
         | 
| 18 | 
            +
                      respond_with_capabilities(initialization_options.fetch(:enabledFeatures, []))
         | 
| 16 19 | 
             
                    end
         | 
| 17 20 |  | 
| 18 21 | 
             
                    on("textDocument/didChange") do |request|
         | 
| 19 22 | 
             
                      uri = request.dig(:params, :textDocument, :uri)
         | 
| 20 | 
            -
                       | 
| 21 | 
            -
                      store.set(uri, text)
         | 
| 23 | 
            +
                      store.push_edits(uri, request.dig(:params, :contentChanges))
         | 
| 22 24 |  | 
| 23 25 | 
             
                      send_diagnostics(uri)
         | 
| 24 26 | 
             
                      nil
         | 
| @@ -48,6 +50,13 @@ module RubyLsp | |
| 48 50 | 
             
                      respond_with_folding_ranges(request.dig(:params, :textDocument, :uri))
         | 
| 49 51 | 
             
                    end
         | 
| 50 52 |  | 
| 53 | 
            +
                    on("textDocument/selectionRange") do |request|
         | 
| 54 | 
            +
                      respond_with_selection_ranges(
         | 
| 55 | 
            +
                        request.dig(:params, :textDocument, :uri),
         | 
| 56 | 
            +
                        request.dig(:params, :positions),
         | 
| 57 | 
            +
                      )
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 51 60 | 
             
                    on("textDocument/semanticTokens/full") do |request|
         | 
| 52 61 | 
             
                      respond_with_semantic_highlighting(request.dig(:params, :textDocument, :uri))
         | 
| 53 62 | 
             
                    end
         | 
    
        data/lib/ruby_lsp/document.rb
    CHANGED
    
    | @@ -1,14 +1,17 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "strscan"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module RubyLsp
         | 
| 4 6 | 
             
              class Document
         | 
| 5 | 
            -
                attr_reader :tree, : | 
| 7 | 
            +
                attr_reader :tree, :source, :syntax_error_edits
         | 
| 6 8 |  | 
| 7 9 | 
             
                def initialize(source)
         | 
| 8 | 
            -
                  @ | 
| 9 | 
            -
                  @parser = SyntaxTree::Parser.new(source)
         | 
| 10 | 
            -
                  @tree = @parser.parse
         | 
| 10 | 
            +
                  @tree = SyntaxTree.parse(source)
         | 
| 11 11 | 
             
                  @cache = {}
         | 
| 12 | 
            +
                  @syntax_error_edits = []
         | 
| 13 | 
            +
                  @source = source
         | 
| 14 | 
            +
                  @parsable_source = source.dup
         | 
| 12 15 | 
             
                end
         | 
| 13 16 |  | 
| 14 17 | 
             
                def ==(other)
         | 
| @@ -23,5 +26,65 @@ module RubyLsp | |
| 23 26 | 
             
                  @cache[request_name] = result
         | 
| 24 27 | 
             
                  result
         | 
| 25 28 | 
             
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def push_edits(edits)
         | 
| 31 | 
            +
                  # Apply the edits on the real source
         | 
| 32 | 
            +
                  edits.each { |edit| apply_edit(@source, edit[:range], edit[:text]) }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  @cache.clear
         | 
| 35 | 
            +
                  @tree = SyntaxTree.parse(@source)
         | 
| 36 | 
            +
                  @syntax_error_edits.clear
         | 
| 37 | 
            +
                  @parsable_source = @source.dup
         | 
| 38 | 
            +
                  nil
         | 
| 39 | 
            +
                rescue SyntaxTree::Parser::ParseError
         | 
| 40 | 
            +
                  update_parsable_source(edits)
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def syntax_errors?
         | 
| 44 | 
            +
                  @syntax_error_edits.any?
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def update_parsable_source(edits)
         | 
| 50 | 
            +
                  # If the new edits caused a syntax error, make all edits blank spaces and line breaks to adjust the line and
         | 
| 51 | 
            +
                  # column numbers. This is attempt to make the document parsable while partial edits are being applied
         | 
| 52 | 
            +
                  edits.each do |edit|
         | 
| 53 | 
            +
                    @syntax_error_edits << edit
         | 
| 54 | 
            +
                    next if edit[:text].empty? # skip deletions, since they may have caused the syntax error
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    apply_edit(@parsable_source, edit[:range], edit[:text].gsub(/[^\r\n]/, " "))
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  @tree = SyntaxTree.parse(@parsable_source)
         | 
| 60 | 
            +
                rescue SyntaxTree::Parser::ParseError
         | 
| 61 | 
            +
                  # If we can't parse the source even after emptying the edits, then just fallback to the previous source
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def apply_edit(source, range, text)
         | 
| 65 | 
            +
                  scanner = Scanner.new(source)
         | 
| 66 | 
            +
                  start_position = scanner.find_position(range[:start])
         | 
| 67 | 
            +
                  end_position = scanner.find_position(range[:end])
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  source[start_position...end_position] = text
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                class Scanner
         | 
| 73 | 
            +
                  def initialize(source)
         | 
| 74 | 
            +
                    @source = source
         | 
| 75 | 
            +
                    @scanner = StringScanner.new(source)
         | 
| 76 | 
            +
                    @current_line = 0
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def find_position(position)
         | 
| 80 | 
            +
                    # Move the string scanner counting line breaks until we reach the right line
         | 
| 81 | 
            +
                    until @current_line == position[:line]
         | 
| 82 | 
            +
                      @scanner.scan_until(/\R/)
         | 
| 83 | 
            +
                      @current_line += 1
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    @scanner.pos + position[:character]
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 26 89 | 
             
              end
         | 
| 27 90 | 
             
            end
         | 
    
        data/lib/ruby_lsp/handler.rb
    CHANGED
    
    | @@ -2,9 +2,15 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require "ruby_lsp/requests"
         | 
| 4 4 | 
             
            require "ruby_lsp/store"
         | 
| 5 | 
            +
            require "benchmark"
         | 
| 5 6 |  | 
| 6 7 | 
             
            module RubyLsp
         | 
| 7 8 | 
             
              class Handler
         | 
| 9 | 
            +
                IGNORED_FOR_TELEMETRY = [
         | 
| 10 | 
            +
                  "initialized",
         | 
| 11 | 
            +
                  "$/cancelRequest",
         | 
| 12 | 
            +
                ].freeze
         | 
| 13 | 
            +
             | 
| 8 14 | 
             
                attr_reader :store
         | 
| 9 15 |  | 
| 10 16 | 
             
                Interface = LanguageServer::Protocol::Interface
         | 
| @@ -21,7 +27,7 @@ module RubyLsp | |
| 21 27 | 
             
                def start
         | 
| 22 28 | 
             
                  $stderr.puts "Starting Ruby LSP..."
         | 
| 23 29 | 
             
                  @reader.read do |request|
         | 
| 24 | 
            -
                    handle(request)
         | 
| 30 | 
            +
                    with_telemetry(request) { handle(request) }
         | 
| 25 31 | 
             
                  end
         | 
| 26 32 | 
             
                end
         | 
| 27 33 |  | 
| @@ -76,9 +82,10 @@ module RubyLsp | |
| 76 82 | 
             
                  Interface::InitializeResult.new(
         | 
| 77 83 | 
             
                    capabilities: Interface::ServerCapabilities.new(
         | 
| 78 84 | 
             
                      text_document_sync: Interface::TextDocumentSyncOptions.new(
         | 
| 79 | 
            -
                        change: Constant::TextDocumentSyncKind:: | 
| 85 | 
            +
                        change: Constant::TextDocumentSyncKind::INCREMENTAL,
         | 
| 80 86 | 
             
                        open_close: true,
         | 
| 81 87 | 
             
                      ),
         | 
| 88 | 
            +
                      selection_range_provider: enabled_features.include?("selectionRanges"),
         | 
| 82 89 | 
             
                      document_symbol_provider: document_symbol_provider,
         | 
| 83 90 | 
             
                      folding_range_provider: folding_ranges_provider,
         | 
| 84 91 | 
             
                      semantic_tokens_provider: semantic_tokens_provider,
         | 
| @@ -100,9 +107,25 @@ module RubyLsp | |
| 100 107 | 
             
                  end
         | 
| 101 108 | 
             
                end
         | 
| 102 109 |  | 
| 110 | 
            +
                def respond_with_selection_ranges(uri, positions)
         | 
| 111 | 
            +
                  ranges = store.cache_fetch(uri, :selection_ranges) do |document|
         | 
| 112 | 
            +
                    Requests::SelectionRanges.run(document)
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  # Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
         | 
| 116 | 
            +
                  # every position in the positions array should have an element at the same index in the response
         | 
| 117 | 
            +
                  # array. For positions without a valid selection range, the corresponding element in the response
         | 
| 118 | 
            +
                  # array will be nil.
         | 
| 119 | 
            +
                  positions.map do |position|
         | 
| 120 | 
            +
                    ranges.find do |range|
         | 
| 121 | 
            +
                      range.cover?(position)
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 103 126 | 
             
                def respond_with_semantic_highlighting(uri)
         | 
| 104 127 | 
             
                  store.cache_fetch(uri, :semantic_highlighting) do |document|
         | 
| 105 | 
            -
                    Requests::SemanticHighlighting. | 
| 128 | 
            +
                    Requests::SemanticHighlighting.new(document, encoder: Requests::Support::SemanticTokenEncoder.new).run
         | 
| 106 129 | 
             
                  end
         | 
| 107 130 | 
             
                end
         | 
| 108 131 |  | 
| @@ -129,5 +152,30 @@ module RubyLsp | |
| 129 152 | 
             
                    Requests::CodeActions.run(uri, document, range)
         | 
| 130 153 | 
             
                  end
         | 
| 131 154 | 
             
                end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                def configure_options(initialization_options)
         | 
| 157 | 
            +
                  @telemetry_enabled = initialization_options.fetch(:telemetryEnabled, false)
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                def with_telemetry(request)
         | 
| 161 | 
            +
                  return yield unless @telemetry_enabled && !IGNORED_FOR_TELEMETRY.include?(request[:method])
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                  result = nil
         | 
| 164 | 
            +
                  request_time = Benchmark.realtime do
         | 
| 165 | 
            +
                    result = yield
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                  params = {
         | 
| 169 | 
            +
                    request: request[:method],
         | 
| 170 | 
            +
                    requestTime: request_time,
         | 
| 171 | 
            +
                    lspVersion: RubyLsp::VERSION,
         | 
| 172 | 
            +
                  }
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                  uri = request.dig(:params, :textDocument, :uri)
         | 
| 175 | 
            +
                  params[:uri] = uri if uri
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                  @writer.write(method: "telemetry/event", params: params)
         | 
| 178 | 
            +
                  result
         | 
| 179 | 
            +
                end
         | 
| 132 180 | 
             
              end
         | 
| 133 181 | 
             
            end
         | 
| @@ -2,6 +2,17 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RubyLsp
         | 
| 4 4 | 
             
              module Requests
         | 
| 5 | 
            +
                # The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
         | 
| 6 | 
            +
                # request informs the editor of RuboCop quick fixes that can be applied. These are accesible by hovering over a
         | 
| 7 | 
            +
                # specific diagnostic.
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # # Example
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # ```ruby
         | 
| 12 | 
            +
                # def say_hello
         | 
| 13 | 
            +
                # puts "Hello" # --> code action: quick fix indentation
         | 
| 14 | 
            +
                # end
         | 
| 15 | 
            +
                # ```
         | 
| 5 16 | 
             
                class CodeActions
         | 
| 6 17 | 
             
                  def self.run(uri, document, range)
         | 
| 7 18 | 
             
                    new(uri, document, range).run
         | 
| @@ -16,7 +27,7 @@ module RubyLsp | |
| 16 27 | 
             
                  def run
         | 
| 17 28 | 
             
                    diagnostics = Diagnostics.run(@uri, @document)
         | 
| 18 29 | 
             
                    corrections = diagnostics.select { |diagnostic| diagnostic.correctable? && diagnostic.in_range?(@range) }
         | 
| 19 | 
            -
                    return if corrections.empty?
         | 
| 30 | 
            +
                    return [] if corrections.empty?
         | 
| 20 31 |  | 
| 21 32 | 
             
                    corrections.map!(&:to_lsp_code_action)
         | 
| 22 33 | 
             
                  end
         | 
| @@ -2,95 +2,34 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RubyLsp
         | 
| 4 4 | 
             
              module Requests
         | 
| 5 | 
            +
                # The
         | 
| 6 | 
            +
                # [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
         | 
| 7 | 
            +
                # request informs the editor of RuboCop offenses for a given file.
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # # Example
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # ```ruby
         | 
| 12 | 
            +
                # def say_hello
         | 
| 13 | 
            +
                # puts "Hello" # --> diagnostics: incorrect indentantion
         | 
| 14 | 
            +
                # end
         | 
| 15 | 
            +
                # ```
         | 
| 5 16 | 
             
                class Diagnostics < RuboCopRequest
         | 
| 6 | 
            -
                  RUBOCOP_TO_LSP_SEVERITY = {
         | 
| 7 | 
            -
                    convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
         | 
| 8 | 
            -
                    info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
         | 
| 9 | 
            -
                    refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
         | 
| 10 | 
            -
                    warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
         | 
| 11 | 
            -
                    error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
         | 
| 12 | 
            -
                    fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
         | 
| 13 | 
            -
                  }.freeze
         | 
| 14 | 
            -
             | 
| 15 17 | 
             
                  def run
         | 
| 18 | 
            +
                    return syntax_error_diagnostics if @document.syntax_errors?
         | 
| 19 | 
            +
             | 
| 16 20 | 
             
                    super
         | 
| 17 21 |  | 
| 18 22 | 
             
                    @diagnostics
         | 
| 19 23 | 
             
                  end
         | 
| 20 24 |  | 
| 21 25 | 
             
                  def file_finished(_file, offenses)
         | 
| 22 | 
            -
                    @diagnostics = offenses.map { |offense|  | 
| 26 | 
            +
                    @diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, @uri) }
         | 
| 23 27 | 
             
                  end
         | 
| 24 28 |  | 
| 25 | 
            -
                   | 
| 26 | 
            -
                    attr_reader :replacements
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                    def initialize(offense, uri)
         | 
| 29 | 
            -
                      @offense = offense
         | 
| 30 | 
            -
                      @uri = uri
         | 
| 31 | 
            -
                      @replacements = offense.correctable? ? offense_replacements : []
         | 
| 32 | 
            -
                    end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                    def correctable?
         | 
| 35 | 
            -
                      @offense.correctable?
         | 
| 36 | 
            -
                    end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                    def in_range?(range)
         | 
| 39 | 
            -
                      range.cover?(@offense.line - 1)
         | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                    def to_lsp_code_action
         | 
| 43 | 
            -
                      LanguageServer::Protocol::Interface::CodeAction.new(
         | 
| 44 | 
            -
                        title: "Autocorrect #{@offense.cop_name}",
         | 
| 45 | 
            -
                        kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
         | 
| 46 | 
            -
                        edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
         | 
| 47 | 
            -
                          document_changes: [
         | 
| 48 | 
            -
                            LanguageServer::Protocol::Interface::TextDocumentEdit.new(
         | 
| 49 | 
            -
                              text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
         | 
| 50 | 
            -
                                uri: @uri,
         | 
| 51 | 
            -
                                version: nil
         | 
| 52 | 
            -
                              ),
         | 
| 53 | 
            -
                              edits: @replacements
         | 
| 54 | 
            -
                            ),
         | 
| 55 | 
            -
                          ]
         | 
| 56 | 
            -
                        ),
         | 
| 57 | 
            -
                        is_preferred: true,
         | 
| 58 | 
            -
                      )
         | 
| 59 | 
            -
                    end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                    def to_lsp_diagnostic
         | 
| 62 | 
            -
                      LanguageServer::Protocol::Interface::Diagnostic.new(
         | 
| 63 | 
            -
                        message: @offense.message,
         | 
| 64 | 
            -
                        source: "RuboCop",
         | 
| 65 | 
            -
                        code: @offense.cop_name,
         | 
| 66 | 
            -
                        severity: RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name],
         | 
| 67 | 
            -
                        range: LanguageServer::Protocol::Interface::Range.new(
         | 
| 68 | 
            -
                          start: LanguageServer::Protocol::Interface::Position.new(
         | 
| 69 | 
            -
                            line: @offense.line - 1,
         | 
| 70 | 
            -
                            character: @offense.column
         | 
| 71 | 
            -
                          ),
         | 
| 72 | 
            -
                          end: LanguageServer::Protocol::Interface::Position.new(
         | 
| 73 | 
            -
                            line: @offense.last_line - 1,
         | 
| 74 | 
            -
                            character: @offense.last_column
         | 
| 75 | 
            -
                          )
         | 
| 76 | 
            -
                        )
         | 
| 77 | 
            -
                      )
         | 
| 78 | 
            -
                    end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                    private
         | 
| 29 | 
            +
                  private
         | 
| 81 30 |  | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
                        LanguageServer::Protocol::Interface::TextEdit.new(
         | 
| 85 | 
            -
                          range: LanguageServer::Protocol::Interface::Range.new(
         | 
| 86 | 
            -
                            start: LanguageServer::Protocol::Interface::Position.new(line: range.line - 1, character: range.column),
         | 
| 87 | 
            -
                            end: LanguageServer::Protocol::Interface::Position.new(line: range.last_line - 1,
         | 
| 88 | 
            -
                              character: range.last_column)
         | 
| 89 | 
            -
                          ),
         | 
| 90 | 
            -
                          new_text: replacement
         | 
| 91 | 
            -
                        )
         | 
| 92 | 
            -
                      end
         | 
| 93 | 
            -
                    end
         | 
| 31 | 
            +
                  def syntax_error_diagnostics
         | 
| 32 | 
            +
                    @document.syntax_error_edits.map { |e| Support::SyntaxErrorDiagnostic.new(e) }
         | 
| 94 33 | 
             
                  end
         | 
| 95 34 | 
             
                end
         | 
| 96 35 | 
             
              end
         | 
| @@ -2,6 +2,27 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RubyLsp
         | 
| 4 4 | 
             
              module Requests
         | 
| 5 | 
            +
                # The [document
         | 
| 6 | 
            +
                # symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
         | 
| 7 | 
            +
                # informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
         | 
| 8 | 
            +
                # this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # In VS Code, fuzzy symbol search can be accessed by opened the command palette and inserting an `@` symbol.
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                # # Example
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # ```ruby
         | 
| 15 | 
            +
                # class Person # --> document symbol: class
         | 
| 16 | 
            +
                #   attr_reader :age # --> document symbol: field
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                #   def initialize
         | 
| 19 | 
            +
                #     @age = 0 # --> document symbol: variable
         | 
| 20 | 
            +
                #   end
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                #   def age # --> document symbol: method
         | 
| 23 | 
            +
                #   end
         | 
| 24 | 
            +
                # end
         | 
| 25 | 
            +
                # ```
         | 
| 5 26 | 
             
                class DocumentSymbol < BaseRequest
         | 
| 6 27 | 
             
                  SYMBOL_KIND = {
         | 
| 7 28 | 
             
                    file: 1,
         | 
| @@ -2,6 +2,15 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RubyLsp
         | 
| 4 4 | 
             
              module Requests
         | 
| 5 | 
            +
                # The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
         | 
| 6 | 
            +
                # request informs the editor of the ranges where code can be folded.
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                # # Example
         | 
| 9 | 
            +
                # ```ruby
         | 
| 10 | 
            +
                # def say_hello # <-- folding range start
         | 
| 11 | 
            +
                #   puts "Hello"
         | 
| 12 | 
            +
                # end # <-- folding range end
         | 
| 13 | 
            +
                # ```
         | 
| 5 14 | 
             
                class FoldingRanges < BaseRequest
         | 
| 6 15 | 
             
                  SIMPLE_FOLDABLES = [
         | 
| 7 16 | 
             
                    SyntaxTree::ArrayLiteral,
         | 
| @@ -2,6 +2,17 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RubyLsp
         | 
| 4 4 | 
             
              module Requests
         | 
| 5 | 
            +
                # The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
         | 
| 6 | 
            +
                # request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
         | 
| 7 | 
            +
                # registering the ruby-lsp as the Ruby formatter.
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # # Example
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # ```ruby
         | 
| 12 | 
            +
                # def say_hello
         | 
| 13 | 
            +
                # puts "Hello" # --> formatting: fixes the indentation on save
         | 
| 14 | 
            +
                # end
         | 
| 15 | 
            +
                # ```
         | 
| 5 16 | 
             
                class Formatting < RuboCopRequest
         | 
| 6 17 | 
             
                  RUBOCOP_FLAGS = (COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze
         | 
| 7 18 |  | 
| @@ -5,6 +5,7 @@ require "cgi" | |
| 5 5 |  | 
| 6 6 | 
             
            module RubyLsp
         | 
| 7 7 | 
             
              module Requests
         | 
| 8 | 
            +
                # :nodoc:
         | 
| 8 9 | 
             
                class RuboCopRequest < RuboCop::Runner
         | 
| 9 10 | 
             
                  COMMON_RUBOCOP_FLAGS = [
         | 
| 10 11 | 
             
                    "--stderr", # Print any output to stderr so that our stdout does not get polluted
         | 
| @@ -20,6 +21,7 @@ module RubyLsp | |
| 20 21 |  | 
| 21 22 | 
             
                  def initialize(uri, document)
         | 
| 22 23 | 
             
                    @file = CGI.unescape(URI.parse(uri).path)
         | 
| 24 | 
            +
                    @document = document
         | 
| 23 25 | 
             
                    @text = document.source
         | 
| 24 26 | 
             
                    @uri = uri
         | 
| 25 27 |  | 
| @@ -0,0 +1,102 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyLsp
         | 
| 4 | 
            +
              module Requests
         | 
| 5 | 
            +
                # The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
         | 
| 6 | 
            +
                # request informs the editor of ranges that the user may want to select based on the location(s)
         | 
| 7 | 
            +
                # of their cursor(s).
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # Trigger this request with: Ctrl + Shift + -> or Ctrl + Shift + <-
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # # Example
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # ```ruby
         | 
| 14 | 
            +
                # def foo # --> The next selection range encompasses the entire method definition.
         | 
| 15 | 
            +
                #   puts "Hello, world!" # --> Cursor is on this line
         | 
| 16 | 
            +
                # end
         | 
| 17 | 
            +
                # ```
         | 
| 18 | 
            +
                class SelectionRanges < BaseRequest
         | 
| 19 | 
            +
                  NODES_THAT_CAN_BE_PARENTS = [
         | 
| 20 | 
            +
                    SyntaxTree::Assign,
         | 
| 21 | 
            +
                    SyntaxTree::ArrayLiteral,
         | 
| 22 | 
            +
                    SyntaxTree::Begin,
         | 
| 23 | 
            +
                    SyntaxTree::BraceBlock,
         | 
| 24 | 
            +
                    SyntaxTree::Call,
         | 
| 25 | 
            +
                    SyntaxTree::Case,
         | 
| 26 | 
            +
                    SyntaxTree::ClassDeclaration,
         | 
| 27 | 
            +
                    SyntaxTree::Command,
         | 
| 28 | 
            +
                    SyntaxTree::Def,
         | 
| 29 | 
            +
                    SyntaxTree::Defs,
         | 
| 30 | 
            +
                    SyntaxTree::DoBlock,
         | 
| 31 | 
            +
                    SyntaxTree::Elsif,
         | 
| 32 | 
            +
                    SyntaxTree::Else,
         | 
| 33 | 
            +
                    SyntaxTree::EmbDoc,
         | 
| 34 | 
            +
                    SyntaxTree::Ensure,
         | 
| 35 | 
            +
                    SyntaxTree::FCall,
         | 
| 36 | 
            +
                    SyntaxTree::For,
         | 
| 37 | 
            +
                    SyntaxTree::HashLiteral,
         | 
| 38 | 
            +
                    SyntaxTree::Heredoc,
         | 
| 39 | 
            +
                    SyntaxTree::HeredocBeg,
         | 
| 40 | 
            +
                    SyntaxTree::HshPtn,
         | 
| 41 | 
            +
                    SyntaxTree::If,
         | 
| 42 | 
            +
                    SyntaxTree::In,
         | 
| 43 | 
            +
                    SyntaxTree::Lambda,
         | 
| 44 | 
            +
                    SyntaxTree::MethodAddBlock,
         | 
| 45 | 
            +
                    SyntaxTree::ModuleDeclaration,
         | 
| 46 | 
            +
                    SyntaxTree::Params,
         | 
| 47 | 
            +
                    SyntaxTree::Rescue,
         | 
| 48 | 
            +
                    SyntaxTree::RescueEx,
         | 
| 49 | 
            +
                    SyntaxTree::StringConcat,
         | 
| 50 | 
            +
                    SyntaxTree::StringLiteral,
         | 
| 51 | 
            +
                    SyntaxTree::Unless,
         | 
| 52 | 
            +
                    SyntaxTree::Until,
         | 
| 53 | 
            +
                    SyntaxTree::VCall,
         | 
| 54 | 
            +
                    SyntaxTree::When,
         | 
| 55 | 
            +
                    SyntaxTree::While,
         | 
| 56 | 
            +
                  ].freeze
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def initialize(document)
         | 
| 59 | 
            +
                    super(document)
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    @ranges = []
         | 
| 62 | 
            +
                    @stack = []
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def run
         | 
| 66 | 
            +
                    visit(@document.tree)
         | 
| 67 | 
            +
                    @ranges.reverse!
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  private
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  def visit(node)
         | 
| 73 | 
            +
                    return if node.nil?
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    range = create_selection_range(node.location, @stack.last)
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    @ranges << range
         | 
| 78 | 
            +
                    return if node.child_nodes.empty?
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    @stack << range if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
         | 
| 81 | 
            +
                    visit_all(node.child_nodes)
         | 
| 82 | 
            +
                    @stack.pop if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  def create_selection_range(location, parent = nil)
         | 
| 86 | 
            +
                    RubyLsp::Requests::Support::SelectionRange.new(
         | 
| 87 | 
            +
                      range: LanguageServer::Protocol::Interface::Range.new(
         | 
| 88 | 
            +
                        start: LanguageServer::Protocol::Interface::Position.new(
         | 
| 89 | 
            +
                          line: location.start_line - 1,
         | 
| 90 | 
            +
                          character: location.start_column,
         | 
| 91 | 
            +
                        ),
         | 
| 92 | 
            +
                        end: LanguageServer::Protocol::Interface::Position.new(
         | 
| 93 | 
            +
                          line: location.end_line - 1,
         | 
| 94 | 
            +
                          character: location.end_column,
         | 
| 95 | 
            +
                        ),
         | 
| 96 | 
            +
                      ),
         | 
| 97 | 
            +
                      parent: parent
         | 
| 98 | 
            +
                    )
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
            end
         | 
| @@ -2,6 +2,19 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RubyLsp
         | 
| 4 4 | 
             
              module Requests
         | 
| 5 | 
            +
                # The [semantic
         | 
| 6 | 
            +
                # highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
         | 
| 7 | 
            +
                # request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # # Example
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # ```ruby
         | 
| 12 | 
            +
                # def foo
         | 
| 13 | 
            +
                #   var = 1 # --> semantic highlighting: local variable
         | 
| 14 | 
            +
                #   some_invocation # --> semantic highlighting: method invocation
         | 
| 15 | 
            +
                #   var # --> semantic highlighting: local variable
         | 
| 16 | 
            +
                # end
         | 
| 17 | 
            +
                # ```
         | 
| 5 18 | 
             
                class SemanticHighlighting < BaseRequest
         | 
| 6 19 | 
             
                  TOKEN_TYPES = [
         | 
| 7 20 | 
             
                    :variable,
         | 
| @@ -9,18 +22,21 @@ module RubyLsp | |
| 9 22 | 
             
                  ].freeze
         | 
| 10 23 | 
             
                  TOKEN_MODIFIERS = [].freeze
         | 
| 11 24 |  | 
| 12 | 
            -
                   | 
| 13 | 
            -
                    super
         | 
| 25 | 
            +
                  SemanticToken = Struct.new(:location, :length, :type, :modifier)
         | 
| 14 26 |  | 
| 27 | 
            +
                  def initialize(document, encoder: nil)
         | 
| 28 | 
            +
                    super(document)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    @encoder = encoder
         | 
| 15 31 | 
             
                    @tokens = []
         | 
| 16 32 | 
             
                    @tree = document.tree
         | 
| 17 | 
            -
                    @current_row = 0
         | 
| 18 | 
            -
                    @current_column = 0
         | 
| 19 33 | 
             
                  end
         | 
| 20 34 |  | 
| 21 35 | 
             
                  def run
         | 
| 22 36 | 
             
                    visit(@tree)
         | 
| 23 | 
            -
                     | 
| 37 | 
            +
                    return @tokens unless @encoder
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    @encoder.encode(@tokens)
         | 
| 24 40 | 
             
                  end
         | 
| 25 41 |  | 
| 26 42 | 
             
                  def visit_m_assign(node)
         | 
| @@ -73,44 +89,10 @@ module RubyLsp | |
| 73 89 | 
             
                    add_token(node.value.location, :method)
         | 
| 74 90 | 
             
                  end
         | 
| 75 91 |  | 
| 76 | 
            -
                  def add_token(location,  | 
| 92 | 
            +
                  def add_token(location, type)
         | 
| 77 93 | 
             
                    length = location.end_char - location.start_char
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                    compute_delta(location) do |delta_line, delta_column|
         | 
| 80 | 
            -
                      @tokens.push(delta_line, delta_column, length, TOKEN_TYPES.index(classification), 0)
         | 
| 81 | 
            -
                    end
         | 
| 82 | 
            -
                  end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                  # The delta array is computed according to the LSP specification:
         | 
| 85 | 
            -
                  # > The protocol for the token format relative uses relative
         | 
| 86 | 
            -
                  # > positions, because most tokens remain stable relative to
         | 
| 87 | 
            -
                  # > each other when edits are made in a file. This simplifies
         | 
| 88 | 
            -
                  # > the computation of a delta if a server supports it. So each
         | 
| 89 | 
            -
                  # > token is represented using 5 integers.
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                  # For more information on how each number is calculated, read:
         | 
| 92 | 
            -
                  # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
         | 
| 93 | 
            -
                  def compute_delta(location)
         | 
| 94 | 
            -
                    row = location.start_line - 1
         | 
| 95 | 
            -
                    column = location.start_column
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                    if row < @current_row
         | 
| 98 | 
            -
                      raise InvalidTokenRowError, "Invalid token row detected: " \
         | 
| 99 | 
            -
                        "Ensure tokens are added in the expected order."
         | 
| 100 | 
            -
                    end
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                    delta_line = row - @current_row
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                    delta_column = column
         | 
| 105 | 
            -
                    delta_column -= @current_column if delta_line == 0
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                    yield delta_line, delta_column
         | 
| 108 | 
            -
             | 
| 109 | 
            -
                    @current_row = row
         | 
| 110 | 
            -
                    @current_column = column
         | 
| 94 | 
            +
                    @tokens.push(SemanticToken.new(location, length, TOKEN_TYPES.index(type), 0))
         | 
| 111 95 | 
             
                  end
         | 
| 112 | 
            -
             | 
| 113 | 
            -
                  class InvalidTokenRowError < StandardError; end
         | 
| 114 96 | 
             
                end
         | 
| 115 97 | 
             
              end
         | 
| 116 98 | 
             
            end
         | 
| @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyLsp
         | 
| 4 | 
            +
              module Requests
         | 
| 5 | 
            +
                module Support
         | 
| 6 | 
            +
                  class RuboCopDiagnostic
         | 
| 7 | 
            +
                    RUBOCOP_TO_LSP_SEVERITY = {
         | 
| 8 | 
            +
                      convention: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
         | 
| 9 | 
            +
                      info: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
         | 
| 10 | 
            +
                      refactor: LanguageServer::Protocol::Constant::DiagnosticSeverity::INFORMATION,
         | 
| 11 | 
            +
                      warning: LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING,
         | 
| 12 | 
            +
                      error: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
         | 
| 13 | 
            +
                      fatal: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
         | 
| 14 | 
            +
                    }.freeze
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    attr_reader :replacements
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def initialize(offense, uri)
         | 
| 19 | 
            +
                      @offense = offense
         | 
| 20 | 
            +
                      @uri = uri
         | 
| 21 | 
            +
                      @replacements = offense.correctable? ? offense_replacements : []
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    def correctable?
         | 
| 25 | 
            +
                      @offense.correctable?
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    def in_range?(range)
         | 
| 29 | 
            +
                      range.cover?(@offense.line - 1)
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def to_lsp_code_action
         | 
| 33 | 
            +
                      LanguageServer::Protocol::Interface::CodeAction.new(
         | 
| 34 | 
            +
                        title: "Autocorrect #{@offense.cop_name}",
         | 
| 35 | 
            +
                        kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
         | 
| 36 | 
            +
                        edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
         | 
| 37 | 
            +
                          document_changes: [
         | 
| 38 | 
            +
                            LanguageServer::Protocol::Interface::TextDocumentEdit.new(
         | 
| 39 | 
            +
                              text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
         | 
| 40 | 
            +
                                uri: @uri,
         | 
| 41 | 
            +
                                version: nil
         | 
| 42 | 
            +
                              ),
         | 
| 43 | 
            +
                              edits: @replacements
         | 
| 44 | 
            +
                            ),
         | 
| 45 | 
            +
                          ]
         | 
| 46 | 
            +
                        ),
         | 
| 47 | 
            +
                        is_preferred: true,
         | 
| 48 | 
            +
                      )
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    def to_lsp_diagnostic
         | 
| 52 | 
            +
                      LanguageServer::Protocol::Interface::Diagnostic.new(
         | 
| 53 | 
            +
                        message: @offense.message,
         | 
| 54 | 
            +
                        source: "RuboCop",
         | 
| 55 | 
            +
                        code: @offense.cop_name,
         | 
| 56 | 
            +
                        severity: RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name],
         | 
| 57 | 
            +
                        range: LanguageServer::Protocol::Interface::Range.new(
         | 
| 58 | 
            +
                          start: LanguageServer::Protocol::Interface::Position.new(
         | 
| 59 | 
            +
                            line: @offense.line - 1,
         | 
| 60 | 
            +
                            character: @offense.column
         | 
| 61 | 
            +
                          ),
         | 
| 62 | 
            +
                          end: LanguageServer::Protocol::Interface::Position.new(
         | 
| 63 | 
            +
                            line: @offense.last_line - 1,
         | 
| 64 | 
            +
                            character: @offense.last_column
         | 
| 65 | 
            +
                          )
         | 
| 66 | 
            +
                        )
         | 
| 67 | 
            +
                      )
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    private
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    def offense_replacements
         | 
| 73 | 
            +
                      @offense.corrector.as_replacements.map do |range, replacement|
         | 
| 74 | 
            +
                        LanguageServer::Protocol::Interface::TextEdit.new(
         | 
| 75 | 
            +
                          range: LanguageServer::Protocol::Interface::Range.new(
         | 
| 76 | 
            +
                            start: LanguageServer::Protocol::Interface::Position.new(line: range.line - 1, character: range.column),
         | 
| 77 | 
            +
                            end: LanguageServer::Protocol::Interface::Position.new(line: range.last_line - 1,
         | 
| 78 | 
            +
                              character: range.last_column)
         | 
| 79 | 
            +
                          ),
         | 
| 80 | 
            +
                          new_text: replacement
         | 
| 81 | 
            +
                        )
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyLsp
         | 
| 4 | 
            +
              module Requests
         | 
| 5 | 
            +
                module Support
         | 
| 6 | 
            +
                  class SelectionRange < LanguageServer::Protocol::Interface::SelectionRange
         | 
| 7 | 
            +
                    def cover?(position)
         | 
| 8 | 
            +
                      line_range = (range.start.line..range.end.line)
         | 
| 9 | 
            +
                      character_range = (range.start.character..range.end.character)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                      line_range.cover?(position[:line]) && character_range.cover?(position[:character])
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyLsp
         | 
| 4 | 
            +
              module Requests
         | 
| 5 | 
            +
                module Support
         | 
| 6 | 
            +
                  class SemanticTokenEncoder
         | 
| 7 | 
            +
                    def initialize
         | 
| 8 | 
            +
                      @current_row = 0
         | 
| 9 | 
            +
                      @current_column = 0
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    def encode(tokens)
         | 
| 13 | 
            +
                      delta = tokens
         | 
| 14 | 
            +
                        .sort_by do |token|
         | 
| 15 | 
            +
                          [token.location.start_line, token.location.start_column]
         | 
| 16 | 
            +
                        end
         | 
| 17 | 
            +
                        .flat_map do |token|
         | 
| 18 | 
            +
                          compute_delta(token)
         | 
| 19 | 
            +
                        end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      LanguageServer::Protocol::Interface::SemanticTokens.new(data: delta)
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    # The delta array is computed according to the LSP specification:
         | 
| 25 | 
            +
                    # > The protocol for the token format relative uses relative
         | 
| 26 | 
            +
                    # > positions, because most tokens remain stable relative to
         | 
| 27 | 
            +
                    # > each other when edits are made in a file. This simplifies
         | 
| 28 | 
            +
                    # > the computation of a delta if a server supports it. So each
         | 
| 29 | 
            +
                    # > token is represented using 5 integers.
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    # For more information on how each number is calculated, read:
         | 
| 32 | 
            +
                    # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_semanticTokens
         | 
| 33 | 
            +
                    def compute_delta(token)
         | 
| 34 | 
            +
                      row = token.location.start_line - 1
         | 
| 35 | 
            +
                      column = token.location.start_column
         | 
| 36 | 
            +
                      delta_line = row - @current_row
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      delta_column = column
         | 
| 39 | 
            +
                      delta_column -= @current_column if delta_line == 0
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      [delta_line, delta_column, token.length, token.type, token.modifier]
         | 
| 42 | 
            +
                    ensure
         | 
| 43 | 
            +
                      @current_row = row
         | 
| 44 | 
            +
                      @current_column = column
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyLsp
         | 
| 4 | 
            +
              module Requests
         | 
| 5 | 
            +
                module Support
         | 
| 6 | 
            +
                  class SyntaxErrorDiagnostic
         | 
| 7 | 
            +
                    def initialize(edit)
         | 
| 8 | 
            +
                      @edit = edit
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def correctable?
         | 
| 12 | 
            +
                      false
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def to_lsp_diagnostic
         | 
| 16 | 
            +
                      LanguageServer::Protocol::Interface::Diagnostic.new(
         | 
| 17 | 
            +
                        message: "Syntax error",
         | 
| 18 | 
            +
                        source: "SyntaxTree",
         | 
| 19 | 
            +
                        severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
         | 
| 20 | 
            +
                        range: @edit[:range]
         | 
| 21 | 
            +
                      )
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
    
        data/lib/ruby_lsp/requests.rb
    CHANGED
    
    | @@ -5,10 +5,18 @@ module RubyLsp | |
| 5 5 | 
             
                autoload :BaseRequest, "ruby_lsp/requests/base_request"
         | 
| 6 6 | 
             
                autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
         | 
| 7 7 | 
             
                autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
         | 
| 8 | 
            +
                autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
         | 
| 8 9 | 
             
                autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
         | 
| 9 10 | 
             
                autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
         | 
| 10 11 | 
             
                autoload :Formatting, "ruby_lsp/requests/formatting"
         | 
| 11 12 | 
             
                autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
         | 
| 12 13 | 
             
                autoload :CodeActions, "ruby_lsp/requests/code_actions"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                module Support
         | 
| 16 | 
            +
                  autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
         | 
| 17 | 
            +
                  autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
         | 
| 18 | 
            +
                  autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
         | 
| 19 | 
            +
                  autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
         | 
| 20 | 
            +
                end
         | 
| 13 21 | 
             
              end
         | 
| 14 22 | 
             
            end
         | 
    
        data/lib/ruby_lsp/store.rb
    CHANGED
    
    
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            desc "Check if all LSP requests are documented"
         | 
| 4 | 
            +
            task :check_docs do
         | 
| 5 | 
            +
              require "language_server-protocol"
         | 
| 6 | 
            +
              require "syntax_tree"
         | 
| 7 | 
            +
              require "logger"
         | 
| 8 | 
            +
              require "ruby_lsp/requests/base_request"
         | 
| 9 | 
            +
              require "ruby_lsp/requests/rubocop_request"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              Dir["#{Dir.pwd}/lib/ruby_lsp/requests/*.rb"].each do |file|
         | 
| 12 | 
            +
                require(file)
         | 
| 13 | 
            +
                YARD.parse(file, [], Logger::Severity::FATAL)
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              spec_matcher = %r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}
         | 
| 17 | 
            +
              error_messages = RubyLsp::Requests.constants.each_with_object(Hash.new { |h, k| h[k] = [] }) do |request, errors|
         | 
| 18 | 
            +
                full_name = "RubyLsp::Requests::#{request}"
         | 
| 19 | 
            +
                docs = YARD::Registry.at(full_name).docstring
         | 
| 20 | 
            +
                next if /:nodoc:/.match?(docs)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                if docs.empty?
         | 
| 23 | 
            +
                  errors[full_name] << "Missing documentation for request handler class"
         | 
| 24 | 
            +
                elsif !spec_matcher.match?(docs)
         | 
| 25 | 
            +
                  errors[full_name] << <<~MESSAGE
         | 
| 26 | 
            +
                    Documentation for request handler classes must link to the official LSP specification.
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    For example, if your request handles text document hover, you should add a link to
         | 
| 29 | 
            +
                    https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover.
         | 
| 30 | 
            +
                  MESSAGE
         | 
| 31 | 
            +
                elsif !/# Example/.match?(docs)
         | 
| 32 | 
            +
                  errors[full_name] << <<~MESSAGE
         | 
| 33 | 
            +
                    Documentation for request handler class must contain an example.
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    = Example
         | 
| 36 | 
            +
                        def my_method # <-- something happens here
         | 
| 37 | 
            +
                        end
         | 
| 38 | 
            +
                  MESSAGE
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              formatted_errors = error_messages.map { |name, errors| "#{name}: #{errors.join(", ")}" }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              if error_messages.any?
         | 
| 45 | 
            +
                puts <<~MESSAGE
         | 
| 46 | 
            +
                  The following requests have invalid documentation:
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  #{formatted_errors.join("\n")}
         | 
| 49 | 
            +
                MESSAGE
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                exit!
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ruby-lsp
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Shopify
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022- | 
| 11 | 
            +
            date: 2022-05-27 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: language_server-protocol
         | 
| @@ -64,6 +64,7 @@ files: | |
| 64 64 | 
             
            - ".github/probots.yml"
         | 
| 65 65 | 
             
            - ".github/pull_request_template.md"
         | 
| 66 66 | 
             
            - ".github/workflows/ci.yml"
         | 
| 67 | 
            +
            - ".github/workflows/publish_docs.yml"
         | 
| 67 68 | 
             
            - ".gitignore"
         | 
| 68 69 | 
             
            - ".rubocop.yml"
         | 
| 69 70 | 
             
            - ".vscode/extensions.json"
         | 
| @@ -93,8 +94,14 @@ files: | |
| 93 94 | 
             
            - lib/ruby_lsp/requests/folding_ranges.rb
         | 
| 94 95 | 
             
            - lib/ruby_lsp/requests/formatting.rb
         | 
| 95 96 | 
             
            - lib/ruby_lsp/requests/rubocop_request.rb
         | 
| 97 | 
            +
            - lib/ruby_lsp/requests/selection_ranges.rb
         | 
| 96 98 | 
             
            - lib/ruby_lsp/requests/semantic_highlighting.rb
         | 
| 99 | 
            +
            - lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
         | 
| 100 | 
            +
            - lib/ruby_lsp/requests/support/selection_range.rb
         | 
| 101 | 
            +
            - lib/ruby_lsp/requests/support/semantic_token_encoder.rb
         | 
| 102 | 
            +
            - lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb
         | 
| 97 103 | 
             
            - lib/ruby_lsp/store.rb
         | 
| 104 | 
            +
            - rakelib/check_docs.rake
         | 
| 98 105 | 
             
            - ruby-lsp.gemspec
         | 
| 99 106 | 
             
            - service.yml
         | 
| 100 107 | 
             
            - shipit.production.yml
         |