ruby-lsp-rails-factory-bot 0.4.0 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36e804a6875a0e627b7723e82840cbd502cc7dd4d352a740e9d7917379a2929a
4
- data.tar.gz: e87ef45262a4740710ceae3229b7f681faaf002987d5a611f355e80a4e883378
3
+ metadata.gz: ecaef6088d61c2afa9d54e033f288ab6221f3e607214f31d7a2c4749a9c58450
4
+ data.tar.gz: 813d00d86a4f54c299ab0cd8db98760f8d0e33207d1621fc454813acd70914d7
5
5
  SHA512:
6
- metadata.gz: a60ac6a177b638d4ecb930c2ccb0c0f59f1d457b421ebdcd6b64f55efe26aa179d8de68b80498b7701929c34a357410bc1e0d195835b51dc95312834648790ce
7
- data.tar.gz: aa3ea96af8da24ef905db823967fac046a2a6900bb1f39033d8cbd20a5e284003df43669272ad48ff9eec49c5a433be534145d0855171d4423443114a9b2cc02
6
+ metadata.gz: 04755031614a46a02d65feb63b1f926e0ab4525d3e6b561f7964712ea9e46dfceccbfda8471586e518c2af5e308edb9050a1f36c8cff700b66804946e994c852
7
+ data.tar.gz: 822d8093fc8d3c113d17ddedd6e5fa7c37254b67c79227d6e7c6bbd97ee6bea436f17d7e387ed1a5b160b0e9841e37a23996cdf1ea19a17524e027ab12790c0f
data/README.md CHANGED
@@ -12,30 +12,35 @@ If bundler is not being used to manage dependencies, install the gem by executin
12
12
 
13
13
  $ gem install ruby-lsp-rails-factory-bot
14
14
 
15
- Note that this currenlty uses a fork of ruby-lsp-rails (to extend its server to be able to provide factory information).
16
-
17
15
  ## Usage
18
16
 
19
- Hover over an attribute or factory definition
17
+ Hover over a factory name, trait, or attribute
20
18
 
21
- ![lsp-factory-bot-hover](https://github.com/user-attachments/assets/6f570288-3cf3-4d12-acf9-71c86e834cd8)
19
+ ![lsp-factory-bot-hover-all](https://github.com/user-attachments/assets/16e463cb-dddf-4d12-8a85-3357d47df6ff)
22
20
 
23
21
  Receive completion suggestions as you type
24
22
 
25
23
  ![lsp-factory-bot-completion](https://github.com/user-attachments/assets/4255a86a-8f36-4de2-8d10-8cb5a3f49e50)
26
24
 
25
+ Click through to definitions
26
+
27
+ ![lsp-factory-bot-definition-trait](https://github.com/user-attachments/assets/92a4b224-b587-442d-9463-50d526872039)
28
+
29
+ ![lsp-factory-bot-definition-attribute](https://github.com/user-attachments/assets/d8650678-6759-42c8-b015-0fdbd4045494)
30
+
31
+
27
32
  ### Supports
28
33
 
29
34
  | | Hover | Completion | Go to definition |
30
35
  | ------------- |-------------| -----| ----|
31
- | Factory name | ✅ | ⭕️ | |
32
- | Trait | ✅ | ⭕️ | |
33
- | Attribute | ✅ | | ❌ |
36
+ | Attribute | ✅ | | |
37
+ | Trait | ✅ | ⭕️ |
38
+ | Factory name | ✅ | ⭕️ | ❌ ||
34
39
 
35
40
  Notes:
36
41
 
37
42
  - 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
43
+ - Factory definition is not supported at the moment (limitation of current implementation), but might come in due course
39
44
 
40
45
 
41
46
  ## Development
@@ -5,6 +5,7 @@ require "ruby_lsp/ruby_lsp_rails/addon"
5
5
 
6
6
  require_relative "completion"
7
7
  require_relative "hover"
8
+ require_relative "definition"
8
9
  require_relative "addon_name"
9
10
  require_relative "../factory_bot"
10
11
  require_relative "../../../ruby_lsp_rails_factory_bot"
@@ -37,6 +38,11 @@ module RubyLsp
37
38
  Hover.new(response_builder, node_context, dispatcher, runner_client, @ruby_index)
38
39
  end
39
40
 
41
+ def create_definition_listener(response_builder, _uri, node_context, dispatcher)
42
+ register_addon!
43
+ Definition.new(response_builder, node_context, dispatcher, runner_client)
44
+ end
45
+
40
46
  def workspace_did_change_watched_files(changes)
41
47
  return unless changes.any? do |change|
42
48
  change[:uri].match?(/(?:spec|test).+factor.+\.rb/)
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby_lsp/internal"
4
+
5
+ require_relative "addon_name"
6
+
7
+ module RubyLsp
8
+ module Rails
9
+ module FactoryBot
10
+ # Definition listener - calls the registered methods when the appropriate nodes are entered
11
+ class Definition
12
+ include RubyLsp::Requests::Support::Common
13
+
14
+ def initialize(response_builder, node_context, dispatcher, server_client)
15
+ @response_builder = response_builder
16
+ @node_context = node_context
17
+ @server_client = server_client
18
+
19
+ dispatcher.register self, :on_symbol_node_enter
20
+ end
21
+
22
+ def on_symbol_node_enter(symbol_node)
23
+ parent = @node_context.parent
24
+ call_node = @node_context.call_node
25
+
26
+ return unless call_node
27
+
28
+ # "parent" isn't strictly speaking the immediate parent as in the AST - the
29
+ # element it refers to is a bit opinionated... in this case, it is always the call node,
30
+ # whether the symbol is an argument in the call_node args, or a symbol in a kw hash :/
31
+ unless parent.is_a?(Prism::CallNode) || FactoryBot::FACTORY_BOT_METHODS.include?(call_node.message.to_sym)
32
+ return
33
+ end
34
+
35
+ process_arguments_pattern(symbol_node, call_node.arguments.arguments)
36
+ end
37
+
38
+ private
39
+
40
+ def process_arguments_pattern(symbol_node, arguments) # rubocop:disable Metrics/MethodLength
41
+ case arguments
42
+ in [^symbol_node, *]
43
+ # factory location currently not available
44
+ #
45
+ # handle_factory(symbol_node)
46
+ in [Prism::SymbolNode => _factory_node, *, ^symbol_node] |
47
+ [Prism::SymbolNode => _factory_node, *, ^symbol_node, Prism::KeywordHashNode] |
48
+ [Prism::SymbolNode => _factory_node, *, ^symbol_node, Prism::HashNode] |
49
+ [Prism::SymbolNode => _factory_node, ^symbol_node, *] |
50
+ [Prism::SymbolNode => _factory_node, Prism::IntegerNode, ^symbol_node, *] |
51
+ [Prism::SymbolNode => _factory_node, Prism::IntegerNode, *, ^symbol_node, Prism::KeywordHashNode] |
52
+ [Prism::SymbolNode => _factory_node, Prism::IntegerNode, *, ^symbol_node, Prism::HashNode]
53
+
54
+ handle_trait(symbol_node, _factory_node)
55
+
56
+ in [Prism::SymbolNode => _factory_node, *, Prism::KeywordHashNode => _kw_hash] |
57
+ [Prism::SymbolNode => _factory_node, *, Prism::HashNode => _kw_hash]
58
+
59
+ handle_attribute(symbol_node, _factory_node) if _kw_hash.elements.any? { |e| e.key == symbol_node }
60
+ else
61
+ nil
62
+ end
63
+ end
64
+
65
+ def handle_attribute(symbol_node, factory_node)
66
+ name = symbol_node.value.to_s
67
+ attribute = make_request(
68
+ :attributes,
69
+ factory_name: factory_node.value.to_s, name: name,
70
+ )&.find { |attr| attr[:name] == name }
71
+
72
+ return unless attribute && attribute[:source_location]&.length&.positive?
73
+
74
+ @response_builder << Support::LocationBuilder.line_location_from_s(attribute[:source_location].join(":"))
75
+ end
76
+
77
+ def handle_factory(symbol_node)
78
+ name = symbol_node.value.to_s
79
+ factory = make_request(:factories, name: name)&.find { |f| f[:name] == name }
80
+ return unless factory && factory[:source_location]&.length&.positive?
81
+
82
+ @response_builder << Support::LocationBuilder.line_location_from_s(factory[:source_location].join(":"))
83
+ end
84
+
85
+ def handle_trait(symbol_node, factory_node)
86
+ factory_name = factory_node.value.to_s
87
+ trait_name = symbol_node.value.to_s
88
+
89
+ trait = make_request(:traits, factory_name: factory_name, name: trait_name)&.find do |tr|
90
+ tr[:name] == trait_name
91
+ end
92
+
93
+ return unless trait && trait[:source_location]&.length&.positive?
94
+
95
+ @response_builder << Support::LocationBuilder.line_location_from_s(trait[:source_location].join(":"))
96
+ end
97
+
98
+ def make_request(request_name, **params)
99
+ @server_client.delegate_request(
100
+ server_addon_name: FactoryBot::ADDON_NAME,
101
+ request_name: request_name.to_s,
102
+ **params,
103
+ )
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -7,7 +7,7 @@ require_relative "addon_name"
7
7
  module RubyLsp
8
8
  module Rails
9
9
  module FactoryBot
10
- # The listener that is created during relevant hover actions
10
+ # Hover listener - calls the registered methods when the appropriate nodes are entered
11
11
  class Hover
12
12
  include RubyLsp::Requests::Support::Common
13
13
 
@@ -61,6 +61,12 @@ module RubyLsp
61
61
  end
62
62
  end
63
63
 
64
+ def build_response(title, documentation, link)
65
+ @response_builder.push(title, category: :title) if title
66
+ @response_builder.push(documentation, category: :documentation) if documentation
67
+ @response_builder.push(link, category: :links) if link
68
+ end
69
+
64
70
  def handle_attribute(symbol_node, factory_node)
65
71
  name = symbol_node.value.to_s
66
72
  attribute = make_request(
@@ -70,10 +76,14 @@ module RubyLsp
70
76
 
71
77
  return unless attribute
72
78
 
73
- @response_builder.push(
74
- "#{attribute[:name]} (#{attribute[:type]})",
75
- category: :documentation,
76
- )
79
+ build_response(attribute[:name].to_s, attribute[:type].to_s, link_location(attribute[:source_location]))
80
+ end
81
+
82
+ def factory_documentation(factory)
83
+ index_entry = @ruby_index.first_unqualified_const(factory[:name])
84
+ return markdown_from_index_entries(factory[:model_class], index_entry) if index_entry
85
+
86
+ factory[:model_class].to_s
77
87
  end
78
88
 
79
89
  def handle_factory(symbol_node)
@@ -81,20 +91,12 @@ module RubyLsp
81
91
  factory = make_request(:factories, name: name)&.find { |f| f[:name] == name }
82
92
  return unless factory
83
93
 
84
- index_entry = @ruby_index.first_unqualified_const(factory[:name])
85
-
86
- hint = if index_entry
87
- markdown_from_index_entries(factory[:model_class], index_entry)
88
- else
89
- "#{factory[:name]} (#{factory[:model_class]})"
90
- end
91
-
92
- @response_builder.push(hint, category: :documentation)
94
+ build_response(factory[:name], factory_documentation(factory), nil)
93
95
  end
94
96
 
95
97
  def trait_tooltip(trait, factory_name)
96
98
  source = trait[:source]&.length&.positive? ? trait[:source] : nil
97
- source || "#{trait[:name]} (trait of #{trait[:owner] || factory_name})"
99
+ source ? "```ruby\n#{source}\n```" : "trait of #{trait[:owner] || factory_name}"
98
100
  end
99
101
 
100
102
  def handle_trait(symbol_node, factory_node)
@@ -107,7 +109,7 @@ module RubyLsp
107
109
 
108
110
  return unless trait
109
111
 
110
- @response_builder.push(trait_tooltip(trait, factory_name), category: :documentation)
112
+ build_response(trait[:name], trait_tooltip(trait, factory_name), link_location(trait[:source_location]))
111
113
  end
112
114
 
113
115
  def make_request(request_name, **params)
@@ -117,6 +119,14 @@ module RubyLsp
117
119
  **params,
118
120
  )
119
121
  end
122
+
123
+ def link_location(source_location)
124
+ return unless source_location
125
+
126
+ return if source_location.empty?
127
+
128
+ "[Definition](#{URI::Generic.from_path(path: source_location[0])}#L#{source_location[1]})"
129
+ end
120
130
  end
121
131
  end
122
132
  end
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  # helper - might be best to live elsewhere?
27
27
  def block_source(attr)
28
28
  blk = block_for(attr)
29
- blk.source if blk.respond_to? :source
29
+ blk.source&.strip_heredoc if blk.respond_to? :source
30
30
  end
31
31
  end
32
32
  end
@@ -3,7 +3,7 @@
3
3
  module RubyLsp
4
4
  module Rails
5
5
  module FactoryBot
6
- VERSION = "0.4.0"
6
+ VERSION = "0.6.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.4.0
4
+ version: 0.6.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-21 00:00:00.000000000 Z
11
+ date: 2025-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: factory_bot
@@ -66,8 +66,8 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- description: A ruby-lsp-rails extension for factorybot, providing factory, trait and
70
- attribute completion, and more
69
+ description: A ruby-lsp-rails extension for factorybot, supporting hover, go-to-definition
70
+ and autocompletion - for factories, traits and attributes
71
71
  email:
72
72
  - 43235608+johansenja@users.noreply.github.com
73
73
  executables: []
@@ -82,6 +82,7 @@ files:
82
82
  - lib/ruby_lsp/rails/factory_bot/addon.rb
83
83
  - lib/ruby_lsp/rails/factory_bot/addon_name.rb
84
84
  - lib/ruby_lsp/rails/factory_bot/completion.rb
85
+ - lib/ruby_lsp/rails/factory_bot/definition.rb
85
86
  - lib/ruby_lsp/rails/factory_bot/hover.rb
86
87
  - lib/ruby_lsp/rails/factory_bot/server_addon.rb
87
88
  - lib/ruby_lsp/rails/factory_bot/server_addon/attribute_handler.rb
@@ -113,8 +114,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
114
  - !ruby/object:Gem::Version
114
115
  version: '0'
115
116
  requirements: []
116
- rubygems_version: 3.3.26
117
+ rubygems_version: 3.5.11
117
118
  signing_key:
118
119
  specification_version: 4
119
- summary: A ruby-lsp-rails extension for factorybot
120
+ summary: A ruby-lsp-rails extension for factorybot, supporting hover, go-to-definition
121
+ and autocompletion
120
122
  test_files: []