ruby-lsp 0.1.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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