ruby-lsp 0.4.4 → 0.5.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -86
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +10 -1
  5. data/lib/ruby_lsp/check_docs.rb +112 -0
  6. data/lib/ruby_lsp/document.rb +13 -2
  7. data/lib/ruby_lsp/event_emitter.rb +86 -18
  8. data/lib/ruby_lsp/executor.rb +146 -45
  9. data/lib/ruby_lsp/extension.rb +104 -0
  10. data/lib/ruby_lsp/internal.rb +2 -0
  11. data/lib/ruby_lsp/listener.rb +14 -11
  12. data/lib/ruby_lsp/requests/base_request.rb +0 -5
  13. data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
  14. data/lib/ruby_lsp/requests/code_actions.rb +1 -1
  15. data/lib/ruby_lsp/requests/code_lens.rb +82 -66
  16. data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
  17. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  18. data/lib/ruby_lsp/requests/document_link.rb +17 -15
  19. data/lib/ruby_lsp/requests/document_symbol.rb +51 -31
  20. data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
  21. data/lib/ruby_lsp/requests/formatting.rb +10 -11
  22. data/lib/ruby_lsp/requests/hover.rb +34 -19
  23. data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
  24. data/lib/ruby_lsp/requests/on_type_formatting.rb +5 -1
  25. data/lib/ruby_lsp/requests/path_completion.rb +21 -57
  26. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  27. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  28. data/lib/ruby_lsp/requests/support/common.rb +36 -0
  29. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -1
  30. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
  31. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +5 -2
  32. data/lib/ruby_lsp/requests.rb +15 -15
  33. data/lib/ruby_lsp/server.rb +44 -11
  34. data/lib/ruby_lsp/store.rb +1 -1
  35. data/lib/ruby_lsp/utils.rb +9 -8
  36. metadata +5 -3
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code lens demo](../../misc/code_lens.gif)
6
+ # ![Code lens demo](../../code_lens.gif)
7
7
  #
8
8
  # This feature is currently experimental. Clients will need to pass `experimentalFeaturesEnabled`
9
9
  # in the initialization options to enable it.
@@ -19,41 +19,41 @@ module RubyLsp
19
19
  # class Test < Minitest::Test
20
20
  # end
21
21
  # ```
22
+ class CodeLens < Listener
23
+ extend T::Sig
24
+ extend T::Generic
25
+
26
+ ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
22
27
 
23
- class CodeLens < BaseRequest
24
28
  BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
25
29
  ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
26
30
 
27
- sig do
28
- params(
29
- document: Document,
30
- ).void
31
- end
32
- def initialize(document)
33
- super(document)
34
- @results = T.let([], T::Array[Interface::CodeLens])
35
- @path = T.let(document.uri.delete_prefix("file://"), String)
36
- @modifier = T.let("public", String)
37
- end
31
+ sig { override.returns(ResponseType) }
32
+ attr_reader :response
33
+
34
+ sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
35
+ def initialize(uri, emitter, message_queue)
36
+ super(emitter, message_queue)
37
+
38
+ @response = T.let([], ResponseType)
39
+ @path = T.let(T.must(URI(uri).path), String)
40
+ @visibility = T.let("public", String)
41
+ @prev_visibility = T.let("public", String)
38
42
 
39
- sig { override.returns(T.all(T::Array[Interface::CodeLens], Object)) }
40
- def run
41
- visit(@document.tree) if @document.parsed?
42
- @results
43
+ emitter.register(self, :on_class, :on_def, :on_command, :after_command, :on_call, :after_call, :on_vcall)
43
44
  end
44
45
 
45
- sig { override.params(node: SyntaxTree::ClassDeclaration).void }
46
- def visit_class(node)
46
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
47
+ def on_class(node)
47
48
  class_name = node.constant.constant.value
48
49
  if class_name.end_with?("Test")
49
50
  add_code_lens(node, name: class_name, command: BASE_COMMAND + @path)
50
51
  end
51
- visit(node.bodystmt)
52
52
  end
53
53
 
54
- sig { override.params(node: SyntaxTree::DefNode).void }
55
- def visit_def(node)
56
- if @modifier == "public"
54
+ sig { params(node: SyntaxTree::DefNode).void }
55
+ def on_def(node)
56
+ if @visibility == "public"
57
57
  method_name = node.name.value
58
58
  if method_name.start_with?("test_")
59
59
  add_code_lens(
@@ -65,69 +65,85 @@ module RubyLsp
65
65
  end
66
66
  end
67
67
 
68
- sig { override.params(node: SyntaxTree::Command).void }
69
- def visit_command(node)
70
- if node.message.value == "public"
71
- with_visiblity("public", node)
68
+ sig { params(node: SyntaxTree::Command).void }
69
+ def on_command(node)
70
+ if ACCESS_MODIFIERS.include?(node.message.value) && node.arguments.parts.any?
71
+ @prev_visibility = @visibility
72
+ @visibility = node.message.value
72
73
  end
73
74
  end
74
75
 
75
- sig { override.params(node: SyntaxTree::CallNode).void }
76
- def visit_call(node)
76
+ sig { params(node: SyntaxTree::Command).void }
77
+ def after_command(node)
78
+ @visibility = @prev_visibility
79
+ end
80
+
81
+ sig { params(node: SyntaxTree::CallNode).void }
82
+ def on_call(node)
77
83
  ident = node.message if node.message.is_a?(SyntaxTree::Ident)
78
84
 
79
85
  if ident
80
- if T.cast(ident, SyntaxTree::Ident).value == "public"
81
- with_visiblity("public", node)
86
+ ident_value = T.cast(ident, SyntaxTree::Ident).value
87
+ if ACCESS_MODIFIERS.include?(ident_value)
88
+ @prev_visibility = @visibility
89
+ @visibility = ident_value
82
90
  end
83
91
  end
84
92
  end
85
93
 
86
- sig { override.params(node: SyntaxTree::VCall).void }
87
- def visit_vcall(node)
94
+ sig { params(node: SyntaxTree::CallNode).void }
95
+ def after_call(node)
96
+ @visibility = @prev_visibility
97
+ end
98
+
99
+ sig { params(node: SyntaxTree::VCall).void }
100
+ def on_vcall(node)
88
101
  vcall_value = node.value.value
89
102
 
90
103
  if ACCESS_MODIFIERS.include?(vcall_value)
91
- @modifier = vcall_value
104
+ @prev_visibility = vcall_value
105
+ @visibility = vcall_value
92
106
  end
93
107
  end
94
108
 
95
- private
96
-
97
- sig do
98
- params(
99
- visibility: String,
100
- node: T.any(SyntaxTree::CallNode, SyntaxTree::Command),
101
- ).void
102
- end
103
- def with_visiblity(visibility, node)
104
- current_visibility = @modifier
105
- @modifier = visibility
106
- visit(node.arguments)
107
- ensure
108
- @modifier = T.must(current_visibility)
109
+ sig { params(other: Listener[ResponseType]).returns(T.self_type) }
110
+ def merge_response!(other)
111
+ @response.concat(other.response)
112
+ self
109
113
  end
110
114
 
115
+ private
116
+
111
117
  sig { params(node: SyntaxTree::Node, name: String, command: String).void }
112
118
  def add_code_lens(node, name:, command:)
113
- @results << Interface::CodeLens.new(
114
- range: range_from_syntax_tree_node(node),
115
- command: Interface::Command.new(
116
- title: "Run",
117
- command: "rubyLsp.runTest",
118
- arguments: [
119
- @path,
120
- name,
121
- command,
122
- {
123
- start_line: node.location.start_line - 1,
124
- start_column: node.location.start_column,
125
- end_line: node.location.end_line - 1,
126
- end_column: node.location.end_column,
127
- },
128
- ],
129
- ),
130
- data: { type: "test" },
119
+ @response << create_code_lens(
120
+ node,
121
+ title: "Run",
122
+ command_name: "rubyLsp.runTest",
123
+ path: @path,
124
+ name: name,
125
+ test_command: command,
126
+ type: "test",
127
+ )
128
+
129
+ @response << create_code_lens(
130
+ node,
131
+ title: "Run In Terminal",
132
+ command_name: "rubyLsp.runTestInTerminal",
133
+ path: @path,
134
+ name: name,
135
+ test_command: command,
136
+ type: "test_in_terminal",
137
+ )
138
+
139
+ @response << create_code_lens(
140
+ node,
141
+ title: "Debug",
142
+ command_name: "rubyLsp.debugTest",
143
+ path: @path,
144
+ name: name,
145
+ test_command: command,
146
+ type: "debug",
131
147
  )
132
148
  end
133
149
  end
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/rubocop_diagnostics_runner"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Diagnostics demo](../../misc/diagnostics.gif)
8
+ # ![Diagnostics demo](../../diagnostics.gif)
9
9
  #
10
10
  # The
11
11
  # [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
@@ -34,7 +34,7 @@ module RubyLsp
34
34
  return unless defined?(Support::RuboCopDiagnosticsRunner)
35
35
 
36
36
  # Don't try to run RuboCop diagnostics for files outside the current working directory
37
- return unless @uri.start_with?(WORKSPACE_URI)
37
+ return unless URI(@uri).path&.start_with?(T.must(WORKSPACE_URI.path))
38
38
 
39
39
  Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
40
40
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Document highlight demo](../../misc/document_highlight.gif)
6
+ # ![Document highlight demo](../../document_highlight.gif)
7
7
  #
8
8
  # The [document highlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
9
9
  # informs the editor all relevant elements of the currently pointed item for highlighting. For example, when
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/source_uri"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Document link demo](../../misc/document_link.gif)
8
+ # ![Document link demo](../../document_link.gif)
9
9
  #
10
10
  # The [document link](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentLink)
11
11
  # makes `# source://PATH_TO_FILE#line` comments in a Ruby/RBI file clickable if the file exists.
@@ -18,8 +18,11 @@ module RubyLsp
18
18
  # def format(source, maxwidth = T.unsafe(nil))
19
19
  # end
20
20
  # ```
21
- class DocumentLink < BaseRequest
21
+ class DocumentLink < Listener
22
22
  extend T::Sig
23
+ extend T::Generic
24
+
25
+ ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
23
26
 
24
27
  GEM_TO_VERSION_MAP = T.let(
25
28
  [*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
@@ -69,25 +72,24 @@ module RubyLsp
69
72
  end
70
73
  end
71
74
 
72
- sig { params(document: Document).void }
73
- def initialize(document)
74
- super(document)
75
+ sig { override.returns(ResponseType) }
76
+ attr_reader :response
77
+
78
+ sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
79
+ def initialize(uri, emitter, message_queue)
80
+ super(emitter, message_queue)
75
81
 
76
82
  # Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
77
83
  # in the URI
78
- version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(document.uri)
84
+ version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(uri)
79
85
  @gem_version = T.let(version_match && version_match[0], T.nilable(String))
80
- @links = T.let([], T::Array[Interface::DocumentLink])
81
- end
86
+ @response = T.let([], T::Array[Interface::DocumentLink])
82
87
 
83
- sig { override.returns(T.all(T::Array[Interface::DocumentLink], Object)) }
84
- def run
85
- visit(@document.tree) if @document.parsed?
86
- @links
88
+ emitter.register(self, :on_comment)
87
89
  end
88
90
 
89
- sig { override.params(node: SyntaxTree::Comment).void }
90
- def visit_comment(node)
91
+ sig { params(node: SyntaxTree::Comment).void }
92
+ def on_comment(node)
91
93
  match = node.value.match(%r{source://.*#\d+$})
92
94
  return unless match
93
95
 
@@ -96,7 +98,7 @@ module RubyLsp
96
98
  file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
97
99
  return if file_path.nil?
98
100
 
99
- @links << Interface::DocumentLink.new(
101
+ @response << Interface::DocumentLink.new(
100
102
  range: range_from_syntax_tree_node(node),
101
103
  target: "file://#{file_path}##{uri.line_number}",
102
104
  tooltip: "Jump to #{file_path}##{uri.line_number}",
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Document symbol demo](../../misc/document_symbol.gif)
6
+ # ![Document symbol demo](../../document_symbol.gif)
7
7
  #
8
8
  # The [document
9
9
  # symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
@@ -26,8 +26,11 @@ module RubyLsp
26
26
  # end
27
27
  # end
28
28
  # ```
29
- class DocumentSymbol < BaseRequest
29
+ class DocumentSymbol < Listener
30
30
  extend T::Sig
31
+ extend T::Generic
32
+
33
+ ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
31
34
 
32
35
  SYMBOL_KIND = T.let(
33
36
  {
@@ -75,40 +78,53 @@ module RubyLsp
75
78
  end
76
79
  end
77
80
 
78
- sig { params(document: Document).void }
79
- def initialize(document)
81
+ sig { override.returns(T::Array[Interface::DocumentSymbol]) }
82
+ attr_reader :response
83
+
84
+ sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
85
+ def initialize(emitter, message_queue)
80
86
  super
81
87
 
82
88
  @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
89
+ @response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
83
90
  @stack = T.let(
84
91
  [@root],
85
92
  T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
86
93
  )
87
- end
88
94
 
89
- sig { override.returns(T.all(T::Array[Interface::DocumentSymbol], Object)) }
90
- def run
91
- visit(@document.tree) if @document.parsed?
92
- @root.children
95
+ emitter.register(
96
+ self,
97
+ :on_class,
98
+ :after_class,
99
+ :on_command,
100
+ :on_const_path_field,
101
+ :on_def,
102
+ :after_def,
103
+ :on_module,
104
+ :after_module,
105
+ :on_top_const_field,
106
+ :on_var_field,
107
+ )
93
108
  end
94
109
 
95
- sig { override.params(node: SyntaxTree::ClassDeclaration).void }
96
- def visit_class(node)
97
- symbol = create_document_symbol(
110
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
111
+ def on_class(node)
112
+ @stack << create_document_symbol(
98
113
  name: full_constant_name(node.constant),
99
114
  kind: :class,
100
115
  range_node: node,
101
116
  selection_range_node: node.constant,
102
117
  )
118
+ end
103
119
 
104
- @stack << symbol
105
- visit(node.bodystmt)
120
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
121
+ def after_class(node)
106
122
  @stack.pop
107
123
  end
108
124
 
109
- sig { override.params(node: SyntaxTree::Command).void }
110
- def visit_command(node)
111
- return visit(node.arguments) unless ATTR_ACCESSORS.include?(node.message.value)
125
+ sig { params(node: SyntaxTree::Command).void }
126
+ def on_command(node)
127
+ return unless ATTR_ACCESSORS.include?(node.message.value)
112
128
 
113
129
  node.arguments.parts.each do |argument|
114
130
  next unless argument.is_a?(SyntaxTree::SymbolLiteral)
@@ -122,8 +138,8 @@ module RubyLsp
122
138
  end
123
139
  end
124
140
 
125
- sig { override.params(node: SyntaxTree::ConstPathField).void }
126
- def visit_const_path_field(node)
141
+ sig { params(node: SyntaxTree::ConstPathField).void }
142
+ def on_const_path_field(node)
127
143
  create_document_symbol(
128
144
  name: node.constant.value,
129
145
  kind: :constant,
@@ -132,8 +148,8 @@ module RubyLsp
132
148
  )
133
149
  end
134
150
 
135
- sig { override.params(node: SyntaxTree::DefNode).void }
136
- def visit_def(node)
151
+ sig { params(node: SyntaxTree::DefNode).void }
152
+ def on_def(node)
137
153
  target = node.target
138
154
 
139
155
  if target.is_a?(SyntaxTree::VarRef) && target.value.is_a?(SyntaxTree::Kw) && target.value.value == "self"
@@ -152,26 +168,30 @@ module RubyLsp
152
168
  )
153
169
 
154
170
  @stack << symbol
155
- visit(node.bodystmt)
171
+ end
172
+
173
+ sig { params(node: SyntaxTree::DefNode).void }
174
+ def after_def(node)
156
175
  @stack.pop
157
176
  end
158
177
 
159
- sig { override.params(node: SyntaxTree::ModuleDeclaration).void }
160
- def visit_module(node)
161
- symbol = create_document_symbol(
178
+ sig { params(node: SyntaxTree::ModuleDeclaration).void }
179
+ def on_module(node)
180
+ @stack << create_document_symbol(
162
181
  name: full_constant_name(node.constant),
163
182
  kind: :module,
164
183
  range_node: node,
165
184
  selection_range_node: node.constant,
166
185
  )
186
+ end
167
187
 
168
- @stack << symbol
169
- visit(node.bodystmt)
188
+ sig { params(node: SyntaxTree::ModuleDeclaration).void }
189
+ def after_module(node)
170
190
  @stack.pop
171
191
  end
172
192
 
173
- sig { override.params(node: SyntaxTree::TopConstField).void }
174
- def visit_top_const_field(node)
193
+ sig { params(node: SyntaxTree::TopConstField).void }
194
+ def on_top_const_field(node)
175
195
  create_document_symbol(
176
196
  name: node.constant.value,
177
197
  kind: :constant,
@@ -180,8 +200,8 @@ module RubyLsp
180
200
  )
181
201
  end
182
202
 
183
- sig { override.params(node: SyntaxTree::VarField).void }
184
- def visit_var_field(node)
203
+ sig { params(node: SyntaxTree::VarField).void }
204
+ def on_var_field(node)
185
205
  value = node.value
186
206
  kind = case value
187
207
  when SyntaxTree::Const
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Folding ranges demo](../../misc/folding_ranges.gif)
6
+ # ![Folding ranges demo](../../folding_ranges.gif)
7
7
  #
8
8
  # The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
9
9
  # request informs the editor of the ranges where and how code can be folded.
@@ -6,13 +6,17 @@ require "ruby_lsp/requests/support/syntax_tree_formatting_runner"
6
6
 
7
7
  module RubyLsp
8
8
  module Requests
9
- # ![Formatting symbol demo](../../misc/formatting.gif)
9
+ # ![Formatting symbol demo](../../formatting.gif)
10
10
  #
11
11
  # The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
12
12
  # request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
13
13
  # registering the ruby-lsp as the Ruby formatter.
14
14
  #
15
- # If RuboCop is not available, the request will fall back to SyntaxTree.
15
+ # The `rubyLsp.formatter` setting specifies which formatter to use.
16
+ # If set to `auto`` then it behaves as follows:
17
+ # * It will use RuboCop if it is part of the bundle.
18
+ # * If RuboCop is not available, and `syntax_tree` is a direct dependency, it will use that.
19
+ # * Otherwise, no formatting will be applied.
16
20
  #
17
21
  # # Example
18
22
  #
@@ -32,14 +36,7 @@ module RubyLsp
32
36
  super(document)
33
37
 
34
38
  @uri = T.let(document.uri, String)
35
- @formatter = T.let(
36
- if formatter == "auto"
37
- defined?(Support::RuboCopFormattingRunner) ? "rubocop" : "syntax_tree"
38
- else
39
- formatter
40
- end,
41
- String,
42
- )
39
+ @formatter = formatter
43
40
  end
44
41
 
45
42
  sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
@@ -72,7 +69,9 @@ module RubyLsp
72
69
  def formatted_file
73
70
  case @formatter
74
71
  when "rubocop"
75
- Support::RuboCopFormattingRunner.instance.run(@uri, @document)
72
+ if defined?(Support::RuboCopFormattingRunner)
73
+ Support::RuboCopFormattingRunner.instance.run(@uri, @document)
74
+ end
76
75
  when "syntax_tree"
77
76
  Support::SyntaxTreeFormattingRunner.instance.run(@uri, @document)
78
77
  else
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Hover demo](../../misc/rails_document_link_hover.gif)
6
+ # ![Hover demo](../../rails_document_link_hover.gif)
7
7
  #
8
8
  # The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
9
9
  # renders a clickable link to the code's official documentation.
@@ -35,31 +35,46 @@ module RubyLsp
35
35
  sig { override.returns(ResponseType) }
36
36
  attr_reader :response
37
37
 
38
- sig { void }
39
- def initialize
38
+ sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
39
+ def initialize(emitter, message_queue)
40
+ super
41
+
40
42
  @response = T.let(nil, ResponseType)
41
- super()
43
+ emitter.register(self, :on_command, :on_const_path_ref, :on_call)
42
44
  end
43
45
 
44
- listener_events do
45
- sig { params(node: SyntaxTree::Command).void }
46
- def on_command(node)
47
- message = node.message
48
- @response = generate_rails_document_link_hover(message.value, message)
49
- end
46
+ # Merges responses from other hover listeners
47
+ sig { params(other: Listener[ResponseType]).returns(T.self_type) }
48
+ def merge_response!(other)
49
+ other_response = other.response
50
+ return self unless other_response
50
51
 
51
- sig { params(node: SyntaxTree::ConstPathRef).void }
52
- def on_const_path_ref(node)
53
- @response = generate_rails_document_link_hover(full_constant_name(node), node)
52
+ if @response.nil?
53
+ @response = other.response
54
+ else
55
+ @response.contents.value << other_response.contents.value << "\n\n"
54
56
  end
55
57
 
56
- sig { params(node: SyntaxTree::CallNode).void }
57
- def on_call(node)
58
- message = node.message
59
- return if message.is_a?(Symbol)
58
+ self
59
+ end
60
60
 
61
- @response = generate_rails_document_link_hover(message.value, message)
62
- end
61
+ sig { params(node: SyntaxTree::Command).void }
62
+ def on_command(node)
63
+ message = node.message
64
+ @response = generate_rails_document_link_hover(message.value, message)
65
+ end
66
+
67
+ sig { params(node: SyntaxTree::ConstPathRef).void }
68
+ def on_const_path_ref(node)
69
+ @response = generate_rails_document_link_hover(full_constant_name(node), node)
70
+ end
71
+
72
+ sig { params(node: SyntaxTree::CallNode).void }
73
+ def on_call(node)
74
+ message = node.message
75
+ return if message.is_a?(Symbol)
76
+
77
+ @response = generate_rails_document_link_hover(message.value, message)
63
78
  end
64
79
 
65
80
  private
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Inlay hint demo](../../misc/inlay_hint.gif)
6
+ # ![Inlay hint demo](../../inlay_hint.gif)
7
7
  #
8
8
  # [Inlay hints](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
9
9
  # are labels added directly in the code that explicitly show the user something that might
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![On type formatting demo](../../misc/on_type_formatting.gif)
6
+ # ![On type formatting demo](../../on_type_formatting.gif)
7
7
  #
8
8
  # The [on type formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_onTypeFormatting)
9
9
  # request formats code as the user is typing. For example, automatically adding `end` to class definitions.
@@ -80,6 +80,10 @@ module RubyLsp
80
80
 
81
81
  sig { void }
82
82
  def handle_statement_end
83
+ # If a keyword occurs in a line which appears be a comment or a string, we will not try to format it, since
84
+ # it could be a coincidental match. This approach is not perfect, but it should cover most cases.
85
+ return if @previous_line.start_with?(/["'#]/)
86
+
83
87
  return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
84
88
 
85
89
  indents = " " * @indentation