|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Ruby LSP is a Language Server Protocol implementation for Ruby, providing IDE features across different editors. The project consists of: |
| 8 | + |
| 9 | +- Core Ruby LSP gem (language server implementation) |
| 10 | +- Ruby Indexer (code indexing for navigation) |
| 11 | +- VS Code extension (TypeScript frontend) |
| 12 | +- Documentation site (Jekyll-based) |
| 13 | + |
| 14 | +## Development Commands |
| 15 | + |
| 16 | +### Ruby LSP Core |
| 17 | + |
| 18 | +```bash |
| 19 | +# Run all tests |
| 20 | +bundle exec rake |
| 21 | + |
| 22 | +# Run specific test file |
| 23 | +bin/test test/requests/completion_test.rb |
| 24 | + |
| 25 | +# Run tests matching a pattern |
| 26 | +bin/test test/requests/completion_test.rb test_name_pattern |
| 27 | + |
| 28 | +# Type check with Sorbet |
| 29 | +bundle exec srb tc |
| 30 | + |
| 31 | +# Lint with RuboCop |
| 32 | +bin/rubocop |
| 33 | + |
| 34 | +# Auto-fix RuboCop violations |
| 35 | +bin/rubocop -a |
| 36 | +``` |
| 37 | + |
| 38 | +### VS Code Extension |
| 39 | + |
| 40 | +```bash |
| 41 | +cd vscode |
| 42 | +yarn install # Install dependencies |
| 43 | +yarn run lint # Lint TypeScript code |
| 44 | +yarn run test # Run extension tests |
| 45 | +``` |
| 46 | + |
| 47 | +### Executables |
| 48 | + |
| 49 | +- `exe/ruby-lsp` - Main language server executable |
| 50 | +- `exe/ruby-lsp-check` - Validation tool |
| 51 | +- `exe/ruby-lsp-launcher` - Experimental launcher |
| 52 | + |
| 53 | +## Architecture |
| 54 | + |
| 55 | +### Server Structure |
| 56 | + |
| 57 | +The server (`lib/ruby_lsp/server.rb`) processes LSP messages and delegates to: |
| 58 | + |
| 59 | +- **Requests** (`lib/ruby_lsp/requests/`) - Handle specific LSP features |
| 60 | +- **Listeners** (`lib/ruby_lsp/listeners/`) - Analyze Ruby code for the specific feature |
| 61 | +- **Response Builders** (`lib/ruby_lsp/response_builders/`) - Construct LSP responses |
| 62 | + |
| 63 | +### Key Components |
| 64 | + |
| 65 | +- **Document Store** (`lib/ruby_lsp/document.rb`) - Manages open documents and parsed ASTs |
| 66 | +- **Ruby Indexer** (`lib/ruby_indexer/`) - Indexes Ruby code for navigation features |
| 67 | +- **Addon System** (`lib/ruby_lsp/addon.rb`) - Extensibility framework for third-party addons |
| 68 | + |
| 69 | +### Design Patterns |
| 70 | + |
| 71 | +- **Visitor Pattern**: AST traversal using Prism parser visitors |
| 72 | +- **Request/Response**: Each LSP feature implemented as a request class |
| 73 | +- **Type Safety**: Sorbet signatures throughout (strict for lib/, test for tests) |
| 74 | + |
| 75 | +### Important Top-Level Files |
| 76 | + |
| 77 | +#### Core Server Components |
| 78 | + |
| 79 | +- **`lib/ruby_lsp/server.rb`** - Main LSP server handling all client requests and message routing |
| 80 | +- **`lib/ruby_lsp/base_server.rb`** - Abstract base with core LSP infrastructure (message I/O, worker threads) |
| 81 | +- **`lib/ruby_lsp/global_state.rb`** - Central configuration and state management (formatters, linters, client capabilities) |
| 82 | +- **`lib/ruby_lsp/store.rb`** - Document storage and lifecycle management |
| 83 | + |
| 84 | +#### Document Handling |
| 85 | + |
| 86 | +- **`lib/ruby_lsp/document.rb`** - Abstract base for all document types (versioning, edits, encoding) |
| 87 | +- **`lib/ruby_lsp/ruby_document.rb`** - Ruby source file handling with Prism parsing |
| 88 | +- **`lib/ruby_lsp/erb_document.rb`** - ERB template file handling |
| 89 | +- **`lib/ruby_lsp/rbs_document.rb`** - RBS type definition file handling |
| 90 | + |
| 91 | +#### Supporting Infrastructure |
| 92 | + |
| 93 | +- **`lib/ruby_lsp/addon.rb`** - Extension framework for third-party add-ons |
| 94 | +- **`lib/ruby_lsp/node_context.rb`** - AST node context information (nesting, scope) |
| 95 | +- **`lib/ruby_lsp/type_inferrer.rb`** - Type inference functionality |
| 96 | +- **`lib/ruby_lsp/client_capabilities.rb`** - Client feature capability tracking |
| 97 | + |
| 98 | +### Component Interaction Flow |
| 99 | + |
| 100 | +```mermaid |
| 101 | +graph TD |
| 102 | + Client[VS Code/Editor Client] |
| 103 | + Server[server.rb<br/>Main LSP Server] |
| 104 | + BaseServer[base_server.rb<br/>Message I/O & Threading] |
| 105 | + GlobalState[global_state.rb<br/>Configuration & State] |
| 106 | + Store[store.rb<br/>Document Manager] |
| 107 | +
|
| 108 | + RubyDoc[ruby_document.rb<br/>Ruby Files] |
| 109 | + ERBDoc[erb_document.rb<br/>ERB Templates] |
| 110 | + RBSDoc[rbs_document.rb<br/>RBS Types] |
| 111 | +
|
| 112 | + Requests[requests/*<br/>Feature Handlers] |
| 113 | + Listeners[listeners/*<br/>AST Analyzers] |
| 114 | + Indexer[ruby_indexer/*<br/>Symbol Index] |
| 115 | +
|
| 116 | + Addon[addon.rb<br/>Extension System] |
| 117 | + NodeContext[node_context.rb<br/>AST Context] |
| 118 | +
|
| 119 | + Client <-->|LSP Protocol| Server |
| 120 | + Server -.->|inherits| BaseServer |
| 121 | + Server --> GlobalState |
| 122 | + Server --> Store |
| 123 | + Server --> Requests |
| 124 | + Server --> Addon |
| 125 | +
|
| 126 | + GlobalState --> Requests |
| 127 | + Document --> Requests |
| 128 | +
|
| 129 | + Store --> RubyDoc |
| 130 | + Store --> ERBDoc |
| 131 | + Store --> RBSDoc |
| 132 | +
|
| 133 | + RubyDoc -.->|inherits| Document[document.rb] |
| 134 | + ERBDoc -.->|inherits| Document |
| 135 | + RBSDoc -.->|inherits| Document |
| 136 | +
|
| 137 | + Requests --> Listeners |
| 138 | + Requests --> NodeContext |
| 139 | + Listeners --> Indexer |
| 140 | +
|
| 141 | + Addon -->|extends| Requests |
| 142 | +
|
| 143 | + style Server fill:#f9f,stroke:#333,stroke-width:4px |
| 144 | + style GlobalState fill:#9ff,stroke:#333,stroke-width:2px |
| 145 | + style Store fill:#9ff,stroke:#333,stroke-width:2px |
| 146 | + style Indexer fill:#ff9,stroke:#333,stroke-width:2px |
| 147 | +``` |
| 148 | + |
| 149 | +## Testing Approach |
| 150 | + |
| 151 | +Tests use expectation-based patterns: |
| 152 | + |
| 153 | +```ruby |
| 154 | +# Tests typically follow this structure: |
| 155 | +def test_feature_name |
| 156 | + source = <<~RUBY |
| 157 | + # Ruby code to test |
| 158 | + RUBY |
| 159 | + |
| 160 | + with_server(source) do |server, _uri| |
| 161 | + # Make LSP request |
| 162 | + # Assert response |
| 163 | + end |
| 164 | +end |
| 165 | +``` |
| 166 | + |
| 167 | +Test fixtures are in `test/fixtures/` and expectations in `test/expectations/`. |
| 168 | + |
| 169 | +## Common Development Tasks |
| 170 | + |
| 171 | +### Adding a New LSP Feature |
| 172 | + |
| 173 | +1. Create request class in `lib/ruby_lsp/requests/` |
| 174 | +2. Register in `lib/ruby_lsp/executor.rb` |
| 175 | +3. Add tests in `test/requests/` |
| 176 | +4. Update VS Code extension if needed |
| 177 | + |
| 178 | +### Working with the Indexer |
| 179 | + |
| 180 | +The Ruby Indexer (`lib/ruby_indexer/`) handles: |
| 181 | + |
| 182 | +- Building symbol tables for classes, modules, methods, and constants |
| 183 | +- Resolving constant references and method calls |
| 184 | +- Finding definitions across files |
| 185 | +- Providing completion candidates for constants and methods |
| 186 | +- Dealing with inheritance and ancestor linearization |
| 187 | + |
| 188 | +### Typechecking with Sorbet |
| 189 | + |
| 190 | +Ruby LSP uses Sorbet (typed: strict) with inline RBS annotations for static typechecking. |
| 191 | + |
| 192 | +**Key Guidelines:** |
| 193 | + |
| 194 | +- Use RBS inline annotations (`#:`) exclusively - never use RBI style `sig { }` |
| 195 | +- Place type annotations immediately before method definitions or after variable assignments |
| 196 | +- Run `bundle exec srb tc` to ensure typechecking passes |
| 197 | + |
| 198 | +**Common RBS Patterns:** |
| 199 | + |
| 200 | +```ruby |
| 201 | +# Method signatures (placed above method definition) |
| 202 | +#: (String name) -> void |
| 203 | +def process(name) |
| 204 | + # ... |
| 205 | +end |
| 206 | + |
| 207 | +# Variable annotations (placed after assignment) |
| 208 | +@documents = {} #: Hash[URI::Generic, Document] |
| 209 | + |
| 210 | +# Attribute type declarations (placed above attribute) |
| 211 | +#: String? |
| 212 | +attr_reader :parent_class |
| 213 | + |
| 214 | +# Generic types |
| 215 | +#: [T] () { (String) -> T } -> T |
| 216 | +def with_cache(&block) |
| 217 | + # ... |
| 218 | +end |
| 219 | + |
| 220 | +# Union and nullable types |
| 221 | +result = nil #: (String | Symbol)? |
| 222 | +``` |
| 223 | + |
| 224 | +**Type Syntax Reference:** <https://sorbet.org/docs/rbs-support> |
0 commit comments