ruby-lsp-rails-factory-bot 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28d0eed4aef05099e03470f4aae508ad687ce937db3d080289e5acf44d71b4a1
4
- data.tar.gz: cfa4ac0edb00841d67a6052a90ecea3c0649420fbe7acc00dcf0d1df0aca8651
3
+ metadata.gz: 01d1b4f936612100587a05ace6ca2c46fd654484c7d1125245e3c3afd7ce2fb2
4
+ data.tar.gz: cd15946860a27dbaa31ba14b8f429c5afb7f4bd21a9eeb1aca0f01411006bebf
5
5
  SHA512:
6
- metadata.gz: 69904e9e37c3a82cbc7f6c098d8c0c72fce15ae5e4c6a6cbf4a596d60b178be7e1577411776c191605ba0d359aef7c685dffc30ba756111f006b9b3873a44489
7
- data.tar.gz: bbaf4f6cad8a9e394b423c6c49a029fcd768543473e1165676780813e588de3d5518020bd1f0c9b0d1a11cea7d2cd2502e18ad5b0b4e8b90da6700774e639032
6
+ metadata.gz: e09bd8301b1003c30ba6efdba15c6c945f4f0ca0adbda81e8f290bcf4a0dff480c1c482e28499a5477d80a656a358018788a6a5022441da8698c940272089e2c
7
+ data.tar.gz: 3b145ca533d21b291cd545dde273f4c6f333ea255beca536c01f44e488b02a813d4beab114a4d4269b3c0add785b020df3cc875777d28183b2f338a7005a38df
data/README.md CHANGED
@@ -20,6 +20,24 @@ Hover over an attribute or factory definition
20
20
 
21
21
  ![lsp-factory-bot-hover](https://github.com/user-attachments/assets/6f570288-3cf3-4d12-acf9-71c86e834cd8)
22
22
 
23
+ Receive completion suggestions as you type
24
+
25
+ ![lsp-factory-bot-completion](https://github.com/user-attachments/assets/4255a86a-8f36-4de2-8d10-8cb5a3f49e50)
26
+
27
+ ### Supports
28
+
29
+ | | Hover | Completion | Go to definition |
30
+ | ------------- |-------------| -----| ----|
31
+ | Factory name | ✅ | ⭕️ | ❌ |
32
+ | Trait | ✅ | ⭕️ | ❌ |
33
+ | Attribute | ✅ | ✅ | ❌ |
34
+
35
+ Notes:
36
+
37
+ - The extension has "understanding" of factory/trait completion items, but due to limitations on when ruby-lsp displays the completion suggestions, they aren't visible for Symbols (eg. factory/trait names) :/ though they happen to be visible for symbols in Hash/Kw notation (ie with `:` after - `key: ...`)
38
+ - Go to definition not supported yet but might come soon
39
+
40
+
23
41
  ## Development
24
42
 
25
43
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -27,15 +27,10 @@ module RubyLsp
27
27
  FactoryBot::ADDON_NAME
28
28
  end
29
29
 
30
- # def create_completion_listener(response_builder, node_context, dispatcher, uri)
31
- # path = uri.to_standardized_path
32
- # return unless path&.end_with?("_test.rb") || path&.end_with?("_spec.rb")
33
- # return unless factory_bot_call_args?(node_context)
34
-
35
- # ensure_addon_registered!
36
-
37
- # Completion.new(response_builder, node_context, dispatcher, runner_client)
38
- # end
30
+ def create_completion_listener(response_builder, node_context, dispatcher, _uri)
31
+ register_addon!
32
+ Completion.new(response_builder, node_context, dispatcher, runner_client)
33
+ end
39
34
 
40
35
  # TODO: need URI param to be able to filter by file name
41
36
  def create_hover_listener(response_builder, node_context, dispatcher)
@@ -75,18 +70,6 @@ module RubyLsp
75
70
  @rails_addon.rails_runner_client
76
71
  end
77
72
 
78
- FACTORY_BOT_METHODS = %i[
79
- create
80
- build
81
- build_stubbed
82
- attributes_for
83
- ].flat_map { |attr| [attr, :"#{attr}_list", :"#{attr}_pair"] }.freeze
84
-
85
- def factory_bot_call_args?(node_context)
86
- node_context.call_node && FACTORY_BOT_METHODS.include?(node_context.call_node.name)
87
- true
88
- end
89
-
90
73
  def log(msg)
91
74
  return if !@outgoing_queue || @outgoing_queue.closed?
92
75
 
@@ -8,11 +8,7 @@ module RubyLsp
8
8
  module Rails
9
9
  module FactoryBot
10
10
  # The listener that is created when the user requests autocomplete at the relevant time.
11
- #
12
- # NOTE: autocompletion is only triggered on certain node types - almost exclusively call nodes
13
- # and constants IIRC, so you cannot currently receive autocomplete options for symbols (eg.
14
- # factory or trait names) :/
15
- class Completion
11
+ class Completion # rubocop:disable Metrics/ClassLength
16
12
  include RubyLsp::Requests::Support::Common
17
13
 
18
14
  def initialize(response_builder, node_context, dispatcher, server_client)
@@ -24,10 +20,12 @@ module RubyLsp
24
20
  end
25
21
 
26
22
  def on_call_node_enter(node)
27
- return unless FactoryBot::FACTORY_BOT_METHODS.include?(node.name) ||
28
- FactoryBot::FACTORY_BOT_METHODS.include?(@node_context.parent.name)
23
+ call_node = @node_context.call_node
24
+ return unless call_node
29
25
 
30
- process_arguments_pattern(node, node.arguments)
26
+ return unless FactoryBot::FACTORY_BOT_METHODS.include?(call_node.name)
27
+
28
+ process_arguments_pattern(node, call_node.arguments&.arguments)
31
29
  rescue StandardError => e
32
30
  $stderr.write(e, e.backtrace)
33
31
  end
@@ -40,26 +38,50 @@ module RubyLsp
40
38
  handle_factory(factory_name_node, node_string_value(factory_name_node))
41
39
 
42
40
  in [Prism::SymbolNode => factory_name_node, *, Prism::SymbolNode => trait_node]
43
- handle_trait(node_string_value(factory_name_node), node, node_string_value(trait_node))
41
+ already_used_traits = gather_already_used_traits(arguments)
42
+ handle_trait(
43
+ node_string_value(factory_name_node), node, already_used_traits, node_string_value(trait_node),
44
+ )
44
45
 
45
46
  in [Prism::SymbolNode => _factory_name_node, *, Prism::KeywordHashNode => _kw_node] |
46
47
  [Prism::SymbolNode => _factory_name_node, *, Prism::HashNode => _kw_node] |
47
48
  [Prism::SymbolNode => _factory_name_node, *, Prism::CallNode => _call_node]
48
49
 
49
- attr_name = _call_node ? _call_node.message : _kw_node.elements.last.key.value&.to_s
50
- handle_attribute(node_string_value(_factory_name_node), node, attr_name)
50
+ attr_name = node_string_value(_call_node || _kw_node.elements.last.key)
51
+ already_used_attrs = gather_already_used_attrs(_kw_node)
52
+ handle_attribute(node_string_value(_factory_name_node), node, already_used_attrs, attr_name)
51
53
  else
52
54
  nil
53
55
  end
54
56
  end
55
57
 
56
58
  def node_string_value(node)
57
- node.value.to_s
59
+ case node
60
+ when Prism::CallNode
61
+ node.name.to_s
62
+ when Prism::SymbolNode
63
+ node.value.to_s
64
+ when nil
65
+ ""
66
+ end
67
+ end
68
+
69
+ def gather_already_used_attrs(kw_node)
70
+ attrs = Set.new
71
+ return attrs unless kw_node
72
+
73
+ kw_node.elements.each do |e|
74
+ attrs.add(node_string_value(e.key))
75
+ end
76
+
77
+ attrs
58
78
  end
59
79
 
60
- def handle_attribute(factory_name, node, value = "")
80
+ def handle_attribute(factory_name, node, already_used_attrs, value = "")
61
81
  range = range_from_node(node)
62
82
  make_request(:attributes, factory_name: factory_name, name: value)&.each do |attr|
83
+ next if already_used_attrs.member?(attr[:name].to_s)
84
+
63
85
  label_details = Interface::CompletionItemLabelDetails.new(description: attr[:type])
64
86
 
65
87
  @response_builder << serialise_attribute(attr[:name], label_details, attr[:owner], range)
@@ -77,24 +99,39 @@ module RubyLsp
77
99
  )
78
100
  end
79
101
 
80
- def handle_trait(factory_name, node, value = "")
102
+ def gather_already_used_traits(arguments)
103
+ trait_names = Set.new
104
+ # skip the first one because it's factory name
105
+ 1.upto(arguments.length - 1) do |i|
106
+ arg = arguments[i]
107
+ next if arg.is_a?(Prism::IntegerNode)
108
+ break unless arg.is_a?(Prism::SymbolNode)
109
+
110
+ trait_names.add(arg.value.to_s)
111
+ end
112
+ trait_names
113
+ end
114
+
115
+ def handle_trait(factory_name, node, already_used_traits, value = "")
81
116
  make_request(:traits, factory_name: factory_name, name: value)&.each do |tr|
117
+ next if already_used_traits.member?(tr[:name].to_s)
118
+
82
119
  label_details = Interface::CompletionItemLabelDetails.new(description: tr[:owner])
83
120
  range = range_from_node(node)
84
121
  name = tr[:name]
85
122
 
86
- @response_builder << serialise_trait(name, range, label_details)
123
+ @response_builder << serialise_trait(name, range, label_details, tr[:owner])
87
124
  end
88
125
  end
89
126
 
90
- def serialise_trait(name, range, label_details)
127
+ def serialise_trait(name, range, label_details, owner)
91
128
  Interface::CompletionItem.new(
92
129
  label: name,
93
130
  filter_text: name,
94
131
  label_details: label_details,
95
132
  text_edit: Interface::TextEdit.new(range: range, new_text: name),
96
133
  kind: Constant::CompletionItemKind::PROPERTY,
97
- data: { owner_name: nil, guessed_type: tr[:owner] },
134
+ data: { owner_name: nil, guessed_type: owner },
98
135
  )
99
136
  end
100
137
 
@@ -3,7 +3,7 @@
3
3
  module RubyLsp
4
4
  module Rails
5
5
  module FactoryBot
6
- VERSION = "0.2.0"
6
+ VERSION = "0.3.0"
7
7
  REQUIRED_RUBY_LSP_RAILS_VERSION = "~> 0.4"
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-rails-factory-bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - johansenja
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-20 00:00:00.000000000 Z
11
+ date: 2025-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: factory_bot