ruby-lsp 0.0.2 → 0.1.0

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