-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
124 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#!/usr/bin/env ruby | ||
# frozen_string_literal: true | ||
|
||
# !!!!!!! | ||
# No gems can be required in this file until we invoke bundler setup except inside the forked process that sets up the | ||
# composed bundle | ||
# !!!!!!! | ||
|
||
setup_error = nil | ||
|
||
# Read the initialize request before even starting the server. We need to do this to figure out the workspace URI. | ||
# Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need | ||
# to ensure that we're setting up the bundle in the right place | ||
$stdin.binmode | ||
headers = $stdin.gets("\r\n\r\n") | ||
content_length = headers[/Content-Length: (\d+)/i, 1].to_i | ||
raw_initialize = $stdin.read(content_length) | ||
|
||
bundle_gemfile_file = File.join(".ruby-lsp", "bundle_gemfile") | ||
locked_bundler_version_file = File.join(".ruby-lsp", "locked_bundler_version") | ||
|
||
# Composed the Ruby LSP bundle in a forked process so that we can require gems without polluting the main process | ||
# `$LOAD_PATH` and `Gem.loaded_specs` | ||
pid = fork do | ||
require_relative "../lib/ruby_lsp/setup_bundler" | ||
require "json" | ||
require "uri" | ||
require "core_ext/uri" | ||
|
||
initialize_request = JSON.parse(raw_initialize, symbolize_names: true) | ||
workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri) | ||
workspace_path = workspace_uri && URI(workspace_uri).to_standardized_path | ||
workspace_path ||= Dir.pwd | ||
|
||
env = RubyLsp::SetupBundler.new(workspace_path).setup! | ||
File.write(bundle_gemfile_file, env["BUNDLE_GEMFILE"]) | ||
File.write(locked_bundler_version_file, env["BUNDLER_VERSION"]) if env["BUNDLER_VERSION"] | ||
rescue RubyLsp::SetupBundler::BundleNotLocked | ||
warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server") | ||
end | ||
|
||
# Wait until the composed Bundle is finished | ||
Process.wait(pid) | ||
|
||
begin | ||
# We can't require `bundler/setup` because that file prematurely exits the process if setup fails. However, we can't | ||
# simply require bundler either because the version required might conflict with the one locked in the composed | ||
# bundle. We need the composed bundle sub-process to inform us of the locked Bundler version, so that we can then | ||
# activate the right spec and require the exact Bundler version required by the app | ||
if File.exist?(locked_bundler_version_file) | ||
locked_bundler_version = File.read(locked_bundler_version_file) | ||
Gem::Specification.find_by_name("bundler", locked_bundler_version).activate | ||
end | ||
|
||
require "bundler" | ||
Bundler.ui.level = :silent | ||
|
||
# The composed bundle logic informs the main process about which Gemfile to setup Bundler with | ||
if File.exist?(bundle_gemfile_file) | ||
ENV["BUNDLE_GEMFILE"] = File.read(bundle_gemfile_file) | ||
Bundler.setup | ||
end | ||
rescue StandardError => e | ||
# If installing gems failed for any reason, we don't want to exit the process prematurely. We can still provide most | ||
# features in a degraded mode. We simply save the error so that we can report to the user that certain gems might be | ||
# missing, but we respect the LSP life cycle | ||
setup_error = e | ||
|
||
# If we failed to setup the bundle, the minimum we need is to have our own dependencies activated so that we can | ||
# require the gems the LSP depends on. If even that fails, then there's no way we can continue to run the language | ||
# server | ||
Gem::Specification.find_by_name("ruby-lsp").activate | ||
end | ||
|
||
# Now that the bundle is set up, we can begin actually launching the server | ||
|
||
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__)) | ||
|
||
require "ruby_lsp/load_sorbet" | ||
require "ruby_lsp/internal" | ||
|
||
T::Utils.run_all_sig_blocks | ||
|
||
if ARGV.include?("--debug") | ||
if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM) | ||
$stderr.puts "Debugging is not supported on Windows" | ||
else | ||
begin | ||
ENV.delete("RUBY_DEBUG_IRB_CONSOLE") | ||
require "debug/open_nonstop" | ||
rescue LoadError | ||
$stderr.puts("You need to install the debug gem to use the --debug flag") | ||
end | ||
end | ||
end | ||
|
||
# Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device. | ||
$> = $stderr | ||
|
||
initialize_request = JSON.parse(raw_initialize, symbolize_names: true) if raw_initialize | ||
|
||
RubyLsp::Server.new( | ||
setup_error: setup_error, | ||
initialize_request: initialize_request, | ||
).start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters