diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 000000000..676885459
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,245 @@
+# FsAutoComplete Copilot Instructions
+
+## Project Overview
+
+FsAutoComplete (FSAC) is a Language Server Protocol (LSP) backend service that provides rich editing and intellisense features for F# development. It serves as the core engine behind F# support in various editors including Visual Studio Code (Ionide), Emacs, Neovim, Vim, Sublime Text, and Zed.
+
+## Supported Editors
+
+FsAutoComplete currently provides F# support for:
+- **Visual Studio Code** (via [Ionide](https://github.com/ionide/ionide-vscode-fsharp))
+- **Emacs** (via [emacs-fsharp-mode](https://github.com/fsharp/emacs-fsharp-mode))
+- **Neovim** (via [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#fsautocomplete))
+- **Vim** (via [vim-fsharp](https://github.com/fsharp/vim-fsharp))
+- **Sublime Text** (via [LSP package](https://lsp.sublimetext.io/language_servers/#f))
+- **Zed** (via [zed-fsharp](https://github.com/nathanjcollins/zed-fsharp))
+
+## Architecture
+
+### Core Components
+
+- **FsAutoComplete.Core**: Contains the core functionality, including:
+ - F# compiler service interfaces
+ - Code generation and refactoring utilities
+ - Symbol resolution and type checking
+ - Signature formatting and documentation
+ - File system abstractions
+
+- **FsAutoComplete**: Main LSP server implementation with:
+ - LSP protocol handlers and endpoints
+ - Code fixes and quick actions
+ - Parser for LSP requests/responses
+ - Program entry point
+
+- **FsAutoComplete.Logging**: Centralized logging infrastructure
+
+### Key Dependencies
+
+- **FSharp.Compiler.Service** (>= 43.9.300): Core F# compiler APIs for language analysis
+- **Ionide.ProjInfo** (>= 0.71.2): Project and solution file parsing, with separate packages for FCS integration and project system
+- **FSharpLint.Core**: Code linting and static analysis
+- **Fantomas.Client** (>= 0.9): F# code formatting
+- **Microsoft.Build** (>= 17.2): MSBuild integration for project loading
+- **Serilog** (>= 2.10.0): Structured logging infrastructure
+- **Language Server Protocol**: Communication with editors
+
+#### Target Frameworks
+- **netstandard2.0 & netstandard2.1**: For broader compatibility
+- **net8.0 & net9.0**: For latest .NET features and performance
+
+## Development Workflow
+
+### Building the Project
+
+Requirements:
+- .NET SDK (see `global.json` for exact version - minimum >= 6.0, recommended >= 7.0)
+
+```bash
+# Restore .NET tools (including Paket)
+dotnet tool restore
+
+# Build the entire solution
+dotnet build
+
+# Run tests
+dotnet test
+
+# Run specific test project
+dotnet test -f net8.0 ./test/FsAutoComplete.Tests.Lsp/FsAutoComplete.Tests.Lsp.fsproj
+
+# Format code
+dotnet fantomas src/ test/
+```
+
+#### Development Environment Options
+- **DevContainer**: Use with VSCode's Remote Containers extension for stable development environment
+- **Gitpod**: Web-based VSCode IDE available at https://gitpod.io/#https://github.com/fsharp/fsautocomplete
+
+### Project Dependencies
+
+This project uses **Paket** for dependency management instead of NuGet directly:
+- Dependencies are declared in `paket.dependencies`
+- Lock file is `paket.lock`
+- Each project has its own `paket.references` file
+
+### Code Organization
+
+#### Code Fixes
+- Located in `src/FsAutoComplete/CodeFixes/`
+- Each code fix is typically a separate F# module
+- Follow the pattern: analyze issue โ generate fix โ apply transformation
+- **Scaffolding**: Use `dotnet fsi build.fsx -- -p ScaffoldCodeFix YourCodeFixName` to create new code fixes
+- This generates implementation file, signature file, and unit test, plus updates registration files
+- Examples include: `ImplementInterface.fs`, `GenerateUnionCases.fs`, `AddMissingEqualsToTypeDefinition.fs`
+
+#### LSP Endpoints
+- Standard LSP endpoints in `src/FsAutoComplete/LspServers/`
+- Key server files: `AdaptiveFSharpLspServer.fs`, `AdaptiveServerState.fs`, `ProjectWorkspace.fs`
+- Custom F#-specific endpoints prefixed with `fsharp/`
+- Request/response types in `CommandResponse.fs`
+- Interface definitions in `IFSharpLspServer.fs`
+
+#### Testing
+- Main test suite in `test/FsAutoComplete.Tests.Lsp/`
+- Tests organized by feature area (CompletionTests, CodeFixTests, etc.)
+- Uses F# testing frameworks with custom helpers in `Helpers.fs`
+- Test cases often in `TestCases/` subdirectories
+
+## F# Language Conventions
+
+### Coding Style
+- Follow F# community conventions
+- Use `fantomas` for code formatting (configured in the project)
+- Prefer immutable data structures and functional programming patterns
+- Use explicit type annotations where they improve clarity
+
+### Module Organization
+- One primary type/feature per file
+- Use `.fs` and `.fsi` pairs for public APIs
+- Organize related functionality into modules
+- Follow naming conventions: `CamelCase` for types, `camelCase` for values
+
+### Error Handling
+- Use F# Result types for error handling where appropriate
+- Use FsToolkit.ErrorHandling for railway-oriented programming
+- Prefer explicit error types over generic exceptions
+
+## LSP Implementation Details
+
+### Supported Standard LSP Features
+- `textDocument/completion` with `completionItem/resolve`
+- `textDocument/hover`, `textDocument/definition`, `textDocument/references`
+- `textDocument/codeAction`, `textDocument/codeLens`
+- `textDocument/formatting` (via Fantomas)
+- `textDocument/rename`, `textDocument/signatureHelp`
+- Workspace management and file watching
+
+### Custom F# Extensions
+- `fsharp/signature`: Get formatted signature at position
+- `fsharp/compile`: Compile project and return diagnostics
+- `fsharp/workspacePeek`: Discover available projects/solutions
+- `fsharp/workspaceLoad`: Load specific projects
+- `fsproj/addFile`, `fsproj/removeFile`: Project file manipulation
+- `fsharp/documentationForSymbol`: Get documentation for symbols
+- `fsharp/f1Help`: F1 help functionality
+- `fsharp/fsi`: F# Interactive integration
+
+## Testing Guidelines
+
+### Test Structure
+- Tests are organized by feature area
+- Use descriptive test names that explain the scenario
+- Include both positive and negative test cases
+- Test with realistic F# code examples
+
+### Adding New Tests
+1. Identify the appropriate test file (e.g., `CompletionTests.fs` for completion features)
+2. Follow existing patterns for test setup and assertions
+3. Use the helpers in `Helpers.fs` for common operations
+4. Include edge cases and error conditions
+5. For code fixes: Run focused tests with `dotnet run -f net8.0 --project ./test/FsAutoComplete.Tests.Lsp/FsAutoComplete.Tests.Lsp.fsproj`
+6. Remove focused test markers before submitting PRs (they cause CI failures)
+
+### Test Data
+- Sample F# projects in `TestCases/` directories
+- Use minimal, focused examples that demonstrate specific features
+- Avoid overly complex test scenarios that are hard to debug
+
+## Performance Considerations
+
+### Memory Management
+- Be mindful of memory usage in long-running language server scenarios
+- Dispose of compiler service resources appropriately
+- Use caching judiciously to balance performance and memory
+
+### Responsiveness
+- LSP operations should be fast and non-blocking
+- Use async/await patterns for I/O operations
+- Consider cancellation tokens for long-running operations
+
+## Debugging and Telemetry
+
+### OpenTelemetry Integration
+- Tracing is available with `--otel-exporter-enabled` flag
+- Use Jaeger for trace visualization during development
+- Activity tracing helps debug performance issues
+
+### Logging
+- Structured logging via Serilog
+- Use appropriate log levels (Debug, Info, Warning, Error)
+- Include relevant context in log messages
+
+## Common Patterns
+
+### Working with FCS (F# Compiler Service)
+- Always work with `FSharpCheckFileResults` and `FSharpParseFileResults`
+- Handle both parsed and typed ASTs appropriately
+- Be aware of file dependencies and project context
+
+### LSP Request Handling
+- Validate input parameters
+- Handle exceptions gracefully
+- Return appropriate error responses for invalid requests
+- Use proper JSON serialization
+
+### Code Generation
+- Use the F# AST utilities in `TypedAstUtils.fs` and `UntypedAstUtils.fs`
+- Consider both syntactic and semantic correctness
+- Test generated code compiles and has expected behavior
+
+## Contributing Guidelines
+
+### Before Submitting Changes
+1. Ensure all tests pass: `dotnet test`
+2. Run code formatting: `dotnet fantomas src/ test/`
+3. Verify the solution builds cleanly
+4. Test your changes with a real F# project if possible
+
+### Code Review Focus Areas
+- Correctness of F# language analysis
+- Performance impact on language server operations
+- Compatibility with different F# project types
+- LSP protocol compliance
+- Test coverage for new features
+
+## Resources
+
+### Core Documentation
+- [FsAutoComplete GitHub Repository](https://github.com/ionide/FsAutoComplete)
+- [LSP Specification](https://microsoft.github.io/language-server-protocol/)
+- [F# Compiler Service Documentation](https://fsharp.github.io/FSharp.Compiler.Service/)
+
+### F# Development Guidelines
+- [F# Style Guide](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/)
+- [F# Formatting Guidelines](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting)
+- [F# Component Design Guidelines](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/component-design-guidelines)
+
+### Project-Specific Guides
+- [Creating a New Code Fix Guide](./docs/Creating%20a%20new%20code%20fix.md)
+- [Ionide.ProjInfo Documentation](https://github.com/ionide/proj-info)
+- [Fantomas Configuration](https://fsprojects.github.io/fantomas/)
+
+### Related Tools
+- [FSharpLint](https://github.com/fsprojects/FSharpLint/) - Static analysis tool
+- [Paket](https://fsprojects.github.io/Paket/) - Dependency management
+- [FAKE](https://fake.build/) - Build automation (used for scaffolding)
\ No newline at end of file
diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml
new file mode 100644
index 000000000..e0861425a
--- /dev/null
+++ b/.github/workflows/copilot-setup-steps.yml
@@ -0,0 +1,61 @@
+name: Copilot Setup Steps
+
+# This workflow is designed to be used by GitHub Copilot coding agents
+# to set up the environment needed to work with the FsAutoComplete repository
+
+on:
+ workflow_dispatch:
+ push:
+ paths:
+ - .github/workflows/copilot-setup-steps.yml
+ pull_request:
+ paths:
+ - .github/workflows/copilot-setup-steps.yml
+
+jobs:
+ copilot-setup-steps:
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ global-json-file: global.json
+
+ - name: Announce .NET version
+ run: dotnet --info
+
+ - name: Restore tools
+ run: dotnet tool restore
+
+ - name: Restore packages
+ run: dotnet restore
+
+ - name: Build solution
+ run: dotnet build -c Release --no-restore
+
+ - name: Verify Fantomas formatting
+ run: dotnet fantomas --check build.fsx src
+ env:
+ DOTNET_ROLL_FORWARD: LatestMajor
+ DOTNET_ROLL_FORWARD_TO_PRERELEASE: 1
+
+ - name: Run quick tests
+ run: dotnet test -c Release -f net8.0 --no-restore --no-build --logger "console;verbosity=minimal" -- Expecto.fail-on-focused-tests=true
+ working-directory: test/FsAutoComplete.Tests.Lsp
+ timeout-minutes: 5
+ continue-on-error: true # Don't fail the setup if tests fail
+
+ - name: Setup completed
+ run: |
+ echo "โ
Copilot setup completed successfully!"
+ echo "๐ฆ Dependencies restored"
+ echo "๐จ Build verified"
+ echo "๐ Code formatting checked"
+ echo "๐งช Basic tests run"
+ echo ""
+ echo "The environment is ready for Copilot to work with FsAutoComplete."
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..676885459
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,245 @@
+# FsAutoComplete Copilot Instructions
+
+## Project Overview
+
+FsAutoComplete (FSAC) is a Language Server Protocol (LSP) backend service that provides rich editing and intellisense features for F# development. It serves as the core engine behind F# support in various editors including Visual Studio Code (Ionide), Emacs, Neovim, Vim, Sublime Text, and Zed.
+
+## Supported Editors
+
+FsAutoComplete currently provides F# support for:
+- **Visual Studio Code** (via [Ionide](https://github.com/ionide/ionide-vscode-fsharp))
+- **Emacs** (via [emacs-fsharp-mode](https://github.com/fsharp/emacs-fsharp-mode))
+- **Neovim** (via [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#fsautocomplete))
+- **Vim** (via [vim-fsharp](https://github.com/fsharp/vim-fsharp))
+- **Sublime Text** (via [LSP package](https://lsp.sublimetext.io/language_servers/#f))
+- **Zed** (via [zed-fsharp](https://github.com/nathanjcollins/zed-fsharp))
+
+## Architecture
+
+### Core Components
+
+- **FsAutoComplete.Core**: Contains the core functionality, including:
+ - F# compiler service interfaces
+ - Code generation and refactoring utilities
+ - Symbol resolution and type checking
+ - Signature formatting and documentation
+ - File system abstractions
+
+- **FsAutoComplete**: Main LSP server implementation with:
+ - LSP protocol handlers and endpoints
+ - Code fixes and quick actions
+ - Parser for LSP requests/responses
+ - Program entry point
+
+- **FsAutoComplete.Logging**: Centralized logging infrastructure
+
+### Key Dependencies
+
+- **FSharp.Compiler.Service** (>= 43.9.300): Core F# compiler APIs for language analysis
+- **Ionide.ProjInfo** (>= 0.71.2): Project and solution file parsing, with separate packages for FCS integration and project system
+- **FSharpLint.Core**: Code linting and static analysis
+- **Fantomas.Client** (>= 0.9): F# code formatting
+- **Microsoft.Build** (>= 17.2): MSBuild integration for project loading
+- **Serilog** (>= 2.10.0): Structured logging infrastructure
+- **Language Server Protocol**: Communication with editors
+
+#### Target Frameworks
+- **netstandard2.0 & netstandard2.1**: For broader compatibility
+- **net8.0 & net9.0**: For latest .NET features and performance
+
+## Development Workflow
+
+### Building the Project
+
+Requirements:
+- .NET SDK (see `global.json` for exact version - minimum >= 6.0, recommended >= 7.0)
+
+```bash
+# Restore .NET tools (including Paket)
+dotnet tool restore
+
+# Build the entire solution
+dotnet build
+
+# Run tests
+dotnet test
+
+# Run specific test project
+dotnet test -f net8.0 ./test/FsAutoComplete.Tests.Lsp/FsAutoComplete.Tests.Lsp.fsproj
+
+# Format code
+dotnet fantomas src/ test/
+```
+
+#### Development Environment Options
+- **DevContainer**: Use with VSCode's Remote Containers extension for stable development environment
+- **Gitpod**: Web-based VSCode IDE available at https://gitpod.io/#https://github.com/fsharp/fsautocomplete
+
+### Project Dependencies
+
+This project uses **Paket** for dependency management instead of NuGet directly:
+- Dependencies are declared in `paket.dependencies`
+- Lock file is `paket.lock`
+- Each project has its own `paket.references` file
+
+### Code Organization
+
+#### Code Fixes
+- Located in `src/FsAutoComplete/CodeFixes/`
+- Each code fix is typically a separate F# module
+- Follow the pattern: analyze issue โ generate fix โ apply transformation
+- **Scaffolding**: Use `dotnet fsi build.fsx -- -p ScaffoldCodeFix YourCodeFixName` to create new code fixes
+- This generates implementation file, signature file, and unit test, plus updates registration files
+- Examples include: `ImplementInterface.fs`, `GenerateUnionCases.fs`, `AddMissingEqualsToTypeDefinition.fs`
+
+#### LSP Endpoints
+- Standard LSP endpoints in `src/FsAutoComplete/LspServers/`
+- Key server files: `AdaptiveFSharpLspServer.fs`, `AdaptiveServerState.fs`, `ProjectWorkspace.fs`
+- Custom F#-specific endpoints prefixed with `fsharp/`
+- Request/response types in `CommandResponse.fs`
+- Interface definitions in `IFSharpLspServer.fs`
+
+#### Testing
+- Main test suite in `test/FsAutoComplete.Tests.Lsp/`
+- Tests organized by feature area (CompletionTests, CodeFixTests, etc.)
+- Uses F# testing frameworks with custom helpers in `Helpers.fs`
+- Test cases often in `TestCases/` subdirectories
+
+## F# Language Conventions
+
+### Coding Style
+- Follow F# community conventions
+- Use `fantomas` for code formatting (configured in the project)
+- Prefer immutable data structures and functional programming patterns
+- Use explicit type annotations where they improve clarity
+
+### Module Organization
+- One primary type/feature per file
+- Use `.fs` and `.fsi` pairs for public APIs
+- Organize related functionality into modules
+- Follow naming conventions: `CamelCase` for types, `camelCase` for values
+
+### Error Handling
+- Use F# Result types for error handling where appropriate
+- Use FsToolkit.ErrorHandling for railway-oriented programming
+- Prefer explicit error types over generic exceptions
+
+## LSP Implementation Details
+
+### Supported Standard LSP Features
+- `textDocument/completion` with `completionItem/resolve`
+- `textDocument/hover`, `textDocument/definition`, `textDocument/references`
+- `textDocument/codeAction`, `textDocument/codeLens`
+- `textDocument/formatting` (via Fantomas)
+- `textDocument/rename`, `textDocument/signatureHelp`
+- Workspace management and file watching
+
+### Custom F# Extensions
+- `fsharp/signature`: Get formatted signature at position
+- `fsharp/compile`: Compile project and return diagnostics
+- `fsharp/workspacePeek`: Discover available projects/solutions
+- `fsharp/workspaceLoad`: Load specific projects
+- `fsproj/addFile`, `fsproj/removeFile`: Project file manipulation
+- `fsharp/documentationForSymbol`: Get documentation for symbols
+- `fsharp/f1Help`: F1 help functionality
+- `fsharp/fsi`: F# Interactive integration
+
+## Testing Guidelines
+
+### Test Structure
+- Tests are organized by feature area
+- Use descriptive test names that explain the scenario
+- Include both positive and negative test cases
+- Test with realistic F# code examples
+
+### Adding New Tests
+1. Identify the appropriate test file (e.g., `CompletionTests.fs` for completion features)
+2. Follow existing patterns for test setup and assertions
+3. Use the helpers in `Helpers.fs` for common operations
+4. Include edge cases and error conditions
+5. For code fixes: Run focused tests with `dotnet run -f net8.0 --project ./test/FsAutoComplete.Tests.Lsp/FsAutoComplete.Tests.Lsp.fsproj`
+6. Remove focused test markers before submitting PRs (they cause CI failures)
+
+### Test Data
+- Sample F# projects in `TestCases/` directories
+- Use minimal, focused examples that demonstrate specific features
+- Avoid overly complex test scenarios that are hard to debug
+
+## Performance Considerations
+
+### Memory Management
+- Be mindful of memory usage in long-running language server scenarios
+- Dispose of compiler service resources appropriately
+- Use caching judiciously to balance performance and memory
+
+### Responsiveness
+- LSP operations should be fast and non-blocking
+- Use async/await patterns for I/O operations
+- Consider cancellation tokens for long-running operations
+
+## Debugging and Telemetry
+
+### OpenTelemetry Integration
+- Tracing is available with `--otel-exporter-enabled` flag
+- Use Jaeger for trace visualization during development
+- Activity tracing helps debug performance issues
+
+### Logging
+- Structured logging via Serilog
+- Use appropriate log levels (Debug, Info, Warning, Error)
+- Include relevant context in log messages
+
+## Common Patterns
+
+### Working with FCS (F# Compiler Service)
+- Always work with `FSharpCheckFileResults` and `FSharpParseFileResults`
+- Handle both parsed and typed ASTs appropriately
+- Be aware of file dependencies and project context
+
+### LSP Request Handling
+- Validate input parameters
+- Handle exceptions gracefully
+- Return appropriate error responses for invalid requests
+- Use proper JSON serialization
+
+### Code Generation
+- Use the F# AST utilities in `TypedAstUtils.fs` and `UntypedAstUtils.fs`
+- Consider both syntactic and semantic correctness
+- Test generated code compiles and has expected behavior
+
+## Contributing Guidelines
+
+### Before Submitting Changes
+1. Ensure all tests pass: `dotnet test`
+2. Run code formatting: `dotnet fantomas src/ test/`
+3. Verify the solution builds cleanly
+4. Test your changes with a real F# project if possible
+
+### Code Review Focus Areas
+- Correctness of F# language analysis
+- Performance impact on language server operations
+- Compatibility with different F# project types
+- LSP protocol compliance
+- Test coverage for new features
+
+## Resources
+
+### Core Documentation
+- [FsAutoComplete GitHub Repository](https://github.com/ionide/FsAutoComplete)
+- [LSP Specification](https://microsoft.github.io/language-server-protocol/)
+- [F# Compiler Service Documentation](https://fsharp.github.io/FSharp.Compiler.Service/)
+
+### F# Development Guidelines
+- [F# Style Guide](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/)
+- [F# Formatting Guidelines](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting)
+- [F# Component Design Guidelines](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/component-design-guidelines)
+
+### Project-Specific Guides
+- [Creating a New Code Fix Guide](./docs/Creating%20a%20new%20code%20fix.md)
+- [Ionide.ProjInfo Documentation](https://github.com/ionide/proj-info)
+- [Fantomas Configuration](https://fsprojects.github.io/fantomas/)
+
+### Related Tools
+- [FSharpLint](https://github.com/fsprojects/FSharpLint/) - Static analysis tool
+- [Paket](https://fsprojects.github.io/Paket/) - Dependency management
+- [FAKE](https://fake.build/) - Build automation (used for scaffolding)
\ No newline at end of file
diff --git a/FsAutoComplete.sln b/FsAutoComplete.sln
index 880144ac3..994c9f56f 100644
--- a/FsAutoComplete.sln
+++ b/FsAutoComplete.sln
@@ -27,6 +27,10 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsAutoComplete.DependencyMa
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "benchmarks", "benchmarks\benchmarks.fsproj", "{0CD029D8-B39E-4CBE-A190-C84A7A811180}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsAutoComplete.BuildServer", "src\FsAutoComplete.BuildServer\FsAutoComplete.BuildServer.fsproj", "{20A60741-D0BB-43AE-8912-21C4D3F3817E}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsAutoComplete.BuildServerProtocol", "src\FsAutoComplete.BuildServerProtocol\FsAutoComplete.BuildServerProtocol.fsproj", "{35E025B2-B255-4E95-A5C6-B8B52361808F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -65,6 +69,14 @@ Global
{0CD029D8-B39E-4CBE-A190-C84A7A811180}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CD029D8-B39E-4CBE-A190-C84A7A811180}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CD029D8-B39E-4CBE-A190-C84A7A811180}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20A60741-D0BB-43AE-8912-21C4D3F3817E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20A60741-D0BB-43AE-8912-21C4D3F3817E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20A60741-D0BB-43AE-8912-21C4D3F3817E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20A60741-D0BB-43AE-8912-21C4D3F3817E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {35E025B2-B255-4E95-A5C6-B8B52361808F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {35E025B2-B255-4E95-A5C6-B8B52361808F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {35E025B2-B255-4E95-A5C6-B8B52361808F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {35E025B2-B255-4E95-A5C6-B8B52361808F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -74,6 +86,8 @@ Global
{38C1F619-3E1E-4784-9833-E8A2AA95CDAE} = {BA56455D-4AEA-45FC-A569-027A68A76BA6}
{14C55B44-2063-4891-98BE-8184CAB1BE87} = {443E0B8D-9AD0-436E-A331-E8CC12965F07}
{C58701B0-D8E3-4B68-A7DE-8524C95F86C0} = {443E0B8D-9AD0-436E-A331-E8CC12965F07}
+ {20A60741-D0BB-43AE-8912-21C4D3F3817E} = {BA56455D-4AEA-45FC-A569-027A68A76BA6}
+ {35E025B2-B255-4E95-A5C6-B8B52361808F} = {BA56455D-4AEA-45FC-A569-027A68A76BA6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1C4EE83B-632A-4929-8C96-38F14254229E}
diff --git a/paket.dependencies b/paket.dependencies
index 3839a9f26..e6aad8c03 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -25,6 +25,7 @@ nuget Microsoft.CodeAnalysis
nuget FSharp.Analyzers.SDK 0.32.1
nuget ICSharpCode.Decompiler
nuget Mono.Cecil >= 0.11.4
+nuget Newtonsoft.Json >= 13.0
nuget FSharpLint.Core
nuget Serilog >= 2.10.0
nuget Serilog.Sinks.File >= 5.0.0
diff --git a/src/FsAutoComplete.BuildServer/FsAutoComplete.BuildServer.fsproj b/src/FsAutoComplete.BuildServer/FsAutoComplete.BuildServer.fsproj
new file mode 100644
index 000000000..22a1662e7
--- /dev/null
+++ b/src/FsAutoComplete.BuildServer/FsAutoComplete.BuildServer.fsproj
@@ -0,0 +1,19 @@
+
+
+ net8.0
+ net8.0;net9.0
+ Exe
+ false
+ fsautocomplete-buildserver
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FsAutoComplete.BuildServer/JsonRpcServer.fs b/src/FsAutoComplete.BuildServer/JsonRpcServer.fs
new file mode 100644
index 000000000..1ae6b5514
--- /dev/null
+++ b/src/FsAutoComplete.BuildServer/JsonRpcServer.fs
@@ -0,0 +1,224 @@
+namespace FsAutoComplete.BuildServer
+
+open System
+open System.IO
+open System.Text
+open System.Threading.Tasks
+open Newtonsoft.Json
+open Newtonsoft.Json.Linq
+open FsAutoComplete.Logging
+open FsAutoComplete.BuildServerProtocol.JsonRpc
+open FsAutoComplete.BuildServerProtocol.BuildServerProtocol
+open WorkspaceOperations
+
+/// JSON RPC server for Build Server Protocol communication
+module JsonRpcServer =
+
+ let private logger = LogProvider.getLoggerByName "JsonRpcServer"
+
+ type RequestHandler = JsonRpcRequest -> Task
+ type NotificationHandler = JsonRpcNotification -> Task
+
+ let private jsonSettings =
+ JsonSerializerSettings(
+ NullValueHandling = NullValueHandling.Ignore,
+ DefaultValueHandling = DefaultValueHandling.Ignore
+ )
+
+ let private serialize obj = JsonConvert.SerializeObject(obj, jsonSettings)
+
+ let private deserialize<'T> (json: string) = JsonConvert.DeserializeObject<'T>(json, jsonSettings)
+
+ let private tryDeserialize<'T> (token: JToken) =
+ try
+ Some(token.ToObject<'T>())
+ with _ ->
+ None
+
+ /// Create a successful response
+ let private createSuccessResponse (id: JToken option) (result: obj) =
+ { Id = id
+ Result = Some(JToken.FromObject(result))
+ Error = None }
+
+ /// Create an error response
+ let private createErrorResponse (id: JToken option) (code: int) (message: string) =
+ { Id = id
+ Result = None
+ Error =
+ Some
+ { Code = code
+ Message = message
+ Data = None } }
+
+ /// Handle BSP requests
+ let private handleBspRequest (request: JsonRpcRequest) : Task =
+ task {
+ try
+ logger.info (
+ Log.setMessage "Handling BSP request: {method}"
+ >> Log.addContext "method" request.Method
+ )
+
+ match request.Method with
+ | "build/initialize" ->
+ let! result = initializeWorkspace ()
+
+ match result with
+ | Result.Ok() ->
+ let capabilities =
+ { CompileProvider = Some true
+ TestProvider = None
+ RunProvider = None
+ DebugProvider = None
+ InverseSourcesProvider = None
+ DependencySourcesProvider = None
+ DependencyModulesProvider = None
+ ResourcesProvider = None
+ OutputPathsProvider = None
+ BuildTargetChangedProvider = None
+ JvmRunEnvironmentProvider = None
+ JvmTestEnvironmentProvider = None
+ CanReload = Some true }
+
+ return createSuccessResponse (Some request.Id) capabilities
+ | Result.Error msg -> return createErrorResponse (Some request.Id) ErrorCodes.InternalError msg
+
+ | "build/shutdown" ->
+ let! result = shutdown ()
+
+ match result with
+ | Result.Ok() -> return createSuccessResponse (Some request.Id) ()
+ | Result.Error msg -> return createErrorResponse (Some request.Id) ErrorCodes.InternalError msg
+
+ | "workspace/buildTargets" ->
+ // Return empty build targets for now
+ let result = { Targets = [||] }
+ return createSuccessResponse (Some request.Id) result
+
+ | "fsharp/workspacePeek" ->
+ match request.Params with
+ | Some parameters ->
+ match tryDeserialize parameters with
+ | Some peekRequest ->
+ let! result = peekWorkspace peekRequest
+
+ match result with
+ | Result.Ok response -> return createSuccessResponse (Some request.Id) response
+ | Result.Error msg -> return createErrorResponse (Some request.Id) ErrorCodes.InternalError msg
+ | None ->
+ return createErrorResponse (Some request.Id) ErrorCodes.InvalidParams "Invalid workspace peek parameters"
+ | None ->
+ return createErrorResponse (Some request.Id) ErrorCodes.InvalidParams "Missing workspace peek parameters"
+
+ | "fsharp/workspaceLoad" ->
+ match request.Params with
+ | Some parameters ->
+ match tryDeserialize parameters with
+ | Some loadRequest ->
+ let! result = loadWorkspace loadRequest
+
+ match result with
+ | Result.Ok response -> return createSuccessResponse (Some request.Id) response
+ | Result.Error msg -> return createErrorResponse (Some request.Id) ErrorCodes.InternalError msg
+ | None ->
+ return createErrorResponse (Some request.Id) ErrorCodes.InvalidParams "Invalid workspace load parameters"
+ | None ->
+ return createErrorResponse (Some request.Id) ErrorCodes.InvalidParams "Missing workspace load parameters"
+
+ | _ ->
+ logger.warn (
+ Log.setMessage "Unknown method: {method}"
+ >> Log.addContext "method" request.Method
+ )
+
+ return createErrorResponse (Some request.Id) ErrorCodes.MethodNotFound $"Method not found: {request.Method}"
+ with ex ->
+ logger.error (
+ Log.setMessage "Error handling request: {error}"
+ >> Log.addContext "error" ex.Message
+ )
+
+ return createErrorResponse (Some request.Id) ErrorCodes.InternalError ex.Message
+ }
+
+ /// Handle JSON RPC notifications
+ let private handleNotification (notification: JsonRpcNotification) : Task =
+ task {
+ try
+ logger.info (
+ Log.setMessage "Handling notification: {method}"
+ >> Log.addContext "method" notification.Method
+ )
+
+ match notification.Method with
+ | "build/exit" ->
+ logger.info (Log.setMessage "Received exit notification")
+ Environment.Exit(0)
+ | _ ->
+ logger.warn (
+ Log.setMessage "Unknown notification method: {method}"
+ >> Log.addContext "method" notification.Method
+ )
+ with ex ->
+ logger.error (
+ Log.setMessage "Error handling notification: {error}"
+ >> Log.addContext "error" ex.Message
+ )
+ }
+
+ /// Process a single JSON RPC message
+ let processMessage (messageText: string) : Task =
+ task {
+ try
+ let message = JObject.Parse(messageText)
+
+ if message.ContainsKey("id") then
+ // This is a request
+ let request = message.ToObject()
+ let! response = handleBspRequest request
+ return Some(serialize response)
+ else
+ // This is a notification
+ let notification = message.ToObject()
+ do! handleNotification notification
+ return None
+ with ex ->
+ logger.error (
+ Log.setMessage "Error processing message: {error}"
+ >> Log.addContext "error" ex.Message
+ )
+
+ let errorResponse = createErrorResponse None ErrorCodes.ParseError "Parse error"
+ return Some(serialize errorResponse)
+ }
+
+ /// Main server loop for stdin/stdout communication
+ let runServer () =
+ task {
+ logger.info (Log.setMessage "Starting JSON RPC server...")
+
+ use reader = new StreamReader(Console.OpenStandardInput())
+ use writer = new StreamWriter(Console.OpenStandardOutput())
+ writer.AutoFlush <- true
+
+ let mutable keepRunning = true
+
+ while keepRunning do
+ try
+ let! line = reader.ReadLineAsync()
+
+ if not (isNull line) then
+ let! response = processMessage line
+
+ match response with
+ | Some responseText -> do! writer.WriteLineAsync(responseText)
+ | None -> () // No response needed for notifications
+ else
+ keepRunning <- false
+ with ex ->
+ logger.error (Log.setMessage "Server loop error: {error}" >> Log.addContext "error" ex.Message)
+ keepRunning <- false
+
+ logger.info (Log.setMessage "JSON RPC server stopped")
+ }
diff --git a/src/FsAutoComplete.BuildServer/Program.fs b/src/FsAutoComplete.BuildServer/Program.fs
new file mode 100644
index 000000000..0064166d3
--- /dev/null
+++ b/src/FsAutoComplete.BuildServer/Program.fs
@@ -0,0 +1,20 @@
+module FsAutoComplete.BuildServer.Program
+
+open System
+open System.IO
+open FsAutoComplete.Logging
+open JsonRpcServer
+
+[]
+let main _args =
+ // Set up basic logging
+ printfn "FsAutoComplete Build Server starting"
+
+ try
+ // Run the JSON RPC server
+ let serverTask = runServer ()
+ serverTask.Wait()
+ 0
+ with ex ->
+ printfn "Build server error: %s" ex.Message
+ 1
diff --git a/src/FsAutoComplete.BuildServer/WorkspaceOperations.fs b/src/FsAutoComplete.BuildServer/WorkspaceOperations.fs
new file mode 100644
index 000000000..3b7daf5a3
--- /dev/null
+++ b/src/FsAutoComplete.BuildServer/WorkspaceOperations.fs
@@ -0,0 +1,46 @@
+namespace FsAutoComplete.BuildServer
+
+open System
+open System.IO
+open System.Threading.Tasks
+open FsAutoComplete.Logging
+open FsAutoComplete.BuildServerProtocol.JsonRpc
+open FsAutoComplete.BuildServerProtocol.BuildServerProtocol
+
+/// Simple workspace operations for Build Server Protocol
+module WorkspaceOperations =
+
+ let private logger = LogProvider.getLoggerByName "WorkspaceOperations"
+
+ /// Initialize workspace - for now just log and return success
+ let initializeWorkspace () =
+ logger.info (Log.setMessage "Initializing workspace...")
+ Task.FromResult(Result.Ok())
+
+ /// Peek workspace - simplified for now
+ let peekWorkspace (request: WorkspacePeekRequest) =
+ logger.info (
+ Log.setMessage "Peeking workspace at {directory}"
+ >> Log.addContext "directory" request.Directory
+ )
+
+ let response = { Found = [||] }
+ Task.FromResult(Result.Ok response)
+
+ /// Load workspace - simplified for now
+ let loadWorkspace (request: WorkspaceLoadRequest) =
+ logger.info (
+ Log.setMessage "Loading workspace with {documentCount} documents"
+ >> Log.addContext "documentCount" request.TextDocuments.Length
+ )
+
+ let response =
+ { WorkspaceRoot = Environment.CurrentDirectory
+ Projects = [||] }
+
+ Task.FromResult(Result.Ok response)
+
+ /// Shutdown workspace
+ let shutdown () =
+ logger.info (Log.setMessage "Shutting down workspace...")
+ Task.FromResult(Result.Ok())
diff --git a/src/FsAutoComplete.BuildServer/paket.references b/src/FsAutoComplete.BuildServer/paket.references
new file mode 100644
index 000000000..0dc4ba3bb
--- /dev/null
+++ b/src/FsAutoComplete.BuildServer/paket.references
@@ -0,0 +1,2 @@
+FSharp.Core
+Newtonsoft.Json
\ No newline at end of file
diff --git a/src/FsAutoComplete.BuildServerProtocol/BuildServerProtocol.fs b/src/FsAutoComplete.BuildServerProtocol/BuildServerProtocol.fs
new file mode 100644
index 000000000..c478dd4f9
--- /dev/null
+++ b/src/FsAutoComplete.BuildServerProtocol/BuildServerProtocol.fs
@@ -0,0 +1,165 @@
+namespace FsAutoComplete.BuildServerProtocol
+
+open System
+open Newtonsoft.Json.Linq
+
+/// Build Server Protocol types based on https://build-server-protocol.github.io/docs/specification.html
+module BuildServerProtocol =
+
+ /// Base types for BSP
+ type BuildClientCapabilities = { LanguageIds: string[] }
+
+ type BuildServerCapabilities =
+ { CompileProvider: bool option
+ TestProvider: bool option
+ RunProvider: bool option
+ DebugProvider: bool option
+ InverseSourcesProvider: bool option
+ DependencySourcesProvider: bool option
+ DependencyModulesProvider: bool option
+ ResourcesProvider: bool option
+ OutputPathsProvider: bool option
+ BuildTargetChangedProvider: bool option
+ JvmRunEnvironmentProvider: bool option
+ JvmTestEnvironmentProvider: bool option
+ CanReload: bool option }
+
+ /// Workspace/project discovery and loading
+
+ type WorkspacePeekRequest =
+ { Directory: string
+ Deep: int
+ ExcludedDirs: string[] }
+
+ type WorkspacePeekResponse = { Found: WorkspaceProjectState[] }
+
+ and WorkspaceProjectState =
+ { Project: ProjectDescription
+ Crosswalk: Crosswalk[] option
+ Sdk: ProjectSdkInfo option }
+
+ and ProjectDescription =
+ { Project: string
+ Name: string
+ Virtual: bool option
+ Dependencies: string[] option }
+
+ and Crosswalk =
+ { MSBuildProject: string
+ ProjectFile: string }
+
+ and ProjectSdkInfo = { Type: string; Path: string option }
+
+ type WorkspaceLoadRequest = { TextDocuments: string[] }
+
+ type WorkspaceLoadResponse =
+ { WorkspaceRoot: string
+ Projects: ProjectDetails[] }
+
+ and ProjectDetails =
+ { Project: string
+ Name: string
+ SourceFiles: string[]
+ ProjectReferences: string[]
+ PackageReferences: PackageReference[]
+ FrameworkVersion: string
+ TargetFramework: string
+ OutputType: string
+ OutputFile: string
+ IsTestProject: bool option
+ Properties: Map option }
+
+ and PackageReference =
+ { Name: string
+ Version: string
+ FullPath: string option }
+
+ /// Build target related types
+
+ type BuildTargetIdentifier = { Uri: string }
+
+ type BuildTarget =
+ { Id: BuildTargetIdentifier
+ DisplayName: string option
+ BaseDirectory: string option
+ Tags: string[]
+ Capabilities: BuildTargetCapabilities
+ LanguageIds: string[]
+ Dependencies: BuildTargetIdentifier[]
+ DataKind: string option
+ Data: JObject option }
+
+ and BuildTargetCapabilities =
+ { CanCompile: bool
+ CanTest: bool
+ CanRun: bool
+ CanDebug: bool }
+
+ type WorkspaceBuildTargetsResult = { Targets: BuildTarget[] }
+
+ /// Build/compile related types
+
+ type CompileParams =
+ { Targets: BuildTargetIdentifier[]
+ OriginId: string option
+ Arguments: string[] option }
+
+ type CompileResult =
+ { OriginId: string option
+ StatusCode: int
+ DataKind: string option
+ Data: JObject option }
+
+ /// Diagnostics and notifications
+
+ type Diagnostic =
+ { Range: Range
+ Severity: DiagnosticSeverity option
+ Code: string option
+ CodeDescription: CodeDescription option
+ Source: string option
+ Message: string
+ Tags: DiagnosticTag[] option
+ RelatedInformation: DiagnosticRelatedInformation[] option
+ Data: JObject option }
+
+ and Range = { Start: Position; End: Position }
+
+ and Position = { Line: int; Character: int }
+
+ and DiagnosticSeverity =
+ | Error = 1
+ | Warning = 2
+ | Information = 3
+ | Hint = 4
+
+ and CodeDescription = { Href: string }
+
+ and DiagnosticTag =
+ | Unnecessary = 1
+ | Deprecated = 2
+
+ and DiagnosticRelatedInformation = { Location: Location; Message: string }
+
+ and Location = { Uri: string; Range: Range }
+
+ type PublishDiagnosticsParams =
+ { TextDocument: TextDocumentIdentifier
+ BuildTarget: BuildTargetIdentifier
+ OriginId: string option
+ Diagnostics: Diagnostic[]
+ Reset: bool }
+
+ and TextDocumentIdentifier = { Uri: string }
+
+ /// Custom FSAC extensions for F# specific functionality
+
+ type FSharpWorkspacePeekRequest = WorkspacePeekRequest
+
+ type FSharpWorkspaceLoadRequest =
+ { TextDocuments: string[]
+ ExcludeProjectDirectories: string[] option }
+
+ type FSharpProjectRequest = { Project: string }
+
+ type FSharpProjectResponse = { Project: ProjectDetails }
diff --git a/src/FsAutoComplete.BuildServerProtocol/FsAutoComplete.BuildServerProtocol.fsproj b/src/FsAutoComplete.BuildServerProtocol/FsAutoComplete.BuildServerProtocol.fsproj
new file mode 100644
index 000000000..27ff4e4f2
--- /dev/null
+++ b/src/FsAutoComplete.BuildServerProtocol/FsAutoComplete.BuildServerProtocol.fsproj
@@ -0,0 +1,14 @@
+
+
+
+ netstandard2.0;net8.0
+ true
+ true
+ FS0025
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/FsAutoComplete.BuildServerProtocol/JsonRpc.fs b/src/FsAutoComplete.BuildServerProtocol/JsonRpc.fs
new file mode 100644
index 000000000..8355a7fc4
--- /dev/null
+++ b/src/FsAutoComplete.BuildServerProtocol/JsonRpc.fs
@@ -0,0 +1,41 @@
+namespace FsAutoComplete.BuildServerProtocol
+
+open Newtonsoft.Json.Linq
+
+/// Core JSON RPC 2.0 protocol types for communication
+module JsonRpc =
+
+ /// JSON RPC request message
+ type JsonRpcRequest =
+ { Id: JToken
+ Method: string
+ Params: JToken option }
+
+ /// JSON RPC response message
+ type JsonRpcResponse =
+ { Id: JToken option
+ Result: JToken option
+ Error: JsonRpcError option }
+
+ /// JSON RPC error object
+ and JsonRpcError =
+ { Code: int
+ Message: string
+ Data: JToken option }
+
+ /// JSON RPC notification message
+ type JsonRpcNotification =
+ { Method: string
+ Params: JToken option }
+
+ /// Standard JSON RPC error codes
+ module ErrorCodes =
+ let ParseError = -32700
+ let InvalidRequest = -32600
+ let MethodNotFound = -32601
+ let InvalidParams = -32602
+ let InternalError = -32603
+
+ // Server error range
+ let ServerErrorStart = -32099
+ let ServerErrorEnd = -32000
diff --git a/src/FsAutoComplete.BuildServerProtocol/paket.references b/src/FsAutoComplete.BuildServerProtocol/paket.references
new file mode 100644
index 000000000..0dc4ba3bb
--- /dev/null
+++ b/src/FsAutoComplete.BuildServerProtocol/paket.references
@@ -0,0 +1,2 @@
+FSharp.Core
+Newtonsoft.Json
\ No newline at end of file
diff --git a/src/FsAutoComplete/BuildServerWorkspaceLoader.fs b/src/FsAutoComplete/BuildServerWorkspaceLoader.fs
new file mode 100644
index 000000000..9cf9c67da
--- /dev/null
+++ b/src/FsAutoComplete/BuildServerWorkspaceLoader.fs
@@ -0,0 +1,110 @@
+namespace FsAutoComplete.Lsp
+
+open System
+open System.IO
+open System.Diagnostics
+open System.Threading.Tasks
+open System.Text
+open FsAutoComplete.Logging
+open Ionide.ProjInfo.ProjectSystem
+open Ionide.ProjInfo.Types
+open Ionide.ProjInfo
+
+/// Build Server Protocol client for communicating with the separate build server process
+module BuildServerClient =
+
+ let logger = LogProvider.getLoggerByName "BuildServerClient"
+
+ type BuildServerProcess =
+ { Process: Process
+ Writer: StreamWriter
+ Reader: StreamReader }
+
+ let mutable private currentBuildServer: BuildServerProcess option = None
+
+ /// Start the build server process
+ let startBuildServer (buildServerPath: string) : Task> =
+ task {
+ try
+ logger.info (Log.setMessage "Starting build server")
+
+ let startInfo = ProcessStartInfo()
+ startInfo.FileName <- "dotnet"
+ startInfo.Arguments <- buildServerPath
+ startInfo.UseShellExecute <- false
+ startInfo.RedirectStandardInput <- true
+ startInfo.RedirectStandardOutput <- true
+ startInfo.RedirectStandardError <- true
+ startInfo.CreateNoWindow <- true
+
+ let proc = Process.Start(startInfo)
+
+ if isNull proc then
+ return Error "Failed to start build server process"
+ else
+ let writer = new StreamWriter(proc.StandardInput.BaseStream, Encoding.UTF8)
+ let reader = new StreamReader(proc.StandardOutput.BaseStream, Encoding.UTF8)
+
+ let buildServer =
+ { Process = proc
+ Writer = writer
+ Reader = reader }
+
+ currentBuildServer <- Some buildServer
+ logger.info (Log.setMessage "Build server started successfully")
+ return Ok buildServer
+ with ex ->
+ logger.error (Log.setMessage "Failed to start build server" >> Log.addExn ex)
+ return Error ex.Message
+ }
+
+ /// Send a message to the build server
+ let sendMessage (buildServer: BuildServerProcess) (message: string) : Task> =
+ task {
+ try
+ logger.debug (Log.setMessage "Sending message to build server")
+
+ do! buildServer.Writer.WriteLineAsync(message)
+ do! buildServer.Writer.FlushAsync()
+
+ let! response = buildServer.Reader.ReadLineAsync()
+
+ if isNull response then
+ return Error "Build server returned null response"
+ else
+ logger.debug (Log.setMessage "Received response from build server")
+ return Ok response
+ with ex ->
+ logger.error (Log.setMessage "Failed to communicate with build server" >> Log.addExn ex)
+ return Error ex.Message
+ }
+
+ /// Stop the build server process
+ let stopBuildServer (buildServer: BuildServerProcess) : Task =
+ task {
+ try
+ logger.info (Log.setMessage "Stopping build server")
+
+ buildServer.Writer.Dispose()
+ buildServer.Reader.Dispose()
+
+ if not buildServer.Process.HasExited then
+ buildServer.Process.Kill()
+ let! _exited = buildServer.Process.WaitForExitAsync()
+ ()
+
+ buildServer.Process.Dispose()
+
+ logger.info (Log.setMessage "Build server stopped")
+ with ex ->
+ logger.error (Log.setMessage "Error stopping build server" >> Log.addExn ex)
+ }
+
+/// Factory function to create a Build Server workspace loader
+module BuildServerWorkspaceLoaderFactory =
+
+ let create (toolsPath) : IWorkspaceLoader =
+ let logger = LogProvider.getLoggerByName "BuildServerWorkspaceLoaderFactory"
+ logger.info (Log.setMessage "Creating BuildServerWorkspaceLoader - falling back to regular loader for now")
+ // For now, fall back to the regular workspace loader until we implement the full BSP
+ Ionide.ProjInfo.WorkspaceLoader.Create(toolsPath, [])
diff --git a/src/FsAutoComplete/FsAutoComplete.fsproj b/src/FsAutoComplete/FsAutoComplete.fsproj
index d50fd1309..34074b361 100644
--- a/src/FsAutoComplete/FsAutoComplete.fsproj
+++ b/src/FsAutoComplete/FsAutoComplete.fsproj
@@ -22,6 +22,7 @@
+
@@ -49,6 +50,7 @@
+
fsautocomplete
diff --git a/src/FsAutoComplete/Parser.fs b/src/FsAutoComplete/Parser.fs
index 3beed7817..9ba961233 100644
--- a/src/FsAutoComplete/Parser.fs
+++ b/src/FsAutoComplete/Parser.fs
@@ -15,6 +15,7 @@ open OpenTelemetry
open OpenTelemetry.Resources
open OpenTelemetry.Trace
open OpenTelemetry.Metrics
+open FsAutoComplete.Lsp.BuildServerWorkspaceLoaderFactory
module Parser =
open FsAutoComplete.Core
@@ -102,6 +103,12 @@ module Parser =
"Use Transparent Compiler in FSharp.Compiler.Services. Should have better performance characteristics, but is experimental. See https://github.com/dotnet/fsharp/pull/15179 for more details."
)
+ let useBuildServerOption =
+ Option(
+ "--use-build-server",
+ "Enable the separate build server process for MSBuild evaluation (experimental)."
+ )
+
let stateLocationOption =
Option(
"--state-directory",
@@ -124,57 +131,62 @@ module Parser =
rootCommand.AddOption stateLocationOption
rootCommand.AddOption otelTracingOption
rootCommand.AddOption useTransparentCompilerOption
+ rootCommand.AddOption useBuildServerOption
// for back-compat - we removed some options and this broke some clients.
rootCommand.TreatUnmatchedTokensAsErrors <- false
rootCommand.SetHandler(
- Func<_, _, _, _, Task>(fun projectGraphEnabled stateDirectory adaptiveLspEnabled useTransparentCompiler ->
- let workspaceLoaderFactory =
- fun toolsPath ->
- if projectGraphEnabled then
- Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create(toolsPath, ProjectLoader.globalProperties)
+ Func<_, _, _, _, _, Task>
+ (fun projectGraphEnabled stateDirectory adaptiveLspEnabled useTransparentCompiler useBuildServer ->
+ let workspaceLoaderFactory =
+ fun toolsPath ->
+ if useBuildServer then
+ BuildServerWorkspaceLoaderFactory.create toolsPath
+ elif projectGraphEnabled then
+ Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create(toolsPath, ProjectLoader.globalProperties)
+ else
+ Ionide.ProjInfo.WorkspaceLoader.Create(toolsPath, ProjectLoader.globalProperties)
+
+ let sourceTextFactory: ISourceTextFactory = new RoslynSourceTextFactory()
+
+ let dotnetPath =
+ if
+ Environment.ProcessPath.EndsWith("dotnet", StringComparison.Ordinal)
+ || Environment.ProcessPath.EndsWith("dotnet.exe", StringComparison.Ordinal)
+ then
+ // this is valid when not running as a global tool
+ Some(FileInfo(Environment.ProcessPath))
else
- Ionide.ProjInfo.WorkspaceLoader.Create(toolsPath, ProjectLoader.globalProperties)
-
- let sourceTextFactory: ISourceTextFactory = new RoslynSourceTextFactory()
-
- let dotnetPath =
- if
- Environment.ProcessPath.EndsWith("dotnet", StringComparison.Ordinal)
- || Environment.ProcessPath.EndsWith("dotnet.exe", StringComparison.Ordinal)
- then
- // this is valid when not running as a global tool
- Some(FileInfo(Environment.ProcessPath))
- else
- None
-
- let toolsPath =
- Ionide.ProjInfo.Init.init (IO.DirectoryInfo Environment.CurrentDirectory) dotnetPath
-
- let lspFactory =
- if adaptiveLspEnabled then
- fun () ->
- AdaptiveFSharpLspServer.startCore
- toolsPath
- workspaceLoaderFactory
- sourceTextFactory
- useTransparentCompiler
- else
- fun () ->
- AdaptiveFSharpLspServer.startCore
- toolsPath
- workspaceLoaderFactory
- sourceTextFactory
- useTransparentCompiler
-
- let result = AdaptiveFSharpLspServer.start lspFactory
-
- Task.FromResult result),
+ None
+
+ let toolsPath =
+ Ionide.ProjInfo.Init.init (IO.DirectoryInfo Environment.CurrentDirectory) dotnetPath
+
+ let lspFactory =
+ if adaptiveLspEnabled then
+ fun () ->
+ AdaptiveFSharpLspServer.startCore
+ toolsPath
+ workspaceLoaderFactory
+ sourceTextFactory
+ useTransparentCompiler
+ else
+ fun () ->
+ AdaptiveFSharpLspServer.startCore
+ toolsPath
+ workspaceLoaderFactory
+ sourceTextFactory
+ useTransparentCompiler
+
+ let result = AdaptiveFSharpLspServer.start lspFactory
+
+ Task.FromResult result),
projectGraphOption,
stateLocationOption,
adaptiveLspServerOption,
- useTransparentCompilerOption
+ useTransparentCompilerOption,
+ useBuildServerOption
)
rootCommand