orbacle 0.1.0 → 0.2.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 +4 -0
- data/Gemfile.lock +4 -4
- data/Makefile +5 -22
- data/README.md +28 -4
- data/lib/orbacle.rb +13 -2
- data/lib/orbacle/builder.rb +29 -31
- data/lib/orbacle/builder/operator_assignment_processors.rb +1 -1
- data/lib/orbacle/const_ref.rb +0 -8
- data/lib/orbacle/define_builtins.rb +119 -40
- data/lib/orbacle/engine.rb +38 -4
- data/lib/orbacle/find_call_under_position.rb +75 -0
- data/lib/orbacle/find_definition_under_position.rb +37 -3
- data/lib/orbacle/global_tree.rb +58 -6
- data/lib/orbacle/graph.rb +25 -8
- data/lib/orbacle/lang_server.rb +24 -1
- data/lib/orbacle/nesting.rb +0 -4
- data/lib/orbacle/ruby_parser.rb +12 -2
- data/lib/orbacle/typing_service.rb +40 -205
- data/lib/orbacle/worklist.rb +16 -6
- data/orbacle.gemspec +3 -3
- metadata +9 -8
- data/.ruby-version +0 -1
data/lib/orbacle/engine.rb
CHANGED
@@ -28,18 +28,44 @@ module Orbacle
|
|
28
28
|
case result
|
29
29
|
when FindDefinitionUnderPosition::ConstantResult
|
30
30
|
constants = @state.solve_reference2(result.const_ref)
|
31
|
-
constants
|
31
|
+
definitions_locations(constants)
|
32
32
|
when FindDefinitionUnderPosition::MessageResult
|
33
33
|
caller_type = get_type_of_caller_from_message_send(file_path, result.position_range)
|
34
34
|
methods_definitions = get_methods_definitions_for_type(caller_type, result.name)
|
35
35
|
methods_definitions = @state.get_methods(result.name) if methods_definitions.empty?
|
36
|
-
methods_definitions
|
36
|
+
definitions_locations(methods_definitions)
|
37
|
+
when FindDefinitionUnderPosition::SuperResult
|
38
|
+
method_surrounding_super = @state.find_method_including_position(file_path, result.keyword_position_range.start)
|
39
|
+
return [] if method_surrounding_super.nil?
|
40
|
+
super_method = @state.find_super_method(method_surrounding_super.id)
|
41
|
+
return definitions_locations(@state.get_methods(method_surrounding_super.name) - [method_surrounding_super]) if super_method.nil?
|
42
|
+
definitions_locations([super_method])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def completions_for_call_under_position(file_content, position)
|
47
|
+
result = FindCallUnderPosition.new(RubyParser.new).process_file(file_content, position)
|
48
|
+
case result
|
49
|
+
when FindCallUnderPosition::SelfResult
|
50
|
+
filtered_methods_from_class_name(result.nesting.to_scope.to_const_name.to_string, result.message_name)
|
51
|
+
when FindCallUnderPosition::IvarResult
|
52
|
+
ivar_node = @graph.get_ivar_definition_node(result.nesting.to_scope, result.ivar_name)
|
53
|
+
ivar_type = @state.type_of(ivar_node)
|
54
|
+
methods = []
|
55
|
+
ivar_type.each_possible_type do |type|
|
56
|
+
methods.concat(filtered_methods_from_class_name(type.name, result.message_name))
|
57
|
+
end
|
58
|
+
methods
|
37
59
|
else
|
38
60
|
[]
|
39
61
|
end
|
40
62
|
end
|
41
63
|
|
42
64
|
private
|
65
|
+
def definitions_locations(collection)
|
66
|
+
collection.map(&:location).compact
|
67
|
+
end
|
68
|
+
|
43
69
|
def get_type_of_caller_from_message_send(file_path, position_range)
|
44
70
|
message_send = @worklist
|
45
71
|
.message_sends
|
@@ -50,9 +76,9 @@ module Orbacle
|
|
50
76
|
def get_methods_definitions_for_type(type, method_name)
|
51
77
|
case type
|
52
78
|
when NominalType
|
53
|
-
@state.
|
79
|
+
@state.get_deep_instance_methods_from_class_name(type.name, method_name)
|
54
80
|
when ClassType
|
55
|
-
@state.
|
81
|
+
@state.get_deep_class_methods_from_class_name(type.name, method_name)
|
56
82
|
when UnionType
|
57
83
|
type.types_set.flat_map {|t| get_methods_definitions_for_type(t, method_name) }
|
58
84
|
else
|
@@ -70,5 +96,13 @@ module Orbacle
|
|
70
96
|
def pretty_print_type(type)
|
71
97
|
TypePrettyPrinter.new.(type)
|
72
98
|
end
|
99
|
+
|
100
|
+
def filtered_methods_from_class_name(class_name, message_name)
|
101
|
+
all_methods = @state.get_all_instance_methods_from_class_name(class_name)
|
102
|
+
starting_with = all_methods.select do |metod|
|
103
|
+
metod.name.to_s.start_with?(message_name.to_s)
|
104
|
+
end
|
105
|
+
starting_with.map(&:name).map(&:to_s)
|
106
|
+
end
|
73
107
|
end
|
74
108
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser'
|
4
|
+
|
5
|
+
module Orbacle
|
6
|
+
class FindCallUnderPosition < Parser::AST::Processor
|
7
|
+
include AstUtils
|
8
|
+
|
9
|
+
SelfResult = Struct.new(:message_name, :nesting)
|
10
|
+
IvarResult = Struct.new(:message_name, :ivar_name, :nesting)
|
11
|
+
|
12
|
+
def initialize(parser)
|
13
|
+
@parser = parser
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_file(file_content, searched_position)
|
17
|
+
ast = parser.parse(file_content)
|
18
|
+
|
19
|
+
@current_nesting = Nesting.empty
|
20
|
+
@searched_position = searched_position
|
21
|
+
|
22
|
+
process(ast)
|
23
|
+
|
24
|
+
@result
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :parser
|
28
|
+
|
29
|
+
def on_send(ast)
|
30
|
+
if ast.loc.selector && build_position_range_from_parser_range(ast.loc.selector).include_position?(@searched_position)
|
31
|
+
message_name = ast.children.fetch(1)
|
32
|
+
selector_position_range = build_position_range_from_parser_range(ast.loc.selector)
|
33
|
+
@result = if ast.children[0] == nil
|
34
|
+
SelfResult.new(message_name, @current_nesting)
|
35
|
+
else
|
36
|
+
case ast.children[0].type
|
37
|
+
when :self
|
38
|
+
SelfResult.new(message_name, @current_nesting)
|
39
|
+
when :ivar
|
40
|
+
IvarResult.new(message_name, ast.children[0].children[0], @current_nesting)
|
41
|
+
else
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_class(ast)
|
51
|
+
klass_name_ast, _ = ast.children
|
52
|
+
klass_name_ref = ConstRef.from_ast(klass_name_ast, @current_nesting)
|
53
|
+
with_new_nesting(@current_nesting.increase_nesting_const(klass_name_ref)) do
|
54
|
+
super
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_module(ast)
|
60
|
+
module_name_ast, _ = ast.children
|
61
|
+
module_name_ref = ConstRef.from_ast(module_name_ast, @current_nesting)
|
62
|
+
with_new_nesting(@current_nesting.increase_nesting_const(module_name_ref)) do
|
63
|
+
super
|
64
|
+
end
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def with_new_nesting(new_nesting)
|
69
|
+
previous_nesting = @current_nesting
|
70
|
+
@current_nesting = new_nesting
|
71
|
+
yield
|
72
|
+
@current_nesting = previous_nesting
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'parser'
|
4
|
+
|
3
5
|
module Orbacle
|
4
6
|
class FindDefinitionUnderPosition < Parser::AST::Processor
|
5
7
|
include AstUtils
|
6
8
|
|
7
9
|
ConstantResult = Struct.new(:const_ref)
|
8
10
|
MessageResult = Struct.new(:name, :position_range)
|
11
|
+
SuperResult = Struct.new(:keyword_position_range)
|
9
12
|
|
10
13
|
def initialize(parser)
|
11
14
|
@parser = parser
|
@@ -57,15 +60,46 @@ module Orbacle
|
|
57
60
|
|
58
61
|
def on_send(ast)
|
59
62
|
if ast.loc.selector && build_position_range_from_parser_range(ast.loc.selector).include_position?(@searched_position)
|
60
|
-
message_name = ast.children.fetch(1)
|
61
|
-
|
62
|
-
|
63
|
+
message_name = ast.children.fetch(1)
|
64
|
+
if message_name.equal?(:[])
|
65
|
+
selector_position_range = build_position_range_from_parser_range(ast.loc.selector)
|
66
|
+
if selector_position_range.on_edges?(@searched_position)
|
67
|
+
@result = MessageResult.new(message_name, selector_position_range)
|
68
|
+
else
|
69
|
+
super
|
70
|
+
end
|
71
|
+
else
|
72
|
+
selector_position_range = build_position_range_from_parser_range(ast.loc.selector)
|
73
|
+
@result = MessageResult.new(message_name, selector_position_range)
|
74
|
+
end
|
75
|
+
elsif ast.loc.dot && build_position_range_from_parser_range(ast.loc.dot).include_position?(@searched_position)
|
76
|
+
message_name = ast.children.fetch(1)
|
77
|
+
dot_position_range = build_position_range_from_parser_range(ast.loc.dot)
|
78
|
+
@result = MessageResult.new(message_name, dot_position_range)
|
63
79
|
else
|
64
80
|
super
|
65
81
|
end
|
66
82
|
nil
|
67
83
|
end
|
68
84
|
|
85
|
+
def on_super(ast)
|
86
|
+
keyword_position_range = build_position_range_from_parser_range(ast.loc.keyword)
|
87
|
+
if keyword_position_range.include_position?(@searched_position)
|
88
|
+
@result = SuperResult.new(keyword_position_range)
|
89
|
+
else
|
90
|
+
super
|
91
|
+
end
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def on_zsuper(ast)
|
96
|
+
keyword_position_range = build_position_range_from_parser_range(ast.loc.keyword)
|
97
|
+
if keyword_position_range.include_position?(@searched_position)
|
98
|
+
@result = SuperResult.new(keyword_position_range)
|
99
|
+
end
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
69
103
|
def with_new_nesting(new_nesting)
|
70
104
|
previous_nesting = @current_nesting
|
71
105
|
@current_nesting = new_nesting
|
data/lib/orbacle/global_tree.rb
CHANGED
@@ -105,7 +105,7 @@ module Orbacle
|
|
105
105
|
metod = Method.new(id, place_of_definition_id, name, location, visibility, args)
|
106
106
|
@methods_by_class_id[metod.place_of_definition_id][metod.name] << metod
|
107
107
|
@methods_by_id[metod.id] = metod
|
108
|
-
|
108
|
+
metod
|
109
109
|
end
|
110
110
|
|
111
111
|
def find_instance_method_from_class_name(class_name, method_name)
|
@@ -133,6 +133,12 @@ module Orbacle
|
|
133
133
|
get_instance_methods_from_class_id(eigenclass.id, method_name)
|
134
134
|
end
|
135
135
|
|
136
|
+
def get_all_instance_methods_from_class_name(class_name)
|
137
|
+
klass = find_class_by_name(class_name)
|
138
|
+
return [] if klass.nil?
|
139
|
+
@methods_by_class_id[klass.id].values.flatten
|
140
|
+
end
|
141
|
+
|
136
142
|
def find_class_method_from_class_name(class_name, method_name)
|
137
143
|
get_class_methods_from_class_name(class_name, method_name).first
|
138
144
|
end
|
@@ -158,15 +164,25 @@ module Orbacle
|
|
158
164
|
end
|
159
165
|
end
|
160
166
|
|
167
|
+
def find_method_including_position(file_path, position)
|
168
|
+
@methods_by_id
|
169
|
+
.values
|
170
|
+
.select {|m| m.location &&
|
171
|
+
m.location.uri.eql?(file_path) &&
|
172
|
+
m.location.position_range.include_position?(position) }
|
173
|
+
.sort_by {|m| m.location.span }
|
174
|
+
.first
|
175
|
+
end
|
176
|
+
|
161
177
|
### Definitions
|
162
178
|
|
163
|
-
def
|
179
|
+
def add_class(parent_ref)
|
164
180
|
klass = Klass.new(id_generator.call, parent_ref)
|
165
181
|
@classes_by_id[klass.id] = klass
|
166
182
|
klass
|
167
183
|
end
|
168
184
|
|
169
|
-
def
|
185
|
+
def add_module
|
170
186
|
mod = Mod.new(id_generator.call)
|
171
187
|
@modules_by_id[mod.id] = mod
|
172
188
|
mod
|
@@ -189,7 +205,7 @@ module Orbacle
|
|
189
205
|
if definition.eigenclass_id
|
190
206
|
get_class(definition.eigenclass_id)
|
191
207
|
else
|
192
|
-
eigenclass =
|
208
|
+
eigenclass = add_class(nil)
|
193
209
|
definition.eigenclass_id = eigenclass.id
|
194
210
|
eigenclass
|
195
211
|
end
|
@@ -199,7 +215,7 @@ module Orbacle
|
|
199
215
|
|
200
216
|
def add_constant(constant)
|
201
217
|
@constants.add_element(constant.scope, constant.name, constant)
|
202
|
-
|
218
|
+
constant
|
203
219
|
end
|
204
220
|
|
205
221
|
def solve_reference(const_ref)
|
@@ -260,7 +276,43 @@ module Orbacle
|
|
260
276
|
|
261
277
|
def find_constant_for_definition(definition_id)
|
262
278
|
@constants.find do |constant|
|
263
|
-
constant.definition_id
|
279
|
+
constant.definition_id.equal?(definition_id)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def find_deep_instance_method_from_class_name(class_name, method_name)
|
284
|
+
get_deep_instance_methods_from_class_name(class_name, method_name).first
|
285
|
+
end
|
286
|
+
|
287
|
+
def get_deep_instance_methods_from_class_name(class_name, method_name)
|
288
|
+
found_methods = get_instance_methods_from_class_name(class_name, method_name)
|
289
|
+
if found_methods.empty?
|
290
|
+
parent_name = get_parent_of(class_name)
|
291
|
+
if parent_name
|
292
|
+
get_deep_instance_methods_from_class_name(parent_name, method_name)
|
293
|
+
else
|
294
|
+
[]
|
295
|
+
end
|
296
|
+
else
|
297
|
+
found_methods
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def find_deep_class_method_from_class_name(class_name, method_name)
|
302
|
+
get_deep_class_methods_from_class_name(class_name, method_name).first
|
303
|
+
end
|
304
|
+
|
305
|
+
def get_deep_class_methods_from_class_name(class_name, method_name)
|
306
|
+
found_methods = get_class_methods_from_class_name(class_name, method_name)
|
307
|
+
if found_methods.empty?
|
308
|
+
parent_name = get_parent_of(class_name)
|
309
|
+
if parent_name
|
310
|
+
get_deep_class_methods_from_class_name(parent_name, method_name)
|
311
|
+
else
|
312
|
+
[]
|
313
|
+
end
|
314
|
+
else
|
315
|
+
found_methods
|
264
316
|
end
|
265
317
|
end
|
266
318
|
|
data/lib/orbacle/graph.rb
CHANGED
@@ -6,7 +6,8 @@ module Orbacle
|
|
6
6
|
class Graph
|
7
7
|
ZSuper = Struct.new(:send_result, :block)
|
8
8
|
Yield = Struct.new(:send_args, :send_result)
|
9
|
-
Metod = Struct.new(:args, :result, :yields, :zsupers)
|
9
|
+
Metod = Struct.new(:args, :result, :yields, :zsupers, :caller_node)
|
10
|
+
MetodGraph = Struct.new(:args, :result, :yields, :caller_node, :all_nodes, :all_edges)
|
10
11
|
Lambda = Struct.new(:args, :result)
|
11
12
|
|
12
13
|
def initialize
|
@@ -99,17 +100,33 @@ module Orbacle
|
|
99
100
|
return cvariables[scope.absolute_str][ivar_name]
|
100
101
|
end
|
101
102
|
|
102
|
-
def get_metod_nodes(
|
103
|
-
|
103
|
+
def get_metod_nodes(method_id)
|
104
|
+
metod = metods[method_id]
|
105
|
+
if metod.instance_of?(Metod)
|
106
|
+
metod
|
107
|
+
else
|
108
|
+
new_nodes = metod.all_nodes.map(&:clone)
|
109
|
+
mapping = metod.all_nodes.zip(new_nodes).to_h
|
110
|
+
new_nodes.each(&method(:add_vertex))
|
111
|
+
metod.all_edges.each do |v1, v2|
|
112
|
+
add_edge(mapping.fetch(v1), mapping.fetch(v2))
|
113
|
+
end
|
114
|
+
new_arguments_nodes = metod.args.each_with_object({}) do |(k, v), h|
|
115
|
+
h[k] = mapping.fetch(v)
|
116
|
+
end
|
117
|
+
new_yields = metod.yields.map {|y| Yield.new(y.send_args.map {|a| mapping.fetch(a) }, mapping.fetch(y.send_result)) }
|
118
|
+
Metod.new(new_arguments_nodes, mapping.fetch(metod.result), new_yields, [], mapping.fetch(metod.caller_node))
|
119
|
+
end
|
104
120
|
end
|
105
121
|
|
106
122
|
def store_metod_nodes(metod_id, arguments_nodes)
|
107
123
|
raise if !arguments_nodes.is_a?(Hash)
|
108
|
-
metods[metod_id] ||= Metod.new(
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
124
|
+
metods[metod_id] ||= Metod.new(arguments_nodes, add_vertex(Node.new(:method_result, {})), [], [], nil)
|
125
|
+
end
|
126
|
+
|
127
|
+
def store_metod_subgraph(metod_id, arguments_nodes, caller_node, result_node, yields, all_nodes, all_edges)
|
128
|
+
raise if !arguments_nodes.is_a?(Hash)
|
129
|
+
metods[metod_id] ||= MetodGraph.new(arguments_nodes, result_node, yields, caller_node, all_nodes, all_edges)
|
113
130
|
end
|
114
131
|
|
115
132
|
def get_lambda_nodes(lambda_id)
|
data/lib/orbacle/lang_server.rb
CHANGED
@@ -7,9 +7,14 @@ module Orbacle
|
|
7
7
|
class LangServer
|
8
8
|
include Lsp::LanguageServer
|
9
9
|
|
10
|
+
module Errors
|
11
|
+
NoDefinitionFound = Lsp::ResponseError::Base.new(1101, "No definition found under cursor position", nil)
|
12
|
+
end
|
13
|
+
|
10
14
|
def initialize(logger, engine)
|
11
15
|
@logger = logger
|
12
16
|
@engine = engine
|
17
|
+
@file_contents = {}
|
13
18
|
end
|
14
19
|
|
15
20
|
attr_reader :logger, :engine
|
@@ -39,11 +44,25 @@ module Orbacle
|
|
39
44
|
if locations
|
40
45
|
Lsp::ResponseMessage.successful(locations.map(&method(:location_to_lsp_location)))
|
41
46
|
else
|
42
|
-
Lsp::ResponseMessage.
|
47
|
+
Lsp::ResponseMessage.new(nil, Errors::NoDefinitionFound)
|
43
48
|
end
|
44
49
|
end
|
45
50
|
end
|
46
51
|
|
52
|
+
def handle_text_document_completion(request)
|
53
|
+
log_errors do
|
54
|
+
file_content = get_file_content(request.text_document.uri)
|
55
|
+
completions = engine.completions_for_call_under_position(file_content, Position.new(request.position.line, request.position.character - 1))
|
56
|
+
Lsp::ResponseMessage.successful(completions.map {|c| Lsp::CompletionItem.new(c) })
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_text_document_did_change(request)
|
61
|
+
log_errors do
|
62
|
+
@file_contents[request.text_document.uri.path] = request.content_changes[0].text
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
47
66
|
def log_errors
|
48
67
|
begin
|
49
68
|
yield
|
@@ -53,6 +72,10 @@ module Orbacle
|
|
53
72
|
end
|
54
73
|
end
|
55
74
|
|
75
|
+
def get_file_content(uri)
|
76
|
+
@file_contents[uri.path] || File.read(uri.path)
|
77
|
+
end
|
78
|
+
|
56
79
|
def location_to_lsp_location(location)
|
57
80
|
Lsp::Location.new(
|
58
81
|
URI("file://#{location.uri}"),
|
data/lib/orbacle/nesting.rb
CHANGED
data/lib/orbacle/ruby_parser.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'parser/
|
3
|
+
require 'parser/ruby25'
|
4
4
|
|
5
5
|
module Orbacle
|
6
6
|
class RubyParser
|
@@ -8,8 +8,18 @@ module Orbacle
|
|
8
8
|
SyntaxError = Class.new(Error)
|
9
9
|
EncodingError = Class.new(Error)
|
10
10
|
|
11
|
+
def initialize
|
12
|
+
@my_parser = Class.new(Parser::Ruby25) do
|
13
|
+
def self.default_parser
|
14
|
+
my_parser = super()
|
15
|
+
my_parser.diagnostics.consumer = nil
|
16
|
+
my_parser
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
def parse(content)
|
12
|
-
|
22
|
+
@my_parser.parse(content)
|
13
23
|
rescue Parser::SyntaxError
|
14
24
|
raise SyntaxError
|
15
25
|
rescue ::EncodingError
|