tapioca 0.16.9 → 0.17.7

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.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -0
  3. data/exe/tapioca +6 -1
  4. data/lib/ruby_lsp/tapioca/addon.rb +73 -43
  5. data/lib/ruby_lsp/tapioca/run_gem_rbi_check.rb +43 -43
  6. data/lib/ruby_lsp/tapioca/server_addon.rb +13 -10
  7. data/lib/tapioca/bundler_ext/auto_require_hook.rb +6 -14
  8. data/lib/tapioca/cli.rb +16 -8
  9. data/lib/tapioca/commands/abstract_dsl.rb +39 -66
  10. data/lib/tapioca/commands/abstract_gem.rb +25 -46
  11. data/lib/tapioca/commands/annotations.rb +28 -34
  12. data/lib/tapioca/commands/check_shims.rb +6 -15
  13. data/lib/tapioca/commands/command.rb +12 -26
  14. data/lib/tapioca/commands/command_without_tracker.rb +2 -5
  15. data/lib/tapioca/commands/configure.rb +11 -16
  16. data/lib/tapioca/commands/dsl_compiler_list.rb +2 -1
  17. data/lib/tapioca/commands/dsl_generate.rb +2 -1
  18. data/lib/tapioca/commands/dsl_verify.rb +2 -1
  19. data/lib/tapioca/commands/gem_generate.rb +5 -9
  20. data/lib/tapioca/commands/gem_sync.rb +2 -1
  21. data/lib/tapioca/commands/gem_verify.rb +3 -2
  22. data/lib/tapioca/commands/require.rb +3 -7
  23. data/lib/tapioca/commands/todo.rb +6 -10
  24. data/lib/tapioca/dsl/compiler.rb +36 -63
  25. data/lib/tapioca/dsl/compilers/aasm.rb +33 -44
  26. data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +8 -7
  27. data/lib/tapioca/dsl/compilers/action_mailer.rb +6 -5
  28. data/lib/tapioca/dsl/compilers/action_text.rb +6 -5
  29. data/lib/tapioca/dsl/compilers/active_job.rb +6 -10
  30. data/lib/tapioca/dsl/compilers/active_model_attributes.rb +10 -11
  31. data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +5 -6
  32. data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +5 -12
  33. data/lib/tapioca/dsl/compilers/active_record_associations.rb +17 -44
  34. data/lib/tapioca/dsl/compilers/active_record_columns.rb +20 -26
  35. data/lib/tapioca/dsl/compilers/active_record_delegated_types.rb +9 -8
  36. data/lib/tapioca/dsl/compilers/active_record_enum.rb +7 -6
  37. data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +54 -62
  38. data/lib/tapioca/dsl/compilers/active_record_relations.rb +148 -209
  39. data/lib/tapioca/dsl/compilers/active_record_scope.rb +8 -13
  40. data/lib/tapioca/dsl/compilers/active_record_secure_token.rb +5 -4
  41. data/lib/tapioca/dsl/compilers/active_record_store.rb +5 -4
  42. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +19 -28
  43. data/lib/tapioca/dsl/compilers/active_resource.rb +19 -21
  44. data/lib/tapioca/dsl/compilers/active_storage.rb +6 -14
  45. data/lib/tapioca/dsl/compilers/active_support_concern.rb +9 -8
  46. data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +8 -7
  47. data/lib/tapioca/dsl/compilers/active_support_time_ext.rb +5 -4
  48. data/lib/tapioca/dsl/compilers/config.rb +5 -4
  49. data/lib/tapioca/dsl/compilers/frozen_record.rb +7 -11
  50. data/lib/tapioca/dsl/compilers/graphql_input_object.rb +9 -10
  51. data/lib/tapioca/dsl/compilers/graphql_mutation.rb +6 -10
  52. data/lib/tapioca/dsl/compilers/identity_cache.rb +11 -39
  53. data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +9 -18
  54. data/lib/tapioca/dsl/compilers/kredis.rb +7 -8
  55. data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +5 -4
  56. data/lib/tapioca/dsl/compilers/protobuf.rb +13 -26
  57. data/lib/tapioca/dsl/compilers/rails_generators.rb +9 -11
  58. data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +23 -13
  59. data/lib/tapioca/dsl/compilers/smart_properties.rb +32 -38
  60. data/lib/tapioca/dsl/compilers/state_machines.rb +15 -26
  61. data/lib/tapioca/dsl/compilers/url_helpers.rb +10 -9
  62. data/lib/tapioca/dsl/compilers.rb +4 -7
  63. data/lib/tapioca/dsl/helpers/active_model_type_helper.rb +13 -16
  64. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +13 -28
  65. data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +19 -15
  66. data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +5 -24
  67. data/lib/tapioca/dsl/pipeline.rb +30 -58
  68. data/lib/tapioca/executor.rb +6 -12
  69. data/lib/tapioca/gem/events.rb +24 -34
  70. data/lib/tapioca/gem/listeners/base.rb +7 -10
  71. data/lib/tapioca/gem/listeners/dynamic_mixins.rb +4 -2
  72. data/lib/tapioca/gem/listeners/foreign_constants.rb +5 -7
  73. data/lib/tapioca/gem/listeners/methods.rb +36 -47
  74. data/lib/tapioca/gem/listeners/mixins.rb +6 -18
  75. data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +4 -2
  76. data/lib/tapioca/gem/listeners/sorbet_enums.rb +4 -2
  77. data/lib/tapioca/gem/listeners/sorbet_helpers.rb +4 -2
  78. data/lib/tapioca/gem/listeners/sorbet_props.rb +4 -2
  79. data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +4 -2
  80. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +7 -5
  81. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +6 -4
  82. data/lib/tapioca/gem/listeners/source_location.rb +15 -8
  83. data/lib/tapioca/gem/listeners/subconstants.rb +5 -4
  84. data/lib/tapioca/gem/listeners/yard_doc.rb +30 -23
  85. data/lib/tapioca/gem/pipeline.rb +107 -91
  86. data/lib/tapioca/gem_info.rb +1 -1
  87. data/lib/tapioca/gemfile.rb +64 -73
  88. data/lib/tapioca/helpers/cli_helper.rb +4 -7
  89. data/lib/tapioca/helpers/config_helper.rb +17 -29
  90. data/lib/tapioca/helpers/env_helper.rb +2 -5
  91. data/lib/tapioca/helpers/gem_helper.rb +5 -5
  92. data/lib/tapioca/helpers/git_attributes.rb +3 -3
  93. data/lib/tapioca/helpers/rbi_files_helper.rb +76 -73
  94. data/lib/tapioca/helpers/rbi_helper.rb +14 -22
  95. data/lib/tapioca/helpers/sorbet_helper.rb +9 -18
  96. data/lib/tapioca/helpers/source_uri.rb +15 -25
  97. data/lib/tapioca/helpers/test/content.rb +7 -10
  98. data/lib/tapioca/helpers/test/dsl_compiler.rb +20 -33
  99. data/lib/tapioca/helpers/test/isolation.rb +10 -14
  100. data/lib/tapioca/helpers/test/template.rb +6 -11
  101. data/lib/tapioca/internal.rb +18 -8
  102. data/lib/tapioca/loaders/dsl.rb +11 -19
  103. data/lib/tapioca/loaders/gem.rb +6 -21
  104. data/lib/tapioca/loaders/loader.rb +21 -39
  105. data/lib/tapioca/rbi_ext/model.rb +12 -37
  106. data/lib/tapioca/rbi_formatter.rb +10 -19
  107. data/lib/tapioca/rbs/rewriter.rb +55 -0
  108. data/lib/tapioca/repo_index.rb +7 -9
  109. data/lib/tapioca/runtime/attached_class_of_32.rb +1 -1
  110. data/lib/tapioca/runtime/attached_class_of_legacy.rb +2 -5
  111. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +23 -23
  112. data/lib/tapioca/runtime/generic_type_registry.rb +13 -23
  113. data/lib/tapioca/runtime/reflection.rb +81 -60
  114. data/lib/tapioca/runtime/source_location.rb +44 -0
  115. data/lib/tapioca/runtime/trackers/autoload.rb +7 -9
  116. data/lib/tapioca/runtime/trackers/constant_definition.rb +18 -14
  117. data/lib/tapioca/runtime/trackers/method_definition.rb +65 -0
  118. data/lib/tapioca/runtime/trackers/mixin.rb +8 -11
  119. data/lib/tapioca/runtime/trackers/required_ancestor.rb +3 -3
  120. data/lib/tapioca/runtime/trackers/tracker.rb +3 -6
  121. data/lib/tapioca/runtime/trackers.rb +5 -8
  122. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +9 -15
  123. data/lib/tapioca/sorbet_ext/name_patch.rb +2 -2
  124. data/lib/tapioca/sorbet_ext/proc_bind_patch.rb +1 -1
  125. data/lib/tapioca/static/requires_compiler.rb +6 -6
  126. data/lib/tapioca/static/symbol_loader.rb +14 -16
  127. data/lib/tapioca/static/symbol_table_parser.rb +8 -8
  128. data/lib/tapioca/version.rb +1 -1
  129. data/lib/tapioca.rb +22 -29
  130. metadata +27 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2d6abd53e84e3b22cc6045597788c98d8f9571f486976baa8db4e5beb6ccd10
4
- data.tar.gz: 5aa9162275489d2280f07419f74a4243c9ee813f922dd367e6260bae0fa37331
3
+ metadata.gz: d003e839857cc260e7b1ab86fd4a48d93efc53e4a43ac9ccac6c85d9a89e63a1
4
+ data.tar.gz: 900f2d0369802b0fac34f60c04e3c238cd68e439c1d6d60f4b9ed5d8c82770f0
5
5
  SHA512:
6
- metadata.gz: 4fd946bacca4a56fe9f9befaa65404393719cacedf4877ce2e8ba0d9d5ab150d9957bea7298783a04e456170086b035b06b42b0312bc2840e1465631259801a1
7
- data.tar.gz: '0825b9451bed3958b7132008993675de624753d41d5b7b267b50e8b7cf2900f344e5c666ac7726686ebfe9c997592dec6c02b5b2e1d14b144cdbaf837daeac08'
6
+ metadata.gz: b2f68a283b2499203877730fb5f981ed1e6c09c686a6c3e59297bea18361b5dd2d1831581f7b3edce6cf38936819544feeeebe59b1747520738542b0ae606ffe
7
+ data.tar.gz: '09769a8ab2aac1763cadecd013c9979c58d750c052f94c26eccfcbbd0a7ca07e0cb027c18e3737e90b90e85d4bebfca2fb4e8e559fdd9c4a0dd472ce5e59ee77'
data/README.md CHANGED
@@ -52,6 +52,9 @@ Tapioca makes it easy to work with [Sorbet](https://sorbet.org) in your codebase
52
52
  * [Writing custom DSL extensions](#writing-custom-dsl-extensions)
53
53
  * [RBI files for missing constants and methods](#rbi-files-for-missing-constants-and-methods)
54
54
  * [Configuration](#configuration)
55
+ * [Editor Integration](#editor-integration)
56
+ * [Setup](#setup)
57
+ * [Features](#features)
55
58
  * [Contributing](#contributing)
56
59
  * [DSL compilers](#dsl-compilers)
57
60
  * [License](#license)
@@ -395,6 +398,12 @@ Tapioca also supports pulling annotations from multiple sources:
395
398
  $ bin/tapioca annotations --sources https://raw.githubusercontent.com/$USER/$REPO1/$BRANCH https://raw.githubusercontent.com/$USER/$REPO2/$BRANCH
396
399
  ```
397
400
 
401
+ You can also specify a local directory path to pull annotations from
402
+
403
+ ```shell
404
+ $ bin/tapioca annotations --sources path/to/folder
405
+ ```
406
+
398
407
  #### Basic authentication
399
408
 
400
409
  Private repositories can be used as sources by passing the option `--auth` with an authentication string. For Github, this string is `token $TOKEN` where `$TOKEN` is a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token):
@@ -993,6 +1002,18 @@ annotations:
993
1002
  ```
994
1003
  <!-- END_CONFIG_TEMPLATE -->
995
1004
 
1005
+ ## Editor Integration
1006
+
1007
+ ### Setup
1008
+
1009
+ Tapioca supports generating RBIs upon file changes through the [Ruby LSP's add-on system](https://shopify.github.io/ruby-lsp/#add-ons). It's enabled by default on VS Code using [Ruby LSP](https://shopify.github.io/ruby-lsp/#usage), and you can enable it for other editors by supplying the `enabledFeatureFlags` option in LSP configuration with a hash value of `"tapiocaAddon": true`.
1010
+
1011
+ If you'd like to disable the Tapioca add-on you can set `tapiocaAddon` to `false` in your LSP configuration. For VS Code this looks like `"rubyLsp.featureFlags": { "tapiocaAddon": false }` in your `settings.json`.
1012
+
1013
+ ### Features
1014
+ - DSL RBI generation: When editing a Ruby file, Tapioca will execute `bin/tapioca dsl` with the constants found in your file, e.g. `bin/tapioca dsl MyClass`
1015
+ - Gem RBI generation: When changes are made to `Gemfile.lock`, Tapioca will execute `bin/tapioca gem` with the updated gem names, e.g. `bin/tapioca gem my_gem`
1016
+
996
1017
  ## Contributing
997
1018
 
998
1019
  Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/tapioca. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://github.com/Shopify/tapioca/blob/main/CODE_OF_CONDUCT.md) code of conduct.
data/exe/tapioca CHANGED
@@ -27,4 +27,9 @@ end
27
27
 
28
28
  require_relative "../lib/tapioca/internal"
29
29
 
30
- Tapioca::Cli.start(ARGV)
30
+ begin
31
+ Tapioca::Cli.start(ARGV)
32
+ rescue Tapioca::Error => e
33
+ warn(e.message)
34
+ exit(1)
35
+ end
@@ -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.1", "< 0.24")
4
+ RubyLsp::Addon.depend_on_ruby_lsp!(">= 0.23.10", "< 0.27")
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
@@ -18,21 +18,20 @@ require "ruby_lsp/tapioca/run_gem_rbi_check"
18
18
  module RubyLsp
19
19
  module Tapioca
20
20
  class Addon < ::RubyLsp::Addon
21
- extend T::Sig
22
-
23
- sig { void }
21
+ #: -> void
24
22
  def initialize
25
23
  super
26
24
 
27
- @global_state = T.let(nil, T.nilable(RubyLsp::GlobalState))
28
- @rails_runner_client = T.let(Rails::NullClient.new, RubyLsp::Rails::RunnerClient)
29
- @index = T.let(nil, T.nilable(RubyIndexer::Index))
30
- @file_checksums = T.let({}, T::Hash[String, String])
31
- @lockfile_diff = T.let(nil, T.nilable(String))
32
- @outgoing_queue = T.let(nil, T.nilable(Thread::Queue))
25
+ @global_state = nil #: RubyLsp::GlobalState?
26
+ @rails_runner_client = Rails::NullClient.new #: RubyLsp::Rails::RunnerClient
27
+ @index = nil #: RubyIndexer::Index?
28
+ @file_checksums = {} #: Hash[String, String]
29
+ @lockfile_diff = nil #: String?
30
+ @outgoing_queue = nil #: Thread::Queue?
33
31
  end
34
32
 
35
- sig { override.params(global_state: RubyLsp::GlobalState, outgoing_queue: Thread::Queue).void }
33
+ # @override
34
+ #: (RubyLsp::GlobalState global_state, Thread::Queue outgoing_queue) -> void
36
35
  def activate(global_state, outgoing_queue)
37
36
  @global_state = global_state
38
37
  return unless @global_state.enabled_feature?(:tapiocaAddon)
@@ -43,7 +42,7 @@ module RubyLsp
43
42
  # Get a handle to the Rails add-on's runtime client. The call to `rails_runner_client` will block this thread
44
43
  # until the server has finished booting, but it will not block the main LSP. This has to happen inside of a
45
44
  # thread
46
- addon = T.cast(::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.4.0", "< 0.5"), ::RubyLsp::Rails::Addon)
45
+ addon = ::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.4.0", "< 0.5") #: as ::RubyLsp::Rails::Addon
47
46
  @rails_runner_client = addon.rails_runner_client
48
47
  @outgoing_queue << Notification.window_log_message("Activating Tapioca add-on v#{version}")
49
48
  @rails_runner_client.register_server_addon(File.expand_path("server_addon.rb", __dir__))
@@ -53,8 +52,11 @@ module RubyLsp
53
52
  workspace_path: @global_state.workspace_path,
54
53
  )
55
54
 
55
+ send_usage_telemetry("activated")
56
56
  run_gem_rbi_check
57
57
  rescue IncompatibleApiError
58
+ send_usage_telemetry("incompatible_api_error")
59
+
58
60
  # The requested version for the Rails add-on no longer matches. We need to upgrade and fix the breaking
59
61
  # changes
60
62
  @outgoing_queue << Notification.window_log_message(
@@ -64,36 +66,39 @@ module RubyLsp
64
66
  end
65
67
  end
66
68
 
67
- sig { override.void }
69
+ # @override
70
+ #: -> void
68
71
  def deactivate
69
72
  end
70
73
 
71
- sig { override.returns(String) }
74
+ # @override
75
+ #: -> String
72
76
  def name
73
77
  "Tapioca"
74
78
  end
75
79
 
76
- sig { override.returns(String) }
80
+ # @override
81
+ #: -> String
77
82
  def version
78
- "0.1.1"
83
+ "0.1.3"
79
84
  end
80
85
 
81
- sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
86
+ #: (Array[{uri: String, type: Integer}] changes) -> void
82
87
  def workspace_did_change_watched_files(changes)
83
- return unless T.must(@global_state).enabled_feature?(:tapiocaAddon)
88
+ return unless @global_state&.enabled_feature?(:tapiocaAddon)
84
89
  return unless @rails_runner_client.connected?
85
90
 
86
- has_route_change = T.let(false, T::Boolean)
87
- has_fixtures_change = T.let(false, T::Boolean)
88
- needs_compiler_reload = T.let(false, T::Boolean)
91
+ has_route_change = false #: bool
92
+ has_fixtures_change = false #: bool
93
+ needs_compiler_reload = false #: bool
94
+ index = @index #: as !nil
89
95
 
90
96
  constants = changes.flat_map do |change|
91
- path = URI(change[:uri]).to_standardized_path
92
- next if path.end_with?("_test.rb", "_spec.rb")
93
- next unless file_updated?(change, path)
97
+ path = URI(change[:uri]).to_standardized_path #: String?
98
+ next unless path && file_updated?(change, path)
94
99
 
95
- if File.fnmatch?("**/tapioca/**/compilers/**/*.rb", path, File::FNM_PATHNAME)
96
- needs_compiler_reload = true
100
+ if File.fnmatch("**/fixtures/**/*.yml{,.erb}", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
101
+ has_fixtures_change = true
97
102
  next
98
103
  end
99
104
 
@@ -102,13 +107,14 @@ module RubyLsp
102
107
  next
103
108
  end
104
109
 
105
- # NOTE: We only get notification for fixtures if ruby-lsp-rails is v0.3.31 or higher
106
- if File.fnmatch("**/fixtures/**/*.yml{,.erb}", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
107
- has_fixtures_change = true
110
+ next if File.fnmatch?("**/{test,spec,features}/**/*", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
111
+
112
+ if File.fnmatch?("**/tapioca/**/compilers/**/*.rb", path, File::FNM_PATHNAME)
113
+ needs_compiler_reload = true
108
114
  next
109
115
  end
110
116
 
111
- entries = T.must(@index).entries_for(change[:uri])
117
+ entries = index.entries_for(change[:uri])
112
118
  next unless entries
113
119
 
114
120
  entries.filter_map do |entry|
@@ -124,19 +130,22 @@ module RubyLsp
124
130
  @rails_runner_client.delegate_notification(
125
131
  server_addon_name: "Tapioca",
126
132
  request_name: "reload_workspace_compilers",
127
- workspace_path: T.must(@global_state).workspace_path,
133
+ workspace_path: @global_state.workspace_path,
128
134
  )
129
135
  end
130
136
 
131
137
  if has_route_change
138
+ send_usage_telemetry("route_dsl")
132
139
  @rails_runner_client.delegate_notification(server_addon_name: "Tapioca", request_name: "route_dsl")
133
140
  end
134
141
 
135
142
  if has_fixtures_change
143
+ send_usage_telemetry("fixtures_dsl")
136
144
  @rails_runner_client.delegate_notification(server_addon_name: "Tapioca", request_name: "fixtures_dsl")
137
145
  end
138
146
 
139
147
  if constants.any?
148
+ send_usage_telemetry("dsl")
140
149
  @rails_runner_client.delegate_notification(
141
150
  server_addon_name: "Tapioca",
142
151
  request_name: "dsl",
@@ -147,8 +156,29 @@ module RubyLsp
147
156
 
148
157
  private
149
158
 
150
- sig { params(change: T::Hash[Symbol, T.untyped], path: String).returns(T::Boolean) }
159
+ #: (String feature_name) -> void
160
+ def send_usage_telemetry(feature_name)
161
+ return unless @outgoing_queue && @global_state
162
+
163
+ # Telemetry is not captured by default even if events are produced by the server
164
+ # See https://github.com/Shopify/ruby-lsp/tree/main/vscode#telemetry
165
+ @outgoing_queue << Notification.telemetry({
166
+ eventName: "tapioca_addon.feature_usage",
167
+ type: "data",
168
+ data: {
169
+ type: "counter",
170
+ attributes: {
171
+ label: feature_name,
172
+ machineId: @global_state.telemetry_machine_id,
173
+ },
174
+ },
175
+ })
176
+ end
177
+
178
+ #: (Hash[Symbol, untyped] change, String path) -> bool
151
179
  def file_updated?(change, path)
180
+ queue = @outgoing_queue #: as !nil
181
+
152
182
  case change[:type]
153
183
  when Constant::FileChangeType::CREATED
154
184
  @file_checksums[path] = Zlib.crc32(File.read(path)).to_s
@@ -156,7 +186,7 @@ module RubyLsp
156
186
  when Constant::FileChangeType::CHANGED
157
187
  current_checksum = Zlib.crc32(File.read(path)).to_s
158
188
  if @file_checksums[path] == current_checksum
159
- T.must(@outgoing_queue) << Notification.window_log_message(
189
+ queue << Notification.window_log_message(
160
190
  "File has not changed. Skipping #{path}",
161
191
  type: Constant::MessageType::INFO,
162
192
  )
@@ -167,7 +197,7 @@ module RubyLsp
167
197
  when Constant::FileChangeType::DELETED
168
198
  @file_checksums.delete(path)
169
199
  else
170
- T.must(@outgoing_queue) << Notification.window_log_message(
200
+ queue << Notification.window_log_message(
171
201
  "Unexpected file change type: #{change[:type]}",
172
202
  type: Constant::MessageType::WARNING,
173
203
  )
@@ -176,18 +206,18 @@ module RubyLsp
176
206
  false
177
207
  end
178
208
 
179
- sig { void }
209
+ #: -> void
180
210
  def run_gem_rbi_check
181
- gem_rbi_check = RunGemRbiCheck.new(T.must(@global_state).workspace_path)
211
+ state = @global_state #: as !nil
212
+ gem_rbi_check = RunGemRbiCheck.new(state.workspace_path)
182
213
  gem_rbi_check.run
183
214
 
184
- T.must(@outgoing_queue) << Notification.window_log_message(
185
- gem_rbi_check.stdout,
186
- ) unless gem_rbi_check.stdout.empty?
187
- T.must(@outgoing_queue) << Notification.window_log_message(
188
- gem_rbi_check.stderr,
189
- type: Constant::MessageType::WARNING,
190
- ) unless gem_rbi_check.stderr.empty?
215
+ queue = @outgoing_queue #: as !nil
216
+ queue << Notification.window_log_message(gem_rbi_check.stdout) unless gem_rbi_check.stdout.empty?
217
+
218
+ unless gem_rbi_check.stderr.empty?
219
+ queue << Notification.window_log_message(gem_rbi_check.stderr, type: Constant::MessageType::WARNING)
220
+ end
191
221
  end
192
222
  end
193
223
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "open3"
@@ -7,21 +7,21 @@ require "ruby_lsp/tapioca/lockfile_diff_parser"
7
7
  module RubyLsp
8
8
  module Tapioca
9
9
  class RunGemRbiCheck
10
- extend T::Sig
10
+ #: String
11
+ attr_reader :stdout, :stderr
11
12
 
12
- attr_reader :stdout
13
- attr_reader :stderr
13
+ #: Process::Status?
14
14
  attr_reader :status
15
15
 
16
- sig { params(project_path: String).void }
16
+ #: (String project_path) -> void
17
17
  def initialize(project_path)
18
18
  @project_path = project_path
19
- @stdout = T.let("", String)
20
- @stderr = T.let("", String)
21
- @status = T.let(nil, T.nilable(Process::Status))
19
+ @stdout = "" #: String
20
+ @stderr = "" #: String
21
+ @status = nil #: Process::Status?
22
22
  end
23
23
 
24
- sig { void }
24
+ #: -> void
25
25
  def run
26
26
  return log_message("Not a git repository") unless git_repo?
27
27
 
@@ -34,37 +34,35 @@ module RubyLsp
34
34
 
35
35
  private
36
36
 
37
- attr_reader :project_path
38
-
39
- sig { returns(T.nilable(T::Boolean)) }
37
+ #: -> bool?
40
38
  def git_repo?
41
- _, status = Open3.capture2e("git", "rev-parse", "--is-inside-work-tree", chdir: project_path)
39
+ _, status = Open3.capture2e("git", "rev-parse", "--is-inside-work-tree", chdir: @project_path)
42
40
  status.success?
43
41
  end
44
42
 
45
- sig { returns(T::Boolean) }
43
+ #: -> bool
46
44
  def lockfile_changed?
47
45
  !lockfile_diff.empty?
48
46
  end
49
47
 
50
- sig { returns(Pathname) }
48
+ #: -> Pathname
51
49
  def lockfile
52
- @lockfile ||= T.let(Pathname(project_path).join("Gemfile.lock"), T.nilable(Pathname))
50
+ @lockfile ||= Pathname(@project_path).join("Gemfile.lock") #: Pathname?
53
51
  end
54
52
 
55
- sig { returns(String) }
53
+ #: -> String
56
54
  def lockfile_diff
57
- @lockfile_diff ||= T.let(read_lockfile_diff, T.nilable(String))
55
+ @lockfile_diff ||= read_lockfile_diff #: String?
58
56
  end
59
57
 
60
- sig { returns(String) }
58
+ #: -> String
61
59
  def read_lockfile_diff
62
60
  return "" unless lockfile.exist?
63
61
 
64
62
  execute_in_project_path("git", "diff", lockfile.to_s).strip
65
63
  end
66
64
 
67
- sig { void }
65
+ #: -> void
68
66
  def generate_gem_rbis
69
67
  parser = Tapioca::LockfileDiffParser.new(@lockfile_diff)
70
68
  removed_gems = parser.removed_gems
@@ -78,18 +76,19 @@ module RubyLsp
78
76
  end
79
77
  end
80
78
 
81
- sig { params(gems: T::Array[String]).void }
79
+ #: (Array[String] gems) -> void
82
80
  def execute_tapioca_gem_command(gems)
83
81
  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
- )
82
+ stdout, stderr, status = Open3 #: as untyped
83
+ .capture3(
84
+ "bundle",
85
+ "exec",
86
+ "tapioca",
87
+ "gem",
88
+ "--lsp_addon",
89
+ *gems,
90
+ chdir: @project_path,
91
+ )
93
92
 
94
93
  log_message(stdout) unless stdout.empty?
95
94
  @stderr = stderr unless stderr.empty?
@@ -97,16 +96,16 @@ module RubyLsp
97
96
  end
98
97
  end
99
98
 
100
- sig { params(gems: T::Array[String]).void }
99
+ #: (Array[String] gems) -> void
101
100
  def remove_rbis(gems)
102
101
  files = Dir.glob(
103
102
  "sorbet/rbi/gems/{#{gems.join(",")}}@*.rbi",
104
- base: project_path,
103
+ base: @project_path,
105
104
  )
106
105
  delete_files(files, "Removed RBIs for")
107
106
  end
108
107
 
109
- sig { void }
108
+ #: -> void
110
109
  def cleanup_orphaned_rbis
111
110
  untracked_files = git_ls_gem_rbis("--others", "--exclude-standard")
112
111
  deleted_files = git_ls_gem_rbis("--deleted")
@@ -115,37 +114,38 @@ module RubyLsp
115
114
  restore_files(deleted_files, "Restored deleted RBIs")
116
115
  end
117
116
 
118
- sig { params(flags: T.untyped).returns(T::Array[String]) }
117
+ #: (*untyped flags) -> Array[String]
119
118
  def git_ls_gem_rbis(*flags)
120
- flags = T.unsafe(["git", "ls-files", *flags, "sorbet/rbi/gems/"])
121
-
122
- execute_in_project_path(*flags)
119
+ self #: as untyped # rubocop:disable Style/RedundantSelf
120
+ .execute_in_project_path("git", "ls-files", *flags, "sorbet/rbi/gems/")
123
121
  .lines
124
122
  .map(&:strip)
125
123
  end
126
124
 
127
- sig { params(files: T::Array[String], message: String).void }
125
+ #: (Array[String] files, String message) -> void
128
126
  def delete_files(files, message)
129
- files_to_remove = files.map { |file| File.join(project_path, file) }
127
+ files_to_remove = files.map { |file| File.join(@project_path, file) }
130
128
  FileUtils.rm(files_to_remove)
131
129
  log_message("#{message}: #{files.join(", ")}") unless files.empty?
132
130
  end
133
131
 
134
- sig { params(files: T::Array[String], message: String).void }
132
+ #: (Array[String] files, String message) -> void
135
133
  def restore_files(files, message)
136
134
  execute_in_project_path("git", "checkout", "--pathspec-from-file=-", stdin: files.join("\n"))
137
135
  log_message("#{message}: #{files.join(", ")}") unless files.empty?
138
136
  end
139
137
 
140
- sig { params(message: String).void }
138
+ #: (String message) -> void
141
139
  def log_message(message)
142
140
  @stdout += "#{message}\n"
143
141
  end
144
142
 
143
+ #: (*String, ?stdin: String?) -> String
145
144
  def execute_in_project_path(*parts, stdin: nil)
146
- options = { chdir: project_path }
145
+ options = { chdir: @project_path }
147
146
  options[:stdin_data] = stdin if stdin
148
- stdout_and_stderr, _status = T.unsafe(Open3).capture2e(*parts, options)
147
+ stdout_and_stderr, _status = Open3 #: as untyped
148
+ .capture2e(*parts, options)
149
149
  stdout_and_stderr
150
150
  end
151
151
  end
@@ -2,8 +2,6 @@
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"
7
5
 
8
6
  module RubyLsp
9
7
  module Tapioca
@@ -21,13 +19,16 @@ module RubyLsp
21
19
  when "load_compilers_and_extensions"
22
20
  # Load DSL extensions and compilers ahead of time, so that we don't have to pay the price of invoking
23
21
  # `Gem.find_files` on every execution, which is quite expensive
24
- @loader = ::Tapioca::Loaders::Dsl.new(
25
- tapioca_path: ::Tapioca::TAPIOCA_DIR,
26
- eager_load: false,
27
- app_root: params[:workspace_path],
28
- halt_upon_load_error: false,
29
- )
30
- @loader.load_dsl_extensions_and_compilers
22
+ with_notification_wrapper("load_compilers_and_extensions", "Loading DSL compilers") do
23
+ ::Tapioca::Dsl::Compiler.extend(T::Generic)
24
+ @loader = ::Tapioca::Loaders::Dsl.new(
25
+ tapioca_path: ::Tapioca::TAPIOCA_DIR,
26
+ eager_load: false,
27
+ app_root: params[:workspace_path],
28
+ halt_upon_load_error: false,
29
+ )
30
+ @loader.load_dsl_extensions_and_compilers
31
+ end
31
32
  when "dsl"
32
33
  fork do
33
34
  with_notification_wrapper("dsl", "Generating DSL RBIs") do
@@ -62,11 +63,13 @@ module RubyLsp
62
63
  def dsl(constants, *args)
63
64
  load("tapioca/cli.rb") # Reload the CLI to reset thor defaults between requests
64
65
 
66
+ ::Tapioca::Cli.addon_mode!
67
+
65
68
  # Order here is important to avoid having Thor confuse arguments. Do not put an array argument at the end before
66
69
  # the list of constants
67
70
  arguments = ["dsl"]
68
71
  arguments.concat(args)
69
- arguments.push("--lsp_addon", "--workers=1")
72
+ arguments.push("--workers=1")
70
73
  arguments.concat(constants)
71
74
 
72
75
  ::Tapioca::Cli.start(arguments)
@@ -6,19 +6,16 @@ module Tapioca
6
6
  # This is a module that gets prepended to `Bundler::Dependency` and
7
7
  # makes sure even gems marked as `require: false` are required during
8
8
  # `Bundler.require`.
9
+ # @requires_ancestor: ::Bundler::Dependency
9
10
  module AutoRequireHook
10
11
  extend T::Sig
11
- extend T::Helpers
12
-
13
- requires_ancestor { ::Bundler::Dependency }
14
-
15
- @exclude = T.let([], T::Array[String])
16
- @enabled = T.let(false, T::Boolean)
12
+ @exclude = [] #: Array[String]
13
+ @enabled = false #: bool
17
14
 
18
15
  class << self
19
16
  extend T::Sig
20
17
 
21
- sig { params(name: T.untyped).returns(T::Boolean) }
18
+ #: (untyped name) -> bool
22
19
  def excluded?(name)
23
20
  @exclude.include?(name)
24
21
  end
@@ -27,12 +24,7 @@ module Tapioca
27
24
  @enabled
28
25
  end
29
26
 
30
- sig do
31
- type_parameters(:Result).params(
32
- exclude: T::Array[String],
33
- blk: T.proc.returns(T.type_parameter(:Result)),
34
- ).returns(T.type_parameter(:Result))
35
- end
27
+ #: [Result] (exclude: Array[String]) { -> Result } -> Result
36
28
  def override_require_false(exclude:, &blk)
37
29
  @enabled = true
38
30
  @exclude = exclude
@@ -42,7 +34,7 @@ module Tapioca
42
34
  end
43
35
  end
44
36
 
45
- sig { returns(T.untyped).checked(:never) }
37
+ #: -> untyped
46
38
  def autorequire
47
39
  value = super
48
40
 
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: options[:lsp_addon],
168
+ lsp_addon: self.class.addon_mode,
174
169
  }
175
170
 
176
171
  command = if options[:verify]
@@ -358,7 +353,7 @@ module Tapioca
358
353
  default: {}
359
354
  def annotations
360
355
  if !options[:netrc] && options[:netrc_file]
361
- raise Thor::Error, set_color("Options `--no-netrc` and `--netrc-file` can't be used together", :bold, :red)
356
+ raise Tapioca::Error, set_color("Options `--no-netrc` and `--netrc-file` can't be used together", :bold, :red)
362
357
  end
363
358
 
364
359
  command = Commands::Annotations.new(
@@ -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
+ #: -> void
386
+ def addon_mode!
387
+ @addon_mode = true
388
+ end
389
+
390
+ #: -> bool
383
391
  def exit_on_failure?
384
- true
392
+ !@addon_mode
385
393
  end
386
394
  end
387
395
  end