ruby-lsp 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +9 -1
  3. data/.github/workflows/publish_docs.yml +32 -0
  4. data/.rubocop.yml +25 -0
  5. data/CHANGELOG.md +23 -0
  6. data/Gemfile +8 -4
  7. data/Gemfile.lock +64 -13
  8. data/README.md +58 -1
  9. data/Rakefile +5 -0
  10. data/VERSION +1 -1
  11. data/bin/tapioca +29 -0
  12. data/dev.yml +3 -0
  13. data/exe/ruby-lsp +19 -3
  14. data/lib/ruby-lsp.rb +2 -0
  15. data/lib/ruby_lsp/cli.rb +23 -7
  16. data/lib/ruby_lsp/document.rb +98 -6
  17. data/lib/ruby_lsp/handler.rb +119 -18
  18. data/lib/ruby_lsp/internal.rb +7 -0
  19. data/lib/ruby_lsp/requests/base_request.rb +19 -5
  20. data/lib/ruby_lsp/requests/code_actions.rb +30 -9
  21. data/lib/ruby_lsp/requests/diagnostics.rb +29 -77
  22. data/lib/ruby_lsp/requests/document_highlight.rb +111 -0
  23. data/lib/ruby_lsp/requests/document_symbol.rb +75 -16
  24. data/lib/ruby_lsp/requests/folding_ranges.rb +63 -19
  25. data/lib/ruby_lsp/requests/formatting.rb +19 -2
  26. data/lib/ruby_lsp/requests/rubocop_request.rb +21 -8
  27. data/lib/ruby_lsp/requests/selection_ranges.rb +114 -0
  28. data/lib/ruby_lsp/requests/semantic_highlighting.rb +132 -61
  29. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +100 -0
  30. data/lib/ruby_lsp/requests/support/selection_range.rb +20 -0
  31. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +70 -0
  32. data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +32 -0
  33. data/lib/ruby_lsp/requests.rb +10 -0
  34. data/lib/ruby_lsp/store.rb +23 -2
  35. data/rakelib/check_docs.rake +57 -0
  36. data/ruby-lsp.gemspec +2 -1
  37. data/sorbet/config +4 -0
  38. data/sorbet/rbi/.rubocop.yml +8 -0
  39. data/sorbet/rbi/gems/ansi@1.5.0.rbi +338 -0
  40. data/sorbet/rbi/gems/ast@2.4.2.rbi +522 -0
  41. data/sorbet/rbi/gems/builder@3.2.4.rbi +418 -0
  42. data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
  43. data/sorbet/rbi/gems/debug@1.5.0.rbi +1273 -0
  44. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +867 -0
  45. data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
  46. data/sorbet/rbi/gems/irb@1.4.1.rbi +376 -0
  47. data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +7325 -0
  48. data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
  49. data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +612 -0
  50. data/sorbet/rbi/gems/minitest@5.15.0.rbi +994 -0
  51. data/sorbet/rbi/gems/parallel@1.22.1.rbi +163 -0
  52. data/sorbet/rbi/gems/parser@3.1.2.0.rbi +3968 -0
  53. data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +734 -0
  54. data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
  55. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +227 -0
  56. data/sorbet/rbi/gems/rake@13.0.6.rbi +1853 -0
  57. data/sorbet/rbi/gems/rbi@0.0.14.rbi +2337 -0
  58. data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +1854 -0
  59. data/sorbet/rbi/gems/reline@0.3.1.rbi +1274 -0
  60. data/sorbet/rbi/gems/rexml@3.2.5.rbi +3852 -0
  61. data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +4180 -0
  62. data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +1369 -0
  63. data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +246 -0
  64. data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +8 -0
  65. data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +652 -0
  66. data/sorbet/rbi/gems/rubocop@1.30.0.rbi +36729 -0
  67. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +732 -0
  68. data/sorbet/rbi/gems/spoom@1.1.11.rbi +1600 -0
  69. data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +6777 -0
  70. data/sorbet/rbi/gems/tapioca@0.8.1.rbi +1972 -0
  71. data/sorbet/rbi/gems/thor@1.2.1.rbi +2921 -0
  72. data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +27 -0
  73. data/sorbet/rbi/gems/unparser@0.6.5.rbi +2789 -0
  74. data/sorbet/rbi/gems/webrick@1.7.0.rbi +1779 -0
  75. data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +289 -0
  76. data/sorbet/rbi/gems/yard@0.9.27.rbi +13048 -0
  77. data/sorbet/rbi/shims/fiddle.rbi +4 -0
  78. data/sorbet/rbi/shims/hash.rbi +6 -0
  79. data/sorbet/rbi/shims/rdoc.rbi +4 -0
  80. data/sorbet/tapioca/config.yml +13 -0
  81. data/sorbet/tapioca/require.rb +7 -0
  82. metadata +74 -6
  83. data/shipit.production.yml +0 -1
@@ -1,9 +1,33 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
6
+ # The [document
7
+ # symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
8
+ # informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
9
+ # this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
10
+ #
11
+ # In VS Code, fuzzy symbol search can be accessed by opened the command palette and inserting an `@` symbol.
12
+ #
13
+ # # Example
14
+ #
15
+ # ```ruby
16
+ # class Person # --> document symbol: class
17
+ # attr_reader :age # --> document symbol: field
18
+ #
19
+ # def initialize
20
+ # @age = 0 # --> document symbol: variable
21
+ # end
22
+ #
23
+ # def age # --> document symbol: method
24
+ # end
25
+ # end
26
+ # ```
5
27
  class DocumentSymbol < BaseRequest
6
- SYMBOL_KIND = {
28
+ extend T::Sig
29
+
30
+ SYMBOL_KIND = T.let({
7
31
  file: 1,
8
32
  module: 2,
9
33
  namespace: 3,
@@ -30,33 +54,43 @@ module RubyLsp
30
54
  event: 24,
31
55
  operator: 25,
32
56
  typeparameter: 26,
33
- }.freeze
57
+ }.freeze, T::Hash[Symbol, Integer])
34
58
 
35
- ATTR_ACCESSORS = ["attr_reader", "attr_writer", "attr_accessor"].freeze
59
+ ATTR_ACCESSORS = T.let(["attr_reader", "attr_writer", "attr_accessor"].freeze, T::Array[String])
36
60
 
37
61
  class SymbolHierarchyRoot
62
+ extend T::Sig
63
+
64
+ sig { returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
38
65
  attr_reader :children
39
66
 
67
+ sig { void }
40
68
  def initialize
41
- @children = []
69
+ @children = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentSymbol])
42
70
  end
43
71
  end
44
72
 
73
+ sig { params(document: Document).void }
45
74
  def initialize(document)
46
75
  super
47
76
 
48
- @root = SymbolHierarchyRoot.new
49
- @stack = [@root]
77
+ @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
78
+ @stack = T.let(
79
+ [@root],
80
+ T::Array[T.any(SymbolHierarchyRoot, LanguageServer::Protocol::Interface::DocumentSymbol)]
81
+ )
50
82
  end
51
83
 
84
+ sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol], Object)) }
52
85
  def run
53
86
  visit(@document.tree)
54
87
  @root.children
55
88
  end
56
89
 
90
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
57
91
  def visit_class(node)
58
92
  symbol = create_document_symbol(
59
- name: node.constant.constant.value,
93
+ name: fully_qualified_name(node),
60
94
  kind: :class,
61
95
  range_node: node,
62
96
  selection_range_node: node.constant
@@ -67,6 +101,7 @@ module RubyLsp
67
101
  @stack.pop
68
102
  end
69
103
 
104
+ sig { params(node: SyntaxTree::Command).void }
70
105
  def visit_command(node)
71
106
  return unless ATTR_ACCESSORS.include?(node.message.value)
72
107
 
@@ -82,6 +117,7 @@ module RubyLsp
82
117
  end
83
118
  end
84
119
 
120
+ sig { params(node: SyntaxTree::ConstPathField).void }
85
121
  def visit_const_path_field(node)
86
122
  create_document_symbol(
87
123
  name: node.constant.value,
@@ -91,6 +127,7 @@ module RubyLsp
91
127
  )
92
128
  end
93
129
 
130
+ sig { params(node: SyntaxTree::Def).void }
94
131
  def visit_def(node)
95
132
  name = node.name.value
96
133
 
@@ -106,6 +143,7 @@ module RubyLsp
106
143
  @stack.pop
107
144
  end
108
145
 
146
+ sig { params(node: SyntaxTree::DefEndless).void }
109
147
  def visit_def_endless(node)
110
148
  name = node.name.value
111
149
 
@@ -121,6 +159,7 @@ module RubyLsp
121
159
  @stack.pop
122
160
  end
123
161
 
162
+ sig { params(node: SyntaxTree::Defs).void }
124
163
  def visit_defs(node)
125
164
  symbol = create_document_symbol(
126
165
  name: "self.#{node.name.value}",
@@ -134,9 +173,10 @@ module RubyLsp
134
173
  @stack.pop
135
174
  end
136
175
 
176
+ sig { params(node: SyntaxTree::ModuleDeclaration).void }
137
177
  def visit_module(node)
138
178
  symbol = create_document_symbol(
139
- name: node.constant.constant.value,
179
+ name: fully_qualified_name(node),
140
180
  kind: :module,
141
181
  range_node: node,
142
182
  selection_range_node: node.constant
@@ -147,6 +187,7 @@ module RubyLsp
147
187
  @stack.pop
148
188
  end
149
189
 
190
+ sig { params(node: SyntaxTree::TopConstField).void }
150
191
  def visit_top_const_field(node)
151
192
  create_document_symbol(
152
193
  name: node.constant.value,
@@ -156,6 +197,7 @@ module RubyLsp
156
197
  )
157
198
  end
158
199
 
200
+ sig { params(node: SyntaxTree::VarField).void }
159
201
  def visit_var_field(node)
160
202
  kind = case node.value
161
203
  when SyntaxTree::Const
@@ -176,6 +218,14 @@ module RubyLsp
176
218
 
177
219
  private
178
220
 
221
+ sig do
222
+ params(
223
+ name: String,
224
+ kind: Symbol,
225
+ range_node: SyntaxTree::Node,
226
+ selection_range_node: SyntaxTree::Node
227
+ ).returns(LanguageServer::Protocol::Interface::DocumentSymbol)
228
+ end
179
229
  def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
180
230
  symbol = LanguageServer::Protocol::Interface::DocumentSymbol.new(
181
231
  name: name,
@@ -185,19 +235,28 @@ module RubyLsp
185
235
  children: [],
186
236
  )
187
237
 
188
- @stack.last.children << symbol
238
+ T.must(@stack.last).children << symbol
189
239
 
190
240
  symbol
191
241
  end
192
242
 
193
- def range_from_syntax_tree_node(node)
194
- loc = node.location
243
+ sig { params(node: T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)).returns(String) }
244
+ def fully_qualified_name(node)
245
+ constant = T.let(node.constant, SyntaxTree::Node)
246
+ name = +node.constant.constant.value
195
247
 
196
- LanguageServer::Protocol::Interface::Range.new(
197
- start: LanguageServer::Protocol::Interface::Position.new(line: loc.start_line - 1,
198
- character: loc.start_column),
199
- end: LanguageServer::Protocol::Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
200
- )
248
+ while constant.is_a?(SyntaxTree::ConstPathRef)
249
+ constant = constant.parent
250
+
251
+ case constant
252
+ when SyntaxTree::ConstPathRef
253
+ name.prepend("#{constant.constant.value}::")
254
+ when SyntaxTree::VarRef
255
+ name.prepend("#{constant.value.value}::")
256
+ end
257
+ end
258
+
259
+ name
201
260
  end
202
261
  end
203
262
  end
@@ -1,9 +1,21 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
6
+ # 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.
8
+ #
9
+ # # Example
10
+ # ```ruby
11
+ # def say_hello # <-- folding range start
12
+ # puts "Hello"
13
+ # end # <-- folding range end
14
+ # ```
5
15
  class FoldingRanges < BaseRequest
6
- SIMPLE_FOLDABLES = [
16
+ extend T::Sig
17
+
18
+ SIMPLE_FOLDABLES = T.let([
7
19
  SyntaxTree::ArrayLiteral,
8
20
  SyntaxTree::BraceBlock,
9
21
  SyntaxTree::Case,
@@ -20,24 +32,37 @@ module RubyLsp
20
32
  SyntaxTree::Unless,
21
33
  SyntaxTree::Until,
22
34
  SyntaxTree::While,
23
- ].freeze
35
+ ].freeze, T::Array[T.class_of(SyntaxTree::Node)])
24
36
 
25
- NODES_WITH_STATEMENTS = [
37
+ NODES_WITH_STATEMENTS = T.let([
26
38
  SyntaxTree::Else,
27
39
  SyntaxTree::Elsif,
28
40
  SyntaxTree::Ensure,
29
41
  SyntaxTree::In,
30
42
  SyntaxTree::Rescue,
31
43
  SyntaxTree::When,
32
- ].freeze
44
+ ].freeze, T::Array[T.class_of(SyntaxTree::Node)])
45
+
46
+ StatementNode = T.type_alias do
47
+ T.any(
48
+ SyntaxTree::Else,
49
+ SyntaxTree::Elsif,
50
+ SyntaxTree::Ensure,
51
+ SyntaxTree::In,
52
+ SyntaxTree::Rescue,
53
+ SyntaxTree::When,
54
+ )
55
+ end
33
56
 
57
+ sig { params(document: Document).void }
34
58
  def initialize(document)
35
59
  super
36
60
 
37
- @ranges = []
38
- @partial_range = nil
61
+ @ranges = T.let([], T::Array[LanguageServer::Protocol::Interface::FoldingRange])
62
+ @partial_range = T.let(nil, T.nilable(PartialRange))
39
63
  end
40
64
 
65
+ sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::FoldingRange], Object)) }
41
66
  def run
42
67
  visit(@document.tree)
43
68
  emit_partial_range
@@ -46,14 +71,15 @@ module RubyLsp
46
71
 
47
72
  private
48
73
 
74
+ sig { params(node: T.nilable(SyntaxTree::Node)).void }
49
75
  def visit(node)
50
76
  return unless handle_partial_range(node)
51
77
 
52
78
  case node
53
79
  when *SIMPLE_FOLDABLES
54
- add_node_range(node)
80
+ add_node_range(T.must(node))
55
81
  when *NODES_WITH_STATEMENTS
56
- add_statements_range(node, node.statements)
82
+ add_statements_range(T.must(node), T.cast(node, StatementNode).statements)
57
83
  when SyntaxTree::Begin
58
84
  add_statements_range(node, node.bodystmt.statements)
59
85
  when SyntaxTree::Call, SyntaxTree::CommandCall
@@ -70,27 +96,38 @@ module RubyLsp
70
96
  end
71
97
 
72
98
  class PartialRange
73
- attr_reader :kind, :end_line
99
+ extend T::Sig
74
100
 
101
+ sig { returns(String) }
102
+ attr_reader :kind
103
+
104
+ sig { returns(Integer) }
105
+ attr_reader :end_line
106
+
107
+ sig { params(node: SyntaxTree::Node, kind: String).returns(PartialRange) }
75
108
  def self.from(node, kind)
76
109
  new(node.location.start_line - 1, node.location.end_line - 1, kind)
77
110
  end
78
111
 
112
+ sig { params(start_line: Integer, end_line: Integer, kind: String).void }
79
113
  def initialize(start_line, end_line, kind)
80
114
  @start_line = start_line
81
115
  @end_line = end_line
82
116
  @kind = kind
83
117
  end
84
118
 
119
+ sig { params(node: SyntaxTree::Node).returns(PartialRange) }
85
120
  def extend_to(node)
86
121
  @end_line = node.location.end_line - 1
87
122
  self
88
123
  end
89
124
 
125
+ sig { params(node: SyntaxTree::Node).returns(T::Boolean) }
90
126
  def new_section?(node)
91
127
  node.is_a?(SyntaxTree::Comment) && @end_line + 1 != node.location.start_line - 1
92
128
  end
93
129
 
130
+ sig { returns(LanguageServer::Protocol::Interface::FoldingRange) }
94
131
  def to_range
95
132
  LanguageServer::Protocol::Interface::FoldingRange.new(
96
133
  start_line: @start_line,
@@ -100,6 +137,7 @@ module RubyLsp
100
137
  end
101
138
  end
102
139
 
140
+ sig { params(node: T.nilable(SyntaxTree::Node)).returns(T::Boolean) }
103
141
  def handle_partial_range(node)
104
142
  kind = partial_range_kind(node)
105
143
 
@@ -108,18 +146,20 @@ module RubyLsp
108
146
  return true
109
147
  end
110
148
 
149
+ target_node = T.must(node)
111
150
  @partial_range = if @partial_range.nil?
112
- PartialRange.from(node, kind)
113
- elsif @partial_range.kind != kind || @partial_range.new_section?(node)
151
+ PartialRange.from(target_node, kind)
152
+ elsif @partial_range.kind != kind || @partial_range.new_section?(target_node)
114
153
  emit_partial_range
115
- PartialRange.from(node, kind)
154
+ PartialRange.from(target_node, kind)
116
155
  else
117
- @partial_range.extend_to(node)
156
+ @partial_range.extend_to(target_node)
118
157
  end
119
158
 
120
159
  false
121
160
  end
122
161
 
162
+ sig { params(node: T.nilable(SyntaxTree::Node)).returns(T.nilable(String)) }
123
163
  def partial_range_kind(node)
124
164
  case node
125
165
  when SyntaxTree::Comment
@@ -131,6 +171,7 @@ module RubyLsp
131
171
  end
132
172
  end
133
173
 
174
+ sig { void }
134
175
  def emit_partial_range
135
176
  return if @partial_range.nil?
136
177
 
@@ -138,8 +179,9 @@ module RubyLsp
138
179
  @partial_range = nil
139
180
  end
140
181
 
182
+ sig { params(node: T.any(SyntaxTree::Call, SyntaxTree::CommandCall)).void }
141
183
  def add_call_range(node)
142
- receiver = node.receiver
184
+ receiver = T.let(node.receiver, SyntaxTree::Node)
143
185
  loop do
144
186
  case receiver
145
187
  when SyntaxTree::Call
@@ -158,6 +200,7 @@ module RubyLsp
158
200
  visit(node.arguments)
159
201
  end
160
202
 
203
+ sig { params(node: T.any(SyntaxTree::Def, SyntaxTree::Defs)).void }
161
204
  def add_def_range(node)
162
205
  params_location = node.params.location
163
206
 
@@ -170,25 +213,26 @@ module RubyLsp
170
213
  visit(node.bodystmt.statements)
171
214
  end
172
215
 
216
+ sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
173
217
  def add_statements_range(node, statements)
174
218
  add_lines_range(node.location.start_line, statements.location.end_line) unless statements.empty?
175
219
  end
176
220
 
221
+ sig { params(node: SyntaxTree::StringConcat).void }
177
222
  def add_string_concat(node)
178
- left = node.left
223
+ left = T.let(node.left, SyntaxTree::Node)
179
224
  left = left.left while left.is_a?(SyntaxTree::StringConcat)
180
225
 
181
226
  add_lines_range(left.location.start_line, node.right.location.end_line)
182
227
  end
183
228
 
229
+ sig { params(node: SyntaxTree::Node).void }
184
230
  def add_node_range(node)
185
- add_location_range(node.location)
186
- end
187
-
188
- def add_location_range(location)
231
+ location = node.location
189
232
  add_lines_range(location.start_line, location.end_line)
190
233
  end
191
234
 
235
+ sig { params(start_line: Integer, end_line: Integer).void }
192
236
  def add_lines_range(start_line, end_line)
193
237
  return if start_line >= end_line
194
238
 
@@ -1,15 +1,31 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module RubyLsp
4
5
  module Requests
6
+ # The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
7
+ # request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
8
+ # registering the ruby-lsp as the Ruby formatter.
9
+ #
10
+ # # Example
11
+ #
12
+ # ```ruby
13
+ # def say_hello
14
+ # puts "Hello" # --> formatting: fixes the indentation on save
15
+ # end
16
+ # ```
5
17
  class Formatting < RuboCopRequest
6
- RUBOCOP_FLAGS = (COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze
18
+ extend T::Sig
7
19
 
20
+ RUBOCOP_FLAGS = T.let((COMMON_RUBOCOP_FLAGS + ["--auto-correct"]).freeze, T::Array[String])
21
+
22
+ sig { params(uri: String, document: Document).void }
8
23
  def initialize(uri, document)
9
24
  super
10
- @formatted_text = nil
25
+ @formatted_text = T.let(nil, T.nilable(String))
11
26
  end
12
27
 
28
+ sig { override.returns(T.nilable(T.all(T::Array[LanguageServer::Protocol::Interface::TextEdit], Object))) }
13
29
  def run
14
30
  super
15
31
 
@@ -32,6 +48,7 @@ module RubyLsp
32
48
 
33
49
  private
34
50
 
51
+ sig { returns(T::Array[String]) }
35
52
  def rubocop_flags
36
53
  RUBOCOP_FLAGS
37
54
  end
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "rubocop"
@@ -5,23 +6,33 @@ require "cgi"
5
6
 
6
7
  module RubyLsp
7
8
  module Requests
9
+ # :nodoc:
8
10
  class RuboCopRequest < RuboCop::Runner
9
- COMMON_RUBOCOP_FLAGS = [
11
+ extend T::Sig
12
+ extend T::Helpers
13
+
14
+ abstract!
15
+
16
+ COMMON_RUBOCOP_FLAGS = T.let([
10
17
  "--stderr", # Print any output to stderr so that our stdout does not get polluted
11
18
  "--format",
12
19
  "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
13
- ].freeze
20
+ ].freeze, T::Array[String])
14
21
 
15
- attr_reader :file, :text
22
+ sig { returns(String) }
23
+ attr_reader :file
16
24
 
17
- def self.run(uri, document)
18
- new(uri, document).run
19
- end
25
+ sig { returns(String) }
26
+ attr_reader :text
20
27
 
28
+ sig { params(uri: String, document: Document).void }
21
29
  def initialize(uri, document)
22
- @file = CGI.unescape(URI.parse(uri).path)
23
- @text = document.source
30
+ @file = T.let(CGI.unescape(URI.parse(uri).path), String)
31
+ @document = document
32
+ @text = T.let(document.source, String)
24
33
  @uri = uri
34
+ @options = T.let({}, T::Hash[Symbol, T.untyped])
35
+ @diagnostics = T.let([], T::Array[Support::RuboCopDiagnostic])
25
36
 
26
37
  super(
27
38
  ::RuboCop::Options.new.parse(rubocop_flags).first,
@@ -29,6 +40,7 @@ module RubyLsp
29
40
  )
30
41
  end
31
42
 
43
+ sig { overridable.returns(Object) }
32
44
  def run
33
45
  # We communicate with Rubocop via stdin
34
46
  @options[:stdin] = text
@@ -39,6 +51,7 @@ module RubyLsp
39
51
 
40
52
  private
41
53
 
54
+ sig { returns(T::Array[String]) }
42
55
  def rubocop_flags
43
56
  COMMON_RUBOCOP_FLAGS
44
57
  end
@@ -0,0 +1,114 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
7
+ # request informs the editor of ranges that the user may want to select based on the location(s)
8
+ # of their cursor(s).
9
+ #
10
+ # Trigger this request with: Ctrl + Shift + -> or Ctrl + Shift + <-
11
+ #
12
+ # # Example
13
+ #
14
+ # ```ruby
15
+ # def foo # --> The next selection range encompasses the entire method definition.
16
+ # puts "Hello, world!" # --> Cursor is on this line
17
+ # end
18
+ # ```
19
+ class SelectionRanges < BaseRequest
20
+ extend T::Sig
21
+
22
+ NODES_THAT_CAN_BE_PARENTS = T.let([
23
+ SyntaxTree::Assign,
24
+ SyntaxTree::ArrayLiteral,
25
+ SyntaxTree::Begin,
26
+ SyntaxTree::BraceBlock,
27
+ SyntaxTree::Call,
28
+ SyntaxTree::Case,
29
+ SyntaxTree::ClassDeclaration,
30
+ SyntaxTree::Command,
31
+ SyntaxTree::Def,
32
+ SyntaxTree::Defs,
33
+ SyntaxTree::DoBlock,
34
+ SyntaxTree::Elsif,
35
+ SyntaxTree::Else,
36
+ SyntaxTree::EmbDoc,
37
+ SyntaxTree::Ensure,
38
+ SyntaxTree::FCall,
39
+ SyntaxTree::For,
40
+ SyntaxTree::HashLiteral,
41
+ SyntaxTree::Heredoc,
42
+ SyntaxTree::HeredocBeg,
43
+ SyntaxTree::HshPtn,
44
+ SyntaxTree::If,
45
+ SyntaxTree::In,
46
+ SyntaxTree::Lambda,
47
+ SyntaxTree::MethodAddBlock,
48
+ SyntaxTree::ModuleDeclaration,
49
+ SyntaxTree::Params,
50
+ SyntaxTree::Rescue,
51
+ SyntaxTree::RescueEx,
52
+ SyntaxTree::StringConcat,
53
+ SyntaxTree::StringLiteral,
54
+ SyntaxTree::Unless,
55
+ SyntaxTree::Until,
56
+ SyntaxTree::VCall,
57
+ SyntaxTree::When,
58
+ SyntaxTree::While,
59
+ ].freeze, T::Array[T.class_of(SyntaxTree::Node)])
60
+
61
+ sig { params(document: Document).void }
62
+ def initialize(document)
63
+ super(document)
64
+
65
+ @ranges = T.let([], T::Array[Support::SelectionRange])
66
+ @stack = T.let([], T::Array[Support::SelectionRange])
67
+ end
68
+
69
+ sig { override.returns(T.all(T::Array[Support::SelectionRange], Object)) }
70
+ def run
71
+ visit(@document.tree)
72
+ @ranges.reverse!
73
+ end
74
+
75
+ private
76
+
77
+ sig { params(node: T.nilable(SyntaxTree::Node)).void }
78
+ def visit(node)
79
+ return if node.nil?
80
+
81
+ range = create_selection_range(node.location, @stack.last)
82
+
83
+ @ranges << range
84
+ return if node.child_nodes.empty?
85
+
86
+ @stack << range if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
87
+ visit_all(node.child_nodes)
88
+ @stack.pop if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
89
+ end
90
+
91
+ sig do
92
+ params(
93
+ location: SyntaxTree::Location,
94
+ parent: T.nilable(Support::SelectionRange)
95
+ ).returns(Support::SelectionRange)
96
+ end
97
+ def create_selection_range(location, parent = nil)
98
+ RubyLsp::Requests::Support::SelectionRange.new(
99
+ range: LanguageServer::Protocol::Interface::Range.new(
100
+ start: LanguageServer::Protocol::Interface::Position.new(
101
+ line: location.start_line - 1,
102
+ character: location.start_column,
103
+ ),
104
+ end: LanguageServer::Protocol::Interface::Position.new(
105
+ line: location.end_line - 1,
106
+ character: location.end_column,
107
+ ),
108
+ ),
109
+ parent: parent
110
+ )
111
+ end
112
+ end
113
+ end
114
+ end