ruby-lsp-ree 0.1.7 → 0.1.8

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: 8d8bf3dfcc23d881b4bc1248851b63de1073b810186baeebe6d60894ea6c366a
4
- data.tar.gz: fe3e91cb35202109f9884bfb523d473b7d54921ae839299d4201b782ea6ee0ee
3
+ metadata.gz: ccc9c39cc6ca44feeb3c8c5369e259a790dc01c02a0aecf9a14eee2d19e65015
4
+ data.tar.gz: 61233b0b379f7ab994f102fc748e2ef54eed53e605017bb0d28793ea160a45d7
5
5
  SHA512:
6
- metadata.gz: 9fc76ea86567ce249f96af8a5d50732c9f23b1dd8e0f0a307ce82d7e74234cef0e908d3cd0ad644b6f5617ab4c3f450a26a9dfe707907a46e0009a12b920b206
7
- data.tar.gz: 507f4c378489292422e1560cfda1bfe6e3d4fc8cf8642f3f8786e0d75d389b0a9a4881b035da089ede1a8f90377f0016590441b740b5f207eae1c74b143b6f11
6
+ metadata.gz: 07cf2e0c6c6670f5405a967e7fffe9af19af8a29dc55d33afef3b9dd0f231df826a0cd11d4e5b91ad23b7348635107c0186a7bb8b878bce00d9a480710267608
7
+ data.tar.gz: 5a5bcdf420fe934e46ec0b0318c6325cd5c6a0753ca18ea73a8188f54dc19a1c7abd6b3e41a777fb4f0a8b6f6bf83c8fd015226ceb21ee54c8228264874f0aaf
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.1.8] - 2025-03-21
2
+
3
+ - hover: show missing locales
4
+ - formatter: add raised error definition if missing
5
+ - formatter: add placeholder for missing locale
6
+ - formatter: improved adding raised errors into contract throw section
7
+ - Go To Definition: improved location for error locales
8
+ - Go To Definition: got to missing locale placeholder
9
+
1
10
  ## [0.1.7] - 2025-03-14
2
11
 
3
12
  - Go To Definition for ree errors into locales
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-lsp-ree (0.1.7)
4
+ ruby-lsp-ree (0.1.8)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,13 @@
1
+ module RubyLsp
2
+ module Ree
3
+ class BaseFormatter
4
+ def self.call(source, uri)
5
+ new.call(source, uri)
6
+ end
7
+
8
+ def call(source, uri)
9
+ raise 'abstract method'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ require_relative 'base_formatter'
2
+
3
+ module RubyLsp
4
+ module Ree
5
+ class MissingErrorContractsFormatter < BaseFormatter
6
+ def call(source, _uri)
7
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
8
+ return source if !parsed_doc || !parsed_doc.class_node
9
+
10
+ parsed_doc.parse_error_definitions
11
+ parsed_doc.parse_instance_methods
12
+
13
+ parsed_doc.doc_instance_methods.select(&:has_contract?).each do |doc_instance_method|
14
+ doc_instance_method.parse_nested_local_methods(parsed_doc.doc_instance_methods)
15
+
16
+ raised_errors = doc_instance_method.raised_errors_nested
17
+ throws_errors = doc_instance_method.throws_errors
18
+
19
+ missed_errors = raised_errors - throws_errors
20
+ source = add_missed_errors(source, doc_instance_method, missed_errors)
21
+ end
22
+
23
+ source
24
+ end
25
+
26
+ private
27
+
28
+ def add_missed_errors(source, doc_instance_method, missed_errors)
29
+ return source if missed_errors.size == 0
30
+
31
+ source_lines = source.lines
32
+
33
+ if doc_instance_method.has_throw_section?
34
+ position = doc_instance_method.throw_arguments_end_position
35
+ line = doc_instance_method.throw_arguments_end_line
36
+
37
+ if source_lines[line].strip.end_with?(")")
38
+ source_lines[line] = source_lines[line][0..position] + ", #{missed_errors.join(', ')})\n"
39
+ else
40
+ source_lines[line] = source_lines[line][0..position] + ", #{missed_errors.join(', ')}\n"
41
+ end
42
+ else
43
+ position = doc_instance_method.contract_node_end_position
44
+ line = doc_instance_method.contract_node_end_line
45
+
46
+ source_lines[line] = source_lines[line][0..position] + ".throws(#{missed_errors.join(', ')})\n"
47
+ end
48
+
49
+ source_lines.join
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,54 @@
1
+ require_relative 'base_formatter'
2
+
3
+ module RubyLsp
4
+ module Ree
5
+ class MissingErrorDefinitionsFormatter < BaseFormatter
6
+ include RubyLsp::Ree::ReeLspUtils
7
+
8
+ def call(source, _uri)
9
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
10
+ return source if !parsed_doc || !parsed_doc.class_node
11
+
12
+ parsed_doc.parse_error_definitions
13
+ parsed_doc.parse_instance_methods
14
+ parsed_doc.parse_links
15
+
16
+ existing_errors = parsed_doc.error_definition_names + parsed_doc.imported_constants
17
+
18
+ missed_errors = []
19
+ parsed_doc.doc_instance_methods.each do |doc_instance_method|
20
+ doc_instance_method.parse_nested_local_methods(parsed_doc.doc_instance_methods)
21
+
22
+ raised_errors = doc_instance_method.raised_errors_nested
23
+
24
+ missed_errors += raised_errors - existing_errors
25
+ end
26
+
27
+ missed_errors = missed_errors.uniq.reject{ Object.const_defined?(_1) }
28
+
29
+ add_missed_error_definitions(source, parsed_doc, missed_errors.uniq)
30
+ end
31
+
32
+ private
33
+
34
+ def add_missed_error_definitions(source, parsed_doc, missed_errors)
35
+ return source if missed_errors.size == 0
36
+
37
+ source_lines = source.lines
38
+
39
+ if parsed_doc.error_definitions.size > 0
40
+ change_line = parsed_doc.error_definitions.map{ _1.location.start_line }.max - 1
41
+ else
42
+ change_line = parsed_doc.links_container_node.location.end_line - 1
43
+ source_lines[change_line] += "\n"
44
+ end
45
+
46
+ missed_errors.each do |err|
47
+ source_lines[change_line] += "\s\s#{err} = invalid_param_error(:#{underscore(err)})\n"
48
+ end
49
+
50
+ source_lines.join
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,84 @@
1
+ require_relative 'base_formatter'
2
+
3
+ module RubyLsp
4
+ module Ree
5
+ class MissingErrorLocalesFormatter < BaseFormatter
6
+ include RubyLsp::Ree::ReeLspUtils
7
+ include RubyLsp::Ree::ReeLocaleUtils
8
+
9
+ MISSING_LOCALE_PLACEHOLDER = '_MISSING_LOCALE_'
10
+
11
+ def call(source, uri)
12
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
13
+
14
+ locales_folder = package_locales_folder_path(URI.parse(uri.to_s).path)
15
+ return source if !locales_folder || !File.directory?(locales_folder)
16
+
17
+ result = []
18
+ key_paths = []
19
+ parsed_doc.parse_error_definitions
20
+ parsed_doc.error_definitions.each do |error_definition|
21
+ key_path = if error_definition.value.arguments.arguments.size > 1
22
+ error_definition.value.arguments.arguments[1].unescaped
23
+ else
24
+ mod = underscore(parsed_doc.module_name)
25
+ "#{mod}.errors.#{error_definition.value.arguments.arguments[0].unescaped}"
26
+ end
27
+
28
+ key_paths << key_path
29
+ end
30
+
31
+ Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
32
+ key_paths.each do |key_path|
33
+ value = find_locale_value(locale_file, key_path)
34
+ unless value
35
+ loc_key = File.basename(locale_file, '.yml')
36
+
37
+ add_locale_placeholder(locale_file, key_path)
38
+ end
39
+ end
40
+ end
41
+
42
+ source
43
+ end
44
+
45
+ private
46
+
47
+ def add_locale_placeholder(file_path, key_path)
48
+ loc_key = File.basename(file_path, '.yml')
49
+ key_parts = [loc_key] + key_path.split('.')
50
+
51
+ last_found_index = 0
52
+ last_found_key = nil
53
+ current_node = YamlFileParser.parse_with_key_coordinates(file_path)
54
+
55
+ key_parts.each_with_index do |key_part, index|
56
+ found_key, next_node = YamlFileParser.find_key_in_node(current_node, key_part)
57
+
58
+ break unless found_key
59
+
60
+ current_node = next_node
61
+ last_found_index = index
62
+ last_found_key = found_key
63
+ end
64
+
65
+ missed_key_parts = key_parts[last_found_index+1..-1]
66
+
67
+ identation = last_found_key.column
68
+ adding_string = ''
69
+ missed_key_parts.each_with_index do |key_part, index|
70
+ identation += 2
71
+ if index == missed_key_parts.size - 1
72
+ adding_string += "\s" * identation + "#{key_part}: #{MISSING_LOCALE_PLACEHOLDER}\n"
73
+ else
74
+ adding_string += "\s" * identation + "#{key_part}:\n"
75
+ end
76
+ end
77
+
78
+ lines = File.read(file_path).lines
79
+ lines[last_found_key.line] += adding_string
80
+ File.write(file_path, lines.join)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,51 @@
1
+ require_relative 'base_formatter'
2
+
3
+ module RubyLsp
4
+ module Ree
5
+ class SortLinksFormatter < BaseFormatter
6
+ def call(source, _uri)
7
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
8
+ return source if !parsed_doc || !parsed_doc.link_nodes&.any?
9
+
10
+ if parsed_doc.link_nodes.any?{ _1.location.start_line != _1.location.end_line }
11
+ $stderr.puts("multiline link definitions, don't sort")
12
+ return source
13
+ end
14
+
15
+ # sort link nodes
16
+ sorted_link_nodes = parsed_doc.link_nodes.sort{ |a, b|
17
+ a_name = a.node.arguments.arguments.first
18
+ b_name = b.node.arguments.arguments.first
19
+
20
+ if a_name.is_a?(Prism::SymbolNode) && !b_name.is_a?(Prism::SymbolNode)
21
+ -1
22
+ elsif b_name.is_a?(Prism::SymbolNode) && !a_name.is_a?(Prism::SymbolNode)
23
+ 1
24
+ else
25
+ a_name.unescaped <=> b_name.unescaped
26
+ end
27
+ }
28
+
29
+ # check if no re-order
30
+ if parsed_doc.link_nodes.map{ _1.node.arguments.arguments.first.unescaped } == sorted_link_nodes.map{ _1.node.arguments.arguments.first.unescaped }
31
+ return source
32
+ end
33
+
34
+ # insert nodes to source
35
+ link_lines = parsed_doc.link_nodes.map{ _1.location.start_line }
36
+
37
+ source_lines = source.lines
38
+
39
+ sorted_lines = sorted_link_nodes.map do |sorted_link|
40
+ source_lines[sorted_link.location.start_line - 1]
41
+ end
42
+
43
+ link_lines.each_with_index do |link_line, index|
44
+ source_lines[link_line - 1] = sorted_lines[index]
45
+ end
46
+
47
+ source_lines.join()
48
+ end
49
+ end
50
+ end
51
+ end
@@ -148,13 +148,13 @@ module RubyLsp
148
148
  key_path = node.unescaped
149
149
 
150
150
  Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
151
- line = find_locale_key_line(locale_file, key_path)
151
+ location = find_locale_key_location(locale_file, key_path)
152
152
 
153
153
  result << Interface::Location.new(
154
154
  uri: locale_file,
155
155
  range: Interface::Range.new(
156
- start: Interface::Position.new(line: line, character: 0),
157
- end: Interface::Position.new(line: line, character: 0),
156
+ start: Interface::Position.new(line: location.line, character: location.column),
157
+ end: Interface::Position.new(line: location.line, character: location.column),
158
158
  ),
159
159
  )
160
160
  end
@@ -164,7 +164,6 @@ module RubyLsp
164
164
 
165
165
  def get_error_code_definition_items(node)
166
166
  locales_folder = package_locales_folder_path(@uri.path)
167
-
168
167
  return [] unless File.directory?(locales_folder)
169
168
 
170
169
  result = []
@@ -179,13 +178,13 @@ module RubyLsp
179
178
  end
180
179
 
181
180
  Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
182
- line = find_locale_key_line(locale_file, key_path)
181
+ location = find_locale_key_location(locale_file, key_path)
183
182
 
184
183
  result << Interface::Location.new(
185
184
  uri: locale_file,
186
185
  range: Interface::Range.new(
187
- start: Interface::Position.new(line: line, character: 0),
188
- end: Interface::Position.new(line: line, character: 0),
186
+ start: Interface::Position.new(line: location.line, character: location.column),
187
+ end: Interface::Position.new(line: location.line, character: location.column),
189
188
  ),
190
189
  )
191
190
  end
@@ -11,6 +11,8 @@ module RubyLsp
11
11
  include RubyLsp::Ree::ReeLspUtils
12
12
  include RubyLsp::Ree::ReeLocaleUtils
13
13
 
14
+ MISSING_LOCALE_PLACEHOLDER = '_MISSING_LOCALE_'
15
+
14
16
  def initialize(index, node_context)
15
17
  @index = index
16
18
  @node_context = node_context
@@ -79,10 +81,18 @@ module RubyLsp
79
81
 
80
82
  Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
81
83
  value = find_locale_value(locale_file, key_path)
84
+ loc_key = File.basename(locale_file, '.yml')
82
85
 
83
86
  if value
84
- loc_key = File.basename(locale_file, '.yml')
85
- documentation += "#{loc_key}: #{value}\n\n"
87
+ if value == MISSING_LOCALE_PLACEHOLDER
88
+ value_location = find_locale_key_location(locale_file, key_path)
89
+ file_uri = "#{locale_file}" # TODO add line to uri :#{value_location.line+1}:#{value_location.column}"
90
+ documentation += "#{loc_key}: [#{value}](#{file_uri})\n\n"
91
+ else
92
+ documentation += "#{loc_key}: #{value}\n\n"
93
+ end
94
+ else
95
+ documentation += "#{loc_key}: [MISSING TRANSLATION](#{locale_file})\n\n"
86
96
  end
87
97
  end
88
98
 
@@ -110,9 +120,18 @@ module RubyLsp
110
120
  Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
111
121
  value = find_locale_value(locale_file, key_path)
112
122
 
123
+ loc_key = File.basename(locale_file, '.yml')
124
+
113
125
  if value
114
- loc_key = File.basename(locale_file, '.yml')
115
- documentation += "#{loc_key}: #{value}\n\n"
126
+ if value == MISSING_LOCALE_PLACEHOLDER
127
+ value_location = find_locale_key_location(locale_file, key_path)
128
+ file_uri = "#{locale_file}" # TODO add line to uri :#{value_location.line+1}:#{value_location.column}"
129
+ documentation += "#{loc_key}: [#{value}](#{file_uri})\n\n"
130
+ else
131
+ documentation += "#{loc_key}: #{value}\n\n"
132
+ end
133
+ else
134
+ documentation += "#{loc_key}: [MISSING TRANSLATION](#{locale_file})\n\n"
116
135
  end
117
136
  end
118
137
 
@@ -26,7 +26,7 @@ class RubyLsp::Ree::ParsedDocument
26
26
  attr_reader :ast, :package_name, :class_node, :fn_node, :class_includes,
27
27
  :link_nodes, :values, :action_node, :dao_node, :filters,
28
28
  :bean_node, :bean_methods, :mapper_node, :links_container_block_node, :aggregate_node,
29
- :error_definitions, :doc_instance_methods
29
+ :error_definitions, :error_definition_names, :doc_instance_methods
30
30
 
31
31
  def initialize(ast)
32
32
  @ast = ast
@@ -218,6 +218,8 @@ class RubyLsp::Ree::ParsedDocument
218
218
  @error_definitions = class_node.body.body
219
219
  .select{ _1.is_a?(Prism::ConstantWriteNode) }
220
220
  .select{ ERROR_DEFINITION_NAMES.include?(node_name(_1.value)) }
221
+
222
+ @error_definition_names = @error_definitions.map(&:name)
221
223
  end
222
224
 
223
225
  def class_name
@@ -242,4 +244,8 @@ class RubyLsp::Ree::ParsedDocument
242
244
 
243
245
  node.name
244
246
  end
247
+
248
+ def imported_constants
249
+ @link_nodes.map(&:imports).flatten.map(&:to_sym)
250
+ end
245
251
  end
@@ -24,42 +24,43 @@ class RubyLsp::Ree::ParsedMethodNode
24
24
  @method_node.location.end_line - 1
25
25
  end
26
26
 
27
- def raised_errors_nested(source, error_definitions)
28
- return [] if error_definitions.size == 0
27
+ def raised_errors_nested
28
+ return @raised_errors_nested if @raised_errors_nested
29
+ raised = raised_errors
29
30
 
30
- raised = raised_errors(source, error_definitions)
31
-
32
- not_detected_errors = error_definitions.select{ !raised.include?(_1.name.to_s) }
33
31
  @nested_local_methods.each do |nested_method|
34
- raised += nested_method.raised_errors_nested(source, not_detected_errors)
35
- not_detected_errors = error_definitions.select{ !raised.include?(_1.name.to_s) }
32
+ raised += nested_method.raised_errors_nested
36
33
  end
37
34
 
38
- raised
35
+ @raised_errors_nested = raised
39
36
  end
40
37
 
41
- def raised_errors(source, error_definitions)
42
- raised = []
43
- error_names = error_definitions.map(&:name).map(&:to_s)
38
+ def raised_errors
39
+ return @raised_errors if @raised_errors
40
+ return [] unless @method_node.body
44
41
 
45
- source.lines[start_line+1 .. end_line-1].each do |line|
46
- error_names.each do |error_name|
47
- regex = /\braise #{Regexp.escape(error_name)}\b/
42
+ call_objects = parse_body_call_objects(@method_node.body.body)
43
+ raise_objects = call_objects.select{ _1.name == :raise }
44
+ @raised_errors = raise_objects.map{ parse_raised_class_name(_1) }.compact
45
+ end
48
46
 
49
- if line.match?(regex)
50
- raised << error_name
51
- end
52
- end
53
- end
47
+ def parse_raised_class_name(raise_node)
48
+ return unless raise_node.arguments
54
49
 
55
- raised.uniq
50
+ if raise_node.arguments.arguments.first.is_a?(Prism::ConstantReadNode)
51
+ raise_node.arguments.arguments.first.name
52
+ elsif raise_node.arguments.arguments.first.is_a?(Prism::CallNode)
53
+ raise_node.arguments.arguments.first.receiver.name
54
+ else
55
+ nil
56
+ end
56
57
  end
57
58
 
58
59
  def throws_errors
59
60
  return [] unless has_contract?
60
61
  return [] unless has_throw_section?
61
62
 
62
- @contract_node.arguments.arguments.map{ _1.name.to_s }
63
+ @contract_node.arguments.arguments.map{ _1.name }
63
64
  end
64
65
 
65
66
  def has_throw_section?
@@ -83,6 +84,11 @@ class RubyLsp::Ree::ParsedMethodNode
83
84
  end
84
85
 
85
86
  def parse_nested_local_methods(local_methods)
87
+ unless @method_node.body
88
+ @nested_local_methods = []
89
+ return
90
+ end
91
+
86
92
  local_method_names = local_methods.map(&:name)
87
93
  call_nodes = parse_body_call_objects(@method_node.body.body)
88
94
  call_node_names = call_nodes.map(&:name)
@@ -1,106 +1,84 @@
1
+ require_relative 'formatters/sort_links_formatter'
2
+ require_relative 'formatters/missing_error_definitions_formatter'
3
+ require_relative 'formatters/missing_error_contracts_formatter'
4
+ require_relative 'formatters/missing_error_locales_formatter'
5
+
1
6
  module RubyLsp
2
7
  module Ree
3
8
  class ReeFormatter
4
9
  include RubyLsp::Requests::Support::Formatter
5
10
  include RubyLsp::Ree::ReeLspUtils
6
-
7
- def initialize
8
- end
11
+ include RubyLsp::Ree::ReeLocaleUtils
9
12
 
10
13
  def run_formatting(uri, document)
11
14
  source = document.source
12
-
13
- sorted_source = sort_links(source)
14
- add_missing_error_contracts(sorted_source)
15
+
16
+ formatters = [
17
+ RubyLsp::Ree::SortLinksFormatter,
18
+ RubyLsp::Ree::MissingErrorDefinitionsFormatter,
19
+ RubyLsp::Ree::MissingErrorContractsFormatter,
20
+ RubyLsp::Ree::MissingErrorLocalesFormatter
21
+ ]
22
+
23
+ formatters.reduce(source){ |s, formatter| formatter.call(s, uri) }
15
24
  rescue => e
16
25
  $stderr.puts("error in ree_formatter: #{e.message} : #{e.backtrace.first}")
17
26
  end
18
27
 
28
+ def run_diagnostic(uri, document)
29
+ $stderr.puts("ree_formatter_diagnostic")
30
+ detect_missing_error_locales(uri, document)
31
+ rescue => e
32
+ $stderr.puts("error in ree_formatter_diagnostic: #{e.message} : #{e.backtrace.first}")
33
+ end
34
+
19
35
  private
20
36
 
21
- def sort_links(source)
22
- parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
23
- return source if !parsed_doc.link_nodes&.any?
37
+ def detect_missing_error_locales(uri, document)
38
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(document.source)
24
39
 
25
- if parsed_doc.link_nodes.any?{ _1.location.start_line != _1.location.end_line }
26
- $stderr.puts("multiline link definitions, don't sort")
27
- return source
28
- end
40
+ locales_folder = package_locales_folder_path(uri.path)
41
+ return [] unless File.directory?(locales_folder)
29
42
 
30
- # sort link nodes
31
- sorted_link_nodes = parsed_doc.link_nodes.sort{ |a, b|
32
- a_name = a.node.arguments.arguments.first
33
- b_name = b.node.arguments.arguments.first
34
-
35
- if a_name.is_a?(Prism::SymbolNode) && !b_name.is_a?(Prism::SymbolNode)
36
- -1
37
- elsif b_name.is_a?(Prism::SymbolNode) && !a_name.is_a?(Prism::SymbolNode)
38
- 1
43
+ result = []
44
+ key_paths = []
45
+ parsed_doc.parse_error_definitions
46
+ parsed_doc.error_definitions.each do |error_definition|
47
+ key_path = if error_definition.value.arguments.arguments.size > 1
48
+ error_definition.value.arguments.arguments[1].unescaped
39
49
  else
40
- a_name.unescaped <=> b_name.unescaped
50
+ mod = underscore(parsed_doc.module_name)
51
+ "#{mod}.errors.#{error_definition.value.arguments.arguments[0].unescaped}"
41
52
  end
42
- }
43
-
44
- # check if no re-order
45
- if parsed_doc.link_nodes.map{ _1.node.arguments.arguments.first.unescaped } == sorted_link_nodes.map{ _1.node.arguments.arguments.first.unescaped }
46
- return source
47
- end
48
-
49
- # insert nodes to source
50
- link_lines = parsed_doc.link_nodes.map{ _1.location.start_line }
51
-
52
- source_lines = source.lines
53
-
54
- sorted_lines = sorted_link_nodes.map do |sorted_link|
55
- source_lines[sorted_link.location.start_line - 1]
56
- end
57
53
 
58
- link_lines.each_with_index do |link_line, index|
59
- source_lines[link_line - 1] = sorted_lines[index]
54
+ key_paths << key_path
60
55
  end
61
56
 
62
- source_lines.join()
63
- end
64
-
65
- def add_missing_error_contracts(source)
66
- parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
67
- return source if !parsed_doc || !parsed_doc.class_node
68
-
69
- parsed_doc.parse_error_definitions
70
- parsed_doc.parse_instance_methods
71
-
72
- parsed_doc.doc_instance_methods.select(&:has_contract?).each do |doc_instance_method|
73
- doc_instance_method.parse_nested_local_methods(parsed_doc.doc_instance_methods)
74
-
75
- raised_errors = doc_instance_method.raised_errors_nested(source, parsed_doc.error_definitions)
76
- throws_errors = doc_instance_method.throws_errors
77
-
78
- missed_errors = raised_errors - throws_errors
79
- source = add_missed_errors(source, doc_instance_method, missed_errors)
80
- end
81
-
82
- source
83
- end
84
-
85
- def add_missed_errors(source, doc_instance_method, missed_errors)
86
- return source if missed_errors.size == 0
87
-
88
- source_lines = source.lines
89
-
90
- if doc_instance_method.has_throw_section?
91
- position = doc_instance_method.throw_arguments_end_position
92
- line = doc_instance_method.throw_arguments_end_line
93
-
94
- source_lines[line] = source_lines[line][0..position] + ", #{missed_errors.join(', ')})\n"
95
- else
96
- position = doc_instance_method.contract_node_end_position
97
- line = doc_instance_method.contract_node_end_line
98
-
99
- source_lines[line] = source_lines[line][0..position] + ".throws(#{missed_errors.join(', ')})\n"
57
+ $stderr.puts("ree_formatter_diagnostic #{key_paths}")
58
+
59
+ Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
60
+ key_paths.each do |key_path|
61
+ value = find_locale_value(locale_file, key_path)
62
+ unless value
63
+ loc_key = File.basename(locale_file, '.yml')
64
+
65
+ $stderr.puts("ree_formatter_diagnostic add diagnostic")
66
+
67
+ # TODO correct error range
68
+ result << RubyLsp::Interface::Diagnostic.new(
69
+ message: "Missing locale #{loc_key}: #{key_path}",
70
+ source: "Ree formatter",
71
+ severity: RubyLsp::Constant::DiagnosticSeverity::ERROR,
72
+ range: RubyLsp::Interface::Range.new(
73
+ start: RubyLsp::Interface::Position.new(line: 0, character: 0),
74
+ end: RubyLsp::Interface::Position.new(line: 0, character: 0),
75
+ ),
76
+ )
77
+ end
78
+ end
100
79
  end
101
80
 
102
-
103
- source_lines.join()
81
+ result
104
82
  end
105
83
  end
106
84
  end
@@ -1,4 +1,5 @@
1
1
  require 'yaml'
2
+ require_relative 'yaml_file_parser'
2
3
 
3
4
  module RubyLsp
4
5
  module Ree
@@ -21,27 +22,19 @@ module RubyLsp
21
22
  loc_yaml.dig(*key_parts)
22
23
  end
23
24
 
24
- def find_locale_key_line(file_path, key_path)
25
+ def find_locale_key_location(file_path, key_path)
25
26
  loc_key = File.basename(file_path, '.yml')
26
27
 
27
28
  key_parts = [loc_key] + key_path.split('.')
29
+ parsed_yaml = RubyLsp::Ree::YamlFileParser.parse(file_path)
30
+ key_location = parsed_yaml.dig(*key_parts)
28
31
 
29
- current_key_index = 0
30
- current_key = key_parts[current_key_index]
31
- regex = /^\s*#{Regexp.escape(current_key)}:/
32
-
33
- File.open(file_path, 'r:UTF-8').each_with_index do |line, line_index|
34
- if line.match?(regex)
35
- current_key_index += 1
36
- current_key = key_parts[current_key_index]
37
- return line_index unless current_key
38
-
39
- regex = /^\s*#{Regexp.escape(current_key)}:/
40
- end
32
+ if key_location
33
+ OpenStruct.new(line: key_location.line, column: key_location.column)
34
+ else
35
+ OpenStruct.new(line: 0, column: 0)
41
36
  end
42
-
43
- 0
44
37
  end
45
38
  end
46
39
  end
47
- end
40
+ end
@@ -0,0 +1,53 @@
1
+ require 'psych'
2
+
3
+ module RubyLsp
4
+ module Ree
5
+ class NodeVisitor < Psych::Visitors::ToRuby
6
+ def visit_Psych_Nodes_Scalar(o)
7
+ register(o, OpenStruct.new(value: deserialize(o), line: o.start_line, column: o.start_column))
8
+ end
9
+ end
10
+
11
+ class YamlFileParser
12
+ def self.parse(file_path)
13
+ parser = Psych::Parser.new(Psych::TreeBuilder.new)
14
+ parser.parse(File.read(file_path))
15
+
16
+ parse_result = NodeVisitor.create.accept(parser.handler.root)
17
+ normalize_hash_keys(parse_result.first)
18
+ end
19
+
20
+ def self.parse_with_key_coordinates(file_path)
21
+ parser = Psych::Parser.new(Psych::TreeBuilder.new)
22
+ parser.parse(File.read(file_path))
23
+
24
+ NodeVisitor.create.accept(parser.handler.root).first
25
+ end
26
+
27
+ def self.find_key_in_node(current_node, key)
28
+ matched_key_el = current_node.detect{ _1[0].value == key }
29
+ return [nil, nil] unless matched_key_el
30
+ matched_key_el
31
+ end
32
+
33
+ def self.normalize_hash_keys(res)
34
+ deep_transform_keys_in_object!(res){ |k| k.value }
35
+ end
36
+
37
+ def self.deep_transform_keys_in_object!(object, &block)
38
+ case object
39
+ when Hash
40
+ object.keys.each do |key|
41
+ value = object.delete(key)
42
+ object[yield(key)] = deep_transform_keys_in_object!(value, &block)
43
+ end
44
+ object
45
+ when Array
46
+ object.map! { |e| deep_transform_keys_in_object!(e, &block) }
47
+ else
48
+ object
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLsp
4
4
  module Ree
5
- VERSION = "0.1.7"
5
+ VERSION = "0.1.8"
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.7
4
+ version: 0.1.8
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-03-20 00:00:00.000000000 Z
11
+ date: 2025-03-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:
@@ -26,6 +26,11 @@ files:
26
26
  - README.md
27
27
  - Rakefile
28
28
  - lib/ruby_lsp/ruby_lsp_ree/addon.rb
29
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/base_formatter.rb
30
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_contracts_formatter.rb
31
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_definitions_formatter.rb
32
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_locales_formatter.rb
33
+ - lib/ruby_lsp/ruby_lsp_ree/formatters/sort_links_formatter.rb
29
34
  - lib/ruby_lsp/ruby_lsp_ree/handlers/completion_handler.rb
30
35
  - lib/ruby_lsp/ruby_lsp_ree/handlers/definition_handler.rb
31
36
  - lib/ruby_lsp/ruby_lsp_ree/handlers/hover_handler.rb
@@ -45,6 +50,7 @@ files:
45
50
  - lib/ruby_lsp/ruby_lsp_ree/ree_template_applicator.rb
46
51
  - lib/ruby_lsp/ruby_lsp_ree/utils/ree_locale_utils.rb
47
52
  - lib/ruby_lsp/ruby_lsp_ree/utils/ree_lsp_utils.rb
53
+ - lib/ruby_lsp/ruby_lsp_ree/utils/yaml_file_parser.rb
48
54
  - lib/ruby_lsp_ree.rb
49
55
  - lib/ruby_lsp_ree/version.rb
50
56
  - ruby-lsp-ree.gemspec