From 81c81d3199d38bb6f0dafb2103d2226a9941e2ea Mon Sep 17 00:00:00 2001 From: Andy Waite Date: Mon, 25 Mar 2024 14:08:25 -0400 Subject: [PATCH] Allow config to be passed into Indexer (#1814) * Allow config to be passed into Indexer * Update lib/ruby_lsp/server.rb Co-authored-by: Vinicius Stock * Update lib/ruby_lsp/server.rb Co-authored-by: Vinicius Stock * Adjust error message --------- Co-authored-by: Andy Waite Co-authored-by: Vinicius Stock --- .../lib/ruby_indexer/configuration.rb | 34 ++++++------------- lib/ruby_indexer/test/configuration_test.rb | 13 ++----- lib/ruby_lsp/server.rb | 30 +++++++++++++--- test/server_test.rb | 15 ++++++++ 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/lib/ruby_indexer/lib/ruby_indexer/configuration.rb b/lib/ruby_indexer/lib/ruby_indexer/configuration.rb index c9892a0b3..34d5e5c80 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/configuration.rb @@ -43,20 +43,6 @@ def initialize ) end - sig { void } - def load_config - return unless File.exist?(".index.yml") - - config = YAML.parse_file(".index.yml") - return unless config - - config_hash = config.to_ruby - validate_config!(config_hash) - apply_config(config_hash) - rescue Psych::SyntaxError => e - raise e, "Syntax error while loading .index.yml configuration: #{e.message}" - end - sig { returns(T::Array[IndexablePath]) } def indexables excluded_gems = @excluded_gems - @included_gems @@ -158,6 +144,17 @@ def magic_comment_regex @magic_comment_regex ||= T.let(/^#\s*#{@excluded_magic_comments.join("|")}/, T.nilable(Regexp)) end + sig { params(config: T::Hash[String, T.untyped]).void } + def apply_config(config) + validate_config!(config) + + @excluded_gems.concat(config["excluded_gems"]) if config["excluded_gems"] + @included_gems.concat(config["included_gems"]) if config["included_gems"] + @excluded_patterns.concat(config["excluded_patterns"]) if config["excluded_patterns"] + @included_patterns.concat(config["included_patterns"]) if config["included_patterns"] + @excluded_magic_comments.concat(config["excluded_magic_comments"]) if config["excluded_magic_comments"] + end + private sig { params(config: T::Hash[String, T.untyped]).void } @@ -175,15 +172,6 @@ def validate_config!(config) raise ArgumentError, errors.join("\n") if errors.any? end - sig { params(config: T::Hash[String, T.untyped]).void } - def apply_config(config) - @excluded_gems.concat(config["excluded_gems"]) if config["excluded_gems"] - @included_gems.concat(config["included_gems"]) if config["included_gems"] - @excluded_patterns.concat(config["excluded_patterns"]) if config["excluded_patterns"] - @included_patterns.concat(config["included_patterns"]) if config["included_patterns"] - @excluded_magic_comments.concat(config["excluded_magic_comments"]) if config["excluded_magic_comments"] - end - sig { returns(T::Array[String]) } def initial_excluded_gems excluded, others = Bundler.definition.dependencies.partition do |dependency| diff --git a/lib/ruby_indexer/test/configuration_test.rb b/lib/ruby_indexer/test/configuration_test.rb index 31722a06c..09a342815 100644 --- a/lib/ruby_indexer/test/configuration_test.rb +++ b/lib/ruby_indexer/test/configuration_test.rb @@ -10,7 +10,7 @@ def setup end def test_load_configuration_executes_configure_block - @config.load_config + @config.apply_config({ "excluded_patterns" => ["**/test/fixtures/**/*.rb"] }) indexables = @config.indexables assert(indexables.none? { |indexable| indexable.full_path.include?("test/fixtures") }) @@ -21,7 +21,6 @@ def test_load_configuration_executes_configure_block end def test_indexables_only_includes_gem_require_paths - @config.load_config indexables = @config.indexables Bundler.locked_gems.specs.each do |lazy_spec| @@ -35,7 +34,6 @@ def test_indexables_only_includes_gem_require_paths end def test_indexables_does_not_include_default_gem_path_when_in_bundle - @config.load_config indexables = @config.indexables assert( @@ -44,7 +42,6 @@ def test_indexables_does_not_include_default_gem_path_when_in_bundle end def test_indexables_includes_default_gems - @config.load_config indexables = @config.indexables.map(&:full_path) assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb") @@ -53,7 +50,6 @@ def test_indexables_includes_default_gems end def test_indexables_includes_project_files - @config.load_config indexables = @config.indexables.map(&:full_path) Dir.glob("#{Dir.pwd}/lib/**/*.rb").each do |path| @@ -66,7 +62,6 @@ def test_indexables_includes_project_files def test_indexables_avoids_duplicates_if_bundle_path_is_inside_project Bundler.settings.set_global("path", "vendor/bundle") config = Configuration.new - config.load_config assert_includes(config.instance_variable_get(:@excluded_patterns), "#{Dir.pwd}/vendor/bundle/**/*.rb") ensure @@ -74,7 +69,6 @@ def test_indexables_avoids_duplicates_if_bundle_path_is_inside_project end def test_indexables_does_not_include_gems_own_installed_files - @config.load_config indexables = @config.indexables assert( @@ -95,17 +89,14 @@ def test_indexables_does_not_include_non_ruby_files_inside_rubylibdir end def test_paths_are_unique - @config.load_config indexables = @config.indexables assert_equal(indexables.uniq.length, indexables.length) end def test_configuration_raises_for_unknown_keys - Psych::Nodes::Document.any_instance.expects(:to_ruby).returns({ "unknown_config" => 123 }) - assert_raises(ArgumentError) do - @config.load_config + @config.apply_config({ "unknown_config" => 123 }) end end diff --git a/lib/ruby_lsp/server.rb b/lib/ruby_lsp/server.rb index fa485f459..45864fdcd 100644 --- a/lib/ruby_lsp/server.rb +++ b/lib/ruby_lsp/server.rb @@ -236,7 +236,29 @@ def run_initialized RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable) - perform_initial_indexing + indexing_config = {} + + # Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink. + index_path = File.join(@store.workspace_uri.to_standardized_path, ".index.yml") + + if File.exist?(index_path) + begin + indexing_config = YAML.parse_file(index_path).to_ruby + rescue Psych::SyntaxError => e + message = "Syntax error while loading configuration: #{e.message}" + send_message( + Notification.new( + method: "window/showMessage", + params: Interface::ShowMessageParams.new( + type: Constant::MessageType::WARNING, + message: message, + ), + ), + ) + end + end + + perform_initial_indexing(indexing_config) check_formatter_is_available end @@ -639,11 +661,11 @@ def shutdown Addon.addons.each(&:deactivate) end - sig { void } - def perform_initial_indexing + sig { params(config_hash: T::Hash[String, T.untyped]).void } + def perform_initial_indexing(config_hash) # The begin progress invocation happens during `initialize`, so that the notification is sent before we are # stuck indexing files - RubyIndexer.configuration.load_config + RubyIndexer.configuration.apply_config(config_hash) Thread.new do begin diff --git a/test/server_test.rb b/test/server_test.rb index 7fe9c0e1b..7e5fee08f 100644 --- a/test/server_test.rb +++ b/test/server_test.rb @@ -325,6 +325,21 @@ def test_initialize_features_with_enable_all_configuration assert(store.features_configuration.dig(:inlayHint).enabled?(:implicitHashValue)) end + def test_handles_invalid_configuration + FileUtils.mv(".index.yml", ".index.yml.tmp") + File.write(".index.yml", "} invalid yaml") + + @server.process_message({ method: "initialized" }) + notification = @server.pop_response + assert_equal("window/showMessage", notification.method) + assert_match( + /Syntax error while loading configuration/, + T.cast(notification.params, RubyLsp::Interface::ShowMessageParams).message, + ) + ensure + FileUtils.mv(".index.yml.tmp", ".index.yml") + end + def test_detects_rubocop_if_direct_dependency stub_dependencies(rubocop: true, syntax_tree: false)