steep 0.49.1 → 0.52.0
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/CHANGELOG.md +22 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +8 -5
- data/lib/steep/ast/annotation/collection.rb +10 -8
- data/lib/steep/ast/types/factory.rb +5 -5
- data/lib/steep/cli.rb +83 -1
- data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
- data/lib/steep/diagnostic/ruby.rb +21 -15
- data/lib/steep/drivers/check.rb +1 -0
- data/lib/steep/drivers/langserver.rb +2 -2
- data/lib/steep/drivers/stats.rb +1 -0
- data/lib/steep/drivers/utils/jobs_count.rb +1 -1
- data/lib/steep/drivers/watch.rb +1 -1
- data/lib/steep/index/source_index.rb +18 -1
- data/lib/steep/interface/function.rb +5 -0
- data/lib/steep/module_helper.rb +4 -7
- data/lib/steep/project.rb +15 -1
- data/lib/steep/server/interaction_worker.rb +47 -4
- data/lib/steep/server/master.rb +10 -5
- data/lib/steep/server/type_check_worker.rb +19 -4
- data/lib/steep/server/worker_process.rb +15 -6
- data/lib/steep/services/completion_provider.rb +109 -1
- data/lib/steep/services/goto_service.rb +23 -10
- data/lib/steep/services/hover_content.rb +52 -4
- data/lib/steep/services/type_check_service.rb +6 -3
- data/lib/steep/source.rb +71 -18
- data/lib/steep/type_construction.rb +209 -162
- data/lib/steep/type_inference/constant_env.rb +33 -15
- data/lib/steep/type_inference/context.rb +6 -7
- data/lib/steep/type_inference/type_env.rb +16 -54
- data/lib/steep/version.rb +1 -1
- data/smoke/const/test_expectations.yml +24 -19
- data/smoke/diagnostics/test_expectations.yml +77 -17
- data/smoke/integer/test_expectations.yml +24 -4
- data/smoke/method/test_expectations.yml +30 -0
- data/smoke/regression/test_expectations.yml +24 -0
- data/smoke/yield/test_expectations.yml +20 -0
- data/steep.gemspec +1 -1
- metadata +5 -4
@@ -135,6 +135,21 @@ module Steep
|
|
135
135
|
end
|
136
136
|
|
137
137
|
def handle_job(job)
|
138
|
+
job_path = if job.respond_to?(:path)
|
139
|
+
if Gem.win_platform?
|
140
|
+
# FIXME: Sometimes drive letter is missing, using base_dir
|
141
|
+
if job.path.to_s.start_with?(%r{/[a-z](:|%3A)/}i)
|
142
|
+
job.path.to_s
|
143
|
+
else
|
144
|
+
"/#{project.base_dir.to_s.split("/").first}/#{job.path}"
|
145
|
+
end
|
146
|
+
else
|
147
|
+
job.path.to_s
|
148
|
+
end
|
149
|
+
else
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
|
138
153
|
case job
|
139
154
|
when StartTypeCheckJob
|
140
155
|
Steep.logger.info { "Processing StartTypeCheckJob for guid=#{job.guid}" }
|
@@ -149,7 +164,7 @@ module Steep
|
|
149
164
|
writer.write(
|
150
165
|
method: :"textDocument/publishDiagnostics",
|
151
166
|
params: LSP::Interface::PublishDiagnosticsParams.new(
|
152
|
-
uri: URI.parse(
|
167
|
+
uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
|
153
168
|
diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq
|
154
169
|
)
|
155
170
|
)
|
@@ -167,7 +182,7 @@ module Steep
|
|
167
182
|
writer.write(
|
168
183
|
method: :"textDocument/publishDiagnostics",
|
169
184
|
params: LSP::Interface::PublishDiagnosticsParams.new(
|
170
|
-
uri: URI.parse(
|
185
|
+
uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
|
171
186
|
diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq.compact
|
172
187
|
)
|
173
188
|
)
|
@@ -186,7 +201,7 @@ module Steep
|
|
186
201
|
writer.write(
|
187
202
|
method: :"textDocument/publishDiagnostics",
|
188
203
|
params: LSP::Interface::PublishDiagnosticsParams.new(
|
189
|
-
uri: URI.parse(
|
204
|
+
uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
|
190
205
|
diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq.compact
|
191
206
|
)
|
192
207
|
)
|
@@ -268,7 +283,7 @@ module Steep
|
|
268
283
|
line = job.params[:position][:line] + 1
|
269
284
|
column = job.params[:position][:character]
|
270
285
|
|
271
|
-
goto_service = Services::GotoService.new(type_check: service)
|
286
|
+
goto_service = Services::GotoService.new(type_check: service, assignment: assignment)
|
272
287
|
locations =
|
273
288
|
case
|
274
289
|
when job.definition?
|
@@ -18,13 +18,17 @@ module Steep
|
|
18
18
|
@index = index
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.spawn_worker(type, name:, steepfile:, options: [], delay_shutdown: false, index: nil)
|
22
|
-
|
21
|
+
def self.spawn_worker(type, name:, steepfile:, steep_command: "steep", options: [], delay_shutdown: false, index: nil)
|
22
|
+
args = ["--name=#{name}", "--steepfile=#{steepfile}"]
|
23
|
+
args << (%w(debug info warn error fatal unknown)[Steep.logger.level].yield_self {|log_level| "--log-level=#{log_level}" })
|
24
|
+
if Steep.log_output.is_a?(String)
|
25
|
+
args << "--log-output=#{Steep.log_output}"
|
26
|
+
end
|
23
27
|
command = case type
|
24
28
|
when :interaction
|
25
|
-
[
|
29
|
+
[steep_command, "worker", "--interaction", *args, *options]
|
26
30
|
when :typecheck
|
27
|
-
[
|
31
|
+
[steep_command, "worker", "--typecheck", *args, *options]
|
28
32
|
else
|
29
33
|
raise "Unknown type: #{type}"
|
30
34
|
end
|
@@ -33,7 +37,11 @@ module Steep
|
|
33
37
|
command << "--delay-shutdown"
|
34
38
|
end
|
35
39
|
|
36
|
-
stdin, stdout, thread =
|
40
|
+
stdin, stdout, thread = if Gem.win_platform?
|
41
|
+
Open3.popen2(*command, new_pgroup: true)
|
42
|
+
else
|
43
|
+
Open3.popen2(*command, pgroup: true)
|
44
|
+
end
|
37
45
|
stderr = nil
|
38
46
|
|
39
47
|
writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdin)
|
@@ -42,11 +50,12 @@ module Steep
|
|
42
50
|
new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name, index: index)
|
43
51
|
end
|
44
52
|
|
45
|
-
def self.spawn_typecheck_workers(steepfile:, args:, count: [Etc.nprocessors - 1, 1].max, delay_shutdown: false)
|
53
|
+
def self.spawn_typecheck_workers(steepfile:, args:, steep_command: "steep", count: [Etc.nprocessors - 1, 1].max, delay_shutdown: false)
|
46
54
|
count.times.map do |i|
|
47
55
|
spawn_worker(:typecheck,
|
48
56
|
name: "typecheck@#{i}",
|
49
57
|
steepfile: steepfile,
|
58
|
+
steep_command: steep_command,
|
50
59
|
options: ["--max-index=#{count}", "--index=#{i}", *args],
|
51
60
|
delay_shutdown: delay_shutdown,
|
52
61
|
index: i)
|
@@ -10,6 +10,30 @@ module Steep
|
|
10
10
|
|
11
11
|
InstanceVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
|
12
12
|
LocalVariableItem = Struct.new(:identifier, :range, :type, keyword_init: true)
|
13
|
+
ConstantItem = Struct.new(:env, :identifier, :range, :type, :full_name, keyword_init: true) do
|
14
|
+
def class?
|
15
|
+
if decl = env.class_decls[full_name]
|
16
|
+
decl.primary.decl.is_a?(RBS::AST::Declarations::Class)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def module?
|
21
|
+
if decl = env.class_decls[full_name]
|
22
|
+
decl.primary.decl.is_a?(RBS::AST::Declarations::Module)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def comments
|
27
|
+
case
|
28
|
+
when decl = env.class_decls[full_name]
|
29
|
+
decl.decls.filter_map {|d| d.decl.comment }
|
30
|
+
when comment = env.constant_decls[full_name]&.decl&.comment
|
31
|
+
[comment]
|
32
|
+
else
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
13
37
|
MethodNameItem = Struct.new(:identifier, :range, :receiver_type, :method_type, :method_decls, keyword_init: true) do
|
14
38
|
def comment
|
15
39
|
case method_decls.size
|
@@ -61,6 +85,10 @@ module Steep
|
|
61
85
|
end
|
62
86
|
end
|
63
87
|
|
88
|
+
def env
|
89
|
+
subtyping.factory.env
|
90
|
+
end
|
91
|
+
|
64
92
|
def run(line:, column:)
|
65
93
|
source_text = self.source_text.dup
|
66
94
|
index = index_for(source_text, line:line, column: column)
|
@@ -92,6 +120,15 @@ module Steep
|
|
92
120
|
source_text[index-1] = " "
|
93
121
|
type_check!(source_text, line: line, column: column)
|
94
122
|
items_for_atmark(position: position)
|
123
|
+
when ":"
|
124
|
+
if source_text[index-2] == ":"
|
125
|
+
source_text[index-1] = " "
|
126
|
+
source_text[index-2] = " "
|
127
|
+
type_check!(source_text, line: line, column: column)
|
128
|
+
items_for_colon2(position: position)
|
129
|
+
else
|
130
|
+
[]
|
131
|
+
end
|
95
132
|
else
|
96
133
|
[]
|
97
134
|
end
|
@@ -170,8 +207,25 @@ module Steep
|
|
170
207
|
prefix: prefix,
|
171
208
|
position: position,
|
172
209
|
items: items)
|
210
|
+
constant_items_for_context(context, prefix: prefix, position: position, items: items)
|
173
211
|
|
174
|
-
when node.type == :
|
212
|
+
when node.type == :const && node.children[0] && at_end?(position, of: node.loc)
|
213
|
+
# Foo::Ba ← (const)
|
214
|
+
parent_node = node.children[0]
|
215
|
+
parent_type = typing.type_of(node: parent_node)
|
216
|
+
|
217
|
+
if parent_type
|
218
|
+
prefix = node.children[1].to_s
|
219
|
+
|
220
|
+
method_items_for_receiver_type(parent_type,
|
221
|
+
include_private: false,
|
222
|
+
prefix: prefix,
|
223
|
+
position: position,
|
224
|
+
items: items)
|
225
|
+
constant_items_for_context(context, parent: parent_node, prefix: prefix, position: position, items: items)
|
226
|
+
end
|
227
|
+
|
228
|
+
when node.type == :send && at_end?(position, of: node.loc.dot) && node.loc.dot.source == "."
|
175
229
|
# foo.← ba
|
176
230
|
receiver_type = case (type = typing.type_of(node: node.children[0]))
|
177
231
|
when AST::Types::Self
|
@@ -186,6 +240,10 @@ module Steep
|
|
186
240
|
position: position,
|
187
241
|
items: items)
|
188
242
|
|
243
|
+
when node.type == :send && at_end?(position, of: node.loc.dot) && node.loc.dot.source == "::"
|
244
|
+
# foo::← ba
|
245
|
+
items.push(*items_for_colon2(position: position))
|
246
|
+
|
189
247
|
when node.type == :ivar && at_end?(position, of: node.loc)
|
190
248
|
# @fo ←
|
191
249
|
instance_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
|
@@ -198,6 +256,7 @@ module Steep
|
|
198
256
|
items: items)
|
199
257
|
local_variable_items_for_context(context, position: position, prefix: "", items: items)
|
200
258
|
instance_variable_items_for_context(context, position: position, prefix: "", items: items)
|
259
|
+
constant_items_for_context(context, position: position, prefix: "", items: items)
|
201
260
|
end
|
202
261
|
|
203
262
|
items
|
@@ -236,6 +295,31 @@ module Steep
|
|
236
295
|
end
|
237
296
|
end
|
238
297
|
|
298
|
+
def items_for_colon2(position:)
|
299
|
+
# :: ←
|
300
|
+
shift_pos = position-2
|
301
|
+
node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
|
302
|
+
node ||= source.node
|
303
|
+
|
304
|
+
items = []
|
305
|
+
case node&.type
|
306
|
+
when :const
|
307
|
+
# Constant:: ←
|
308
|
+
context = typing.context_at(line: position.line, column: position.column)
|
309
|
+
constant_items_for_context(context, parent: node, position: position, items: items, prefix: "")
|
310
|
+
when nil
|
311
|
+
# :: ←
|
312
|
+
context = typing.context_at(line: position.line, column: position.column)
|
313
|
+
constant_items_for_context(context, parent: nil, position: position, items: items, prefix: "")
|
314
|
+
end
|
315
|
+
|
316
|
+
if node
|
317
|
+
items.push(*items_for_dot(position: position - 1))
|
318
|
+
end
|
319
|
+
|
320
|
+
items
|
321
|
+
end
|
322
|
+
|
239
323
|
def items_for_atmark(position:)
|
240
324
|
# @ ←
|
241
325
|
shift_pos = position-1
|
@@ -290,6 +374,30 @@ module Steep
|
|
290
374
|
end
|
291
375
|
end
|
292
376
|
|
377
|
+
def constant_items_for_context(context, parent: nil, position:, prefix:, items:)
|
378
|
+
range = range_for(position, prefix: prefix)
|
379
|
+
|
380
|
+
if parent
|
381
|
+
case parent.type
|
382
|
+
when :const
|
383
|
+
const_name = typing.source_index.reference(constant_node: parent)
|
384
|
+
consts = context.module_context.const_env.children(const_name)
|
385
|
+
end
|
386
|
+
else
|
387
|
+
consts = context.module_context.const_env.constants
|
388
|
+
end
|
389
|
+
|
390
|
+
if consts
|
391
|
+
consts.each do |name, tuple|
|
392
|
+
type, full_name, _ = tuple
|
393
|
+
|
394
|
+
if name.to_s.start_with?(prefix)
|
395
|
+
items << ConstantItem.new(env: env, identifier: name, range: range, type: type, full_name: full_name)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
293
401
|
def instance_variable_items_for_context(context, position:, prefix:, items:)
|
294
402
|
range = range_for(position, prefix: prefix)
|
295
403
|
context.type_env.ivar_types.map do |name, type|
|
@@ -21,10 +21,11 @@ module Steep
|
|
21
21
|
end
|
22
22
|
TypeNameQuery = Struct.new(:name, keyword_init: true)
|
23
23
|
|
24
|
-
attr_reader :type_check
|
24
|
+
attr_reader :type_check, :assignment
|
25
25
|
|
26
|
-
def initialize(type_check:)
|
26
|
+
def initialize(type_check:, assignment:)
|
27
27
|
@type_check = type_check
|
28
|
+
@assignment = assignment
|
28
29
|
end
|
29
30
|
|
30
31
|
def project
|
@@ -78,7 +79,17 @@ module Steep
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
81
|
-
|
82
|
+
# Drop un-assigned paths here.
|
83
|
+
# The path assignment makes sense only for `.rbs` files, because un-assigned `.rb` files are already skipped since they are not type checked.
|
84
|
+
#
|
85
|
+
locations.uniq.select do |loc|
|
86
|
+
case loc
|
87
|
+
when RBS::Location
|
88
|
+
assignment =~ loc.name
|
89
|
+
else
|
90
|
+
true
|
91
|
+
end
|
92
|
+
end
|
82
93
|
end
|
83
94
|
|
84
95
|
def test_ast_location(loc, line:, column:)
|
@@ -100,14 +111,13 @@ module Steep
|
|
100
111
|
typing, signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
|
101
112
|
if typing
|
102
113
|
node, *parents = typing.source.find_nodes(line: line, column: column)
|
114
|
+
|
103
115
|
if node
|
104
116
|
case node.type
|
105
117
|
when :const, :casgn
|
106
118
|
if test_ast_location(node.location.name, line: line, column: column)
|
107
|
-
if
|
108
|
-
|
109
|
-
const = const_env.lookup_constant(module_name_from_node(node))
|
110
|
-
queries << ConstantQuery.new(name: const.name, from: :ruby)
|
119
|
+
if name = typing.source_index.reference(constant_node: node)
|
120
|
+
queries << ConstantQuery.new(name: name, from: :ruby)
|
111
121
|
end
|
112
122
|
end
|
113
123
|
when :def, :defs
|
@@ -125,7 +135,10 @@ module Steep
|
|
125
135
|
end
|
126
136
|
when :send
|
127
137
|
if test_ast_location(node.location.selector, line: line, column: column)
|
128
|
-
|
138
|
+
if (parent = parents[0]) && parent.type == :block && parent.children[0] == node
|
139
|
+
node = parents[0]
|
140
|
+
end
|
141
|
+
|
129
142
|
case call = typing.call_of(node: node)
|
130
143
|
when TypeInference::MethodCall::Typed, TypeInference::MethodCall::Error
|
131
144
|
call.method_decls.each do |decl|
|
@@ -232,8 +245,8 @@ module Steep
|
|
232
245
|
entry = typing.source_index.entry(constant: name)
|
233
246
|
entry.definitions.each do |node|
|
234
247
|
case node.type
|
235
|
-
when :
|
236
|
-
locations << node.
|
248
|
+
when :const
|
249
|
+
locations << node.location.expression
|
237
250
|
when :casgn
|
238
251
|
parent = node.children[0]
|
239
252
|
location =
|
@@ -5,16 +5,42 @@ module Steep
|
|
5
5
|
VariableContent = Struct.new(:node, :name, :type, :location, keyword_init: true)
|
6
6
|
MethodCallContent = Struct.new(:node, :method_name, :type, :definition, :location, keyword_init: true)
|
7
7
|
DefinitionContent = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true) do
|
8
|
-
TypeAliasContent = Struct.new(:location, :decl, keyword_init: true)
|
9
|
-
ClassContent = Struct.new(:location, :decl, keyword_init: true)
|
10
|
-
InterfaceContent = Struct.new(:location, :decl, keyword_init: true)
|
11
|
-
|
12
8
|
def comment_string
|
13
9
|
if comments = definition&.comments
|
14
10
|
comments.map {|c| c.string.chomp }.uniq.join("\n----\n")
|
15
11
|
end
|
16
12
|
end
|
17
13
|
end
|
14
|
+
ConstantContent = Struct.new(:location, :full_name, :type, :decl, keyword_init: true) do
|
15
|
+
def comment_string
|
16
|
+
if class_or_module?
|
17
|
+
comments = decl.decls.filter_map {|d| d.decl.comment&.string }
|
18
|
+
unless comments.empty?
|
19
|
+
return comments.join("\n----\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if constant?
|
24
|
+
if comment = decl.decl.comment
|
25
|
+
return comment.string
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def constant?
|
33
|
+
decl.is_a?(RBS::Environment::SingleEntry)
|
34
|
+
end
|
35
|
+
|
36
|
+
def class_or_module?
|
37
|
+
decl.is_a?(RBS::Environment::MultiEntry)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
TypeAliasContent = Struct.new(:location, :decl, keyword_init: true)
|
42
|
+
ClassContent = Struct.new(:location, :decl, keyword_init: true)
|
43
|
+
InterfaceContent = Struct.new(:location, :decl, keyword_init: true)
|
18
44
|
|
19
45
|
InstanceMethodName = Struct.new(:class_name, :method_name)
|
20
46
|
SingletonMethodName = Struct.new(:class_name, :method_name)
|
@@ -190,6 +216,28 @@ module Steep
|
|
190
216
|
location: node.loc.expression
|
191
217
|
)
|
192
218
|
end
|
219
|
+
when :const, :casgn
|
220
|
+
context = typing.context_at(line: line, column: column)
|
221
|
+
|
222
|
+
type = typing.type_of(node: node)
|
223
|
+
const_name = typing.source_index.reference(constant_node: node)
|
224
|
+
|
225
|
+
if const_name
|
226
|
+
decl = context.env.class_decls[const_name] || context.env.constant_decls[const_name]
|
227
|
+
|
228
|
+
ConstantContent.new(
|
229
|
+
location: node.location.name,
|
230
|
+
full_name: const_name,
|
231
|
+
type: type,
|
232
|
+
decl: decl
|
233
|
+
)
|
234
|
+
else
|
235
|
+
TypeContent.new(
|
236
|
+
node: node,
|
237
|
+
type: type,
|
238
|
+
location: node.location.expression
|
239
|
+
)
|
240
|
+
end
|
193
241
|
else
|
194
242
|
type = typing.type_of(node: node)
|
195
243
|
|
@@ -338,8 +338,12 @@ module Steep
|
|
338
338
|
end
|
339
339
|
|
340
340
|
def self.type_check(source:, subtyping:)
|
341
|
-
annotations = source.annotations(block: source.node, factory: subtyping.factory,
|
342
|
-
const_env = TypeInference::ConstantEnv.new(
|
341
|
+
annotations = source.annotations(block: source.node, factory: subtyping.factory, context: nil)
|
342
|
+
const_env = TypeInference::ConstantEnv.new(
|
343
|
+
factory: subtyping.factory,
|
344
|
+
context: nil,
|
345
|
+
resolver: RBS::Resolver::ConstantResolver.new(builder: subtyping.factory.definition_builder)
|
346
|
+
)
|
343
347
|
type_env = TypeInference::TypeEnv.build(annotations: annotations,
|
344
348
|
subtyping: subtyping,
|
345
349
|
const_env: const_env,
|
@@ -357,7 +361,6 @@ module Steep
|
|
357
361
|
instance_type: AST::Builtin::Object.instance_type,
|
358
362
|
module_type: AST::Builtin::Object.module_type,
|
359
363
|
implement_name: nil,
|
360
|
-
current_namespace: RBS::Namespace.root,
|
361
364
|
const_env: const_env,
|
362
365
|
class_name: AST::Builtin::Object.module_name,
|
363
366
|
instance_definition: subtyping.factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
|
data/lib/steep/source.rb
CHANGED
@@ -283,11 +283,11 @@ module Steep
|
|
283
283
|
node.updated(nil, children)
|
284
284
|
end
|
285
285
|
|
286
|
-
def annotations(block:, factory:,
|
286
|
+
def annotations(block:, factory:, context:)
|
287
287
|
AST::Annotation::Collection.new(
|
288
288
|
annotations: (mapping[block] || []).map(&:annotation),
|
289
289
|
factory: factory,
|
290
|
-
|
290
|
+
context: context
|
291
291
|
)
|
292
292
|
end
|
293
293
|
|
@@ -301,11 +301,42 @@ module Steep
|
|
301
301
|
end
|
302
302
|
end
|
303
303
|
|
304
|
-
def
|
305
|
-
|
306
|
-
node.
|
307
|
-
|
304
|
+
def each_heredoc_node(node = self.node, parents = [], &block)
|
305
|
+
if block
|
306
|
+
case node.type
|
307
|
+
when :dstr, :str
|
308
|
+
if node.location.is_a?(Parser::Source::Map::Heredoc)
|
309
|
+
yield [node, *parents]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
parents.unshift(node)
|
314
|
+
Source.each_child_node(node) do |child|
|
315
|
+
each_heredoc_node(child, parents, &block)
|
316
|
+
end
|
317
|
+
parents.shift()
|
318
|
+
else
|
319
|
+
enum_for :each_heredoc_node, node
|
320
|
+
end
|
321
|
+
end
|
308
322
|
|
323
|
+
def find_heredoc_nodes(line, column, position)
|
324
|
+
each_heredoc_node() do |nodes|
|
325
|
+
node = nodes[0]
|
326
|
+
|
327
|
+
range = node.location.heredoc_body&.yield_self do |r|
|
328
|
+
r.begin_pos..r.end_pos
|
329
|
+
end
|
330
|
+
|
331
|
+
if range && (range === position)
|
332
|
+
return nodes
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
nil
|
337
|
+
end
|
338
|
+
|
339
|
+
def find_nodes_loc(node, position, parents)
|
309
340
|
range = node.location.expression&.yield_self do |r|
|
310
341
|
r.begin_pos..r.end_pos
|
311
342
|
end
|
@@ -315,7 +346,7 @@ module Steep
|
|
315
346
|
parents.unshift node
|
316
347
|
|
317
348
|
Source.each_child_node(node) do |child|
|
318
|
-
ns =
|
349
|
+
ns = find_nodes_loc(child, position, parents) and return ns
|
319
350
|
end
|
320
351
|
|
321
352
|
parents
|
@@ -323,6 +354,24 @@ module Steep
|
|
323
354
|
end
|
324
355
|
end
|
325
356
|
|
357
|
+
def find_nodes(line:, column:)
|
358
|
+
return [] unless node
|
359
|
+
|
360
|
+
position = (line-1).times.sum do |i|
|
361
|
+
node.location.expression.source_buffer.source_line(i+1).size + 1
|
362
|
+
end + column
|
363
|
+
|
364
|
+
if nodes = find_heredoc_nodes(line, column, position)
|
365
|
+
Source.each_child_node(nodes[0]) do |child|
|
366
|
+
find_nodes_loc(child, position, nodes) and break
|
367
|
+
end
|
368
|
+
|
369
|
+
nodes
|
370
|
+
else
|
371
|
+
find_nodes_loc(node, position, [])
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
326
375
|
def self.delete_defs(node, allow_list)
|
327
376
|
case node.type
|
328
377
|
when :def
|
@@ -345,22 +394,26 @@ module Steep
|
|
345
394
|
end
|
346
395
|
|
347
396
|
def without_unrelated_defs(line:, column:)
|
348
|
-
|
349
|
-
|
397
|
+
if node
|
398
|
+
nodes = find_nodes(line: line, column: column) || []
|
399
|
+
defs = Set[].compare_by_identity.merge(nodes.select {|node| node.type == :def || node.type == :defs })
|
350
400
|
|
351
|
-
|
401
|
+
node_ = Source.delete_defs(node, defs)
|
352
402
|
|
353
|
-
|
403
|
+
mapping = {}.compare_by_identity
|
354
404
|
|
355
|
-
|
356
|
-
|
405
|
+
annotations = self.mapping.values.flatten
|
406
|
+
Source.construct_mapping(node: node_, annotations: annotations, mapping: mapping)
|
357
407
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
408
|
+
annotations.each do |annot|
|
409
|
+
mapping[node] ||= []
|
410
|
+
mapping[node] << annot
|
411
|
+
end
|
362
412
|
|
363
|
-
|
413
|
+
Source.new(path: path, node: node_, mapping: mapping)
|
414
|
+
else
|
415
|
+
self
|
416
|
+
end
|
364
417
|
end
|
365
418
|
|
366
419
|
def compact_siblings(node)
|