orbacle 0.1.0 → 0.2.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.
@@ -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.map(&:location)
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.map(&:location).compact
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.get_instance_methods_from_class_name(type.name, method_name)
79
+ @state.get_deep_instance_methods_from_class_name(type.name, method_name)
54
80
  when ClassType
55
- @state.get_class_methods_from_class_name(type.name, method_name)
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).to_s
61
- selector_position_range = build_position_range_from_parser_range(ast.loc.selector)
62
- @result = MessageResult.new(message_name, selector_position_range)
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
@@ -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
- return metod
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 add_klass(parent_ref)
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 add_mod
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 = add_klass(nil)
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
- return constant
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 == 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(metod_id)
103
- return metods[metod_id]
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
- arguments_nodes,
110
- add_vertex(Node.new(:method_result, {})),
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)
@@ -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.successful(nil)
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}"),
@@ -45,10 +45,6 @@ module Orbacle
45
45
 
46
46
  attr_reader :levels
47
47
 
48
- def to_primitive
49
- levels.map {|level| level.full_name }
50
- end
51
-
52
48
  def increase_nesting_const(const_ref)
53
49
  Nesting.new(levels + [ConstLevel.new(const_ref)])
54
50
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parser/current'
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
- Parser::CurrentRuby.parse(content)
22
+ @my_parser.parse(content)
13
23
  rescue Parser::SyntaxError
14
24
  raise SyntaxError
15
25
  rescue ::EncodingError