ruby-lsp 0.26.9 → 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.
@@ -62,12 +62,12 @@ module RubyLsp
62
62
  when Prism::ClassNode, Prism::ModuleNode
63
63
  nesting << node.constant_path.slice
64
64
  when Prism::SingletonClassNode
65
- nesting << "<Class:#{nesting.flat_map { |n| n.split("::") }.last}>"
65
+ nesting << "<#{nesting.flat_map { |n| n.split("::") }.last}>"
66
66
  when Prism::DefNode
67
67
  surrounding_method = node.name.to_s
68
68
  next unless node.receiver.is_a?(Prism::SelfNode)
69
69
 
70
- nesting << "<Class:#{nesting.flat_map { |n| n.split("::") }.last}>"
70
+ nesting << "<#{nesting.flat_map { |n| n.split("::") }.last}>"
71
71
  end
72
72
  end
73
73
 
@@ -120,6 +120,10 @@ module RubyLsp
120
120
 
121
121
  return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
122
122
 
123
+ # Endless method definitions (e.g., `def foo = 42`) are complete statements
124
+ # and should not have `end` added
125
+ return if @previous_line.match?(/\bdef\s+[\w.]+[!?=]?(\([^)]*\))?\s*=[^=~>]/)
126
+
123
127
  indents = " " * @indentation
124
128
  current_line = @lines[@position[:line]]
125
129
  next_line = @lines[@position[:line] + 1]
@@ -22,6 +22,7 @@ module RubyLsp
22
22
  def initialize(global_state, store, document, params)
23
23
  super()
24
24
  @global_state = global_state
25
+ @graph = global_state.graph #: Rubydex::Graph
25
26
  @store = store
26
27
  @document = document
27
28
  @position = params[:position] #: Hash[Symbol, Integer]
@@ -56,17 +57,14 @@ module RubyLsp
56
57
  name = RubyIndexer::Index.constant_name(target)
57
58
  return unless name
58
59
 
59
- entries = @global_state.index.resolve(name, node_context.nesting)
60
- return unless entries
60
+ declaration = @graph.resolve_constant(name, node_context.nesting)
61
+ return unless declaration
61
62
 
62
- if (conflict_entries = @global_state.index.resolve(@new_name, node_context.nesting))
63
- raise InvalidNameError, "The new name is already in use by #{conflict_entries.first&.name}"
63
+ if (conflict = @graph.resolve_constant(@new_name, node_context.nesting))
64
+ raise InvalidNameError, "The new name is already in use by #{conflict.name}"
64
65
  end
65
66
 
66
- fully_qualified_name = entries.first #: as !nil
67
- .name
68
- reference_target = RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
69
- changes = collect_text_edits(reference_target, name)
67
+ changes = collect_text_edits(declaration, name)
70
68
 
71
69
  # If the client doesn't support resource operations, such as renaming files, then we can only return the basic
72
70
  # text changes
@@ -78,99 +76,93 @@ module RubyLsp
78
76
  # renamed and then the URI associated to the text edit no longer exists, causing it to be dropped
79
77
  document_changes = changes.map do |uri, edits|
80
78
  Interface::TextDocumentEdit.new(
81
- text_document: Interface::VersionedTextDocumentIdentifier.new(uri: uri, version: nil),
79
+ text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(uri: uri, version: nil),
82
80
  edits: edits,
83
81
  )
84
82
  end
85
83
 
86
- collect_file_renames(fully_qualified_name, document_changes)
84
+ collect_file_renames(declaration, document_changes)
87
85
  Interface::WorkspaceEdit.new(document_changes: document_changes)
88
86
  end
89
87
 
90
88
  private
91
89
 
92
- #: (String fully_qualified_name, Array[(Interface::RenameFile | Interface::TextDocumentEdit)] document_changes) -> void
93
- def collect_file_renames(fully_qualified_name, document_changes)
90
+ #: (Rubydex::Declaration, Array[(Interface::RenameFile | Interface::TextDocumentEdit)]) -> void
91
+ def collect_file_renames(declaration, document_changes)
94
92
  # Check if the declarations of the symbol being renamed match the file name. In case they do, we automatically
95
93
  # rename the files for the user.
96
94
  #
97
95
  # We also look for an associated test file and rename it too
98
- short_name = fully_qualified_name.split("::").last #: as !nil
99
96
 
100
- @global_state.index[fully_qualified_name]&.each do |entry|
97
+ unless [
98
+ Rubydex::Class,
99
+ Rubydex::Module,
100
+ Rubydex::Constant,
101
+ Rubydex::ConstantAlias,
102
+ ].any? { |type| declaration.is_a?(type) }
103
+ return
104
+ end
105
+
106
+ short_name = declaration.unqualified_name
107
+
108
+ declaration.definitions.each do |definition|
101
109
  # Do not rename files that are not part of the workspace
102
- uri = entry.uri
110
+ uri = URI(definition.location.uri)
103
111
  file_path = uri.full_path
104
112
  next unless file_path&.start_with?(@global_state.workspace_path)
105
113
 
106
- case entry
107
- when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
108
- RubyIndexer::Entry::ConstantAlias, RubyIndexer::Entry::UnresolvedConstantAlias
109
-
110
- file_name = file_from_constant_name(short_name)
114
+ file_name = file_from_constant_name(short_name)
115
+ next unless "#{file_name}.rb" == File.basename(file_path)
111
116
 
112
- if "#{file_name}.rb" == entry.file_name
113
- new_file_name = file_from_constant_name(
114
- @new_name.split("::").last, #: as !nil
115
- )
117
+ new_file_name = file_from_constant_name(
118
+ @new_name.split("::").last, #: as !nil
119
+ )
116
120
 
117
- new_uri = URI::Generic.from_path(path: File.join(
118
- File.dirname(file_path),
119
- "#{new_file_name}.rb",
120
- )).to_s
121
+ new_uri = URI::Generic.from_path(path: File.join(
122
+ File.dirname(file_path),
123
+ "#{new_file_name}.rb",
124
+ )).to_s
121
125
 
122
- document_changes << Interface::RenameFile.new(kind: "rename", old_uri: uri.to_s, new_uri: new_uri)
123
- end
124
- end
126
+ document_changes << Interface::RenameFile.new(kind: "rename", old_uri: uri.to_s, new_uri: new_uri)
125
127
  end
126
128
  end
127
129
 
128
- #: (RubyIndexer::ReferenceFinder::Target target, String name) -> Hash[String, Array[Interface::TextEdit]]
129
- def collect_text_edits(target, name)
130
- changes = {}
131
-
132
- Dir.glob(File.join(@global_state.workspace_path, "**/*.rb")).each do |path|
133
- uri = URI::Generic.from_path(path: path)
134
- # If the document is being managed by the client, then we should use whatever is present in the store instead
135
- # of reading from disk
136
- next if @store.key?(uri)
137
-
138
- parse_result = Prism.parse_file(path)
139
- edits = collect_changes(target, parse_result.value, name, uri)
140
- changes[uri.to_s] = edits unless edits.empty?
141
- rescue Errno::EISDIR, Errno::ENOENT
142
- # If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
143
- end
144
-
145
- @store.each do |uri, document|
146
- next unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
130
+ #: (Rubydex::Declaration declaration, String name) -> Hash[String, Array[Interface::TextEdit]]
131
+ def collect_text_edits(declaration, name)
132
+ changes = {} #: Hash[String, Array[Interface::TextEdit]]
133
+ short_name = name.split("::").last #: as !nil
134
+ new_short_name = @new_name.split("::").last #: as !nil
135
+
136
+ # Collect edits for definition sites (where the constant is declared)
137
+ declaration.definitions.each do |definition|
138
+ name_loc = definition.name_location
139
+ next unless name_loc
140
+
141
+ uri_string = name_loc.uri
142
+ edits = (changes[uri_string] ||= [])
143
+
144
+ # The name_location spans the constant name as written in the definition.
145
+ # We only replace the unqualified name portion (the last segment).
146
+ range = Interface::Range.new(
147
+ start: Interface::Position.new(
148
+ line: name_loc.end_line,
149
+ character: name_loc.end_column - short_name.length,
150
+ ),
151
+ end: Interface::Position.new(line: name_loc.end_line, character: name_loc.end_column),
152
+ )
147
153
 
148
- edits = collect_changes(target, document.ast, name, document.uri)
149
- changes[uri] = edits unless edits.empty?
154
+ edits << Interface::TextEdit.new(range: range, new_text: new_short_name)
150
155
  end
151
156
 
152
- changes
153
- end
154
-
155
- #: (RubyIndexer::ReferenceFinder::Target target, Prism::Node ast, String name, URI::Generic uri) -> Array[Interface::TextEdit]
156
- def collect_changes(target, ast, name, uri)
157
- dispatcher = Prism::Dispatcher.new
158
- finder = RubyIndexer::ReferenceFinder.new(target, @global_state.index, dispatcher, uri)
159
- dispatcher.visit(ast)
160
-
161
- finder.references.map do |reference|
162
- adjust_reference_for_edit(name, reference)
157
+ # Collect edits for reference sites (where the constant is used)
158
+ declaration.references.each do |reference|
159
+ ref = reference #: as Rubydex::ConstantReference
160
+ uri_string = ref.location.uri
161
+ edits = (changes[uri_string] ||= [])
162
+ edits << Interface::TextEdit.new(range: ref.to_lsp_range, new_text: new_short_name)
163
163
  end
164
- end
165
-
166
- #: (String name, RubyIndexer::ReferenceFinder::Reference reference) -> Interface::TextEdit
167
- def adjust_reference_for_edit(name, reference)
168
- # The reference may include a namespace in front. We need to check if the rename new name includes namespaces
169
- # and then adjust both the text and the location to produce the correct edit
170
- location = reference.location
171
- new_text = reference.name.sub(name, @new_name)
172
164
 
173
- Interface::TextEdit.new(range: range_from_location(location), new_text: new_text)
165
+ changes
174
166
  end
175
167
 
176
168
  #: (String constant_name) -> String
@@ -64,6 +64,46 @@ module RubyLsp
64
64
  receiver.nil? || receiver.is_a?(Prism::SelfNode)
65
65
  end
66
66
 
67
+ #: (String, Enumerable[Rubydex::Definition], ?Integer?) -> Hash[Symbol, String]
68
+ def categorized_markdown_from_definitions(title, definitions, max_entries = nil)
69
+ markdown_title = "```ruby\n#{title}\n```"
70
+ file_links = []
71
+ content = +""
72
+ defs = max_entries ? definitions.take(max_entries) : definitions
73
+ defs.each do |definition|
74
+ # For Markdown links, we need 1 based display locations
75
+ loc = definition.location.to_display
76
+ uri = URI(loc.uri)
77
+ file_name = if uri.scheme == "untitled"
78
+ uri.opaque #: as !nil
79
+ else
80
+ File.basename(
81
+ uri.full_path, #: as !nil
82
+ )
83
+ end
84
+
85
+ # The format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
86
+ string_uri = "#{loc.uri}#L#{loc.start_line},#{loc.start_column}-#{loc.end_line},#{loc.end_column}"
87
+ file_links << "[#{file_name}](#{string_uri})"
88
+ content << "\n\n#{definition.comments.map { |comment| comment.string.delete_prefix("# ") }.join("\n")}" unless definition.comments.empty?
89
+ end
90
+
91
+ total_definitions = definitions.count
92
+
93
+ additional_entries_text = if max_entries && total_definitions > max_entries
94
+ additional = total_definitions - max_entries
95
+ " | #{additional} other#{additional > 1 ? "s" : ""}"
96
+ else
97
+ ""
98
+ end
99
+
100
+ {
101
+ title: markdown_title,
102
+ links: "**Definitions**: #{file_links.join(" | ")}#{additional_entries_text}",
103
+ documentation: content,
104
+ }
105
+ end
106
+
67
107
  #: (String title, (Array[RubyIndexer::Entry] | RubyIndexer::Entry) entries, ?Integer? max_entries) -> Hash[Symbol, String]
68
108
  def categorized_markdown_from_index_entries(title, entries, max_entries = nil)
69
109
  markdown_title = "```ruby\n#{title}\n```"
@@ -12,54 +12,32 @@ module RubyLsp
12
12
  #: (GlobalState global_state, String? query) -> void
13
13
  def initialize(global_state, query)
14
14
  super()
15
- @global_state = global_state
16
15
  @query = query
17
- @index = global_state.index #: RubyIndexer::Index
16
+ @graph = global_state.graph #: Rubydex::Graph
18
17
  end
19
18
 
20
19
  # @override
21
20
  #: -> Array[Interface::WorkspaceSymbol]
22
21
  def perform
23
- fuzzy_search.filter_map do |entry|
24
- kind = kind_for_entry(entry)
25
- loc = entry.location
22
+ response = []
26
23
 
27
- # We use the namespace as the container name, but we also use the full name as the regular name. The reason we
28
- # do this is to allow people to search for fully qualified names (e.g.: `Foo::Bar`). If we only included the
29
- # short name `Bar`, then searching for `Foo::Bar` would not return any results
30
- *container, _short_name = entry.name.split("::")
24
+ @graph.fuzzy_search(@query || "").each do |declaration|
25
+ name = declaration.name
31
26
 
32
- Interface::WorkspaceSymbol.new(
33
- name: entry.name,
34
- container_name: container.join("::"),
35
- kind: kind,
36
- location: Interface::Location.new(
37
- uri: entry.uri.to_s,
38
- range: Interface::Range.new(
39
- start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
40
- end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
41
- ),
42
- ),
43
- )
44
- end
45
- end
46
-
47
- private
27
+ declaration.definitions.each do |definition|
28
+ location = definition.location
29
+ uri = URI(location.uri)
30
+ file_path = uri.full_path
48
31
 
49
- #: -> Array[RubyIndexer::Entry]
50
- def fuzzy_search
51
- @index.fuzzy_search(@query) do |entry|
52
- file_path = entry.uri.full_path
32
+ # We only show symbols declared in the workspace
33
+ in_dependencies = file_path && !not_in_dependencies?(file_path)
34
+ next if in_dependencies
53
35
 
54
- # We only show symbols declared in the workspace
55
- in_dependencies = file_path && !not_in_dependencies?(file_path)
56
- next if in_dependencies
57
-
58
- # We should never show private symbols when searching the entire workspace
59
- next if entry.private?
60
-
61
- true
36
+ response << definition.to_lsp_workspace_symbol(name)
37
+ end
62
38
  end
39
+
40
+ response
63
41
  end
64
42
  end
65
43
  end
@@ -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
@@ -415,7 +415,16 @@ module RubyLsp
415
415
  params = message[:params]
416
416
  text_document = params[:textDocument]
417
417
 
418
- @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
419
428
  end
420
429
 
421
430
  #: (Hash[Symbol, untyped] message) -> void
@@ -1033,6 +1042,23 @@ module RubyLsp
1033
1042
  # is fine, but we shouldn't process the same file changes more than once
1034
1043
  changes.uniq!
1035
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
+
1036
1062
  index = @global_state.index
1037
1063
  changes.each do |change|
1038
1064
  # File change events include folders, but we're only interested in files
@@ -1228,12 +1254,18 @@ module RubyLsp
1228
1254
 
1229
1255
  #: -> void
1230
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
+
1231
1263
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
1232
1264
  # stuck indexing files
1233
1265
  Thread.new do
1234
1266
  begin
1235
1267
  @global_state.index.index_all do |percentage|
1236
- progress("indexing-progress", percentage)
1268
+ progress("indexing-progress", percentage: percentage)
1237
1269
  true
1238
1270
  rescue ClosedQueueError
1239
1271
  # Since we run indexing on a separate thread, it's possible to kill the server before indexing is complete.
@@ -1287,11 +1319,13 @@ module RubyLsp
1287
1319
  send_message(Notification.progress_begin(id, title, percentage: percentage, message: "#{percentage}% completed"))
1288
1320
  end
1289
1321
 
1290
- #: (String id, Integer percentage) -> void
1291
- def progress(id, percentage)
1322
+ #: (String, ?message: String?, ?percentage: Integer?) -> void
1323
+ def progress(id, message: nil, percentage: nil)
1292
1324
  return unless @global_state.client_capabilities.supports_progress
1293
1325
 
1294
- 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))
1295
1329
  end
1296
1330
 
1297
1331
  #: (String id) -> void
@@ -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