ruby-lsp 0.2.0 → 0.2.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/CHANGELOG.md +27 -0
- data/VERSION +1 -1
- data/exe/ruby-lsp +1 -3
- data/lib/ruby-lsp.rb +2 -2
- data/lib/ruby_lsp/document.rb +10 -3
- data/lib/ruby_lsp/handler.rb +17 -138
- data/lib/ruby_lsp/internal.rb +3 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +25 -42
- data/lib/ruby_lsp/requests/document_link.rb +119 -0
- data/lib/ruby_lsp/requests/folding_ranges.rb +5 -1
- data/lib/ruby_lsp/requests/formatting.rb +1 -0
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +17 -7
- data/lib/ruby_lsp/requests/support/highlight_target.rb +88 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +9 -2
- data/lib/ruby_lsp/requests/support/source_uri.rb +82 -0
- data/lib/ruby_lsp/requests.rb +3 -0
- data/lib/ruby_lsp/server.rb +193 -0
- data/lib/ruby_lsp/store.rb +12 -5
- metadata +6 -70
- data/.github/dependabot.yml +0 -11
- data/.github/probots.yml +0 -2
- data/.github/pull_request_template.md +0 -15
- data/.github/workflows/ci.yml +0 -31
- data/.github/workflows/publish_docs.yml +0 -32
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -39
- data/.vscode/extensions.json +0 -5
- data/.vscode/settings.json +0 -5
- data/.vscode/tasks.json +0 -25
- data/CODE_OF_CONDUCT.md +0 -78
- data/Gemfile +0 -18
- data/Gemfile.lock +0 -126
- data/Rakefile +0 -28
- data/bin/console +0 -19
- data/bin/rubocop +0 -29
- data/bin/tapioca +0 -29
- data/bin/test +0 -9
- data/dev.yml +0 -20
- data/lib/ruby_lsp/cli.rb +0 -89
- data/rakelib/check_docs.rake +0 -81
- data/ruby-lsp.gemspec +0 -27
- data/service.yml +0 -2
- data/sorbet/config +0 -4
- data/sorbet/rbi/.rubocop.yml +0 -8
- data/sorbet/rbi/gems/ansi@1.5.0.rbi +0 -338
- data/sorbet/rbi/gems/ast@2.4.2.rbi +0 -522
- data/sorbet/rbi/gems/builder@3.2.4.rbi +0 -418
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +0 -8
- data/sorbet/rbi/gems/debug@1.5.0.rbi +0 -1273
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +0 -867
- data/sorbet/rbi/gems/io-console@0.5.11.rbi +0 -8
- data/sorbet/rbi/gems/irb@1.4.1.rbi +0 -376
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +0 -7325
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +0 -8
- data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +0 -612
- data/sorbet/rbi/gems/minitest@5.15.0.rbi +0 -994
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +0 -163
- data/sorbet/rbi/gems/parser@3.1.2.0.rbi +0 -3968
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +0 -734
- data/sorbet/rbi/gems/pry@0.14.1.rbi +0 -8
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +0 -227
- data/sorbet/rbi/gems/rake@13.0.6.rbi +0 -1853
- data/sorbet/rbi/gems/rbi@0.0.14.rbi +0 -2337
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +0 -1854
- data/sorbet/rbi/gems/reline@0.3.1.rbi +0 -1274
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +0 -3852
- data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +0 -4180
- data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +0 -1369
- data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +0 -246
- data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +0 -8
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +0 -652
- data/sorbet/rbi/gems/rubocop@1.30.0.rbi +0 -36729
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +0 -732
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +0 -1600
- data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +0 -6777
- data/sorbet/rbi/gems/tapioca@0.8.1.rbi +0 -1972
- data/sorbet/rbi/gems/thor@1.2.1.rbi +0 -2921
- data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +0 -27
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +0 -2789
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +0 -1779
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +0 -289
- data/sorbet/rbi/gems/yard@0.9.27.rbi +0 -13048
- data/sorbet/rbi/shims/fiddle.rbi +0 -4
- data/sorbet/rbi/shims/hash.rbi +0 -6
- data/sorbet/rbi/shims/rdoc.rbi +0 -4
- data/sorbet/tapioca/config.yml +0 -13
- data/sorbet/tapioca/require.rb +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 96c21e90970f4cbf198609a42471cf554ce86a3b6af9296ded95994ad373317a
|
|
4
|
+
data.tar.gz: a28aa2c13ba4d79f53082160b0eca7e8461efad831b6c6cca2a6a4d34765a6c0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ab1bf48ae4943a710f34ef0aace64c1d8991271bdca71482a2367c3a103299c52ebaef203fa32945236d689dda67d2bb1b5cc5ca9ffa698f5dd057dd62e242f
|
|
7
|
+
data.tar.gz: 8b9453d71b65634e1ea275f3a2552548f530dc144acd18cdcbcee493f153b824a13bc60bbbe5ac937a993d9e9c56ce8cb70773d354b5cea7acda3f4f44722ffd
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,33 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.3]
|
|
10
|
+
|
|
11
|
+
- Resolve generic source URIs for jump to gem source (https://github.com/Shopify/ruby-lsp/pull/237)
|
|
12
|
+
|
|
13
|
+
## [0.2.2]
|
|
14
|
+
|
|
15
|
+
- Support document links (https://github.com/Shopify/ruby-lsp/pull/195)
|
|
16
|
+
- Avoid returning on request blocks (https://github.com/Shopify/ruby-lsp/pull/232)
|
|
17
|
+
- Better specify gemspec files (https://github.com/Shopify/ruby-lsp/pull/233)
|
|
18
|
+
- Include Kernel instance methods as special methods for semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/231)
|
|
19
|
+
- Fix call processing when message is a :call symbol literal (https://github.com/Shopify/ruby-lsp/pull/236)
|
|
20
|
+
- Alert users about non auto-correctable diagnostics (https://github.com/Shopify/ruby-lsp/pull/230)
|
|
21
|
+
- Let clients pull diagnostics instead of pushing on edits (https://github.com/Shopify/ruby-lsp/pull/242)
|
|
22
|
+
|
|
23
|
+
## [0.2.1]
|
|
24
|
+
|
|
25
|
+
- Implement the exit lifecycle request (https://github.com/Shopify/ruby-lsp/pull/198)
|
|
26
|
+
- Remove the Sorbet runtime from the gem's default load path (https://github.com/Shopify/ruby-lsp/pull/214)
|
|
27
|
+
- Return nil if the document is already formatted (https://github.com/Shopify/ruby-lsp/pull/216)
|
|
28
|
+
- Handle nameless keyword rest parameters in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/222)
|
|
29
|
+
- Display a warning on invalid RuboCop configuration (https://github.com/Shopify/ruby-lsp/pull/226)
|
|
30
|
+
- Centralize request handling logic in server.rb (https://github.com/Shopify/ruby-lsp/pull/221)
|
|
31
|
+
- Fix folding ranges for chained invocations involving an FCall (https://github.com/Shopify/ruby-lsp/pull/223)
|
|
32
|
+
- Fix handling of argument fowarding in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/228)
|
|
33
|
+
- Recover from initial syntax errors when opening documents (https://github.com/Shopify/ruby-lsp/pull/224)
|
|
34
|
+
- Highlight occurrences and definitions in document highlight (https://github.com/Shopify/ruby-lsp/pull/187)
|
|
35
|
+
|
|
9
36
|
## [0.2.0]
|
|
10
37
|
|
|
11
38
|
- Add semantic token for keyword and keyword rest params (https://github.com/Shopify/ruby-lsp/pull/142)
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.3
|
data/exe/ruby-lsp
CHANGED
data/lib/ruby-lsp.rb
CHANGED
data/lib/ruby_lsp/document.rb
CHANGED
|
@@ -9,7 +9,7 @@ module RubyLsp
|
|
|
9
9
|
RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
|
|
10
10
|
EditShape = T.type_alias { { range: RangeShape, text: String } }
|
|
11
11
|
|
|
12
|
-
sig { returns(SyntaxTree::Node) }
|
|
12
|
+
sig { returns(T.nilable(SyntaxTree::Node)) }
|
|
13
13
|
attr_reader :tree
|
|
14
14
|
|
|
15
15
|
sig { returns(String) }
|
|
@@ -20,11 +20,13 @@ module RubyLsp
|
|
|
20
20
|
|
|
21
21
|
sig { params(source: String).void }
|
|
22
22
|
def initialize(source)
|
|
23
|
-
@tree = T.let(SyntaxTree.parse(source), SyntaxTree::Node)
|
|
24
23
|
@cache = T.let({}, T::Hash[Symbol, T.untyped])
|
|
25
24
|
@syntax_error_edits = T.let([], T::Array[EditShape])
|
|
26
|
-
@source = source
|
|
25
|
+
@source = T.let(source, String)
|
|
27
26
|
@parsable_source = T.let(source.dup, String)
|
|
27
|
+
@tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
|
|
28
|
+
rescue SyntaxTree::Parser::ParseError
|
|
29
|
+
# Do not raise if we failed to parse
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
sig { params(other: Document).returns(T::Boolean) }
|
|
@@ -67,6 +69,11 @@ module RubyLsp
|
|
|
67
69
|
@syntax_error_edits.any?
|
|
68
70
|
end
|
|
69
71
|
|
|
72
|
+
sig { returns(T::Boolean) }
|
|
73
|
+
def parsed?
|
|
74
|
+
!@tree.nil?
|
|
75
|
+
end
|
|
76
|
+
|
|
70
77
|
private
|
|
71
78
|
|
|
72
79
|
sig { params(edits: T::Array[EditShape]).void }
|
data/lib/ruby_lsp/handler.rb
CHANGED
|
@@ -6,17 +6,24 @@ require "ruby_lsp/store"
|
|
|
6
6
|
require "benchmark"
|
|
7
7
|
|
|
8
8
|
module RubyLsp
|
|
9
|
+
Interface = LanguageServer::Protocol::Interface
|
|
10
|
+
Constant = LanguageServer::Protocol::Constant
|
|
11
|
+
Transport = LanguageServer::Protocol::Transport
|
|
12
|
+
|
|
9
13
|
class Handler
|
|
10
14
|
extend T::Sig
|
|
11
15
|
VOID = T.let(Object.new.freeze, Object)
|
|
12
16
|
|
|
17
|
+
sig { params(blk: T.proc.bind(Handler).params(arg0: T.untyped).void).void }
|
|
18
|
+
def self.start(&blk)
|
|
19
|
+
handler = new
|
|
20
|
+
handler.instance_exec(&blk)
|
|
21
|
+
handler.start
|
|
22
|
+
end
|
|
23
|
+
|
|
13
24
|
sig { returns(Store) }
|
|
14
25
|
attr_reader :store
|
|
15
26
|
|
|
16
|
-
Interface = LanguageServer::Protocol::Interface
|
|
17
|
-
Constant = LanguageServer::Protocol::Constant
|
|
18
|
-
Transport = LanguageServer::Protocol::Transport
|
|
19
|
-
|
|
20
27
|
sig { void }
|
|
21
28
|
def initialize
|
|
22
29
|
@writer = T.let(Transport::Stdio::Writer.new, Transport::Stdio::Writer)
|
|
@@ -31,11 +38,6 @@ module RubyLsp
|
|
|
31
38
|
@reader.read { |request| handle(request) }
|
|
32
39
|
end
|
|
33
40
|
|
|
34
|
-
sig { params(blk: T.proc.bind(Handler).params(arg0: T.untyped).void).void }
|
|
35
|
-
def config(&blk)
|
|
36
|
-
instance_exec(&blk)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
41
|
private
|
|
40
42
|
|
|
41
43
|
sig do
|
|
@@ -84,118 +86,6 @@ module RubyLsp
|
|
|
84
86
|
store.clear
|
|
85
87
|
end
|
|
86
88
|
|
|
87
|
-
sig { params(enabled_features: T::Array[String]).returns(Interface::InitializeResult) }
|
|
88
|
-
def respond_with_capabilities(enabled_features)
|
|
89
|
-
document_symbol_provider = if enabled_features.include?("documentSymbols")
|
|
90
|
-
Interface::DocumentSymbolClientCapabilities.new(
|
|
91
|
-
hierarchical_document_symbol_support: true,
|
|
92
|
-
symbol_kind: {
|
|
93
|
-
value_set: Requests::DocumentSymbol::SYMBOL_KIND.values,
|
|
94
|
-
}
|
|
95
|
-
)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
folding_ranges_provider = if enabled_features.include?("foldingRanges")
|
|
99
|
-
Interface::FoldingRangeClientCapabilities.new(line_folding_only: true)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
semantic_tokens_provider = if enabled_features.include?("semanticHighlighting")
|
|
103
|
-
Interface::SemanticTokensRegistrationOptions.new(
|
|
104
|
-
document_selector: { scheme: "file", language: "ruby" },
|
|
105
|
-
legend: Interface::SemanticTokensLegend.new(
|
|
106
|
-
token_types: Requests::SemanticHighlighting::TOKEN_TYPES.keys,
|
|
107
|
-
token_modifiers: Requests::SemanticHighlighting::TOKEN_MODIFIERS.keys
|
|
108
|
-
),
|
|
109
|
-
range: false,
|
|
110
|
-
full: {
|
|
111
|
-
delta: true,
|
|
112
|
-
}
|
|
113
|
-
)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
Interface::InitializeResult.new(
|
|
117
|
-
capabilities: Interface::ServerCapabilities.new(
|
|
118
|
-
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
|
119
|
-
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
|
120
|
-
open_close: true,
|
|
121
|
-
),
|
|
122
|
-
selection_range_provider: enabled_features.include?("selectionRanges"),
|
|
123
|
-
document_symbol_provider: document_symbol_provider,
|
|
124
|
-
folding_range_provider: folding_ranges_provider,
|
|
125
|
-
semantic_tokens_provider: semantic_tokens_provider,
|
|
126
|
-
document_formatting_provider: enabled_features.include?("formatting"),
|
|
127
|
-
document_highlight_provider: enabled_features.include?("documentHighlights"),
|
|
128
|
-
code_action_provider: enabled_features.include?("codeActions")
|
|
129
|
-
)
|
|
130
|
-
)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
|
|
134
|
-
def respond_with_document_symbol(uri)
|
|
135
|
-
store.cache_fetch(uri, :document_symbol) do |document|
|
|
136
|
-
RubyLsp::Requests::DocumentSymbol.new(document).run
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
sig { params(uri: String).returns(T::Array[LanguageServer::Protocol::Interface::FoldingRange]) }
|
|
141
|
-
def respond_with_folding_ranges(uri)
|
|
142
|
-
store.cache_fetch(uri, :folding_ranges) do |document|
|
|
143
|
-
Requests::FoldingRanges.new(document).run
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
sig do
|
|
148
|
-
params(
|
|
149
|
-
uri: String,
|
|
150
|
-
positions: T::Array[Document::PositionShape]
|
|
151
|
-
).returns(T::Array[T.nilable(RubyLsp::Requests::Support::SelectionRange)])
|
|
152
|
-
end
|
|
153
|
-
def respond_with_selection_ranges(uri, positions)
|
|
154
|
-
ranges = store.cache_fetch(uri, :selection_ranges) do |document|
|
|
155
|
-
Requests::SelectionRanges.new(document).run
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
|
|
159
|
-
# every position in the positions array should have an element at the same index in the response
|
|
160
|
-
# array. For positions without a valid selection range, the corresponding element in the response
|
|
161
|
-
# array will be nil.
|
|
162
|
-
positions.map do |position|
|
|
163
|
-
ranges.find do |range|
|
|
164
|
-
range.cover?(position)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
sig { params(uri: String).returns(LanguageServer::Protocol::Interface::SemanticTokens) }
|
|
170
|
-
def respond_with_semantic_highlighting(uri)
|
|
171
|
-
store.cache_fetch(uri, :semantic_highlighting) do |document|
|
|
172
|
-
T.cast(
|
|
173
|
-
Requests::SemanticHighlighting.new(document, encoder: Requests::Support::SemanticTokenEncoder.new).run,
|
|
174
|
-
LanguageServer::Protocol::Interface::SemanticTokens
|
|
175
|
-
)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
sig { params(uri: String).returns(T.nilable(T::Array[LanguageServer::Protocol::Interface::TextEdit])) }
|
|
180
|
-
def respond_with_formatting(uri)
|
|
181
|
-
Requests::Formatting.new(uri, store.get(uri)).run
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
sig { params(uri: String).void }
|
|
185
|
-
def send_diagnostics(uri)
|
|
186
|
-
response = store.cache_fetch(uri, :diagnostics) do |document|
|
|
187
|
-
Requests::Diagnostics.new(uri, document).run
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
@writer.write(
|
|
191
|
-
method: "textDocument/publishDiagnostics",
|
|
192
|
-
params: Interface::PublishDiagnosticsParams.new(
|
|
193
|
-
uri: uri,
|
|
194
|
-
diagnostics: response.map(&:to_lsp_diagnostic)
|
|
195
|
-
)
|
|
196
|
-
)
|
|
197
|
-
end
|
|
198
|
-
|
|
199
89
|
sig { params(uri: String).void }
|
|
200
90
|
def clear_diagnostics(uri)
|
|
201
91
|
@writer.write(
|
|
@@ -204,23 +94,12 @@ module RubyLsp
|
|
|
204
94
|
)
|
|
205
95
|
end
|
|
206
96
|
|
|
207
|
-
sig
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
sig do
|
|
217
|
-
params(
|
|
218
|
-
uri: String,
|
|
219
|
-
position: Document::PositionShape
|
|
220
|
-
).returns(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
|
|
221
|
-
end
|
|
222
|
-
def respond_with_document_highlight(uri, position)
|
|
223
|
-
Requests::DocumentHighlight.new(store.get(uri), position).run
|
|
97
|
+
sig { params(type: Integer, message: String).void }
|
|
98
|
+
def show_message(type, message)
|
|
99
|
+
@writer.write(
|
|
100
|
+
method: "window/showMessage",
|
|
101
|
+
params: Interface::ShowMessageParams.new(type: type, message: message)
|
|
102
|
+
)
|
|
224
103
|
end
|
|
225
104
|
|
|
226
105
|
sig do
|
data/lib/ruby_lsp/internal.rb
CHANGED
|
@@ -25,21 +25,11 @@ module RubyLsp
|
|
|
25
25
|
class DocumentHighlight < BaseRequest
|
|
26
26
|
extend T::Sig
|
|
27
27
|
|
|
28
|
-
VarNodes = T.type_alias do
|
|
29
|
-
T.any(
|
|
30
|
-
SyntaxTree::GVar,
|
|
31
|
-
SyntaxTree::Ident,
|
|
32
|
-
SyntaxTree::IVar,
|
|
33
|
-
SyntaxTree::Const,
|
|
34
|
-
SyntaxTree::CVar
|
|
35
|
-
)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
28
|
sig { params(document: Document, position: Document::PositionShape).void }
|
|
39
29
|
def initialize(document, position)
|
|
40
30
|
@highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
|
|
41
31
|
position = Document::Scanner.new(document.source).find_position(position)
|
|
42
|
-
@target = T.let(find(document.tree, position), T.nilable(
|
|
32
|
+
@target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
|
|
43
33
|
|
|
44
34
|
super(document)
|
|
45
35
|
end
|
|
@@ -53,33 +43,24 @@ module RubyLsp
|
|
|
53
43
|
@highlights
|
|
54
44
|
end
|
|
55
45
|
|
|
56
|
-
sig { params(node: SyntaxTree::
|
|
57
|
-
def
|
|
58
|
-
if
|
|
59
|
-
add_highlight(
|
|
60
|
-
node.value,
|
|
61
|
-
LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
|
|
62
|
-
)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
super
|
|
66
|
-
end
|
|
46
|
+
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
|
47
|
+
def visit(node)
|
|
48
|
+
return if node.nil?
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if matches_target?(node.value)
|
|
71
|
-
add_highlight(
|
|
72
|
-
node.value,
|
|
73
|
-
LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
|
|
74
|
-
)
|
|
75
|
-
end
|
|
50
|
+
match = T.must(@target).highlight_type(node)
|
|
51
|
+
add_highlight(match) if match
|
|
76
52
|
|
|
77
53
|
super
|
|
78
54
|
end
|
|
79
55
|
|
|
80
56
|
private
|
|
81
57
|
|
|
82
|
-
sig
|
|
58
|
+
sig do
|
|
59
|
+
params(
|
|
60
|
+
node: SyntaxTree::Node,
|
|
61
|
+
position: Integer,
|
|
62
|
+
).returns(T.nilable(Support::HighlightTarget))
|
|
63
|
+
end
|
|
83
64
|
def find(node, position)
|
|
84
65
|
matched =
|
|
85
66
|
node.child_nodes.compact.bsearch do |child|
|
|
@@ -91,22 +72,24 @@ module RubyLsp
|
|
|
91
72
|
end
|
|
92
73
|
|
|
93
74
|
case matched
|
|
94
|
-
when SyntaxTree::GVar,
|
|
95
|
-
|
|
75
|
+
when SyntaxTree::GVar,
|
|
76
|
+
SyntaxTree::IVar,
|
|
77
|
+
SyntaxTree::Const,
|
|
78
|
+
SyntaxTree::CVar,
|
|
79
|
+
SyntaxTree::VarField
|
|
80
|
+
Support::HighlightTarget.new(matched)
|
|
81
|
+
when SyntaxTree::Ident
|
|
82
|
+
relevant_node = node.is_a?(SyntaxTree::Params) ? matched : node
|
|
83
|
+
Support::HighlightTarget.new(relevant_node)
|
|
96
84
|
when SyntaxTree::Node
|
|
97
85
|
find(matched, position)
|
|
98
86
|
end
|
|
99
87
|
end
|
|
100
88
|
|
|
101
|
-
sig { params(
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
sig { params(node: SyntaxTree::Node, kind: Integer).void }
|
|
107
|
-
def add_highlight(node, kind)
|
|
108
|
-
range = range_from_syntax_tree_node(node)
|
|
109
|
-
@highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: kind)
|
|
89
|
+
sig { params(match: Support::HighlightTarget::HighlightMatch).void }
|
|
90
|
+
def add_highlight(match)
|
|
91
|
+
range = range_from_syntax_tree_node(match.node)
|
|
92
|
+
@highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: match.type)
|
|
110
93
|
end
|
|
111
94
|
end
|
|
112
95
|
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "ruby_lsp/requests/support/source_uri"
|
|
5
|
+
|
|
6
|
+
module RubyLsp
|
|
7
|
+
module Requests
|
|
8
|
+
# 
|
|
9
|
+
#
|
|
10
|
+
# The [document link](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentLink)
|
|
11
|
+
# makes `# source://PATH_TO_FILE#line` comments in a Ruby/RBI file clickable if the file exists.
|
|
12
|
+
# When the user clicks the link, it'll open that location.
|
|
13
|
+
#
|
|
14
|
+
# # Example
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# # source://syntax_tree/3.2.1/lib/syntax_tree.rb#51 <- it will be clickable and will take the user to that location
|
|
18
|
+
# def format(source, maxwidth = T.unsafe(nil))
|
|
19
|
+
# end
|
|
20
|
+
# ```
|
|
21
|
+
class DocumentLink < BaseRequest
|
|
22
|
+
extend T::Sig
|
|
23
|
+
|
|
24
|
+
GEM_TO_VERSION_MAP = T.let(
|
|
25
|
+
[*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
|
|
26
|
+
[s.name, s.version.to_s]
|
|
27
|
+
end.to_h.freeze,
|
|
28
|
+
T::Hash[String, String]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
extend T::Sig
|
|
33
|
+
|
|
34
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
35
|
+
def gem_paths
|
|
36
|
+
@gem_paths ||= T.let(begin
|
|
37
|
+
lookup = {}
|
|
38
|
+
|
|
39
|
+
Gem::Specification.stubs.each do |stub|
|
|
40
|
+
spec = stub.to_spec
|
|
41
|
+
lookup[spec.name] = {}
|
|
42
|
+
lookup[spec.name][spec.version.to_s] = {}
|
|
43
|
+
|
|
44
|
+
Dir.glob("**/*.rb", base: "#{spec.full_gem_path}/").each do |path|
|
|
45
|
+
lookup[spec.name][spec.version.to_s][path] = "#{spec.full_gem_path}/#{path}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Gem::Specification.default_stubs.each do |stub|
|
|
50
|
+
spec = stub.to_spec
|
|
51
|
+
lookup[spec.name] = {}
|
|
52
|
+
lookup[spec.name][spec.version.to_s] = {}
|
|
53
|
+
prefix_matchers = [//]
|
|
54
|
+
prefix_matchers.concat(spec.require_paths.map { |rp| Regexp.new("^#{rp}/") })
|
|
55
|
+
prefix_matcher = Regexp.union(prefix_matchers)
|
|
56
|
+
|
|
57
|
+
spec.files.each do |file|
|
|
58
|
+
path = file.sub(prefix_matcher, "")
|
|
59
|
+
lookup[spec.name][spec.version.to_s][path] = "#{RbConfig::CONFIG["rubylibdir"]}/#{path}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
lookup
|
|
64
|
+
end, T.nilable(T::Hash[String, T::Array[String]]))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sig { params(uri: String, document: Document).void }
|
|
69
|
+
def initialize(uri, document)
|
|
70
|
+
super(document)
|
|
71
|
+
|
|
72
|
+
# Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
|
|
73
|
+
# in the URI
|
|
74
|
+
version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(uri)
|
|
75
|
+
@gem_version = T.let(version_match && version_match[0], T.nilable(String))
|
|
76
|
+
@links = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentLink])
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentLink], Object)) }
|
|
80
|
+
def run
|
|
81
|
+
visit(@document.tree)
|
|
82
|
+
@links
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
sig { params(node: SyntaxTree::Comment).void }
|
|
86
|
+
def visit_comment(node)
|
|
87
|
+
match = node.value.match(%r{source://.*#\d+$})
|
|
88
|
+
return unless match
|
|
89
|
+
|
|
90
|
+
uri = T.cast(URI(match[0]), URI::Source)
|
|
91
|
+
gem_version = resolve_version(uri)
|
|
92
|
+
file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
|
|
93
|
+
return if file_path.nil?
|
|
94
|
+
|
|
95
|
+
@links << LanguageServer::Protocol::Interface::DocumentLink.new(
|
|
96
|
+
range: range_from_syntax_tree_node(node),
|
|
97
|
+
target: "file://#{file_path}##{uri.line_number}",
|
|
98
|
+
tooltip: "Jump to #{file_path}##{uri.line_number}"
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
# Try to figure out the gem version for a source:// link. The order of precedence is:
|
|
105
|
+
# 1. The version in the URI
|
|
106
|
+
# 2. The version in the RBI file name
|
|
107
|
+
# 3. The version from the gemspec
|
|
108
|
+
sig { params(uri: URI::Source).returns(T.nilable(String)) }
|
|
109
|
+
def resolve_version(uri)
|
|
110
|
+
version = uri.gem_version
|
|
111
|
+
return version unless version.nil? || version.empty?
|
|
112
|
+
|
|
113
|
+
return @gem_version unless @gem_version.nil? || @gem_version.empty?
|
|
114
|
+
|
|
115
|
+
GEM_TO_VERSION_MAP[uri.gem_name]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -195,7 +195,11 @@ module RubyLsp
|
|
|
195
195
|
receiver = receiver.receiver
|
|
196
196
|
when SyntaxTree::MethodAddBlock
|
|
197
197
|
visit(receiver.block)
|
|
198
|
-
receiver = receiver.call
|
|
198
|
+
receiver = receiver.call
|
|
199
|
+
|
|
200
|
+
if receiver.is_a?(SyntaxTree::Call) || receiver.is_a?(SyntaxTree::CommandCall)
|
|
201
|
+
receiver = receiver.receiver
|
|
202
|
+
end
|
|
199
203
|
else
|
|
200
204
|
break
|
|
201
205
|
end
|
|
@@ -60,10 +60,13 @@ module RubyLsp
|
|
|
60
60
|
default_library: 9,
|
|
61
61
|
}.freeze, T::Hash[Symbol, Integer])
|
|
62
62
|
|
|
63
|
-
SPECIAL_RUBY_METHODS = T.let(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.
|
|
63
|
+
SPECIAL_RUBY_METHODS = T.let([
|
|
64
|
+
Module.instance_methods(false),
|
|
65
|
+
Kernel.instance_methods(false),
|
|
66
|
+
Kernel.methods(false),
|
|
67
|
+
Bundler::Dsl.instance_methods(false),
|
|
68
|
+
Module.private_instance_methods(false),
|
|
69
|
+
].flatten.map(&:to_s), T::Array[String])
|
|
67
70
|
|
|
68
71
|
class SemanticToken < T::Struct
|
|
69
72
|
const :location, SyntaxTree::Location
|
|
@@ -78,7 +81,7 @@ module RubyLsp
|
|
|
78
81
|
|
|
79
82
|
@encoder = encoder
|
|
80
83
|
@tokens = T.let([], T::Array[SemanticToken])
|
|
81
|
-
@tree = T.let(document.tree, SyntaxTree::Node)
|
|
84
|
+
@tree = T.let(T.must(document.tree), SyntaxTree::Node)
|
|
82
85
|
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
|
83
86
|
end
|
|
84
87
|
|
|
@@ -105,7 +108,10 @@ module RubyLsp
|
|
|
105
108
|
sig { params(node: SyntaxTree::Call).void }
|
|
106
109
|
def visit_call(node)
|
|
107
110
|
visit(node.receiver)
|
|
108
|
-
|
|
111
|
+
|
|
112
|
+
message = node.message
|
|
113
|
+
add_token(message.location, :method) if message != :call
|
|
114
|
+
|
|
109
115
|
visit(node.arguments)
|
|
110
116
|
end
|
|
111
117
|
|
|
@@ -179,7 +185,11 @@ module RubyLsp
|
|
|
179
185
|
add_token(location_without_colon(location), :variable)
|
|
180
186
|
end
|
|
181
187
|
|
|
182
|
-
|
|
188
|
+
rest = node.keyword_rest
|
|
189
|
+
return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
|
|
190
|
+
|
|
191
|
+
name = rest.name
|
|
192
|
+
add_token(name.location, :variable) if name
|
|
183
193
|
end
|
|
184
194
|
|
|
185
195
|
sig { params(node: SyntaxTree::VarField).void }
|