Skip to content

Commit

Permalink
Introduce .ruby-lsp config file
Browse files Browse the repository at this point in the history
  • Loading branch information
andyw8 committed Mar 22, 2024
1 parent ee23b2e commit 6e67b3d
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 35 deletions.
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
30 changes: 30 additions & 0 deletions lib/ruby_lsp/configuration.rb
@@ -0,0 +1,30 @@
# 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)
$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")
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.yml").to_ruby
rescue Psych::SyntaxError => e
message = "Syntax error while loading .index.yml 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
60 changes: 60 additions & 0 deletions test/configuration_test.rb
@@ -0,0 +1,60 @@
# typed: true
# frozen_string_literal: true

require "test_helper"

module RubyLsp
class IndexConfigTest < 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.build(scheme: "file", host: nil, 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.build(scheme: "file", host: nil, 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.build(scheme: "file", host: nil, 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
11 changes: 5 additions & 6 deletions test/server_test.rb
Expand Up @@ -326,19 +326,18 @@ 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_equal(
"Syntax error while loading .index.yml configuration: (.index.yml): did not find expected node content " \
"while parsing a block node at line 1 column 1",
assert_includes(
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

0 comments on commit 6e67b3d

Please sign in to comment.