steep 0.30.0 → 0.31.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 +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
|