ruby-lsp-ree 0.1.5 → 0.1.7
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 +12 -0
- data/Gemfile.lock +1 -1
- data/lib/ruby_lsp/ruby_lsp_ree/addon.rb +11 -6
- data/lib/ruby_lsp/ruby_lsp_ree/handlers/completion_handler.rb +1 -1
- data/lib/ruby_lsp/ruby_lsp_ree/handlers/definition_handler.rb +66 -1
- data/lib/ruby_lsp/ruby_lsp_ree/handlers/hover_handler.rb +86 -5
- data/lib/ruby_lsp/ruby_lsp_ree/listeners/definition_listener.rb +14 -2
- data/lib/ruby_lsp/ruby_lsp_ree/listeners/hover_listener.rb +30 -2
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document.rb +59 -2
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb +3 -3
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_method_node.rb +109 -0
- data/lib/ruby_lsp/ruby_lsp_ree/ree_context.rb +28 -0
- data/lib/ruby_lsp/ruby_lsp_ree/ree_formatter.rb +44 -1
- data/lib/ruby_lsp/ruby_lsp_ree/ree_indexing_enhancement.rb +30 -6
- data/lib/ruby_lsp/ruby_lsp_ree/ree_object_finder.rb +15 -0
- data/lib/ruby_lsp/ruby_lsp_ree/ree_rename_handler.rb +39 -0
- data/lib/ruby_lsp/ruby_lsp_ree/utils/ree_locale_utils.rb +47 -0
- data/lib/ruby_lsp/ruby_lsp_ree/utils/ree_lsp_utils.rb +12 -1
- data/lib/ruby_lsp_ree/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d8bf3dfcc23d881b4bc1248851b63de1073b810186baeebe6d60894ea6c366a
|
4
|
+
data.tar.gz: fe3e91cb35202109f9884bfb523d473b7d54921ae839299d4201b782ea6ee0ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fc76ea86567ce249f96af8a5d50732c9f23b1dd8e0f0a307ce82d7e74234cef0e908d3cd0ad644b6f5617ab4c3f450a26a9dfe707907a46e0009a12b920b206
|
7
|
+
data.tar.gz: 507f4c378489292422e1560cfda1bfe6e3d4fc8cf8642f3f8786e0d75d389b0a9a4881b035da089ede1a8f90377f0016590441b740b5f207eae1c74b143b6f11
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## [0.1.7] - 2025-03-14
|
2
|
+
|
3
|
+
- Go To Definition for ree errors into locales
|
4
|
+
- hover for ree error locales
|
5
|
+
- formatter: add raised ree errors to `throws` section
|
6
|
+
|
7
|
+
## [0.1.6] - 2025-03-04
|
8
|
+
|
9
|
+
- file rename triggers class name change
|
10
|
+
- add documentation string to hover
|
11
|
+
- add hover on link section beans
|
12
|
+
|
1
13
|
## [0.1.5] - 2025-02-28
|
2
14
|
|
3
15
|
- improved hover format
|
data/Gemfile.lock
CHANGED
@@ -6,6 +6,7 @@ require_relative "ree_indexing_enhancement"
|
|
6
6
|
require_relative "utils/ree_lsp_utils"
|
7
7
|
require_relative "ree_formatter"
|
8
8
|
require_relative "ree_template_applicator"
|
9
|
+
require_relative "ree_rename_handler"
|
9
10
|
require_relative "parsing/parsed_document_builder"
|
10
11
|
|
11
12
|
module RubyLsp
|
@@ -46,8 +47,6 @@ module RubyLsp
|
|
46
47
|
# Clients are not required to implement this capability
|
47
48
|
return unless global_state.supports_watching_files
|
48
49
|
|
49
|
-
return unless @template_applicator.template_dir_exists?
|
50
|
-
|
51
50
|
message_queue << Request.new(
|
52
51
|
id: "ruby-lsp-ree-file-create-watcher",
|
53
52
|
method: "client/registerCapability",
|
@@ -60,7 +59,7 @@ module RubyLsp
|
|
60
59
|
watchers: [
|
61
60
|
Interface::FileSystemWatcher.new(
|
62
61
|
glob_pattern: "**/*.rb",
|
63
|
-
kind: Constant::WatchKind::CREATE,
|
62
|
+
kind: Constant::WatchKind::CREATE | Constant::WatchKind::CHANGE,
|
64
63
|
),
|
65
64
|
],
|
66
65
|
),
|
@@ -71,10 +70,16 @@ module RubyLsp
|
|
71
70
|
end
|
72
71
|
|
73
72
|
def workspace_did_change_watched_files(changes)
|
74
|
-
|
73
|
+
if changes.size == 1 && changes[0][:type] == Constant::FileChangeType::CREATED
|
74
|
+
$stderr.puts("file created #{changes[0][:uri]}")
|
75
|
+
|
76
|
+
return unless @template_applicator.template_dir_exists?
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
+
@template_applicator.apply(changes[0])
|
79
|
+
elsif changes.size == 2 && changes.any?{ _1[:type] == Constant::FileChangeType::CREATED } && changes.any?{ _1[:type] == Constant::FileChangeType::DELETED }
|
80
|
+
$stderr.puts("file renamed #{changes[0][:uri]} #{changes[1][:uri]}")
|
81
|
+
|
82
|
+
RubyLsp::Ree::ReeRenameHandler.call(changes)
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
@@ -110,7 +110,7 @@ module RubyLsp
|
|
110
110
|
def get_enum_values_completion_items(enum_obj, location)
|
111
111
|
enum_node = RubyLsp::Ree::ParsedDocumentBuilder.build_from_uri(enum_obj.uri, :enum)
|
112
112
|
|
113
|
-
class_name = enum_node.
|
113
|
+
class_name = enum_node.full_class_name
|
114
114
|
|
115
115
|
label_details = Interface::CompletionItemLabelDetails.new(
|
116
116
|
description: "from: #{class_name}",
|
@@ -2,17 +2,20 @@ require_relative "../utils/ree_lsp_utils"
|
|
2
2
|
require_relative "../ree_object_finder"
|
3
3
|
require_relative "../parsing/parsed_link_node"
|
4
4
|
require_relative "../parsing/parsed_document_builder"
|
5
|
+
require_relative "../utils/ree_locale_utils"
|
5
6
|
|
6
7
|
module RubyLsp
|
7
8
|
module Ree
|
8
9
|
class DefinitionHandler
|
9
10
|
include Requests::Support::Common
|
10
11
|
include RubyLsp::Ree::ReeLspUtils
|
12
|
+
include RubyLsp::Ree::ReeLocaleUtils
|
11
13
|
|
12
14
|
def initialize(index, uri, node_context)
|
13
15
|
@index = index
|
14
16
|
@uri = uri
|
15
17
|
@node_context = node_context
|
18
|
+
@root_node = @node_context.instance_variable_get(:@nesting_nodes).first
|
16
19
|
@finder = ReeObjectFinder.new(@index)
|
17
20
|
end
|
18
21
|
|
@@ -71,7 +74,15 @@ module RubyLsp
|
|
71
74
|
message = node.message
|
72
75
|
result = []
|
73
76
|
|
74
|
-
|
77
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_ast(@root_node, @uri)
|
78
|
+
link_node = parsed_doc.find_link_node(message)
|
79
|
+
|
80
|
+
definition_item = if link_node
|
81
|
+
@finder.find_object_for_package(message, link_node.link_package_name)
|
82
|
+
else
|
83
|
+
@finder.find_object(message)
|
84
|
+
end
|
85
|
+
|
75
86
|
return [] unless definition_item
|
76
87
|
|
77
88
|
definition_uri = definition_item.uri.to_s
|
@@ -127,6 +138,60 @@ module RubyLsp
|
|
127
138
|
),
|
128
139
|
)
|
129
140
|
end
|
141
|
+
|
142
|
+
def get_error_locales_definition_items(node)
|
143
|
+
locales_folder = package_locales_folder_path(@uri.path)
|
144
|
+
|
145
|
+
return [] unless File.directory?(locales_folder)
|
146
|
+
|
147
|
+
result = []
|
148
|
+
key_path = node.unescaped
|
149
|
+
|
150
|
+
Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
|
151
|
+
line = find_locale_key_line(locale_file, key_path)
|
152
|
+
|
153
|
+
result << Interface::Location.new(
|
154
|
+
uri: locale_file,
|
155
|
+
range: Interface::Range.new(
|
156
|
+
start: Interface::Position.new(line: line, character: 0),
|
157
|
+
end: Interface::Position.new(line: line, character: 0),
|
158
|
+
),
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
result
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_error_code_definition_items(node)
|
166
|
+
locales_folder = package_locales_folder_path(@uri.path)
|
167
|
+
|
168
|
+
return [] unless File.directory?(locales_folder)
|
169
|
+
|
170
|
+
result = []
|
171
|
+
|
172
|
+
key_path = if @node_context.parent.arguments.arguments.size > 1
|
173
|
+
@node_context.parent.arguments.arguments[1].unescaped
|
174
|
+
else
|
175
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_ast(@root_node, @uri)
|
176
|
+
|
177
|
+
mod = underscore(parsed_doc.module_name)
|
178
|
+
"#{mod}.errors.#{node.unescaped}"
|
179
|
+
end
|
180
|
+
|
181
|
+
Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
|
182
|
+
line = find_locale_key_line(locale_file, key_path)
|
183
|
+
|
184
|
+
result << Interface::Location.new(
|
185
|
+
uri: locale_file,
|
186
|
+
range: Interface::Range.new(
|
187
|
+
start: Interface::Position.new(line: line, character: 0),
|
188
|
+
end: Interface::Position.new(line: line, character: 0),
|
189
|
+
),
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
result
|
194
|
+
end
|
130
195
|
end
|
131
196
|
end
|
132
197
|
end
|
@@ -2,17 +2,20 @@ require_relative "../ree_object_finder"
|
|
2
2
|
require_relative "../parsing/parsed_document_builder"
|
3
3
|
require_relative "../parsing/parsed_link_node"
|
4
4
|
require_relative "../utils/ree_lsp_utils"
|
5
|
+
require_relative "../utils/ree_locale_utils"
|
5
6
|
|
6
7
|
module RubyLsp
|
7
8
|
module Ree
|
8
9
|
class HoverHandler
|
9
10
|
include Requests::Support::Common
|
10
11
|
include RubyLsp::Ree::ReeLspUtils
|
12
|
+
include RubyLsp::Ree::ReeLocaleUtils
|
11
13
|
|
12
14
|
def initialize(index, node_context)
|
13
15
|
@index = index
|
14
16
|
@node_context = node_context
|
15
17
|
@finder = ReeObjectFinder.new(@index)
|
18
|
+
@root_node = @node_context.instance_variable_get(:@nesting_nodes).first
|
16
19
|
end
|
17
20
|
|
18
21
|
def get_ree_object_hover_items(node)
|
@@ -20,17 +23,34 @@ module RubyLsp
|
|
20
23
|
|
21
24
|
return [] unless ree_object
|
22
25
|
|
23
|
-
documentation =
|
26
|
+
documentation = get_object_documentation(ree_object)
|
27
|
+
|
28
|
+
[documentation]
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_linked_object_hover_items(node)
|
32
|
+
parent_node = @node_context.parent
|
33
|
+
return [] unless parent_node.name == :link
|
34
|
+
|
35
|
+
ree_object = @finder.find_object(node.unescaped)
|
36
|
+
|
37
|
+
return [] unless ree_object
|
38
|
+
|
39
|
+
documentation = get_object_documentation(ree_object)
|
40
|
+
|
41
|
+
[documentation]
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_object_documentation(ree_object)
|
45
|
+
<<~DOC
|
24
46
|
\`\`\`ruby
|
25
|
-
#{
|
47
|
+
#{ree_object.name}#{get_detail_string(ree_object)}
|
26
48
|
\`\`\`
|
27
49
|
---
|
28
|
-
|
50
|
+
#{@finder.object_documentation(ree_object)}
|
29
51
|
|
30
52
|
[#{path_from_package_folder(ree_object.uri)}](#{ree_object.uri})
|
31
53
|
DOC
|
32
|
-
|
33
|
-
[documentation]
|
34
54
|
end
|
35
55
|
|
36
56
|
def get_detail_string(ree_object)
|
@@ -44,6 +64,67 @@ module RubyLsp
|
|
44
64
|
|
45
65
|
signature.parameters.map(&:decorated_name).join(', ')
|
46
66
|
end
|
67
|
+
|
68
|
+
def get_error_locales_hover_items(node)
|
69
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_ast(@root_node, nil)
|
70
|
+
uri = get_uri_from_object(parsed_doc)
|
71
|
+
|
72
|
+
locales_folder = package_locales_folder_path(uri.path)
|
73
|
+
return [] unless File.directory?(locales_folder)
|
74
|
+
|
75
|
+
result = []
|
76
|
+
key_path = node.unescaped
|
77
|
+
|
78
|
+
documentation = ''
|
79
|
+
|
80
|
+
Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
|
81
|
+
value = find_locale_value(locale_file, key_path)
|
82
|
+
|
83
|
+
if value
|
84
|
+
loc_key = File.basename(locale_file, '.yml')
|
85
|
+
documentation += "#{loc_key}: #{value}\n\n"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
[documentation]
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_error_code_hover_items(node)
|
93
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_ast(@root_node, nil)
|
94
|
+
uri = get_uri_from_object(parsed_doc)
|
95
|
+
|
96
|
+
locales_folder = package_locales_folder_path(uri.path)
|
97
|
+
return [] unless File.directory?(locales_folder)
|
98
|
+
|
99
|
+
result = []
|
100
|
+
|
101
|
+
key_path = if @node_context.parent.arguments.arguments.size > 1
|
102
|
+
@node_context.parent.arguments.arguments[1].unescaped
|
103
|
+
else
|
104
|
+
mod = underscore(parsed_doc.module_name)
|
105
|
+
"#{mod}.errors.#{node.unescaped}"
|
106
|
+
end
|
107
|
+
|
108
|
+
documentation = ''
|
109
|
+
|
110
|
+
Dir.glob(File.join(locales_folder, '**/*.yml')).each do |locale_file|
|
111
|
+
value = find_locale_value(locale_file, key_path)
|
112
|
+
|
113
|
+
if value
|
114
|
+
loc_key = File.basename(locale_file, '.yml')
|
115
|
+
documentation += "#{loc_key}: #{value}\n\n"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
[documentation]
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_uri_from_object(parsed_doc)
|
123
|
+
obj = parsed_doc.links_container_node_name
|
124
|
+
|
125
|
+
ree_obj = @finder.find_object(obj)
|
126
|
+
ree_obj.uri
|
127
|
+
end
|
47
128
|
end
|
48
129
|
end
|
49
130
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative "../handlers/definition_handler"
|
2
|
+
require_relative "../ree_context"
|
2
3
|
|
3
4
|
module RubyLsp
|
4
5
|
module Ree
|
@@ -8,6 +9,7 @@ module RubyLsp
|
|
8
9
|
def initialize(response_builder, node_context, index, dispatcher, uri)
|
9
10
|
@response_builder = response_builder
|
10
11
|
@handler = RubyLsp::Ree::DefinitionHandler.new(index, uri, node_context)
|
12
|
+
@ree_context = RubyLsp::Ree::ReeContext.new(node_context)
|
11
13
|
|
12
14
|
dispatcher.register(
|
13
15
|
self,
|
@@ -40,14 +42,24 @@ module RubyLsp
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def on_symbol_node_enter(node)
|
43
|
-
definition_items = @
|
45
|
+
definition_items = if @ree_context.is_error_definition?
|
46
|
+
@handler.get_error_code_definition_items(node)
|
47
|
+
else
|
48
|
+
@handler.get_linked_object_definition_items(node)
|
49
|
+
end
|
50
|
+
|
44
51
|
put_items_into_response(definition_items)
|
45
52
|
rescue => e
|
46
53
|
$stderr.puts("error in definition listener(on_symbol_node_enter): #{e.message} : #{e.backtrace.first}")
|
47
54
|
end
|
48
55
|
|
49
56
|
def on_string_node_enter(node)
|
50
|
-
definition_items = @
|
57
|
+
definition_items = if @ree_context.is_error_definition?
|
58
|
+
@handler.get_error_locales_definition_items(node)
|
59
|
+
else
|
60
|
+
@handler.get_linked_filepath_definition_items(node)
|
61
|
+
end
|
62
|
+
|
51
63
|
put_items_into_response(definition_items)
|
52
64
|
rescue => e
|
53
65
|
$stderr.puts("error in definition listener(on_string_node_enter): #{e.message} : #{e.backtrace.first}")
|
@@ -8,9 +8,15 @@ module RubyLsp
|
|
8
8
|
def initialize(response_builder, node_context, index, dispatcher)
|
9
9
|
@response_builder = response_builder
|
10
10
|
@handler = RubyLsp::Ree::HoverHandler.new(index, node_context)
|
11
|
+
@ree_context = RubyLsp::Ree::ReeContext.new(node_context)
|
11
12
|
|
12
|
-
dispatcher.register(
|
13
|
-
|
13
|
+
dispatcher.register(
|
14
|
+
self,
|
15
|
+
:on_call_node_enter,
|
16
|
+
:on_symbol_node_enter,
|
17
|
+
:on_string_node_enter,
|
18
|
+
:on_constant_read_node_enter
|
19
|
+
)
|
14
20
|
end
|
15
21
|
|
16
22
|
def on_constant_read_node_enter(node)
|
@@ -32,6 +38,28 @@ module RubyLsp
|
|
32
38
|
$stderr.puts("error in hover listener(on_call_node_enter): #{e.message} : #{e.backtrace.first}")
|
33
39
|
end
|
34
40
|
|
41
|
+
def on_string_node_enter(node)
|
42
|
+
return unless @ree_context.is_error_definition?
|
43
|
+
|
44
|
+
hover_items = @handler.get_error_locales_hover_items(node)
|
45
|
+
|
46
|
+
put_items_into_response(hover_items)
|
47
|
+
rescue => e
|
48
|
+
$stderr.puts("error in hover listener(on_string_node_enter): #{e.message} : #{e.backtrace.first}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_symbol_node_enter(node)
|
52
|
+
hover_items = if @ree_context.is_error_definition?
|
53
|
+
@handler.get_error_code_hover_items(node)
|
54
|
+
else
|
55
|
+
@handler.get_linked_object_hover_items(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
put_items_into_response(hover_items)
|
59
|
+
rescue => e
|
60
|
+
$stderr.puts("error in hover listener(on_symbol_node_enter): #{e.message} : #{e.backtrace.first}")
|
61
|
+
end
|
62
|
+
|
35
63
|
def put_items_into_response(items)
|
36
64
|
items.each do |item|
|
37
65
|
@response_builder.push(item, category: :documentation)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'parsed_link_node'
|
2
|
+
require_relative 'parsed_method_node'
|
2
3
|
require 'ostruct'
|
3
4
|
|
4
5
|
class RubyLsp::Ree::ParsedDocument
|
@@ -6,9 +7,26 @@ class RubyLsp::Ree::ParsedDocument
|
|
6
7
|
|
7
8
|
LINK_DSL_MODULE = 'Ree::LinkDSL'
|
8
9
|
|
10
|
+
ERROR_DEFINITION_NAMES = [
|
11
|
+
:auth_error,
|
12
|
+
:build_error,
|
13
|
+
:conflict_error,
|
14
|
+
:invalid_param_error,
|
15
|
+
:not_found_error,
|
16
|
+
:payment_required_error,
|
17
|
+
:permission_error,
|
18
|
+
:validation_error
|
19
|
+
]
|
20
|
+
|
21
|
+
CONTRACT_CALL_NODE_NAMES = [
|
22
|
+
:contract,
|
23
|
+
:throws
|
24
|
+
]
|
25
|
+
|
9
26
|
attr_reader :ast, :package_name, :class_node, :fn_node, :class_includes,
|
10
27
|
:link_nodes, :values, :action_node, :dao_node, :filters,
|
11
|
-
:bean_node, :bean_methods, :mapper_node, :links_container_block_node, :aggregate_node
|
28
|
+
:bean_node, :bean_methods, :mapper_node, :links_container_block_node, :aggregate_node,
|
29
|
+
:error_definitions, :doc_instance_methods
|
12
30
|
|
13
31
|
def initialize(ast)
|
14
32
|
@ast = ast
|
@@ -161,6 +179,25 @@ class RubyLsp::Ree::ParsedDocument
|
|
161
179
|
.map{ OpenStruct.new(name: node_name(_1).to_s, signatures: parse_signatures_from_params(_1.parameters)) }
|
162
180
|
end
|
163
181
|
|
182
|
+
def parse_instance_methods
|
183
|
+
@doc_instance_methods = []
|
184
|
+
|
185
|
+
current_contract_node = nil
|
186
|
+
class_node.body.body.each do |node|
|
187
|
+
if node.is_a?(Prism::CallNode) && CONTRACT_CALL_NODE_NAMES.include?(node_name(node))
|
188
|
+
current_contract_node = node
|
189
|
+
else
|
190
|
+
if node.is_a?(Prism::DefNode)
|
191
|
+
@doc_instance_methods << RubyLsp::Ree::ParsedMethodNode.new(node, current_contract_node)
|
192
|
+
end
|
193
|
+
|
194
|
+
current_contract_node = nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
@doc_instance_methods
|
199
|
+
end
|
200
|
+
|
164
201
|
def parse_filter_signature(filter_node)
|
165
202
|
return [] unless filter_node
|
166
203
|
|
@@ -175,11 +212,31 @@ class RubyLsp::Ree::ParsedDocument
|
|
175
212
|
[RubyIndexer::Entry::Signature.new(signature_params)]
|
176
213
|
end
|
177
214
|
|
178
|
-
def
|
215
|
+
def parse_error_definitions
|
216
|
+
return unless class_node
|
217
|
+
|
218
|
+
@error_definitions = class_node.body.body
|
219
|
+
.select{ _1.is_a?(Prism::ConstantWriteNode) }
|
220
|
+
.select{ ERROR_DEFINITION_NAMES.include?(node_name(_1.value)) }
|
221
|
+
end
|
222
|
+
|
223
|
+
def class_name
|
224
|
+
class_node.constant_path.name.to_s
|
225
|
+
end
|
226
|
+
|
227
|
+
def module_name
|
228
|
+
class_node.constant_path&.parent&.name.to_s
|
229
|
+
end
|
230
|
+
|
231
|
+
def full_class_name
|
179
232
|
name_parts = [class_node.constant_path&.parent&.name, class_node.constant_path.name]
|
180
233
|
name_parts.compact.map(&:to_s).join('::')
|
181
234
|
end
|
182
235
|
|
236
|
+
def links_container_node_name
|
237
|
+
links_container_node.arguments.arguments.first.unescaped
|
238
|
+
end
|
239
|
+
|
183
240
|
def node_name(node)
|
184
241
|
return nil unless node.respond_to?(:name)
|
185
242
|
|
@@ -38,11 +38,11 @@ class RubyLsp::Ree::ParsedDocumentBuilder
|
|
38
38
|
when :bean
|
39
39
|
build_bean_document(ast)
|
40
40
|
else
|
41
|
-
|
41
|
+
build_detected_document_type(ast)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def self.
|
45
|
+
def self.build_detected_document_type(ast)
|
46
46
|
if has_root_class?(ast)
|
47
47
|
build_regular_document(ast)
|
48
48
|
elsif has_root_rspec_call?(ast)
|
@@ -53,7 +53,7 @@ class RubyLsp::Ree::ParsedDocumentBuilder
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def self.has_root_class?(ast)
|
56
|
-
ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
|
56
|
+
!!ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
|
57
57
|
end
|
58
58
|
|
59
59
|
def self.has_root_rspec_call?(ast)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'prism'
|
2
|
+
|
3
|
+
class RubyLsp::Ree::ParsedMethodNode
|
4
|
+
attr_reader :method_node, :contract_node
|
5
|
+
|
6
|
+
def initialize(method_node, contract_node)
|
7
|
+
@method_node = method_node
|
8
|
+
@contract_node = contract_node
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@method_node.name
|
13
|
+
end
|
14
|
+
|
15
|
+
def has_contract?
|
16
|
+
!!@contract_node
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_line
|
20
|
+
@method_node.location.start_line - 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def end_line
|
24
|
+
@method_node.location.end_line - 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def raised_errors_nested(source, error_definitions)
|
28
|
+
return [] if error_definitions.size == 0
|
29
|
+
|
30
|
+
raised = raised_errors(source, error_definitions)
|
31
|
+
|
32
|
+
not_detected_errors = error_definitions.select{ !raised.include?(_1.name.to_s) }
|
33
|
+
@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) }
|
36
|
+
end
|
37
|
+
|
38
|
+
raised
|
39
|
+
end
|
40
|
+
|
41
|
+
def raised_errors(source, error_definitions)
|
42
|
+
raised = []
|
43
|
+
error_names = error_definitions.map(&:name).map(&:to_s)
|
44
|
+
|
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/
|
48
|
+
|
49
|
+
if line.match?(regex)
|
50
|
+
raised << error_name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
raised.uniq
|
56
|
+
end
|
57
|
+
|
58
|
+
def throws_errors
|
59
|
+
return [] unless has_contract?
|
60
|
+
return [] unless has_throw_section?
|
61
|
+
|
62
|
+
@contract_node.arguments.arguments.map{ _1.name.to_s }
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_throw_section?
|
66
|
+
@contract_node && @contract_node.name == :throws
|
67
|
+
end
|
68
|
+
|
69
|
+
def throw_arguments_end_position
|
70
|
+
@contract_node.arguments.arguments.last.location.end_column - 1
|
71
|
+
end
|
72
|
+
|
73
|
+
def throw_arguments_end_line
|
74
|
+
@contract_node.arguments.arguments.last.location.end_line - 1
|
75
|
+
end
|
76
|
+
|
77
|
+
def contract_node_end_position
|
78
|
+
@contract_node.location.end_column - 1
|
79
|
+
end
|
80
|
+
|
81
|
+
def contract_node_end_line
|
82
|
+
@contract_node.location.end_line - 1
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_nested_local_methods(local_methods)
|
86
|
+
local_method_names = local_methods.map(&:name)
|
87
|
+
call_nodes = parse_body_call_objects(@method_node.body.body)
|
88
|
+
call_node_names = call_nodes.map(&:name)
|
89
|
+
|
90
|
+
@nested_local_methods = local_methods.select{ call_node_names.include?(_1.name) }
|
91
|
+
@nested_local_methods.each{ _1.parse_nested_local_methods(local_methods) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse_body_call_objects(node_body)
|
95
|
+
call_nodes = []
|
96
|
+
|
97
|
+
node_body.each do |node|
|
98
|
+
if node.is_a?(Prism::CallNode) && !node.receiver
|
99
|
+
call_nodes << node
|
100
|
+
elsif node.respond_to?(:statements)
|
101
|
+
call_nodes += parse_body_call_objects(node.statements.body)
|
102
|
+
elsif node.respond_to?(:block) && node.block
|
103
|
+
call_nodes += parse_body_call_objects(node.block.body.body)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
call_nodes
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'prism'
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Ree
|
5
|
+
class ReeContext
|
6
|
+
ERROR_DEFINITION_NAMES = [
|
7
|
+
:auth_error,
|
8
|
+
:build_error,
|
9
|
+
:conflict_error,
|
10
|
+
:invalid_param_error,
|
11
|
+
:not_found_error,
|
12
|
+
:payment_required_error,
|
13
|
+
:permission_error,
|
14
|
+
:validation_error
|
15
|
+
]
|
16
|
+
|
17
|
+
def initialize(node_context)
|
18
|
+
@node_context = node_context
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_error_definition?
|
22
|
+
return false if !@node_context || !@node_context.parent || !@node_context.parent.is_a?(Prism::CallNode)
|
23
|
+
|
24
|
+
ERROR_DEFINITION_NAMES.include?(@node_context.parent.name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -9,7 +9,9 @@ module RubyLsp
|
|
9
9
|
|
10
10
|
def run_formatting(uri, document)
|
11
11
|
source = document.source
|
12
|
-
|
12
|
+
|
13
|
+
sorted_source = sort_links(source)
|
14
|
+
add_missing_error_contracts(sorted_source)
|
13
15
|
rescue => e
|
14
16
|
$stderr.puts("error in ree_formatter: #{e.message} : #{e.backtrace.first}")
|
15
17
|
end
|
@@ -57,6 +59,47 @@ module RubyLsp
|
|
57
59
|
source_lines[link_line - 1] = sorted_lines[index]
|
58
60
|
end
|
59
61
|
|
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"
|
100
|
+
end
|
101
|
+
|
102
|
+
|
60
103
|
source_lines.join()
|
61
104
|
end
|
62
105
|
end
|
@@ -18,9 +18,14 @@ module RubyLsp
|
|
18
18
|
obj_name = node.arguments.child_nodes.first.unescaped
|
19
19
|
return unless current_filename == obj_name
|
20
20
|
|
21
|
+
source = @listener.instance_variable_get(:@source_lines).join
|
22
|
+
ast = Prism.parse(source).value # TODO use doc builder
|
23
|
+
|
21
24
|
location = node.location
|
22
|
-
signatures = parse_signatures(obj_name)
|
23
|
-
|
25
|
+
signatures = parse_signatures(obj_name, ast)
|
26
|
+
documentation = parse_documentation(obj_name, ast)
|
27
|
+
|
28
|
+
comments = "ree_object\ntype: :#{node.name}\n#{documentation}"
|
24
29
|
|
25
30
|
@listener.add_method(
|
26
31
|
obj_name,
|
@@ -32,10 +37,7 @@ module RubyLsp
|
|
32
37
|
|
33
38
|
private
|
34
39
|
|
35
|
-
def parse_signatures(fn_name)
|
36
|
-
source = @listener.instance_variable_get(:@source_lines).join
|
37
|
-
ast = Prism.parse(source).value
|
38
|
-
|
40
|
+
def parse_signatures(fn_name, ast)
|
39
41
|
class_node = ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
|
40
42
|
return [] unless class_node
|
41
43
|
|
@@ -47,6 +49,28 @@ module RubyLsp
|
|
47
49
|
[RubyIndexer::Entry::Signature.new(signature_params)]
|
48
50
|
end
|
49
51
|
|
52
|
+
def parse_documentation(fn_name, ast)
|
53
|
+
class_node = ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
|
54
|
+
return '' unless class_node
|
55
|
+
|
56
|
+
doc_node = class_node.body.body.detect{ |node| node.respond_to?(:name) && node.name == :doc }
|
57
|
+
return '' unless doc_node
|
58
|
+
|
59
|
+
str_node = doc_node.arguments.arguments.first
|
60
|
+
|
61
|
+
case str_node
|
62
|
+
when Prism::StringNode
|
63
|
+
str_node.unescaped
|
64
|
+
when Prism::InterpolatedStringNode
|
65
|
+
str_node.parts.map(&:unescaped).join
|
66
|
+
else
|
67
|
+
''
|
68
|
+
end
|
69
|
+
rescue => e
|
70
|
+
$stderr.puts("error parsing documentation for #{fn_name}: #{e.message} : #{e.backtrace.first}")
|
71
|
+
return ''
|
72
|
+
end
|
73
|
+
|
50
74
|
def current_filename
|
51
75
|
uri = @listener.instance_variable_get(:@uri)
|
52
76
|
File.basename(uri.path, '.rb')
|
@@ -1,6 +1,10 @@
|
|
1
|
+
require_relative "utils/ree_lsp_utils"
|
2
|
+
|
1
3
|
module RubyLsp
|
2
4
|
module Ree
|
3
5
|
class ReeObjectFinder
|
6
|
+
include RubyLsp::Ree::ReeLspUtils
|
7
|
+
|
4
8
|
MAX_LIMIT = 1000
|
5
9
|
|
6
10
|
REE_OBJECT_STRING = 'ree_object'
|
@@ -42,6 +46,13 @@ module RubyLsp
|
|
42
46
|
objects_by_name.detect{ _1.comments.to_s.lines.first&.chomp == REE_OBJECT_STRING }
|
43
47
|
end
|
44
48
|
|
49
|
+
def find_object_for_package(name, package_name)
|
50
|
+
objects_by_name = @index[name]
|
51
|
+
return unless objects_by_name
|
52
|
+
|
53
|
+
objects_by_name.detect{ _1.comments.to_s.lines.first&.chomp == REE_OBJECT_STRING && package_name_from_uri(_1.uri) == package_name }
|
54
|
+
end
|
55
|
+
|
45
56
|
def find_objects_by_types(name, types)
|
46
57
|
objects_by_name = @index[name]
|
47
58
|
return [] unless objects_by_name
|
@@ -76,6 +87,10 @@ module RubyLsp
|
|
76
87
|
|
77
88
|
type_str.split(' ').last[1..-1].to_sym
|
78
89
|
end
|
90
|
+
|
91
|
+
def object_documentation(ree_object)
|
92
|
+
ree_object.comments.lines[2..-1].join("\n").chomp
|
93
|
+
end
|
79
94
|
end
|
80
95
|
end
|
81
96
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module RubyLsp
|
2
|
+
module Ree
|
3
|
+
class ReeRenameHandler
|
4
|
+
include RubyLsp::Ree::ReeLspUtils
|
5
|
+
|
6
|
+
def self.call(changes)
|
7
|
+
old_uri = URI.parse(changes.detect{ _1[:type] == Constant::FileChangeType::DELETED }[:uri])
|
8
|
+
new_uri = URI.parse(changes.detect{ _1[:type] == Constant::FileChangeType::CREATED }[:uri])
|
9
|
+
|
10
|
+
old_file_name = File.basename(old_uri, '.rb')
|
11
|
+
new_file_name = File.basename(new_uri, '.rb')
|
12
|
+
|
13
|
+
return if old_file_name == new_file_name
|
14
|
+
|
15
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_uri(new_uri)
|
16
|
+
return if !parsed_doc || !parsed_doc.class_node
|
17
|
+
|
18
|
+
old_class_name = old_file_name.split('_').collect(&:capitalize).join
|
19
|
+
new_class_name = new_file_name.split('_').collect(&:capitalize).join
|
20
|
+
|
21
|
+
return unless parsed_doc.class_name == old_class_name
|
22
|
+
|
23
|
+
file_content_lines = File.read(new_uri.path).lines
|
24
|
+
|
25
|
+
class_line = parsed_doc.class_node.location.start_line - 1
|
26
|
+
|
27
|
+
file_content_lines[class_line].gsub!(/\b#{old_class_name}\b/, new_class_name)
|
28
|
+
|
29
|
+
if parsed_doc.links_container_node
|
30
|
+
links_container_node_line = parsed_doc.links_container_node.location.start_line - 1
|
31
|
+
|
32
|
+
file_content_lines[links_container_node_line].gsub!(/\b#{old_file_name}\b/, new_file_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
File.write(new_uri.path, file_content_lines.join)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module RubyLsp
|
4
|
+
module Ree
|
5
|
+
module ReeLocaleUtils
|
6
|
+
def package_locales_folder_path(uri)
|
7
|
+
uri_parts = uri.to_s.chomp(File.extname(uri.to_s)).split('/')
|
8
|
+
|
9
|
+
package_folder_index = uri_parts.index('package')
|
10
|
+
return unless package_folder_index
|
11
|
+
|
12
|
+
path_parts = uri_parts.take(package_folder_index+2) + ['locales']
|
13
|
+
path_parts.join('/')
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_locale_value(file_path, key_path)
|
17
|
+
loc_yaml = YAML.load_file(file_path)
|
18
|
+
loc_key = File.basename(file_path, '.yml')
|
19
|
+
key_parts = [loc_key] + key_path.split('.')
|
20
|
+
|
21
|
+
loc_yaml.dig(*key_parts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_locale_key_line(file_path, key_path)
|
25
|
+
loc_key = File.basename(file_path, '.yml')
|
26
|
+
|
27
|
+
key_parts = [loc_key] + key_path.split('.')
|
28
|
+
|
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
|
41
|
+
end
|
42
|
+
|
43
|
+
0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -7,7 +7,7 @@ module RubyLsp
|
|
7
7
|
file_name = file_path + ".rb"
|
8
8
|
Dir[File.join('**', file_name)].first
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def package_name_from_uri(uri)
|
12
12
|
uri_parts = uri.to_s.split('/')
|
13
13
|
|
@@ -170,6 +170,17 @@ module RubyLsp
|
|
170
170
|
:"(#{names_with_commas})"
|
171
171
|
end
|
172
172
|
end
|
173
|
+
|
174
|
+
# copied from ree string_utils
|
175
|
+
def underscore(camel_cased_word)
|
176
|
+
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
|
177
|
+
word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
|
178
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
|
179
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
|
180
|
+
word.tr!("-".freeze, "_".freeze)
|
181
|
+
word.downcase!
|
182
|
+
word
|
183
|
+
end
|
173
184
|
end
|
174
185
|
end
|
175
186
|
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.7
|
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-
|
11
|
+
date: 2025-03-20 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:
|
@@ -35,11 +35,15 @@ files:
|
|
35
35
|
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document.rb
|
36
36
|
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb
|
37
37
|
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_link_node.rb
|
38
|
+
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_method_node.rb
|
38
39
|
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_rspec_document.rb
|
40
|
+
- lib/ruby_lsp/ruby_lsp_ree/ree_context.rb
|
39
41
|
- lib/ruby_lsp/ruby_lsp_ree/ree_formatter.rb
|
40
42
|
- lib/ruby_lsp/ruby_lsp_ree/ree_indexing_enhancement.rb
|
41
43
|
- lib/ruby_lsp/ruby_lsp_ree/ree_object_finder.rb
|
44
|
+
- lib/ruby_lsp/ruby_lsp_ree/ree_rename_handler.rb
|
42
45
|
- lib/ruby_lsp/ruby_lsp_ree/ree_template_applicator.rb
|
46
|
+
- lib/ruby_lsp/ruby_lsp_ree/utils/ree_locale_utils.rb
|
43
47
|
- lib/ruby_lsp/ruby_lsp_ree/utils/ree_lsp_utils.rb
|
44
48
|
- lib/ruby_lsp_ree.rb
|
45
49
|
- lib/ruby_lsp_ree/version.rb
|