ruby-lsp 0.0.1 → 0.0.4

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +7 -16
  3. data/.github/pull_request_template.md +15 -0
  4. data/.github/workflows/ci.yml +31 -0
  5. data/.github/workflows/publish_docs.yml +32 -0
  6. data/.gitignore +9 -12
  7. data/.rubocop.yml +20 -2
  8. data/.vscode/settings.json +5 -0
  9. data/CHANGELOG.md +29 -0
  10. data/Gemfile +8 -4
  11. data/Gemfile.lock +76 -14
  12. data/README.md +69 -2
  13. data/Rakefile +5 -0
  14. data/VERSION +1 -1
  15. data/bin/tapioca +29 -0
  16. data/bin/test +7 -1
  17. data/dev.yml +7 -7
  18. data/exe/ruby-lsp +19 -2
  19. data/lib/internal.rb +7 -0
  20. data/lib/ruby-lsp.rb +4 -1
  21. data/lib/ruby_lsp/cli.rb +88 -0
  22. data/lib/ruby_lsp/document.rb +113 -0
  23. data/lib/ruby_lsp/handler.rb +236 -0
  24. data/lib/ruby_lsp/requests/base_request.rb +33 -0
  25. data/lib/ruby_lsp/requests/code_actions.rb +37 -0
  26. data/lib/ruby_lsp/requests/diagnostics.rb +37 -0
  27. data/lib/ruby_lsp/requests/document_highlight.rb +96 -0
  28. data/lib/ruby_lsp/requests/document_symbol.rb +216 -0
  29. data/lib/ruby_lsp/requests/folding_ranges.rb +213 -0
  30. data/lib/ruby_lsp/requests/formatting.rb +52 -0
  31. data/lib/ruby_lsp/requests/rubocop_request.rb +50 -0
  32. data/lib/ruby_lsp/requests/selection_ranges.rb +103 -0
  33. data/lib/ruby_lsp/requests/semantic_highlighting.rb +112 -0
  34. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +88 -0
  35. data/lib/ruby_lsp/requests/support/selection_range.rb +17 -0
  36. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +60 -0
  37. data/lib/ruby_lsp/requests/support/syntax_error_diagnostic.rb +27 -0
  38. data/lib/ruby_lsp/requests.rb +24 -0
  39. data/lib/ruby_lsp/store.rb +59 -0
  40. data/rakelib/check_docs.rake +56 -0
  41. data/ruby-lsp.gemspec +5 -1
  42. data/{shipit.yml → shipit.production.yml} +0 -0
  43. data/sorbet/config +4 -0
  44. data/sorbet/rbi/.rubocop.yml +8 -0
  45. data/sorbet/rbi/gems/ansi@1.5.0.rbi +338 -0
  46. data/sorbet/rbi/gems/ast@2.4.2.rbi +522 -0
  47. data/sorbet/rbi/gems/builder@3.2.4.rbi +418 -0
  48. data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
  49. data/sorbet/rbi/gems/debug@1.5.0.rbi +1273 -0
  50. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +867 -0
  51. data/sorbet/rbi/gems/io-console@0.5.11.rbi +8 -0
  52. data/sorbet/rbi/gems/irb@1.4.1.rbi +376 -0
  53. data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +7325 -0
  54. data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
  55. data/sorbet/rbi/gems/minitest-reporters@1.5.0.rbi +612 -0
  56. data/sorbet/rbi/gems/minitest@5.15.0.rbi +994 -0
  57. data/sorbet/rbi/gems/parallel@1.22.1.rbi +163 -0
  58. data/sorbet/rbi/gems/parser@3.1.2.0.rbi +3968 -0
  59. data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +734 -0
  60. data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
  61. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +227 -0
  62. data/sorbet/rbi/gems/rake@13.0.6.rbi +1853 -0
  63. data/sorbet/rbi/gems/rbi@0.0.14.rbi +2337 -0
  64. data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +1854 -0
  65. data/sorbet/rbi/gems/reline@0.3.1.rbi +1274 -0
  66. data/sorbet/rbi/gems/rexml@3.2.5.rbi +3852 -0
  67. data/sorbet/rbi/gems/rubocop-ast@1.18.0.rbi +4180 -0
  68. data/sorbet/rbi/gems/rubocop-minitest@0.20.0.rbi +1369 -0
  69. data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +246 -0
  70. data/sorbet/rbi/gems/rubocop-shopify@2.6.0.rbi +8 -0
  71. data/sorbet/rbi/gems/rubocop-sorbet@0.6.8.rbi +652 -0
  72. data/sorbet/rbi/gems/rubocop@1.30.0.rbi +36729 -0
  73. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +732 -0
  74. data/sorbet/rbi/gems/spoom@1.1.11.rbi +1600 -0
  75. data/sorbet/rbi/gems/syntax_tree@2.7.1.rbi +6777 -0
  76. data/sorbet/rbi/gems/tapioca@0.8.1.rbi +1972 -0
  77. data/sorbet/rbi/gems/thor@1.2.1.rbi +2921 -0
  78. data/sorbet/rbi/gems/unicode-display_width@2.1.0.rbi +27 -0
  79. data/sorbet/rbi/gems/unparser@0.6.5.rbi +2789 -0
  80. data/sorbet/rbi/gems/webrick@1.7.0.rbi +1779 -0
  81. data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +289 -0
  82. data/sorbet/rbi/gems/yard@0.9.27.rbi +13048 -0
  83. data/sorbet/rbi/shims/fiddle.rbi +4 -0
  84. data/sorbet/rbi/shims/hash.rbi +6 -0
  85. data/sorbet/rbi/shims/rdoc.rbi +4 -0
  86. data/sorbet/tapioca/config.yml +13 -0
  87. data/sorbet/tapioca/require.rb +7 -0
  88. metadata +119 -9
  89. data/.vscode/launch.json +0 -19
  90. data/bin/package_extension +0 -5
  91. data/bin/style +0 -10
  92. data/lib/ruby/lsp/cli.rb +0 -37
  93. data/lib/ruby/lsp.rb +0 -3
@@ -0,0 +1,216 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
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
+ # ```
27
+ class DocumentSymbol < BaseRequest
28
+ SYMBOL_KIND = {
29
+ file: 1,
30
+ module: 2,
31
+ namespace: 3,
32
+ package: 4,
33
+ class: 5,
34
+ method: 6,
35
+ property: 7,
36
+ field: 8,
37
+ constructor: 9,
38
+ enum: 10,
39
+ interface: 11,
40
+ function: 12,
41
+ variable: 13,
42
+ constant: 14,
43
+ string: 15,
44
+ number: 16,
45
+ boolean: 17,
46
+ array: 18,
47
+ object: 19,
48
+ key: 20,
49
+ null: 21,
50
+ enummember: 22,
51
+ struct: 23,
52
+ event: 24,
53
+ operator: 25,
54
+ typeparameter: 26,
55
+ }.freeze
56
+
57
+ ATTR_ACCESSORS = ["attr_reader", "attr_writer", "attr_accessor"].freeze
58
+
59
+ class SymbolHierarchyRoot
60
+ attr_reader :children
61
+
62
+ def initialize
63
+ @children = []
64
+ end
65
+ end
66
+
67
+ def initialize(document)
68
+ super
69
+
70
+ @root = SymbolHierarchyRoot.new
71
+ @stack = [@root]
72
+ end
73
+
74
+ def run
75
+ visit(@document.tree)
76
+ @root.children
77
+ end
78
+
79
+ def visit_class(node)
80
+ symbol = create_document_symbol(
81
+ name: node.constant.constant.value,
82
+ kind: :class,
83
+ range_node: node,
84
+ selection_range_node: node.constant
85
+ )
86
+
87
+ @stack << symbol
88
+ visit(node.bodystmt)
89
+ @stack.pop
90
+ end
91
+
92
+ def visit_command(node)
93
+ return unless ATTR_ACCESSORS.include?(node.message.value)
94
+
95
+ node.arguments.parts.each do |argument|
96
+ next unless argument.is_a?(SyntaxTree::SymbolLiteral)
97
+
98
+ create_document_symbol(
99
+ name: argument.value.value,
100
+ kind: :field,
101
+ range_node: argument,
102
+ selection_range_node: argument.value
103
+ )
104
+ end
105
+ end
106
+
107
+ def visit_const_path_field(node)
108
+ create_document_symbol(
109
+ name: node.constant.value,
110
+ kind: :constant,
111
+ range_node: node,
112
+ selection_range_node: node.constant
113
+ )
114
+ end
115
+
116
+ def visit_def(node)
117
+ name = node.name.value
118
+
119
+ symbol = create_document_symbol(
120
+ name: name,
121
+ kind: name == "initialize" ? :constructor : :method,
122
+ range_node: node,
123
+ selection_range_node: node.name
124
+ )
125
+
126
+ @stack << symbol
127
+ visit(node.bodystmt)
128
+ @stack.pop
129
+ end
130
+
131
+ def visit_def_endless(node)
132
+ name = node.name.value
133
+
134
+ symbol = create_document_symbol(
135
+ name: name,
136
+ kind: name == "initialize" ? :constructor : :method,
137
+ range_node: node,
138
+ selection_range_node: node.name
139
+ )
140
+
141
+ @stack << symbol
142
+ visit(node.statement)
143
+ @stack.pop
144
+ end
145
+
146
+ def visit_defs(node)
147
+ symbol = create_document_symbol(
148
+ name: "self.#{node.name.value}",
149
+ kind: :method,
150
+ range_node: node,
151
+ selection_range_node: node.name
152
+ )
153
+
154
+ @stack << symbol
155
+ visit(node.bodystmt)
156
+ @stack.pop
157
+ end
158
+
159
+ def visit_module(node)
160
+ symbol = create_document_symbol(
161
+ name: node.constant.constant.value,
162
+ kind: :module,
163
+ range_node: node,
164
+ selection_range_node: node.constant
165
+ )
166
+
167
+ @stack << symbol
168
+ visit(node.bodystmt)
169
+ @stack.pop
170
+ end
171
+
172
+ def visit_top_const_field(node)
173
+ create_document_symbol(
174
+ name: node.constant.value,
175
+ kind: :constant,
176
+ range_node: node,
177
+ selection_range_node: node.constant
178
+ )
179
+ end
180
+
181
+ def visit_var_field(node)
182
+ kind = case node.value
183
+ when SyntaxTree::Const
184
+ :constant
185
+ when SyntaxTree::CVar, SyntaxTree::IVar
186
+ :variable
187
+ else
188
+ return
189
+ end
190
+
191
+ create_document_symbol(
192
+ name: node.value.value,
193
+ kind: kind,
194
+ range_node: node,
195
+ selection_range_node: node.value
196
+ )
197
+ end
198
+
199
+ private
200
+
201
+ def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
202
+ symbol = LanguageServer::Protocol::Interface::DocumentSymbol.new(
203
+ name: name,
204
+ kind: SYMBOL_KIND[kind],
205
+ range: range_from_syntax_tree_node(range_node),
206
+ selection_range: range_from_syntax_tree_node(selection_range_node),
207
+ children: [],
208
+ )
209
+
210
+ @stack.last.children << symbol
211
+
212
+ symbol
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,213 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
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
+ # ```
15
+ class FoldingRanges < BaseRequest
16
+ SIMPLE_FOLDABLES = [
17
+ SyntaxTree::ArrayLiteral,
18
+ SyntaxTree::BraceBlock,
19
+ SyntaxTree::Case,
20
+ SyntaxTree::ClassDeclaration,
21
+ SyntaxTree::Command,
22
+ SyntaxTree::DoBlock,
23
+ SyntaxTree::FCall,
24
+ SyntaxTree::For,
25
+ SyntaxTree::HashLiteral,
26
+ SyntaxTree::Heredoc,
27
+ SyntaxTree::If,
28
+ SyntaxTree::ModuleDeclaration,
29
+ SyntaxTree::SClass,
30
+ SyntaxTree::Unless,
31
+ SyntaxTree::Until,
32
+ SyntaxTree::While,
33
+ ].freeze
34
+
35
+ NODES_WITH_STATEMENTS = [
36
+ SyntaxTree::Else,
37
+ SyntaxTree::Elsif,
38
+ SyntaxTree::Ensure,
39
+ SyntaxTree::In,
40
+ SyntaxTree::Rescue,
41
+ SyntaxTree::When,
42
+ ].freeze
43
+
44
+ def initialize(document)
45
+ super
46
+
47
+ @ranges = []
48
+ @partial_range = nil
49
+ end
50
+
51
+ def run
52
+ visit(@document.tree)
53
+ emit_partial_range
54
+ @ranges
55
+ end
56
+
57
+ private
58
+
59
+ def visit(node)
60
+ return unless handle_partial_range(node)
61
+
62
+ case node
63
+ when *SIMPLE_FOLDABLES
64
+ add_node_range(node)
65
+ when *NODES_WITH_STATEMENTS
66
+ add_statements_range(node, node.statements)
67
+ when SyntaxTree::Begin
68
+ add_statements_range(node, node.bodystmt.statements)
69
+ when SyntaxTree::Call, SyntaxTree::CommandCall
70
+ add_call_range(node)
71
+ return
72
+ when SyntaxTree::Def, SyntaxTree::Defs
73
+ add_def_range(node)
74
+ when SyntaxTree::StringConcat
75
+ add_string_concat(node)
76
+ return
77
+ end
78
+
79
+ super
80
+ end
81
+
82
+ class PartialRange
83
+ attr_reader :kind, :end_line
84
+
85
+ def self.from(node, kind)
86
+ new(node.location.start_line - 1, node.location.end_line - 1, kind)
87
+ end
88
+
89
+ def initialize(start_line, end_line, kind)
90
+ @start_line = start_line
91
+ @end_line = end_line
92
+ @kind = kind
93
+ end
94
+
95
+ def extend_to(node)
96
+ @end_line = node.location.end_line - 1
97
+ self
98
+ end
99
+
100
+ def new_section?(node)
101
+ node.is_a?(SyntaxTree::Comment) && @end_line + 1 != node.location.start_line - 1
102
+ end
103
+
104
+ def to_range
105
+ LanguageServer::Protocol::Interface::FoldingRange.new(
106
+ start_line: @start_line,
107
+ end_line: @end_line,
108
+ kind: @kind
109
+ )
110
+ end
111
+ end
112
+
113
+ def handle_partial_range(node)
114
+ kind = partial_range_kind(node)
115
+
116
+ if kind.nil?
117
+ emit_partial_range
118
+ return true
119
+ end
120
+
121
+ @partial_range = if @partial_range.nil?
122
+ PartialRange.from(node, kind)
123
+ elsif @partial_range.kind != kind || @partial_range.new_section?(node)
124
+ emit_partial_range
125
+ PartialRange.from(node, kind)
126
+ else
127
+ @partial_range.extend_to(node)
128
+ end
129
+
130
+ false
131
+ end
132
+
133
+ def partial_range_kind(node)
134
+ case node
135
+ when SyntaxTree::Comment
136
+ "comment"
137
+ when SyntaxTree::Command
138
+ if node.message.value == "require" || node.message.value == "require_relative"
139
+ "imports"
140
+ end
141
+ end
142
+ end
143
+
144
+ def emit_partial_range
145
+ return if @partial_range.nil?
146
+
147
+ @ranges << @partial_range.to_range
148
+ @partial_range = nil
149
+ end
150
+
151
+ def add_call_range(node)
152
+ receiver = T.let(node.receiver, SyntaxTree::Node)
153
+ loop do
154
+ case receiver
155
+ when SyntaxTree::Call
156
+ visit(receiver.arguments)
157
+ receiver = receiver.receiver
158
+ when SyntaxTree::MethodAddBlock
159
+ visit(receiver.block)
160
+ receiver = receiver.call.receiver
161
+ else
162
+ break
163
+ end
164
+ end
165
+
166
+ add_lines_range(receiver.location.start_line, node.location.end_line)
167
+
168
+ visit(node.arguments)
169
+ end
170
+
171
+ def add_def_range(node)
172
+ params_location = node.params.location
173
+
174
+ if params_location.start_line < params_location.end_line
175
+ add_lines_range(params_location.end_line, node.location.end_line)
176
+ else
177
+ add_node_range(node)
178
+ end
179
+
180
+ visit(node.bodystmt.statements)
181
+ end
182
+
183
+ def add_statements_range(node, statements)
184
+ add_lines_range(node.location.start_line, statements.location.end_line) unless statements.empty?
185
+ end
186
+
187
+ def add_string_concat(node)
188
+ left = T.let(node.left, SyntaxTree::Node)
189
+ left = left.left while left.is_a?(SyntaxTree::StringConcat)
190
+
191
+ add_lines_range(left.location.start_line, node.right.location.end_line)
192
+ end
193
+
194
+ def add_node_range(node)
195
+ add_location_range(node.location)
196
+ end
197
+
198
+ def add_location_range(location)
199
+ add_lines_range(location.start_line, location.end_line)
200
+ end
201
+
202
+ def add_lines_range(start_line, end_line)
203
+ return if start_line >= end_line
204
+
205
+ @ranges << LanguageServer::Protocol::Interface::FoldingRange.new(
206
+ start_line: start_line - 1,
207
+ end_line: end_line - 1,
208
+ kind: "region"
209
+ )
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,52 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
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
+ # ```
17
+ class Formatting < RuboCopRequest
18
+ RUBOCOP_FLAGS = (COMMON_RUBOCOP_FLAGS + ["--autocorrect"]).freeze
19
+
20
+ def initialize(uri, document)
21
+ super
22
+ @formatted_text = nil
23
+ end
24
+
25
+ def run
26
+ super
27
+
28
+ @formatted_text = @options[:stdin] # Rubocop applies the corrections on stdin
29
+ return unless @formatted_text
30
+
31
+ [
32
+ LanguageServer::Protocol::Interface::TextEdit.new(
33
+ range: LanguageServer::Protocol::Interface::Range.new(
34
+ start: LanguageServer::Protocol::Interface::Position.new(line: 0, character: 0),
35
+ end: LanguageServer::Protocol::Interface::Position.new(
36
+ line: text.size,
37
+ character: text.size
38
+ )
39
+ ),
40
+ new_text: @formatted_text
41
+ ),
42
+ ]
43
+ end
44
+
45
+ private
46
+
47
+ def rubocop_flags
48
+ RUBOCOP_FLAGS
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,50 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "rubocop"
5
+ require "cgi"
6
+
7
+ module RubyLsp
8
+ module Requests
9
+ # :nodoc:
10
+ class RuboCopRequest < RuboCop::Runner
11
+ COMMON_RUBOCOP_FLAGS = [
12
+ "--stderr", # Print any output to stderr so that our stdout does not get polluted
13
+ "--format",
14
+ "RuboCop::Formatter::BaseFormatter", # Suppress any output by using the base formatter
15
+ ].freeze
16
+
17
+ attr_reader :file, :text
18
+
19
+ def self.run(uri, document)
20
+ new(uri, document).run
21
+ end
22
+
23
+ def initialize(uri, document)
24
+ @file = CGI.unescape(URI.parse(uri).path)
25
+ @document = document
26
+ @text = document.source
27
+ @uri = uri
28
+
29
+ super(
30
+ ::RuboCop::Options.new.parse(rubocop_flags).first,
31
+ ::RuboCop::ConfigStore.new
32
+ )
33
+ end
34
+
35
+ def run
36
+ # We communicate with Rubocop via stdin
37
+ @options[:stdin] = text
38
+
39
+ # Invoke the actual run method with just this file in `paths`
40
+ super([file])
41
+ end
42
+
43
+ private
44
+
45
+ def rubocop_flags
46
+ COMMON_RUBOCOP_FLAGS
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,103 @@
1
+ # typed: true
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
+ NODES_THAT_CAN_BE_PARENTS = [
21
+ SyntaxTree::Assign,
22
+ SyntaxTree::ArrayLiteral,
23
+ SyntaxTree::Begin,
24
+ SyntaxTree::BraceBlock,
25
+ SyntaxTree::Call,
26
+ SyntaxTree::Case,
27
+ SyntaxTree::ClassDeclaration,
28
+ SyntaxTree::Command,
29
+ SyntaxTree::Def,
30
+ SyntaxTree::Defs,
31
+ SyntaxTree::DoBlock,
32
+ SyntaxTree::Elsif,
33
+ SyntaxTree::Else,
34
+ SyntaxTree::EmbDoc,
35
+ SyntaxTree::Ensure,
36
+ SyntaxTree::FCall,
37
+ SyntaxTree::For,
38
+ SyntaxTree::HashLiteral,
39
+ SyntaxTree::Heredoc,
40
+ SyntaxTree::HeredocBeg,
41
+ SyntaxTree::HshPtn,
42
+ SyntaxTree::If,
43
+ SyntaxTree::In,
44
+ SyntaxTree::Lambda,
45
+ SyntaxTree::MethodAddBlock,
46
+ SyntaxTree::ModuleDeclaration,
47
+ SyntaxTree::Params,
48
+ SyntaxTree::Rescue,
49
+ SyntaxTree::RescueEx,
50
+ SyntaxTree::StringConcat,
51
+ SyntaxTree::StringLiteral,
52
+ SyntaxTree::Unless,
53
+ SyntaxTree::Until,
54
+ SyntaxTree::VCall,
55
+ SyntaxTree::When,
56
+ SyntaxTree::While,
57
+ ].freeze
58
+
59
+ def initialize(document)
60
+ super(document)
61
+
62
+ @ranges = []
63
+ @stack = []
64
+ end
65
+
66
+ def run
67
+ visit(@document.tree)
68
+ @ranges.reverse!
69
+ end
70
+
71
+ private
72
+
73
+ def visit(node)
74
+ return if node.nil?
75
+
76
+ range = create_selection_range(node.location, @stack.last)
77
+
78
+ @ranges << range
79
+ return if node.child_nodes.empty?
80
+
81
+ @stack << range if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
82
+ visit_all(node.child_nodes)
83
+ @stack.pop if NODES_THAT_CAN_BE_PARENTS.include?(node.class)
84
+ end
85
+
86
+ def create_selection_range(location, parent = nil)
87
+ RubyLsp::Requests::Support::SelectionRange.new(
88
+ range: LanguageServer::Protocol::Interface::Range.new(
89
+ start: LanguageServer::Protocol::Interface::Position.new(
90
+ line: location.start_line - 1,
91
+ character: location.start_column,
92
+ ),
93
+ end: LanguageServer::Protocol::Interface::Position.new(
94
+ line: location.end_line - 1,
95
+ character: location.end_column,
96
+ ),
97
+ ),
98
+ parent: parent
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end