ruby-lsp-ree 0.1.19 → 0.1.21

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: 3f11e82aca2c43fe42fb37077f3bc8590c5c68e06c13d9068c6872ae859faaad
4
+ data.tar.gz: 07fa4705541847c82764e5c4e27c13b190c240e63c12bb86a98fd49577be6090
5
5
  SHA512:
6
- metadata.gz: 9e0464c6765abdef69d671440ed281d7829dd4d0178c1faf1274bc9edde0abd0c87ab985f76b19a236fd7288a0d2603d6708690e7b6c025e122e6a04d617199a
7
- data.tar.gz: cd97920a10f8525baeb400eb68e83af9844a6935d5a29e37da1724149ba52d65db835591068fa821dc891b8136edf9d0cbbc8f4e8c7c5b31fe8d05a95765ce10
6
+ metadata.gz: b7bc67742989ee60af3e3b9f4dd38b44183522e86d7b8f1af90c255edb10179086e55255312fd80abf7306af37d318f0e8343f6183926ffb424288ff479fce4f
7
+ data.tar.gz: 593ebeac60988a3a9f9db7137d99a313b07d7ebd6385e7e3a566c93960a7ebfd803ce1fccef8d595b12ecd53c5e22a3afa41b4fad279d8de49870ffa9a1c4737
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.1.21] - 2025-05-21
2
+
3
+ - formatter: deduplicate dao columns
4
+ - unused links formatter: handle link aliases
5
+
6
+ ## [0.1.20] - 2025-05-15
7
+
8
+ - formatter: add missing columns to entities
9
+ - add missing imports: add missing imports from string interpolation and blocks
10
+ - add missing imports: add imports in dtos
11
+ - Go To Definition: fixed for entities inside dao
12
+
1
13
  ## [0.1.18] - 2025-05-05
2
14
 
3
15
  - 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.21)
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,103 @@
1
+ require_relative 'base_formatter'
2
+ require_relative '../ree_source_editor'
3
+
4
+ module RubyLsp
5
+ module Ree
6
+ class SyncDaoColumnsFormatter < BaseFormatter
7
+ include RubyLsp::Ree::ReeLspUtils
8
+
9
+ def call(source, uri)
10
+ path = get_uri_path(uri)
11
+ path_parts = path.split('/')
12
+ return source unless path_parts.include?('dao')
13
+
14
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source, type: :dao)
15
+ return source if !parsed_doc
16
+
17
+ parsed_doc.parse_class_includes
18
+ return source unless parsed_doc.includes_dao_dsl?
19
+ return source unless parsed_doc.has_schema?
20
+
21
+ source = deduplicate_dao_fields(parsed_doc, source)
22
+
23
+ dao_folder_index = path_parts.index('dao')
24
+ entities_folder = path_parts.take(dao_folder_index).join('/') + '/entities'
25
+
26
+ entity_filename = underscore(parsed_doc.schema_name) + '.rb'
27
+
28
+ entity_paths = Dir[File.join(entities_folder, '**', entity_filename)]
29
+ if entity_paths.size > 1
30
+ $stderr.puts("multiple entity paths for #{path}")
31
+ return source
32
+ elsif entity_paths.size == 0
33
+ $stderr.puts("no entity paths #{path}")
34
+ return source
35
+ end
36
+
37
+ entity_path = entity_paths.first
38
+ entity_source = File.read(entity_path)
39
+ entity_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(entity_source, type: :entity)
40
+
41
+ missed_column_names = parsed_doc.dao_fields.map(&:name) - entity_doc.columns.map(&:name)
42
+ missed_columns = parsed_doc.dao_fields.select{ missed_column_names.include?(_1.name) }
43
+ add_columns(entity_path, entity_source, entity_doc, missed_columns)
44
+
45
+ source
46
+ end
47
+
48
+ private
49
+
50
+ def add_columns(entity_path, entity_source, entity_doc, missed_columns)
51
+ return if !missed_columns || missed_columns.size == 0
52
+
53
+ columns_strs = missed_columns.map do |col|
54
+ str = " column :#{col.name}, #{col.type}"
55
+ if col.has_default?
56
+ str += ", default: #{col.default}"
57
+ end
58
+ str
59
+ end
60
+
61
+ columns_str = columns_strs.join("\n") + "\n"
62
+
63
+ source_lines = entity_source.lines
64
+
65
+ prev_line_location = if entity_doc.columns.size > 0
66
+ entity_doc.columns.last.location
67
+ else
68
+ entity_doc.build_dto_node.location
69
+ end
70
+
71
+ line = prev_line_location.start_line - 1
72
+
73
+ source_lines[line] += columns_str
74
+
75
+ File.write(entity_path, source_lines.join)
76
+ end
77
+
78
+ def deduplicate_dao_fields(parsed_doc, source)
79
+ grouped_fields = parsed_doc.dao_fields.group_by(&:name)
80
+ uniq_fields = []
81
+ fields_to_remove = []
82
+
83
+ grouped_fields.each do |name, fields|
84
+ uniq_fields << fields.first
85
+ if fields.size > 1
86
+ fields_to_remove += fields[1..-1]
87
+ end
88
+ end
89
+
90
+ return source if fields_to_remove.size == 0
91
+
92
+ parsed_doc.set_dao_fields(uniq_fields)
93
+ editor = RubyLsp::Ree::ReeSourceEditor.new(source)
94
+
95
+ fields_to_remove.each do |field|
96
+ editor.remove_dao_field(field)
97
+ end
98
+
99
+ editor.source
100
+ end
101
+ end
102
+ end
103
+ 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,100 @@
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
+ def set_dao_fields(fields)
36
+ @dao_fields = fields
37
+ end
38
+
39
+ private
40
+
41
+ def parse_dao_fields
42
+ @dao_fields = []
43
+ @schema_node = nil
44
+ return unless has_body?
45
+
46
+ @schema_node = class_node.body.body
47
+ .detect{ |node| node.is_a?(Prism::CallNode) && node.name == :schema }
48
+
49
+ return unless @schema_node
50
+
51
+ @dao_fields = @schema_node.block.body.body.map do |node|
52
+ if node.name.to_s == 'pg_jsonb'
53
+ field_type = "Nilor[Hash]"
54
+ default_val = "nil"
55
+ else
56
+ field_type = camelize(node.name.to_s)
57
+ default_val = nil
58
+
59
+ if field_allows_null?(node)
60
+ field_type = "Nilor[#{field_type}]"
61
+ default_val = "nil"
62
+ end
63
+ end
64
+
65
+ DaoField.new(
66
+ name: node.arguments.arguments.first.unescaped,
67
+ location: node.location,
68
+ type: field_type,
69
+ default: default_val
70
+ )
71
+ end
72
+ end
73
+
74
+ def parse_filters
75
+ return unless has_body?
76
+
77
+ @filters ||= class_node.body.body
78
+ .select{ node_name(_1) == :filter }
79
+ .map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped, signatures: parse_filter_signature(_1)) }
80
+ end
81
+
82
+ def parse_filter_signature(filter_node)
83
+ return [] unless filter_node
84
+
85
+ lambda_node = filter_node.arguments&.arguments[1]
86
+ return [] if !lambda_node || !lambda_node.parameters
87
+
88
+ parse_signatures_from_params(lambda_node.parameters.parameters)
89
+ end
90
+
91
+ def field_allows_null?(node)
92
+ kw_node = node.arguments.arguments.detect{ _1.is_a?(Prism::KeywordHashNode) }
93
+ return false unless kw_node
94
+
95
+ null_el = kw_node.elements.detect{ _1.key.unescaped == "null" }
96
+ return false unless null_el
97
+
98
+ null_el.value.is_a?(Prism::TrueNode)
99
+ end
100
+ 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
@@ -5,6 +5,7 @@ class RubyLsp::Ree::ParsedLinkNode
5
5
 
6
6
  FROM_ARG_KEY = 'from'
7
7
  IMPORT_ARG_KEY = 'import'
8
+ AS_ARG_KEY = 'as'
8
9
 
9
10
  class ImportItem
10
11
  attr_reader :name, :original_name
@@ -44,6 +45,11 @@ class RubyLsp::Ree::ParsedLinkNode
44
45
  @node.location
45
46
  end
46
47
 
48
+ def usage_name
49
+ return @alias_name if @alias_name
50
+ @name
51
+ end
52
+
47
53
  def from_arg_value
48
54
  return unless @from_param
49
55
 
@@ -75,17 +81,6 @@ class RubyLsp::Ree::ParsedLinkNode
75
81
  link_type == :object_name
76
82
  end
77
83
 
78
- def parse_name
79
- case name_arg_node
80
- when Prism::SymbolNode
81
- name_arg_node.value
82
- when Prism::StringNode
83
- name_arg_node.unescaped
84
- else
85
- ""
86
- end
87
- end
88
-
89
84
  def parse_imports
90
85
  @import_items ||= get_import_items
91
86
  end
@@ -123,12 +118,25 @@ class RubyLsp::Ree::ParsedLinkNode
123
118
 
124
119
  private
125
120
 
121
+ def parse_name
122
+ case name_arg_node
123
+ when Prism::SymbolNode
124
+ name_arg_node.value
125
+ when Prism::StringNode
126
+ name_arg_node.unescaped
127
+ else
128
+ ""
129
+ end
130
+ end
131
+
126
132
  def parse_params
127
133
  @kw_args = @node.arguments.arguments.detect{ |arg| arg.is_a?(Prism::KeywordHashNode) }
128
134
  @from_param = nil
129
135
  return unless @kw_args
130
136
 
131
137
  @from_param = @kw_args.elements.detect{ _1.key.unescaped == FROM_ARG_KEY }
138
+ @as_param = @kw_args.elements.detect{ _1.key.unescaped == AS_ARG_KEY }
139
+ @alias_name = @as_param ? @as_param.value.unescaped : nil
132
140
  end
133
141
 
134
142
  def last_arg
@@ -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
@@ -15,7 +15,7 @@ module RubyLsp
15
15
 
16
16
  def contains_link_usage?(link_node)
17
17
  source_lines_except_link = source_lines[0...(link_node.location.start_line-1)] + source_lines[(link_node.location.end_line)..-1]
18
- source_lines_except_link.any?{ |source_line| source_line.match?(/\W#{link_node.name}\W/)}
18
+ source_lines_except_link.any?{ |source_line| source_line.match?(/\W#{link_node.usage_name}\W/)}
19
19
  end
20
20
 
21
21
  def contains_link_import_usage?(link_node, link_import)
@@ -24,7 +24,7 @@ module RubyLsp
24
24
  end
25
25
 
26
26
  def remove_link(link_node)
27
- set_empty_lines!(link_node.location.start_line-1, link_node.location.end_line-1)
27
+ set_empty_lines_for_location!(link_node.location)
28
28
  end
29
29
 
30
30
  def remove_link_imports(link_node, link_imports)
@@ -58,10 +58,8 @@ module RubyLsp
58
58
  set_empty_lines!(link_container_start_line+1, link_container_end_line)
59
59
  end
60
60
 
61
- def set_empty_lines!(start_line, end_line)
62
- (start_line .. end_line).each do |i|
63
- source_lines[i] = ''
64
- end
61
+ def remove_dao_field(field)
62
+ set_empty_lines_for_location!(field.location)
65
63
  end
66
64
 
67
65
  def add_links(parsed_doc, ree_objects, current_package)
@@ -88,9 +86,16 @@ module RubyLsp
88
86
  new_text = "\sdo#{new_text}\s\send\n"
89
87
  end
90
88
 
91
- line = parsed_doc.links_container_node.location.start_line - 1
92
-
93
- source_lines[line] = source_lines[line].chomp + new_text
89
+ if parsed_doc.links_container_node
90
+ line = parsed_doc.links_container_node.location.start_line - 1
91
+ source_lines[line] = source_lines[line].chomp + new_text
92
+ elsif parsed_doc.link_nodes.size > 0
93
+ line = parsed_doc.link_nodes.last.location.end_line - 1
94
+ source_lines[line] = source_lines[line].chomp + new_text
95
+ else
96
+ line = parsed_doc.class_includes.last.location.end_line - 1
97
+ source_lines[line] = source_lines[line].chomp + "\n" + new_text
98
+ end
94
99
  end
95
100
 
96
101
  def change_link_package(link_node, new_package, current_package)
@@ -120,6 +125,18 @@ module RubyLsp
120
125
  source_lines[line] = source_lines[line][0..start_column] + ", from: :#{new_package}" + source_lines[line][start_column+1..-1]
121
126
  end
122
127
  end
128
+
129
+ private
130
+
131
+ def set_empty_lines_for_location!(location)
132
+ set_empty_lines!(location.start_line-1, location.end_line-1)
133
+ end
134
+
135
+ def set_empty_lines!(start_line, end_line)
136
+ (start_line .. end_line).each do |i|
137
+ source_lines[i] = ''
138
+ end
139
+ end
123
140
  end
124
141
  end
125
142
  end
@@ -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.21"
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.21
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-21 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