ruby-lsp 0.26.11 → 0.27.0.beta1

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.
@@ -0,0 +1,200 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Rubydex
5
+ # @abstract
6
+ class Definition
7
+ #: () -> RubyLsp::Interface::LocationLink
8
+ def to_lsp_location_link
9
+ selection_range = to_lsp_selection_range
10
+
11
+ RubyLsp::Interface::LocationLink.new(
12
+ target_uri: location.uri,
13
+ target_range: selection_range,
14
+ target_selection_range: to_lsp_name_range || selection_range,
15
+ )
16
+ end
17
+
18
+ # @abstract
19
+ #: () -> Integer
20
+ def to_lsp_kind
21
+ raise RubyLsp::AbstractMethodInvokedError
22
+ end
23
+
24
+ #: (String name) -> RubyLsp::Interface::WorkspaceSymbol
25
+ def to_lsp_workspace_symbol(name)
26
+ # We use the namespace as the container name, but we also use the full name as the regular name. The reason we do
27
+ # this is to allow people to search for fully qualified names (e.g.: `Foo::Bar`). If we only included the short
28
+ # name `Bar`, then searching for `Foo::Bar` would not return any results
29
+ *container, _short_name = name.split("::")
30
+ container_name = container.join("::")
31
+
32
+ RubyLsp::Interface::WorkspaceSymbol.new(
33
+ name: name,
34
+ container_name: container_name,
35
+ kind: to_lsp_kind,
36
+ location: to_lsp_selection_location,
37
+ )
38
+ end
39
+
40
+ #: () -> RubyLsp::Interface::Range
41
+ def to_lsp_selection_range
42
+ loc = location
43
+
44
+ RubyLsp::Interface::Range.new(
45
+ start: RubyLsp::Interface::Position.new(line: loc.start_line, character: loc.start_column),
46
+ end: RubyLsp::Interface::Position.new(line: loc.end_line, character: loc.end_column),
47
+ )
48
+ end
49
+
50
+ #: () -> RubyLsp::Interface::Location
51
+ def to_lsp_selection_location
52
+ location = self.location
53
+
54
+ RubyLsp::Interface::Location.new(
55
+ uri: location.uri,
56
+ range: RubyLsp::Interface::Range.new(
57
+ start: RubyLsp::Interface::Position.new(line: location.start_line, character: location.start_column),
58
+ end: RubyLsp::Interface::Position.new(line: location.end_line, character: location.end_column),
59
+ ),
60
+ )
61
+ end
62
+
63
+ #: () -> RubyLsp::Interface::Range?
64
+ def to_lsp_name_range
65
+ loc = name_location
66
+ return unless loc
67
+
68
+ RubyLsp::Interface::Range.new(
69
+ start: RubyLsp::Interface::Position.new(line: loc.start_line, character: loc.start_column),
70
+ end: RubyLsp::Interface::Position.new(line: loc.end_line, character: loc.end_column),
71
+ )
72
+ end
73
+
74
+ #: () -> RubyLsp::Interface::Location?
75
+ def to_lsp_name_location
76
+ location = name_location
77
+ return unless location
78
+
79
+ RubyLsp::Interface::Location.new(
80
+ uri: location.uri,
81
+ range: RubyLsp::Interface::Range.new(
82
+ start: RubyLsp::Interface::Position.new(line: location.start_line, character: location.start_column),
83
+ end: RubyLsp::Interface::Position.new(line: location.end_line, character: location.end_column),
84
+ ),
85
+ )
86
+ end
87
+ end
88
+
89
+ class ClassDefinition
90
+ # @override
91
+ #: () -> Integer
92
+ def to_lsp_kind
93
+ RubyLsp::Constant::SymbolKind::CLASS
94
+ end
95
+ end
96
+
97
+ class ModuleDefinition
98
+ # @override
99
+ #: () -> Integer
100
+ def to_lsp_kind
101
+ RubyLsp::Constant::SymbolKind::NAMESPACE
102
+ end
103
+ end
104
+
105
+ class SingletonClassDefinition
106
+ # @override
107
+ #: () -> Integer
108
+ def to_lsp_kind
109
+ RubyLsp::Constant::SymbolKind::CLASS
110
+ end
111
+ end
112
+
113
+ class ConstantDefinition
114
+ # @override
115
+ #: () -> Integer
116
+ def to_lsp_kind
117
+ RubyLsp::Constant::SymbolKind::CONSTANT
118
+ end
119
+ end
120
+
121
+ class ConstantAliasDefinition
122
+ # @override
123
+ #: () -> Integer
124
+ def to_lsp_kind
125
+ RubyLsp::Constant::SymbolKind::CONSTANT
126
+ end
127
+ end
128
+
129
+ class MethodDefinition
130
+ # @override
131
+ #: () -> Integer
132
+ def to_lsp_kind
133
+ name == "initialize()" ? RubyLsp::Constant::SymbolKind::CONSTRUCTOR : RubyLsp::Constant::SymbolKind::METHOD
134
+ end
135
+ end
136
+
137
+ class MethodAliasDefinition
138
+ # @override
139
+ #: () -> Integer
140
+ def to_lsp_kind
141
+ RubyLsp::Constant::SymbolKind::METHOD
142
+ end
143
+ end
144
+
145
+ class AttrReaderDefinition
146
+ # @override
147
+ #: () -> Integer
148
+ def to_lsp_kind
149
+ RubyLsp::Constant::SymbolKind::PROPERTY
150
+ end
151
+ end
152
+
153
+ class AttrWriterDefinition
154
+ # @override
155
+ #: () -> Integer
156
+ def to_lsp_kind
157
+ RubyLsp::Constant::SymbolKind::PROPERTY
158
+ end
159
+ end
160
+
161
+ class AttrAccessorDefinition
162
+ # @override
163
+ #: () -> Integer
164
+ def to_lsp_kind
165
+ RubyLsp::Constant::SymbolKind::PROPERTY
166
+ end
167
+ end
168
+
169
+ class InstanceVariableDefinition
170
+ # @override
171
+ #: () -> Integer
172
+ def to_lsp_kind
173
+ RubyLsp::Constant::SymbolKind::FIELD
174
+ end
175
+ end
176
+
177
+ class ClassVariableDefinition
178
+ # @override
179
+ #: () -> Integer
180
+ def to_lsp_kind
181
+ RubyLsp::Constant::SymbolKind::FIELD
182
+ end
183
+ end
184
+
185
+ class GlobalVariableDefinition
186
+ # @override
187
+ #: () -> Integer
188
+ def to_lsp_kind
189
+ RubyLsp::Constant::SymbolKind::VARIABLE
190
+ end
191
+ end
192
+
193
+ class GlobalVariableAliasDefinition
194
+ # @override
195
+ #: () -> Integer
196
+ def to_lsp_kind
197
+ RubyLsp::Constant::SymbolKind::VARIABLE
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,16 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Rubydex
5
+ class ConstantReference
6
+ #: () -> RubyLsp::Interface::Range
7
+ def to_lsp_range
8
+ loc = location
9
+
10
+ RubyLsp::Interface::Range.new(
11
+ start: RubyLsp::Interface::Position.new(line: loc.start_line, character: loc.start_column),
12
+ end: RubyLsp::Interface::Position.new(line: loc.end_line, character: loc.end_column),
13
+ )
14
+ end
15
+ end
16
+ end
@@ -3,11 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  class Server < BaseServer
6
- NON_REPORTABLE_SETUP_ERRORS = [
7
- Bundler::GemNotFound,
8
- Bundler::GitError,
9
- Bundler::Dsl::DSLError,
10
- ].freeze #: Array[singleton(StandardError)]
6
+ NON_REPORTABLE_SETUP_ERRORS = [Bundler::GemNotFound, Bundler::GitError].freeze #: Array[singleton(StandardError)]
11
7
 
12
8
  # Only for testing
13
9
  #: GlobalState
@@ -124,16 +120,6 @@ module RubyLsp
124
120
  @global_state.synchronize { @cancelled_requests << message[:params][:id] }
125
121
  when nil
126
122
  process_response(message) if message[:result]
127
- else
128
- id = message[:id]
129
-
130
- if id
131
- send_message(Error.new(
132
- id: id,
133
- code: Constant::ErrorCodes::METHOD_NOT_FOUND,
134
- message: "Method not found: #{message[:method]}",
135
- ))
136
- end
137
123
  end
138
124
  rescue DelegateRequestError
139
125
  send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
@@ -429,7 +415,16 @@ module RubyLsp
429
415
  params = message[:params]
430
416
  text_document = params[:textDocument]
431
417
 
432
- @store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
418
+ document = @store.get(text_document[:uri])
419
+ document.push_edits(params[:contentChanges], version: text_document[:version])
420
+
421
+ language_id = document.language_id
422
+
423
+ if [:ruby, :rbs].include?(language_id)
424
+ graph = @global_state.graph
425
+ graph.index_source(text_document[:uri].to_s, document.source, language_id.to_s)
426
+ graph.resolve
427
+ end
433
428
  end
434
429
 
435
430
  #: (Hash[Symbol, untyped] message) -> void
@@ -1047,6 +1042,23 @@ module RubyLsp
1047
1042
  # is fine, but we shouldn't process the same file changes more than once
1048
1043
  changes.uniq!
1049
1044
 
1045
+ graph = @global_state.graph
1046
+
1047
+ # Handle deletions and accumulate additions and changes for indexing
1048
+ additions_and_changes = changes.each_with_object([]) do |change, acc|
1049
+ if change[:type] == Constant::FileChangeType::DELETED
1050
+ graph.delete_document(change[:uri])
1051
+ else
1052
+ path = URI(change[:uri]).to_standardized_path
1053
+ next if path.nil?
1054
+ next unless File.directory?(path) || [".rb", ".rbs"].include?(File.extname(path))
1055
+
1056
+ acc << path
1057
+ end
1058
+ end
1059
+ graph.index_all(additions_and_changes)
1060
+ graph.resolve
1061
+
1050
1062
  index = @global_state.index
1051
1063
  changes.each do |change|
1052
1064
  # File change events include folders, but we're only interested in files
@@ -1242,12 +1254,18 @@ module RubyLsp
1242
1254
 
1243
1255
  #: -> void
1244
1256
  def perform_initial_indexing
1257
+ progress("indexing-progress", message: "Indexing workspace...")
1258
+ @global_state.graph.index_workspace
1259
+
1260
+ progress("indexing-progress", message: "Resolving graph...")
1261
+ @global_state.graph.resolve
1262
+
1245
1263
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
1246
1264
  # stuck indexing files
1247
1265
  Thread.new do
1248
1266
  begin
1249
1267
  @global_state.index.index_all do |percentage|
1250
- progress("indexing-progress", percentage)
1268
+ progress("indexing-progress", percentage: percentage)
1251
1269
  true
1252
1270
  rescue ClosedQueueError
1253
1271
  # Since we run indexing on a separate thread, it's possible to kill the server before indexing is complete.
@@ -1301,11 +1319,13 @@ module RubyLsp
1301
1319
  send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1302
1320
  end
1303
1321
 
1304
- #: (String id, Integer percentage) -> void
1305
- def progress(id, percentage)
1322
+ #: (String, ?message: String?, ?percentage: Integer?) -> void
1323
+ def progress(id, message: nil, percentage: nil)
1306
1324
  return unless @global_state.client_capabilities.supports_progress
1307
1325
 
1308
- send_message(Notification.progress_report(id, percentage: percentage, message: "#{percentage}% completed"))
1326
+ message ||= "#{percentage}% completed" if percentage
1327
+
1328
+ send_message(Notification.progress_report(id, percentage: percentage, message: message))
1309
1329
  end
1310
1330
 
1311
1331
  #: (String id) -> void
@@ -191,11 +191,7 @@ module RubyLsp
191
191
  end
192
192
 
193
193
  if @rails_app && !@dependencies["ruby-lsp-rails"]
194
- parts << if @beta
195
- 'gem "ruby-lsp-rails", ">= 0.a", require: false, group: :development'
196
- else
197
- 'gem "ruby-lsp-rails", require: false, group: :development'
198
- end
194
+ parts << 'gem "ruby-lsp-rails", require: false, group: :development'
199
195
  end
200
196
 
201
197
  content = parts.join("\n")
@@ -449,7 +445,7 @@ module RubyLsp
449
445
  requirement = Gem::Requirement.new(@bundler_version.to_s)
450
446
  return if Gem::Specification.any? { |s| s.name == "bundler" && requirement =~ s.version }
451
447
 
452
- Gem.install("bundler", @bundler_version.to_s, env_shebang: true)
448
+ Gem.install("bundler", @bundler_version.to_s)
453
449
  end
454
450
 
455
451
  #: -> bool
@@ -53,12 +53,6 @@ module RubyLsp
53
53
  end
54
54
  end
55
55
 
56
- #: (uri: URI::Generic, edits: Array[Hash[Symbol, untyped]], version: Integer) -> void
57
- def push_edits(uri:, edits:, version:)
58
- @state[uri.to_s] #: as !nil
59
- .push_edits(edits, version: version)
60
- end
61
-
62
56
  #: -> void
63
57
  def clear
64
58
  @state.clear
@@ -30,6 +30,9 @@ module RubyLsp
30
30
  })
31
31
 
32
32
  server.global_state.index.index_single(uri, source)
33
+ graph = server.global_state.graph
34
+ graph.index_source(uri.to_s, source, "ruby")
35
+ graph.resolve
33
36
  end
34
37
 
35
38
  server.load_addons(include_project_addons: false) if load_addons
@@ -5,9 +5,9 @@ module RubyLsp
5
5
  # A minimalistic type checker to try to resolve types that can be inferred without requiring a type system or
6
6
  # annotations
7
7
  class TypeInferrer
8
- #: (RubyIndexer::Index index) -> void
9
- def initialize(index)
10
- @index = index
8
+ #: (Rubydex::Graph) -> void
9
+ def initialize(graph)
10
+ @graph = graph
11
11
  end
12
12
 
13
13
  #: (NodeContext node_context) -> Type?
@@ -81,14 +81,13 @@ module RubyLsp
81
81
  receiver_name = RubyIndexer::Index.constant_name(receiver)
82
82
  return unless receiver_name
83
83
 
84
- resolved_receiver = @index.resolve(receiver_name, node_context.nesting)
85
- name = resolved_receiver&.first&.name
86
- return unless name
84
+ resolved_receiver = @graph.resolve_constant(receiver_name, node_context.nesting)
85
+ return unless resolved_receiver
87
86
 
88
- *parts, last = name.split("::")
89
- return Type.new("#{last}::<Class:#{last}>") if parts.empty?
87
+ *parts, last = resolved_receiver.name.split("::")
88
+ return Type.new("#{last}::<#{last}>") if parts.empty?
90
89
 
91
- Type.new("#{parts.join("::")}::#{last}::<Class:#{last}>")
90
+ Type.new("#{parts.join("::")}::#{last}::<#{last}>")
92
91
  when Prism::CallNode
93
92
  raw_receiver = receiver.message
94
93
 
@@ -96,12 +95,14 @@ module RubyLsp
96
95
  # When invoking `new`, we recursively infer the type of the receiver to get the class type its being invoked
97
96
  # on and then return the attached version of that type, since it's being instantiated.
98
97
  type = infer_receiver_for_call_node(receiver, node_context)
99
-
100
98
  return unless type
101
99
 
102
100
  # If the method `new` was overridden, then we cannot assume that it will return a new instance of the class
103
- new_method = @index.resolve_method("new", type.name)&.first
104
- return if new_method && new_method.owner&.name != "Class"
101
+ declaration = @graph[type.name] #: as Rubydex::Namespace?
102
+ return unless declaration
103
+
104
+ new_method = declaration.find_member("new()")
105
+ return if new_method && new_method.owner.name != "Class"
105
106
 
106
107
  type.attached
107
108
  elsif raw_receiver
@@ -121,11 +122,11 @@ module RubyLsp
121
122
  .map(&:capitalize)
122
123
  .join
123
124
 
124
- entries = @index.resolve(guessed_name, nesting) || @index.first_unqualified_const(guessed_name)
125
- name = entries&.first&.name
126
- return unless name
125
+ declaration = @graph.resolve_constant(guessed_name, nesting)
126
+ declaration ||= @graph.search(guessed_name).first
127
+ return unless declaration
127
128
 
128
- GuessedType.new(name)
129
+ GuessedType.new(declaration.name)
129
130
  end
130
131
 
131
132
  #: (NodeContext node_context) -> Type
@@ -142,24 +143,25 @@ module RubyLsp
142
143
  # If the class/module definition is using compact style (e.g.: `class Foo::Bar`), then we need to split the name
143
144
  # into its individual parts to build the correct singleton name
144
145
  parts = nesting.flat_map { |part| part.split("::") }
145
- Type.new("#{parts.join("::")}::<Class:#{parts.last}>")
146
+ Type.new("#{parts.join("::")}::<#{parts.last}>")
146
147
  end
147
148
 
148
149
  #: (NodeContext node_context) -> Type?
149
150
  def infer_receiver_for_class_variables(node_context)
150
151
  nesting_parts = node_context.nesting.dup
151
-
152
152
  return Type.new("Object") if nesting_parts.empty?
153
153
 
154
154
  nesting_parts.reverse_each do |part|
155
- break unless part.include?("<Class:")
155
+ break unless part.start_with?("<")
156
156
 
157
157
  nesting_parts.pop
158
158
  end
159
159
 
160
- receiver_name = nesting_parts.join("::")
161
- resolved_receiver = @index.resolve(receiver_name, node_context.nesting)&.first
162
- return unless resolved_receiver&.name
160
+ resolved_receiver = @graph.resolve_constant(
161
+ nesting_parts.last, #: as !nil
162
+ nesting_parts[0...-1], #: as !nil
163
+ )
164
+ return unless resolved_receiver
163
165
 
164
166
  Type.new(resolved_receiver.name)
165
167
  end
@@ -174,7 +176,7 @@ module RubyLsp
174
176
  @name = name
175
177
  end
176
178
 
177
- # Returns the attached version of this type by removing the `<Class:...>` part from its name
179
+ # Returns the attached version of this type by removing the `<...>` part from its name
178
180
  #: -> Type
179
181
  def attached
180
182
  Type.new(
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.11
4
+ version: 0.27.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
@@ -63,6 +63,20 @@ dependencies:
63
63
  - - "<"
64
64
  - !ruby/object:Gem::Version
65
65
  version: '5'
66
+ - !ruby/object:Gem::Dependency
67
+ name: rubydex
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: 0.1.0.beta1
73
+ type: :runtime
74
+ prerelease: false
75
+ version_requirements: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 0.1.0.beta1
66
80
  description: An opinionated language server for Ruby
67
81
  email:
68
82
  - ruby@shopify.com
@@ -168,6 +182,8 @@ files:
168
182
  - lib/ruby_lsp/response_builders/signature_help.rb
169
183
  - lib/ruby_lsp/response_builders/test_collection.rb
170
184
  - lib/ruby_lsp/ruby_document.rb
185
+ - lib/ruby_lsp/rubydex/definition.rb
186
+ - lib/ruby_lsp/rubydex/reference.rb
171
187
  - lib/ruby_lsp/scope.rb
172
188
  - lib/ruby_lsp/scripts/compose_bundle.rb
173
189
  - lib/ruby_lsp/scripts/compose_bundle_windows.rb
@@ -203,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
219
  - !ruby/object:Gem::Version
204
220
  version: '0'
205
221
  requirements: []
206
- rubygems_version: 4.0.10
222
+ rubygems_version: 4.0.3
207
223
  specification_version: 4
208
224
  summary: An opinionated language server for Ruby
209
225
  test_files: []