ruby-lsp-ree 0.1.7 → 0.1.9
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/lib/ruby_lsp/ruby_lsp_ree/formatters/base_formatter.rb +13 -0
- data/lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_contracts_formatter.rb +53 -0
- data/lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_definitions_formatter.rb +54 -0
- data/lib/ruby_lsp/ruby_lsp_ree/formatters/missing_error_locales_formatter.rb +84 -0
- data/lib/ruby_lsp/ruby_lsp_ree/formatters/sort_links_formatter.rb +51 -0
- data/lib/ruby_lsp/ruby_lsp_ree/handlers/definition_handler.rb +6 -7
- data/lib/ruby_lsp/ruby_lsp_ree/handlers/hover_handler.rb +23 -4
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document.rb +9 -2
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb +8 -12
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_method_node.rb +27 -21
- data/lib/ruby_lsp/ruby_lsp_ree/ree_formatter.rb +58 -80
- data/lib/ruby_lsp/ruby_lsp_ree/utils/ree_locale_utils.rb +9 -16
- data/lib/ruby_lsp/ruby_lsp_ree/utils/yaml_file_parser.rb +53 -0
- data/lib/ruby_lsp_ree/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2340c5046b16e260fd66ff9696e36c4e04db071520018f683e59e4ddcaef34f
|
4
|
+
data.tar.gz: b3a89234d7a4627371ec1e8514970ad00ac540b5b4b6b9cdbd046a2729aa9fee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7e8650725c0188ec521e53188cfdd68679d5d73a392ce624b9f89c3f2daf683497e2558e932daed14cf652d25140a4bca891c3c9dbb536cfecae9cb92d72912
|
7
|
+
data.tar.gz: c8e71b80054434730dbc10bdb85cb82e5277f2582a00879688e25e32078e239620f0e481b0eacac4619acca9ef606933a9fd820c87bf7a075619b506b5ef0a1c
|
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
@@ -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
|
-
|
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:
|
157
|
-
end: Interface::Position.new(line: line, character:
|
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
|
-
|
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:
|
188
|
-
end: Interface::Position.new(line: line, character:
|
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
|
-
|
85
|
-
|
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
|
-
|
115
|
-
|
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,10 +26,11 @@ 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
|
-
def initialize(ast)
|
31
|
+
def initialize(ast, package_name = nil)
|
32
32
|
@ast = ast
|
33
|
+
set_package_name(package_name) if package_name
|
33
34
|
end
|
34
35
|
|
35
36
|
def links_container_node
|
@@ -218,6 +219,8 @@ class RubyLsp::Ree::ParsedDocument
|
|
218
219
|
@error_definitions = class_node.body.body
|
219
220
|
.select{ _1.is_a?(Prism::ConstantWriteNode) }
|
220
221
|
.select{ ERROR_DEFINITION_NAMES.include?(node_name(_1.value)) }
|
222
|
+
|
223
|
+
@error_definition_names = @error_definitions.map(&:name)
|
221
224
|
end
|
222
225
|
|
223
226
|
def class_name
|
@@ -242,4 +245,8 @@ class RubyLsp::Ree::ParsedDocument
|
|
242
245
|
|
243
246
|
node.name
|
244
247
|
end
|
248
|
+
|
249
|
+
def imported_constants
|
250
|
+
@link_nodes.map(&:imports).flatten.map(&:to_sym)
|
251
|
+
end
|
245
252
|
end
|
@@ -7,20 +7,16 @@ class RubyLsp::Ree::ParsedDocumentBuilder
|
|
7
7
|
|
8
8
|
def self.build_from_uri(uri, type = nil)
|
9
9
|
ast = Prism.parse_file(uri.path).value
|
10
|
-
document = build_document(ast, type)
|
10
|
+
document = build_document(ast, type, package_name_from_uri(uri))
|
11
11
|
return unless document
|
12
12
|
|
13
|
-
document.set_package_name(package_name_from_uri(uri))
|
14
|
-
|
15
13
|
document
|
16
14
|
end
|
17
15
|
|
18
16
|
def self.build_from_ast(ast, uri, type = nil)
|
19
|
-
document = build_document(ast, type)
|
17
|
+
document = build_document(ast, type, package_name_from_uri(uri))
|
20
18
|
return unless document
|
21
19
|
|
22
|
-
document.set_package_name(package_name_from_uri(uri))
|
23
|
-
|
24
20
|
document
|
25
21
|
end
|
26
22
|
|
@@ -29,7 +25,7 @@ class RubyLsp::Ree::ParsedDocumentBuilder
|
|
29
25
|
build_document(ast, type)
|
30
26
|
end
|
31
27
|
|
32
|
-
def self.build_document(ast, type)
|
28
|
+
def self.build_document(ast, type, package_name = nil)
|
33
29
|
case type
|
34
30
|
when :enum
|
35
31
|
build_enum_document(ast)
|
@@ -38,13 +34,13 @@ class RubyLsp::Ree::ParsedDocumentBuilder
|
|
38
34
|
when :bean
|
39
35
|
build_bean_document(ast)
|
40
36
|
else
|
41
|
-
build_detected_document_type(ast)
|
37
|
+
build_detected_document_type(ast, package_name)
|
42
38
|
end
|
43
39
|
end
|
44
40
|
|
45
|
-
def self.build_detected_document_type(ast)
|
41
|
+
def self.build_detected_document_type(ast, package_name = nil)
|
46
42
|
if has_root_class?(ast)
|
47
|
-
build_regular_document(ast)
|
43
|
+
build_regular_document(ast, package_name)
|
48
44
|
elsif has_root_rspec_call?(ast)
|
49
45
|
build_rspec_document(ast)
|
50
46
|
else
|
@@ -69,8 +65,8 @@ class RubyLsp::Ree::ParsedDocumentBuilder
|
|
69
65
|
document
|
70
66
|
end
|
71
67
|
|
72
|
-
def self.build_regular_document(ast)
|
73
|
-
document = RubyLsp::Ree::ParsedDocument.new(ast)
|
68
|
+
def self.build_regular_document(ast, package_name)
|
69
|
+
document = RubyLsp::Ree::ParsedDocument.new(ast, package_name)
|
74
70
|
|
75
71
|
document.parse_class_node
|
76
72
|
document.parse_fn_node
|
@@ -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
|
28
|
-
return
|
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
|
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
|
42
|
-
|
43
|
-
|
38
|
+
def raised_errors
|
39
|
+
return @raised_errors if @raised_errors
|
40
|
+
return [] unless @method_node.body
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
47
|
+
def parse_raised_class_name(raise_node)
|
48
|
+
return unless raise_node.arguments
|
54
49
|
|
55
|
-
|
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
|
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
|
-
|
14
|
-
|
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
|
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
|
-
|
26
|
-
|
27
|
-
return source
|
28
|
-
end
|
40
|
+
locales_folder = package_locales_folder_path(uri.path)
|
41
|
+
return [] unless File.directory?(locales_folder)
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
-
|
59
|
-
source_lines[link_line - 1] = sorted_lines[index]
|
54
|
+
key_paths << key_path
|
60
55
|
end
|
61
56
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|
data/lib/ruby_lsp_ree/version.rb
CHANGED
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.
|
4
|
+
version: 0.1.9
|
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-
|
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
|