ruby-lsp-ree 0.1.0 → 0.1.1
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 +17 -0
- data/Gemfile.lock +19 -0
- data/lib/ruby_lsp/ruby_lsp_ree/addon.rb +2 -3
- data/lib/ruby_lsp/ruby_lsp_ree/completion.rb +43 -118
- data/lib/ruby_lsp/ruby_lsp_ree/completion_utils.rb +234 -0
- data/lib/ruby_lsp/ruby_lsp_ree/definition.rb +48 -3
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document.rb +130 -0
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb +61 -0
- data/lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_link_node.rb +71 -0
- data/lib/ruby_lsp/ruby_lsp_ree/ree_formatter.rb +9 -16
- data/lib/ruby_lsp/ruby_lsp_ree/ree_indexing_enhancement.rb +24 -24
- data/lib/ruby_lsp/ruby_lsp_ree/ree_lsp_utils.rb +103 -68
- data/lib/ruby_lsp/ruby_lsp_ree/ree_object_finder.rb +33 -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: 350bb1ae1a376f2a8594e3945c396c49d01907b1c840d36311e28761c2fe9d8e
|
|
4
|
+
data.tar.gz: dfa9bc947cf5f951d62e2686290a7ac603c5dfa34652cb21be4b1547d2e3b5e1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eaaa38d222082eb2a55f73a977114a2632d2db2a2b7d10a0d96d2284b87fbd2c02bcab43e45ec617afa63e5c9fd4f9e64339f7cb919ba23365a5deae1aa3df4e
|
|
7
|
+
data.tar.gz: 64e7ce28fdb802eadbe27c6f7fd5d5e156c9c3d39627c9a7e191bf808fc9519eb26befa49182cc62bda12941c8e6fd4f478060ec61e33d38035aa10726c087d1
|
data/CHANGELOG.md
CHANGED
|
@@ -3,3 +3,20 @@
|
|
|
3
3
|
## [0.1.0] - 2025-01-31
|
|
4
4
|
|
|
5
5
|
- Initial release
|
|
6
|
+
|
|
7
|
+
## [0.1.1] - 2025-02-10
|
|
8
|
+
|
|
9
|
+
- sort links for objects with FnDSL
|
|
10
|
+
- autocomplete for objects with FnDSL
|
|
11
|
+
- Add Link for objects with FnDSL
|
|
12
|
+
- improve params in autocomplete
|
|
13
|
+
- Go To Definition for symbols in link section
|
|
14
|
+
- autocomplete for enums
|
|
15
|
+
- autocomplete for enum values
|
|
16
|
+
- Add Link for enums
|
|
17
|
+
- Go To Definition for enums
|
|
18
|
+
- autocomplete for ree actions
|
|
19
|
+
- Add Link for ree actions
|
|
20
|
+
- sort links in ree actions
|
|
21
|
+
- autocomplete for ree dao
|
|
22
|
+
- autocomplete for dao filters
|
data/Gemfile.lock
ADDED
|
@@ -4,6 +4,7 @@ require_relative "completion"
|
|
|
4
4
|
require_relative "ree_indexing_enhancement"
|
|
5
5
|
require_relative "ree_lsp_utils"
|
|
6
6
|
require_relative "ree_formatter"
|
|
7
|
+
require_relative "parsing/parsed_document_builder"
|
|
7
8
|
|
|
8
9
|
module RubyLsp
|
|
9
10
|
module Ree
|
|
@@ -23,10 +24,8 @@ module RubyLsp
|
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def create_definition_listener(response_builder, uri, node_context, dispatcher)
|
|
26
|
-
$stderr.puts("create_definition_listener")
|
|
27
|
-
|
|
28
27
|
index = @global_state.index
|
|
29
|
-
RubyLsp::Ree::Definition.new(response_builder, node_context, index, dispatcher)
|
|
28
|
+
RubyLsp::Ree::Definition.new(response_builder, node_context, index, dispatcher, uri)
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def create_completion_listener(response_builder, node_context, dispatcher, uri)
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
require_relative "ree_lsp_utils"
|
|
2
|
+
require_relative "completion_utils"
|
|
3
|
+
require_relative "ree_object_finder"
|
|
2
4
|
|
|
3
5
|
module RubyLsp
|
|
4
6
|
module Ree
|
|
5
7
|
class Completion
|
|
6
8
|
include Requests::Support::Common
|
|
7
9
|
include RubyLsp::Ree::ReeLspUtils
|
|
10
|
+
include RubyLsp::Ree::CompletionUtils
|
|
8
11
|
|
|
9
|
-
CHARS_COUNT =
|
|
12
|
+
CHARS_COUNT = 3
|
|
13
|
+
CANDIDATES_LIMIT = 20
|
|
10
14
|
|
|
11
15
|
def initialize(response_builder, node_context, index, dispatcher, uri)
|
|
12
16
|
@response_builder = response_builder
|
|
13
17
|
@index = index
|
|
14
18
|
@uri = uri
|
|
19
|
+
@node_context = node_context
|
|
15
20
|
|
|
16
21
|
dispatcher.register(self, :on_call_node_enter)
|
|
17
22
|
dispatcher.register(self, :on_constant_read_node_enter)
|
|
@@ -24,142 +29,62 @@ module RubyLsp
|
|
|
24
29
|
class_name_objects = @index.instance_variable_get(:@entries).keys.select{ _1.split('::').last[0...node_name.size] == node_name}
|
|
25
30
|
return if class_name_objects.size == 0
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class_name_objects.take(15).each do |full_class_name|
|
|
30
|
-
entry = @index[full_class_name].first
|
|
31
|
-
class_name = full_class_name.split('::').last
|
|
32
|
-
|
|
33
|
-
package_name = package_name_from_uri(entry.uri)
|
|
34
|
-
|
|
35
|
-
label_details = Interface::CompletionItemLabelDetails.new(
|
|
36
|
-
description: "from: :#{package_name}",
|
|
37
|
-
detail: ""
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
@response_builder << Interface::CompletionItem.new(
|
|
41
|
-
label: class_name,
|
|
42
|
-
label_details: label_details,
|
|
43
|
-
filter_text: class_name,
|
|
44
|
-
text_edit: Interface::TextEdit.new(
|
|
45
|
-
range: range_from_location(node.location),
|
|
46
|
-
new_text: class_name,
|
|
47
|
-
),
|
|
48
|
-
kind: Constant::CompletionItemKind::CLASS,
|
|
49
|
-
additional_text_edits: get_additional_text_edits_for_constant(doc_info, class_name, package_name, entry)
|
|
50
|
-
)
|
|
51
|
-
end
|
|
32
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_uri(@uri)
|
|
52
33
|
|
|
53
|
-
|
|
34
|
+
completion_items = get_class_name_completion_items(class_name_objects, parsed_doc, node, @index, CANDIDATES_LIMIT)
|
|
35
|
+
puts_items_into_response(completion_items)
|
|
54
36
|
end
|
|
55
37
|
|
|
56
38
|
def on_call_node_enter(node)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
ree_objects = @index.prefix_search(node.name.to_s)
|
|
61
|
-
.take(50).map(&:first)
|
|
62
|
-
.select{ _1.comments }
|
|
63
|
-
.select{ _1.comments.to_s.lines.first&.chomp == 'ree_object' }
|
|
64
|
-
.take(10)
|
|
65
|
-
|
|
66
|
-
return if ree_objects.size == 0
|
|
67
|
-
|
|
68
|
-
doc_info = parse_document_from_uri(@uri)
|
|
69
|
-
|
|
70
|
-
ree_objects.each do |ree_object|
|
|
71
|
-
fn_name = ree_object.name
|
|
72
|
-
|
|
73
|
-
package_name = package_name_from_uri(ree_object.uri)
|
|
74
|
-
|
|
75
|
-
params_str = ree_object.signatures.first.parameters.map(&:name).join(', ')
|
|
76
|
-
|
|
77
|
-
label_details = Interface::CompletionItemLabelDetails.new(
|
|
78
|
-
description: "from: :#{package_name}",
|
|
79
|
-
detail: "(#{params_str})"
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
$stderr.puts("ree object #{ree_object.inspect}")
|
|
83
|
-
|
|
84
|
-
@response_builder << Interface::CompletionItem.new(
|
|
85
|
-
label: fn_name,
|
|
86
|
-
label_details: label_details,
|
|
87
|
-
filter_text: fn_name,
|
|
88
|
-
text_edit: Interface::TextEdit.new(
|
|
89
|
-
range: range_from_location(node.location),
|
|
90
|
-
new_text: "#{fn_name}(#{params_str})",
|
|
91
|
-
),
|
|
92
|
-
kind: Constant::CompletionItemKind::METHOD,
|
|
93
|
-
data: {
|
|
94
|
-
owner_name: "Object",
|
|
95
|
-
guessed_type: false,
|
|
96
|
-
},
|
|
97
|
-
additional_text_edits: get_additional_text_edits_for_method(doc_info, fn_name, package_name)
|
|
98
|
-
)
|
|
39
|
+
if receiver_is_enum?(node)
|
|
40
|
+
return enum_value_completion(node)
|
|
99
41
|
end
|
|
100
|
-
|
|
101
|
-
nil
|
|
102
|
-
end
|
|
103
42
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
$stderr.puts("links already include #{class_name}")
|
|
107
|
-
return []
|
|
43
|
+
if receiver_is_dao?(node)
|
|
44
|
+
return dao_filter_completion(node)
|
|
108
45
|
end
|
|
109
46
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
link_text = if doc_info.package_name == package_name
|
|
113
|
-
fn_name = File.basename(entry_uri, ".*")
|
|
114
|
-
"\n\s\s\s\slink :#{fn_name}, import: -> { #{class_name} }"
|
|
115
|
-
else
|
|
116
|
-
path = path_from_package(entry_uri)
|
|
117
|
-
"\n\s\s\s\slink \"#{path}\", import: -> { #{class_name} }"
|
|
118
|
-
end
|
|
47
|
+
return if node.receiver
|
|
48
|
+
return if node.name.to_s.size < CHARS_COUNT
|
|
119
49
|
|
|
120
|
-
|
|
50
|
+
ree_objects = ReeObjectFinder.search_objects(@index, node.name.to_s, CANDIDATES_LIMIT)
|
|
51
|
+
return if ree_objects.size == 0
|
|
121
52
|
|
|
122
|
-
|
|
123
|
-
new_text = "\sdo#{link_text}\n\s\send\n"
|
|
124
|
-
end
|
|
53
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_uri(@uri)
|
|
125
54
|
|
|
126
|
-
|
|
55
|
+
completion_items = get_ree_objects_completions_items(ree_objects, parsed_doc, node)
|
|
56
|
+
puts_items_into_response(completion_items)
|
|
57
|
+
end
|
|
127
58
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
range: range,
|
|
131
|
-
new_text: new_text,
|
|
132
|
-
)
|
|
133
|
-
]
|
|
59
|
+
def receiver_is_enum?(node)
|
|
60
|
+
node.receiver && node.receiver.is_a?(Prism::CallNode) && ReeObjectFinder.find_enum(@index, node.receiver.name.to_s)
|
|
134
61
|
end
|
|
135
62
|
|
|
136
|
-
def
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return []
|
|
140
|
-
end
|
|
63
|
+
def receiver_is_dao?(node)
|
|
64
|
+
node.receiver && node.receiver.is_a?(Prism::CallNode) && ReeObjectFinder.find_dao(@index, node.receiver.name.to_s)
|
|
65
|
+
end
|
|
141
66
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
"\n\s\s\s\slink :#{fn_name}, from: :#{package_name}"
|
|
146
|
-
end
|
|
67
|
+
def enum_value_completion(node)
|
|
68
|
+
enum_obj = ReeObjectFinder.find_enum(@index, node.receiver.name.to_s)
|
|
69
|
+
location = node.receiver.location
|
|
147
70
|
|
|
148
|
-
|
|
71
|
+
completion_items = get_enum_values_completion_items(enum_obj, location)
|
|
72
|
+
puts_items_into_response(completion_items)
|
|
73
|
+
end
|
|
149
74
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
75
|
+
def dao_filter_completion(node)
|
|
76
|
+
dao_obj = ReeObjectFinder.find_dao(@index, node.receiver.name.to_s)
|
|
77
|
+
location = node.receiver.location
|
|
153
78
|
|
|
154
|
-
|
|
79
|
+
completion_items = get_dao_filters_completion_items(dao_obj, location)
|
|
80
|
+
puts_items_into_response(completion_items)
|
|
81
|
+
end
|
|
155
82
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
]
|
|
83
|
+
def puts_items_into_response(items)
|
|
84
|
+
items.each do |item|
|
|
85
|
+
@response_builder << item
|
|
86
|
+
end
|
|
162
87
|
end
|
|
163
88
|
end
|
|
164
89
|
end
|
|
165
|
-
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
require_relative "ree_lsp_utils"
|
|
2
|
+
|
|
3
|
+
module RubyLsp
|
|
4
|
+
module Ree
|
|
5
|
+
module CompletionUtils
|
|
6
|
+
include Requests::Support::Common
|
|
7
|
+
include RubyLsp::Ree::ReeLspUtils
|
|
8
|
+
|
|
9
|
+
def get_dao_filters_completion_items(dao_obj, location)
|
|
10
|
+
dao_node = RubyLsp::Ree::ParsedDocumentBuilder.build_from_uri(dao_obj.uri, :dao)
|
|
11
|
+
|
|
12
|
+
range = Interface::Range.new(
|
|
13
|
+
start: Interface::Position.new(line: location.start_line - 1, character: location.end_column + 1),
|
|
14
|
+
end: Interface::Position.new(line: location.start_line - 1, character: location.end_column + 1),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
dao_node.filters.map do |filter|
|
|
18
|
+
signature = filter.signatures.first
|
|
19
|
+
|
|
20
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
|
21
|
+
description: "filter",
|
|
22
|
+
detail: get_detail_string(signature)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
Interface::CompletionItem.new(
|
|
26
|
+
label: filter.name,
|
|
27
|
+
label_details: label_details,
|
|
28
|
+
filter_text: filter.name,
|
|
29
|
+
text_edit: Interface::TextEdit.new(
|
|
30
|
+
range: range,
|
|
31
|
+
new_text: get_method_string(filter.name, signature)
|
|
32
|
+
),
|
|
33
|
+
kind: Constant::CompletionItemKind::METHOD,
|
|
34
|
+
insert_text_format: Constant::InsertTextFormat::SNIPPET,
|
|
35
|
+
data: {
|
|
36
|
+
owner_name: "Object",
|
|
37
|
+
guessed_type: false,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_enum_values_completion_items(enum_obj, location)
|
|
44
|
+
enum_node = RubyLsp::Ree::ParsedDocumentBuilder.build_from_uri(enum_obj.uri, :enum)
|
|
45
|
+
|
|
46
|
+
class_name = enum_node.get_class_name
|
|
47
|
+
|
|
48
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
|
49
|
+
description: "from: #{class_name}",
|
|
50
|
+
detail: ''
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
range = Interface::Range.new(
|
|
54
|
+
start: Interface::Position.new(line: location.start_line - 1, character: location.end_column + 1),
|
|
55
|
+
end: Interface::Position.new(line: location.start_line - 1, character: location.end_column + 1),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
enum_node.values.map do |val|
|
|
59
|
+
Interface::CompletionItem.new(
|
|
60
|
+
label: val.name,
|
|
61
|
+
label_details: label_details,
|
|
62
|
+
filter_text: val.name,
|
|
63
|
+
text_edit: Interface::TextEdit.new(
|
|
64
|
+
range: range,
|
|
65
|
+
new_text: val.name
|
|
66
|
+
),
|
|
67
|
+
kind: Constant::CompletionItemKind::METHOD,
|
|
68
|
+
data: {
|
|
69
|
+
owner_name: "Object",
|
|
70
|
+
guessed_type: false,
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def get_class_name_completion_items(class_name_objects, parsed_doc, node, index, limit)
|
|
77
|
+
class_name_objects.take(limit).map do |full_class_name|
|
|
78
|
+
entry = index[full_class_name].first
|
|
79
|
+
class_name = full_class_name.split('::').last
|
|
80
|
+
|
|
81
|
+
package_name = package_name_from_uri(entry.uri)
|
|
82
|
+
|
|
83
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
|
84
|
+
description: "from: :#{package_name}",
|
|
85
|
+
detail: ""
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
Interface::CompletionItem.new(
|
|
89
|
+
label: class_name,
|
|
90
|
+
label_details: label_details,
|
|
91
|
+
filter_text: class_name,
|
|
92
|
+
text_edit: Interface::TextEdit.new(
|
|
93
|
+
range: range_from_location(node.location),
|
|
94
|
+
new_text: class_name,
|
|
95
|
+
),
|
|
96
|
+
kind: Constant::CompletionItemKind::CLASS,
|
|
97
|
+
additional_text_edits: get_additional_text_edits_for_constant(parsed_doc, class_name, package_name, entry)
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def get_ree_objects_completions_items(ree_objects, parsed_doc, node)
|
|
103
|
+
ree_objects.map do |ree_object|
|
|
104
|
+
ree_object_name = ree_object.name
|
|
105
|
+
package_name = package_name_from_uri(ree_object.uri)
|
|
106
|
+
signature = ree_object.signatures.first
|
|
107
|
+
ree_type = get_ree_type(ree_object)
|
|
108
|
+
|
|
109
|
+
label_details = Interface::CompletionItemLabelDetails.new(
|
|
110
|
+
description: "#{ree_type}, from: :#{package_name}",
|
|
111
|
+
detail: get_detail_string(signature)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
Interface::CompletionItem.new(
|
|
115
|
+
label: ree_object_name,
|
|
116
|
+
label_details: label_details,
|
|
117
|
+
filter_text: ree_object_name,
|
|
118
|
+
text_edit: Interface::TextEdit.new(
|
|
119
|
+
range: range_from_location(node.location),
|
|
120
|
+
new_text: get_method_string(ree_object_name, signature)
|
|
121
|
+
),
|
|
122
|
+
kind: Constant::CompletionItemKind::METHOD,
|
|
123
|
+
insert_text_format: Constant::InsertTextFormat::SNIPPET,
|
|
124
|
+
data: {
|
|
125
|
+
owner_name: "Object",
|
|
126
|
+
guessed_type: false,
|
|
127
|
+
},
|
|
128
|
+
additional_text_edits: get_additional_text_edits_for_method(parsed_doc, ree_object_name, package_name)
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def get_detail_string(signature)
|
|
134
|
+
return '' unless signature
|
|
135
|
+
|
|
136
|
+
"(#{get_parameters_string(signature)})"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def get_parameters_string(signature)
|
|
140
|
+
return '' unless signature
|
|
141
|
+
|
|
142
|
+
signature.parameters.map(&:decorated_name).join(', ')
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def get_method_string(fn_name, signature)
|
|
146
|
+
return fn_name unless signature
|
|
147
|
+
|
|
148
|
+
"#{fn_name}(#{get_parameters_placeholder(signature)})"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def get_parameters_placeholder(signature)
|
|
152
|
+
return '' unless signature
|
|
153
|
+
|
|
154
|
+
signature.parameters.to_enum.with_index.map do |signature_param, index|
|
|
155
|
+
case signature_param
|
|
156
|
+
when RubyIndexer::Entry::KeywordParameter, RubyIndexer::Entry::OptionalKeywordParameter
|
|
157
|
+
"#{signature_param.name}: ${#{index+1}:#{signature_param.name}}"
|
|
158
|
+
else
|
|
159
|
+
"${#{index+1}:#{signature_param.name}}"
|
|
160
|
+
end
|
|
161
|
+
end.join(', ')
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def get_additional_text_edits_for_constant(parsed_doc, class_name, package_name, entry)
|
|
165
|
+
if parsed_doc.includes_linked_constant?(class_name)
|
|
166
|
+
$stderr.puts("links already include #{class_name}")
|
|
167
|
+
return []
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
entry_uri = entry.uri.to_s
|
|
171
|
+
|
|
172
|
+
link_text = if parsed_doc.package_name == package_name
|
|
173
|
+
fn_name = File.basename(entry_uri, ".*")
|
|
174
|
+
"\s\slink :#{fn_name}, import: -> { #{class_name} }"
|
|
175
|
+
else
|
|
176
|
+
path = path_from_package(entry_uri)
|
|
177
|
+
"\s\slink \"#{path}\", import: -> { #{class_name} }"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
if parsed_doc.links_container_node
|
|
181
|
+
link_text = "\s\s" + link_text
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
new_text = "\n" + link_text
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if parsed_doc.has_blank_links_container?
|
|
188
|
+
new_text = "\sdo#{link_text}\n\s\send\n"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
range = get_range_for_fn_insert(parsed_doc, link_text)
|
|
192
|
+
|
|
193
|
+
[
|
|
194
|
+
Interface::TextEdit.new(
|
|
195
|
+
range: range,
|
|
196
|
+
new_text: new_text,
|
|
197
|
+
)
|
|
198
|
+
]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def get_additional_text_edits_for_method(parsed_doc, fn_name, package_name)
|
|
202
|
+
if parsed_doc.includes_linked_object?(fn_name)
|
|
203
|
+
$stderr.puts("links already include #{fn_name}")
|
|
204
|
+
return []
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
link_text = if parsed_doc.package_name == package_name
|
|
208
|
+
"\s\slink :#{fn_name}"
|
|
209
|
+
else
|
|
210
|
+
"\s\slink :#{fn_name}, from: :#{package_name}"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
if parsed_doc.links_container_node
|
|
214
|
+
link_text = "\s\s" + link_text
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
new_text = "\n" + link_text
|
|
218
|
+
|
|
219
|
+
if parsed_doc.has_blank_links_container?
|
|
220
|
+
new_text = "\sdo#{link_text}\n\s\send\n"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
range = get_range_for_fn_insert(parsed_doc, link_text)
|
|
224
|
+
|
|
225
|
+
[
|
|
226
|
+
Interface::TextEdit.new(
|
|
227
|
+
range: range,
|
|
228
|
+
new_text: new_text,
|
|
229
|
+
)
|
|
230
|
+
]
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
require_relative "ree_lsp_utils"
|
|
2
|
+
require_relative "parsing/parsed_link_node"
|
|
3
|
+
|
|
1
4
|
module RubyLsp
|
|
2
5
|
module Ree
|
|
3
6
|
class Definition
|
|
4
7
|
include Requests::Support::Common
|
|
8
|
+
include RubyLsp::Ree::ReeLspUtils
|
|
5
9
|
|
|
6
|
-
def initialize(response_builder, node_context, index, dispatcher)
|
|
10
|
+
def initialize(response_builder, node_context, index, dispatcher, uri)
|
|
7
11
|
@response_builder = response_builder
|
|
8
12
|
@node_context = node_context
|
|
9
13
|
@nesting = node_context.nesting
|
|
10
14
|
@index = index
|
|
15
|
+
@uri = uri
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
dispatcher.register(self, :on_call_node_enter)
|
|
17
|
+
dispatcher.register(self, :on_call_node_enter, :on_symbol_node_enter, :on_string_node_enter)
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def on_call_node_enter(node)
|
|
@@ -33,6 +37,47 @@ module RubyLsp
|
|
|
33
37
|
|
|
34
38
|
nil
|
|
35
39
|
end
|
|
40
|
+
|
|
41
|
+
def on_symbol_node_enter(node)
|
|
42
|
+
parent_node = @node_context.parent
|
|
43
|
+
return unless parent_node.name == :link
|
|
44
|
+
|
|
45
|
+
link_node = RubyLsp::Ree::ParsedLinkNode.new(parent_node, package_name_from_uri(@uri))
|
|
46
|
+
package_name = link_node.link_package_name
|
|
47
|
+
|
|
48
|
+
method_candidates = @index[node.unescaped]
|
|
49
|
+
return if !method_candidates || method_candidates.size == 0
|
|
50
|
+
|
|
51
|
+
method = method_candidates.detect{ package_name_from_uri(_1.uri) == package_name }
|
|
52
|
+
return unless method
|
|
53
|
+
|
|
54
|
+
@response_builder << Interface::Location.new(
|
|
55
|
+
uri: method.uri.to_s,
|
|
56
|
+
range: Interface::Range.new(
|
|
57
|
+
start: Interface::Position.new(line: 0, character: 0),
|
|
58
|
+
end: Interface::Position.new(line: 0, character: 0),
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def on_string_node_enter(node)
|
|
66
|
+
file_name = node.unescaped + ".rb"
|
|
67
|
+
local_path = Dir[File.join('**', file_name)].first
|
|
68
|
+
|
|
69
|
+
if local_path
|
|
70
|
+
@response_builder << Interface::Location.new(
|
|
71
|
+
uri: File.join(Dir.pwd, local_path),
|
|
72
|
+
range: Interface::Range.new(
|
|
73
|
+
start: Interface::Position.new(line: 0, character: 0),
|
|
74
|
+
end: Interface::Position.new(line: 0, character: 0),
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
36
81
|
end
|
|
37
82
|
end
|
|
38
83
|
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require_relative 'parsed_link_node'
|
|
2
|
+
|
|
3
|
+
class RubyLsp::Ree::ParsedDocument
|
|
4
|
+
include RubyLsp::Ree::ReeLspUtils
|
|
5
|
+
|
|
6
|
+
LINK_DSL_MODULE = 'Ree::LinkDSL'
|
|
7
|
+
|
|
8
|
+
attr_reader :ast, :package_name, :class_node, :fn_node, :fn_block_node, :class_includes,
|
|
9
|
+
:link_nodes, :values, :action_node, :action_block_node, :dao_node, :dao_block_node, :filters
|
|
10
|
+
|
|
11
|
+
def initialize(ast)
|
|
12
|
+
@ast = ast
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def links_container_node
|
|
16
|
+
@fn_node || @action_node || @dao_node
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def links_container_block_node
|
|
20
|
+
@fn_block_node || @action_block_node || @dao_block_node
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def includes_link_dsl?
|
|
24
|
+
@class_includes.any?{ _1.name == LINK_DSL_MODULE }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def includes_linked_constant?(const_name)
|
|
28
|
+
@link_nodes.map(&:imports).flatten.include?(const_name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def includes_linked_object?(obj_name)
|
|
32
|
+
@link_nodes.map(&:name).include?(obj_name)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def has_blank_links_container?
|
|
36
|
+
links_container_node && !links_container_block_node
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def set_package_name(package_name)
|
|
40
|
+
@package_name = package_name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse_class_node
|
|
44
|
+
@class_node ||= ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def parse_fn_node
|
|
48
|
+
return unless class_node
|
|
49
|
+
|
|
50
|
+
@fn_node ||= class_node.body.body.detect{ |node| node.name == :fn }
|
|
51
|
+
@fn_block_node = @fn_node&.block
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def parse_action_node
|
|
55
|
+
return unless class_node
|
|
56
|
+
|
|
57
|
+
@action_node ||= class_node.body.body.detect{ |node| node.name == :action }
|
|
58
|
+
@action_block_node = @action_node&.block
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def parse_dao_node
|
|
62
|
+
return unless class_node
|
|
63
|
+
|
|
64
|
+
@dao_node ||= class_node.body.body.detect{ |node| node.name == :dao }
|
|
65
|
+
@dao_block_node = @dao_node&.block
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_class_includes
|
|
69
|
+
return unless class_node
|
|
70
|
+
|
|
71
|
+
@class_includes ||= class_node.body.body.select{ _1.name == :include }.map do |class_include|
|
|
72
|
+
parent_name = class_include.arguments.arguments.first.parent.name.to_s
|
|
73
|
+
module_name = class_include.arguments.arguments.first.name
|
|
74
|
+
|
|
75
|
+
OpenStruct.new(
|
|
76
|
+
name: [parent_name, module_name].compact.join('::')
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def parse_links
|
|
82
|
+
return unless class_node
|
|
83
|
+
|
|
84
|
+
nodes = if links_container_node && links_container_block_node.body
|
|
85
|
+
links_container_block_node.body.body.select{ |node| node.name == :link }
|
|
86
|
+
elsif class_includes.any?{ _1.name == LINK_DSL_MODULE }
|
|
87
|
+
class_node.body.body.select{ |node| node.name == :link }
|
|
88
|
+
else
|
|
89
|
+
[]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@link_nodes = nodes.map do |link_node|
|
|
93
|
+
link_node = RubyLsp::Ree::ParsedLinkNode.new(link_node, package_name)
|
|
94
|
+
link_node.parse_imports
|
|
95
|
+
link_node
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def parse_values
|
|
100
|
+
return unless class_node
|
|
101
|
+
|
|
102
|
+
@values ||= class_node.body.body
|
|
103
|
+
.select{ _1.name == :val }
|
|
104
|
+
.map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def parse_filters
|
|
108
|
+
return unless class_node
|
|
109
|
+
|
|
110
|
+
@filters ||= class_node.body.body
|
|
111
|
+
.select{ _1.name == :filter }
|
|
112
|
+
.map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped, signatures: parse_filter_signature(_1)) }
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def parse_filter_signature(filter_node)
|
|
117
|
+
return [] unless filter_node
|
|
118
|
+
|
|
119
|
+
lambda_node = filter_node.arguments&.arguments[1]
|
|
120
|
+
return [] unless lambda_node
|
|
121
|
+
|
|
122
|
+
signature_params = signature_params_from_node(lambda_node.parameters.parameters)
|
|
123
|
+
[RubyIndexer::Entry::Signature.new(signature_params)]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def get_class_name
|
|
127
|
+
name_parts = [class_node.constant_path&.parent&.name, class_node.constant_path.name]
|
|
128
|
+
name_parts.compact.map(&:to_s).join('::')
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'prism'
|
|
2
|
+
require_relative 'parsed_document'
|
|
3
|
+
|
|
4
|
+
class RubyLsp::Ree::ParsedDocumentBuilder
|
|
5
|
+
extend RubyLsp::Ree::ReeLspUtils
|
|
6
|
+
|
|
7
|
+
def self.build_from_uri(uri, type = nil)
|
|
8
|
+
ast = Prism.parse_file(uri.path).value
|
|
9
|
+
document = build_document(ast, type)
|
|
10
|
+
|
|
11
|
+
document.set_package_name(package_name_from_uri(uri))
|
|
12
|
+
|
|
13
|
+
document
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.build_from_source(source, type = nil)
|
|
17
|
+
ast = Prism.parse(source).value
|
|
18
|
+
build_document(ast, type)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.build_document(ast, type)
|
|
22
|
+
case type
|
|
23
|
+
when :enum
|
|
24
|
+
build_enum_document(ast)
|
|
25
|
+
when :dao
|
|
26
|
+
build_dao_document(ast)
|
|
27
|
+
else
|
|
28
|
+
build_regular_document(ast)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.build_regular_document(ast)
|
|
33
|
+
document = RubyLsp::Ree::ParsedDocument.new(ast)
|
|
34
|
+
|
|
35
|
+
document.parse_class_node
|
|
36
|
+
document.parse_fn_node
|
|
37
|
+
document.parse_action_node
|
|
38
|
+
document.parse_class_includes
|
|
39
|
+
document.parse_links
|
|
40
|
+
|
|
41
|
+
document
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.build_enum_document(ast)
|
|
45
|
+
document = RubyLsp::Ree::ParsedDocument.new(ast)
|
|
46
|
+
|
|
47
|
+
document.parse_class_node
|
|
48
|
+
document.parse_values
|
|
49
|
+
|
|
50
|
+
document
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.build_dao_document(ast)
|
|
54
|
+
document = RubyLsp::Ree::ParsedDocument.new(ast)
|
|
55
|
+
|
|
56
|
+
document.parse_class_node
|
|
57
|
+
document.parse_filters
|
|
58
|
+
|
|
59
|
+
document
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'prism'
|
|
2
|
+
|
|
3
|
+
class RubyLsp::Ree::ParsedLinkNode
|
|
4
|
+
attr_reader :node, :document_package, :name, :imports
|
|
5
|
+
|
|
6
|
+
FROM_ARG_KEY = 'from'
|
|
7
|
+
IMPORT_ARG_KEY = 'import'
|
|
8
|
+
|
|
9
|
+
def initialize(node, document_package = nil)
|
|
10
|
+
@node = node
|
|
11
|
+
@document_package = document_package
|
|
12
|
+
@name = parse_name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def link_package_name
|
|
16
|
+
from_arg_value || document_package
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def location
|
|
20
|
+
@node.location
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def from_arg_value
|
|
24
|
+
@kw_args ||= @node.arguments.arguments.detect{ |arg| arg.is_a?(Prism::KeywordHashNode) }
|
|
25
|
+
return unless @kw_args
|
|
26
|
+
|
|
27
|
+
@from_param ||= @kw_args.elements.detect{ _1.key.unescaped == FROM_ARG_KEY }
|
|
28
|
+
return unless @from_param
|
|
29
|
+
|
|
30
|
+
@from_param.value.unescaped
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_name
|
|
34
|
+
name_arg_node = @node.arguments.arguments.first
|
|
35
|
+
|
|
36
|
+
case name_arg_node
|
|
37
|
+
when Prism::SymbolNode
|
|
38
|
+
name_arg_node.value
|
|
39
|
+
when Prism::StringNode
|
|
40
|
+
name_arg_node.unescaped
|
|
41
|
+
else
|
|
42
|
+
""
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse_imports
|
|
47
|
+
@imports ||= get_imports
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def get_imports
|
|
53
|
+
return [] if @node.arguments.arguments.size == 1
|
|
54
|
+
|
|
55
|
+
last_arg = @node.arguments.arguments.last
|
|
56
|
+
|
|
57
|
+
if last_arg.is_a?(Prism::KeywordHashNode)
|
|
58
|
+
import_arg = last_arg.elements.detect{ _1.key.unescaped == IMPORT_ARG_KEY }
|
|
59
|
+
return [] unless import_arg
|
|
60
|
+
|
|
61
|
+
[import_arg.value.body.body.first.name.to_s]
|
|
62
|
+
elsif last_arg.is_a?(Prism::LambdaNode)
|
|
63
|
+
[last_arg.body.body.first.name.to_s]
|
|
64
|
+
else
|
|
65
|
+
return []
|
|
66
|
+
end
|
|
67
|
+
rescue => e
|
|
68
|
+
$stderr.puts("can't parse imports: #{e.message}")
|
|
69
|
+
return []
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -17,25 +17,18 @@ module RubyLsp
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def sort_links(source)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if doc_info.link_nodes.size < doc_info.block_node.body.body.size
|
|
26
|
-
$stderr.puts("block contains not only link, don't sort")
|
|
27
|
-
return source
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
if doc_info.link_nodes.any?{ _1.location.start_line != _1.location.end_line }
|
|
20
|
+
parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
|
|
21
|
+
return source if parsed_doc.link_nodes.size == 0
|
|
22
|
+
|
|
23
|
+
if parsed_doc.link_nodes.any?{ _1.location.start_line != _1.location.end_line }
|
|
31
24
|
$stderr.puts("multiline link definitions, don't sort")
|
|
32
25
|
return source
|
|
33
26
|
end
|
|
34
27
|
|
|
35
28
|
# sort link nodes
|
|
36
|
-
sorted_link_nodes =
|
|
37
|
-
a_name = a.arguments.arguments.first
|
|
38
|
-
b_name = b.arguments.arguments.first
|
|
29
|
+
sorted_link_nodes = parsed_doc.link_nodes.sort{ |a, b|
|
|
30
|
+
a_name = a.node.arguments.arguments.first
|
|
31
|
+
b_name = b.node.arguments.arguments.first
|
|
39
32
|
|
|
40
33
|
if a_name.is_a?(Prism::SymbolNode) && !b_name.is_a?(Prism::SymbolNode)
|
|
41
34
|
-1
|
|
@@ -47,12 +40,12 @@ module RubyLsp
|
|
|
47
40
|
}
|
|
48
41
|
|
|
49
42
|
# check if no re-order
|
|
50
|
-
if
|
|
43
|
+
if parsed_doc.link_nodes.map{ _1.node.arguments.arguments.first.unescaped } == sorted_link_nodes.map{ _1.node.arguments.arguments.first.unescaped }
|
|
51
44
|
return source
|
|
52
45
|
end
|
|
53
46
|
|
|
54
47
|
# insert nodes to source
|
|
55
|
-
link_lines =
|
|
48
|
+
link_lines = parsed_doc.link_nodes.map{ _1.location.start_line }
|
|
56
49
|
|
|
57
50
|
source_lines = source.lines
|
|
58
51
|
|
|
@@ -1,42 +1,37 @@
|
|
|
1
|
+
require 'prism'
|
|
2
|
+
require_relative "ree_lsp_utils"
|
|
3
|
+
|
|
1
4
|
module RubyLsp
|
|
2
5
|
module Ree
|
|
3
6
|
class ReeIndexingEnhancement < RubyIndexer::Enhancement
|
|
7
|
+
include RubyLsp::Ree::ReeLspUtils
|
|
8
|
+
|
|
9
|
+
REE_INDEXED_OBJECTS = [:fn, :enum, :action, :dao]
|
|
10
|
+
|
|
4
11
|
def on_call_node_enter(node)
|
|
5
12
|
return unless @listener.current_owner
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
return unless node.name == :fn
|
|
14
|
+
return unless REE_INDEXED_OBJECTS.include?(node.name)
|
|
9
15
|
return unless node.arguments
|
|
16
|
+
return unless node.arguments.child_nodes.first.is_a?(Prism::SymbolNode)
|
|
17
|
+
|
|
18
|
+
obj_name = node.arguments.child_nodes.first.unescaped
|
|
19
|
+
return unless current_filename == obj_name
|
|
10
20
|
|
|
11
|
-
# index = @listener.instance_variable_get(:@index)
|
|
12
|
-
|
|
13
21
|
location = node.location
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
comments = "ree_object\nsome_documentation"
|
|
17
|
-
|
|
18
|
-
# visibility = RubyIndexer::Entry::Visibility::PUBLIC
|
|
19
|
-
# owner = index['Object'].first
|
|
20
|
-
|
|
21
|
-
# index.add(RubyIndexer::Entry::Method.new(
|
|
22
|
-
# fn_name,
|
|
23
|
-
# @listener.instance_variable_get(:@uri),
|
|
24
|
-
# location,
|
|
25
|
-
# location,
|
|
26
|
-
# comments,
|
|
27
|
-
# signatures,
|
|
28
|
-
# visibility,
|
|
29
|
-
# owner,
|
|
30
|
-
# ))
|
|
22
|
+
signatures = parse_signatures(obj_name)
|
|
23
|
+
comments = "ree_object\ntype: :#{node.name}"
|
|
31
24
|
|
|
32
25
|
@listener.add_method(
|
|
33
|
-
|
|
26
|
+
obj_name,
|
|
34
27
|
location,
|
|
35
28
|
signatures,
|
|
36
29
|
comments: comments
|
|
37
30
|
)
|
|
38
31
|
end
|
|
39
32
|
|
|
33
|
+
private
|
|
34
|
+
|
|
40
35
|
def parse_signatures(fn_name)
|
|
41
36
|
uri = @listener.instance_variable_get(:@uri)
|
|
42
37
|
ast = Prism.parse_file(uri.path).value
|
|
@@ -47,10 +42,15 @@ module RubyLsp
|
|
|
47
42
|
call_node = class_node.body.body.detect{ |node| node.name == :call }
|
|
48
43
|
return [] unless call_node
|
|
49
44
|
|
|
50
|
-
signature_params =
|
|
51
|
-
|
|
45
|
+
signature_params = signature_params_from_node(call_node.parameters)
|
|
46
|
+
|
|
52
47
|
[RubyIndexer::Entry::Signature.new(signature_params)]
|
|
53
48
|
end
|
|
49
|
+
|
|
50
|
+
def current_filename
|
|
51
|
+
uri = @listener.instance_variable_get(:@uri)
|
|
52
|
+
File.basename(uri.path, '.rb')
|
|
53
|
+
end
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module RubyLsp
|
|
2
2
|
module Ree
|
|
3
3
|
module ReeLspUtils
|
|
4
|
+
Entry = RubyIndexer::Entry
|
|
5
|
+
|
|
4
6
|
def package_name_from_uri(uri)
|
|
5
7
|
uri_parts = uri.to_s.split('/')
|
|
6
8
|
package_index = uri_parts.find_index('package') + 1
|
|
@@ -13,66 +15,29 @@ module RubyLsp
|
|
|
13
15
|
uri_parts.drop(pack_folder_index+1).join('/')
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
result.package_name = package_name_from_uri(uri)
|
|
21
|
-
|
|
22
|
-
result
|
|
23
|
-
end
|
|
18
|
+
def get_ree_type(ree_object)
|
|
19
|
+
type_comment = ree_object.comments.to_s.lines[1]
|
|
20
|
+
return unless type_comment
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
ast = Prism.parse(source).value
|
|
27
|
-
parse_document(ast)
|
|
22
|
+
type_comment.split(' ').last
|
|
28
23
|
end
|
|
29
24
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
block_node = fn_node.block
|
|
34
|
-
|
|
35
|
-
link_nodes = if block_node && block_node.body
|
|
36
|
-
block_node.body.body.select{ |node| node.name == :link }
|
|
37
|
-
else
|
|
38
|
-
[]
|
|
39
|
-
end
|
|
25
|
+
def get_range_for_fn_insert(parsed_doc, link_text)
|
|
26
|
+
fn_line = nil
|
|
27
|
+
position = nil
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
if parsed_doc.links_container_node
|
|
30
|
+
links_container_node = parsed_doc.links_container_node
|
|
31
|
+
fn_line = links_container_node.location.start_line
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
name_arg_node.value
|
|
47
|
-
when Prism::StringNode
|
|
48
|
-
name_arg_node.unescaped
|
|
33
|
+
position = if parsed_doc.links_container_block_node
|
|
34
|
+
parsed_doc.links_container_block_node.opening_loc.end_column + 1
|
|
49
35
|
else
|
|
50
|
-
|
|
36
|
+
links_container_node.arguments.location.end_column + 1
|
|
51
37
|
end
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
imports: parse_link_node_imports(link_node)
|
|
56
|
-
)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
return OpenStruct.new(
|
|
60
|
-
ast: ast,
|
|
61
|
-
class_node: class_node,
|
|
62
|
-
fn_node: fn_node,
|
|
63
|
-
block_node: block_node,
|
|
64
|
-
link_nodes: link_nodes,
|
|
65
|
-
linked_objects: linked_objects
|
|
66
|
-
)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def get_range_for_fn_insert(doc_info, link_text)
|
|
70
|
-
fn_line = doc_info.fn_node.location.start_line
|
|
71
|
-
|
|
72
|
-
position = if doc_info.block_node
|
|
73
|
-
doc_info.block_node.opening_loc.end_column + 1
|
|
74
|
-
else
|
|
75
|
-
doc_info.fn_node.arguments.location.end_column + 1
|
|
38
|
+
elsif parsed_doc.includes_link_dsl?
|
|
39
|
+
fn_line = parsed_doc.link_nodes.first.location.start_line - 1
|
|
40
|
+
position = parsed_doc.link_nodes.first.location.start_column
|
|
76
41
|
end
|
|
77
42
|
|
|
78
43
|
Interface::Range.new(
|
|
@@ -81,24 +46,94 @@ module RubyLsp
|
|
|
81
46
|
)
|
|
82
47
|
end
|
|
83
48
|
|
|
84
|
-
def parse_link_node_imports(node)
|
|
85
|
-
return [] if node.arguments.arguments.size == 1
|
|
86
|
-
|
|
87
|
-
last_arg = node.arguments.arguments.last
|
|
88
49
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
50
|
+
# params(parameters_node: Prism::ParametersNode).returns(Array[Entry::Parameter])
|
|
51
|
+
# copied from ruby-lsp DeclarationListener#list_params
|
|
52
|
+
def signature_params_from_node(parameters_node)
|
|
53
|
+
return [] unless parameters_node
|
|
54
|
+
|
|
55
|
+
parameters = []
|
|
56
|
+
|
|
57
|
+
parameters_node.requireds.each do |required|
|
|
58
|
+
name = parameter_name(required)
|
|
59
|
+
next unless name
|
|
60
|
+
|
|
61
|
+
parameters << Entry::RequiredParameter.new(name: name)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
parameters_node.optionals.each do |optional|
|
|
65
|
+
name = parameter_name(optional)
|
|
66
|
+
next unless name
|
|
67
|
+
|
|
68
|
+
parameters << Entry::OptionalParameter.new(name: name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
rest = parameters_node.rest
|
|
72
|
+
|
|
73
|
+
if rest.is_a?(Prism::RestParameterNode)
|
|
74
|
+
rest_name = rest.name || Entry::RestParameter::DEFAULT_NAME
|
|
75
|
+
parameters << Entry::RestParameter.new(name: rest_name)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
parameters_node.keywords.each do |keyword|
|
|
79
|
+
name = parameter_name(keyword)
|
|
80
|
+
next unless name
|
|
81
|
+
|
|
82
|
+
case keyword
|
|
83
|
+
when Prism::RequiredKeywordParameterNode
|
|
84
|
+
parameters << Entry::KeywordParameter.new(name: name)
|
|
85
|
+
when Prism::OptionalKeywordParameterNode
|
|
86
|
+
parameters << Entry::OptionalKeywordParameter.new(name: name)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
keyword_rest = parameters_node.keyword_rest
|
|
91
|
+
|
|
92
|
+
case keyword_rest
|
|
93
|
+
when Prism::KeywordRestParameterNode
|
|
94
|
+
keyword_rest_name = parameter_name(keyword_rest) || Entry::KeywordRestParameter::DEFAULT_NAME
|
|
95
|
+
parameters << Entry::KeywordRestParameter.new(name: keyword_rest_name)
|
|
96
|
+
when Prism::ForwardingParameterNode
|
|
97
|
+
parameters << Entry::ForwardingParameter.new
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
parameters_node.posts.each do |post|
|
|
101
|
+
name = parameter_name(post)
|
|
102
|
+
next unless name
|
|
103
|
+
|
|
104
|
+
parameters << Entry::RequiredParameter.new(name: name)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
block = parameters_node.block
|
|
108
|
+
parameters << Entry::BlockParameter.new(name: block.name || Entry::BlockParameter::DEFAULT_NAME) if block
|
|
109
|
+
|
|
110
|
+
parameters
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# params(node: Prism::Node).returns(Symbol)
|
|
114
|
+
# copied from ruby-lsp DeclarationListener#parameter_name
|
|
115
|
+
def parameter_name(node)
|
|
116
|
+
case node
|
|
117
|
+
when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
|
|
118
|
+
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode,
|
|
119
|
+
Prism::RestParameterNode, Prism::KeywordRestParameterNode
|
|
120
|
+
node.name
|
|
121
|
+
when Prism::MultiTargetNode
|
|
122
|
+
names = node.lefts.map { |parameter_node| parameter_name(parameter_node) }
|
|
123
|
+
|
|
124
|
+
rest = node.rest
|
|
125
|
+
if rest.is_a?(Prism::SplatNode)
|
|
126
|
+
name = rest.expression&.slice
|
|
127
|
+
names << (rest.operator == "*" ? "*#{name}".to_sym : name&.to_sym)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
names << nil if rest.is_a?(Prism::ImplicitRestNode)
|
|
131
|
+
|
|
132
|
+
names.concat(node.rights.map { |parameter_node| parameter_name(parameter_node) })
|
|
92
133
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
[last_arg.body.body.first.name.to_s]
|
|
96
|
-
else
|
|
97
|
-
return []
|
|
134
|
+
names_with_commas = names.join(", ")
|
|
135
|
+
:"(#{names_with_commas})"
|
|
98
136
|
end
|
|
99
|
-
rescue => e
|
|
100
|
-
$stderr.puts("can't parse imports: #{e.message}")
|
|
101
|
-
return []
|
|
102
137
|
end
|
|
103
138
|
end
|
|
104
139
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module RubyLsp
|
|
2
|
+
module Ree
|
|
3
|
+
class ReeObjectFinder
|
|
4
|
+
MAX_LIMIT = 100
|
|
5
|
+
|
|
6
|
+
REE_OBJECT_STRING = 'ree_object'
|
|
7
|
+
ENUM_TYPE_STRING = 'type: :enum'
|
|
8
|
+
DAO_TYPE_STRING = 'type: :dao'
|
|
9
|
+
|
|
10
|
+
def self.search_objects(index, name, limit)
|
|
11
|
+
index.prefix_search(name)
|
|
12
|
+
.take(MAX_LIMIT).map(&:first)
|
|
13
|
+
.select{ _1.comments }
|
|
14
|
+
.select{ _1.comments.to_s.lines.first&.chomp == REE_OBJECT_STRING }
|
|
15
|
+
.take(limit)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.find_enum(index, name)
|
|
19
|
+
objects_by_name = index[name]
|
|
20
|
+
return unless objects_by_name
|
|
21
|
+
|
|
22
|
+
objects_by_name.detect{ _1.comments.lines[1]&.chomp == ENUM_TYPE_STRING }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.find_dao(index, name)
|
|
26
|
+
objects_by_name = index[name]
|
|
27
|
+
return unless objects_by_name
|
|
28
|
+
|
|
29
|
+
objects_by_name.detect{ _1.comments.lines[1]&.chomp == DAO_TYPE_STRING }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
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.1
|
|
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-02-10 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:
|
|
@@ -20,15 +20,21 @@ files:
|
|
|
20
20
|
- CHANGELOG.md
|
|
21
21
|
- CODE_OF_CONDUCT.md
|
|
22
22
|
- Gemfile
|
|
23
|
+
- Gemfile.lock
|
|
23
24
|
- LICENSE.txt
|
|
24
25
|
- README.md
|
|
25
26
|
- Rakefile
|
|
26
27
|
- lib/ruby_lsp/ruby_lsp_ree/addon.rb
|
|
27
28
|
- lib/ruby_lsp/ruby_lsp_ree/completion.rb
|
|
29
|
+
- lib/ruby_lsp/ruby_lsp_ree/completion_utils.rb
|
|
28
30
|
- lib/ruby_lsp/ruby_lsp_ree/definition.rb
|
|
31
|
+
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document.rb
|
|
32
|
+
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_document_builder.rb
|
|
33
|
+
- lib/ruby_lsp/ruby_lsp_ree/parsing/parsed_link_node.rb
|
|
29
34
|
- lib/ruby_lsp/ruby_lsp_ree/ree_formatter.rb
|
|
30
35
|
- lib/ruby_lsp/ruby_lsp_ree/ree_indexing_enhancement.rb
|
|
31
36
|
- lib/ruby_lsp/ruby_lsp_ree/ree_lsp_utils.rb
|
|
37
|
+
- lib/ruby_lsp/ruby_lsp_ree/ree_object_finder.rb
|
|
32
38
|
- lib/ruby_lsp_ree.rb
|
|
33
39
|
- lib/ruby_lsp_ree/version.rb
|
|
34
40
|
- ruby-lsp-ree.gemspec
|