@@ -9,6 +9,7 @@ module Rails
9
9
# - Run tests in the VS Terminal
10
10
# - Run tests in the VS Code Test Explorer
11
11
# - Debug tests
12
+ # - Run migrations in the VS Terminal
12
13
#
13
14
# The
14
15
# [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
@@ -31,13 +32,23 @@ module Rails
31
32
# end
32
33
# ````
33
34
#
35
+ # # Example:
36
+ # ```ruby
37
+ # Run in terminal
38
+ # class AddFirstNameToUsers < ActiveRecord::Migration[7.1]
39
+ # # ...
40
+ # end
41
+ # ````
42
+ #
34
43
# The code lenses will be displayed above the class and above each test method.
35
44
class CodeLens < ::RubyLsp ::Listener
36
45
extend T ::Sig
37
46
extend T ::Generic
38
47
39
48
ResponseType = type_member { { fixed : T ::Array [ ::RubyLsp ::Interface ::CodeLens ] } }
40
- BASE_COMMAND = "bin/rails test"
49
+ MIGRATE_COMMAND = "bin/rails db:migrate"
50
+ TEST_COMMAND = "bin/rails test"
51
+ BASE_COMMAND = TEST_COMMAND # TODO: Deprecate?
41
52
42
53
sig { override . returns ( ResponseType ) }
43
54
attr_reader :_response
@@ -78,29 +89,37 @@ def on_call_node_enter(node)
78
89
return unless content && !content . empty?
79
90
80
91
line_number = node . location . start_line
81
- command = "#{ BASE_COMMAND } #{ @path } :#{ line_number } "
92
+ command = "#{ TEST_COMMAND } #{ @path } :#{ line_number } "
82
93
add_test_code_lens ( node , name : content , command : command , kind : :example )
83
94
end
84
95
85
96
# Although uncommon, Rails tests can be written with the classic "def test_name" syntax.
86
97
sig { params ( node : Prism ::DefNode ) . void }
87
98
def on_def_node_enter ( node )
88
99
method_name = node . name . to_s
100
+
89
101
if method_name . start_with? ( "test_" )
90
102
line_number = node . location . start_line
91
- command = "#{ BASE_COMMAND } #{ @path } :#{ line_number } "
103
+ command = "#{ TEST_COMMAND } #{ @path } :#{ line_number } "
92
104
add_test_code_lens ( node , name : method_name , command : command , kind : :example )
93
105
end
94
106
end
95
107
96
108
sig { params ( node : Prism ::ClassNode ) . void }
97
109
def on_class_node_enter ( node )
98
110
class_name = node . constant_path . slice
111
+ superclass_name = node . superclass &.slice
112
+
99
113
if class_name . end_with? ( "Test" )
100
- command = "#{ BASE_COMMAND } #{ @path } "
114
+ command = "#{ TEST_COMMAND } #{ @path } "
101
115
add_test_code_lens ( node , name : class_name , command : command , kind : :group )
102
116
end
103
117
118
+ if superclass_name &.start_with? ( "ActiveRecord::Migration" )
119
+ command = "#{ MIGRATE_COMMAND } VERSION=#{ migration_version } "
120
+ add_migrate_code_lens ( node , name : class_name , command : command )
121
+ end
122
+
104
123
@group_id_stack . push ( @group_id )
105
124
@group_id += 1
106
125
end
@@ -112,6 +131,36 @@ def on_class_node_leave(node)
112
131
113
132
private
114
133
134
+ sig { returns ( T . nilable ( String ) ) }
135
+ def migration_version
136
+ File . basename ( T . must ( @path ) ) . split ( "_" ) . first
137
+ end
138
+
139
+ sig { params ( node : Prism ::Node , name : String , command : String ) . void }
140
+ def add_migrate_code_lens ( node , name :, command :)
141
+ return unless @path
142
+
143
+ arguments = [
144
+ @path ,
145
+ name ,
146
+ command ,
147
+ {
148
+ start_line : node . location . start_line - 1 ,
149
+ start_column : node . location . start_column ,
150
+ end_line : node . location . end_line - 1 ,
151
+ end_column : node . location . end_column ,
152
+ } ,
153
+ ]
154
+
155
+ @_response << create_code_lens (
156
+ node ,
157
+ title : "Run in terminal" ,
158
+ command_name : "rubyLsp.runMigrationInTerminal" ,
159
+ arguments : arguments ,
160
+ data : { type : "migrate" } ,
161
+ )
162
+ end
163
+
115
164
sig { params ( node : Prism ::Node , name : String , command : String , kind : Symbol ) . void }
116
165
def add_test_code_lens ( node , name :, command :, kind :)
117
166
return unless @path
0 commit comments