Skip to content

Commit

Permalink
Refactor generating gem rbis with git diff
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcrocha committed Nov 23, 2024
1 parent e225e1a commit 01306e6
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 37 deletions.
35 changes: 13 additions & 22 deletions lib/ruby_lsp/tapioca/addon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ module Tapioca
class Addon < ::RubyLsp::Addon
extend T::Sig

GEMFILE_LOCK_SNAPSHOT = "tmp/tapioca/.gemfile_lock_snapshot"

sig { void }
def initialize
super
Expand All @@ -29,6 +27,7 @@ def initialize
@rails_runner_client = T.let(nil, T.nilable(RubyLsp::Rails::RunnerClient))
@index = T.let(nil, T.nilable(RubyIndexer::Index))
@file_checksums = T.let({}, T::Hash[String, String])
@lockfile_diff = T.let(nil, T.nilable(String))
end

sig { override.params(global_state: RubyLsp::GlobalState, outgoing_queue: Thread::Queue).void }
Expand All @@ -47,7 +46,7 @@ def activate(global_state, outgoing_queue)
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__))

generate_gem_rbis if lockfile_changed?
generate_gem_rbis if git_repo? && lockfile_changed?
rescue IncompatibleApiError
# The requested version for the Rails add-on no longer matches. We need to upgrade and fix the breaking
# changes
Expand Down Expand Up @@ -113,36 +112,28 @@ def workspace_did_change_watched_files(changes)

private

sig { returns(T::Boolean) }
def git_repo?
Dir.exist?(".git")
end

sig { returns(T::Boolean) }
def lockfile_changed?
return false unless Dir.exist?(".git")
!fetch_lockfile_diff.empty?
end

gemfile_status = %x(git status --porcelain Gemfile.lock).strip
!gemfile_status.empty?
sig { returns(String) }
def fetch_lockfile_diff
@lockfile_diff = %x(git diff HEAD Gemfile.lock).strip
end

sig { void }
def generate_gem_rbis
current_lockfile = File.read("Gemfile.lock")
snapshot_lockfile = File.read(GEMFILE_LOCK_SNAPSHOT) if File.exist?(GEMFILE_LOCK_SNAPSHOT)

unless snapshot_lockfile
$stdout.puts("Creating initial Gemfile.lock snapshot at #{GEMFILE_LOCK_SNAPSHOT}")
FileUtils.mkdir_p(File.dirname(GEMFILE_LOCK_SNAPSHOT))
File.write(GEMFILE_LOCK_SNAPSHOT, current_lockfile)
return
end

return if current_lockfile == snapshot_lockfile

T.must(@rails_runner_client).delegate_notification(
server_addon_name: "Tapioca",
request_name: "gem",
snapshot_lockfile: snapshot_lockfile,
current_lockfile: current_lockfile,
diff: T.must(@lockfile_diff),
)

File.write(GEMFILE_LOCK_SNAPSHOT, current_lockfile)
end
end
end
Expand Down
44 changes: 44 additions & 0 deletions lib/ruby_lsp/tapioca/lockfile_diff_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# typed: true
# frozen_string_literal: true

module RubyLsp
module Tapioca
class LockfileDiffParser
GEM_PATTERN = /[+-](.*[\w\-]+)\s*\(/
ADDED_LINE_PATTERN = /^\+.*[\w\-]+ \(.*\)/
REMOVED_LINE_PATTERN = /^-.*[\w\-]+ \(.*\)/

attr_reader :added_or_modified_gems
attr_reader :removed_gems

def initialize(diff_content)
@diff_content = diff_content
@added_or_modified_gems = parse_added_or_modified_gems
@removed_gems = parse_removed_gems
end

private

def parse_added_or_modified_gems
@diff_content
.lines
.filter { |line| line.match?(ADDED_LINE_PATTERN) }
.map { |line| extract_gem(line) }
.uniq
end

def parse_removed_gems
@diff_content
.lines
.filter { |line| line.match?(REMOVED_LINE_PATTERN) }
.map { |line| extract_gem(line) }
.reject { |gem| @added_or_modified_gems.include?(gem) }
.uniq
end

def extract_gem(line)
line.match(GEM_PATTERN)[1].strip
end
end
end
end
22 changes: 7 additions & 15 deletions lib/ruby_lsp/tapioca/server_addon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# frozen_string_literal: true

require "tapioca/internal"
require_relative "lockfile_diff_parser"

module RubyLsp
module Tapioca
Expand All @@ -27,32 +28,23 @@ def dsl(params)
end

def gem(params)
snapshot_specs = parse_lockfile(params[:snapshot_lockfile])
current_specs = parse_lockfile(params[:current_lockfile])
gem_changes = LockfileDiffParser.new(params[:diff])

removed_gems = snapshot_specs.keys - current_specs.keys
changed_gems = current_specs.select { |name, version| snapshot_specs[name] != version }.keys

return $stdout.puts("No gem changes detected") if removed_gems.empty? && changed_gems.empty?
removed_gems = gem_changes.removed_gems
added_or_modified_gems = gem_changes.added_or_modified_gems

if removed_gems.any?
$stdout.puts("Removing RBIs for deleted gems: #{removed_gems.join(", ")}")
FileUtils.rm_f(Dir.glob("sorbet/rbi/gems/{#{removed_gems.join(",")}}@*.rbi"))
end

if changed_gems.any?
$stdout.puts("Generating RBIs for changed gems: #{changed_gems.join(", ")}")
if added_or_modified_gems.any?
$stdout.puts("Generating RBIs for added or modified gems: #{added_or_modified_gems.join(", ")}")

load("tapioca/cli.rb") # Reload the CLI to reset thor defaults between requests
::Tapioca::Cli.start(["gem"] + changed_gems)
::Tapioca::Cli.start(["gem"] + added_or_modified_gems)
end
end

def parse_lockfile(content)
return {} if content.to_s.empty?

Bundler::LockfileParser.new(content).specs.to_h { |spec| [spec.name, spec.version.to_s] }
end
end
end
end

0 comments on commit 01306e6

Please sign in to comment.