ruby-lsp 0.10.1 → 0.11.0

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