steep 0.30.0 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/bin/steep-prof +1 -2
- data/lib/steep/ast/types/factory.rb +14 -1
- data/lib/steep/project/completion_provider.rb +9 -7
- data/lib/steep/project/hover_content.rb +91 -80
- data/lib/steep/server/interaction_worker.rb +42 -38
- data/lib/steep/source.rb +57 -0
- data/lib/steep/version.rb +1 -1
- data/steep.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e315b0a9541b0289ae3c9dd3acb19c2112e990a321176f94ed44f25d4190acb4
|
4
|
+
data.tar.gz: 9ce6e2024e81e5a2452fc5d7ca381decd1d91ed917d385dc280133652af51e06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd8db01573d1a2e31b362fb893bfad1325969b8343415d1bfd82428a76f0a6c8f8fe2875f7e474ab94244476e9901b1dda186f57cb90a49602638453826534a8
|
7
|
+
data.tar.gz: f1f30e2afc48265917468a4c5dd040fff3e226f92cb17441c3f3fcbba7f95aaa1002172ece399410d567358d9caf7bd80c1f1c27b9083c81a39e1abe61b74a68
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.31.0 (2020-10-04)
|
6
|
+
|
7
|
+
* Fix type checking performance ([#230](https://github.com/soutaro/steep/pull/230))
|
8
|
+
* Improve LSP completion/hover performance ([#232](https://github.com/soutaro/steep/pull/232))
|
9
|
+
* Fix instance variable completion ([#234](https://github.com/soutaro/steep/pull/234))
|
10
|
+
* Relax version requirements on Listen to allow installing on Ruby 3 ([#235](https://github.com/soutaro/steep/pull/235))
|
11
|
+
|
5
12
|
## 0.30.0 (2020-10-03)
|
6
13
|
|
7
14
|
* Let top-level defs be methods of Object ([#227](https://github.com/soutaro/steep/pull/227))
|
data/bin/steep-prof
CHANGED
@@ -7,11 +7,14 @@ module Steep
|
|
7
7
|
attr_reader :type_name_cache
|
8
8
|
attr_reader :type_cache
|
9
9
|
|
10
|
+
attr_reader :type_interface_cache
|
11
|
+
|
10
12
|
def initialize(builder:)
|
11
13
|
@definition_builder = builder
|
12
14
|
|
13
15
|
@type_name_cache = {}
|
14
16
|
@type_cache = {}
|
17
|
+
@type_interface_cache = {}
|
15
18
|
end
|
16
19
|
|
17
20
|
def type_name_resolver
|
@@ -390,15 +393,23 @@ module Steep
|
|
390
393
|
|
391
394
|
def interface(type, private:, self_type: type)
|
392
395
|
Steep.logger.debug { "Factory#interface: #{type}, private=#{private}, self_type=#{self_type}" }
|
393
|
-
|
396
|
+
|
397
|
+
cache_key = [type, self_type, private]
|
398
|
+
if type_interface_cache.key?(cache_key)
|
399
|
+
return type_interface_cache[cache_key]
|
400
|
+
end
|
394
401
|
|
395
402
|
case type
|
403
|
+
when Name::Alias
|
404
|
+
interface(expand_alias(type), private: private, self_type: self_type)
|
405
|
+
|
396
406
|
when Self
|
397
407
|
if self_type != type
|
398
408
|
interface self_type, private: private, self_type: Self.new
|
399
409
|
else
|
400
410
|
raise "Unexpected `self` type interface"
|
401
411
|
end
|
412
|
+
|
402
413
|
when Name::Instance
|
403
414
|
Interface::Interface.new(type: self_type, private: private).tap do |interface|
|
404
415
|
definition = definition_builder.build_instance(type.name)
|
@@ -689,6 +700,8 @@ module Steep
|
|
689
700
|
|
690
701
|
else
|
691
702
|
raise "Unexpected type for interface: #{type}"
|
703
|
+
end.tap do |interface|
|
704
|
+
type_interface_cache[cache_key] = interface
|
692
705
|
end
|
693
706
|
end
|
694
707
|
|
@@ -29,11 +29,13 @@ module Steep
|
|
29
29
|
@subtyping = subtyping
|
30
30
|
end
|
31
31
|
|
32
|
-
def type_check!(text)
|
32
|
+
def type_check!(text, line:, column:)
|
33
33
|
@modified_text = text
|
34
34
|
|
35
35
|
Steep.measure "parsing" do
|
36
|
-
@source = SourceFile
|
36
|
+
@source = SourceFile
|
37
|
+
.parse(text, path: path, factory: subtyping.factory)
|
38
|
+
.without_unrelated_defs(line: line, column: column)
|
37
39
|
end
|
38
40
|
|
39
41
|
Steep.measure "typechecking" do
|
@@ -53,7 +55,7 @@ module Steep
|
|
53
55
|
begin
|
54
56
|
Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
|
55
57
|
Steep.measure "type_check!" do
|
56
|
-
type_check!(source_text)
|
58
|
+
type_check!(source_text, line: line, column: column)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
@@ -66,11 +68,11 @@ module Steep
|
|
66
68
|
case possible_trigger
|
67
69
|
when "."
|
68
70
|
source_text[index-1] = " "
|
69
|
-
type_check!(source_text)
|
71
|
+
type_check!(source_text, line: line, column: column)
|
70
72
|
items_for_dot(position: position)
|
71
73
|
when "@"
|
72
74
|
source_text[index-1] = " "
|
73
|
-
type_check!(source_text)
|
75
|
+
type_check!(source_text, line: line, column: column)
|
74
76
|
items_for_atmark(position: position)
|
75
77
|
else
|
76
78
|
[]
|
@@ -219,14 +221,14 @@ module Steep
|
|
219
221
|
def items_for_atmark(position:)
|
220
222
|
# @ ←
|
221
223
|
shift_pos = position-1
|
222
|
-
node, *
|
224
|
+
node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
|
223
225
|
node ||= source.node
|
224
226
|
|
225
227
|
return [] unless node
|
226
228
|
|
227
229
|
context = typing.context_at(line: position.line, column: position.column)
|
228
230
|
items = []
|
229
|
-
instance_variable_items_for_context(context, prefix: "", position: position, items: items)
|
231
|
+
instance_variable_items_for_context(context, prefix: "@", position: position, items: items)
|
230
232
|
items
|
231
233
|
end
|
232
234
|
|
@@ -36,102 +36,113 @@ module Steep
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
def typecheck(target, path:, line:, column:)
|
40
|
+
target.type_check(target_sources: [], validate_signatures: false)
|
41
|
+
|
42
|
+
case (status = target.status)
|
43
|
+
when Project::Target::TypeCheckStatus
|
44
|
+
subtyping = status.subtyping
|
45
|
+
source = SourceFile
|
46
|
+
.parse(target.source_files[path].content, path: path, factory: subtyping.factory)
|
47
|
+
.without_unrelated_defs(line: line, column: column)
|
48
|
+
SourceFile.type_check(source, subtyping: subtyping)
|
49
|
+
end
|
50
|
+
rescue
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
39
54
|
def content_for(path:, line:, column:)
|
40
|
-
target = project.
|
55
|
+
target = project.target_for_source_path(path)
|
41
56
|
|
42
57
|
if target
|
43
|
-
|
44
|
-
|
58
|
+
typing = typecheck(target, path: path, line: line, column: column) or return
|
59
|
+
|
60
|
+
node, *parents = typing.source.find_nodes(line: line, column: column)
|
61
|
+
|
62
|
+
if node
|
63
|
+
case node.type
|
64
|
+
when :lvar
|
65
|
+
var_name = node.children[0]
|
66
|
+
context = typing.context_at(line: line, column: column)
|
67
|
+
var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
|
45
68
|
|
46
|
-
|
47
|
-
|
48
|
-
|
69
|
+
VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
|
70
|
+
when :lvasgn
|
71
|
+
var_name, rhs = node.children
|
72
|
+
context = typing.context_at(line: line, column: column)
|
73
|
+
type = context.lvar_env[var_name.name] || typing.type_of(node: rhs)
|
49
74
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
var_name = node.children[0]
|
54
|
-
context = status.typing.context_at(line: line, column: column)
|
55
|
-
var_type = context.lvar_env[var_name.name] || AST::Types::Any.new(location: nil)
|
75
|
+
VariableContent.new(node: node, name: var_name.name, type: type, location: node.location.name)
|
76
|
+
when :send
|
77
|
+
receiver, method_name, *_ = node.children
|
56
78
|
|
57
|
-
VariableContent.new(node: node, name: var_name.name, type: var_type, location: node.location.name)
|
58
|
-
when :lvasgn
|
59
|
-
var_name, rhs = node.children
|
60
|
-
context = status.typing.context_at(line: line, column: column)
|
61
|
-
type = context.lvar_env[var_name.name] || status.typing.type_of(node: rhs)
|
62
79
|
|
63
|
-
|
64
|
-
|
65
|
-
|
80
|
+
result_node = if parents[0]&.type == :block
|
81
|
+
parents[0]
|
82
|
+
else
|
83
|
+
node
|
84
|
+
end
|
66
85
|
|
86
|
+
context = typing.context_at(line: line, column: column)
|
67
87
|
|
68
|
-
|
69
|
-
|
88
|
+
receiver_type = if receiver
|
89
|
+
typing.type_of(node: receiver)
|
70
90
|
else
|
71
|
-
|
91
|
+
context.self_type
|
72
92
|
end
|
73
93
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
when AST::Types::Name::Instance
|
85
|
-
method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
|
86
|
-
if method_definition&.defined_in
|
87
|
-
owner_name = method_definition.defined_in
|
88
|
-
[
|
89
|
-
InstanceMethodName.new(owner_name, method_name),
|
90
|
-
method_definition
|
91
|
-
]
|
92
|
-
end
|
93
|
-
when AST::Types::Name::Singleton
|
94
|
-
method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
|
95
|
-
if method_definition&.defined_in
|
96
|
-
owner_name = method_definition.defined_in
|
97
|
-
[
|
98
|
-
SingletonMethodName.new(owner_name, method_name),
|
99
|
-
method_definition
|
100
|
-
]
|
101
|
-
end
|
102
|
-
else
|
103
|
-
nil
|
94
|
+
factory = context.type_env.subtyping.factory
|
95
|
+
method_name, definition = case receiver_type
|
96
|
+
when AST::Types::Name::Instance
|
97
|
+
method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name)
|
98
|
+
if method_definition&.defined_in
|
99
|
+
owner_name = method_definition.defined_in
|
100
|
+
[
|
101
|
+
InstanceMethodName.new(owner_name, method_name),
|
102
|
+
method_definition
|
103
|
+
]
|
104
104
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
105
|
+
when AST::Types::Name::Singleton
|
106
|
+
method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name)
|
107
|
+
if method_definition&.defined_in
|
108
|
+
owner_name = method_definition.defined_in
|
109
|
+
[
|
110
|
+
SingletonMethodName.new(owner_name, method_name),
|
111
|
+
method_definition
|
112
|
+
]
|
113
|
+
end
|
114
|
+
else
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
MethodCallContent.new(
|
119
|
+
node: node,
|
120
|
+
method_name: method_name,
|
121
|
+
type: typing.type_of(node: result_node),
|
122
|
+
definition: definition,
|
123
|
+
location: result_node.location.expression
|
124
|
+
)
|
125
|
+
when :def, :defs
|
126
|
+
context = typing.context_at(line: line, column: column)
|
127
|
+
method_context = context.method_context
|
128
|
+
|
129
|
+
if method_context && method_context.method
|
130
|
+
DefinitionContent.new(
|
130
131
|
node: node,
|
131
|
-
|
132
|
-
|
132
|
+
method_name: method_context.name,
|
133
|
+
method_type: method_context.method_type,
|
134
|
+
definition: method_context.method,
|
135
|
+
location: node.loc.expression
|
133
136
|
)
|
134
137
|
end
|
138
|
+
else
|
139
|
+
type = typing.type_of(node: node)
|
140
|
+
|
141
|
+
TypeContent.new(
|
142
|
+
node: node,
|
143
|
+
type: type,
|
144
|
+
location: node.location.expression
|
145
|
+
)
|
135
146
|
end
|
136
147
|
end
|
137
148
|
end
|
@@ -52,21 +52,23 @@ module Steep
|
|
52
52
|
|
53
53
|
def response_to_hover(path:, line:, column:)
|
54
54
|
Steep.logger.tagged "#response_to_hover" do
|
55
|
-
Steep.
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
Steep.measure "Generating response" do
|
56
|
+
Steep.logger.info { "path=#{path}, line=#{line}, column=#{column}" }
|
57
|
+
|
58
|
+
hover = Project::HoverContent.new(project: project)
|
59
|
+
content = hover.content_for(path: path, line: line+1, column: column+1)
|
60
|
+
if content
|
61
|
+
range = content.location.yield_self do |location|
|
62
|
+
start_position = { line: location.line - 1, character: location.column }
|
63
|
+
end_position = { line: location.last_line - 1, character: location.last_column }
|
64
|
+
{ start: start_position, end: end_position }
|
65
|
+
end
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
LSP::Interface::Hover.new(
|
68
|
+
contents: { kind: "markdown", value: format_hover(content) },
|
69
|
+
range: range
|
70
|
+
)
|
71
|
+
end
|
70
72
|
end
|
71
73
|
rescue Typing::UnknownNodeError => exn
|
72
74
|
Steep.log_error exn, message: "Failed to compute hover: #{exn.inspect}"
|
@@ -126,33 +128,35 @@ HOVER
|
|
126
128
|
|
127
129
|
def response_to_completion(path:, line:, column:, trigger:)
|
128
130
|
Steep.logger.tagged("#response_to_completion") do
|
129
|
-
Steep.
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
131
|
+
Steep.measure "Generating response" do
|
132
|
+
Steep.logger.info "path: #{path}, line: #{line}, column: #{column}, trigger: #{trigger}"
|
133
|
+
|
134
|
+
target = project.target_for_source_path(path) or return
|
135
|
+
target.type_check(target_sources: [], validate_signatures: false)
|
136
|
+
|
137
|
+
case (status = target&.status)
|
138
|
+
when Project::Target::TypeCheckStatus
|
139
|
+
subtyping = status.subtyping
|
140
|
+
source = target.source_files[path]
|
141
|
+
|
142
|
+
provider = Project::CompletionProvider.new(source_text: source.content, path: path, subtyping: subtyping)
|
143
|
+
items = begin
|
144
|
+
provider.run(line: line, column: column)
|
145
|
+
rescue Parser::SyntaxError
|
146
|
+
[]
|
147
|
+
end
|
148
|
+
|
149
|
+
completion_items = items.map do |item|
|
150
|
+
format_completion_item(item)
|
151
|
+
end
|
138
152
|
|
139
|
-
|
140
|
-
items = begin
|
141
|
-
provider.run(line: line, column: column)
|
142
|
-
rescue Parser::SyntaxError
|
143
|
-
[]
|
144
|
-
end
|
153
|
+
Steep.logger.debug "items = #{completion_items.inspect}"
|
145
154
|
|
146
|
-
|
147
|
-
|
155
|
+
LSP::Interface::CompletionList.new(
|
156
|
+
is_incomplete: false,
|
157
|
+
items: completion_items
|
158
|
+
)
|
148
159
|
end
|
149
|
-
|
150
|
-
Steep.logger.debug "items = #{completion_items.inspect}"
|
151
|
-
|
152
|
-
LSP::Interface::CompletionList.new(
|
153
|
-
is_incomplete: false,
|
154
|
-
items: completion_items
|
155
|
-
)
|
156
160
|
end
|
157
161
|
end
|
158
162
|
end
|
data/lib/steep/source.rb
CHANGED
@@ -276,6 +276,18 @@ module Steep
|
|
276
276
|
end
|
277
277
|
end
|
278
278
|
|
279
|
+
def self.map_child_nodes(node)
|
280
|
+
children = node.children.map do |child|
|
281
|
+
if child.is_a?(::AST::Node)
|
282
|
+
yield child
|
283
|
+
else
|
284
|
+
child
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
node.updated(nil, children)
|
289
|
+
end
|
290
|
+
|
279
291
|
def annotations(block:, factory:, current_module:)
|
280
292
|
AST::Annotation::Collection.new(
|
281
293
|
annotations: mapping[block.__id__] || [],
|
@@ -316,5 +328,50 @@ module Steep
|
|
316
328
|
end
|
317
329
|
end
|
318
330
|
end
|
331
|
+
|
332
|
+
def self.delete_defs(node, allow_list)
|
333
|
+
case node.type
|
334
|
+
when :def
|
335
|
+
if allow_list.include?(node)
|
336
|
+
node
|
337
|
+
else
|
338
|
+
node.updated(:nil, [])
|
339
|
+
end
|
340
|
+
when :defs
|
341
|
+
if allow_list.include?(node)
|
342
|
+
node
|
343
|
+
else
|
344
|
+
delete_defs(node.children[0], allow_list)
|
345
|
+
end
|
346
|
+
else
|
347
|
+
map_child_nodes(node) do |child|
|
348
|
+
delete_defs(child, allow_list)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def without_unrelated_defs(line:, column:)
|
354
|
+
nodes = find_nodes(line: line, column: column) || []
|
355
|
+
defs = Set[].compare_by_identity.merge(nodes.select {|node| node.type == :def || node.type == :defs })
|
356
|
+
|
357
|
+
node_ = Source.delete_defs(node, defs)
|
358
|
+
|
359
|
+
Source.new(path: path, node: node_, mapping: mapping)
|
360
|
+
end
|
361
|
+
|
362
|
+
def compact_siblings(node)
|
363
|
+
case node
|
364
|
+
when :def
|
365
|
+
node.updated(:nil, [])
|
366
|
+
when :defs
|
367
|
+
node.children[0]
|
368
|
+
when :class
|
369
|
+
node.updated(:class, [node.children[0], node.children[1], nil])
|
370
|
+
when :module
|
371
|
+
node.updated(:module, [node.children[0], nil])
|
372
|
+
else
|
373
|
+
node
|
374
|
+
end
|
375
|
+
end
|
319
376
|
end
|
320
377
|
end
|
data/lib/steep/version.rb
CHANGED
data/steep.gemspec
CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_runtime_dependency "ast_utils", "~> 0.3.0"
|
33
33
|
spec.add_runtime_dependency "activesupport", ">= 5.1"
|
34
34
|
spec.add_runtime_dependency "rainbow", ">= 2.2.2", "< 4.0"
|
35
|
-
spec.add_runtime_dependency "listen", "~> 3.
|
35
|
+
spec.add_runtime_dependency "listen", "~> 3.0"
|
36
36
|
spec.add_runtime_dependency "language_server-protocol", "~> 3.15.0.1"
|
37
37
|
spec.add_runtime_dependency "rbs", "~> 0.12.0"
|
38
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: steep
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.31.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Soutaro Matsumoto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -78,14 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '3.
|
81
|
+
version: '3.0'
|
82
82
|
type: :runtime
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: '3.
|
88
|
+
version: '3.0'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: language_server-protocol
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|