Skip to content

Commit 5ee21de

Browse files
authored
Add code lens for running migrations (#239)
Adds code lens to run migrations to specific versions in the terminal. This is convenient because it allows developers to quickly rollback or fast-forward to specific schema versions with a click.
1 parent d30e709 commit 5ee21de

File tree

3 files changed

+62
-8
lines changed

3 files changed

+62
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Ruby LSP Rails is a [Ruby LSP](https://github.com/Shopify/ruby-lsp) addon for ex
99
* Navigate to associations, validations, callbacks and test cases using your editor's "Go to Symbol" feature, or outline view.
1010
* Jump to the definition of callbacks using your editor's "Go to Definition" feature.
1111
* Jump to the declaration of a route.
12+
* Code Lens allowing fast-forwarding or rewinding of migrations.
1213

1314
## Installation
1415

lib/ruby_lsp/ruby_lsp_rails/code_lens.rb

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Rails
1010
# - Run tests in the VS Terminal
1111
# - Run tests in the VS Code Test Explorer
1212
# - Debug tests
13+
# - Run migrations in the VS Terminal
1314
#
1415
# The
1516
# [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
@@ -34,6 +35,14 @@ module Rails
3435
# end
3536
# ````
3637
#
38+
# # Example:
39+
# ```ruby
40+
# Run
41+
# class AddFirstNameToUsers < ActiveRecord::Migration[7.1]
42+
# # ...
43+
# end
44+
# ````
45+
#
3746
# The code lenses will be displayed above the class and above each test method.
3847
#
3948
# Note: When using the Test Explorer view, if your code contains a statement to pause execution (e.g. `debugger`) it
@@ -74,6 +83,7 @@ def on_call_node_enter(node)
7483
sig { params(node: Prism::DefNode).void }
7584
def on_def_node_enter(node)
7685
method_name = node.name.to_s
86+
7787
if method_name.start_with?("test_")
7888
line_number = node.location.start_line
7989
command = "#{test_command} #{@path}:#{line_number}"
@@ -84,12 +94,19 @@ def on_def_node_enter(node)
8494
sig { params(node: Prism::ClassNode).void }
8595
def on_class_node_enter(node)
8696
class_name = node.constant_path.slice
97+
superclass_name = node.superclass&.slice
98+
8799
if class_name.end_with?("Test")
88100
command = "#{test_command} #{@path}"
89101
add_test_code_lens(node, name: class_name, command: command, kind: :group)
90102
@group_id_stack.push(@group_id)
91103
@group_id += 1
92104
end
105+
106+
if superclass_name&.start_with?("ActiveRecord::Migration")
107+
command = "#{migrate_command} VERSION=#{migration_version}"
108+
add_migrate_code_lens(node, name: class_name, command: command)
109+
end
93110
end
94111

95112
sig { params(node: Prism::ClassNode).void }
@@ -104,11 +121,30 @@ def on_class_node_leave(node)
104121

105122
sig { returns(String) }
106123
def test_command
107-
if Gem.win_platform?
108-
"ruby bin/rails test"
109-
else
110-
"bin/rails test"
111-
end
124+
"#{RbConfig.ruby} bin/rails test"
125+
end
126+
127+
sig { returns(String) }
128+
def migrate_command
129+
"#{RbConfig.ruby} bin/rails db:migrate"
130+
end
131+
132+
sig { returns(T.nilable(String)) }
133+
def migration_version
134+
File.basename(T.must(@path)).split("_").first
135+
end
136+
137+
sig { params(node: Prism::Node, name: String, command: String).void }
138+
def add_migrate_code_lens(node, name:, command:)
139+
return unless @path
140+
141+
@response_builder << create_code_lens(
142+
node,
143+
title: "Run",
144+
command_name: "rubyLsp.runTask",
145+
arguments: [command],
146+
data: { type: "migrate" },
147+
)
112148
end
113149

114150
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }

test/ruby_lsp_rails/code_lens_test.rb

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Rails
88
class CodeLensTest < ActiveSupport::TestCase
99
setup do
1010
GlobalState.any_instance.stubs(:test_library).returns("rails")
11+
@ruby = Gem.win_platform? ? "ruby.exe" : "ruby"
1112
end
1213

1314
test "does not create code lenses if rails is not the test library" do
@@ -270,13 +271,29 @@ class Test < ActiveSupport::TestCase
270271
end
271272
RUBY
272273

273-
assert_equal("ruby bin/rails test /fake.rb", response[0].command.arguments[2])
274+
assert_match("#{ruby} bin/rails test /fake.rb", response[0].command.arguments[2])
275+
end
276+
277+
test "recognizes migrations" do
278+
response = generate_code_lens_for_source(<<~RUBY, file: "file://db/migrate/123456_add_first_name_to_users.rb")
279+
class AddFirstNameToUsers < ActiveRecord::Migration[7.1]
280+
def change
281+
add_column(:users, :first_name, :string)
282+
end
283+
end
284+
RUBY
285+
286+
assert_equal(1, response.size)
287+
assert_match("Run", response[0].command.title)
288+
assert_match("#{ruby} bin/rails db:migrate VERSION=123456", response[0].command.arguments[0])
274289
end
275290

276291
private
277292

278-
def generate_code_lens_for_source(source)
279-
with_server(source) do |server, uri|
293+
attr_reader :ruby
294+
295+
def generate_code_lens_for_source(source, file: "/fake.rb")
296+
with_server(source, URI(file)) do |server, uri|
280297
server.process_message(
281298
id: 1,
282299
method: "textDocument/codeLens",

0 commit comments

Comments
 (0)