ruby-lsp-ree 0.1.16 → 0.1.18

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: 4d1b2bf9c373c729b6412704fd6daf364ed4c8b278405f52b4c1fc32b256570c
4
- data.tar.gz: 0bde5c4413ec6d4ccfd807dc6e6eb33d1e47ab018dfbfaad47200775b2c3badb
3
+ metadata.gz: 9061105dc55ff366d35880fe7960838159e9635115e26b352197837cf30c70c7
4
+ data.tar.gz: 147936b7c46a0f5ac07442195079fdee2ae98ca6b88df0a800e48ab3d2c62f77
5
5
  SHA512:
6
- metadata.gz: 6a97047d4924f208415ba6c5ed45e6abb57141e74fd44ad79368a9a7dd025a2ce0c36cfe2ca95db6fd0dab9586d9a1c5484be2e930ab129ab6acb7975fce310f
7
- data.tar.gz: 17daba371512607775021704c56895bcc9e41666e598c9603c7da22ebac567de1cc7a2957f050a6657429057557dcbbc86654141eba98faa8c7ddde6e892a030
6
+ metadata.gz: 344f57eaefab54e046008b80b81f6d131eb9128e5b88230543f09ebc8d93af0b979310b370c0f951fd1667a895ab9d5dff5a5d85a96e34b005bebb04d02ee992
7
+ data.tar.gz: 0ea492e66ed6beeb7deef48e3f89f8c54295645afeece6e290c43b41979a28980aa470e840ef1fc9e77c4e04f176cd29f1e75b6c32638d994615344e83831868
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [0.1.18] - 2025-05-05
2
+
3
+ - formatter: do not remove links from mappers
4
+ - formatter: change incorrect package for link on save
5
+ - add missing imports: add more cases with missing imports
6
+
7
+ ## [0.1.17] - 2025-04-28
8
+
9
+ - add missing imports: improved handling imports with the same name
10
+ - add missing imports: handle more cases with missing imports (nested calls, predicates, etc.)
11
+
1
12
  ## [0.1.16] - 2025-04-24
2
13
 
3
14
  - formatter: add missing imports
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-lsp-ree (0.1.16)
4
+ ruby-lsp-ree (0.1.18)
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`, `MissingImportsFormatter`
35
+ available formatters: `SortLinksFormatter`, `MissingErrorDefinitionsFormatter`, `MissingErrorContractsFormatter`, `MissingErrorLocalesFormatter`, `UnusedLinksFormatter`, `MissingImportsFormatter`, `ImportPackagesFormatter`
36
36
 
37
37
  ## Functions
38
38
 
@@ -43,4 +43,5 @@ available formatters: `SortLinksFormatter`, `MissingErrorDefinitionsFormatter`,
43
43
  - missing error locales detection
44
44
  - Go To Definition for Ree objects
45
45
  - hover information for Ree objects and error locales
46
- - Ree templates support
46
+ - Ree templates support
47
+ - add missing import links on save
@@ -0,0 +1,42 @@
1
+ require_relative 'base_formatter'
2
+ require_relative "../ree_object_finder"
3
+
4
+ module RubyLsp
5
+ module Ree
6
+ class ImportPackagesFormatter < 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, uri)
13
+
14
+ finder = ReeObjectFinder.new(@index)
15
+ editor = RubyLsp::Ree::ReeSourceEditor.new(source)
16
+
17
+ current_package = package_name_from_uri(uri)
18
+
19
+ parsed_doc.link_nodes.select(&:object_name_type?).each do |link_node|
20
+ next if finder.find_object_for_package(link_node.name, link_node.link_package_name)
21
+
22
+ ree_objects = finder.find_objects(link_node.name)
23
+
24
+ if ree_objects.size == 1
25
+ editor.change_link_package(link_node, ree_objects.first.object_package, current_package)
26
+ else
27
+ send_message(link_node.name)
28
+ end
29
+ end
30
+
31
+ editor.source
32
+ end
33
+
34
+ private
35
+
36
+ def send_message(link_name)
37
+ message = "Link package is invalid for: #{link_name}"
38
+ @message_queue << RubyLsp::Notification.window_show_message(message, type: Constant::MessageType::ERROR)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -17,16 +17,11 @@ module RubyLsp
17
17
 
18
18
  current_package = package_name_from_uri(uri)
19
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)
20
+ method_calls = parsed_doc.parse_method_calls
21
+ filtered_method_calls = filter_method_calls(parsed_doc, method_calls)
22
+ objects_to_add = filtered_method_calls.map{ |fn_call|
23
+ ree_objects = finder.find_objects(fn_call.name.to_s)
24
+ choose_object_to_add(ree_objects, current_package)
30
25
  }.compact
31
26
 
32
27
  objects_to_add.uniq!{ |obj| obj.name }
@@ -39,25 +34,40 @@ module RubyLsp
39
34
 
40
35
  private
41
36
 
42
- def filter_fn_calls(parsed_doc, fn_calls)
37
+ def filter_method_calls(parsed_doc, method_calls)
43
38
  parsed_doc.parse_instance_methods
44
39
 
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
40
+ method_calls.select do |method_call|
41
+ if !method_call.method_name
53
42
  true
43
+ elsif parsed_doc.doc_instance_methods.map(&:name).include?(method_call.name)
44
+ false
54
45
  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)
46
+ method_obj = parsed_doc.doc_instance_methods.detect{ _1.name == method_call.method_name }
47
+ local_variables = method_obj.parse_local_variables.map(&:name)
48
+ method_params = method_obj.param_names
49
+
50
+ !local_variables.include?(method_call.name) && !method_params.include?(method_call.name)
58
51
  end
59
52
  end
60
53
  end
54
+
55
+ def choose_object_to_add(ree_objects, current_package)
56
+ return if !ree_objects || ree_objects.size == 0
57
+ return ree_objects.first if ree_objects.size == 1
58
+
59
+ current_package_object = ree_objects.detect{ _1.object_package == current_package }
60
+ return current_package_object if current_package_object
61
+
62
+ package_names = ree_objects.map(&:object_package)
63
+ if package_names.sort == ['ree_date', 'ree_datetime'].sort
64
+ return ree_objects.detect{ _1.object_package == 'ree_datetime' }
65
+ end
66
+
67
+ ree_object = ree_objects.first
68
+ ree_object.set_package!('FILL_PACKAGE')
69
+ ree_object
70
+ end
61
71
  end
62
72
  end
63
73
  end
@@ -34,7 +34,7 @@ module RubyLsp
34
34
  end
35
35
  end
36
36
 
37
- next if link_is_used?(link_node, remove_imports)
37
+ next if link_is_used?(link_node, remove_imports) || parsed_doc.includes_mapper_dsl?
38
38
 
39
39
  editor.remove_link(link_node)
40
40
  removed_links += 1
@@ -52,24 +52,59 @@ class RubyLsp::Ree::CallObjectsParser
52
52
 
53
53
  def parse_body_call_objects(node_body)
54
54
  call_objects = []
55
-
55
+
56
56
  node_body.each do |node|
57
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])
58
+ if node.receiver
59
+ receiver = get_first_receiver(node)
60
+
61
+ if receiver.is_a?(Prism::CallNode)
62
+ call_objects += parse_body_call_objects([receiver])
63
+ end
64
+ else
65
+ call_objects << CallObject.new(name: node.name, type: :method_call)
66
+ end
67
+
68
+ call_objects += parse_call_objects_from_args(node.arguments)
69
+ else
70
+ if node.respond_to?(:elements)
71
+ call_objects += parse_body_call_objects(node.elements)
72
+ end
73
+
74
+ if node.respond_to?(:predicate)
75
+ call_objects += parse_body_call_objects([node.predicate])
76
+ end
77
+
78
+ if node.respond_to?(:statements)
79
+ call_objects += parse_body_call_objects(node.statements.body)
80
+ end
81
+
82
+ if node.respond_to?(:block) && node.block && node.block.is_a?(Prism::BlockNode)
83
+ call_objects += parse_body_call_objects(get_method_body(node.block))
84
+ end
85
+
86
+ if node.respond_to?(:value) && node.value
87
+ call_objects += parse_body_call_objects([node.value])
88
+ end
89
+
90
+ if node.respond_to?(:left) && node.left
91
+ call_objects += parse_body_call_objects([node.left])
92
+ end
93
+
94
+ if node.respond_to?(:right) && node.right
95
+ call_objects += parse_body_call_objects([node.right])
96
+ end
67
97
  end
68
98
  end
69
99
 
70
100
  call_objects
71
101
  end
72
102
 
103
+ def parse_call_objects_from_args(node_arguments)
104
+ return [] if !node_arguments || !node_arguments.arguments
105
+ parse_body_call_objects(node_arguments.arguments)
106
+ end
107
+
73
108
  def parse_body_call_expressions(node_body)
74
109
  call_expressions = []
75
110
 
@@ -28,6 +28,10 @@ class RubyLsp::Ree::ParsedBaseDocument
28
28
  false
29
29
  end
30
30
 
31
+ def includes_mapper_dsl?
32
+ false
33
+ end
34
+
31
35
  def includes_linked_object?(obj_name)
32
36
  @link_nodes.map(&:name).include?(obj_name)
33
37
  end
@@ -36,6 +40,12 @@ class RubyLsp::Ree::ParsedBaseDocument
36
40
  @link_nodes.detect{ node_name(_1) == name }
37
41
  end
38
42
 
43
+ def find_import_for_package(name, package_name)
44
+ @link_nodes.detect do |link_node|
45
+ link_node.imports.include?(name) && link_node.link_package_name == package_name
46
+ end
47
+ end
48
+
39
49
  def node_name(node)
40
50
  return nil unless node.respond_to?(:name)
41
51
 
@@ -40,6 +40,10 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
40
40
  @class_includes.any?{ node_name(_1) == ROUTES_DSL_MODULE }
41
41
  end
42
42
 
43
+ def includes_mapper_dsl?
44
+ @class_includes.any?{ node_name(_1) == MAPPER_DSL_MODULE }
45
+ end
46
+
43
47
  def includes_ree_dsl?
44
48
  ree_dsls.size > 0
45
49
  end
@@ -54,12 +58,6 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
54
58
  end
55
59
  end
56
60
 
57
- def find_import_for_package(name, package_name)
58
- @link_nodes.detect do |link_node|
59
- link_node.imports.include?(name) && link_node.link_package_name == package_name
60
- end
61
- end
62
-
63
61
  def has_blank_links_container?
64
62
  links_container_node && !@links_container_block_node
65
63
  end
@@ -77,9 +75,11 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
77
75
  end
78
76
 
79
77
  def parse_class_includes
80
- return unless has_body?
78
+ return @class_includes if @class_includes
79
+ @class_includes = []
80
+ return @class_includes unless has_body?
81
81
 
82
- @class_includes ||= class_node.body.body.select{ node_name(_1) == :include }.map do |class_include|
82
+ @class_includes = class_node.body.body.select{ node_name(_1) == :include }.map do |class_include|
83
83
  first_arg = class_include.arguments.arguments.first
84
84
 
85
85
  include_name = case first_arg
@@ -205,12 +205,8 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
205
205
  .map(&:name)
206
206
  end
207
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 }
208
+ def parse_method_calls
209
+ RubyLsp::Ree::CallObjectsParser.new(self).class_call_objects
214
210
  end
215
211
 
216
212
  def class_name
@@ -1,7 +1,7 @@
1
1
  require 'prism'
2
2
 
3
3
  class RubyLsp::Ree::ParsedLinkNode
4
- attr_reader :node, :document_package, :name, :import_items
4
+ attr_reader :node, :document_package, :name, :import_items, :from_param
5
5
 
6
6
  FROM_ARG_KEY = 'from'
7
7
  IMPORT_ARG_KEY = 'import'
@@ -27,6 +27,8 @@ class RubyLsp::Ree::ParsedLinkNode
27
27
  @node = node
28
28
  @document_package = document_package
29
29
  @name = parse_name
30
+
31
+ parse_params
30
32
  end
31
33
 
32
34
  def link_package_name
@@ -43,13 +45,9 @@ class RubyLsp::Ree::ParsedLinkNode
43
45
  end
44
46
 
45
47
  def from_arg_value
46
- @kw_args ||= @node.arguments.arguments.detect{ |arg| arg.is_a?(Prism::KeywordHashNode) }
47
- return unless @kw_args
48
-
49
- @from_param ||= @kw_args.elements.detect{ _1.key.unescaped == FROM_ARG_KEY }
50
48
  return unless @from_param
51
49
 
52
- @from_param.value.unescaped
50
+ @from_param.value.respond_to?(:unescaped) ? @from_param.value.unescaped : nil
53
51
  end
54
52
 
55
53
  def name_arg_node
@@ -125,6 +123,14 @@ class RubyLsp::Ree::ParsedLinkNode
125
123
 
126
124
  private
127
125
 
126
+ def parse_params
127
+ @kw_args = @node.arguments.arguments.detect{ |arg| arg.is_a?(Prism::KeywordHashNode) }
128
+ @from_param = nil
129
+ return unless @kw_args
130
+
131
+ @from_param = @kw_args.elements.detect{ _1.key.unescaped == FROM_ARG_KEY }
132
+ end
133
+
128
134
  def last_arg
129
135
  @node.arguments.arguments.last
130
136
  end
@@ -13,6 +13,13 @@ class RubyLsp::Ree::ParsedMethodNode
13
13
  @method_node.name
14
14
  end
15
15
 
16
+ def param_names
17
+ @method_node.parameters.requireds.map(&:name) +
18
+ @method_node.parameters.keywords.map(&:name) +
19
+ [@method_node.parameters.rest&.name] +
20
+ [@method_node.parameters.keyword_rest&.name]
21
+ end
22
+
16
23
  def has_contract?
17
24
  !!@contract_node
18
25
  end
@@ -3,6 +3,7 @@ module RubyLsp
3
3
  module ReeConstants
4
4
  LINK_DSL_MODULE = 'Ree::LinkDSL'
5
5
  ROUTES_DSL_MODULE = 'ReeRoutes::DSL'
6
+ MAPPER_DSL_MODULE = 'ReeMapper::DSL'
6
7
 
7
8
  LINKS_CONTAINER_TYPES = [
8
9
  :fn,
@@ -4,6 +4,7 @@ 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
6
  require_relative 'formatters/missing_imports_formatter'
7
+ require_relative 'formatters/import_packages_formatter'
7
8
 
8
9
  module RubyLsp
9
10
  module Ree
@@ -29,6 +30,7 @@ module RubyLsp
29
30
  RubyLsp::Ree::MissingErrorLocalesFormatter,
30
31
  RubyLsp::Ree::UnusedLinksFormatter,
31
32
  RubyLsp::Ree::MissingImportsFormatter,
33
+ RubyLsp::Ree::ImportPackagesFormatter,
32
34
  RubyLsp::Ree::SortLinksFormatter,
33
35
  ].select do |formatter|
34
36
  formatter_name = formatter.name.split('::').last.to_sym
@@ -1,3 +1,4 @@
1
+ require 'delegate'
1
2
  require_relative "utils/ree_lsp_utils"
2
3
 
3
4
  module RubyLsp
@@ -9,6 +10,20 @@ module RubyLsp
9
10
 
10
11
  REE_OBJECT_STRING = 'ree_object'
11
12
 
13
+ class ReeObjectDecorator < SimpleDelegator
14
+ include RubyLsp::Ree::ReeLspUtils
15
+
16
+ def object_package
17
+ return @package_name if @package_name
18
+
19
+ package_name_from_uri(uri)
20
+ end
21
+
22
+ def set_package!(package_name)
23
+ @package_name = package_name
24
+ end
25
+ end
26
+
12
27
  def initialize(index)
13
28
  @index = index
14
29
  end
@@ -41,6 +56,14 @@ module RubyLsp
41
56
  objects_by_name.detect{ _1.comments.to_s.lines.first&.chomp == REE_OBJECT_STRING }
42
57
  end
43
58
 
59
+ def find_objects(name)
60
+ objects_by_name = @index[name]
61
+ return [] unless objects_by_name
62
+
63
+ ree_objects = objects_by_name.select{ _1.comments.to_s.lines.first&.chomp == REE_OBJECT_STRING }
64
+ decorate_objects(ree_objects)
65
+ end
66
+
44
67
  def find_object_for_package(name, package_name)
45
68
  objects_by_name = @index[name]
46
69
  return unless objects_by_name
@@ -65,6 +88,12 @@ module RubyLsp
65
88
  def object_documentation(ree_object)
66
89
  ree_object.comments.lines[2..-1].join("\n").chomp
67
90
  end
91
+
92
+ private
93
+
94
+ def decorate_objects(ree_objects)
95
+ ree_objects.map{ ReeObjectDecorator.new(_1) }
96
+ end
68
97
  end
69
98
  end
70
99
  end
@@ -68,12 +68,11 @@ module RubyLsp
68
68
  new_text = ''
69
69
 
70
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
71
+ link_text = if current_package == ree_object.object_package
74
72
  "\s\slink :#{ree_object.name}"
75
73
  else
76
- "\s\slink :#{ree_object.name}, from: :#{object_package}"
74
+ package_str = ree_object.object_package == 'FILL_PACKAGE' ? 'FILL_PACKAGE' : ":#{ree_object.object_package}"
75
+ "\s\slink :#{ree_object.name}, from: #{package_str}"
77
76
  end
78
77
 
79
78
  if parsed_doc.links_container_node
@@ -93,6 +92,34 @@ module RubyLsp
93
92
 
94
93
  source_lines[line] = source_lines[line].chomp + new_text
95
94
  end
95
+
96
+ def change_link_package(link_node, new_package, current_package)
97
+ if new_package == current_package # change package to current -> remove 'from' param
98
+ return unless link_node.from_param
99
+
100
+ from_param_location = link_node.from_param.location
101
+ name_location = link_node.first_arg_location
102
+
103
+ line = from_param_location.start_line - 1
104
+ start_column = name_location.end_column - 1
105
+ end_column = from_param_location.end_column
106
+
107
+ source_lines[line] = source_lines[line][0..start_column] + source_lines[line][end_column..-1]
108
+ elsif link_node.from_param
109
+ from_param_location = link_node.from_param.value.location
110
+ line = from_param_location.start_line - 1
111
+ start_column = from_param_location.start_column - 1
112
+ end_column = from_param_location.end_column
113
+
114
+ source_lines[line] = source_lines[line][0..start_column] + ":#{new_package}" + source_lines[line][end_column..-1]
115
+ else
116
+ name_location = link_node.first_arg_location
117
+ line = name_location.start_line - 1
118
+ start_column = name_location.end_column - 1
119
+
120
+ source_lines[line] = source_lines[line][0..start_column] + ", from: :#{new_package}" + source_lines[line][start_column+1..-1]
121
+ end
122
+ end
96
123
  end
97
124
  end
98
125
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLsp
4
4
  module Ree
5
- VERSION = "0.1.16"
5
+ VERSION = "0.1.18"
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.16
4
+ version: 0.1.18
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-24 00:00:00.000000000 Z
11
+ date: 2025-05-05 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:
@@ -30,6 +30,7 @@ files:
30
30
  - lib/ruby_lsp/ruby_lsp_ree/completion/const_additional_text_edits_creator.rb
31
31
  - lib/ruby_lsp/ruby_lsp_ree/completion/method_additional_text_edits_creator.rb
32
32
  - lib/ruby_lsp/ruby_lsp_ree/formatters/base_formatter.rb
33
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/import_packages_formatter.rb
33
34
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_contracts_formatter.rb
34
35
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_definitions_formatter.rb
35
36
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_locales_formatter.rb