ruby-lsp-ree 0.1.0 → 0.1.2

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.
@@ -0,0 +1,150 @@
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
+ :bean_node, :bean_block_node, :bean_methods
11
+
12
+ def initialize(ast)
13
+ @ast = ast
14
+ end
15
+
16
+ def links_container_node
17
+ @fn_node || @action_node || @dao_node || @bean_node
18
+ end
19
+
20
+ def links_container_block_node
21
+ @fn_block_node || @action_block_node || @dao_block_node || @bean_block_node
22
+ end
23
+
24
+ def includes_link_dsl?
25
+ @class_includes.any?{ _1.name == LINK_DSL_MODULE }
26
+ end
27
+
28
+ def includes_linked_constant?(const_name)
29
+ @link_nodes.map(&:imports).flatten.include?(const_name)
30
+ end
31
+
32
+ def includes_linked_object?(obj_name)
33
+ @link_nodes.map(&:name).include?(obj_name)
34
+ end
35
+
36
+ def has_blank_links_container?
37
+ links_container_node && !links_container_block_node
38
+ end
39
+
40
+ def set_package_name(package_name)
41
+ @package_name = package_name
42
+ end
43
+
44
+ def parse_class_node
45
+ @class_node ||= ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
46
+ end
47
+
48
+ def parse_fn_node
49
+ return unless class_node
50
+
51
+ @fn_node ||= class_node.body.body.detect{ |node| node.name == :fn }
52
+ @fn_block_node = @fn_node&.block
53
+ end
54
+
55
+ def parse_action_node
56
+ return unless class_node
57
+
58
+ @action_node ||= class_node.body.body.detect{ |node| node.name == :action }
59
+ @action_block_node = @action_node&.block
60
+ end
61
+
62
+ def parse_dao_node
63
+ return unless class_node
64
+
65
+ @dao_node ||= class_node.body.body.detect{ |node| node.name == :dao }
66
+ @dao_block_node = @dao_node&.block
67
+ end
68
+
69
+ def parse_bean_node
70
+ return unless class_node
71
+
72
+ @bean_node ||= class_node.body.body.detect{ |node| node.name == :bean }
73
+ @bean_block_node = @bean_node&.block
74
+ end
75
+
76
+ def parse_class_includes
77
+ return unless class_node
78
+
79
+ @class_includes ||= class_node.body.body.select{ _1.name == :include }.map do |class_include|
80
+ parent_name = class_include.arguments.arguments.first.parent.name.to_s
81
+ module_name = class_include.arguments.arguments.first.name
82
+
83
+ OpenStruct.new(
84
+ name: [parent_name, module_name].compact.join('::')
85
+ )
86
+ end
87
+ end
88
+
89
+ def parse_links
90
+ return unless class_node
91
+
92
+ nodes = if links_container_node && links_container_block_node.body
93
+ links_container_block_node.body.body.select{ |node| node.name == :link }
94
+ elsif class_includes.any?{ _1.name == LINK_DSL_MODULE }
95
+ class_node.body.body.select{ |node| node.name == :link }
96
+ else
97
+ []
98
+ end
99
+
100
+ @link_nodes = nodes.map do |link_node|
101
+ link_node = RubyLsp::Ree::ParsedLinkNode.new(link_node, package_name)
102
+ link_node.parse_imports
103
+ link_node
104
+ end
105
+ end
106
+
107
+ def parse_values
108
+ return unless class_node
109
+
110
+ @values ||= class_node.body.body
111
+ .select{ _1.name == :val }
112
+ .map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped) }
113
+ end
114
+
115
+ def parse_filters
116
+ return unless class_node
117
+
118
+ @filters ||= class_node.body.body
119
+ .select{ _1.name == :filter }
120
+ .map{ OpenStruct.new(name: _1.arguments.arguments.first.unescaped, signatures: parse_filter_signature(_1)) }
121
+
122
+ end
123
+
124
+ def parse_bean_methods
125
+ return unless class_node
126
+
127
+ @bean_methods ||= class_node.body.body
128
+ .select{ _1.is_a?(Prism::DefNode) }
129
+ .map{ OpenStruct.new(name: _1.name.to_s, signatures: parse_signatures_from_params(_1.parameters)) }
130
+ end
131
+
132
+ def parse_filter_signature(filter_node)
133
+ return [] unless filter_node
134
+
135
+ lambda_node = filter_node.arguments&.arguments[1]
136
+ return [] unless lambda_node
137
+
138
+ parse_signatures_from_params(lambda_node.parameters.parameters)
139
+ end
140
+
141
+ def parse_signatures_from_params(parameters)
142
+ signature_params = signature_params_from_node(parameters)
143
+ [RubyIndexer::Entry::Signature.new(signature_params)]
144
+ end
145
+
146
+ def get_class_name
147
+ name_parts = [class_node.constant_path&.parent&.name, class_node.constant_path.name]
148
+ name_parts.compact.map(&:to_s).join('::')
149
+ end
150
+ end
@@ -0,0 +1,82 @@
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_ast(ast, uri, type = nil)
17
+ document = build_document(ast, type)
18
+
19
+ document.set_package_name(package_name_from_uri(uri))
20
+
21
+ document
22
+ end
23
+
24
+ def self.build_from_source(source, type = nil)
25
+ ast = Prism.parse(source).value
26
+ build_document(ast, type)
27
+ end
28
+
29
+ def self.build_document(ast, type)
30
+ case type
31
+ when :enum
32
+ build_enum_document(ast)
33
+ when :dao
34
+ build_dao_document(ast)
35
+ when :bean
36
+ build_bean_document(ast)
37
+ else
38
+ build_regular_document(ast)
39
+ end
40
+ end
41
+
42
+ def self.build_regular_document(ast)
43
+ document = RubyLsp::Ree::ParsedDocument.new(ast)
44
+
45
+ document.parse_class_node
46
+ document.parse_fn_node
47
+ document.parse_action_node
48
+ document.parse_bean_node
49
+ document.parse_dao_node
50
+ document.parse_class_includes
51
+ document.parse_links
52
+
53
+ document
54
+ end
55
+
56
+ def self.build_enum_document(ast)
57
+ document = RubyLsp::Ree::ParsedDocument.new(ast)
58
+
59
+ document.parse_class_node
60
+ document.parse_values
61
+
62
+ document
63
+ end
64
+
65
+ def self.build_dao_document(ast)
66
+ document = RubyLsp::Ree::ParsedDocument.new(ast)
67
+
68
+ document.parse_class_node
69
+ document.parse_filters
70
+
71
+ document
72
+ end
73
+
74
+ def self.build_bean_document(ast)
75
+ document = RubyLsp::Ree::ParsedDocument.new(ast)
76
+
77
+ document.parse_class_node
78
+ document.parse_bean_methods
79
+
80
+ document
81
+ end
82
+ end
@@ -0,0 +1,94 @@
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 name_arg_node
34
+ @node.arguments.arguments.first
35
+ end
36
+
37
+ def link_type
38
+ return @link_type if @link_type
39
+
40
+ @link_type = case name_arg_node
41
+ when Prism::SymbolNode
42
+ :object_name
43
+ when Prism::StringNode
44
+ :file_path
45
+ else
46
+ nil
47
+ end
48
+ end
49
+
50
+ def file_path_type?
51
+ link_type == :file_path
52
+ end
53
+
54
+ def object_name_type?
55
+ link_type == :object_name
56
+ end
57
+
58
+ def parse_name
59
+ case name_arg_node
60
+ when Prism::SymbolNode
61
+ name_arg_node.value
62
+ when Prism::StringNode
63
+ name_arg_node.unescaped
64
+ else
65
+ ""
66
+ end
67
+ end
68
+
69
+ def parse_imports
70
+ @imports ||= get_imports
71
+ end
72
+
73
+ private
74
+
75
+ def get_imports
76
+ return [] if @node.arguments.arguments.size == 1
77
+
78
+ last_arg = @node.arguments.arguments.last
79
+
80
+ if last_arg.is_a?(Prism::KeywordHashNode)
81
+ import_arg = last_arg.elements.detect{ _1.key.unescaped == IMPORT_ARG_KEY }
82
+ return [] unless import_arg
83
+
84
+ [import_arg.value.body.body.first.name.to_s]
85
+ elsif last_arg.is_a?(Prism::LambdaNode)
86
+ [last_arg.body.body.first.name.to_s]
87
+ else
88
+ return []
89
+ end
90
+ rescue => e
91
+ $stderr.puts("can't parse imports: #{e.message}")
92
+ return []
93
+ end
94
+ end
@@ -3,40 +3,31 @@ module RubyLsp
3
3
  class ReeFormatter
4
4
  include RubyLsp::Requests::Support::Formatter
5
5
  include RubyLsp::Ree::ReeLspUtils
6
-
6
+
7
7
  def initialize
8
8
  end
9
-
9
+
10
10
  def run_formatting(uri, document)
11
- $stderr.puts("run_formating")
12
-
13
11
  source = document.source
14
12
  sort_links(source)
15
13
  end
16
-
14
+
17
15
  private
18
-
16
+
19
17
  def sort_links(source)
20
- doc_info = parse_document_from_source(source)
21
-
22
- return source unless doc_info.fn_node
23
- return source if doc_info.fn_node && !doc_info.block_node
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 }
18
+ parsed_doc = RubyLsp::Ree::ParsedDocumentBuilder.build_from_source(source)
19
+ return source if !parsed_doc.link_nodes&.any?
20
+
21
+ if parsed_doc.link_nodes.any?{ _1.location.start_line != _1.location.end_line }
31
22
  $stderr.puts("multiline link definitions, don't sort")
32
23
  return source
33
24
  end
34
-
25
+
35
26
  # sort link nodes
36
- sorted_link_nodes = doc_info.link_nodes.sort{ |a, b|
37
- a_name = a.arguments.arguments.first
38
- b_name = b.arguments.arguments.first
39
-
27
+ sorted_link_nodes = parsed_doc.link_nodes.sort{ |a, b|
28
+ a_name = a.node.arguments.arguments.first
29
+ b_name = b.node.arguments.arguments.first
30
+
40
31
  if a_name.is_a?(Prism::SymbolNode) && !b_name.is_a?(Prism::SymbolNode)
41
32
  -1
42
33
  elsif b_name.is_a?(Prism::SymbolNode) && !a_name.is_a?(Prism::SymbolNode)
@@ -45,25 +36,25 @@ module RubyLsp
45
36
  a_name.unescaped <=> b_name.unescaped
46
37
  end
47
38
  }
48
-
39
+
49
40
  # check if no re-order
50
- if doc_info.link_nodes.map{ _1.arguments.arguments.first.unescaped } == sorted_link_nodes.map{ _1.arguments.arguments.first.unescaped }
41
+ if parsed_doc.link_nodes.map{ _1.node.arguments.arguments.first.unescaped } == sorted_link_nodes.map{ _1.node.arguments.arguments.first.unescaped }
51
42
  return source
52
43
  end
53
-
44
+
54
45
  # insert nodes to source
55
- link_lines = doc_info.link_nodes.map{ _1.location.start_line }
56
-
46
+ link_lines = parsed_doc.link_nodes.map{ _1.location.start_line }
47
+
57
48
  source_lines = source.lines
58
-
49
+
59
50
  sorted_lines = sorted_link_nodes.map do |sorted_link|
60
51
  source_lines[sorted_link.location.start_line - 1]
61
52
  end
62
-
53
+
63
54
  link_lines.each_with_index do |link_line, index|
64
55
  source_lines[link_line - 1] = sorted_lines[index]
65
56
  end
66
-
57
+
67
58
  source_lines.join()
68
59
  end
69
60
  end
@@ -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, :bean]
10
+
4
11
  def on_call_node_enter(node)
5
12
  return unless @listener.current_owner
6
13
 
7
- # Return early unless the method call is the one we want to handle
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
- fn_name = node.arguments.child_nodes.first.unescaped
15
- signatures = parse_signatures(fn_name)
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
- fn_name,
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
@@ -44,13 +39,18 @@ module RubyLsp
44
39
  class_node = ast.statements.body.detect{ |node| node.is_a?(Prism::ClassNode) }
45
40
  return [] unless class_node
46
41
 
47
- call_node = class_node.body.body.detect{ |node| node.name == :call }
42
+ call_node = class_node.body.body.detect{ |node| node.respond_to?(:name) && node.name == :call }
48
43
  return [] unless call_node
49
-
50
- signature_params = @listener.send(:list_params, call_node.parameters)
44
+
45
+ signature_params = signature_params_from_node(call_node.parameters)
51
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