ruby-lsp 0.26.8 → 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
@@ -7,6 +7,4 @@ require_relative "compose_bundle"
7
7
  # invoke the compose method from inside a forked process
8
8
  options = {}
9
9
  options[:beta] = true if ARGV.include?("--beta")
10
- branch_index = ARGV.index("--branch")
11
- options[:branch] = ARGV[branch_index + 1] if branch_index
12
10
  compose(ARGV.first, **options)
@@ -3,6 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  class Server < BaseServer
6
+ NON_REPORTABLE_SETUP_ERRORS = [Bundler::GemNotFound, Bundler::GitError].freeze #: Array[singleton(StandardError)]
7
+
6
8
  # Only for testing
7
9
  #: GlobalState
8
10
  attr_reader :global_state
@@ -315,7 +317,7 @@ module RubyLsp
315
317
 
316
318
  global_state_notifications.each { |notification| send_message(notification) }
317
319
 
318
- if @setup_error
320
+ if @setup_error && NON_REPORTABLE_SETUP_ERRORS.none? { |error_class| @setup_error.is_a?(error_class) }
319
321
  send_message(Notification.telemetry(
320
322
  type: "error",
321
323
  errorMessage: @setup_error.message,
@@ -413,7 +415,16 @@ module RubyLsp
413
415
  params = message[:params]
414
416
  text_document = params[:textDocument]
415
417
 
416
- @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
417
428
  end
418
429
 
419
430
  #: (Hash[Symbol, untyped] message) -> void
@@ -1031,6 +1042,23 @@ module RubyLsp
1031
1042
  # is fine, but we shouldn't process the same file changes more than once
1032
1043
  changes.uniq!
1033
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
+
1034
1062
  index = @global_state.index
1035
1063
  changes.each do |change|
1036
1064
  # File change events include folders, but we're only interested in files
@@ -1211,7 +1239,7 @@ module RubyLsp
1211
1239
  }
1212
1240
  end
1213
1241
  end
1214
- rescue Bundler::GemNotFound, Bundler::GemfileNotFound
1242
+ rescue Bundler::GemNotFound, Bundler::GemfileNotFound, Errno::ENOENT
1215
1243
  []
1216
1244
  end
1217
1245
 
@@ -1226,12 +1254,18 @@ module RubyLsp
1226
1254
 
1227
1255
  #: -> void
1228
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
+
1229
1263
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
1230
1264
  # stuck indexing files
1231
1265
  Thread.new do
1232
1266
  begin
1233
1267
  @global_state.index.index_all do |percentage|
1234
- progress("indexing-progress", percentage)
1268
+ progress("indexing-progress", percentage: percentage)
1235
1269
  true
1236
1270
  rescue ClosedQueueError
1237
1271
  # Since we run indexing on a separate thread, it's possible to kill the server before indexing is complete.
@@ -1285,11 +1319,13 @@ module RubyLsp
1285
1319
  send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1286
1320
  end
1287
1321
 
1288
- #: (String id, Integer percentage) -> void
1289
- def progress(id, percentage)
1322
+ #: (String, ?message: String?, ?percentage: Integer?) -> void
1323
+ def progress(id, message: nil, percentage: nil)
1290
1324
  return unless @global_state.client_capabilities.supports_progress
1291
1325
 
1292
- 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))
1293
1329
  end
1294
1330
 
1295
1331
  #: (String id) -> void
@@ -39,7 +39,6 @@ module RubyLsp
39
39
  #: (String project_path, **untyped options) -> void
40
40
  def initialize(project_path, **options)
41
41
  @project_path = project_path
42
- @branch = options[:branch] #: String?
43
42
  @launcher = options[:launcher] #: bool?
44
43
  @beta = options[:beta] #: bool?
45
44
  force_output_to_stderr! if @launcher
@@ -178,9 +177,7 @@ module RubyLsp
178
177
 
179
178
  unless @dependencies["ruby-lsp"]
180
179
  version = @beta ? "0.a" : RUBY_LSP_MIN_VERSION
181
- ruby_lsp_entry = +"gem \"ruby-lsp\", \">= #{version}\", require: false, group: :development"
182
- ruby_lsp_entry << ", github: \"Shopify/ruby-lsp\", branch: \"#{@branch}\"" if @branch
183
- parts << ruby_lsp_entry
180
+ parts << "gem \"ruby-lsp\", \">= #{version}\", require: false, group: :development"
184
181
  end
185
182
 
186
183
  unless @dependencies["debug"]
@@ -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.8
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