### RubyGems Configuration Example Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/troubleshooting.markdown Shows how to configure RubyGems installation paths using the ~/.gemrc file. ```yaml ```yaml gem: --user-install # Or gem: --install-dir /my/preferred/path/for/gem/install ``` ``` -------------------------------- ### Custom Ruby Version File Source: https://github.com/shopify/ruby-lsp/blob/main/vscode/README.md Example of a .ruby-version file to specify a Ruby version for a custom Gemfile setup. ```shell # the/directory/.ruby-version 3.2.2 ``` -------------------------------- ### Custom Gemfile Example Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/vscode-extension.markdown Example of a custom Gemfile for development tools, including the ruby-lsp and rubocop. Formatters and their extensions should also be included. ```ruby source "https://rubygems.org" gem "ruby-lsp" gem "rubocop" ``` ```ruby gem "rubocop-packaging" gem "rubocop-performance" gem "rubocop-rspec" gem "rubocop-shopify" gem "rubocop-thread_safety" ``` -------------------------------- ### Example Commands for Resolving Test Execution Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_explorer.markdown Provides an example of the minimum number of commands required to execute a given hierarchy of tests, as determined by the `rubyLsp/resolveTestCommands` request. ```ruby [ "bin/rails test test/foo_test.rb:13:25:40", "bin/rails test test/bar_test.rb --name \"/^BarTest::NestedTest(#|::)/\"" ] ``` -------------------------------- ### Setup Ruby LSP with mason-lspconfig Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Configures Ruby LSP using mason.nvim and mason-lspconfig.nvim. Note the potential ABI issues mentioned in the documentation. ```lua local capabilities = vim.lsp.protocol.make_client_capabilities() local mason_lspconfig = require("mason-lspconfig") local servers = { ruby_lsp = {}, } mason_lspconfig.setup { ensure_installed = vim.tbl_keys(servers), } mason_lspconfig.setup_handlers { function(server_name) require("lspconfig")[server_name].setup { capabilities = capabilities, on_attach = on_attach, settings = servers[server_name], filetypes = (servers[server_name] or {}).filetypes, } end } ``` -------------------------------- ### Example Copilot Chat Command for Design Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/vscode-extension.markdown Demonstrates how to use the @ruby /design command in GitHub Copilot Chat to model concepts for Rails applications. It shows an example prompt for designing courses and their relation to students. ```markdown @ruby /design I'm working on a web application for schools. How do I model courses? And how do they relate to students? ``` -------------------------------- ### Install Ruby LSP Gem Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/index.markdown Install the Ruby LSP gem using the standard RubyGems command. This command should be run without 'bundle exec' to ensure proper integration with project dependencies. ```shell gem install ruby-lsp ``` -------------------------------- ### LSP Reporter for Example Test Framework Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown A placeholder comment for an Lsp reporter implementation for a custom test framework. ```ruby # An Lsp reporter for our example test framework ``` -------------------------------- ### Run a specific fixture example with the custom test framework Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/contributing.markdown Execute a specific test case using the custom test framework, targeting a particular request and fixture. ```shell bin/test test/requests/diagnostics_expectations_test.rb def_bad_formatting ``` -------------------------------- ### Ambiguous Syntax Example Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/semantic-highlighting.markdown This example demonstrates ambiguous syntax in Ruby where 'foo' could be a local variable or a method call. Semantic highlighting resolves this ambiguity. ```ruby foo ``` -------------------------------- ### Manually Booting the Ruby LSP Server Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/troubleshooting.markdown Use this command to manually start the Ruby LSP server from your terminal. This helps determine if issues are specific to the extension's activation process. Do not use `bundle exec`. ```shell # Do not use bundle exec ruby-lsp ``` -------------------------------- ### Configure Ruby LSP with Standard Add-on in nvim-lspconfig Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Enables the 'standard' add-on for formatting and pull-style diagnostics using nvim-lspconfig. Requires Ruby LSP to be installed. ```lua local lspconfig = require('lspconfig') lspconfig.ruby_lsp.setup({ init_options = { formatter = 'standard', linters = { 'standard' }, }, }) ``` -------------------------------- ### Testing Ruby LSP Features Source: https://github.com/shopify/ruby-lsp/blob/main/CLAUDE.md Example of how to test language server features using the provided test helpers. It sets up a server with source code and makes LSP requests to assert responses. ```ruby def test_feature_name source = <<~RUBY # Ruby code to test RUBY with_server(source) do |server, _uri| # Make LSP request # Assert response end end ``` -------------------------------- ### Configure Ruby LSP Rails Add-on Settings Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Example of configuring specific add-on settings for 'Ruby LSP Rails', such as disabling the pending migrations prompt. ```lua init_options = { addonSettings = { ["Ruby LSP Rails"] = { enablePendingMigrationsPrompt = false, }, }, } ``` -------------------------------- ### Ruby LSP Addon for Custom Test Framework Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown An example of a Ruby LSP add-on that implements the `resolve_test_commands` method to handle custom test frameworks. ```ruby module RubyLsp module MyTestFrameworkGem class Addon < ::RubyLsp::Addon # Items is the hierarchy of test items to be executed. The return is the list of minimum shell commands required # to run them #: (Array[Hash[Symbol, untyped]]) -> Array[String] def resolve_test_commands(items) commands = [] queue = items.dup until queue.empty? item = queue.shift tags = Set.new(item[:tags]) next unless tags.include?("framework:my_framework") children = item[:children] if tags.include?("test_dir") # Handle running entire directories elsif tags.include?("test_file") # Handle running entire files elsif tags.include?("test_group") # Handle running groups else # Handle running examples end queue.concat(children) unless children.empty? end commands end end end end ``` -------------------------------- ### Configure Ruby LSP Indexing Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/vscode-extension.markdown Example of how to configure Ruby LSP's indexing behavior by specifying excluded and included file patterns, as well as excluding specific gems and magic comments. ```jsonc // PROJECT/.vscode/settings.json { "rubyLsp.indexing": { "excludedPatterns": ["**/test/**/*.rb"], "includedPatterns": ["**/bin/**/*"], "excludedGems": ["rubocop", "rubocop-performance"], "excludedMagicComments": ["compiled:true"], }, } ``` -------------------------------- ### Example of Guessed Types in Ruby Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/index.markdown Illustrates how the Ruby LSP attempts to identify the type of a receiver based on its identifier. ```ruby puts "hello" # Guessed type: String ``` -------------------------------- ### Custom Reporter CLI Argument Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown Example of a shell command including a custom reporter argument for test execution. ```shell bundle exec my_framework /path/to/project/test/foo_test.rb --reporter MyFrameworkLspReporter ``` -------------------------------- ### Shell Commands for Running Tests Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown Examples of shell commands used to execute tests in different Ruby frameworks like Rails, Test Unit, and Minitest. ```shell bin/rails test /project/test/models/user_test.rb:10:25 ``` ```shell bundle exec ruby -Itest /test/model_test.rb --testcase "/^ModelTest\$/" --name "/test_something\$/ ``` ```shell bundle exec ruby -Itest /test/model_test.rb --name "/^ModelTest#test_something\$/" ``` -------------------------------- ### Ruby LSP Code Examples: Heuristics for Type System Fallback Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/design-and-roadmap.markdown Demonstrates cases where a type system would be required for full accuracy, and the Ruby LSP falls back to heuristics, such as handling metaprogramming. ```ruby # Cases where a type system would be required and we fallback to heuristics to provide features ## Meta-programming Foo.define_method("some#{interpolation}") do |arg| end ``` -------------------------------- ### Register a Custom Formatter Runner Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown Example of a Ruby LSP add-on that registers a custom formatter. The `activate` method is used to register the formatter with a unique identifier. ```ruby class MyFormatterRubyLspAddon < RubyLsp::Addon def name "My Formatter" end def activate(global_state, message_queue) # The first argument is an identifier users can pick to select this formatter. To use this formatter, users must # have rubyLsp.formatter configured to "my_formatter" # The second argument is a class instance that implements the `FormatterRunner` interface (see below) global_state.register_formatter("my_formatter", MyFormatterRunner.new) end end ``` -------------------------------- ### Lint with RuboCop Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Command to run RuboCop, a Ruby static code analyzer and formatter, to check for style guide violations. ```bash # Lint with RuboCop bin/rubocop ``` -------------------------------- ### Ruby LSP Code Examples: Literals and Direct Invocations Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/design-and-roadmap.markdown Illustrates scenarios where the Ruby LSP can provide satisfactory features without a full type system, including literal usage and direct method invocations on 'self'. ```ruby # Cases where we can provide a satisfactory experience without a type system ## Literals ".upcase 1.to_s {}.merge!({ a: 1 }) [].push(1) ## Scenarios where can assume the receiver type class Foo def bar; end def baz bar # method invoked directly on self end end ## Singleton methods with an explicit receiver Foo.some_singleton_method ## Constant references Foo::Bar ``` -------------------------------- ### Handle Call Site DSLs in Ruby Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown Example of a Rails model using a call site DSL (`validate`). This demonstrates how symbols are treated by Ruby and how add-ons might need to enhance handling for such DSLs to provide features like go-to-definition. ```ruby class User < ApplicationRecord # From Ruby's perspective, `:something` is just a regular symbol. It's Rails that defines this as a DSL and specifies # that the argument represents a method name. # # If an add-on wanted to handle go to definition or completion for these symbols, then it would need to enhance the # handling for call site DSLs validate :something private def something end end ``` -------------------------------- ### Configure Ruby LSP in Sublime Text Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Add this configuration to your LSP client configuration in Sublime Text to enable the Ruby LSP. Ensure LSP for Sublime Text is installed. ```json { "clients": { "ruby-lsp": { "enabled": true, "command": [ "ruby-lsp" ], "selector": "source.ruby", "initializationOptions": { "enabledFeatures": { "diagnostics": false }, "experimentalFeaturesEnabled": true } } } } ``` -------------------------------- ### Define a Custom DSL Method Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown Example of a Ruby class using a custom DSL method that meta-programs the creation of a new method. This is the DSL that the Ruby LSP needs to understand. ```ruby class MyThing < MyLibrary::ParentClass # After invoking this method from the `MyLibrary::ParentClass`, a method called `new_method` will be created, # accepting a single required parameter named `a` my_dsl_that_creates_methods # Produces this with meta-programming # def my_method(a); end end ``` -------------------------------- ### Enhance Ruby LSP Indexing for Custom DSLs Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown An example of a Ruby LSP indexing enhancement class that teaches the LSP to understand a custom DSL. It uses `on_call_node_enter` to identify the DSL method and add the meta-programmed method to the index. ```ruby class MyIndexingEnhancement < RubyIndexer::Enhancement # This on call node handler is invoked any time during indexing when we find a method call. It can be used to insert # more entries into the index depending on the conditions def on_call_node_enter(node) return unless @listener.current_owner # Return early unless the method call is the one we want to handle return unless node.name == :my_dsl_that_creates_methods # Create a new entry to be inserted in the index. This entry will represent the declaration that is created via # meta-programming. All entries are defined in the `entry.rb` file. # # In this example, we will add a new method to the index location = node.location # Create the array of signatures that this method will accept. Every signatures is composed of a list of # parameters. The parameter classes represent each type of parameter signatures = [ RubyIndexer::Entry::Signature.new([RubyIndexer::Entry::RequiredParameter.new(name: :a)]) ] @listener.add_method( "new_method", # Name of the method location, # Prism location for the node defining this method signatures # Signatures available to invoke this method ) end # This method is invoked when the parser has finished processing the method call node. # It can be used to perform cleanups like popping a stack...etc. def on_call_node_leave(node); end end ``` -------------------------------- ### Debugging Live Processes with rdbg Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/vscode-extension.markdown Run your application with the debugger attached to allow VS Code to connect to an existing process. Ensure the 'debug' gem is installed. ```shell bundle exec rdbg -O -n -c -- bin/rails server -p 3000 ``` -------------------------------- ### Example Listener for AST Events Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown This snippet demonstrates a basic listener that works with Prism::Dispatcher to handle specific node types during the parsing of Ruby code. It's a fundamental part of creating add-on functionality. ```ruby ```ruby # Listeners work in conjunction with a `Prism::Dispatcher`, which is responsible for dispatching events during the parsing of Ruby code. Each event corresponds to a specific node in the Abstract Syntax Tree (AST) of the code being parsed. Here's a simple example of a listener: ```ruby ``` -------------------------------- ### Test Item Hierarchy for Specific Example Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown A Ruby data structure representing a test item hierarchy that targets a specific example for execution. ```ruby [{ id: "ModelTest", uri: "file:///test/model_test.rb", label: "ModelTest", range: { start: { line: 0, character: 0 }, end: { line: 30, character: 3 } }, tags: ["framework:minitest", "test_group"], children: [ { id: "ModelTest#test_something", uri: "file:///test/model_test.rb", label: "test_something", range: { start: { line: 1, character: 2 }, end: { line: 10, character: 3 } }, tags: ["framework:minitest"], children: [] } ] }] ``` -------------------------------- ### Integrating Add-ons with Ruby LSP Test Helper Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown Shows how to use the `ruby-lsp` test helper to create a server instance with pre-initialized document content for testing add-on contributions. ```ruby require "test_helper" require "ruby_lsp/test_helper" class MyAddonTest < Minitest::Test def test_my_addon_works source = <<~RUBY # Some test code that allows you to trigger your add-on's contribution class Foo def something end end RUBY with_server(source) do |server, uri| # Tell the server to execute the definition request server.process_message( id: 1, method: "textDocument/definition", params: { textDocument: { uri: uri.to_s, }, position: { line: 3, character: 5 } } ) # Pop the server's response to the definition request result = server.pop_response.response # Assert that the response includes your add-on's contribution assert_equal(123, result.response.location) end end end ``` -------------------------------- ### Serve Jekyll Documentation Locally Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/contributing.markdown Run the Jekyll development server to preview documentation changes locally before deploying. Visit http://localhost:4000/ruby-lsp to see the rendered site. ```shell bundle exec jekyll serve ``` -------------------------------- ### Print Bundler Configuration Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/troubleshooting.markdown Command to display the current Bundler configuration, useful for diagnosing dependency issues. ```shell bundle config ``` -------------------------------- ### Gemfile Exclusion Example Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/troubleshooting.markdown Illustrates how excluding a Gemfile group can prevent the Ruby LSP from finding gems like RuboCop. ```ruby ```ruby # Gemfile # ... # If Bundler is configured to exclude this group, the Ruby LSP will not be able to find `rubocop` group :optional_group do gem "rubocop" end ``` ``` -------------------------------- ### RBS Attribute Type Declaration Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Example of declaring the type for an attribute using RBS format. This is placed above the attribute declaration. ```ruby # Attribute type declarations (placed above attribute) #: String? attr_reader :parent_class ``` -------------------------------- ### Run linting with RuboCop Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/contributing.markdown Check code style and quality using RuboCop. ```shell bundle exec rubocop ``` -------------------------------- ### RBS Variable Annotation Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Example of annotating a variable's type in RBS format. This is placed after the assignment to declare the variable's type. ```ruby # Variable annotations (placed after assignment) @documents = {} #: Hash[URI::Generic, Document] ``` -------------------------------- ### Run Extension Tests Source: https://github.com/shopify/ruby-lsp/blob/main/CLAUDE.md Use this command to run all extension tests. It first compiles the code via the `pretest` hook and then executes the tests using `vscode-test`. ```bash pnpm run test ``` -------------------------------- ### RBS Method Signature Annotation Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Example of annotating a method signature in RBS format. This is placed above the method definition to declare its return type. ```ruby # Method signatures (placed above method definition) #: (String name) -> void def process(name) # ... end ``` -------------------------------- ### Activate Addon for Test Discovery Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown Activates the add-on and sets up the global state. This method is called once when the add-on is loaded. ```ruby module RubyLsp module MyTestFrameworkGem class Addon < ::RubyLsp::Addon #: (GlobalState, Thread::Queue) -> void def activate(global_state, message_queue) @global_state = global_state end # Declare the factory method that will hook a new listener into the test discovery process # @override #: (ResponseBuilders::TestCollection, Prism::Dispatcher, URI::Generic) -> void def create_discover_tests_listener(response_builder, dispatcher, uri) # Because the Ruby LSP runs requests concurrently, there are no guarantees that we'll be done executing # activate when a request for test discovery comes in. If this happens, skip until the global state is ready return unless @global_state # Create our new test discovery listener, which will hook into the dispatcher TestDiscoveryListener.new(response_builder, @global_state, dispatcher, uri) end end end end ``` -------------------------------- ### RBS Generic Type Annotation Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Example of using generic types in RBS annotations. This demonstrates a method that uses a block with a generic return type. ```ruby # Generic types #: [T] () { (String) -> T } -> T def with_cache(&block) # ... end ``` -------------------------------- ### Run the entire test suite Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/contributing.markdown Execute all tests in the Ruby LSP project. ```shell bundle exec rake ``` -------------------------------- ### Testing Incomplete Require Statements Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown Demonstrates testing various incomplete states of the 'require' statement, including no argument, autocompleted quotes without content, and uncommon syntax like Kernel.require. ```ruby # Still no argument require # With quotes autocompleted, but no content on the string require "" # Using uncommon, but valid syntax, such as invoking require directly on Kernel using parenthesis Kernel.require("library") ``` -------------------------------- ### Configure Custom Ruby Version Manager Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/version-managers.markdown Set the Ruby LSP extension to use a custom version manager or manually add a Ruby bin folder to the PATH. Ensure the manager is set to 'custom' and provide the activation command or PATH modification. ```jsonc { // Don't forget to set the manager to custom when using this option "rubyLsp.rubyVersionManager": { "identifier": "custom", }, // Using a different version manager than the ones included by default "rubyLsp.customRubyCommand": "my_custom_version_manager activate", // Adding a custom Ruby bin folder to the PATH "rubyLsp.customRubyCommand": "PATH=/path/to/ruby/bin:$PATH" } ``` -------------------------------- ### RBS Union and Nullable Type Annotation Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Example of annotating a variable with a union and nullable type in RBS format. The variable can hold a String, a Symbol, or be nil. ```ruby # Union and nullable types result = nil #: (String | Symbol)? ``` -------------------------------- ### Add ShowRubyDeps Command for Ruby LSP Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Adds a custom Neovim command 'ShowRubyDeps' to display Ruby LSP dependencies in the quickfix list. Requires the 'rubyLsp/workspace/dependencies' custom method. ```lua local function add_ruby_deps_command(client, bufnr) vim.api.nvim_buf_create_user_command(bufnr, "ShowRubyDeps", function(opts) local params = vim.lsp.util.make_text_document_params() local showAll = opts.args == "all" client.request("rubyLsp/workspace/dependencies", params, function(error, result) if error then print("Error showing deps: " .. error) return end local qf_list = {} for _, item in ipairs(result) do if showAll or item.dependency then table.insert(qf_list, { text = string.format("%s (%s) - %s", item.name, item.version, item.dependency), filename = item.path }) end end vim.fn.setqflist(qf_list) vim.cmd('copen') end, bufnr) end, {nargs = "?", complete = function() return {"all"} end}) end require("lspconfig").ruby_lsp.setup({ on_attach = function(client, buffer) add_ruby_deps_command(client, buffer) end, }) ``` -------------------------------- ### Launch Ruby LSP with chruby Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Activate the Ruby environment using chruby and then launch the Ruby LSP executable. This ensures the correct Ruby version and gem environment are used. ```shell chruby $(cat .ruby-version) && ruby-lsp ``` -------------------------------- ### Custom Test Reporter Class Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown Defines a custom reporter class for a test framework. It records various test events like starting, passing, erroring, and skipping. ```ruby class MyFrameworkGemLspReporter # Record that an example started running. This shows the example as running in the UI def test_started_running(test_object) id = "#{test_object.class.name}##{test_object.method_name}" uri = URI::Generic.from_path(path: test_object.file_path) RubyLsp::LspReporter.instance.start_test(id: id, uri: uri, line: test_object.line_number) end # Record that an example passed, which shows a green checkmark in the UI def test_passed(test_object) RubyLsp::LspReporter.instance.record_pass(id: id, uri: uri) end # Record that an example errored, which shows a red X in the UI and displays the exception message def test_errored(test_object) RubyLsp::LspReporter.instance.record_skip(id: id, uri: uri, message: "Test errored because...") end # Record that an example failed, which shows a red X in the UI and displays the failure message def test_failed(test_object) RubyLsp::LspReporter.instance.record_fail(id: id, uri: uri, message: "Test failed because...") end # Record that an example skipped, which shows a skipped status in the UI def test_skipped(test_object) RubyLsp::LspReporter.instance.record_skip(id: id, uri: uri) end # Normal shutdown flow, when all tests ran without crashing the test process itself def after_all_tests_finished_running LspReporter.instance.shutdown end end ``` -------------------------------- ### Launch Ruby LSP with Mise Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Use this command to launch the Ruby LSP executable within the context of the current Ruby environment managed by Mise. ```shell mise x -- ruby-lsp ``` -------------------------------- ### Enable Ruby LSP with Built-in vim.lsp Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Enables the Ruby LSP client for Neovim's built-in LSP functionality. ```lua vim.lsp.enable("ruby-lsp") ``` -------------------------------- ### Configure Ruby LSP via Built-in vim.lsp Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Defines Ruby LSP configuration for use with Neovim's built-in LSP client. Includes filetypes, command, root markers, and init options. ```lua -- on Linux and macOS the default location is ~/.config/nvim/lsp/ruby-lsp.lua return { filetypes = { "ruby" }, cmd = { "ruby-lsp" } -- or { "bundle", "exec", "ruby-lsp" }, root_markers = { "Gemfile", ".git" }, init_options = { formatter = 'standard', linters = { 'standard' }, addonSettings = { ["Ruby LSP Rails"] = { enablePendingMigrationsPrompt = false, }, }, }, } ``` -------------------------------- ### Test Discovery Logic for Public Methods Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown This snippet outlines the core logic for discovering public test methods within a Ruby class. It checks for public visibility and method names starting with 'test_'. ```ruby # If the method is not public, then it cannot be considered an example. The visibility stack is tracked # automatically by the `RubyLsp::Listeners::TestDiscovery` parent class return if @visibility_stack.last != :public # If the method name doesn't begin with `test_`, then it's not a test example name = node.name.to_s return unless name.start_with?("test_") # The current group of a test example depends on which exact namespace nesting it is defined in. We can use # the Ruby LSP's index to get the fully qualified name of the current namespace using the `@nesting` variable # provided by the TestDiscovery parent class current_group_name = RubyIndexer::Index.actual_nesting(@nesting, nil).join("::") # The test explorer is populated with a hierarchy of items. Groups have children, which can include other # groups and examples. Listeners should always add newly discovered children to the parent item where they # are discovered. For example: # # class MyTest < MyFrameworkGem::Test # # # this NestedTest is a child of MyTest # class NestedTest < MyFrameworkGem::Test # # # this example is a child of NestedTest # def test_something; end # end # # # This example is a child of MyTest # def test_something_else; end # end # # Get the current test item from the response builder using the ID. In this case, the immediate group # enclosing will be based on the nesting test_item = @response_builder[current_group_name] return unless test_item # Create the test item for the example. To make IDs unique, always include the group names as part of the ID # since users can define the same exact example name in multiple different groups example_item = Requests::Support::TestItem.new( "#{current_group_name}##{name}", name, @uri, range_from_node(node), framework: :my_framework, ) # Add the example item to both as an explorer entry and code lens test_item.add(example_item) @response_builder.add_code_lens(example_item) end end end end ``` -------------------------------- ### Launch Ruby LSP with Shadowenv Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Use this command to launch the Ruby LSP executable within the context of the current Ruby environment managed by Shadowenv. ```shell shadowenv exec -- ruby-lsp ``` -------------------------------- ### Configure Ruby LSP Server in Kate Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Override default server configurations in Kate's LSP Client plugin to use Ruby LSP. Ensure 'Incrementally synchronize documents with the LSP server' is enabled. ```json { "servers": { "ruby": { "command": ["ruby-lsp"], "url": "https://github.com/Shopify/ruby-lsp" } } } ``` -------------------------------- ### Combine File and Name Filtering for Tests Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Optimize feedback loop by combining file scoping with name filtering using '--run' and '--grep' flags. ```bash # Combine file scoping with name filtering for the fastest feedback loop pnpm run test -- --run out/test/suite/testController.test.js --grep "discovers tests" ``` -------------------------------- ### Configure Ruby LSP Initialization Options Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown This JSON object configures various features and behaviors of the Ruby LSP during initialization. It allows enabling/disabling features, setting up inlay hints, and defining patterns for file indexing. ```json { "initializationOptions": { "enabledFeatures": { "codeActions": true, "codeLens": true, "completion": true, "definition": true, "diagnostics": true, "documentHighlights": true, "documentLink": true, "documentSymbols": true, "foldingRanges": true, "formatting": true, "hover": true, "inlayHint": true, "onTypeFormatting": true, "selectionRanges": true, "semanticHighlighting": true, "signatureHelp": true, "typeHierarchy": true, "workspaceSymbol": true }, "featuresConfiguration": { "inlayHint": { "implicitHashValue": true, "implicitRescue": true } }, "indexing": { "excludedPatterns": ["path/to/excluded/file.rb"], "includedPatterns": ["path/to/included/file.rb"], "excludedGems": ["gem1", "gem2", "etc."], "excludedMagicComments": ["compiled:true"] }, "formatter": "auto", "linters": [], "experimentalFeaturesEnabled": false } } ``` -------------------------------- ### Monorepo Multi-root Workspace Configuration Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/vscode-extension.markdown Set up a multi-root workspace for a monorepo with client and server subdirectories. Hides the client and server directories from the main workspace view as they are already included as separate workspaces. ```jsonc { "folders": [ // Both parts of the project (client and server) are inside sub-directories. But since the top level might contain // some documentation or build files, we still want it to show up { "name": "awesome_project", "path": "." }, // Inside the client directory, we have the client part of the project { "name": "client", "path": "client" }, // Inside the server directory, we have the server part of the project { "name": "server", "path": "server" } ], "settings": { // We don't want to show duplicates, so we hide the directories that are already showing up as workspaces "files.exclude": { "server": true, "client": true } } } ``` -------------------------------- ### Test Item ID Formatting Requirement Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown This note explains the required formatting for test item IDs, emphasizing the use of '::' for group separation and '#' for example separation. This format is mandatory even for frameworks that don't use classes and methods. ```markdown {: .important } Test item IDs have an implicit formatting requirement: groups must be separated by `::` and examples must be separated by `#`. This is required even for frameworks that do not use classes and methods to define groups and examples. Including spaces in group or example IDs is allowed. For example, if we have the following test: ```ruby class MyTest < MyFrameworkGem::Test class NestedTest < MyFrameworkGem::Test def test_something; end end end ``` the expected ID for the item representing `test_something` should be `MyTest::NestedTest#test_something`. ``` -------------------------------- ### Customizing Test Runner Behavior with Environment Variable Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_explorer.markdown Customize test suite behavior when tests are running through the Ruby LSP by setting the `RUBY_LSP_TEST_RUNNER` environment variable. This example shows conditional logic based on the variable's value. ```ruby # test/test_helper.rb case ENV["RUBY_LSP_TEST_RUNNER"] when "run" # Do something when using run or run in terminal modes when "debug" # Do something when using debug mode when "coverage" # Do something when using coverage mode else # Do something when running outside of the context of the Ruby LSP integration end ``` -------------------------------- ### Configure Ruby LSP Rails Add-on Settings in Zed Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Configure the Ruby LSP add-on settings in Zed to manage features like pending migrations prompts. This requires the Ruby extension for Zed. ```json { "lsp": { "ruby-lsp": { "initialization_options": { "addonSettings": { "Ruby LSP Rails": { "enablePendingMigrationsPrompt": false } } } } } } ``` -------------------------------- ### Run type checking with Sorbet Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/contributing.markdown Perform static type checking on the codebase using Sorbet. ```shell bundle exec srb tc ``` -------------------------------- ### Handle Declaration DSLs in Ruby Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/add-ons.markdown Example of a Rails model using a declaration DSL (`belongs_to`). This DSL mutates the class by adding new methods. Informing the Ruby LSP through an indexing enhancement allows features like go-to-definition and completion to recognize these new declarations. ```ruby class User < ApplicationRecord # When this method is invoked, a bunch of new methods will be defined in the `User` class, such as `company` and # `company=` By informing the Ruby LSP about the new methods through an indexing enhancement, features such as # go to definition, completion, hover, signature help and workspace symbol will automatically pick up the new # declaration belongs_to :company end ``` -------------------------------- ### Run Multiple Compiled Test Files Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Execute multiple compiled test files simultaneously by providing multiple '--run' flags with 'pnpm run test'. ```bash # Run several files at once pnpm run test -- --run out/test/suite/workspace.test.js --run out/test/suite/client.test.js ``` -------------------------------- ### Implement Test Discovery Listener Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_framework_addons.markdown Initializes the listener and registers for class and definition node events. This listener handles discovering tests within files. ```ruby module RubyLsp module MyTestFrameworkGem class TestDiscoveryListener < Listeners::TestDiscovery #: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void def initialize(response_builder, global_state, dispatcher, uri) super(response_builder, global_state, dispatcher, uri) # Register on the dispatcher for the node events we are interested in dispatcher.register(self, :on_class_node_enter, :on_def_node_enter) end #: (Prism::ClassNode node) -> void def on_class_node_enter(node) # Here we use the `with_test_ancestor_tracking` so that we can check if the class we just found inherits # from our framework's parent test class. This check is important because users can define any classes or # modules inside a test file and not all of them are runnable tests with_test_ancestor_tracking(node) do |name, ancestors| if ancestors.include?("MyTestFrameworkGem::Test") # If the test class indeed inherits from our framework, then we can create a new test item representing # this test in the explorer. The expected arguments are: # # - id: a unique ID for this test item. Must match the same IDs reported during test execution # (explained in the next section) # - label: the label that will appear in the explorer # - uri: the URI where this test can be found (e.g.: file:///Users/me/src/my_project/test/my_test.rb). # has to be a URI::Generic object # - range: a RubyLsp::Interface::Range object describing the range inside of `uri` where we can find the # test definition # - framework: a framework ID that will be used for resolving test commands. Each add-on should only # resolve the items marked as their framework test_item = Requests::Support::TestItem.new( name, name, @uri, range_from_node(node), framework: :my_framework ) # Push the test item as an explorer entry @response_builder.add(test_item) # Push the test item for code lenses. This allows users to run tests by clicking the `Run`, # `Run in terminal` and `Debug` buttons directly on top of tests @response_builder.add_code_lens(test_item) end end end #: (Prism::DefNode) -> void def on_def_node_enter(node) ``` -------------------------------- ### Activate Ruby Environment and Output JSON Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/troubleshooting.markdown This command activates the specified Ruby version using a version manager (like rbenv) and outputs the environment variables as JSON. This is used by the VS Code extension to configure the NodeJS process. ```shell /bin/zsh -ic 'rbenv exec ruby -rjson -e "puts JSON.dump(ENV.to_h)"' ``` -------------------------------- ### Dynamically Defined Tests with Minitest Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/test_explorer.markdown Demonstrates how Minitest tests defined dynamically using loops are discovered when the entire file is run. ```ruby class MyTest < Minitest::Spec # These are detected automatically describe "something" do it "does a useful thing" do end end # Dynamically defined tests like these are only discovered while running the entire file [:first, :second, :third].each do |name| it "does the #{name} well" do end end end ``` -------------------------------- ### Launch Ruby LSP with asdf Shims Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Launch the Ruby LSP executable using the shims provided by asdf, which automatically manage the Ruby version. ```shell ~/.asdf/shims/ruby-lsp ``` -------------------------------- ### Combine File Scoping and Name Filtering Source: https://github.com/shopify/ruby-lsp/blob/main/CLAUDE.md Achieve the fastest feedback loop by combining file scoping with name filtering for targeted test execution. ```bash pnpm run test -- --run out/test/suite/testController.test.js --grep "discovers tests" ``` -------------------------------- ### Exploring Class Methods with Type Guessing Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/index.markdown Demonstrates how to use type guessing to explore methods available in classes by simply typing the lowercase name of the class as an identifier. ```ruby # Any class name as an identifier pathname.a integer.a file.a ``` -------------------------------- ### Run Multiple Compiled Test Files Source: https://github.com/shopify/ruby-lsp/blob/main/CLAUDE.md Execute multiple test files simultaneously by listing them after the `--run` flag. ```bash pnpm run test -- --run out/test/suite/workspace.test.js --run out/test/suite/client.test.js ``` -------------------------------- ### Configure Formatter Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/vscode-extension.markdown Specify the tool to be used for formatting files. Options are 'auto' to detect based on the app's bundle, 'none' to disable formatting, or the name of a specific formatter like 'rubocop' or 'syntax_tree'. ```json "rubyLsp.formatter": "auto" ``` -------------------------------- ### Launch Ruby LSP with rbenv Shims Source: https://github.com/shopify/ruby-lsp/blob/main/jekyll/editors.markdown Launch the Ruby LSP executable using the shims provided by rbenv, which automatically manage the Ruby version. ```shell ~/.rbenv/shims/ruby-lsp ``` -------------------------------- ### Run Single Compiled Test File Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Execute a single compiled test file using the '--run' flag with 'pnpm run test'. Ensure you use the .js path, not .ts. ```bash # Run a single compiled test file (use the .js path, not .ts) pnpm run test -- --run out/test/suite/workspace.test.js ``` -------------------------------- ### Run Tests Matching a Pattern Source: https://github.com/shopify/ruby-lsp/blob/main/AGENTS.md Command to execute tests that match a given pattern. This allows for targeted testing of specific test cases. ```bash # Run tests matching a pattern bin/test test/requests/completion_test.rb test_name_pattern ```