tapioca 0.16.8 → 0.16.10
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 +4 -4
- data/lib/ruby_lsp/tapioca/addon.rb +95 -13
- data/lib/ruby_lsp/tapioca/lockfile_diff_parser.rb +49 -0
- data/lib/ruby_lsp/tapioca/run_gem_rbi_check.rb +153 -0
- data/lib/ruby_lsp/tapioca/server_addon.rb +44 -5
- data/lib/tapioca/cli.rb +15 -7
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +1 -1
- data/lib/tapioca/gem/listeners/source_location.rb +1 -1
- data/lib/tapioca/helpers/source_uri.rb +3 -3
- data/lib/tapioca/loaders/dsl.rb +34 -6
- data/lib/tapioca/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b1ddf8615a19929d81d7662c300a28ee8d49d08d7ba984fdaa7644bf70d5dfb
|
|
4
|
+
data.tar.gz: 4f2a79db13ce4a0e7614178d14423f5bd0fded127a6c5b6d0503bf7e2d73df7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a03a8d4866b5faf46af5cea289d4d7087540d58ee5b4eb759cb30cc23c540cc037c03bdd937cdf73e3f955407b2a47865600297fafe6b495e786e64b99f38942
|
|
7
|
+
data.tar.gz: 3a5bf689b47f198577480420b9ea0c1294a6c2170df0f5ba9a6e170a8f60b3d2de73827053f4b33002f1a6ae9f2501a73b70590c250c8306b86d52b244aa7f36
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
RubyLsp::Addon.depend_on_ruby_lsp!(">= 0.23.
|
|
4
|
+
RubyLsp::Addon.depend_on_ruby_lsp!(">= 0.23.10", "< 0.24")
|
|
5
5
|
|
|
6
6
|
begin
|
|
7
7
|
# The Tapioca add-on depends on the Rails add-on to add a runtime component to the runtime server. We can allow the
|
|
@@ -13,6 +13,7 @@ rescue LoadError
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
require "zlib"
|
|
16
|
+
require "ruby_lsp/tapioca/run_gem_rbi_check"
|
|
16
17
|
|
|
17
18
|
module RubyLsp
|
|
18
19
|
module Tapioca
|
|
@@ -24,9 +25,10 @@ module RubyLsp
|
|
|
24
25
|
super
|
|
25
26
|
|
|
26
27
|
@global_state = T.let(nil, T.nilable(RubyLsp::GlobalState))
|
|
27
|
-
@rails_runner_client = T.let(
|
|
28
|
+
@rails_runner_client = T.let(Rails::NullClient.new, RubyLsp::Rails::RunnerClient)
|
|
28
29
|
@index = T.let(nil, T.nilable(RubyIndexer::Index))
|
|
29
30
|
@file_checksums = T.let({}, T::Hash[String, String])
|
|
31
|
+
@lockfile_diff = T.let(nil, T.nilable(String))
|
|
30
32
|
@outgoing_queue = T.let(nil, T.nilable(Thread::Queue))
|
|
31
33
|
end
|
|
32
34
|
|
|
@@ -41,7 +43,7 @@ module RubyLsp
|
|
|
41
43
|
# Get a handle to the Rails add-on's runtime client. The call to `rails_runner_client` will block this thread
|
|
42
44
|
# until the server has finished booting, but it will not block the main LSP. This has to happen inside of a
|
|
43
45
|
# thread
|
|
44
|
-
addon = T.cast(::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.
|
|
46
|
+
addon = T.cast(::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.4.0", "< 0.5"), ::RubyLsp::Rails::Addon)
|
|
45
47
|
@rails_runner_client = addon.rails_runner_client
|
|
46
48
|
@outgoing_queue << Notification.window_log_message("Activating Tapioca add-on v#{version}")
|
|
47
49
|
@rails_runner_client.register_server_addon(File.expand_path("server_addon.rb", __dir__))
|
|
@@ -50,7 +52,12 @@ module RubyLsp
|
|
|
50
52
|
request_name: "load_compilers_and_extensions",
|
|
51
53
|
workspace_path: @global_state.workspace_path,
|
|
52
54
|
)
|
|
55
|
+
|
|
56
|
+
send_usage_telemetry("activated")
|
|
57
|
+
run_gem_rbi_check
|
|
53
58
|
rescue IncompatibleApiError
|
|
59
|
+
send_usage_telemetry("incompatible_api_error")
|
|
60
|
+
|
|
54
61
|
# The requested version for the Rails add-on no longer matches. We need to upgrade and fix the breaking
|
|
55
62
|
# changes
|
|
56
63
|
@outgoing_queue << Notification.window_log_message(
|
|
@@ -71,19 +78,39 @@ module RubyLsp
|
|
|
71
78
|
|
|
72
79
|
sig { override.returns(String) }
|
|
73
80
|
def version
|
|
74
|
-
"0.1.
|
|
81
|
+
"0.1.2"
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
|
|
78
85
|
def workspace_did_change_watched_files(changes)
|
|
79
|
-
return unless
|
|
80
|
-
return unless @rails_runner_client
|
|
86
|
+
return unless @global_state&.enabled_feature?(:tapiocaAddon)
|
|
87
|
+
return unless @rails_runner_client.connected?
|
|
88
|
+
|
|
89
|
+
has_route_change = T.let(false, T::Boolean)
|
|
90
|
+
has_fixtures_change = T.let(false, T::Boolean)
|
|
91
|
+
needs_compiler_reload = T.let(false, T::Boolean)
|
|
81
92
|
|
|
82
93
|
constants = changes.flat_map do |change|
|
|
83
94
|
path = URI(change[:uri]).to_standardized_path
|
|
84
|
-
next if path.end_with?("_test.rb", "_spec.rb")
|
|
85
95
|
next unless file_updated?(change, path)
|
|
86
96
|
|
|
97
|
+
if File.fnmatch("**/fixtures/**/*.yml{,.erb}", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
98
|
+
has_fixtures_change = true
|
|
99
|
+
next
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if File.basename(path) == "routes.rb" || File.fnmatch?("**/routes/**/*.rb", path, File::FNM_PATHNAME)
|
|
103
|
+
has_route_change = true
|
|
104
|
+
next
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
next if File.fnmatch?("**/{test,spec,features}/**/*", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
108
|
+
|
|
109
|
+
if File.fnmatch?("**/tapioca/**/compilers/**/*.rb", path, File::FNM_PATHNAME)
|
|
110
|
+
needs_compiler_reload = true
|
|
111
|
+
next
|
|
112
|
+
end
|
|
113
|
+
|
|
87
114
|
entries = T.must(@index).entries_for(change[:uri])
|
|
88
115
|
next unless entries
|
|
89
116
|
|
|
@@ -92,18 +119,59 @@ module RubyLsp
|
|
|
92
119
|
end
|
|
93
120
|
end.compact
|
|
94
121
|
|
|
95
|
-
return if constants.empty?
|
|
122
|
+
return if constants.empty? && !has_route_change && !has_fixtures_change && !needs_compiler_reload
|
|
96
123
|
|
|
97
124
|
@rails_runner_client.trigger_reload
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
|
|
126
|
+
if needs_compiler_reload
|
|
127
|
+
@rails_runner_client.delegate_notification(
|
|
128
|
+
server_addon_name: "Tapioca",
|
|
129
|
+
request_name: "reload_workspace_compilers",
|
|
130
|
+
workspace_path: @global_state.workspace_path,
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if has_route_change
|
|
135
|
+
send_usage_telemetry("route_dsl")
|
|
136
|
+
@rails_runner_client.delegate_notification(server_addon_name: "Tapioca", request_name: "route_dsl")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
if has_fixtures_change
|
|
140
|
+
send_usage_telemetry("fixtures_dsl")
|
|
141
|
+
@rails_runner_client.delegate_notification(server_addon_name: "Tapioca", request_name: "fixtures_dsl")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if constants.any?
|
|
145
|
+
send_usage_telemetry("dsl")
|
|
146
|
+
@rails_runner_client.delegate_notification(
|
|
147
|
+
server_addon_name: "Tapioca",
|
|
148
|
+
request_name: "dsl",
|
|
149
|
+
constants: constants,
|
|
150
|
+
)
|
|
151
|
+
end
|
|
103
152
|
end
|
|
104
153
|
|
|
105
154
|
private
|
|
106
155
|
|
|
156
|
+
sig { params(feature_name: String).void }
|
|
157
|
+
def send_usage_telemetry(feature_name)
|
|
158
|
+
return unless @outgoing_queue && @global_state
|
|
159
|
+
|
|
160
|
+
# Telemetry is not captured by default even if events are produced by the server
|
|
161
|
+
# See https://github.com/Shopify/ruby-lsp/tree/main/vscode#telemetry
|
|
162
|
+
@outgoing_queue << Notification.telemetry({
|
|
163
|
+
eventName: "tapioca_addon.feature_usage",
|
|
164
|
+
type: "data",
|
|
165
|
+
data: {
|
|
166
|
+
type: "counter",
|
|
167
|
+
attributes: {
|
|
168
|
+
label: feature_name,
|
|
169
|
+
machineId: @global_state.telemetry_machine_id,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
end
|
|
174
|
+
|
|
107
175
|
sig { params(change: T::Hash[Symbol, T.untyped], path: String).returns(T::Boolean) }
|
|
108
176
|
def file_updated?(change, path)
|
|
109
177
|
case change[:type]
|
|
@@ -132,6 +200,20 @@ module RubyLsp
|
|
|
132
200
|
|
|
133
201
|
false
|
|
134
202
|
end
|
|
203
|
+
|
|
204
|
+
sig { void }
|
|
205
|
+
def run_gem_rbi_check
|
|
206
|
+
gem_rbi_check = RunGemRbiCheck.new(T.must(@global_state).workspace_path)
|
|
207
|
+
gem_rbi_check.run
|
|
208
|
+
|
|
209
|
+
T.must(@outgoing_queue) << Notification.window_log_message(
|
|
210
|
+
gem_rbi_check.stdout,
|
|
211
|
+
) unless gem_rbi_check.stdout.empty?
|
|
212
|
+
T.must(@outgoing_queue) << Notification.window_log_message(
|
|
213
|
+
gem_rbi_check.stderr,
|
|
214
|
+
type: Constant::MessageType::WARNING,
|
|
215
|
+
) unless gem_rbi_check.stderr.empty?
|
|
216
|
+
end
|
|
135
217
|
end
|
|
136
218
|
end
|
|
137
219
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler"
|
|
5
|
+
|
|
6
|
+
module RubyLsp
|
|
7
|
+
module Tapioca
|
|
8
|
+
class LockfileDiffParser
|
|
9
|
+
GEM_NAME_PATTERN = /[\w\-]+/
|
|
10
|
+
DIFF_LINE_PATTERN = /[+-](.*#{GEM_NAME_PATTERN})\s*\(/
|
|
11
|
+
ADDED_LINE_PATTERN = /^\+.*#{GEM_NAME_PATTERN} \(.*\)/
|
|
12
|
+
REMOVED_LINE_PATTERN = /^-.*#{GEM_NAME_PATTERN} \(.*\)/
|
|
13
|
+
|
|
14
|
+
attr_reader :added_or_modified_gems
|
|
15
|
+
attr_reader :removed_gems
|
|
16
|
+
|
|
17
|
+
def initialize(diff_content, direct_dependencies: nil)
|
|
18
|
+
@diff_content = diff_content.lines
|
|
19
|
+
@current_dependencies = direct_dependencies ||
|
|
20
|
+
Bundler::LockfileParser.new(Bundler.default_lockfile.read).dependencies.keys
|
|
21
|
+
@added_or_modified_gems = parse_added_or_modified_gems
|
|
22
|
+
@removed_gems = parse_removed_gems
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def parse_added_or_modified_gems
|
|
28
|
+
@diff_content
|
|
29
|
+
.filter_map { |line| extract_gem(line) if line.match?(ADDED_LINE_PATTERN) }
|
|
30
|
+
.uniq
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_removed_gems
|
|
34
|
+
@diff_content.filter_map do |line|
|
|
35
|
+
next unless line.match?(REMOVED_LINE_PATTERN)
|
|
36
|
+
|
|
37
|
+
gem = extract_gem(line)
|
|
38
|
+
next if @current_dependencies.include?(gem)
|
|
39
|
+
|
|
40
|
+
gem
|
|
41
|
+
end.uniq
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extract_gem(line)
|
|
45
|
+
line.match(DIFF_LINE_PATTERN)[1].strip
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "open3"
|
|
5
|
+
require "ruby_lsp/tapioca/lockfile_diff_parser"
|
|
6
|
+
|
|
7
|
+
module RubyLsp
|
|
8
|
+
module Tapioca
|
|
9
|
+
class RunGemRbiCheck
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
attr_reader :stdout
|
|
13
|
+
attr_reader :stderr
|
|
14
|
+
attr_reader :status
|
|
15
|
+
|
|
16
|
+
sig { params(project_path: String).void }
|
|
17
|
+
def initialize(project_path)
|
|
18
|
+
@project_path = project_path
|
|
19
|
+
@stdout = T.let("", String)
|
|
20
|
+
@stderr = T.let("", String)
|
|
21
|
+
@status = T.let(nil, T.nilable(Process::Status))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
sig { void }
|
|
25
|
+
def run
|
|
26
|
+
return log_message("Not a git repository") unless git_repo?
|
|
27
|
+
|
|
28
|
+
cleanup_orphaned_rbis
|
|
29
|
+
|
|
30
|
+
if lockfile_changed?
|
|
31
|
+
generate_gem_rbis
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
attr_reader :project_path
|
|
38
|
+
|
|
39
|
+
sig { returns(T.nilable(T::Boolean)) }
|
|
40
|
+
def git_repo?
|
|
41
|
+
_, status = Open3.capture2e("git", "rev-parse", "--is-inside-work-tree", chdir: project_path)
|
|
42
|
+
status.success?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
sig { returns(T::Boolean) }
|
|
46
|
+
def lockfile_changed?
|
|
47
|
+
!lockfile_diff.empty?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
sig { returns(Pathname) }
|
|
51
|
+
def lockfile
|
|
52
|
+
@lockfile ||= T.let(Pathname(project_path).join("Gemfile.lock"), T.nilable(Pathname))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
sig { returns(String) }
|
|
56
|
+
def lockfile_diff
|
|
57
|
+
@lockfile_diff ||= T.let(read_lockfile_diff, T.nilable(String))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
sig { returns(String) }
|
|
61
|
+
def read_lockfile_diff
|
|
62
|
+
return "" unless lockfile.exist?
|
|
63
|
+
|
|
64
|
+
execute_in_project_path("git", "diff", lockfile.to_s).strip
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sig { void }
|
|
68
|
+
def generate_gem_rbis
|
|
69
|
+
parser = Tapioca::LockfileDiffParser.new(@lockfile_diff)
|
|
70
|
+
removed_gems = parser.removed_gems
|
|
71
|
+
added_or_modified_gems = parser.added_or_modified_gems
|
|
72
|
+
|
|
73
|
+
if added_or_modified_gems.any?
|
|
74
|
+
log_message("Identified lockfile changes, attempting to generate gem RBIs...")
|
|
75
|
+
execute_tapioca_gem_command(added_or_modified_gems)
|
|
76
|
+
elsif removed_gems.any?
|
|
77
|
+
remove_rbis(removed_gems)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sig { params(gems: T::Array[String]).void }
|
|
82
|
+
def execute_tapioca_gem_command(gems)
|
|
83
|
+
Bundler.with_unbundled_env do
|
|
84
|
+
stdout, stderr, status = T.unsafe(Open3).capture3(
|
|
85
|
+
"bundle",
|
|
86
|
+
"exec",
|
|
87
|
+
"tapioca",
|
|
88
|
+
"gem",
|
|
89
|
+
"--lsp_addon",
|
|
90
|
+
*gems,
|
|
91
|
+
chdir: project_path,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
log_message(stdout) unless stdout.empty?
|
|
95
|
+
@stderr = stderr unless stderr.empty?
|
|
96
|
+
@status = status
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
sig { params(gems: T::Array[String]).void }
|
|
101
|
+
def remove_rbis(gems)
|
|
102
|
+
files = Dir.glob(
|
|
103
|
+
"sorbet/rbi/gems/{#{gems.join(",")}}@*.rbi",
|
|
104
|
+
base: project_path,
|
|
105
|
+
)
|
|
106
|
+
delete_files(files, "Removed RBIs for")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
sig { void }
|
|
110
|
+
def cleanup_orphaned_rbis
|
|
111
|
+
untracked_files = git_ls_gem_rbis("--others", "--exclude-standard")
|
|
112
|
+
deleted_files = git_ls_gem_rbis("--deleted")
|
|
113
|
+
|
|
114
|
+
delete_files(untracked_files, "Deleted untracked RBIs")
|
|
115
|
+
restore_files(deleted_files, "Restored deleted RBIs")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
sig { params(flags: T.untyped).returns(T::Array[String]) }
|
|
119
|
+
def git_ls_gem_rbis(*flags)
|
|
120
|
+
flags = T.unsafe(["git", "ls-files", *flags, "sorbet/rbi/gems/"])
|
|
121
|
+
|
|
122
|
+
execute_in_project_path(*flags)
|
|
123
|
+
.lines
|
|
124
|
+
.map(&:strip)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
sig { params(files: T::Array[String], message: String).void }
|
|
128
|
+
def delete_files(files, message)
|
|
129
|
+
files_to_remove = files.map { |file| File.join(project_path, file) }
|
|
130
|
+
FileUtils.rm(files_to_remove)
|
|
131
|
+
log_message("#{message}: #{files.join(", ")}") unless files.empty?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
sig { params(files: T::Array[String], message: String).void }
|
|
135
|
+
def restore_files(files, message)
|
|
136
|
+
execute_in_project_path("git", "checkout", "--pathspec-from-file=-", stdin: files.join("\n"))
|
|
137
|
+
log_message("#{message}: #{files.join(", ")}") unless files.empty?
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
sig { params(message: String).void }
|
|
141
|
+
def log_message(message)
|
|
142
|
+
@stdout += "#{message}\n"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def execute_in_project_path(*parts, stdin: nil)
|
|
146
|
+
options = { chdir: project_path }
|
|
147
|
+
options[:stdin_data] = stdin if stdin
|
|
148
|
+
stdout_and_stderr, _status = T.unsafe(Open3).capture2e(*parts, options)
|
|
149
|
+
stdout_and_stderr
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "tapioca/internal"
|
|
5
|
+
require "tapioca/dsl/compilers/url_helpers"
|
|
6
|
+
require "tapioca/dsl/compilers/active_record_fixtures"
|
|
5
7
|
|
|
6
8
|
module RubyLsp
|
|
7
9
|
module Tapioca
|
|
@@ -12,27 +14,64 @@ module RubyLsp
|
|
|
12
14
|
|
|
13
15
|
def execute(request, params)
|
|
14
16
|
case request
|
|
17
|
+
when "reload_workspace_compilers"
|
|
18
|
+
with_notification_wrapper("reload_workspace_compilers", "Reloading DSL compilers") do
|
|
19
|
+
@loader&.reload_custom_compilers
|
|
20
|
+
end
|
|
15
21
|
when "load_compilers_and_extensions"
|
|
16
22
|
# Load DSL extensions and compilers ahead of time, so that we don't have to pay the price of invoking
|
|
17
23
|
# `Gem.find_files` on every execution, which is quite expensive
|
|
18
|
-
::Tapioca::Loaders::Dsl.new(
|
|
24
|
+
@loader = ::Tapioca::Loaders::Dsl.new(
|
|
19
25
|
tapioca_path: ::Tapioca::TAPIOCA_DIR,
|
|
20
26
|
eager_load: false,
|
|
21
27
|
app_root: params[:workspace_path],
|
|
22
28
|
halt_upon_load_error: false,
|
|
23
|
-
)
|
|
29
|
+
)
|
|
30
|
+
@loader.load_dsl_extensions_and_compilers
|
|
24
31
|
when "dsl"
|
|
25
32
|
fork do
|
|
26
|
-
dsl
|
|
33
|
+
with_notification_wrapper("dsl", "Generating DSL RBIs") do
|
|
34
|
+
dsl(params[:constants])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
when "route_dsl"
|
|
38
|
+
fork do
|
|
39
|
+
with_notification_wrapper("route_dsl", "Generating route DSL RBIs") do
|
|
40
|
+
constants = ::Tapioca::Dsl::Compilers::UrlHelpers.gather_constants
|
|
41
|
+
dsl(constants.map(&:name), "--only=Tapioca::Dsl::Compilers::UrlHelpers", "ActiveSupportConcern")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
when "fixtures_dsl"
|
|
45
|
+
fork do
|
|
46
|
+
with_notification_wrapper("fixture_dsl", "Generating fixture DSL RBIs") do
|
|
47
|
+
constants = ::Tapioca::Dsl::Compilers::ActiveRecordFixtures.gather_constants
|
|
48
|
+
dsl(constants.map(&:name), "--only=Tapioca::Dsl::Compilers::ActiveRecordFixtures")
|
|
49
|
+
end
|
|
27
50
|
end
|
|
28
51
|
end
|
|
29
52
|
end
|
|
30
53
|
|
|
31
54
|
private
|
|
32
55
|
|
|
33
|
-
def
|
|
56
|
+
def with_notification_wrapper(request_name, title, &block)
|
|
57
|
+
with_progress(request_name, title) do
|
|
58
|
+
with_notification_error_handling(request_name, &block)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def dsl(constants, *args)
|
|
34
63
|
load("tapioca/cli.rb") # Reload the CLI to reset thor defaults between requests
|
|
35
|
-
|
|
64
|
+
|
|
65
|
+
::Tapioca::Cli.addon_mode!
|
|
66
|
+
|
|
67
|
+
# Order here is important to avoid having Thor confuse arguments. Do not put an array argument at the end before
|
|
68
|
+
# the list of constants
|
|
69
|
+
arguments = ["dsl"]
|
|
70
|
+
arguments.concat(args)
|
|
71
|
+
arguments.push("--workers=1")
|
|
72
|
+
arguments.concat(constants)
|
|
73
|
+
|
|
74
|
+
::Tapioca::Cli.start(arguments)
|
|
36
75
|
end
|
|
37
76
|
end
|
|
38
77
|
end
|
data/lib/tapioca/cli.rb
CHANGED
|
@@ -143,11 +143,6 @@ module Tapioca
|
|
|
143
143
|
type: :hash,
|
|
144
144
|
desc: "Options to pass to the DSL compilers",
|
|
145
145
|
default: {}
|
|
146
|
-
option :lsp_addon,
|
|
147
|
-
type: :boolean,
|
|
148
|
-
desc: "Generate DSL RBIs from the LSP add-on. Internal to Tapioca and not intended for end-users",
|
|
149
|
-
default: false,
|
|
150
|
-
hide: true
|
|
151
146
|
def dsl(*constant_or_paths)
|
|
152
147
|
set_environment(options)
|
|
153
148
|
|
|
@@ -170,7 +165,7 @@ module Tapioca
|
|
|
170
165
|
app_root: options[:app_root],
|
|
171
166
|
halt_upon_load_error: options[:halt_upon_load_error],
|
|
172
167
|
compiler_options: options[:compiler_options],
|
|
173
|
-
lsp_addon:
|
|
168
|
+
lsp_addon: self.class.addon_mode,
|
|
174
169
|
}
|
|
175
170
|
|
|
176
171
|
command = if options[:verify]
|
|
@@ -379,9 +374,22 @@ module Tapioca
|
|
|
379
374
|
end
|
|
380
375
|
|
|
381
376
|
no_commands do
|
|
377
|
+
@addon_mode = false
|
|
378
|
+
|
|
382
379
|
class << self
|
|
380
|
+
extend T::Sig
|
|
381
|
+
|
|
382
|
+
# Indicates that we are running from the LSP, set using the `addon_mode!` method
|
|
383
|
+
attr_reader :addon_mode
|
|
384
|
+
|
|
385
|
+
sig { void }
|
|
386
|
+
def addon_mode!
|
|
387
|
+
@addon_mode = true
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
sig { returns(T::Boolean) }
|
|
383
391
|
def exit_on_failure?
|
|
384
|
-
|
|
392
|
+
!@addon_mode
|
|
385
393
|
end
|
|
386
394
|
end
|
|
387
395
|
end
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
require "uri/file"
|
|
5
5
|
|
|
6
|
-
module
|
|
7
|
-
class
|
|
6
|
+
module Tapioca
|
|
7
|
+
class SourceURI < URI::File
|
|
8
8
|
extend T::Sig
|
|
9
9
|
|
|
10
10
|
COMPONENT = T.let(
|
|
@@ -37,7 +37,7 @@ module URI
|
|
|
37
37
|
gem_version: T.nilable(String),
|
|
38
38
|
path: String,
|
|
39
39
|
line_number: T.nilable(String),
|
|
40
|
-
).returns(
|
|
40
|
+
).returns(T.attached_class)
|
|
41
41
|
end
|
|
42
42
|
def build(gem_name:, gem_version:, path:, line_number:)
|
|
43
43
|
super(
|
data/lib/tapioca/loaders/dsl.rb
CHANGED
|
@@ -45,6 +45,27 @@ module Tapioca
|
|
|
45
45
|
load_dsl_compilers
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
sig { void }
|
|
49
|
+
def reload_custom_compilers
|
|
50
|
+
# Remove all loaded custom compilers
|
|
51
|
+
::Tapioca::Dsl::Compiler.descendants.each do |compiler|
|
|
52
|
+
name = compiler.name
|
|
53
|
+
next unless name && @custom_compiler_paths.include?(Module.const_source_location(name)&.first)
|
|
54
|
+
|
|
55
|
+
*parts, unqualified_name = name.split("::")
|
|
56
|
+
|
|
57
|
+
if parts.empty?
|
|
58
|
+
Object.send(:remove_const, unqualified_name)
|
|
59
|
+
else
|
|
60
|
+
parts.join("::").safe_constantize.send(:remove_const, unqualified_name)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Remove from $LOADED_FEATURES each workspace compiler file and then re-load
|
|
65
|
+
@custom_compiler_paths.each { |path| $LOADED_FEATURES.delete(path) }
|
|
66
|
+
load_custom_dsl_compilers
|
|
67
|
+
end
|
|
68
|
+
|
|
48
69
|
protected
|
|
49
70
|
|
|
50
71
|
sig do
|
|
@@ -57,6 +78,7 @@ module Tapioca
|
|
|
57
78
|
@eager_load = eager_load
|
|
58
79
|
@app_root = app_root
|
|
59
80
|
@halt_upon_load_error = halt_upon_load_error
|
|
81
|
+
@custom_compiler_paths = T.let([], T::Array[String])
|
|
60
82
|
end
|
|
61
83
|
|
|
62
84
|
sig { void }
|
|
@@ -89,12 +111,7 @@ module Tapioca
|
|
|
89
111
|
end
|
|
90
112
|
|
|
91
113
|
# Load all custom compilers from the project
|
|
92
|
-
|
|
93
|
-
"#{@tapioca_path}/generators/**/*.rb", # TODO: Here for backcompat, remove later
|
|
94
|
-
"#{@tapioca_path}/compilers/**/*.rb",
|
|
95
|
-
]).each do |compiler|
|
|
96
|
-
require File.expand_path(compiler)
|
|
97
|
-
end
|
|
114
|
+
load_custom_dsl_compilers
|
|
98
115
|
|
|
99
116
|
say("Done", :green)
|
|
100
117
|
end
|
|
@@ -112,6 +129,17 @@ module Tapioca
|
|
|
112
129
|
|
|
113
130
|
say("Done", :green)
|
|
114
131
|
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
sig { void }
|
|
136
|
+
def load_custom_dsl_compilers
|
|
137
|
+
@custom_compiler_paths = Dir.glob([
|
|
138
|
+
"#{@tapioca_path}/generators/**/*.rb", # TODO: Here for backcompat, remove later
|
|
139
|
+
"#{@tapioca_path}/compilers/**/*.rb",
|
|
140
|
+
])
|
|
141
|
+
@custom_compiler_paths.each { |compiler| require File.expand_path(compiler) }
|
|
142
|
+
end
|
|
115
143
|
end
|
|
116
144
|
end
|
|
117
145
|
end
|
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tapioca
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.16.
|
|
4
|
+
version: 0.16.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ufuk Kayserilioglu
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
- Peter Zhu
|
|
11
11
|
bindir: exe
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2025-
|
|
13
|
+
date: 2025-02-12 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: benchmark
|
|
@@ -149,6 +149,8 @@ files:
|
|
|
149
149
|
- README.md
|
|
150
150
|
- exe/tapioca
|
|
151
151
|
- lib/ruby_lsp/tapioca/addon.rb
|
|
152
|
+
- lib/ruby_lsp/tapioca/lockfile_diff_parser.rb
|
|
153
|
+
- lib/ruby_lsp/tapioca/run_gem_rbi_check.rb
|
|
152
154
|
- lib/ruby_lsp/tapioca/server_addon.rb
|
|
153
155
|
- lib/tapioca.rb
|
|
154
156
|
- lib/tapioca/bundler_ext/auto_require_hook.rb
|
|
@@ -297,7 +299,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
297
299
|
- !ruby/object:Gem::Version
|
|
298
300
|
version: '0'
|
|
299
301
|
requirements: []
|
|
300
|
-
rubygems_version: 3.6.
|
|
302
|
+
rubygems_version: 3.6.3
|
|
301
303
|
specification_version: 4
|
|
302
304
|
summary: A Ruby Interface file generator for gems, core types and the Ruby standard
|
|
303
305
|
library
|