Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Tapioca LSP #2001

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Sorbet/TrueSigil:
Include:
- "**/*.rb"
- "**/*.rake"
Exclude:
- "lib/ruby_lsp/tapioca/server_addon.rb"

Style/CaseEquality:
Enabled: false
Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ group :test do
end

gem "kramdown", "~> 2.4"

gem "ruby-lsp", path: "../ruby-lsp"
gem "ruby-lsp-rails", path: "../ruby-lsp-rails"
19 changes: 19 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ GIT
specs:
cityhash (0.6.0)

PATH
remote: ../ruby-lsp-rails
specs:
ruby-lsp-rails (0.3.14)
ruby-lsp (>= 0.17.12, < 0.18.0)

PATH
remote: ../ruby-lsp
specs:
ruby-lsp (0.17.18)
language_server-protocol (~> 3.17.0)
prism (~> 1.0)
rbs (>= 3, < 4)
sorbet-runtime (>= 0.5.10782)

PATH
remote: .
specs:
Expand Down Expand Up @@ -271,6 +286,8 @@ GEM
rbi (0.2.0)
prism (~> 1.0)
sorbet-runtime (>= 0.5.9204)
rbs (3.5.3)
logger
rdoc (6.7.0)
psych (>= 4.0.0)
redis (5.0.8)
Expand Down Expand Up @@ -386,6 +403,8 @@ DEPENDENCIES
rubocop-rspec
rubocop-shopify
rubocop-sorbet (>= 0.4.1)
ruby-lsp!
ruby-lsp-rails!
shopify-money
sidekiq
smart_properties
Expand Down
79 changes: 79 additions & 0 deletions lib/ruby_lsp/tapioca/addon.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# typed: true
# frozen_string_literal: true

require "ruby_lsp/addon"
begin
require "ruby-lsp-rails"
rescue LoadError
return
end

require "tapioca/internal"

# require "ruby_lsp/ruby_lsp_rails/server" # for ServerAddon

# TODO: Use async pattern in Rails addo

module RubyLsp
module Tapioca
class Addon < ::RubyLsp::Addon
extend T::Sig

def initialize
super
@index = T.let(nil, T.nilable(RubyIndexer::Index))
end

def activate(global_state, outgoing_queue)
$stderr.puts("Activating Tapioca LSP addon v#{VERSION}")
@index = global_state.index
@global_state = global_state
addon = T.cast(::RubyLsp::Addon.get("Ruby LSP Rails"), ::RubyLsp::Rails::Addon)

Thread.new do
@rails_runner_client = T.let(addon.rails_runner_client, T.nilable(RubyLsp::Rails::RunnerClient))
T.must(@rails_runner_client).register_server_addon(File.expand_path("server_addon.rb", __dir__))
end
rescue AddonNotFoundError
$stderr.puts("Tapioca LSP: The LSP will not be available as the Ruby LSP Rails addon was not found")
end

sig { override.void }
def deactivate
end

sig { override.returns(String) }
def name
"Tapioca"
end

sig { params(changes: T::Array[{ uri: String, type: Integer }]).void }
def workspace_did_change_watched_files(changes)
unless @rails_runner_client
$stderr.puts "Tapioca LSP: Rails runner client not available yet, skipping request"
return
end

constants = changes.filter_map do |change|
path = change[:uri].gsub("file://", "")

entries = T.must(@index).entries_for(path, RubyIndexer::Entry::Namespace)
next unless entries

entries.grep_v(RubyIndexer::Entry::SingletonClass).map(&:name)
end.flatten

return if constants.empty?

$stderr.puts "Tapioca LSP: Making DSL request with constants #{constants}"

@rails_runner_client.send_notification(
"server_addon/delegate",
request_name: "dsl",
server_addon_name: "Tapioca",
constants: constants,
)
end
end
end
end
39 changes: 39 additions & 0 deletions lib/ruby_lsp/tapioca/server_addon.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

require "tapioca/internal"
# require "ruby_lsp/ruby_lsp_rails/server" # for ServerAddon

module RubyLsp
module Tapioca
class ServerAddon < ::RubyLsp::Rails::ServerAddon
def name
"Tapioca"
end

def execute(request, params)
case request
when "dsl"
dsl(params)
end
rescue => e
File.write("tapioca.txt", "rescue\n#{e.full_message}", mode: "a")
end

private

def dsl(params)
command = ::Tapioca::Commands::DslGenerate.new(
requested_constants: params[:constants],
tapioca_path: ::Tapioca::TAPIOCA_DIR,
requested_paths: [],
outpath: Pathname.new(::Tapioca::DEFAULT_DSL_DIR),
file_header: true,
exclude: [],
only: [],
)

command.generate_without_booting
end
end
end
end
1 change: 1 addition & 0 deletions lib/tapioca.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ class Error < StandardError; end
end

require "tapioca/version"
require "ruby_lsp/tapioca/addon"
1 change: 1 addition & 0 deletions lib/tapioca/commands/abstract_dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def constantize(constant_names, ignore_missing: false)
constant_map = constant_names.to_h do |name|
[name, Object.const_get(name)]
rescue NameError
puts "NameError for #{name}"
[name, nil]
end

Expand Down
12 changes: 12 additions & 0 deletions lib/tapioca/commands/dsl_generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,24 @@
module Tapioca
module Commands
class DslGenerate < AbstractDsl
# LSP entrypoint
sig { void }
def generate_without_booting
Loaders::Dsl.load_subset
generate
end

private

# CLI entrypoint
sig { override.void }
def execute
load_application
generate
end

sig { void }
def generate
say("Compiling DSL RBI files...")
say("")

Expand Down
2 changes: 2 additions & 0 deletions lib/tapioca/dsl/pipeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def compilers
end,
T.nilable(T::Array[T.class_of(Compiler)]),
)
@compilers
end

private
Expand Down Expand Up @@ -147,6 +148,7 @@ def gather_active_compilers(requested_compilers, excluded_compilers)
def gather_constants(requested_constants, requested_paths, skipped_constants)
Compiler.requested_constants = requested_constants
constants = Set.new.compare_by_identity

active_compilers.each do |compiler|
constants.merge(compiler.processable_constants)
end
Expand Down
1 change: 1 addition & 0 deletions lib/tapioca/internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
require "netrc"
require "parallel"
require "pathname"
require "ruby_lsp/internal"
require "shellwords"
require "tempfile"
require "thor"
Expand Down
15 changes: 13 additions & 2 deletions lib/tapioca/loaders/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ def load_application(tapioca_path:, eager_load: true, app_root: ".", halt_upon_l
)
loader.load
end

sig { void }
def load_subset
loader = new(
tapioca_path: Tapioca::TAPIOCA_DIR,
eager_load: false,
app_root: ".",
halt_upon_load_error: true,
)
puts "Loader instantiated"
loader.load_dsl_extensions
loader.load_dsl_compilers
end
end

sig { override.void }
Expand All @@ -30,8 +43,6 @@ def load
load_dsl_compilers
end

protected

sig do
params(tapioca_path: String, eager_load: T::Boolean, app_root: String, halt_upon_load_error: T::Boolean).void
end
Expand Down
1 change: 1 addition & 0 deletions sorbet/config
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
--dir
.
--ignore=/vendor
--ignore=lib/ruby_lsp/tapioca/server_addon.rb
--enable-experimental-requires-ancestor
--suppress-payload-superclass-redefinition-for=Reline::ANSI
Loading
Loading