ruby-lsp-ree 0.1.19 → 0.1.20

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: a1e4648f75111e19d2590f3baadc308e6b02440c982b62947f8a5c381217f25d
4
- data.tar.gz: cb32459d1c9a3f9a185f3679649190a57f9da5a15f8a54eff2e0653c6a7f8850
3
+ metadata.gz: 46e3cdb9ee36b1b88e20d71c218ce355571da9aa6d405dfe0dcd879e1b37a5ef
4
+ data.tar.gz: a668bef693a29e639c6acb57f1a8ad2415d630242a2eecc04ff188f3d2fd93f4
5
5
  SHA512:
6
- metadata.gz: 9e0464c6765abdef69d671440ed281d7829dd4d0178c1faf1274bc9edde0abd0c87ab985f76b19a236fd7288a0d2603d6708690e7b6c025e122e6a04d617199a
7
- data.tar.gz: cd97920a10f8525baeb400eb68e83af9844a6935d5a29e37da1724149ba52d65db835591068fa821dc891b8136edf9d0cbbc8f4e8c7c5b31fe8d05a95765ce10
6
+ metadata.gz: bad0e641f6076ceb9544c0e159598a64e44343e890bddb627603d3ba9c0124b6a654f271e5599bc8e045ab7fac117b1844ce25bac5b6d2c4bb1b8457587b43b4
7
+ data.tar.gz: 8e691510c967c4788bc3ba9636571644a611bbe909a8e17f1cab4ea140f81f0878f346e0523f7d0020ae3d007248e402a21be3ab4afa201eb7d328afd1c79160
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.1.20] - 2025-05-15
2
+
3
+ - formatter: add missing columns to entities
4
+ - add missing imports: add missing imports from string interpolation and blocks
5
+ - add missing imports: add imports in dtos
6
+ - Go To Definition: fixed for entities inside dao
7
+
1
8
  ## [0.1.18] - 2025-05-05
2
9
 
3
10
  - formatter: do not remove links from mappers
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-lsp-ree (0.1.19)
4
+ ruby-lsp-ree (0.1.20)
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`, `ImportPackagesFormatter`
35
+ available formatters: `SortLinksFormatter`, `MissingErrorDefinitionsFormatter`, `MissingErrorContractsFormatter`, `MissingErrorLocalesFormatter`, `UnusedLinksFormatter`, `MissingImportsFormatter`, `ImportPackagesFormatter`, `SyncDaoColumnsFormatter`
36
36
 
37
37
  ## Functions
38
38
 
@@ -6,6 +6,8 @@ module RubyLsp
6
6
  class MissingImportsFormatter < BaseFormatter
7
7
  include RubyLsp::Ree::ReeLspUtils
8
8
 
9
+ IGNORE_METHOD_CALL_NAMES = ['build_dto', 'schema']
10
+
9
11
  def call(source, uri)
10
12
  return source unless @index
11
13
 
@@ -25,6 +27,7 @@ module RubyLsp
25
27
  }.compact
26
28
 
27
29
  objects_to_add.uniq!{ |obj| obj.name }
30
+ objects_to_add.reject!{ |obj| IGNORE_METHOD_CALL_NAMES.include?(obj.name) }
28
31
  objects_to_add.reject!{ |obj| parsed_doc.includes_linked_object?(obj.name) }
29
32
  return editor.source if objects_to_add.size == 0
30
33
 
@@ -0,0 +1,76 @@
1
+ require_relative 'base_formatter'
2
+
3
+ module RubyLsp
4
+ module Ree
5
+ class SyncDaoColumnsFormatter < BaseFormatter
6
+ include RubyLsp::Ree::ReeLspUtils
7
+
8
+ def call(source, uri)
9
+ path = get_uri_path(uri)
10
+ path_parts = path.split('/')
11
+ return source unless path_parts.include?('dao')
12
+
13
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source, type: :dao)
14
+ return source if !parsed_doc
15
+
16
+ parsed_doc.parse_class_includes
17
+ return source unless parsed_doc.includes_dao_dsl?
18
+ return source unless parsed_doc.has_schema?
19
+
20
+ dao_folder_index = path_parts.index('dao')
21
+ entities_folder = path_parts.take(dao_folder_index).join('/') + '/entities'
22
+
23
+ entity_filename = underscore(parsed_doc.schema_name) + '.rb'
24
+
25
+ entity_paths = Dir[File.join(entities_folder, '**', entity_filename)]
26
+ if entity_paths.size > 1
27
+ $stderr.puts("multiple entity paths for #{path}")
28
+ return source
29
+ elsif entity_paths.size == 0
30
+ $stderr.puts("no entity paths #{path}")
31
+ return source
32
+ end
33
+
34
+ entity_path = entity_paths.first
35
+ entity_source = File.read(entity_path)
36
+ entity_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(entity_source, type: :entity)
37
+
38
+ missed_column_names = parsed_doc.dao_fields.map(&:name) - entity_doc.columns.map(&:name)
39
+ missed_columns = parsed_doc.dao_fields.select{ missed_column_names.include?(_1.name) }
40
+ add_columns(entity_path, entity_source, entity_doc, missed_columns)
41
+
42
+ source
43
+ end
44
+
45
+ private
46
+
47
+ def add_columns(entity_path, entity_source, entity_doc, missed_columns)
48
+ return if !missed_columns || missed_columns.size == 0
49
+
50
+ columns_strs = missed_columns.map do |col|
51
+ str = " column :#{col.name}, #{col.type}"
52
+ if col.has_default?
53
+ str += ", default: #{col.default}"
54
+ end
55
+ str
56
+ end
57
+
58
+ columns_str = columns_strs.join("\n") + "\n"
59
+
60
+ source_lines = entity_source.lines
61
+
62
+ prev_line_location = if entity_doc.columns.size > 0
63
+ entity_doc.columns.last.location
64
+ else
65
+ entity_doc.build_dto_node.location
66
+ end
67
+
68
+ line = prev_line_location.start_line - 1
69
+
70
+ source_lines[line] += columns_str
71
+
72
+ File.write(entity_path, source_lines.join)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -53,6 +53,8 @@ class RubyLsp::Ree::CallObjectsParser
53
53
  def parse_body_call_objects(node_body)
54
54
  call_objects = []
55
55
 
56
+ return call_objects unless node_body
57
+
56
58
  node_body.each do |node|
57
59
  if node.is_a?(Prism::CallNode)
58
60
  if node.receiver
@@ -78,10 +80,6 @@ class RubyLsp::Ree::CallObjectsParser
78
80
  if node.respond_to?(:statements)
79
81
  call_objects += parse_body_call_objects(node.statements.body)
80
82
  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
83
 
86
84
  if node.respond_to?(:value) && node.value
87
85
  call_objects += parse_body_call_objects([node.value])
@@ -94,6 +92,14 @@ class RubyLsp::Ree::CallObjectsParser
94
92
  if node.respond_to?(:right) && node.right
95
93
  call_objects += parse_body_call_objects([node.right])
96
94
  end
95
+
96
+ if node.respond_to?(:parts) && node.parts
97
+ call_objects += parse_body_call_objects(node.parts)
98
+ end
99
+ end
100
+
101
+ if node.respond_to?(:block) && node.block && node.block.is_a?(Prism::BlockNode)
102
+ call_objects += parse_body_call_objects(get_method_body(node.block))
97
103
  end
98
104
  end
99
105
 
@@ -20,6 +20,10 @@ class RubyLsp::Ree::ParsedBaseDocument
20
20
  false
21
21
  end
22
22
 
23
+ def includes_dao_dsl?
24
+ false
25
+ end
26
+
23
27
  def includes_routes_dsl?
24
28
  false
25
29
  end
@@ -11,7 +11,7 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
11
11
  include RubyLsp::Ree::ReeConstants
12
12
 
13
13
  attr_reader :class_node, :class_includes,
14
- :values, :filters, :bean_methods, :links_container_block_node, :error_definitions,
14
+ :values, :bean_methods, :links_container_block_node, :error_definitions,
15
15
  :error_definition_names, :doc_instance_methods, :links_container_node,
16
16
  :defined_classes, :defined_consts
17
17
 
@@ -36,6 +36,10 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
36
36
  @class_includes.any?{ node_name(_1) == LINK_DSL_MODULE }
37
37
  end
38
38
 
39
+ def includes_dao_dsl?
40
+ @class_includes.any?{ node_name(_1) == DAO_DSL_MODULE }
41
+ end
42
+
39
43
  def includes_routes_dsl?
40
44
  @class_includes.any?{ node_name(_1) == ROUTES_DSL_MODULE }
41
45
  end
@@ -126,15 +130,6 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
126
130
  .map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped) }
127
131
  end
128
132
 
129
- def parse_filters
130
- return unless class_node
131
-
132
- @filters ||= class_node.body.body
133
- .select{ node_name(_1) == :filter }
134
- .map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped, signatures: parse_filter_signature(_1)) }
135
-
136
- end
137
-
138
133
  def parse_bean_methods
139
134
  return unless has_body?
140
135
 
@@ -163,15 +158,6 @@ class RubyLsp::Ree::ParsedClassDocument < RubyLsp::Ree::ParsedBaseDocument
163
158
  @doc_instance_methods
164
159
  end
165
160
 
166
- def parse_filter_signature(filter_node)
167
- return [] unless filter_node
168
-
169
- lambda_node = filter_node.arguments&.arguments[1]
170
- return [] unless lambda_node
171
-
172
- parse_signatures_from_params(lambda_node.parameters.parameters)
173
- end
174
-
175
161
  def parse_signatures_from_params(parameters)
176
162
  signature_params = signature_params_from_node(parameters)
177
163
  [RubyIndexer::Entry::Signature.new(signature_params)]
@@ -0,0 +1,96 @@
1
+ class RubyLsp::Ree::ParsedDaoDocument < RubyLsp::Ree::ParsedClassDocument
2
+ include RubyLsp::Ree::ReeLspUtils
3
+
4
+ attr_reader :dao_fields, :filters
5
+
6
+ class DaoField
7
+ attr_reader :name, :location, :type, :default
8
+
9
+ def initialize(name:, location:, type:, default:)
10
+ @name = name
11
+ @location = location
12
+ @type = type
13
+ @default = default
14
+ end
15
+
16
+ def has_default?
17
+ !!@default
18
+ end
19
+ end
20
+
21
+ def initialize(ast, package_name = nil)
22
+ super
23
+ parse_filters
24
+ parse_dao_fields
25
+ end
26
+
27
+ def has_schema?
28
+ !!@schema_node
29
+ end
30
+
31
+ def schema_name
32
+ @schema_node.arguments.arguments.first.name.to_s
33
+ end
34
+
35
+ private
36
+
37
+ def parse_dao_fields
38
+ @dao_fields = []
39
+ @schema_node = nil
40
+ return unless has_body?
41
+
42
+ @schema_node = class_node.body.body
43
+ .detect{ |node| node.is_a?(Prism::CallNode) && node.name == :schema }
44
+
45
+ return unless @schema_node
46
+
47
+ @dao_fields = @schema_node.block.body.body.map do |node|
48
+ if node.name.to_s == 'pg_jsonb'
49
+ field_type = "Nilor[Hash]"
50
+ default_val = "nil"
51
+ else
52
+ field_type = camelize(node.name.to_s)
53
+ default_val = nil
54
+
55
+ if field_allows_null?(node)
56
+ field_type = "Nilor[#{field_type}]"
57
+ default_val = "nil"
58
+ end
59
+ end
60
+
61
+ DaoField.new(
62
+ name: node.arguments.arguments.first.unescaped,
63
+ location: node.location,
64
+ type: field_type,
65
+ default: default_val
66
+ )
67
+ end
68
+ end
69
+
70
+ def parse_filters
71
+ return unless has_body?
72
+
73
+ @filters ||= class_node.body.body
74
+ .select{ node_name(_1) == :filter }
75
+ .map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped, signatures: parse_filter_signature(_1)) }
76
+ end
77
+
78
+ def parse_filter_signature(filter_node)
79
+ return [] unless filter_node
80
+
81
+ lambda_node = filter_node.arguments&.arguments[1]
82
+ return [] if !lambda_node || !lambda_node.parameters
83
+
84
+ parse_signatures_from_params(lambda_node.parameters.parameters)
85
+ end
86
+
87
+ def field_allows_null?(node)
88
+ kw_node = node.arguments.arguments.detect{ _1.is_a?(Prism::KeywordHashNode) }
89
+ return false unless kw_node
90
+
91
+ null_el = kw_node.elements.detect{ _1.key.unescaped == "null" }
92
+ return false unless null_el
93
+
94
+ null_el.value.is_a?(Prism::TrueNode)
95
+ end
96
+ end
@@ -2,6 +2,8 @@ require 'prism'
2
2
  require_relative 'parsed_class_document'
3
3
  require_relative 'parsed_rspec_document'
4
4
  require_relative 'parsed_route_document'
5
+ require_relative 'parsed_entity_document'
6
+ require_relative 'parsed_dao_document'
5
7
 
6
8
  class RubyLsp::Ree::ParsedDocumentBuilder
7
9
  extend RubyLsp::Ree::ReeLspUtils
@@ -40,6 +42,8 @@ class RubyLsp::Ree::ParsedDocumentBuilder
40
42
  build_bean_document(ast)
41
43
  when :route
42
44
  build_route_document(ast)
45
+ when :entity
46
+ build_entity_document(ast)
43
47
  else
44
48
  build_detected_document_type(ast, package_name)
45
49
  end
@@ -88,15 +92,6 @@ class RubyLsp::Ree::ParsedDocumentBuilder
88
92
 
89
93
  document
90
94
  end
91
-
92
- def self.build_dao_document(ast)
93
- document = RubyLsp::Ree::ParsedClassDocument.new(ast)
94
-
95
- document.parse_class_node
96
- document.parse_filters
97
-
98
- document
99
- end
100
95
 
101
96
  def self.build_bean_document(ast)
102
97
  document = RubyLsp::Ree::ParsedClassDocument.new(ast)
@@ -107,10 +102,18 @@ class RubyLsp::Ree::ParsedDocumentBuilder
107
102
  document
108
103
  end
109
104
 
105
+ def self.build_dao_document(ast)
106
+ RubyLsp::Ree::ParsedDaoDocument.new(ast)
107
+ end
108
+
110
109
  def self.build_route_document(ast)
111
110
  RubyLsp::Ree::ParsedRouteDocument.new(ast)
112
111
  end
113
112
 
113
+ def self.build_entity_document(ast)
114
+ RubyLsp::Ree::ParsedEntityDocument.new(ast)
115
+ end
116
+
114
117
  def self.is_ruby_file?(uri)
115
118
  File.extname(uri.to_s) == '.rb'
116
119
  end
@@ -0,0 +1,41 @@
1
+ class RubyLsp::Ree::ParsedEntityDocument < RubyLsp::Ree::ParsedClassDocument
2
+ include RubyLsp::Ree::ReeLspUtils
3
+
4
+ attr_reader :columns, :build_dto_node
5
+
6
+ class EntityField
7
+ attr_reader :name, :location
8
+
9
+ def initialize(name:, location:)
10
+ @name = name
11
+ @location = location
12
+ end
13
+ end
14
+
15
+ def initialize(ast, package_name = nil)
16
+ super
17
+ parse_class_includes
18
+ parse_build_dto_structure
19
+ end
20
+
21
+ private
22
+
23
+ def parse_build_dto_structure
24
+ @columns = []
25
+ @build_dto_node = nil
26
+
27
+ return unless has_body?
28
+
29
+ @build_dto_node = @class_node.body.body.detect{ node_name(_1) == :build_dto }
30
+ return unless @build_dto_node.block.body
31
+
32
+ @columns = @build_dto_node.block.body.body
33
+ .select{ _1.name == :column }
34
+ .map do
35
+ EntityField.new(
36
+ name: _1.arguments.arguments.first.unescaped,
37
+ location: _1.location
38
+ )
39
+ end
40
+ end
41
+ end
@@ -14,6 +14,8 @@ class RubyLsp::Ree::ParsedMethodNode
14
14
  end
15
15
 
16
16
  def param_names
17
+ return [] unless @method_node.parameters
18
+
17
19
  @method_node.parameters.requireds.map(&:name) +
18
20
  @method_node.parameters.keywords.map(&:name) +
19
21
  [@method_node.parameters.rest&.name] +
@@ -4,6 +4,7 @@ module RubyLsp
4
4
  LINK_DSL_MODULE = 'Ree::LinkDSL'
5
5
  ROUTES_DSL_MODULE = 'ReeRoutes::DSL'
6
6
  MAPPER_DSL_MODULE = 'ReeMapper::DSL'
7
+ DAO_DSL_MODULE = 'ReeDao::DSL'
7
8
 
8
9
  LINKS_CONTAINER_TYPES = [
9
10
  :fn,
@@ -5,6 +5,7 @@ require_relative 'formatters/missing_error_locales_formatter'
5
5
  require_relative 'formatters/unused_links_formatter'
6
6
  require_relative 'formatters/missing_imports_formatter'
7
7
  require_relative 'formatters/import_packages_formatter'
8
+ require_relative 'formatters/sync_dao_columns_formatter'
8
9
 
9
10
  module RubyLsp
10
11
  module Ree
@@ -32,6 +33,7 @@ module RubyLsp
32
33
  RubyLsp::Ree::MissingImportsFormatter,
33
34
  RubyLsp::Ree::ImportPackagesFormatter,
34
35
  RubyLsp::Ree::SortLinksFormatter,
36
+ RubyLsp::Ree::SyncDaoColumnsFormatter,
35
37
  ].select do |formatter|
36
38
  formatter_name = formatter.name.split('::').last.to_sym
37
39
  @settings[formatter_name] != false
@@ -12,9 +12,10 @@ module RubyLsp
12
12
  def on_call_node_enter(node)
13
13
  return unless @listener.current_owner
14
14
 
15
- if node.name == SCHEMA_NODE_NAME
16
- return index_ree_schema(node)
17
- end
15
+ # remove for now as it breaks go-to-definition for entities
16
+ # if node.name == SCHEMA_NODE_NAME
17
+ # return index_ree_schema(node)
18
+ # end
18
19
 
19
20
  return unless REE_INDEXED_OBJECTS.include?(node.name)
20
21
  return unless node.arguments
@@ -88,9 +88,16 @@ module RubyLsp
88
88
  new_text = "\sdo#{new_text}\s\send\n"
89
89
  end
90
90
 
91
- line = parsed_doc.links_container_node.location.start_line - 1
92
-
93
- source_lines[line] = source_lines[line].chomp + new_text
91
+ if parsed_doc.links_container_node
92
+ line = parsed_doc.links_container_node.location.start_line - 1
93
+ source_lines[line] = source_lines[line].chomp + new_text
94
+ elsif parsed_doc.link_nodes.size > 0
95
+ line = parsed_doc.link_nodes.last.location.end_line - 1
96
+ source_lines[line] = source_lines[line].chomp + new_text
97
+ else
98
+ line = parsed_doc.class_includes.last.location.end_line - 1
99
+ source_lines[line] = source_lines[line].chomp + "\n" + new_text
100
+ end
94
101
  end
95
102
 
96
103
  def change_link_package(link_node, new_package, current_package)
@@ -190,6 +190,17 @@ module RubyLsp
190
190
  word.downcase!
191
191
  word
192
192
  end
193
+
194
+ # copied from ree string_utils
195
+ def camelize(string, uppercase_first_letter = true)
196
+ if uppercase_first_letter
197
+ string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
198
+ else
199
+ string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
200
+ end
201
+
202
+ string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub("/", "::")
203
+ end
193
204
  end
194
205
  end
195
206
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLsp
4
4
  module Ree
5
- VERSION = "0.1.19"
5
+ VERSION = "0.1.20"
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.19
4
+ version: 0.1.20
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-05-05 00:00:00.000000000 Z
11
+ date: 2025-05-16 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:
@@ -36,6 +36,7 @@ files:
36
36
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_locales_formatter.rb
37
37
  - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_imports_formatter.rb
38
38
  - lib/ruby_lsp/ruby_lsp_ree/formatters/sort_links_formatter.rb
39
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/sync_dao_columns_formatter.rb
39
40
  - lib/ruby_lsp/ruby_lsp_ree/formatters/unused_links_formatter.rb
40
41
  - lib/ruby_lsp/ruby_lsp_ree/handlers/completion_handler.rb
41
42
  - lib/ruby_lsp/ruby_lsp_ree/handlers/definition_handler.rb
@@ -47,7 +48,9 @@ files:
47
48
  - lib/ruby_lsp/ruby_lsp_ree/parsing/body_parsers/local_variables_parser.rb
48
49
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_base_document.rb
49
50
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_class_document.rb
51
+ - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_dao_document.rb
50
52
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb
53
+ - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_entity_document.rb
51
54
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_link_node.rb
52
55
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_method_node.rb
53
56
  - lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_route_document.rb