ruby-lsp 0.0.2 → 0.1.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/.github/workflows/ci.yml +9 -1
- data/.github/workflows/publish_docs.yml +32 -0
- data/.rubocop.yml +25 -0
- data/CHANGELOG.md +23 -0
- data/Gemfile +8 -4
- data/Gemfile.lock +64 -13
- data/README.md +58 -1
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/bin/tapioca +29 -0
- data/dev.yml +3 -0
- data/exe/ruby-lsp +19 -3
- data/lib/ruby-lsp.rb +2 -0
- data/lib/ruby_lsp/cli.rb +23 -7
- data/lib/ruby_lsp/document.rb +98 -6
- data/lib/ruby_lsp/handler.rb +119 -18
- data/lib/ruby_lsp/internal.rb +7 -0
- data/lib/ruby_lsp/requests/base_request.rb +19 -5
- data/lib/ruby_lsp/requests/code_actions.rb +30 -9
- data/lib/ruby_lsp/requests/diagnostics.rb +29 -77
- data/lib/ruby_lsp/requests/document_highlight.rb +111 -0
- data/lib/ruby_lsp/requests/document_symbol.rb +75 -16
- data/lib/ruby_lsp/requests/folding_ranges.rb +63 -19
- data/lib/ruby_lsp/requests/formatting.rb +19 -2
- data/lib/ruby_lsp/requests/rubocop_request.rb +21 -8
- data/lib/ruby_lsp/requests/selection_ranges.rb +114 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +132 -61
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +100 -0
- data/lib/ruby_lsp/requests/support/selection_range.rb +20 -0
- data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +70 -0
- data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +32 -0
- data/lib/ruby_lsp/requests.rb +10 -0
- data/lib/ruby_lsp/store.rb +23 -2
- data/rakelib/check_docs.rake +57 -0
- data/ruby-lsp.gemspec +2 -1
- data/sorbet/config +4 -0
- data/sorbet/rbi/.rubocop.yml +8 -0
- data/sorbet/rbi/gems/ansi@1.5.0.rbi +338 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +522 -0
- data/sorbet/rbi/gems/builder@3.2.4.rbi +418 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/debug@1.5.0.rbi +1273 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +867 -0
- data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
- data/sorbet/rbi/gems/irb@1.4.1.rbi +376 -0
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +7325 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +612 -0
- data/sorbet/rbi/gems/minitest@5.15.0.rbi +994 -0
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +163 -0
- data/sorbet/rbi/gems/parser@3.1.2.0.rbi +3968 -0
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +734 -0
- data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +227 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +1853 -0
- data/sorbet/rbi/gems/rbi@0.0.14.rbi +2337 -0
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +1854 -0
- data/sorbet/rbi/gems/reline@0.3.1.rbi +1274 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +3852 -0
- data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +4180 -0
- data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +1369 -0
- data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +246 -0
- data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +8 -0
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +652 -0
- data/sorbet/rbi/gems/rubocop@1.30.0.rbi +36729 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +732 -0
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +1600 -0
- data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +6777 -0
- data/sorbet/rbi/gems/tapioca@0.8.1.rbi +1972 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +2921 -0
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +27 -0
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +2789 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +1779 -0
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +289 -0
- data/sorbet/rbi/gems/yard@0.9.27.rbi +13048 -0
- data/sorbet/rbi/shims/fiddle.rbi +4 -0
- data/sorbet/rbi/shims/hash.rbi +6 -0
- data/sorbet/rbi/shims/rdoc.rbi +4 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +7 -0
- metadata +74 -6
- data/shipit.production.yml +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2b072ffa3ea73a83aba6d6c32b931c4de30b0de06fd70655d620c3ed95bd8aa4
|
|
4
|
+
data.tar.gz: 24568688b20ffed38defcbc0b00708eb22a7dad20a9b5f397348242a64359bfd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ea1830f0be08cb291e67d80bbe21dbe17f3e4b4d285b1d9112e004ca2feff32eaca85af755bc2635e3341ab4e64491710c3ba13c5bed617e34495534e1d7a5f8
|
|
7
|
+
data.tar.gz: f84b1b27c5c17455d1e8423f1e52f98a754ed123e1722bd536ee7bfdc754952e5d6619c0f4402664a33dc278bdc2422a3e7a37c8de5f172e4456e7a6c08b7a67
|
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,7 +17,15 @@ jobs:
|
|
|
17
17
|
with:
|
|
18
18
|
ruby-version: ${{ matrix.ruby }}
|
|
19
19
|
bundler-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Check if documentation is up to date
|
|
22
|
+
run: bundle exec rake check_docs
|
|
23
|
+
|
|
24
|
+
- name: Typecheck
|
|
25
|
+
run: bundle exec srb tc
|
|
26
|
+
|
|
20
27
|
- name: Lint Ruby files
|
|
21
28
|
run: bin/rubocop
|
|
29
|
+
|
|
22
30
|
- name: Run tests
|
|
23
31
|
run: bin/test
|
|
@@ -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/.rubocop.yml
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
inherit_gem:
|
|
2
2
|
rubocop-shopify: rubocop.yml
|
|
3
3
|
|
|
4
|
+
require:
|
|
5
|
+
- rubocop-sorbet
|
|
6
|
+
- rubocop-minitest
|
|
7
|
+
- rubocop-rake
|
|
8
|
+
|
|
4
9
|
AllCops:
|
|
5
10
|
NewCops: disable
|
|
6
11
|
SuggestExtensions: false
|
|
@@ -8,7 +13,27 @@ AllCops:
|
|
|
8
13
|
Exclude:
|
|
9
14
|
- "vendor/**/*"
|
|
10
15
|
- "features/**/*"
|
|
16
|
+
- "test/fixtures/**/*"
|
|
11
17
|
|
|
12
18
|
Naming/FileName:
|
|
13
19
|
Exclude:
|
|
14
20
|
- "lib/ruby-lsp.rb"
|
|
21
|
+
|
|
22
|
+
Sorbet/FalseSigil:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Sorbet/TrueSigil:
|
|
26
|
+
Enabled: true
|
|
27
|
+
Include:
|
|
28
|
+
- "test/**/*.rb"
|
|
29
|
+
Exclude:
|
|
30
|
+
- "**/*.rake"
|
|
31
|
+
- "lib/**/*.rb"
|
|
32
|
+
|
|
33
|
+
Sorbet/StrictSigil:
|
|
34
|
+
Enabled: true
|
|
35
|
+
Include:
|
|
36
|
+
- "lib/**/*.rb"
|
|
37
|
+
Exclude:
|
|
38
|
+
- "**/*.rake"
|
|
39
|
+
- "test/**/*.rb"
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.0]
|
|
10
|
+
|
|
11
|
+
- Implement token modifiers in SemanticTokenEncoder ([#112](https://github.com/Shopify/ruby-lsp/pull/112))
|
|
12
|
+
- Add semantic token for name in a method definition ([#133](https://github.com/Shopify/ruby-lsp/pull/133))
|
|
13
|
+
- Add semantic highighting for def endless and singleton method names ([#134](https://github.com/Shopify/ruby-lsp/pull/134))
|
|
14
|
+
- Add semantic token for keyword self ([#137](https://github.com/Shopify/ruby-lsp/pull/137))
|
|
15
|
+
- Add semantic token for constants ([#138](https://github.com/Shopify/ruby-lsp/pull/138))
|
|
16
|
+
- Improve error handling + fix formatting hanging issue ([#149](https://github.com/Shopify/ruby-lsp/pull/149))
|
|
17
|
+
- Set the minimum syntax_tree version to 2.4 ([#151](https://github.com/Shopify/ruby-lsp/pull/151))
|
|
18
|
+
|
|
19
|
+
## [0.0.4]
|
|
20
|
+
|
|
21
|
+
- Add basic document highlight (https://github.com/Shopify/ruby-lsp/pull/91)
|
|
22
|
+
- Add error telemetry (https://github.com/Shopify/ruby-lsp/pull/100)
|
|
23
|
+
- Always push telemetry events from the server (https://github.com/Shopify/ruby-lsp/pull/109)
|
|
24
|
+
- Fix multibyte character handling (https://github.com/Shopify/ruby-lsp/pull/122)
|
|
25
|
+
- Add Sorbet to the Ruby LSP (https://github.com/Shopify/ruby-lsp/pull/119, https://github.com/Shopify/ruby-lsp/pull/123)
|
|
26
|
+
|
|
27
|
+
## [0.0.3]
|
|
28
|
+
|
|
29
|
+
- Fixed code actions return hanging
|
|
30
|
+
- Moved to incremental text synchronization
|
|
31
|
+
- Added syntax error resiliency
|
|
9
32
|
|
|
10
33
|
## [0.0.2]
|
|
11
34
|
|
data/Gemfile
CHANGED
|
@@ -4,10 +4,14 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
gem "debug", "~> 1.5"
|
|
7
|
+
gem "debug", "~> 1.5", require: false
|
|
8
8
|
gem "minitest", "~> 5.15"
|
|
9
9
|
gem "minitest-reporters", "~> 1.5"
|
|
10
10
|
gem "rake", "~> 13.0"
|
|
11
|
-
gem "rubocop-shopify", "~> 2.
|
|
12
|
-
gem "rubocop-minitest", "~> 0.
|
|
13
|
-
gem "rubocop-rake", "~> 0.6.0"
|
|
11
|
+
gem "rubocop-shopify", "~> 2.7", require: false
|
|
12
|
+
gem "rubocop-minitest", "~> 0.20.1", require: false
|
|
13
|
+
gem "rubocop-rake", "~> 0.6.0", require: false
|
|
14
|
+
gem "rubocop-sorbet", "~> 0.6", require: false
|
|
15
|
+
gem "sorbet-static-and-runtime"
|
|
16
|
+
gem "tapioca", "~> 0.8", require: false
|
|
17
|
+
gem "yard", "~> 0.9", require: false
|
data/Gemfile.lock
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
ruby-lsp (0.0
|
|
4
|
+
ruby-lsp (0.1.0)
|
|
5
5
|
language_server-protocol
|
|
6
6
|
rubocop (>= 1.0)
|
|
7
|
-
|
|
7
|
+
sorbet-runtime
|
|
8
|
+
syntax_tree (>= 2.4)
|
|
8
9
|
|
|
9
10
|
GEM
|
|
10
11
|
remote: https://rubygems.org/
|
|
@@ -12,13 +13,16 @@ GEM
|
|
|
12
13
|
ansi (1.5.0)
|
|
13
14
|
ast (2.4.2)
|
|
14
15
|
builder (3.2.4)
|
|
16
|
+
coderay (1.1.3)
|
|
15
17
|
debug (1.5.0)
|
|
16
18
|
irb (>= 1.3.6)
|
|
17
19
|
reline (>= 0.2.7)
|
|
20
|
+
diff-lcs (1.5.0)
|
|
18
21
|
io-console (0.5.11)
|
|
19
22
|
irb (1.4.1)
|
|
20
23
|
reline (>= 0.3.0)
|
|
21
24
|
language_server-protocol (3.16.0.3)
|
|
25
|
+
method_source (1.0.0)
|
|
22
26
|
minitest (5.15.0)
|
|
23
27
|
minitest-reporters (1.5.0)
|
|
24
28
|
ansi
|
|
@@ -28,32 +32,75 @@ GEM
|
|
|
28
32
|
parallel (1.22.1)
|
|
29
33
|
parser (3.1.2.0)
|
|
30
34
|
ast (~> 2.4.1)
|
|
35
|
+
prettier_print (0.1.0)
|
|
36
|
+
pry (0.14.1)
|
|
37
|
+
coderay (~> 1.1)
|
|
38
|
+
method_source (~> 1.0)
|
|
31
39
|
rainbow (3.1.1)
|
|
32
40
|
rake (13.0.6)
|
|
33
|
-
|
|
41
|
+
rbi (0.0.14)
|
|
42
|
+
ast
|
|
43
|
+
parser (>= 2.6.4.0)
|
|
44
|
+
sorbet-runtime (>= 0.5.9204)
|
|
45
|
+
unparser
|
|
46
|
+
regexp_parser (2.5.0)
|
|
34
47
|
reline (0.3.1)
|
|
35
48
|
io-console (~> 0.5)
|
|
36
49
|
rexml (3.2.5)
|
|
37
|
-
rubocop (1.
|
|
50
|
+
rubocop (1.30.1)
|
|
38
51
|
parallel (~> 1.10)
|
|
39
52
|
parser (>= 3.1.0.0)
|
|
40
53
|
rainbow (>= 2.2.2, < 4.0)
|
|
41
54
|
regexp_parser (>= 1.8, < 3.0)
|
|
42
|
-
rexml
|
|
43
|
-
rubocop-ast (>= 1.
|
|
55
|
+
rexml (>= 3.2.5, < 4.0)
|
|
56
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
|
44
57
|
ruby-progressbar (~> 1.7)
|
|
45
58
|
unicode-display_width (>= 1.4.0, < 3.0)
|
|
46
|
-
rubocop-ast (1.
|
|
59
|
+
rubocop-ast (1.18.0)
|
|
47
60
|
parser (>= 3.1.1.0)
|
|
48
|
-
rubocop-minitest (0.
|
|
61
|
+
rubocop-minitest (0.20.1)
|
|
49
62
|
rubocop (>= 0.90, < 2.0)
|
|
50
63
|
rubocop-rake (0.6.0)
|
|
51
64
|
rubocop (~> 1.0)
|
|
52
|
-
rubocop-shopify (2.
|
|
53
|
-
rubocop (~> 1.
|
|
65
|
+
rubocop-shopify (2.7.0)
|
|
66
|
+
rubocop (~> 1.30)
|
|
67
|
+
rubocop-sorbet (0.6.8)
|
|
68
|
+
rubocop (>= 0.90.0)
|
|
54
69
|
ruby-progressbar (1.11.0)
|
|
55
|
-
|
|
70
|
+
sorbet (0.5.10092)
|
|
71
|
+
sorbet-static (= 0.5.10092)
|
|
72
|
+
sorbet-runtime (0.5.10092)
|
|
73
|
+
sorbet-static (0.5.10092-universal-darwin-21)
|
|
74
|
+
sorbet-static (0.5.10092-x86_64-linux)
|
|
75
|
+
sorbet-static-and-runtime (0.5.10092)
|
|
76
|
+
sorbet (= 0.5.10092)
|
|
77
|
+
sorbet-runtime (= 0.5.10092)
|
|
78
|
+
spoom (1.1.11)
|
|
79
|
+
sorbet (>= 0.5.9204)
|
|
80
|
+
sorbet-runtime (>= 0.5.9204)
|
|
81
|
+
thor (>= 0.19.2)
|
|
82
|
+
syntax_tree (2.7.1)
|
|
83
|
+
prettier_print
|
|
84
|
+
tapioca (0.8.1)
|
|
85
|
+
bundler (>= 1.17.3)
|
|
86
|
+
parallel (>= 1.21.0)
|
|
87
|
+
pry (>= 0.12.2)
|
|
88
|
+
rbi (~> 0.0.0, >= 0.0.14)
|
|
89
|
+
sorbet-static-and-runtime (>= 0.5.9204)
|
|
90
|
+
spoom (~> 1.1.0, >= 1.1.11)
|
|
91
|
+
thor (>= 1.2.0)
|
|
92
|
+
yard-sorbet
|
|
93
|
+
thor (1.2.1)
|
|
56
94
|
unicode-display_width (2.1.0)
|
|
95
|
+
unparser (0.6.5)
|
|
96
|
+
diff-lcs (~> 1.3)
|
|
97
|
+
parser (>= 3.1.0)
|
|
98
|
+
webrick (1.7.0)
|
|
99
|
+
yard (0.9.28)
|
|
100
|
+
webrick (~> 1.7.0)
|
|
101
|
+
yard-sorbet (0.6.1)
|
|
102
|
+
sorbet-runtime (>= 0.5)
|
|
103
|
+
yard (>= 0.9)
|
|
57
104
|
|
|
58
105
|
PLATFORMS
|
|
59
106
|
arm64-darwin-21
|
|
@@ -64,10 +111,14 @@ DEPENDENCIES
|
|
|
64
111
|
minitest (~> 5.15)
|
|
65
112
|
minitest-reporters (~> 1.5)
|
|
66
113
|
rake (~> 13.0)
|
|
67
|
-
rubocop-minitest (~> 0.
|
|
114
|
+
rubocop-minitest (~> 0.20.1)
|
|
68
115
|
rubocop-rake (~> 0.6.0)
|
|
69
|
-
rubocop-shopify (~> 2.
|
|
116
|
+
rubocop-shopify (~> 2.7)
|
|
117
|
+
rubocop-sorbet (~> 0.6)
|
|
70
118
|
ruby-lsp!
|
|
119
|
+
sorbet-static-and-runtime
|
|
120
|
+
tapioca (~> 0.8)
|
|
121
|
+
yard (~> 0.9)
|
|
71
122
|
|
|
72
123
|
BUNDLED WITH
|
|
73
124
|
2.3.7
|
data/README.md
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
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.
|
|
19
|
+
|
|
20
|
+
See the [documentation](https://shopify.github.io/ruby-lsp) for supported features.
|
|
2
21
|
|
|
3
22
|
## Contributing
|
|
4
23
|
|
|
@@ -8,6 +27,44 @@ are expected to adhere to the
|
|
|
8
27
|
[Contributor Covenant](https://github.com/Shopify/ruby-lsp/blob/main/CODE_OF_CONDUCT.md)
|
|
9
28
|
code of conduct.
|
|
10
29
|
|
|
30
|
+
### Expectation testing
|
|
31
|
+
|
|
32
|
+
To simplify the way we run tests over different pieces of Ruby code, we use a custom expectations test framework against a set of Ruby fixtures.
|
|
33
|
+
|
|
34
|
+
To add a new fixture to the expectations test suite:
|
|
35
|
+
|
|
36
|
+
1. Add a new fixture `my_fixture.rb` file under `test/fixtures`
|
|
37
|
+
2. (optional) Add new expectations under `test/expectations/$HANDLER` for the request handlers you're concerned by
|
|
38
|
+
3. Profit by running `bin/test test/requests/$HANDLER_expectations_test my_fixture`
|
|
39
|
+
* Handlers for which you added expectations will be checked with `assert_expectations`
|
|
40
|
+
* Handlers without expectations will be ran against your new test to check that nothing breaks
|
|
41
|
+
|
|
42
|
+
To add a new expectations test runner for a new request handler:
|
|
43
|
+
|
|
44
|
+
1. Add a new file under `test/requests/$HANDLER_expectations_test.rb` that subclasses `ExpectationsTestRunner` and calls `expectations_tests $HANDLER, "$EXPECTATIONS_DIR"` where: `$HANDLER` is the fully qualified name or your handler class and `$EXPECTATIONS_DIR` is the directory name where you want to store the expectation files.
|
|
45
|
+
|
|
46
|
+
```rb
|
|
47
|
+
# frozen_string_literal: true
|
|
48
|
+
|
|
49
|
+
require "test_helper"
|
|
50
|
+
require "expectations/expectations_test_runner"
|
|
51
|
+
|
|
52
|
+
class $HANDLERExpectationsTest < ExpectationsTestRunner
|
|
53
|
+
expectations_tests RubyLsp::Requests::$HANDLER, "$EXPECTATIONS_DIR"
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
2. (optional) Override the `run_expectations` and `assert_expectations` methods if needed. See the different request handler expectations runners under `test/requests/*_expectations_test.rb` for examples.
|
|
58
|
+
|
|
59
|
+
4. (optional) Add new fixtures for your handler under `test/fixtures`
|
|
60
|
+
|
|
61
|
+
5. (optional) Add new expectations under `test/expectations/$HANDLER`
|
|
62
|
+
* No need to write the expectations by hand, just run the test with an empty expectation file and copy from the output.
|
|
63
|
+
|
|
64
|
+
7. Profit by running, `bin/test test/expectations_test $HANDLER`
|
|
65
|
+
* Tests with expectations will be checked with `assert_expectations`
|
|
66
|
+
* Tests without expectations will be ran against your new $HANDLER to check that nothing breaks
|
|
67
|
+
|
|
11
68
|
## Debugging
|
|
12
69
|
|
|
13
70
|
### Tracing LSP requests and responses
|
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.1.0
|
data/bin/tapioca
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'tapioca' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("tapioca", "tapioca")
|
data/dev.yml
CHANGED
data/exe/ruby-lsp
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require "
|
|
5
|
-
require "ruby_lsp/cli"
|
|
4
|
+
require "sorbet-runtime"
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
begin
|
|
7
|
+
T::Configuration.default_checked_level = :never
|
|
8
|
+
# Suppresses call validation errors
|
|
9
|
+
T::Configuration.call_validation_error_handler = ->(*) {}
|
|
10
|
+
# Suppresses errors caused by T.cast, T.let, T.must, etc.
|
|
11
|
+
T::Configuration.inline_type_error_handler = ->(*) {}
|
|
12
|
+
# Suppresses errors caused by incorrect parameter ordering
|
|
13
|
+
T::Configuration.sig_validation_error_handler = ->(*) {}
|
|
14
|
+
rescue
|
|
15
|
+
# Need this rescue so that if another gem has
|
|
16
|
+
# already set the checked level by the time we
|
|
17
|
+
# get to it, we don't fail outright.
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require_relative "../lib/ruby_lsp/internal"
|
|
22
|
+
|
|
23
|
+
RubyLsp::Cli.start
|
data/lib/ruby-lsp.rb
CHANGED
data/lib/ruby_lsp/cli.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require "language_server-protocol"
|
|
@@ -6,22 +7,26 @@ require_relative "handler"
|
|
|
6
7
|
|
|
7
8
|
module RubyLsp
|
|
8
9
|
module Cli
|
|
9
|
-
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig { void }
|
|
13
|
+
def self.start
|
|
10
14
|
handler = RubyLsp::Handler.new
|
|
11
15
|
|
|
12
16
|
handler.config do
|
|
13
17
|
on("initialize") do |request|
|
|
14
18
|
store.clear
|
|
15
|
-
|
|
19
|
+
initialization_options = request.dig(:params, :initializationOptions)
|
|
20
|
+
|
|
21
|
+
respond_with_capabilities(initialization_options.fetch(:enabledFeatures, []))
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
on("textDocument/didChange") do |request|
|
|
19
25
|
uri = request.dig(:params, :textDocument, :uri)
|
|
20
|
-
|
|
21
|
-
store.set(uri, text)
|
|
26
|
+
store.push_edits(uri, request.dig(:params, :contentChanges))
|
|
22
27
|
|
|
23
28
|
send_diagnostics(uri)
|
|
24
|
-
|
|
29
|
+
RubyLsp::Handler::VOID
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
on("textDocument/didOpen") do |request|
|
|
@@ -30,14 +35,14 @@ module RubyLsp
|
|
|
30
35
|
store.set(uri, text)
|
|
31
36
|
|
|
32
37
|
send_diagnostics(uri)
|
|
33
|
-
|
|
38
|
+
RubyLsp::Handler::VOID
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
on("textDocument/didClose") do |request|
|
|
37
42
|
uri = request.dig(:params, :textDocument, :uri)
|
|
38
43
|
store.delete(uri)
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
RubyLsp::Handler::VOID
|
|
41
46
|
end
|
|
42
47
|
|
|
43
48
|
on("textDocument/documentSymbol") do |request|
|
|
@@ -48,6 +53,13 @@ module RubyLsp
|
|
|
48
53
|
respond_with_folding_ranges(request.dig(:params, :textDocument, :uri))
|
|
49
54
|
end
|
|
50
55
|
|
|
56
|
+
on("textDocument/selectionRange") do |request|
|
|
57
|
+
respond_with_selection_ranges(
|
|
58
|
+
request.dig(:params, :textDocument, :uri),
|
|
59
|
+
request.dig(:params, :positions),
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
51
63
|
on("textDocument/semanticTokens/full") do |request|
|
|
52
64
|
respond_with_semantic_highlighting(request.dig(:params, :textDocument, :uri))
|
|
53
65
|
end
|
|
@@ -56,6 +68,10 @@ module RubyLsp
|
|
|
56
68
|
respond_with_formatting(request.dig(:params, :textDocument, :uri))
|
|
57
69
|
end
|
|
58
70
|
|
|
71
|
+
on("textDocument/documentHighlight") do |request|
|
|
72
|
+
respond_with_document_highlight(request.dig(:params, :textDocument, :uri), request.dig(:params, :position))
|
|
73
|
+
end
|
|
74
|
+
|
|
59
75
|
on("textDocument/codeAction") do |request|
|
|
60
76
|
range = request.dig(:params, :range)
|
|
61
77
|
start_line = range.dig(:start, :line)
|
data/lib/ruby_lsp/document.rb
CHANGED
|
@@ -1,27 +1,119 @@
|
|
|
1
|
+
# typed: strict
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module RubyLsp
|
|
4
5
|
class Document
|
|
5
|
-
|
|
6
|
+
extend T::Sig
|
|
6
7
|
|
|
8
|
+
PositionShape = T.type_alias { { line: Integer, character: Integer } }
|
|
9
|
+
RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
|
|
10
|
+
EditShape = T.type_alias { { range: RangeShape, text: String } }
|
|
11
|
+
|
|
12
|
+
sig { returns(SyntaxTree::Node) }
|
|
13
|
+
attr_reader :tree
|
|
14
|
+
|
|
15
|
+
sig { returns(String) }
|
|
16
|
+
attr_reader :source
|
|
17
|
+
|
|
18
|
+
sig { returns(T::Array[EditShape]) }
|
|
19
|
+
attr_reader :syntax_error_edits
|
|
20
|
+
|
|
21
|
+
sig { params(source: String).void }
|
|
7
22
|
def initialize(source)
|
|
23
|
+
@tree = T.let(SyntaxTree.parse(source), SyntaxTree::Node)
|
|
24
|
+
@cache = T.let({}, T::Hash[Symbol, T.untyped])
|
|
25
|
+
@syntax_error_edits = T.let([], T::Array[EditShape])
|
|
8
26
|
@source = source
|
|
9
|
-
@
|
|
10
|
-
@tree = @parser.parse
|
|
11
|
-
@cache = {}
|
|
27
|
+
@parsable_source = T.let(source.dup, String)
|
|
12
28
|
end
|
|
13
29
|
|
|
30
|
+
sig { params(other: Document).returns(T::Boolean) }
|
|
14
31
|
def ==(other)
|
|
15
32
|
@source == other.source
|
|
16
33
|
end
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
sig do
|
|
36
|
+
type_parameters(:T)
|
|
37
|
+
.params(
|
|
38
|
+
request_name: Symbol,
|
|
39
|
+
block: T.proc.params(document: Document).returns(T.type_parameter(:T))
|
|
40
|
+
).returns(T.type_parameter(:T))
|
|
41
|
+
end
|
|
42
|
+
def cache_fetch(request_name, &block)
|
|
19
43
|
cached = @cache[request_name]
|
|
20
44
|
return cached if cached
|
|
21
45
|
|
|
22
|
-
result =
|
|
46
|
+
result = block.call(self)
|
|
23
47
|
@cache[request_name] = result
|
|
24
48
|
result
|
|
25
49
|
end
|
|
50
|
+
|
|
51
|
+
sig { params(edits: T::Array[EditShape]).void }
|
|
52
|
+
def push_edits(edits)
|
|
53
|
+
# Apply the edits on the real source
|
|
54
|
+
edits.each { |edit| apply_edit(@source, edit[:range], edit[:text]) }
|
|
55
|
+
|
|
56
|
+
@cache.clear
|
|
57
|
+
@tree = SyntaxTree.parse(@source)
|
|
58
|
+
@syntax_error_edits.clear
|
|
59
|
+
@parsable_source = @source.dup
|
|
60
|
+
nil
|
|
61
|
+
rescue SyntaxTree::Parser::ParseError
|
|
62
|
+
update_parsable_source(edits)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
sig { returns(T::Boolean) }
|
|
66
|
+
def syntax_errors?
|
|
67
|
+
@syntax_error_edits.any?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
sig { params(edits: T::Array[EditShape]).void }
|
|
73
|
+
def update_parsable_source(edits)
|
|
74
|
+
# If the new edits caused a syntax error, make all edits blank spaces and line breaks to adjust the line and
|
|
75
|
+
# column numbers. This is attempt to make the document parsable while partial edits are being applied
|
|
76
|
+
edits.each do |edit|
|
|
77
|
+
@syntax_error_edits << edit
|
|
78
|
+
next if edit[:text].empty? # skip deletions, since they may have caused the syntax error
|
|
79
|
+
|
|
80
|
+
apply_edit(@parsable_source, edit[:range], edit[:text].gsub(/[^\r\n]/, " "))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
@tree = SyntaxTree.parse(@parsable_source)
|
|
84
|
+
rescue SyntaxTree::Parser::ParseError
|
|
85
|
+
# If we can't parse the source even after emptying the edits, then just fallback to the previous source
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
sig { params(source: String, range: RangeShape, text: String).void }
|
|
89
|
+
def apply_edit(source, range, text)
|
|
90
|
+
scanner = Scanner.new(source)
|
|
91
|
+
start_position = scanner.find_position(range[:start])
|
|
92
|
+
end_position = scanner.find_position(range[:end])
|
|
93
|
+
|
|
94
|
+
source[start_position...end_position] = text
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class Scanner
|
|
98
|
+
extend T::Sig
|
|
99
|
+
|
|
100
|
+
sig { params(source: String).void }
|
|
101
|
+
def initialize(source)
|
|
102
|
+
@current_line = T.let(0, Integer)
|
|
103
|
+
@pos = T.let(0, Integer)
|
|
104
|
+
@source = source
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
sig { params(position: PositionShape).returns(Integer) }
|
|
108
|
+
def find_position(position)
|
|
109
|
+
until @current_line == position[:line]
|
|
110
|
+
@pos += 1 until /\R/.match?(@source[@pos])
|
|
111
|
+
@pos += 1
|
|
112
|
+
@current_line += 1
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
@pos + position[:character]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
26
118
|
end
|
|
27
119
|
end
|