Skip to content

Commit f79004b

Browse files
authored
Fix CI (#3)
1 parent e98e6cc commit f79004b

16 files changed

+425
-410
lines changed

.rubocop.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
AllCops:
22
TargetRubyVersion: 3.0
3+
NewCops: enable
4+
Exclude:
5+
- '**/*.gemspec'
6+
- '**/vendor/bundle/**/*'
7+
8+
Metrics/BlockLength:
9+
Exclude:
10+
- 'spec/**/*_spec.rb'
11+
12+
Style/RedundantReturn:
13+
AllowMultipleReturnValues: true
314

415
Style/StringLiterals:
516
EnforcedStyle: double_quotes
617

718
Style/StringLiteralsInInterpolation:
819
EnforcedStyle: double_quotes
20+
21+
Style/TrailingCommaInHashLiteral:
22+
EnforcedStyleForMultiline: consistent_comma
23+
24+
Style/TrailingCommaInArrayLiteral:
25+
EnforcedStyleForMultiline: consistent_comma
26+
27+
Style/TrailingCommaInArguments:
28+
EnforcedStyleForMultiline: consistent_comma

Gemfile.lock

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ GEM
2525
bigdecimal (3.1.8)
2626
concurrent-ruby (1.3.4)
2727
connection_pool (2.4.1)
28+
diff-lcs (1.5.1)
2829
drb (2.2.1)
2930
factory_bot (6.4.6)
3031
activesupport (>= 5.0.0)
@@ -47,6 +48,19 @@ GEM
4748
regexp_parser (2.9.2)
4849
rexml (3.3.2)
4950
strscan
51+
rspec (3.13.0)
52+
rspec-core (~> 3.13.0)
53+
rspec-expectations (~> 3.13.0)
54+
rspec-mocks (~> 3.13.0)
55+
rspec-core (3.13.1)
56+
rspec-support (~> 3.13.0)
57+
rspec-expectations (3.13.3)
58+
diff-lcs (>= 1.2.0, < 2.0)
59+
rspec-support (~> 3.13.0)
60+
rspec-mocks (3.13.2)
61+
diff-lcs (>= 1.2.0, < 2.0)
62+
rspec-support (~> 3.13.0)
63+
rspec-support (3.13.1)
5064
rubocop (1.65.0)
5165
json (~> 2.3)
5266
language_server-protocol (>= 3.17.0)
@@ -81,6 +95,7 @@ PLATFORMS
8195

8296
DEPENDENCIES
8397
rake (~> 13.0)
98+
rspec
8499
rubocop (~> 1.21)
85100
ruby-lsp-rails-factory-bot!
86101

Rakefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
require "bundler/gem_tasks"
44
require "rubocop/rake_task"
5+
require "rspec/core/rake_task"
56

67
RuboCop::RakeTask.new
8+
RSpec::Core::RakeTask.new(:spec)
79

8-
task default: :rubocop
10+
task default: %i[rubocop spec]

lib/ruby_lsp/rails/factory_bot.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
module RubyLsp
4+
module Rails
5+
module FactoryBot
6+
FACTORY_BOT_METHODS = %i[
7+
create
8+
build
9+
build_stubbed
10+
attributes_for
11+
].flat_map { |attr| [attr, :"#{attr}_list", :"#{attr}_pair"] }.freeze
12+
end
13+
end
14+
end

lib/ruby_lsp/rails/factory_bot/addon.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
require_relative "completion"
77
require_relative "hover"
88
require_relative "addon_name"
9+
require_relative "../factory_bot"
910

1011
module RubyLsp
1112
module Rails
1213
module FactoryBot
14+
# The addon to be registered with ruby-lsp. See https://shopify.github.io/ruby-lsp/add-ons.html
1315
class Addon < ::RubyLsp::Addon
1416
def activate(global_state, *)
1517
runner_client.register_server_addon(File.expand_path("server_addon.rb", __dir__))
@@ -49,7 +51,7 @@ def workspace_did_change_watched_files(changes)
4951
def runner_client
5052
@rails_addon ||= ::RubyLsp::Addon.get(
5153
"Ruby LSP Rails",
52-
::RubyLsp::Rails::FactoryBot::REQUIRED_RUBY_LSP_RAILS_VERSION
54+
::RubyLsp::Rails::FactoryBot::REQUIRED_RUBY_LSP_RAILS_VERSION,
5355
)
5456
@rails_addon.rails_runner_client
5557
end
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
# frozen_string_literal: true
2+
13
module RubyLsp
24
module Rails
35
module FactoryBot
4-
ADDON_NAME = "ruby-lsp-rails-factory-bot".freeze
6+
ADDON_NAME = "ruby-lsp-rails-factory-bot"
57
end
68
end
79
end

lib/ruby_lsp/rails/factory_bot/completion.rb

Lines changed: 74 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@
77
module RubyLsp
88
module Rails
99
module FactoryBot
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) :/
1015
class Completion
1116
include RubyLsp::Requests::Support::Common
1217

13-
# TODO: Avoid duplication with other class
14-
FACTORY_BOT_METHODS = %i[
15-
create
16-
build
17-
build_stubbed
18-
attributes_for
19-
].flat_map { |attr| [attr, :"#{attr}_list", :"#{attr}_pair"] }.freeze
20-
2118
def initialize(response_builder, node_context, dispatcher, server_client)
2219
@response_builder = response_builder
2320
@node_context = node_context
@@ -27,112 +24,104 @@ def initialize(response_builder, node_context, dispatcher, server_client)
2724
end
2825

2926
def on_call_node_enter(node)
30-
return unless FACTORY_BOT_METHODS.include?(node.name) ||
31-
FACTORY_BOT_METHODS.include?(@node_context.parent.name)
27+
return unless FactoryBot::FACTORY_BOT_METHODS.include?(node.name) ||
28+
FactoryBot::FACTORY_BOT_METHODS.include?(@node_context.parent.name)
29+
30+
process_arguments_pattern(node, node.arguments)
31+
rescue StandardError => e
32+
$stderr.write(e, e.backtrace)
33+
end
34+
35+
private
3236

33-
case node.arguments
37+
def process_arguments_pattern(node, arguments) # rubocop:disable Metrics/MethodLength
38+
case arguments
3439
in [Prism::SymbolNode => factory_name_node]
35-
handle_factory(factory_name_node, factory_name_node.value.to_s)
40+
handle_factory(factory_name_node, node_string_value(factory_name_node))
3641

3742
in [Prism::SymbolNode => factory_name_node, *, Prism::SymbolNode => trait_node]
38-
handle_trait(factory_name_node.value.to_s, node, trait_node.value.to_s)
39-
40-
in [Prism::SymbolNode => factory_name_node, *, Prism::CallNode => call_node]
41-
handle_attribute(factory_name_node.value.to_s, node, call_node.message)
42-
43-
in [Prism::SymbolNode => factory_name_node, *, Prism::KeywordHashNode => kw_node]
44-
handle_attribute(
45-
factory_name_node.value.to_s, node, kw_node.elements.last.key.value&.to_s
46-
)
47-
in [Prism::SymbolNode => factory_name_node, *, Prism::HashNode => hash_node]
48-
handle_attribute(
49-
factory_name_node.value.to_s, node, hash_node.elements.last.key.value&.to_s
50-
)
43+
handle_trait(node_string_value(factory_name_node), node, node_string_value(trait_node))
44+
45+
in [Prism::SymbolNode => _factory_name_node, *, Prism::KeywordHashNode => _kw_node] |
46+
[Prism::SymbolNode => _factory_name_node, *, Prism::HashNode => _kw_node] |
47+
[Prism::SymbolNode => _factory_name_node, *, Prism::CallNode => _call_node]
48+
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)
5151
else
52-
$stderr.write node.arguments
5352
nil
5453
end
55-
rescue => e
56-
$stderr.write(e, e.backtrace)
5754
end
5855

59-
private
56+
def node_string_value(node)
57+
node.value.to_s
58+
end
6059

6160
def handle_attribute(factory_name, node, value = "")
62-
make_request(
63-
:attributes,
64-
factory_name: factory_name, name: value
65-
)&.each do |attr|
66-
label_details = Interface::CompletionItemLabelDetails.new(
67-
description: attr[:type],
68-
)
69-
range = range_from_node(node)
61+
range = range_from_node(node)
62+
make_request(:attributes, factory_name: factory_name, name: value)&.each do |attr|
63+
label_details = Interface::CompletionItemLabelDetails.new(description: attr[:type])
7064

71-
@response_builder << Interface::CompletionItem.new(
72-
label: attr[:name],
73-
filter_text: attr[:name],
74-
label_details: label_details,
75-
text_edit: Interface::TextEdit.new(range: range, new_text: attr[:name]),
76-
kind: Constant::CompletionItemKind::PROPERTY,
77-
data: {
78-
owner_name: attr[:owner],
79-
guessed_type: attr[:owner], # the type of the owner, not the attribute
80-
},
81-
)
65+
@response_builder << serialise_attribute(attr[:name], label_details, attr[:owner], range)
8266
end
8367
end
8468

69+
def serialise_attribute(name, label_details, owner, range)
70+
Interface::CompletionItem.new(
71+
label: name,
72+
filter_text: name,
73+
label_details: label_details,
74+
text_edit: Interface::TextEdit.new(range: range, new_text: name),
75+
kind: Constant::CompletionItemKind::PROPERTY,
76+
data: { owner_name: owner, guessed_type: owner }, # the type of the owner, not the attribute
77+
)
78+
end
79+
8580
def handle_trait(factory_name, node, value = "")
86-
make_request(
87-
:traits,
88-
factory_name: factory_name, name: value
89-
)&.each do |tr|
90-
label_details = Interface::CompletionItemLabelDetails.new(
91-
description: tr[:owner],
92-
)
81+
make_request(:traits, factory_name: factory_name, name: value)&.each do |tr|
82+
label_details = Interface::CompletionItemLabelDetails.new(description: tr[:owner])
9383
range = range_from_node(node)
94-
9584
name = tr[:name]
9685

97-
@response_builder << Interface::CompletionItem.new(
98-
label: name,
99-
filter_text: name,
100-
label_details: label_details,
101-
text_edit: Interface::TextEdit.new(range: range, new_text: name),
102-
kind: Constant::CompletionItemKind::PROPERTY,
103-
data: {
104-
owner_name: nil,
105-
guessed_type: tr[:owner] # the type of the owner
106-
},
107-
)
86+
@response_builder << serialise_trait(name, range, label_details)
10887
end
10988
end
11089

90+
def serialise_trait(name, range, label_details)
91+
Interface::CompletionItem.new(
92+
label: name,
93+
filter_text: name,
94+
label_details: label_details,
95+
text_edit: Interface::TextEdit.new(range: range, new_text: name),
96+
kind: Constant::CompletionItemKind::PROPERTY,
97+
data: { owner_name: nil, guessed_type: tr[:owner] },
98+
)
99+
end
100+
111101
def handle_factory(node, name)
102+
range = range_from_node(node)
112103
make_request(:factories, name: name)&.each do |fact|
113-
@response_builder << Interface::CompletionItem.new(
114-
label: fact[:name],
115-
filter_text: fact[:name],
116-
label_details: Interface::CompletionItemLabelDetails.new(
117-
description: fact[:model_class]
118-
),
119-
text_edit: Interface::TextEdit.new(
120-
range: range_from_node(node),
121-
new_text: fact[:name],
122-
),
123-
kind: Constant::CompletionItemKind::CLASS,
124-
data: {
125-
guessed_type: fact[:model_class]
126-
}
127-
)
104+
@response_builder << serialise_factory(fact[:name], fact[:model_class], range)
128105
end
129106
end
130107

131108
def make_request(request_name, **params)
132-
@server_client.delegate_request(
109+
resp = @server_client.delegate_request(
133110
server_addon_name: FactoryBot::ADDON_NAME,
134-
request_name: request_name,
135-
**params
111+
request_name: request_name.to_s,
112+
**params,
113+
)
114+
resp[:result] if resp
115+
end
116+
117+
def serialise_factory(name, model_class, range)
118+
Interface::CompletionItem.new(
119+
label: name,
120+
filter_text: name,
121+
label_details: Interface::CompletionItemLabelDetails.new(description: model_class),
122+
text_edit: Interface::TextEdit.new(range: range, new_text: name),
123+
kind: Constant::CompletionItemKind::CLASS,
124+
data: { guessed_type: model_class },
136125
)
137126
end
138127
end

0 commit comments

Comments
 (0)