Class: RubyLsp::Tapioca::Addon

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

Instance Method Summary collapse

Constructor Details

#initializeAddon

: -> void



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

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



35
36
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
# File 'lib/ruby_lsp/tapioca/addon.rb', line 35

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 = ::RubyLsp::Addon.get("Ruby LSP Rails", ">= 0.4.0", "< 0.5") #: as ::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



71
72
# File 'lib/ruby_lsp/tapioca/addon.rb', line 71

def deactivate
end

#nameObject

: -> String



76
77
78
# File 'lib/ruby_lsp/tapioca/addon.rb', line 76

def name
  "Tapioca"
end

#versionObject

: -> String



82
83
84
# File 'lib/ruby_lsp/tapioca/addon.rb', line 82

def version
  "0.1.3"
end

#workspace_did_change_watched_files(changes) ⇒ Object

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



87
88
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
# File 'lib/ruby_lsp/tapioca/addon.rb', line 87

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
  index = @index #: as !nil

  constants = changes.flat_map do |change|
    path = URI(change[:uri]).to_standardized_path #: String?
    next unless path && 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 = 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