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

Introduce .ruby-lsp.yml config file #1434

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions .index.yml

This file was deleted.

3 changes: 3 additions & 0 deletions .ruby-lsp.yml
@@ -0,0 +1,3 @@
indexing:
excluded_patterns:
- "**/test/fixtures/**/*.rb"
26 changes: 17 additions & 9 deletions README.md
Expand Up @@ -68,6 +68,10 @@ See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth de
For creating rich themes for Ruby using the semantic highlighting information, see the [semantic highlighting
documentation](SEMANTIC_HIGHLIGHTING.md).

## Configuration

Configuration is read from an optional `.ruby-lsp.yml` file in the root of your project.

### Configuring code indexing

By default, the Ruby LSP indexes all Ruby files defined in the current project and all of its dependencies, including
Expand All @@ -76,26 +80,30 @@ default gems, except for
- Gems that only appear under the `:development` group
- All Ruby files under `test/**/*.rb`

By creating a `.index.yml` file, these configurations can be overridden and tuned. Note that indexing dependent behavior, such as definition, hover, completion or workspace symbol will be impacted by the configurations placed here.
Within the `.ruby-lsp.yml` file, these configurations can be overridden and tuned. Note that indexing dependent behavior, such as definition, hover, completion or workspace symbol will be impacted by the configurations placed here.

```yaml
# Exclude files based on a given pattern. Often used to exclude test files or fixtures
excluded_patterns:
- "**/spec/**/*.rb"
indexing:
excluded_patterns:
- "**/spec/**/*.rb"

# Include files based on a given pattern. Can be used to index Ruby files that use different extensions
included_patterns:
indexing:
included_patterns:
- "**/bin/*"

# Exclude gems by name. If a gem is never referenced in the project's code and is only used as a tool, excluding it will
# speed up indexing and reduce the amount of results in features like definition or completion
excluded_gems:
- rubocop
- pathname
indexing:
excluded_gems:
- rubocop
- pathname

# Include gems by name. Normally used to include development gems that are excluded by default
included_gems:
- prism
indexing:
included_gems:
- prism
```

### Addons
Expand Down
32 changes: 32 additions & 0 deletions lib/ruby_lsp/configuration.rb
@@ -0,0 +1,32 @@
# typed: strict
# frozen_string_literal: true

module RubyLsp
class Configuration
extend T::Sig

sig { params(workspace_uri: URI::Generic).void }
def initialize(workspace_uri)
@workspace_uri = workspace_uri
end

sig { returns(T::Hash[String, T.untyped]) }
def indexing
# Need to use the workspace URI, otherwise, this will fail for people working on a project that is a symlink.
index_path = File.join(@workspace_uri.to_standardized_path, ".index.yml")
ruby_lsp_path = File.join(@workspace_uri.to_standardized_path, ".ruby-lsp.yml")

if File.exist?(index_path)
unless ENV["RUBY_LSP_ENV"] == "test"
$stderr.puts("The .index.yml configuration file is deprecated. Please rename it to .ruby-lsp.yml and " \
"update the structure as described in the README: https://github.com/Shopify/ruby-lsp#configuration")
end
YAML.parse_file(index_path).to_ruby
elsif File.exist?(ruby_lsp_path)
YAML.parse_file(ruby_lsp_path).to_ruby.fetch("indexing")
else
{}
end
end
end
end
1 change: 1 addition & 0 deletions lib/ruby_lsp/internal.rb
Expand Up @@ -20,6 +20,7 @@
require "language_server-protocol"

require "ruby-lsp"
require "ruby_lsp/configuration"
require "ruby_lsp/base_server"
require "ruby_indexer/ruby_indexer"
require "core_ext/uri"
Expand Down
30 changes: 12 additions & 18 deletions lib/ruby_lsp/server.rb
Expand Up @@ -235,27 +235,21 @@ def run_initialized
end

RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)

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,
),
begin
indexing_config = Configuration.new(@store.workspace_uri).indexing
rescue => e
message = "#{e.class} while loading indexing 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)
Expand Down
59 changes: 59 additions & 0 deletions test/configuration_test.rb
@@ -0,0 +1,59 @@
# typed: true
# frozen_string_literal: true

require "test_helper"

module RubyLsp
class ConfigurationTest < Minitest::Test
def test_returns_empty_hash_when_no_configuration_files_exist
FileUtils.mv(".ruby-lsp.yml", ".ruby-lsp.yml.tmp")
workspace_uri = URI::Generic.build(scheme: "file", host: nil, path: "/path/to/workspace")
result = RubyLsp::Configuration.new(workspace_uri).indexing

assert_empty(result)
ensure
FileUtils.mv(".ruby-lsp.yml.tmp", ".ruby-lsp.yml")
end

def test_supports_depecated_index_configuration_file
FileUtils.mv(".ruby-lsp.yml", ".ruby-lsp.yml.tmp")
File.write(".index.yml", <<~YAML)
excluded_patterns:
- "**/test/fixtures/**/*.rb"
YAML
workspace_uri = URI::Generic.from_path(path: Dir.pwd)

result = RubyLsp::Configuration.new(workspace_uri).indexing

assert_equal({ "excluded_patterns" => ["**/test/fixtures/**/*.rb"] }, result)
ensure
FileUtils.mv(".ruby-lsp.yml.tmp", ".ruby-lsp.yml")
FileUtils.rm_f(".index.yml")
end

def test_supports_newer_configuration
workspace_uri = URI::Generic.from_path(path: Dir.pwd)

result = RubyLsp::Configuration.new(workspace_uri).indexing

assert_equal({ "excluded_patterns" => ["**/test/fixtures/**/*.rb"] }, result)
end

def test_raises_if_indexing_key_is_missing
FileUtils.mv(".ruby-lsp.yml", ".ruby-lsp.yml.tmp")
File.write(".ruby-lsp.yml", <<~YAML)
excluded_patterns:
- "**/test/fixtures/**/*.rb"
YAML
workspace_uri = URI::Generic.from_path(path: Dir.pwd)

error = assert_raises do
RubyLsp::Configuration.new(workspace_uri).indexing
end
assert_equal("key not found: \"indexing\"", error.message)
assert_instance_of(KeyError, error)
ensure
FileUtils.mv(".ruby-lsp.yml.tmp", ".ruby-lsp.yml")
end
end
end
9 changes: 5 additions & 4 deletions test/server_test.rb
Expand Up @@ -326,18 +326,19 @@ def test_initialize_features_with_enable_all_configuration
end

def test_handles_invalid_configuration
FileUtils.mv(".index.yml", ".index.yml.tmp")
File.write(".index.yml", "} invalid yaml")
FileUtils.mv(".ruby-lsp.yml", ".ruby-lsp.yml.tmp")
File.write(".ruby-lsp.yml", "}")

@server.process_message({ method: "initialized" })
notification = @server.pop_response
assert_equal("window/showMessage", notification.method)
assert_match(
/Syntax error while loading configuration/,
/Psych::SyntaxError while loading indexing configuration/,
T.cast(notification.params, RubyLsp::Interface::ShowMessageParams).message,
"Psych::SyntaxError while loading indexing configuration:",
)
ensure
FileUtils.mv(".index.yml.tmp", ".index.yml")
FileUtils.mv(".ruby-lsp.yml.tmp", ".ruby-lsp.yml")
end

def test_detects_rubocop_if_direct_dependency
Expand Down