ruby-lsp 0.1.0 → 0.2.2

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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/README.md +2 -1
  4. data/VERSION +1 -1
  5. data/exe/ruby-lsp +1 -3
  6. data/lib/ruby-lsp.rb +2 -2
  7. data/lib/ruby_lsp/document.rb +10 -3
  8. data/lib/ruby_lsp/handler.rb +30 -134
  9. data/lib/ruby_lsp/internal.rb +3 -1
  10. data/lib/ruby_lsp/requests/code_actions.rb +2 -0
  11. data/lib/ruby_lsp/requests/diagnostics.rb +14 -9
  12. data/lib/ruby_lsp/requests/document_highlight.rb +27 -42
  13. data/lib/ruby_lsp/requests/document_link.rb +59 -0
  14. data/lib/ruby_lsp/requests/document_symbol.rb +2 -0
  15. data/lib/ruby_lsp/requests/folding_ranges.rb +26 -21
  16. data/lib/ruby_lsp/requests/formatting.rb +21 -16
  17. data/lib/ruby_lsp/requests/selection_ranges.rb +2 -0
  18. data/lib/ruby_lsp/requests/semantic_highlighting.rb +85 -11
  19. data/lib/ruby_lsp/requests/support/highlight_target.rb +88 -0
  20. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +9 -2
  21. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +61 -0
  22. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +50 -0
  23. data/lib/ruby_lsp/requests.rb +14 -1
  24. data/lib/ruby_lsp/server.rb +192 -0
  25. data/lib/ruby_lsp/store.rb +12 -5
  26. metadata +10 -87
  27. data/.github/dependabot.yml +0 -11
  28. data/.github/probots.yml +0 -2
  29. data/.github/pull_request_template.md +0 -15
  30. data/.github/workflows/ci.yml +0 -31
  31. data/.github/workflows/publish_docs.yml +0 -32
  32. data/.gitignore +0 -9
  33. data/.rubocop.yml +0 -39
  34. data/.vscode/extensions.json +0 -5
  35. data/.vscode/settings.json +0 -5
  36. data/.vscode/tasks.json +0 -25
  37. data/CODE_OF_CONDUCT.md +0 -78
  38. data/Gemfile +0 -17
  39. data/Gemfile.lock +0 -124
  40. data/Rakefile +0 -21
  41. data/bin/rubocop +0 -29
  42. data/bin/tapioca +0 -29
  43. data/bin/test +0 -9
  44. data/dev.yml +0 -20
  45. data/lib/ruby_lsp/cli.rb +0 -88
  46. data/lib/ruby_lsp/requests/rubocop_request.rb +0 -60
  47. data/rakelib/check_docs.rake +0 -57
  48. data/ruby-lsp.gemspec +0 -26
  49. data/service.yml +0 -2
  50. data/sorbet/config +0 -4
  51. data/sorbet/rbi/.rubocop.yml +0 -8
  52. data/sorbet/rbi/gems/ansi@1.5.0.rbi +0 -338
  53. data/sorbet/rbi/gems/ast@2.4.2.rbi +0 -522
  54. data/sorbet/rbi/gems/builder@3.2.4.rbi +0 -418
  55. data/sorbet/rbi/gems/coderay@1.1.3.rbi +0 -8
  56. data/sorbet/rbi/gems/debug@1.5.0.rbi +0 -1273
  57. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +0 -867
  58. data/sorbet/rbi/gems/io-console@0.5.11.rbi +0 -8
  59. data/sorbet/rbi/gems/irb@1.4.1.rbi +0 -376
  60. data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +0 -7325
  61. data/sorbet/rbi/gems/method_source@1.0.0.rbi +0 -8
  62. data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +0 -612
  63. data/sorbet/rbi/gems/minitest@5.15.0.rbi +0 -994
  64. data/sorbet/rbi/gems/parallel@1.22.1.rbi +0 -163
  65. data/sorbet/rbi/gems/parser@3.1.2.0.rbi +0 -3968
  66. data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +0 -734
  67. data/sorbet/rbi/gems/pry@0.14.1.rbi +0 -8
  68. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +0 -227
  69. data/sorbet/rbi/gems/rake@13.0.6.rbi +0 -1853
  70. data/sorbet/rbi/gems/rbi@0.0.14.rbi +0 -2337
  71. data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +0 -1854
  72. data/sorbet/rbi/gems/reline@0.3.1.rbi +0 -1274
  73. data/sorbet/rbi/gems/rexml@3.2.5.rbi +0 -3852
  74. data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +0 -4180
  75. data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +0 -1369
  76. data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +0 -246
  77. data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +0 -8
  78. data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +0 -652
  79. data/sorbet/rbi/gems/rubocop@1.30.0.rbi +0 -36729
  80. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +0 -732
  81. data/sorbet/rbi/gems/spoom@1.1.11.rbi +0 -1600
  82. data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +0 -6777
  83. data/sorbet/rbi/gems/tapioca@0.8.1.rbi +0 -1972
  84. data/sorbet/rbi/gems/thor@1.2.1.rbi +0 -2921
  85. data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +0 -27
  86. data/sorbet/rbi/gems/unparser@0.6.5.rbi +0 -2789
  87. data/sorbet/rbi/gems/webrick@1.7.0.rbi +0 -1779
  88. data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +0 -289
  89. data/sorbet/rbi/gems/yard@0.9.27.rbi +0 -13048
  90. data/sorbet/rbi/shims/fiddle.rbi +0 -4
  91. data/sorbet/rbi/shims/hash.rbi +0 -6
  92. data/sorbet/rbi/shims/rdoc.rbi +0 -4
  93. data/sorbet/tapioca/config.yml +0 -13
  94. data/sorbet/tapioca/require.rb +0 -7
@@ -3,10 +3,13 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
+ # ![Folding ranges demo](../../misc/folding_ranges.gif)
7
+ #
6
8
  # The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
7
- # request informs the editor of the ranges where code can be folded.
9
+ # request informs the editor of the ranges where and how code can be folded.
8
10
  #
9
11
  # # Example
12
+ #
10
13
  # ```ruby
11
14
  # def say_hello # <-- folding range start
12
15
  # puts "Hello"
@@ -32,12 +35,13 @@ module RubyLsp
32
35
  SyntaxTree::Unless,
33
36
  SyntaxTree::Until,
34
37
  SyntaxTree::While,
38
+ SyntaxTree::Else,
39
+ SyntaxTree::Ensure,
40
+ SyntaxTree::Begin,
35
41
  ].freeze, T::Array[T.class_of(SyntaxTree::Node)])
36
42
 
37
43
  NODES_WITH_STATEMENTS = T.let([
38
- SyntaxTree::Else,
39
44
  SyntaxTree::Elsif,
40
- SyntaxTree::Ensure,
41
45
  SyntaxTree::In,
42
46
  SyntaxTree::Rescue,
43
47
  SyntaxTree::When,
@@ -45,9 +49,7 @@ module RubyLsp
45
49
 
46
50
  StatementNode = T.type_alias do
47
51
  T.any(
48
- SyntaxTree::Else,
49
52
  SyntaxTree::Elsif,
50
- SyntaxTree::Ensure,
51
53
  SyntaxTree::In,
52
54
  SyntaxTree::Rescue,
53
55
  SyntaxTree::When,
@@ -77,11 +79,10 @@ module RubyLsp
77
79
 
78
80
  case node
79
81
  when *SIMPLE_FOLDABLES
80
- add_node_range(T.must(node))
82
+ location = T.must(node).location
83
+ add_lines_range(location.start_line, location.end_line - 1)
81
84
  when *NODES_WITH_STATEMENTS
82
85
  add_statements_range(T.must(node), T.cast(node, StatementNode).statements)
83
- when SyntaxTree::Begin
84
- add_statements_range(node, node.bodystmt.statements)
85
86
  when SyntaxTree::Call, SyntaxTree::CommandCall
86
87
  add_call_range(node)
87
88
  return
@@ -135,6 +136,11 @@ module RubyLsp
135
136
  kind: @kind
136
137
  )
137
138
  end
139
+
140
+ sig { returns(T::Boolean) }
141
+ def multiline?
142
+ @end_line > @start_line
143
+ end
138
144
  end
139
145
 
140
146
  sig { params(node: T.nilable(SyntaxTree::Node)).returns(T::Boolean) }
@@ -175,7 +181,7 @@ module RubyLsp
175
181
  def emit_partial_range
176
182
  return if @partial_range.nil?
177
183
 
178
- @ranges << @partial_range.to_range
184
+ @ranges << @partial_range.to_range if @partial_range.multiline?
179
185
  @partial_range = nil
180
186
  end
181
187
 
@@ -189,13 +195,17 @@ module RubyLsp
189
195
  receiver = receiver.receiver
190
196
  when SyntaxTree::MethodAddBlock
191
197
  visit(receiver.block)
192
- receiver = receiver.call.receiver
198
+ receiver = receiver.call
199
+
200
+ if receiver.is_a?(SyntaxTree::Call) || receiver.is_a?(SyntaxTree::CommandCall)
201
+ receiver = receiver.receiver
202
+ end
193
203
  else
194
204
  break
195
205
  end
196
206
  end
197
207
 
198
- add_lines_range(receiver.location.start_line, node.location.end_line)
208
+ add_lines_range(receiver.location.start_line, node.location.end_line - 1)
199
209
 
200
210
  visit(node.arguments)
201
211
  end
@@ -205,9 +215,10 @@ module RubyLsp
205
215
  params_location = node.params.location
206
216
 
207
217
  if params_location.start_line < params_location.end_line
208
- add_lines_range(params_location.end_line, node.location.end_line)
218
+ add_lines_range(params_location.end_line, node.location.end_line - 1)
209
219
  else
210
- add_node_range(node)
220
+ location = node.location
221
+ add_lines_range(location.start_line, location.end_line - 1)
211
222
  end
212
223
 
213
224
  visit(node.bodystmt.statements)
@@ -215,7 +226,7 @@ module RubyLsp
215
226
 
216
227
  sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
217
228
  def add_statements_range(node, statements)
218
- add_lines_range(node.location.start_line, statements.location.end_line) unless statements.empty?
229
+ add_lines_range(node.location.start_line, statements.body.last.location.end_line) unless statements.empty?
219
230
  end
220
231
 
221
232
  sig { params(node: SyntaxTree::StringConcat).void }
@@ -223,13 +234,7 @@ module RubyLsp
223
234
  left = T.let(node.left, SyntaxTree::Node)
224
235
  left = left.left while left.is_a?(SyntaxTree::StringConcat)
225
236
 
226
- add_lines_range(left.location.start_line, node.right.location.end_line)
227
- end
228
-
229
- sig { params(node: SyntaxTree::Node).void }
230
- def add_node_range(node)
231
- location = node.location
232
- add_lines_range(location.start_line, location.end_line)
237
+ add_lines_range(left.location.start_line, node.right.location.end_line - 1)
233
238
  end
234
239
 
235
240
  sig { params(start_line: Integer, end_line: Integer).void }
@@ -1,8 +1,12 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "ruby_lsp/requests/support/rubocop_formatting_runner"
5
+
4
6
  module RubyLsp
5
7
  module Requests
8
+ # ![Formatting symbol demo](../../misc/formatting.gif)
9
+ #
6
10
  # The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
7
11
  # request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
8
12
  # registering the ruby-lsp as the Ruby formatter.
@@ -14,43 +18,44 @@ module RubyLsp
14
18
  # puts "Hello" # --> formatting: fixes the indentation on save
15
19
  # end
16
20
  # ```
17
- class Formatting < RuboCopRequest
21
+ class Formatting < BaseRequest
18
22
  extend T::Sig
19
23
 
20
- RUBOCOP_FLAGS = T.let((COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze, T::Array[String])
21
-
22
24
  sig { params(uri: String, document: Document).void }
23
25
  def initialize(uri, document)
24
- super
25
- @formatted_text = T.let(nil, T.nilable(String))
26
+ super(document)
27
+
28
+ @uri = uri
26
29
  end
27
30
 
28
31
  sig { override.returns(T.nilable(T.all(T::Array[LanguageServer::Protocol::Interface::TextEdit], Object))) }
29
32
  def run
30
- super
33
+ formatted_text = formatted_file
34
+ return unless formatted_text
31
35
 
32
- @formatted_text = @options[:stdin] # Rubocop applies the corrections on stdin
33
- return unless @formatted_text
36
+ size = @document.source.size
37
+ return if formatted_text.size == size && formatted_text == @document.source
34
38
 
35
39
  [
36
40
  LanguageServer::Protocol::Interface::TextEdit.new(
37
41
  range: LanguageServer::Protocol::Interface::Range.new(
38
42
  start: LanguageServer::Protocol::Interface::Position.new(line: 0, character: 0),
39
- end: LanguageServer::Protocol::Interface::Position.new(
40
- line: text.size,
41
- character: text.size
42
- )
43
+ end: LanguageServer::Protocol::Interface::Position.new(line: size, character: size)
43
44
  ),
44
- new_text: @formatted_text
45
+ new_text: formatted_text
45
46
  ),
46
47
  ]
47
48
  end
48
49
 
49
50
  private
50
51
 
51
- sig { returns(T::Array[String]) }
52
- def rubocop_flags
53
- RUBOCOP_FLAGS
52
+ sig { returns(T.nilable(String)) }
53
+ def formatted_file
54
+ if defined?(Support::RuboCopFormattingRunner)
55
+ Support::RuboCopFormattingRunner.instance.run(@uri, @document)
56
+ else
57
+ SyntaxTree.format(@document.source)
58
+ end
54
59
  end
55
60
  end
56
61
  end
@@ -3,6 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
+ # ![Selection ranges demo](../../misc/selection_ranges.gif)
7
+ #
6
8
  # The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
7
9
  # request informs the editor of ranges that the user may want to select based on the location(s)
8
10
  # of their cursor(s).
@@ -3,6 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
+ # ![Semantic highlighting demo](../../misc/semantic_highlighting.gif)
7
+ #
6
8
  # The [semantic
7
9
  # highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
8
10
  # request informs the editor of the correct token types to provide consistent and accurate highlighting for themes.
@@ -19,11 +21,31 @@ module RubyLsp
19
21
  class SemanticHighlighting < BaseRequest
20
22
  extend T::Sig
21
23
 
22
- TOKEN_TYPES = T.let([
23
- :variable,
24
- :method,
25
- :namespace,
26
- ].freeze, T::Array[Symbol])
24
+ TOKEN_TYPES = T.let({
25
+ namespace: 0,
26
+ type: 1,
27
+ class: 2,
28
+ enum: 3,
29
+ interface: 4,
30
+ struct: 5,
31
+ typeParameter: 6,
32
+ parameter: 7,
33
+ variable: 8,
34
+ property: 9,
35
+ enumMember: 10,
36
+ event: 11,
37
+ function: 12,
38
+ method: 13,
39
+ macro: 14,
40
+ keyword: 15,
41
+ modifier: 16,
42
+ comment: 17,
43
+ string: 18,
44
+ number: 19,
45
+ regexp: 20,
46
+ operator: 21,
47
+ decorator: 22,
48
+ }.freeze, T::Hash[Symbol, Integer])
27
49
 
28
50
  TOKEN_MODIFIERS = T.let({
29
51
  declaration: 0,
@@ -38,6 +60,14 @@ module RubyLsp
38
60
  default_library: 9,
39
61
  }.freeze, T::Hash[Symbol, Integer])
40
62
 
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])
70
+
41
71
  class SemanticToken < T::Struct
42
72
  const :location, SyntaxTree::Location
43
73
  const :length, Integer
@@ -51,7 +81,8 @@ module RubyLsp
51
81
 
52
82
  @encoder = encoder
53
83
  @tokens = T.let([], T::Array[SemanticToken])
54
- @tree = T.let(document.tree, SyntaxTree::Node)
84
+ @tree = T.let(T.must(document.tree), SyntaxTree::Node)
85
+ @special_methods = T.let(nil, T.nilable(T::Array[String]))
55
86
  end
56
87
 
57
88
  sig do
@@ -77,13 +108,16 @@ module RubyLsp
77
108
  sig { params(node: SyntaxTree::Call).void }
78
109
  def visit_call(node)
79
110
  visit(node.receiver)
80
- add_token(node.message.location, :method)
111
+
112
+ message = node.message
113
+ add_token(message.location, :method) if message != :call
114
+
81
115
  visit(node.arguments)
82
116
  end
83
117
 
84
118
  sig { params(node: SyntaxTree::Command).void }
85
119
  def visit_command(node)
86
- add_token(node.message.location, :method)
120
+ add_token(node.message.location, :method) unless special_method?(node.message.value)
87
121
  visit(node.arguments)
88
122
  end
89
123
 
@@ -125,7 +159,7 @@ module RubyLsp
125
159
 
126
160
  sig { params(node: SyntaxTree::FCall).void }
127
161
  def visit_fcall(node)
128
- add_token(node.value.location, :method)
162
+ add_token(node.value.location, :method) unless special_method?(node.value.value)
129
163
  visit(node.arguments)
130
164
  end
131
165
 
@@ -144,6 +178,20 @@ module RubyLsp
144
178
  end
145
179
  end
146
180
 
181
+ sig { params(node: SyntaxTree::Params).void }
182
+ def visit_params(node)
183
+ node.keywords.each do |keyword,|
184
+ location = keyword.location
185
+ add_token(location_without_colon(location), :variable)
186
+ end
187
+
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
193
+ end
194
+
147
195
  sig { params(node: SyntaxTree::VarField).void }
148
196
  def visit_var_field(node)
149
197
  case node.value
@@ -166,7 +214,7 @@ module RubyLsp
166
214
 
167
215
  sig { params(node: SyntaxTree::VCall).void }
168
216
  def visit_vcall(node)
169
- add_token(node.value.location, :method)
217
+ add_token(node.value.location, :method) unless special_method?(node.value.value)
170
218
  end
171
219
 
172
220
  sig { params(location: SyntaxTree::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
@@ -177,11 +225,37 @@ module RubyLsp
177
225
  SemanticToken.new(
178
226
  location: location,
179
227
  length: length,
180
- type: T.must(TOKEN_TYPES.index(type)),
228
+ type: T.must(TOKEN_TYPES[type]),
181
229
  modifier: modifiers_indices
182
230
  )
183
231
  )
184
232
  end
233
+
234
+ private
235
+
236
+ # Exclude the ":" symbol at the end of a location
237
+ # We use it on keyword parameters to be consistent
238
+ # with the rest of the parameters
239
+ sig { params(location: T.untyped).returns(SyntaxTree::Location) }
240
+ def location_without_colon(location)
241
+ SyntaxTree::Location.new(
242
+ start_line: location.start_line,
243
+ start_column: location.start_column,
244
+ start_char: location.start_char,
245
+ end_char: location.end_char - 1,
246
+ end_column: location.end_column - 1,
247
+ end_line: location.end_line,
248
+ )
249
+ end
250
+
251
+ # Textmate provides highlighting for a subset
252
+ # of these special Ruby-specific methods.
253
+ # We want to utilize that highlighting, so we
254
+ # avoid making a semantic token for it.
255
+ sig { params(method_name: String).returns(T::Boolean) }
256
+ def special_method?(method_name)
257
+ SPECIAL_RUBY_METHODS.include?(method_name)
258
+ end
185
259
  end
186
260
  end
187
261
  end
@@ -0,0 +1,88 @@
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
+ message = node.message
80
+ message != :call ? message.value : nil
81
+ when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
82
+ node.constant.constant.value
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -61,11 +61,18 @@ module RubyLsp
61
61
 
62
62
  sig { returns(LanguageServer::Protocol::Interface::Diagnostic) }
63
63
  def to_lsp_diagnostic
64
+ if @offense.correctable?
65
+ severity = RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
66
+ message = @offense.message
67
+ else
68
+ severity = LanguageServer::Protocol::Constant::DiagnosticSeverity::WARNING
69
+ message = "#{@offense.message}\n\nThis offense is not auto-correctable.\n"
70
+ end
64
71
  LanguageServer::Protocol::Interface::Diagnostic.new(
65
- message: @offense.message,
72
+ message: message,
66
73
  source: "RuboCop",
67
74
  code: @offense.cop_name,
68
- severity: RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name],
75
+ severity: severity,
69
76
  range: LanguageServer::Protocol::Interface::Range.new(
70
77
  start: LanguageServer::Protocol::Interface::Position.new(
71
78
  line: @offense.line - 1,
@@ -0,0 +1,61 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "rubocop"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ require "cgi"
11
+ require "singleton"
12
+
13
+ module RubyLsp
14
+ module Requests
15
+ module Support
16
+ # :nodoc:
17
+ class RuboCopDiagnosticsRunner < RuboCop::Runner
18
+ extend T::Sig
19
+ include Singleton
20
+
21
+ sig { void }
22
+ def initialize
23
+ @options = T.let({}, T::Hash[Symbol, T.untyped])
24
+ @uri = T.let(nil, T.nilable(String))
25
+ @diagnostics = T.let([], T::Array[Support::RuboCopDiagnostic])
26
+
27
+ super(
28
+ ::RuboCop::Options.new.parse([
29
+ "--stderr", # Print any output to stderr so that our stdout does not get polluted
30
+ "--force-exclusion",
31
+ "--format",
32
+ "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
33
+ ]).first,
34
+ ::RuboCop::ConfigStore.new
35
+ )
36
+ end
37
+
38
+ sig { params(uri: String, document: Document).returns(T::Array[Support::RuboCopDiagnostic]) }
39
+ def run(uri, document)
40
+ @diagnostics.clear
41
+ @uri = uri
42
+
43
+ file = CGI.unescape(URI.parse(uri).path)
44
+ # We communicate with Rubocop via stdin
45
+ @options[:stdin] = document.source
46
+
47
+ # Invoke RuboCop with just this file in `paths`
48
+ super([file])
49
+ @diagnostics
50
+ end
51
+
52
+ private
53
+
54
+ sig { params(_file: String, offenses: T::Array[RuboCop::Cop::Offense]).void }
55
+ def file_finished(_file, offenses)
56
+ @diagnostics = offenses.map { |offense| Support::RuboCopDiagnostic.new(offense, T.must(@uri)) }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "rubocop"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ require "cgi"
11
+ require "singleton"
12
+
13
+ module RubyLsp
14
+ module Requests
15
+ module Support
16
+ # :nodoc:
17
+ class RuboCopFormattingRunner < RuboCop::Runner
18
+ extend T::Sig
19
+ include Singleton
20
+
21
+ sig { void }
22
+ def initialize
23
+ @options = T.let({}, T::Hash[Symbol, T.untyped])
24
+
25
+ super(
26
+ ::RuboCop::Options.new.parse([
27
+ "--stderr", # Print any output to stderr so that our stdout does not get polluted
28
+ "--force-exclusion",
29
+ "--format",
30
+ "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
31
+ "-a", # --auto-correct
32
+ ]).first,
33
+ ::RuboCop::ConfigStore.new
34
+ )
35
+ end
36
+
37
+ sig { params(uri: String, document: Document).returns(T.nilable(String)) }
38
+ def run(uri, document)
39
+ file = CGI.unescape(URI.parse(uri).path)
40
+ # We communicate with Rubocop via stdin
41
+ @options[:stdin] = document.source
42
+
43
+ # Invoke RuboCop with just this file in `paths`
44
+ super([file])
45
+ @options[:stdin]
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -2,23 +2,36 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
+ # Supported features
6
+ #
7
+ # - {RubyLsp::Requests::DocumentSymbol}
8
+ # - {RubyLsp::Requests::DocumentLink}
9
+ # - {RubyLsp::Requests::FoldingRanges}
10
+ # - {RubyLsp::Requests::SelectionRanges}
11
+ # - {RubyLsp::Requests::SemanticHighlighting}
12
+ # - {RubyLsp::Requests::Formatting}
13
+ # - {RubyLsp::Requests::Diagnostics}
14
+ # - {RubyLsp::Requests::CodeActions}
15
+ # - {RubyLsp::Requests::DocumentHighlight}
5
16
  module Requests
6
17
  autoload :BaseRequest, "ruby_lsp/requests/base_request"
7
18
  autoload :DocumentSymbol, "ruby_lsp/requests/document_symbol"
19
+ autoload :DocumentLink, "ruby_lsp/requests/document_link"
8
20
  autoload :FoldingRanges, "ruby_lsp/requests/folding_ranges"
9
21
  autoload :SelectionRanges, "ruby_lsp/requests/selection_ranges"
10
22
  autoload :SemanticHighlighting, "ruby_lsp/requests/semantic_highlighting"
11
- autoload :RuboCopRequest, "ruby_lsp/requests/rubocop_request"
12
23
  autoload :Formatting, "ruby_lsp/requests/formatting"
13
24
  autoload :Diagnostics, "ruby_lsp/requests/diagnostics"
14
25
  autoload :CodeActions, "ruby_lsp/requests/code_actions"
15
26
  autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
16
27
 
28
+ # :nodoc:
17
29
  module Support
18
30
  autoload :RuboCopDiagnostic, "ruby_lsp/requests/support/rubocop_diagnostic"
19
31
  autoload :SelectionRange, "ruby_lsp/requests/support/selection_range"
20
32
  autoload :SemanticTokenEncoder, "ruby_lsp/requests/support/semantic_token_encoder"
21
33
  autoload :SyntaxErrorDiagnostic, "ruby_lsp/requests/support/syntax_error_diagnostic"
34
+ autoload :HighlightTarget, "ruby_lsp/requests/support/highlight_target"
22
35
  end
23
36
  end
24
37
  end