ruby-lsp 0.10.1 → 0.11.1

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-check +1 -1
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +40 -5
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +141 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/visitor.rb +66 -18
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +23 -0
  9. data/lib/ruby_indexer/test/configuration_test.rb +2 -0
  10. data/lib/ruby_indexer/test/constant_test.rb +202 -0
  11. data/lib/ruby_indexer/test/index_test.rb +20 -0
  12. data/lib/ruby_lsp/{extension.rb → addon.rb} +27 -25
  13. data/lib/ruby_lsp/check_docs.rb +7 -8
  14. data/lib/ruby_lsp/document.rb +35 -38
  15. data/lib/ruby_lsp/event_emitter.rb +239 -77
  16. data/lib/ruby_lsp/executor.rb +45 -55
  17. data/lib/ruby_lsp/internal.rb +2 -3
  18. data/lib/ruby_lsp/listener.rb +8 -7
  19. data/lib/ruby_lsp/parameter_scope.rb +33 -0
  20. data/lib/ruby_lsp/requests/base_request.rb +3 -3
  21. data/lib/ruby_lsp/requests/code_action_resolve.rb +14 -14
  22. data/lib/ruby_lsp/requests/code_lens.rb +39 -63
  23. data/lib/ruby_lsp/requests/completion.rb +54 -32
  24. data/lib/ruby_lsp/requests/definition.rb +30 -27
  25. data/lib/ruby_lsp/requests/diagnostics.rb +26 -3
  26. data/lib/ruby_lsp/requests/document_highlight.rb +18 -19
  27. data/lib/ruby_lsp/requests/document_link.rb +50 -9
  28. data/lib/ruby_lsp/requests/document_symbol.rb +82 -75
  29. data/lib/ruby_lsp/requests/folding_ranges.rb +199 -222
  30. data/lib/ruby_lsp/requests/formatting.rb +5 -6
  31. data/lib/ruby_lsp/requests/hover.rb +33 -22
  32. data/lib/ruby_lsp/requests/inlay_hints.rb +2 -3
  33. data/lib/ruby_lsp/requests/selection_ranges.rb +65 -40
  34. data/lib/ruby_lsp/requests/semantic_highlighting.rb +187 -145
  35. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -4
  36. data/lib/ruby_lsp/requests/support/annotation.rb +18 -17
  37. data/lib/ruby_lsp/requests/support/common.rb +17 -26
  38. data/lib/ruby_lsp/requests/support/dependency_detector.rb +67 -42
  39. data/lib/ruby_lsp/requests/support/highlight_target.rb +64 -45
  40. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -4
  41. data/lib/ruby_lsp/requests/support/selection_range.rb +5 -4
  42. data/lib/ruby_lsp/requests/support/sorbet.rb +2 -57
  43. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +7 -1
  44. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -1
  45. data/lib/ruby_lsp/server.rb +6 -44
  46. data/lib/ruby_lsp/utils.rb +2 -12
  47. metadata +11 -30
@@ -2,24 +2,24 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
- # To register an extension, inherit from this class and implement both `name` and `activate`
5
+ # To register an addon, inherit from this class and implement both `name` and `activate`
6
6
  #
7
7
  # # Example
8
8
  #
9
9
  # ```ruby
10
10
  # module MyGem
11
- # class MyExtension < Extension
11
+ # class MyAddon < Addon
12
12
  # def activate
13
13
  # # Perform any relevant initialization
14
14
  # end
15
15
  #
16
16
  # def name
17
- # "My extension name"
17
+ # "My addon name"
18
18
  # end
19
19
  # end
20
20
  # end
21
21
  # ```
22
- class Extension
22
+ class Addon
23
23
  extend T::Sig
24
24
  extend T::Helpers
25
25
 
@@ -28,37 +28,37 @@ module RubyLsp
28
28
  class << self
29
29
  extend T::Sig
30
30
 
31
- # Automatically track and instantiate extension classes
32
- sig { params(child_class: T.class_of(Extension)).void }
31
+ # Automatically track and instantiate addon classes
32
+ sig { params(child_class: T.class_of(Addon)).void }
33
33
  def inherited(child_class)
34
- extensions << child_class.new
34
+ addons << child_class.new
35
35
  super
36
36
  end
37
37
 
38
- sig { returns(T::Array[Extension]) }
39
- def extensions
40
- @extensions ||= T.let([], T.nilable(T::Array[Extension]))
38
+ sig { returns(T::Array[Addon]) }
39
+ def addons
40
+ @addons ||= T.let([], T.nilable(T::Array[Addon]))
41
41
  end
42
42
 
43
- # Discovers and loads all extensions. Returns the list of activated extensions
44
- sig { returns(T::Array[Extension]) }
45
- def load_extensions
46
- # Require all extensions entry points, which should be placed under
47
- # `some_gem/lib/ruby_lsp/your_gem_name/extension.rb`
48
- Gem.find_files("ruby_lsp/**/extension.rb").each do |extension|
49
- require File.expand_path(extension)
43
+ # Discovers and loads all addons. Returns the list of activated addons
44
+ sig { returns(T::Array[Addon]) }
45
+ def load_addons
46
+ # Require all addons entry points, which should be placed under
47
+ # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb`
48
+ Gem.find_files("ruby_lsp/**/addon.rb").each do |addon|
49
+ require File.expand_path(addon)
50
50
  rescue => e
51
51
  warn(e.message)
52
52
  warn(e.backtrace.to_s) # rubocop:disable Lint/RedundantStringCoercion
53
53
  end
54
54
 
55
- # Activate each one of the discovered extensions. If any problems occur in the extensions, we don't want to
55
+ # Activate each one of the discovered addons. If any problems occur in the addons, we don't want to
56
56
  # fail to boot the server
57
- extensions.each do |extension|
58
- extension.activate
57
+ addons.each do |addon|
58
+ addon.activate
59
59
  nil
60
60
  rescue => e
61
- extension.add_error(e)
61
+ addon.add_error(e)
62
62
  end
63
63
  end
64
64
  end
@@ -92,17 +92,17 @@ module RubyLsp
92
92
  @errors.filter_map(&:backtrace).join("\n\n")
93
93
  end
94
94
 
95
- # Each extension should implement `MyExtension#activate` and use to perform any sort of initialization, such as
95
+ # Each addon should implement `MyAddon#activate` and use to perform any sort of initialization, such as
96
96
  # reading information into memory or even spawning a separate process
97
97
  sig { abstract.void }
98
98
  def activate; end
99
99
 
100
- # Each extension should implement `MyExtension#deactivate` and use to perform any clean up, like shutting down a
100
+ # Each addon should implement `MyAddon#deactivate` and use to perform any clean up, like shutting down a
101
101
  # child process
102
102
  sig { abstract.void }
103
103
  def deactivate; end
104
104
 
105
- # Extensions should override the `name` method to return the extension name
105
+ # Addons should override the `name` method to return the addon name
106
106
  sig { abstract.returns(String) }
107
107
  def name; end
108
108
 
@@ -119,11 +119,13 @@ module RubyLsp
119
119
  # Creates a new Hover listener. This method is invoked on every Hover request
120
120
  sig do
121
121
  overridable.params(
122
+ nesting: T::Array[String],
123
+ index: RubyIndexer::Index,
122
124
  emitter: EventEmitter,
123
125
  message_queue: Thread::Queue,
124
126
  ).returns(T.nilable(Listener[T.nilable(Interface::Hover)]))
125
127
  end
126
- def create_hover_listener(emitter, message_queue); end
128
+ def create_hover_listener(nesting, index, emitter, message_queue); end
127
129
 
128
130
  # Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
129
131
  sig do
@@ -5,9 +5,9 @@ require "ruby_lsp/internal"
5
5
  require "objspace"
6
6
 
7
7
  module RubyLsp
8
- # This rake task checks that all requests or extensions are fully documented. Add the rake task to your Rakefile and
9
- # specify the absolute path for all files that must be required in order to discover all listeners and their
10
- # related GIFs
8
+ # This rake task checks that all requests or addons are fully documented. Add the rake task to your Rakefile and
9
+ # specify the absolute path for all files that must be required in order to discover all listeners and their related
10
+ # GIFs
11
11
  #
12
12
  # # Rakefile
13
13
  # request_files = FileList.new("#{__dir__}/lib/ruby_lsp/requests/*.rb") do |fl|
@@ -53,8 +53,7 @@ module RubyLsp
53
53
  # documented
54
54
  features = ObjectSpace.each_object(Class).filter_map do |k|
55
55
  klass = T.unsafe(k)
56
- klass if klass < RubyLsp::Requests::BaseRequest ||
57
- (klass < RubyLsp::Listener && klass != RubyLsp::ExtensibleListener)
56
+ klass if klass < Requests::BaseRequest || (klass < Listener && klass != ExtensibleListener)
58
57
  end
59
58
 
60
59
  missing_docs = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
@@ -82,14 +81,14 @@ module RubyLsp
82
81
  T.must(missing_docs[class_name]) << "No documentation found"
83
82
  elsif !%r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}.match?(documentation)
84
83
  T.must(missing_docs[class_name]) << <<~DOCS
85
- Missing specification link. Requests and extensions should include a link to the LSP specification for the
84
+ Missing specification link. Requests and addons should include a link to the LSP specification for the
86
85
  related feature. For example:
87
86
 
88
87
  [Inlay hint](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
89
88
  DOCS
90
89
  elsif !documentation.include?("# Example")
91
90
  T.must(missing_docs[class_name]) << <<~DOCS
92
- Missing example. Requests and extensions should include a code example that explains what the feature does.
91
+ Missing example. Requests and addons should include a code example that explains what the feature does.
93
92
 
94
93
  # # Example
95
94
  # ```ruby
@@ -99,7 +98,7 @@ module RubyLsp
99
98
  DOCS
100
99
  elsif !/\[.* demo\]\(.*\.gif\)/.match?(documentation)
101
100
  T.must(missing_docs[class_name]) << <<~DOCS
102
- Missing demonstration GIF. Each request and extension must be documented with a GIF that shows the feature
101
+ Missing demonstration GIF. Each request and addon must be documented with a GIF that shows the feature
103
102
  working. For example:
104
103
 
105
104
  # [Inlay hint demo](../../inlay_hint.gif)
@@ -9,8 +9,8 @@ module RubyLsp
9
9
  RangeShape = T.type_alias { { start: PositionShape, end: PositionShape } }
10
10
  EditShape = T.type_alias { { range: RangeShape, text: String } }
11
11
 
12
- sig { returns(T.nilable(SyntaxTree::Node)) }
13
- attr_reader :tree
12
+ sig { returns(YARP::ParseResult) }
13
+ attr_reader :parse_result
14
14
 
15
15
  sig { returns(String) }
16
16
  attr_reader :source
@@ -28,11 +28,18 @@ module RubyLsp
28
28
  @source = T.let(source, String)
29
29
  @version = T.let(version, Integer)
30
30
  @uri = T.let(uri, URI::Generic)
31
- @unparsed_edits = T.let([], T::Array[EditShape])
32
- @syntax_error = T.let(false, T::Boolean)
33
- @tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
34
- rescue SyntaxTree::Parser::ParseError
35
- @syntax_error = true
31
+ @needs_parsing = T.let(false, T::Boolean)
32
+ @parse_result = T.let(YARP.parse(@source), YARP::ParseResult)
33
+ end
34
+
35
+ sig { returns(YARP::ProgramNode) }
36
+ def tree
37
+ @parse_result.value
38
+ end
39
+
40
+ sig { returns(T::Array[YARP::Comment]) }
41
+ def comments
42
+ @parse_result.comments
36
43
  end
37
44
 
38
45
  sig { params(other: Document).returns(T::Boolean) }
@@ -80,29 +87,21 @@ module RubyLsp
80
87
  end
81
88
 
82
89
  @version = version
83
- @unparsed_edits.concat(edits)
90
+ @needs_parsing = true
84
91
  @cache.clear
85
92
  end
86
93
 
87
94
  sig { void }
88
95
  def parse
89
- return if @unparsed_edits.empty?
96
+ return unless @needs_parsing
90
97
 
91
- @unparsed_edits.clear
92
- @tree = SyntaxTree.parse(@source)
93
- @syntax_error = false
94
- rescue SyntaxTree::Parser::ParseError
95
- @syntax_error = true
98
+ @needs_parsing = false
99
+ @parse_result = YARP.parse(@source)
96
100
  end
97
101
 
98
102
  sig { returns(T::Boolean) }
99
103
  def syntax_error?
100
- @syntax_error
101
- end
102
-
103
- sig { returns(T::Boolean) }
104
- def parsed?
105
- !@tree.nil?
104
+ @parse_result.failure?
106
105
  end
107
106
 
108
107
  sig { returns(Scanner) }
@@ -113,27 +112,25 @@ module RubyLsp
113
112
  sig do
114
113
  params(
115
114
  position: PositionShape,
116
- node_types: T::Array[T.class_of(SyntaxTree::Node)],
117
- ).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
115
+ node_types: T::Array[T.class_of(YARP::Node)],
116
+ ).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
118
117
  end
119
118
  def locate_node(position, node_types: [])
120
- return [nil, nil, []] unless parsed?
121
-
122
- locate(T.must(@tree), create_scanner.find_char_position(position), node_types: node_types)
119
+ locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
123
120
  end
124
121
 
125
122
  sig do
126
123
  params(
127
- node: SyntaxTree::Node,
124
+ node: YARP::Node,
128
125
  char_position: Integer,
129
- node_types: T::Array[T.class_of(SyntaxTree::Node)],
130
- ).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node), T::Array[String]])
126
+ node_types: T::Array[T.class_of(YARP::Node)],
127
+ ).returns([T.nilable(YARP::Node), T.nilable(YARP::Node), T::Array[String]])
131
128
  end
132
129
  def locate(node, char_position, node_types: [])
133
- queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
130
+ queue = T.let(node.child_nodes.compact, T::Array[T.nilable(YARP::Node)])
134
131
  closest = node
135
- parent = T.let(nil, T.nilable(SyntaxTree::Node))
136
- nesting = T.let([], T::Array[T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)])
132
+ parent = T.let(nil, T.nilable(YARP::Node))
133
+ nesting = T.let([], T::Array[T.any(YARP::ClassNode, YARP::ModuleNode)])
137
134
 
138
135
  until queue.empty?
139
136
  candidate = queue.shift
@@ -144,24 +141,24 @@ module RubyLsp
144
141
  # Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
145
142
  # same order as the visiting mechanism, which means searching the child nodes before moving on to the next
146
143
  # sibling
147
- queue.unshift(*candidate.child_nodes)
144
+ T.unsafe(queue).unshift(*candidate.child_nodes)
148
145
 
149
146
  # Skip if the current node doesn't cover the desired position
150
147
  loc = candidate.location
151
- next unless (loc.start_char...loc.end_char).cover?(char_position)
148
+ next unless (loc.start_offset...loc.end_offset).cover?(char_position)
152
149
 
153
150
  # If the node's start character is already past the position, then we should've found the closest node
154
151
  # already
155
- break if char_position < loc.start_char
152
+ break if char_position < loc.start_offset
156
153
 
157
154
  # If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
158
155
  # need to pop the stack
159
156
  previous_level = nesting.last
160
- nesting.pop if previous_level && candidate.start_char > previous_level.end_char
157
+ nesting.pop if previous_level && loc.start_offset > previous_level.location.end_offset
161
158
 
162
159
  # Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
163
160
  # target when it is a constant
164
- if candidate.is_a?(SyntaxTree::ClassDeclaration) || candidate.is_a?(SyntaxTree::ModuleDeclaration)
161
+ if candidate.is_a?(YARP::ClassNode) || candidate.is_a?(YARP::ModuleNode)
165
162
  nesting << candidate
166
163
  end
167
164
 
@@ -170,13 +167,13 @@ module RubyLsp
170
167
 
171
168
  # If the current node is narrower than or equal to the previous closest node, then it is more precise
172
169
  closest_loc = closest.location
173
- if loc.end_char - loc.start_char <= closest_loc.end_char - closest_loc.start_char
170
+ if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
174
171
  parent = closest
175
172
  closest = candidate
176
173
  end
177
174
  end
178
175
 
179
- [closest, parent, nesting.map { |n| n.constant.constant.value }]
176
+ [closest, parent, nesting.map { |n| n.constant_path.location.slice }]
180
177
  end
181
178
 
182
179
  class Scanner