ruby-lsp-ree 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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