Class: RubyLsp::Tapioca::Addon

Inherits:
Addon
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/ruby_lsp/tapioca/addon.rb

Instance Method Summary collapse

Constructor Details

#initializeAddon

: -> void



24
25
26
27
28
29
30
31
32
33
# File 'lib/ruby_lsp/tapioca/addon.rb', line 24

def initialize
  super

  @global_state = nil #: RubyLsp::GlobalState?
  @rails_runner_client = Rails::NullClient.new #: RubyLsp::Rails::RunnerClient
  @index = nil #: RubyIndexer::Index?
  @file_checksums = {} #: Hash[String, String]
  @lockfile_diff = nil #: String?
  @outgoing_queue = nil #: Thread::Queue?
end

Instance Method Details

#activate(global_state, outgoing_queue) ⇒ Object

: (RubyLsp::GlobalState global_state, Thread::Queue outgoing_queue) -> void



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ruby_lsp/tapioca/addon.rb', line 37

def activate(global_state, outgoing_queue)
  @global_state = global_state
  return unless @global_state.enabled_feature?(:tapiocaAddon)

  @index = @global_state.index
  @outgoing_queue = outgoing_queue
  Thread.new do
    # Get a handle to the Rails add-on's runtime client. The call to `rails_runner_client` will block this thread
    # until the server has finished booting, but it will not block the main LSP. This has to happen inside of a
    # thread
    addon = T.cast(::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.4.0", "< 0.5"), ::RubyLsp::Rails::Addon)
    @rails_runner_client = addon.rails_runner_client
    @outgoing_queue << Notification.window_log_message("Activating Tapioca add-on v#{version}")
    @rails_runner_client.register_server_addon(File.expand_path("server_addon.rb", __dir__))
    @rails_runner_client.delegate_notification(
      server_addon_name: "Tapioca",
      request_name: "load_compilers_and_extensions",
      workspace_path: @global_state.workspace_path,
    )

    send_usage_telemetry("activated")
    run_gem_rbi_check
  rescue IncompatibleApiError
    send_usage_telemetry("incompatible_api_error")

    # The requested version for the Rails add-on no longer matches. We need to upgrade and fix the breaking
    # changes
    @outgoing_queue << Notification.window_log_message(
      "IncompatibleApiError: Cannot activate Tapioca LSP add-on",
      type: Constant::MessageType::WARNING,
    )
  end
end

#deactivateObject

: -> void



73
74
# File 'lib/ruby_lsp/tapioca/addon.rb', line 73

def deactivate
end

#nameObject

: -> String



78
79
80
# File 'lib/ruby_lsp/tapioca/addon.rb', line 78

def name
  "Tapioca"
end

#versionObject

: -> String



84
85
86
# File 'lib/ruby_lsp/tapioca/addon.rb', line 84

def version
  "0.1.3"
end

#workspace_did_change_watched_files(changes) ⇒ Object

: (Array[String, type: Integer] changes) -> void



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ruby_lsp/tapioca/addon.rb', line 89

def workspace_did_change_watched_files(changes)
  return unless @global_state&.enabled_feature?(:tapiocaAddon)
  return unless @rails_runner_client.connected?

  has_route_change = false #: bool
  has_fixtures_change = false #: bool
  needs_compiler_reload = false #: bool

  constants = changes.flat_map do |change|
    path = URI(change[:uri]).to_standardized_path
    next unless file_updated?(change, path)

    if File.fnmatch("**/fixtures/**/*.yml{,.erb}", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
      has_fixtures_change = true
      next
    end

    if File.basename(path) == "routes.rb" || File.fnmatch?("**/routes/**/*.rb", path, File::FNM_PATHNAME)
      has_route_change = true
      next
    end

    next if File.fnmatch?("**/{test,spec,features}/**/*", path, File::FNM_PATHNAME | File::FNM_EXTGLOB)

    if File.fnmatch?("**/tapioca/**/compilers/**/*.rb", path, File::FNM_PATHNAME)
      needs_compiler_reload = true
      next
    end

    entries = T.must(@index).entries_for(change[:uri])
    next unless entries

    entries.filter_map do |entry|
      entry.name if entry.class == RubyIndexer::Entry::Class || entry.class == RubyIndexer::Entry::Module
    end
  end.compact

  return if constants.empty? && !has_route_change && !has_fixtures_change && !needs_compiler_reload

  @rails_runner_client.trigger_reload

  if needs_compiler_reload
    @rails_runner_client.delegate_notification(
      server_addon_name: "Tapioca",
      request_name: "reload_workspace_compilers",
      workspace_path: @global_state.workspace_path,
    )
  end

  if has_route_change
    send_usage_telemetry("route_dsl")
    @rails_runner_client.delegate_notification(server_addon_name: "Tapioca", request_name: "route_dsl")
  end

  if has_fixtures_change
    send_usage_telemetry("fixtures_dsl")
    @rails_runner_client.delegate_notification(server_addon_name: "Tapioca", request_name: "fixtures_dsl")
  end

  if constants.any?
    send_usage_telemetry("dsl")
    @rails_runner_client.delegate_notification(
      server_addon_name: "Tapioca",
      request_name: "dsl",
      constants: constants,
    )
  end
end