Skip to content

Commit 3c76670

Browse files
committed
Add code lens for running migrations
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 5704d93 commit 3c76670

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

lib/ruby_lsp/ruby_lsp_rails/code_lens.rb

Lines changed: 47 additions & 0 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 in terminal
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
@@ -43,6 +52,8 @@ class CodeLens
4352
include Requests::Support::Common
4453
include ActiveSupportTestCaseHelper
4554

55+
MIGRATE_COMMAND = "bin/rails db:migrate"
56+
4657
sig do
4758
params(
4859
response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
@@ -74,6 +85,7 @@ def on_call_node_enter(node)
7485
sig { params(node: Prism::DefNode).void }
7586
def on_def_node_enter(node)
7687
method_name = node.name.to_s
88+
7789
if method_name.start_with?("test_")
7890
line_number = node.location.start_line
7991
command = "#{test_command} #{@path}:#{line_number}"
@@ -84,12 +96,19 @@ def on_def_node_enter(node)
8496
sig { params(node: Prism::ClassNode).void }
8597
def on_class_node_enter(node)
8698
class_name = node.constant_path.slice
99+
superclass_name = node.superclass&.slice
100+
87101
if class_name.end_with?("Test")
88102
command = "#{test_command} #{@path}"
89103
add_test_code_lens(node, name: class_name, command: command, kind: :group)
90104
@group_id_stack.push(@group_id)
91105
@group_id += 1
92106
end
107+
108+
if superclass_name&.start_with?("ActiveRecord::Migration")
109+
command = "#{MIGRATE_COMMAND} VERSION=#{migration_version}"
110+
add_migrate_code_lens(node, name: class_name, command: command)
111+
end
93112
end
94113

95114
sig { params(node: Prism::ClassNode).void }
@@ -111,6 +130,34 @@ def test_command
111130
end
112131
end
113132

133+
sig { returns(T.nilable(String)) }
134+
def migration_version
135+
File.basename(T.must(@path)).split("_").first
136+
end
137+
138+
sig { params(node: Prism::Node, name: String, command: String).void }
139+
def add_migrate_code_lens(node, name:, command:)
140+
return unless @path
141+
142+
arguments = [
143+
command,
144+
{
145+
start_line: node.location.start_line - 1,
146+
start_column: node.location.start_column,
147+
end_line: node.location.end_line - 1,
148+
end_column: node.location.end_column,
149+
},
150+
]
151+
152+
@response_builder << create_code_lens(
153+
node,
154+
title: "Run in terminal",
155+
command_name: "rubyLsp.runMigrationInTerminal",
156+
arguments: arguments,
157+
data: { type: "migrate" },
158+
)
159+
end
160+
114161
sig { params(node: Prism::Node, name: String, command: String, kind: Symbol).void }
115162
def add_test_code_lens(node, name:, command:, kind:)
116163
return unless @path

test/ruby_lsp_rails/code_lens_test.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,24 @@ class Test < ActiveSupport::TestCase
273273
assert_equal("ruby bin/rails test /fake.rb", response[0].command.arguments[2])
274274
end
275275

276+
test "recognizes migrations" do
277+
response = generate_code_lens_for_source(<<~RUBY, file: "file://db/migrate/123456_add_first_name_to_users.rb")
278+
class AddFirstNameToUsers < ActiveRecord::Migration[7.1]
279+
def change
280+
add_column(:users, :first_name, :string)
281+
end
282+
end
283+
RUBY
284+
285+
assert_equal(1, response.size)
286+
assert_match("Run in terminal", response[0].command.title)
287+
assert_equal("bin/rails db:migrate VERSION=123456", response[0].command.arguments[0])
288+
end
289+
276290
private
277291

278-
def generate_code_lens_for_source(source)
279-
with_server(source) do |server, uri|
292+
def generate_code_lens_for_source(source, file: "/fake.rb")
293+
with_server(source, URI(file)) do |server, uri|
280294
server.process_message(
281295
id: 1,
282296
method: "textDocument/codeLens",

0 commit comments

Comments
 (0)