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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -8
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +2 -2
- data/lib/ruby_lsp/global_state.rb +10 -1
- data/lib/ruby_lsp/internal.rb +5 -0
- data/lib/ruby_lsp/listeners/definition.rb +64 -98
- data/lib/ruby_lsp/listeners/hover.rb +35 -50
- data/lib/ruby_lsp/listeners/test_style.rb +1 -1
- data/lib/ruby_lsp/node_context.rb +2 -2
- data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -0
- data/lib/ruby_lsp/requests/rename.rb +64 -72
- data/lib/ruby_lsp/requests/support/common.rb +40 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +15 -37
- data/lib/ruby_lsp/rubydex/definition.rb +200 -0
- data/lib/ruby_lsp/rubydex/reference.rb +16 -0
- data/lib/ruby_lsp/server.rb +39 -5
- data/lib/ruby_lsp/store.rb +0 -6
- data/lib/ruby_lsp/test_helper.rb +3 -0
- data/lib/ruby_lsp/type_inferrer.rb +25 -23
- metadata +17 -1
|
@@ -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 << "
|
|
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 << "
|
|
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
|
-
|
|
60
|
-
return unless
|
|
60
|
+
declaration = @graph.resolve_constant(name, node_context.nesting)
|
|
61
|
+
return unless declaration
|
|
61
62
|
|
|
62
|
-
if (
|
|
63
|
-
raise InvalidNameError, "The new name is already in use by #{
|
|
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
|
-
|
|
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::
|
|
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(
|
|
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
|
-
#: (
|
|
93
|
-
def collect_file_renames(
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
117
|
+
new_file_name = file_from_constant_name(
|
|
118
|
+
@new_name.split("::").last, #: as !nil
|
|
119
|
+
)
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
#: (
|
|
129
|
-
def collect_text_edits(
|
|
130
|
-
changes = {}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
changes[
|
|
141
|
-
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
24
|
-
kind = kind_for_entry(entry)
|
|
25
|
-
loc = entry.location
|
|
22
|
+
response = []
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
data/lib/ruby_lsp/server.rb
CHANGED
|
@@ -415,7 +415,16 @@ module RubyLsp
|
|
|
415
415
|
params = message[:params]
|
|
416
416
|
text_document = params[:textDocument]
|
|
417
417
|
|
|
418
|
-
@store.
|
|
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
|
|
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
|
-
|
|
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
|
data/lib/ruby_lsp/store.rb
CHANGED
|
@@ -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
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
|
@@ -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
|