Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Install Nix
uses: cachix/install-nix-action@v31
- name: Run tests
run: ./mill _.test
run: nix develop --command ./mill _.test
92 changes: 92 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build System and Commands

This project uses Mill as the build tool. Essential commands:

- **Build**: `./mill sls.compile` - Compiles the main module
- **Test**: `./mill sls.test` - Runs all tests
- **Test single**: `./mill sls.test.testOnly org.scala.abusers.sls.integration.LanguageFeaturesTests` - Run specific test class
- **Assembly**: `./mill sls.assembly` - Creates executable JAR
- **Run**: `./mill sls.run` - Runs the language server

Try to use Metals MCP to compile, test or run tests

### Development Workflow

- When using multiline string always use """ syntax with | aligned with the third quote and finished with """.stripMargin
- Always check available methods / signatures before using it via metals mcp
- Before running tests via metals mcp, first compile them with `./mill sls.test.compile`
- Try to sketch down tasks in ./docs/plan_$number.md

### Test Categories

- **LanguageFeaturesTests**: Tests LSP language features (completion, hover, definition, etc.) - uses real mill BSP server
- **BSPIntegrationTests**: Tests BSP server integration and capabilities
- **TextDocumentSyncIntegrationTests**: Tests document lifecycle management
- **RealWorldScenarioTests**: Performance and stress tests
- **ProtocolLifecycleTests**: Tests LSP protocol state management

## Architecture Overview

### Core Components

**SimpleScalaServer** (main entry point): Sets up LSP server with JSON-RPC over stdin/stdout. Creates the dependency graph of core services and wires them together.

**ServerImpl**: Main LSP server implementation handling all LSP protocol methods. Delegates to specialized managers for different concerns.

**StateManager**: Coordinates between text document state and BSP build target information. Acts as the bridge between LSP document operations and build server queries.

**BspStateManager**: Manages BSP (Build Server Protocol) connections and tracks mapping between source files and their build targets. Handles build target discovery and caching.

**TextDocumentSyncManager**: Tracks document state (open, modified, saved, closed). Maintains document versions and content for presentation compiler.

**PresentationCompilerProvider**: Manages Scala 3 presentation compiler instances per build target. Provides completions, hover info, diagnostics, and other language features.

**DiagnosticManager**: Handles diagnostic publishing with debouncing to avoid excessive updates during rapid typing.

### Key Data Flow

1. **Initialization**: LSP client connects → ServerImpl.initializeOp → mill BSP server discovery → BspStateManager.importBuild → build targets cached
2. **Document Open**: textDocument/didOpen → StateManager → BspStateManager.didOpen → maps file to build target → creates presentation compiler
3. **Language Features**: completion/hover requests → StateManager gets build target info → PresentationCompilerProvider provides language service
4. **Document Changes**: textDocument/didChange → debounced diagnostics → presentation compiler analysis

### BSP Integration

The server discovers and connects to mill's BSP server automatically during initialization. It uses `findMillExec()` to locate the mill executable and runs `mill.contrib.bloop.Bloop/install` to set up BSP.

Build targets are mapped to source files through BSP's `buildTargetInverseSources` API. Each source file gets associated with a build target containing classpath, compiler options, and Scala version info.

### Smithy Code Generation

The `slsSmithy` module uses Smithy4s to generate LSP and BSP protocol types from `.smithy` definitions. This ensures type-safe protocol handling and keeps the codebase in sync with protocol specifications.

### Testing Infrastructure

Tests use a sophisticated setup with:
- **TestWorkspace**: Creates temporary mill projects with proper build.mill and sources
- **TestLSPClient**: Mock LSP client for capturing server responses
- **MockBSPServer**: Smithy4s-based BSP service stubs for basic connection testing

Integration tests create real mill workspaces and test against actual mill BSP servers to ensure realistic behavior.

#### BSP Testing Pattern

For BSP testing, use smithy4s patterns rather than complex mocking:
- Create simple stub implementations of `bsp.BuildServer[IO]`, `bsp.scala_.ScalaBuildServer[IO]`, etc.
- These provide predictable test data and type-safe interfaces
- Example: `StubBuildServer`, `StubScalaBuildServer` in `MockBSPServer.scala`
- This approach automatically stays in sync with protocol changes

## Important Implementation Notes

- Uses Scala 3.7.2 nightly build
- Leverages cats-effect for async/concurrent programming
- fs2 for streaming and resource management
- Chimney for type transformations between protocol types
- Never mock the BSP server for LanguageFeaturesTests - they require real compilation
- Mill execution requires finding the original mill executable (not copying to temp dirs due to permission issues)

275 changes: 275 additions & 0 deletions docs/plan_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
# Integration Testing Plan for Scala Language Server (SLS)

## Overview
Create comprehensive integration tests leveraging the Smithy-generated LSP and BSP APIs to validate the full protocol flow and real-world usage scenarios for the Simple Language Server.

## Project Architecture Context

The SLS project uses a sophisticated architecture built around:
- **Smithy Protocol Definitions**: LSP and BSP protocols defined in `slsSmithy/smithy/lsp.smithy`
- **Generated Type-Safe APIs**: Smithy4s generates `SlsLanguageServer[IO]` and `SlsLanguageClient[IO]` interfaces
- **BSP Integration**: Connects to Bloop build server for compilation and build management
- **Presentation Compiler**: Uses Scala 3.7.2+ presentation compiler for language features
- **Cats Effect IO**: Functional effect system with proper resource management

## Current Test State

### Existing Tests
- **Unit Tests**: `ResourceSupervisorSpec` - Resource management testing
- **Document Sync Tests**: `TextDocumentSyncSuite` - Basic text synchronization testing
- **Test Framework**: Weaver with cats-effect support

### Gap Analysis
- ❌ **No LSP protocol integration tests** - Server lifecycle, client-server communication
- ❌ **No BSP integration tests** - Build server connection, compilation workflow
- ❌ **No real-world scenario tests** - Multi-file projects, performance, concurrency
- ❌ **No error handling tests** - Protocol errors, timeouts, cancellation

## Integration Testing Strategy

### 1. Leverage Smithy-Generated Structure

#### Benefits of Smithy-Based Testing
- **Type Safety**: Generated case classes ensure compile-time protocol validation
- **Protocol Completeness**: All LSP operations defined in Smithy schema
- **Serialization**: Built-in JSON-RPC serialization/deserialization
- **Client/Server Interfaces**: `SlsLanguageServer[IO]` and `SlsLanguageClient[IO]` provide clean APIs

#### Generated Types Available
```scala
// LSP Protocol
trait SlsLanguageServer[F[_]] {
def initializeOp(params: InitializeParams): F[InitializeOpOutput]
def textDocumentCompletionOp(params: CompletionParams): F[CompletionOpOutput]
def textDocumentHoverOp(params: HoverParams): F[HoverOpOutput]
// ... all other LSP operations
}

trait SlsLanguageClient[F[_]] {
def publishDiagnosticsOp(params: PublishDiagnosticsParams): F[Unit]
def showMessageOp(params: ShowMessageParams): F[Unit]
// ... client notifications
}

// BSP Protocol (via bsp4s)
trait BuildServer[F[_]] {
def buildInitialize(params: InitializeBuildParams): F[InitializeBuildResult]
def buildTargets(): F[WorkspaceBuildTargetsResult]
// ... BSP operations
}
```

### 2. Test Infrastructure Design

#### A. LSP Integration Test Framework

**File Structure:**
```
sls/test/src/org/scala/abusers/sls/integration/
├── LSPIntegrationTestSuite.scala # Base test infrastructure
├── ProtocolLifecycleTests.scala # Initialize/shutdown tests
├── TextDocumentSyncTests.scala # Document sync integration
├── LanguageFeaturesTests.scala # Completion, hover, definition
├── ErrorHandlingTests.scala # Protocol error scenarios
└── utils/
├── TestLSPClient.scala # Mock LSP client implementation
├── TestWorkspace.scala # Test project fixtures
└── TestUtils.scala # Common test utilities
```

#### B. BSP Integration Test Framework

**File Structure:**
```
sls/test/src/org/scala/abusers/sls/integration/bsp/
├── BSPIntegrationTests.scala # BSP connection and lifecycle
├── BuildCompilationTests.scala # Compilation workflow tests
├── MillIntegrationTests.scala # Mill build import tests
└── utils/
├── MockBSPServer.scala # Mock BSP server for testing
└── BSPTestUtils.scala # BSP-specific test utilities
```

### 3. Test Categories and Implementation

#### A. LSP Protocol Integration Tests

##### 1. Server Lifecycle Tests (`ProtocolLifecycleTests.scala`)
```scala
object ProtocolLifecycleTests extends SimpleIOSuite {
test("server initializes with correct capabilities") { _ =>
for {
testClient <- TestLSPClient.create
server <- ServerImpl.create(testClient)
response <- server.initializeOp(InitializeParams(...))
} yield {
expect(response.capabilities.textDocumentSync.isDefined) &&
expect(response.capabilities.completionProvider.isDefined) &&
expect(response.capabilities.hoverProvider.isDefined)
}
}

test("server handles shutdown gracefully") { /* ... */ }
}
```

##### 2. Text Document Synchronization (`TextDocumentSyncTests.scala`)
- **Document Opening**: Test `didOpen` with various file types
- **Incremental Changes**: Test `didChange` with partial updates
- **Document Closing**: Test `didClose` and resource cleanup
- **Save Operations**: Test `didSave` and diagnostic updates

##### 3. Language Features (`LanguageFeaturesTests.scala`)
- **Code Completion**: Test completion requests with various contexts
- **Hover Information**: Test hover responses for symbols, types
- **Go to Definition**: Test definition lookup across files
- **Signature Help**: Test signature assistance for methods
- **Inlay Hints**: Test type and parameter hints

#### B. BSP Integration Tests

##### 1. Build Server Connection (`BSPIntegrationTests.scala`)
```scala
object BSPIntegrationTests extends SimpleIOSuite {
test("connects to Bloop BSP server") { _ =>
for {
workspace <- TestWorkspace.withMillProject
server <- ServerImpl.createWithWorkspace(workspace)
_ <- server.initialize(...)
buildTargets <- server.bspClient.buildTargets()
} yield expect(buildTargets.targets.nonEmpty)
}
}
```

##### 2. Compilation Workflow (`BuildCompilationTests.scala`)
- **Target Discovery**: Test build target identification
- **Compilation Requests**: Test compile operations via BSP
- **Diagnostic Publishing**: Test diagnostic flow from BSP to LSP client
- **Dependency Resolution**: Test classpath and dependency handling

##### 3. Mill Integration (`MillIntegrationTests.scala`)
- **Build Import**: Test Mill build.mill parsing and import
- **Bloop Plugin**: Test Mill Bloop plugin integration
- **Module Dependencies**: Test cross-module dependency resolution

#### C. Real-World Scenario Tests

##### 1. Multi-File Project Tests
- **Cross-File Navigation**: Test go-to-definition across files
- **Project-Wide Completion**: Test completion with project dependencies
- **Module Dependencies**: Test multi-module project handling

##### 2. Performance and Concurrency Tests
- **Large File Handling**: Test performance with large Scala files
- **Concurrent Requests**: Test multiple simultaneous LSP operations
- **Debounced Diagnostics**: Test diagnostic debouncing behavior (300ms)

##### 3. Error Scenarios and Edge Cases
- **Invalid Requests**: Test malformed LSP requests
- **BSP Connection Failures**: Test build server connection issues
- **Timeout Handling**: Test operation cancellation and timeouts
- **File System Changes**: Test workspace file modifications

### 4. Test Fixtures and Utilities

#### A. Test Workspace Management (`TestWorkspace.scala`)
```scala
object TestWorkspace {
def withMillProject: IO[TestWorkspace] = {
// Create temporary directory with sample Mill project
// Include build.mill, source files, dependencies
}

def withMultiModuleProject: IO[TestWorkspace] = {
// Create multi-module Mill project for testing
}

def withLargeProject: IO[TestWorkspace] = {
// Create project with many files for performance testing
}
}
```

#### B. Mock LSP Client (`TestLSPClient.scala`)
```scala
class TestLSPClient extends SlsLanguageClient[IO] {
private val diagnostics = Ref.unsafe[IO, List[PublishDiagnosticsParams]](List.empty)

def publishDiagnosticsOp(params: PublishDiagnosticsParams): IO[Unit] =
diagnostics.update(_ :+ params)

def getPublishedDiagnostics: IO[List[PublishDiagnosticsParams]] =
diagnostics.get
}
```

#### C. Test Fixtures (`sls/test/resources/`)
```
test/resources/
├── projects/
│ ├── simple-scala/ # Basic Scala project with Mill
│ │ ├── build.mill
│ │ └── src/Main.scala
│ ├── multi-module/ # Multi-module project
│ │ ├── build.mill
│ │ ├── core/src/
│ │ └── app/src/
│ └── large-project/ # Performance testing project
└── expected-responses/ # Expected LSP response data
├── completion/
├── hover/
└── diagnostics/
```

### 5. Implementation Phases

#### Phase 1: Core Infrastructure (Week 1)
1. ✅ Update `docs/plan_1.md` with comprehensive plan
2. 📝 Create `LSPIntegrationTestSuite` base class
3. 📝 Implement `TestLSPClient` and `TestWorkspace` utilities
4. 📝 Add basic test fixtures and sample projects

#### Phase 2: LSP Protocol Tests (Week 2)
1. 📝 Implement server lifecycle tests (`initializeOp`, shutdown)
2. 📝 Implement text document synchronization tests
3. 📝 Add language features integration tests
4. 📝 Implement error handling and edge case tests

#### Phase 3: BSP Integration Tests (Week 3)
1. 📝 Create BSP integration test framework
2. 📝 Implement build server connection tests
3. 📝 Add compilation workflow tests
4. 📝 Implement Mill build import tests

#### Phase 4: Real-World Scenarios (Week 4)
1. 📝 Add multi-file project navigation tests
2. 📝 Implement performance and concurrency tests
3. 📝 Add comprehensive error scenario coverage
4. 📝 Validate and document all test results

### 6. Success Criteria

#### Test Coverage Goals
- **~25-30 integration tests** covering all major LSP operations
- **~10-15 BSP integration tests** for build server functionality
- **~5-10 real-world scenario tests** for complex workflows
- **100% coverage** of Smithy-defined LSP operations

#### Quality Metrics
- **Type Safety**: All tests use generated Smithy types
- **Maintainability**: Tests are well-structured and documented
- **Performance**: Tests complete within reasonable time bounds
- **Reliability**: Tests pass consistently and catch regressions

#### Documentation Deliverables
- **Test Architecture Documentation**: How to write and run integration tests
- **Test Scenario Coverage**: What scenarios are tested and why
- **Performance Benchmarks**: Expected performance characteristics
- **Troubleshooting Guide**: Common test failures and solutions

## Conclusion

This comprehensive integration testing plan leverages the project's Smithy-based architecture to create robust, type-safe tests that validate the complete LSP and BSP protocol flow. By using the generated APIs and focusing on real-world scenarios, these tests will ensure the Simple Language Server works reliably in production environments.

The phased implementation approach allows for incremental progress while maintaining test quality and coverage. The emphasis on Smithy-generated types ensures tests remain maintainable and aligned with the protocol definitions.
5 changes: 4 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
perSystem = { system, config, pkgs, ... }:
{
devShells.default = pkgs.mkShell {
packages = [ pkgs.jdk21 ];
packages = [
pkgs.jdk21
pkgs.bloop
];
inputsFrom = [
config.treefmt.build.devShell
];
Expand Down
Loading