ruby-lsp 0.2.0 → 0.2.1
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/.rubocop.yml +1 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +1 -1
- 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 +21 -123
- data/lib/ruby_lsp/internal.rb +3 -1
- data/lib/ruby_lsp/requests/document_highlight.rb +25 -42
- 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 +6 -2
- data/lib/ruby_lsp/requests/support/highlight_target.rb +87 -0
- data/lib/ruby_lsp/requests.rb +1 -0
- data/lib/ruby_lsp/server.rb +160 -0
- data/lib/ruby_lsp/store.rb +12 -5
- metadata +4 -3
- data/lib/ruby_lsp/cli.rb +0 -89
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d067270389ee6b02bb03fb6363e272b6808e7c3d27d77354442801d09a617149
|
|
4
|
+
data.tar.gz: 74e7cd206a790a8c139969f5167ab3d38686f78646c619488153fd8ab139892e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d65637f892541a79fde71e3f523dfc37c887ec4892315ffe58b450718c8fa6e2a342036c394c28f5a94da0a1348948a104609881109c5621488b9d23a581bc1
|
|
7
|
+
data.tar.gz: 1d25dbac214c23c2af2bd991d4c57bdae9dce488ac886cc700d7ab10a7e3b4a0e72ddc2c1d4be459b332e9cdfac69588d42eac11ff78023d365e0d5ef6701f78
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.1]
|
|
10
|
+
|
|
11
|
+
- Implement the exit lifecycle request (https://github.com/Shopify/ruby-lsp/pull/198)
|
|
12
|
+
- Remove the Sorbet runtime from the gem's default load path (https://github.com/Shopify/ruby-lsp/pull/214)
|
|
13
|
+
- Return nil if the document is already formatted (https://github.com/Shopify/ruby-lsp/pull/216)
|
|
14
|
+
- Handle nameless keyword rest parameters in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/222)
|
|
15
|
+
- Display a warning on invalid RuboCop configuration (https://github.com/Shopify/ruby-lsp/pull/226)
|
|
16
|
+
- Centralize request handling logic in server.rb (https://github.com/Shopify/ruby-lsp/pull/221)
|
|
17
|
+
- Fix folding ranges for chained invocations involving an FCall (https://github.com/Shopify/ruby-lsp/pull/223)
|
|
18
|
+
- Fix handling of argument fowarding in semantic highlighting (https://github.com/Shopify/ruby-lsp/pull/228)
|
|
19
|
+
- Recover from initial syntax errors when opening documents (https://github.com/Shopify/ruby-lsp/pull/224)
|
|
20
|
+
- Highlight occurrences and definitions in document highlight (https://github.com/Shopify/ruby-lsp/pull/187)
|
|
21
|
+
|
|
9
22
|
## [0.2.0]
|
|
10
23
|
|
|
11
24
|
- Add semantic token for keyword and keyword rest params (https://github.com/Shopify/ruby-lsp/pull/142)
|
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.1
|
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,109 +86,14 @@ 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
89
|
sig { params(uri: String).void }
|
|
185
90
|
def send_diagnostics(uri)
|
|
186
91
|
response = store.cache_fetch(uri, :diagnostics) do |document|
|
|
187
92
|
Requests::Diagnostics.new(uri, document).run
|
|
188
93
|
end
|
|
189
94
|
|
|
95
|
+
return if response.nil?
|
|
96
|
+
|
|
190
97
|
@writer.write(
|
|
191
98
|
method: "textDocument/publishDiagnostics",
|
|
192
99
|
params: Interface::PublishDiagnosticsParams.new(
|
|
@@ -194,6 +101,8 @@ module RubyLsp
|
|
|
194
101
|
diagnostics: response.map(&:to_lsp_diagnostic)
|
|
195
102
|
)
|
|
196
103
|
)
|
|
104
|
+
rescue RuboCop::ValidationError => e
|
|
105
|
+
show_message(Constant::MessageType::ERROR, "Error in RuboCop configuration file: #{e.message}")
|
|
197
106
|
end
|
|
198
107
|
|
|
199
108
|
sig { params(uri: String).void }
|
|
@@ -204,23 +113,12 @@ module RubyLsp
|
|
|
204
113
|
)
|
|
205
114
|
end
|
|
206
115
|
|
|
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
|
|
116
|
+
sig { params(type: Integer, message: String).void }
|
|
117
|
+
def show_message(type, message)
|
|
118
|
+
@writer.write(
|
|
119
|
+
method: "window/showMessage",
|
|
120
|
+
params: Interface::ShowMessageParams.new(type: type, message: message)
|
|
121
|
+
)
|
|
224
122
|
end
|
|
225
123
|
|
|
226
124
|
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
|
|
@@ -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
|
|
@@ -78,7 +78,7 @@ module RubyLsp
|
|
|
78
78
|
|
|
79
79
|
@encoder = encoder
|
|
80
80
|
@tokens = T.let([], T::Array[SemanticToken])
|
|
81
|
-
@tree = T.let(document.tree, SyntaxTree::Node)
|
|
81
|
+
@tree = T.let(T.must(document.tree), SyntaxTree::Node)
|
|
82
82
|
@special_methods = T.let(nil, T.nilable(T::Array[String]))
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -179,7 +179,11 @@ module RubyLsp
|
|
|
179
179
|
add_token(location_without_colon(location), :variable)
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
rest = node.keyword_rest
|
|
183
|
+
return if rest.nil? || rest.is_a?(SyntaxTree::ArgsForward)
|
|
184
|
+
|
|
185
|
+
name = rest.name
|
|
186
|
+
add_token(name.location, :variable) if name
|
|
183
187
|
end
|
|
184
188
|
|
|
185
189
|
sig { params(node: SyntaxTree::VarField).void }
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module RubyLsp
|
|
5
|
+
module Requests
|
|
6
|
+
module Support
|
|
7
|
+
class HighlightTarget
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
READ = LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
|
|
11
|
+
WRITE = LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
|
|
12
|
+
|
|
13
|
+
class HighlightMatch < T::Struct
|
|
14
|
+
const :type, Integer
|
|
15
|
+
const :node, SyntaxTree::Node
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
sig { params(node: SyntaxTree::Node).void }
|
|
19
|
+
def initialize(node)
|
|
20
|
+
@node = node
|
|
21
|
+
@value = T.let(value(node), T.nilable(String))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
|
|
25
|
+
def highlight_type(other)
|
|
26
|
+
matched_highlight(other) if other.is_a?(SyntaxTree::Params) || (@value && @value == value(other))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Match the target type (where the cursor is positioned) with the `other` type (the node we're currently
|
|
32
|
+
# visiting)
|
|
33
|
+
sig { params(other: SyntaxTree::Node).returns(T.nilable(HighlightMatch)) }
|
|
34
|
+
def matched_highlight(other)
|
|
35
|
+
case @node
|
|
36
|
+
# Method definitions and invocations
|
|
37
|
+
when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command,
|
|
38
|
+
SyntaxTree::CommandCall, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless
|
|
39
|
+
case other
|
|
40
|
+
when SyntaxTree::VCall, SyntaxTree::FCall, SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
|
|
41
|
+
HighlightMatch.new(type: READ, node: other)
|
|
42
|
+
when SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::Defs
|
|
43
|
+
HighlightMatch.new(type: WRITE, node: other.name)
|
|
44
|
+
end
|
|
45
|
+
# Variables, parameters and constants
|
|
46
|
+
when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::VarField,
|
|
47
|
+
SyntaxTree::VarRef, SyntaxTree::Ident
|
|
48
|
+
case other
|
|
49
|
+
when SyntaxTree::VarField
|
|
50
|
+
HighlightMatch.new(type: WRITE, node: other)
|
|
51
|
+
when SyntaxTree::VarRef
|
|
52
|
+
HighlightMatch.new(type: READ, node: other)
|
|
53
|
+
when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
|
|
54
|
+
HighlightMatch.new(type: WRITE, node: other.constant)
|
|
55
|
+
when SyntaxTree::ConstPathRef
|
|
56
|
+
HighlightMatch.new(type: READ, node: other.constant)
|
|
57
|
+
when SyntaxTree::Params
|
|
58
|
+
params = other.child_nodes.compact
|
|
59
|
+
match = params.find { |param| value(param) == @value }
|
|
60
|
+
HighlightMatch.new(type: WRITE, node: match) if match
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
sig { params(node: SyntaxTree::Node).returns(T.nilable(String)) }
|
|
66
|
+
def value(node)
|
|
67
|
+
case node
|
|
68
|
+
when SyntaxTree::ConstPathRef, SyntaxTree::ConstPathField, SyntaxTree::TopConstField
|
|
69
|
+
node.constant.value
|
|
70
|
+
when SyntaxTree::GVar, SyntaxTree::IVar, SyntaxTree::Const, SyntaxTree::CVar, SyntaxTree::Ident,
|
|
71
|
+
SyntaxTree::ArgsForward
|
|
72
|
+
node.value
|
|
73
|
+
when SyntaxTree::Field, SyntaxTree::Def, SyntaxTree::Defs, SyntaxTree::DefEndless, SyntaxTree::RestParam,
|
|
74
|
+
SyntaxTree::KwRestParam, SyntaxTree::BlockArg
|
|
75
|
+
node.name&.value
|
|
76
|
+
when SyntaxTree::VarField, SyntaxTree::VarRef, SyntaxTree::VCall, SyntaxTree::FCall
|
|
77
|
+
node.value&.value
|
|
78
|
+
when SyntaxTree::Call, SyntaxTree::Command, SyntaxTree::CommandCall
|
|
79
|
+
node.message.value
|
|
80
|
+
when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
|
|
81
|
+
node.constant.constant.value
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/ruby_lsp/requests.rb
CHANGED
|
@@ -29,6 +29,7 @@ module RubyLsp
|
|
|
29
29
|
autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
|
|
30
30
|
autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
|
|
31
31
|
autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
|
|
32
|
+
autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "ruby_lsp/internal"
|
|
5
|
+
|
|
6
|
+
module RubyLsp
|
|
7
|
+
Handler.start do
|
|
8
|
+
on("initialize") do |request|
|
|
9
|
+
store.clear
|
|
10
|
+
initialization_options = request.dig(:params, :initializationOptions)
|
|
11
|
+
enabled_features = initialization_options.fetch(:enabledFeatures, [])
|
|
12
|
+
|
|
13
|
+
document_symbol_provider = if enabled_features.include?("documentSymbols")
|
|
14
|
+
Interface::DocumentSymbolClientCapabilities.new(
|
|
15
|
+
hierarchical_document_symbol_support: true,
|
|
16
|
+
symbol_kind: {
|
|
17
|
+
value_set: Requests::DocumentSymbol::SYMBOL_KIND.values,
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
folding_ranges_provider = if enabled_features.include?("foldingRanges")
|
|
23
|
+
Interface::FoldingRangeClientCapabilities.new(line_folding_only: true)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
semantic_tokens_provider = if enabled_features.include?("semanticHighlighting")
|
|
27
|
+
Interface::SemanticTokensRegistrationOptions.new(
|
|
28
|
+
document_selector: { scheme: "file", language: "ruby" },
|
|
29
|
+
legend: Interface::SemanticTokensLegend.new(
|
|
30
|
+
token_types: Requests::SemanticHighlighting::TOKEN_TYPES.keys,
|
|
31
|
+
token_modifiers: Requests::SemanticHighlighting::TOKEN_MODIFIERS.keys
|
|
32
|
+
),
|
|
33
|
+
range: false,
|
|
34
|
+
full: {
|
|
35
|
+
delta: true,
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Interface::InitializeResult.new(
|
|
41
|
+
capabilities: Interface::ServerCapabilities.new(
|
|
42
|
+
text_document_sync: Interface::TextDocumentSyncOptions.new(
|
|
43
|
+
change: Constant::TextDocumentSyncKind::INCREMENTAL,
|
|
44
|
+
open_close: true,
|
|
45
|
+
),
|
|
46
|
+
selection_range_provider: enabled_features.include?("selectionRanges"),
|
|
47
|
+
document_symbol_provider: document_symbol_provider,
|
|
48
|
+
folding_range_provider: folding_ranges_provider,
|
|
49
|
+
semantic_tokens_provider: semantic_tokens_provider,
|
|
50
|
+
document_formatting_provider: enabled_features.include?("formatting"),
|
|
51
|
+
document_highlight_provider: enabled_features.include?("documentHighlights"),
|
|
52
|
+
code_action_provider: enabled_features.include?("codeActions")
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
on("textDocument/didChange") do |request|
|
|
58
|
+
uri = request.dig(:params, :textDocument, :uri)
|
|
59
|
+
store.push_edits(uri, request.dig(:params, :contentChanges))
|
|
60
|
+
|
|
61
|
+
send_diagnostics(uri)
|
|
62
|
+
Handler::VOID
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
on("textDocument/didOpen") do |request|
|
|
66
|
+
uri = request.dig(:params, :textDocument, :uri)
|
|
67
|
+
text = request.dig(:params, :textDocument, :text)
|
|
68
|
+
store.set(uri, text)
|
|
69
|
+
|
|
70
|
+
send_diagnostics(uri)
|
|
71
|
+
Handler::VOID
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
on("textDocument/didClose") do |request|
|
|
75
|
+
uri = request.dig(:params, :textDocument, :uri)
|
|
76
|
+
store.delete(uri)
|
|
77
|
+
clear_diagnostics(uri)
|
|
78
|
+
|
|
79
|
+
Handler::VOID
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
on("textDocument/documentSymbol") do |request|
|
|
83
|
+
store.cache_fetch(request.dig(:params, :textDocument, :uri), :document_symbol) do |document|
|
|
84
|
+
Requests::DocumentSymbol.new(document).run
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
on("textDocument/foldingRange") do |request|
|
|
89
|
+
store.cache_fetch(request.dig(:params, :textDocument, :uri), :folding_ranges) do |document|
|
|
90
|
+
Requests::FoldingRanges.new(document).run
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
on("textDocument/selectionRange") do |request|
|
|
95
|
+
uri = request.dig(:params, :textDocument, :uri)
|
|
96
|
+
positions = request.dig(:params, :positions)
|
|
97
|
+
|
|
98
|
+
ranges = store.cache_fetch(uri, :selection_ranges) do |document|
|
|
99
|
+
Requests::SelectionRanges.new(document).run
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
return if ranges.nil?
|
|
103
|
+
|
|
104
|
+
# Per the selection range request spec (https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange),
|
|
105
|
+
# every position in the positions array should have an element at the same index in the response
|
|
106
|
+
# array. For positions without a valid selection range, the corresponding element in the response
|
|
107
|
+
# array will be nil.
|
|
108
|
+
positions.map do |position|
|
|
109
|
+
ranges.find do |range|
|
|
110
|
+
range.cover?(position)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
on("textDocument/semanticTokens/full") do |request|
|
|
116
|
+
store.cache_fetch(request.dig(:params, :textDocument, :uri), :semantic_highlighting) do |document|
|
|
117
|
+
T.cast(
|
|
118
|
+
Requests::SemanticHighlighting.new(
|
|
119
|
+
document,
|
|
120
|
+
encoder: Requests::Support::SemanticTokenEncoder.new
|
|
121
|
+
).run,
|
|
122
|
+
LanguageServer::Protocol::Interface::SemanticTokens
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
on("textDocument/formatting") do |request|
|
|
128
|
+
uri = request.dig(:params, :textDocument, :uri)
|
|
129
|
+
|
|
130
|
+
Requests::Formatting.new(uri, store.get(uri)).run
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
on("textDocument/documentHighlight") do |request|
|
|
134
|
+
document = store.get(request.dig(:params, :textDocument, :uri))
|
|
135
|
+
return unless document.parsed?
|
|
136
|
+
|
|
137
|
+
Requests::DocumentHighlight.new(document, request.dig(:params, :position)).run
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
on("textDocument/codeAction") do |request|
|
|
141
|
+
uri = request.dig(:params, :textDocument, :uri)
|
|
142
|
+
range = request.dig(:params, :range)
|
|
143
|
+
start_line = range.dig(:start, :line)
|
|
144
|
+
end_line = range.dig(:end, :line)
|
|
145
|
+
|
|
146
|
+
store.cache_fetch(uri, :code_actions) do |document|
|
|
147
|
+
Requests::CodeActions.new(uri, document, start_line..end_line).run
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
on("shutdown") { shutdown }
|
|
152
|
+
|
|
153
|
+
on("exit") do
|
|
154
|
+
# We return zero if shutdown has already been received or one otherwise as per the recommendation in the spec
|
|
155
|
+
# https://microsoft.github.io/language-server-protocol/specification/#exit
|
|
156
|
+
status = store.empty? ? 0 : 1
|
|
157
|
+
exit(status)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
data/lib/ruby_lsp/store.rb
CHANGED
|
@@ -25,9 +25,8 @@ module RubyLsp
|
|
|
25
25
|
|
|
26
26
|
sig { params(uri: String, content: String).void }
|
|
27
27
|
def set(uri, content)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# Do not update the store if there are syntax errors
|
|
28
|
+
document = Document.new(content)
|
|
29
|
+
@state[uri] = document
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
sig { params(uri: String, edits: T::Array[Document::EditShape]).void }
|
|
@@ -40,6 +39,11 @@ module RubyLsp
|
|
|
40
39
|
@state.clear
|
|
41
40
|
end
|
|
42
41
|
|
|
42
|
+
sig { returns(T::Boolean) }
|
|
43
|
+
def empty?
|
|
44
|
+
@state.empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
43
47
|
sig { params(uri: String).void }
|
|
44
48
|
def delete(uri)
|
|
45
49
|
@state.delete(uri)
|
|
@@ -51,10 +55,13 @@ module RubyLsp
|
|
|
51
55
|
uri: String,
|
|
52
56
|
request_name: Symbol,
|
|
53
57
|
block: T.proc.params(document: Document).returns(T.type_parameter(:T))
|
|
54
|
-
).returns(T.type_parameter(:T))
|
|
58
|
+
).returns(T.nilable(T.type_parameter(:T)))
|
|
55
59
|
end
|
|
56
60
|
def cache_fetch(uri, request_name, &block)
|
|
57
|
-
get(uri)
|
|
61
|
+
document = get(uri)
|
|
62
|
+
return unless document.parsed?
|
|
63
|
+
|
|
64
|
+
document.cache_fetch(request_name, &block)
|
|
58
65
|
end
|
|
59
66
|
end
|
|
60
67
|
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.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-07-
|
|
11
|
+
date: 2022-07-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: language_server-protocol
|
|
@@ -85,7 +85,6 @@ files:
|
|
|
85
85
|
- dev.yml
|
|
86
86
|
- exe/ruby-lsp
|
|
87
87
|
- lib/ruby-lsp.rb
|
|
88
|
-
- lib/ruby_lsp/cli.rb
|
|
89
88
|
- lib/ruby_lsp/document.rb
|
|
90
89
|
- lib/ruby_lsp/handler.rb
|
|
91
90
|
- lib/ruby_lsp/internal.rb
|
|
@@ -99,12 +98,14 @@ files:
|
|
|
99
98
|
- lib/ruby_lsp/requests/formatting.rb
|
|
100
99
|
- lib/ruby_lsp/requests/selection_ranges.rb
|
|
101
100
|
- lib/ruby_lsp/requests/semantic_highlighting.rb
|
|
101
|
+
- lib/ruby_lsp/requests/support/highlight_target.rb
|
|
102
102
|
- lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
|
|
103
103
|
- lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb
|
|
104
104
|
- lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb
|
|
105
105
|
- lib/ruby_lsp/requests/support/selection_range.rb
|
|
106
106
|
- lib/ruby_lsp/requests/support/semantic_token_encoder.rb
|
|
107
107
|
- lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb
|
|
108
|
+
- lib/ruby_lsp/server.rb
|
|
108
109
|
- lib/ruby_lsp/store.rb
|
|
109
110
|
- rakelib/check_docs.rake
|
|
110
111
|
- ruby-lsp.gemspec
|
data/lib/ruby_lsp/cli.rb
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "language_server-protocol"
|
|
5
|
-
|
|
6
|
-
require_relative "handler"
|
|
7
|
-
|
|
8
|
-
module RubyLsp
|
|
9
|
-
module Cli
|
|
10
|
-
extend T::Sig
|
|
11
|
-
|
|
12
|
-
sig { void }
|
|
13
|
-
def self.start
|
|
14
|
-
handler = RubyLsp::Handler.new
|
|
15
|
-
|
|
16
|
-
handler.config do
|
|
17
|
-
on("initialize") do |request|
|
|
18
|
-
store.clear
|
|
19
|
-
initialization_options = request.dig(:params, :initializationOptions)
|
|
20
|
-
|
|
21
|
-
respond_with_capabilities(initialization_options.fetch(:enabledFeatures, []))
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
on("textDocument/didChange") do |request|
|
|
25
|
-
uri = request.dig(:params, :textDocument, :uri)
|
|
26
|
-
store.push_edits(uri, request.dig(:params, :contentChanges))
|
|
27
|
-
|
|
28
|
-
send_diagnostics(uri)
|
|
29
|
-
RubyLsp::Handler::VOID
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
on("textDocument/didOpen") do |request|
|
|
33
|
-
uri = request.dig(:params, :textDocument, :uri)
|
|
34
|
-
text = request.dig(:params, :textDocument, :text)
|
|
35
|
-
store.set(uri, text)
|
|
36
|
-
|
|
37
|
-
send_diagnostics(uri)
|
|
38
|
-
RubyLsp::Handler::VOID
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
on("textDocument/didClose") do |request|
|
|
42
|
-
uri = request.dig(:params, :textDocument, :uri)
|
|
43
|
-
store.delete(uri)
|
|
44
|
-
clear_diagnostics(uri)
|
|
45
|
-
|
|
46
|
-
RubyLsp::Handler::VOID
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
on("textDocument/documentSymbol") do |request|
|
|
50
|
-
respond_with_document_symbol(request.dig(:params, :textDocument, :uri))
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
on("textDocument/foldingRange") do |request|
|
|
54
|
-
respond_with_folding_ranges(request.dig(:params, :textDocument, :uri))
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
on("textDocument/selectionRange") do |request|
|
|
58
|
-
respond_with_selection_ranges(
|
|
59
|
-
request.dig(:params, :textDocument, :uri),
|
|
60
|
-
request.dig(:params, :positions),
|
|
61
|
-
)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
on("textDocument/semanticTokens/full") do |request|
|
|
65
|
-
respond_with_semantic_highlighting(request.dig(:params, :textDocument, :uri))
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
on("textDocument/formatting") do |request|
|
|
69
|
-
respond_with_formatting(request.dig(:params, :textDocument, :uri))
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
on("textDocument/documentHighlight") do |request|
|
|
73
|
-
respond_with_document_highlight(request.dig(:params, :textDocument, :uri), request.dig(:params, :position))
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
on("textDocument/codeAction") do |request|
|
|
77
|
-
range = request.dig(:params, :range)
|
|
78
|
-
start_line = range.dig(:start, :line)
|
|
79
|
-
end_line = range.dig(:end, :line)
|
|
80
|
-
respond_with_code_actions(request.dig(:params, :textDocument, :uri), (start_line..end_line))
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
on("shutdown") { shutdown }
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
handler.start
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|