ruby-lsp-ree 0.1.15 → 0.1.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f3673076ca0aee98f0f92d6db25a2939ae21a58464a70e1a1e534bfef3458d0
4
- data.tar.gz: 2191d354bbe75911cc230ca20665e5882e15eabee5de30801db1960990d42c3b
3
+ metadata.gz: 4d1b2bf9c373c729b6412704fd6daf364ed4c8b278405f52b4c1fc32b256570c
4
+ data.tar.gz: 0bde5c4413ec6d4ccfd807dc6e6eb33d1e47ab018dfbfaad47200775b2c3badb
5
5
  SHA512:
6
- metadata.gz: ec2b1c6e15509e7e8f18758ef3a6912c14dccbdc6348d353331894fc53efbd072b043e49b1ee933c2b552e11404a4f322f8336aa29f267f3a842724d67fc8d7a
7
- data.tar.gz: c584181adbf4ed1a02dc2545c1b65cf4ada307ade2d46aee661a97319adf43d67e6d21bb47f7b05d73bc4a2b913053ca990cae0cf1722587c350be942151e385
6
+ metadata.gz: 6a97047d4924f208415ba6c5ed45e6abb57141e74fd44ad79368a9a7dd025a2ce0c36cfe2ca95db6fd0dab9586d9a1c5484be2e930ab129ab6acb7975fce310f
7
+ data.tar.gz: 17daba371512607775021704c56895bcc9e41666e598c9603c7da22ebac567de1cc7a2957f050a6657429057557dcbbc86654141eba98faa8c7ddde6e892a030
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.1.16] - 2025-04-24
2
+
3
+ - formatter: add missing imports
4
+
1
5
  ## [0.1.15] - 2025-04-18
2
6
 
3
7
  - autocomplete - don't overwrite existing arguments
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-lsp-ree (0.1.15)
4
+ ruby-lsp-ree (0.1.16)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -32,7 +32,7 @@ To switch off/on formatter features, use Ruby LSP addon settings:
32
32
  }
33
33
  }
34
34
  ```
35
- available formatters: `SortLinksFormatter`, `MissingErrorDefinitionsFormatter`, `MissingErrorContractsFormatter`, `MissingErrorLocalesFormatter`, `UnusedLinksFormatter`
35
+ available formatters: `SortLinksFormatter`, `MissingErrorDefinitionsFormatter`, `MissingErrorContractsFormatter`, `MissingErrorLocalesFormatter`, `UnusedLinksFormatter`, `MissingImportsFormatter`
36
36
 
37
37
  ## Functions
38
38
 
@@ -33,7 +33,7 @@ module RubyLsp
33
33
  new_text = "\n" + link_text
34
34
 
35
35
  if @parsed_doc.has_blank_links_container?
36
- new_text = "\sdo#{link_text}\n\s\send\n"
36
+ new_text = "\sdo#{new_text}\n\s\send\n"
37
37
  end
38
38
 
39
39
  range = get_range_for_fn_insert(@parsed_doc, link_text)
@@ -4,7 +4,7 @@ module RubyLsp
4
4
  def self.call(source, uri, message_queue, index)
5
5
  new(message_queue, index).call(source, uri)
6
6
  rescue => e
7
- $stderr.puts("error in #{self.class}: #{e.message} : #{e.backtrace.first}")
7
+ $stderr.puts("error in #{self}: #{e.message} : #{e.backtrace.first}")
8
8
  source
9
9
  end
10
10
 
@@ -0,0 +1,63 @@
1
+ require_relative 'base_formatter'
2
+ require_relative "../ree_object_finder"
3
+
4
+ module RubyLsp
5
+ module Ree
6
+ class MissingImportsFormatter < BaseFormatter
7
+ include RubyLsp::Ree::ReeLspUtils
8
+
9
+ def call(source, uri)
10
+ return source unless @index
11
+
12
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
13
+ return source if !parsed_doc || !parsed_doc.has_root_class?
14
+
15
+ finder = ReeObjectFinder.new(@index)
16
+ editor = RubyLsp::Ree::ReeSourceEditor.new(source)
17
+
18
+ current_package = package_name_from_uri(uri)
19
+
20
+ fn_calls = parsed_doc.parse_fn_calls
21
+ filtered_fn_calls = filter_fn_calls(parsed_doc, fn_calls)
22
+ objects_to_add = filtered_fn_calls.map{ |fn_call|
23
+ finder.find_object(fn_call.name.to_s)
24
+ }.compact
25
+
26
+ bean_calls = parsed_doc.parse_bean_calls
27
+ filtered_bean_calls = filter_bean_calls(parsed_doc, bean_calls)
28
+ objects_to_add += filtered_bean_calls.map{ |bean_call|
29
+ finder.find_object(bean_call.receiver_name.to_s)
30
+ }.compact
31
+
32
+ objects_to_add.uniq!{ |obj| obj.name }
33
+ objects_to_add.reject!{ |obj| parsed_doc.includes_linked_object?(obj.name) }
34
+ return editor.source if objects_to_add.size == 0
35
+
36
+ editor.add_links(parsed_doc, objects_to_add, current_package)
37
+ editor.source
38
+ end
39
+
40
+ private
41
+
42
+ def filter_fn_calls(parsed_doc, fn_calls)
43
+ parsed_doc.parse_instance_methods
44
+
45
+ fn_calls.reject{ |fn_call|
46
+ parsed_doc.doc_instance_methods.map(&:name).include?(fn_call.name)
47
+ }
48
+ end
49
+
50
+ def filter_bean_calls(parsed_doc, bean_calls)
51
+ bean_calls.select do |bean_call|
52
+ if !bean_call.method_name
53
+ true
54
+ else
55
+ method_obj = parsed_doc.doc_instance_methods.detect{ _1.name == bean_call.method_name }
56
+ local_variables = method_obj.parse_local_variables
57
+ !local_variables.map(&:name).include?(bean_call.receiver_name)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,104 @@
1
+ class RubyLsp::Ree::CallObjectsParser
2
+ attr_reader :parsed_doc
3
+
4
+ class CallObject
5
+ attr_reader :name, :type, :receiver_name, :method_name
6
+
7
+ def initialize(name:, type:, receiver_name: nil)
8
+ @name = name
9
+ @type = type
10
+ @receiver_name = receiver_name
11
+ @method_name = nil
12
+ end
13
+
14
+ def set_method_name(method_name)
15
+ @method_name = method_name
16
+ end
17
+ end
18
+
19
+ def initialize(parsed_doc)
20
+ @parsed_doc = parsed_doc
21
+ end
22
+
23
+ def class_call_objects
24
+ call_objects = []
25
+ return unless parsed_doc.has_body?
26
+
27
+ call_objects += parse_body_call_objects(parsed_doc.class_node.body.body)
28
+
29
+ parsed_doc.parse_instance_methods
30
+
31
+ parsed_doc.doc_instance_methods.each do |doc_instance_method|
32
+ call_objects += method_call_objects(doc_instance_method)
33
+ end
34
+
35
+ call_objects
36
+ end
37
+
38
+ def method_call_objects(method_object)
39
+ method_body = method_object.method_body
40
+ return [] unless method_body
41
+
42
+ call_nodes = parse_body_call_objects(method_body)
43
+ call_expressions = [] # don't parse call expressions for now parse_body_call_expressions(method_body)
44
+
45
+ call_objects = call_nodes + call_expressions
46
+
47
+ call_objects.each{ |call_object| call_object.set_method_name(method_object.name) }
48
+ call_objects
49
+ end
50
+
51
+ private
52
+
53
+ def parse_body_call_objects(node_body)
54
+ call_objects = []
55
+
56
+ node_body.each do |node|
57
+ if node.is_a?(Prism::CallNode)
58
+ receiver = get_first_receiver(node)
59
+ receiver_name = receiver.respond_to?(:name) ? receiver.name : nil
60
+ call_objects << CallObject.new(name: node.name, type: :method_call, receiver_name: receiver_name)
61
+ elsif node.respond_to?(:statements)
62
+ call_objects += parse_body_call_objects(node.statements.body)
63
+ elsif node.respond_to?(:block) && node.block && node.block.is_a?(Prism::BlockNode)
64
+ call_objects += parse_body_call_objects(get_method_body(node.block))
65
+ elsif node.respond_to?(:value) && node.value
66
+ call_objects += parse_body_call_objects([node.value])
67
+ end
68
+ end
69
+
70
+ call_objects
71
+ end
72
+
73
+ def parse_body_call_expressions(node_body)
74
+ call_expressions = []
75
+
76
+ node_body.each do |node|
77
+ if node.respond_to?(:block) && node.block && node.block.is_a?(Prism::BlockArgumentNode) && node.block.expression.is_a?(Prism::SymbolNode)
78
+ call_expressions << CallObject.new(name: node.block.expression.unescaped.to_sym, type: :proc_to_sym)
79
+ end
80
+ end
81
+
82
+ call_expressions
83
+ end
84
+
85
+ def get_method_body(node)
86
+ return unless node.body
87
+
88
+ if node.body.is_a?(Prism::BeginNode)
89
+ node.body.statements.body
90
+ else
91
+ node.body.body
92
+ end
93
+ end
94
+
95
+ def get_first_receiver(node)
96
+ return nil unless node.receiver
97
+
98
+ if node.receiver.is_a?(Prism::CallNode) && node.receiver.receiver
99
+ return get_first_receiver(node.receiver)
100
+ end
101
+
102
+ node.receiver
103
+ end
104
+ end
@@ -0,0 +1,52 @@
1
+ class RubyLsp::Ree::LocalVariablesParser
2
+ attr_reader :parsed_doc, :method_object
3
+
4
+ class LocalVariable
5
+ attr_reader :name
6
+
7
+ def initialize(name:)
8
+ @name = name
9
+ end
10
+ end
11
+
12
+ def initialize(method_obj)
13
+ @method_object = method_obj
14
+ end
15
+
16
+ def method_local_variables
17
+ method_body = method_object.method_body
18
+ return [] unless method_body
19
+
20
+ parse_body_local_variables(method_body)
21
+ end
22
+
23
+ private
24
+
25
+ def parse_body_local_variables(node_body)
26
+ local_variables = []
27
+
28
+ node_body.each do |node|
29
+ if node.is_a?(Prism::LocalVariableWriteNode)
30
+ local_variables << LocalVariable.new(name: node.name)
31
+ elsif node.is_a?(Prism::MultiWriteNode)
32
+ local_variables += node.lefts.map{ |x| LocalVariable.new(name: x.name) }
33
+ elsif node.respond_to?(:statements)
34
+ local_variables += parse_body_local_variables(node.statements.body)
35
+ elsif node.respond_to?(:block) && node.block && node.block.is_a?(Prism::BlockNode)
36
+ local_variables += parse_body_local_variables(get_method_body(node.block))
37
+ end
38
+ end
39
+
40
+ local_variables
41
+ end
42
+
43
+ def get_method_body(node)
44
+ return unless node.body
45
+
46
+ if node.body.is_a?(Prism::BeginNode)
47
+ node.body.statements.body
48
+ else
49
+ node.body.body
50
+ end
51
+ end
52
+ end
@@ -2,6 +2,8 @@ require_relative 'parsed_base_document'
2
2
  require_relative 'parsed_link_node'
3
3
  require_relative 'parsed_method_node'
4
4
  require_relative "../ree_constants"
5
+ require_relative "body_parsers/call_objects_parser"
6
+
5
7
  require 'ostruct'
6
8
 
7
9
  class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
@@ -142,6 +144,7 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
142
144
  end
143
145
 
144
146
  def parse_instance_methods
147
+ return if @doc_instance_methods
145
148
  @doc_instance_methods = []
146
149
 
147
150
  current_contract_node = nil
@@ -202,6 +205,14 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
202
205
  .map(&:name)
203
206
  end
204
207
 
208
+ def parse_fn_calls
209
+ RubyLsp::Ree::CallObjectsParser.new(self).class_call_objects.select{ !_1.receiver_name }
210
+ end
211
+
212
+ def parse_bean_calls
213
+ RubyLsp::Ree::CallObjectsParser.new(self).class_call_objects.select{ _1.receiver_name }
214
+ end
215
+
205
216
  def class_name
206
217
  class_node.constant_path.name.to_s
207
218
  end
@@ -1,4 +1,5 @@
1
1
  require 'prism'
2
+ require_relative "body_parsers/local_variables_parser"
2
3
 
3
4
  class RubyLsp::Ree::ParsedMethodNode
4
5
  attr_reader :method_node, :contract_node, :nested_local_methods
@@ -24,6 +25,10 @@ class RubyLsp::Ree::ParsedMethodNode
24
25
  @method_node.location.end_line - 1
25
26
  end
26
27
 
28
+ def method_body
29
+ get_method_body(@method_node)
30
+ end
31
+
27
32
  def raised_errors_nested
28
33
  return @raised_errors_nested if @raised_errors_nested
29
34
  raised = raised_errors
@@ -94,6 +99,10 @@ class RubyLsp::Ree::ParsedMethodNode
94
99
  def contract_in_parentheses?
95
100
  @contract_node.opening == '(' && @contract_node.closing == ')'
96
101
  end
102
+
103
+ def parse_local_variables
104
+ RubyLsp::Ree::LocalVariablesParser.new(self).method_local_variables
105
+ end
97
106
 
98
107
  def parse_nested_local_methods(local_methods)
99
108
  unless @method_node.body
@@ -111,6 +120,20 @@ class RubyLsp::Ree::ParsedMethodNode
111
120
  @nested_local_methods.each{ _1.parse_nested_local_methods(local_methods) }
112
121
  end
113
122
 
123
+ def parse_call_objects
124
+ method_body = get_method_body(@method_node)
125
+ return [] unless method_body
126
+
127
+ call_nodes = parse_body_call_objects(method_body)
128
+ call_expressions = parse_body_call_expressions(method_body)
129
+
130
+ call_node_names = call_nodes.map(&:name) + call_expressions
131
+
132
+ call_node_names
133
+ end
134
+
135
+ private
136
+
114
137
  def parse_body_call_objects(node_body)
115
138
  call_nodes = []
116
139
 
@@ -121,6 +144,8 @@ class RubyLsp::Ree::ParsedMethodNode
121
144
  call_nodes += parse_body_call_objects(node.statements.body)
122
145
  elsif node.respond_to?(:block) && node.block && node.block.is_a?(Prism::BlockNode)
123
146
  call_nodes += parse_body_call_objects(get_method_body(node.block))
147
+ elsif node.respond_to?(:value) && node.value
148
+ call_nodes += parse_body_call_objects([node.value])
124
149
  end
125
150
  end
126
151
 
@@ -140,6 +165,8 @@ class RubyLsp::Ree::ParsedMethodNode
140
165
  end
141
166
 
142
167
  def get_method_body(node)
168
+ return unless node.body
169
+
143
170
  if node.body.is_a?(Prism::BeginNode)
144
171
  node.body.statements.body
145
172
  else
@@ -3,6 +3,7 @@ require_relative 'formatters/missing_error_definitions_formatter'
3
3
  require_relative 'formatters/missing_error_contracts_formatter'
4
4
  require_relative 'formatters/missing_error_locales_formatter'
5
5
  require_relative 'formatters/unused_links_formatter'
6
+ require_relative 'formatters/missing_imports_formatter'
6
7
 
7
8
  module RubyLsp
8
9
  module Ree
@@ -23,11 +24,12 @@ module RubyLsp
23
24
  source = document.source
24
25
 
25
26
  formatters = [
26
- RubyLsp::Ree::SortLinksFormatter,
27
27
  RubyLsp::Ree::MissingErrorDefinitionsFormatter,
28
28
  RubyLsp::Ree::MissingErrorContractsFormatter,
29
29
  RubyLsp::Ree::MissingErrorLocalesFormatter,
30
30
  RubyLsp::Ree::UnusedLinksFormatter,
31
+ RubyLsp::Ree::MissingImportsFormatter,
32
+ RubyLsp::Ree::SortLinksFormatter,
31
33
  ].select do |formatter|
32
34
  formatter_name = formatter.name.split('::').last.to_sym
33
35
  @settings[formatter_name] != false
@@ -63,6 +63,36 @@ module RubyLsp
63
63
  source_lines[i] = ''
64
64
  end
65
65
  end
66
+
67
+ def add_links(parsed_doc, ree_objects, current_package)
68
+ new_text = ''
69
+
70
+ ree_objects.each do |ree_object|
71
+ object_package = package_name_from_uri(ree_object.uri)
72
+
73
+ link_text = if current_package == object_package
74
+ "\s\slink :#{ree_object.name}"
75
+ else
76
+ "\s\slink :#{ree_object.name}, from: :#{object_package}"
77
+ end
78
+
79
+ if parsed_doc.links_container_node
80
+ link_text = "\s\s" + link_text
81
+ end
82
+
83
+ new_text += "\n" + link_text
84
+ end
85
+
86
+ new_text += "\n"
87
+
88
+ if parsed_doc.has_blank_links_container?
89
+ new_text = "\sdo#{new_text}\s\send\n"
90
+ end
91
+
92
+ line = parsed_doc.links_container_node.location.start_line - 1
93
+
94
+ source_lines[line] = source_lines[line].chomp + new_text
95
+ end
66
96
  end
67
97
  end
68
98
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLsp
4
4
  module Ree
5
- VERSION = "0.1.15"
5
+ VERSION = "0.1.16"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-ree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-18 00:00:00.000000000 Z
11
+ date: 2025-04-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A Ruby LSP addon that adds extra editor functionality for Ree applications
14
14
  email:
@@ -33,6 +33,7 @@ files:
33
33
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_contracts_formatter.rb
34
34
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_definitions_formatter.rb
35
35
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_locales_formatter.rb
36
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_imports_formatter.rb
36
37
  - lib/ruby_lsp/ruby_lsp_ree/formatters/sort_links_formatter.rb
37
38
  - lib/ruby_lsp/ruby_lsp_ree/formatters/unused_links_formatter.rb
38
39
  - lib/ruby_lsp/ruby_lsp_ree/handlers/completion_handler.rb
@@ -41,6 +42,8 @@ files:
41
42
  - lib/ruby_lsp/ruby_lsp_ree/listeners/completion_listener.rb
42
43
  - lib/ruby_lsp/ruby_lsp_ree/listeners/definition_listener.rb
43
44
  - lib/ruby_lsp/ruby_lsp_ree/listeners/hover_listener.rb
45
+ - lib/ruby_lsp/ruby_lsp_ree/parsing/body_parsers/call_objects_parser.rb
46
+ - lib/ruby_lsp/ruby_lsp_ree/parsing/body_parsers/local_variables_parser.rb
44
47
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_base_document.rb
45
48
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_class_document.rb
46
49
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb