A Go library for implementing Language Server Protocol (LSP) servers with protocol-agnostic core types optimized for CLI tools and server implementations.
- Protocol-Agnostic Core Types: Work with UTF-8 byte offsets naturally in Go
- CLI-First Design: Use LSP data structures in CLI tools without protocol overhead
- Reusable Providers: Write business logic once, use in both CLI and LSP server
- Automatic UTF-16 Conversion: Adapters handle protocol conversion at boundaries per LSP spec requirements
- Full LSP Support: Implements LSP 3.16, 3.17, and 3.18
New to LSP? Check out the LSP Primer - a guide mapping VS Code features (like squiggles, auto-complete, and go-to-definition) to LSP capabilities.
import "github.com/SCKelemen/lsp/core"
func lintFile(content string) []core.Diagnostic {
var diagnostics []core.Diagnostic
severity := core.SeverityWarning
// Work naturally with UTF-8 byte offsets
for i := 0; i < len(content); i++ {
if content[i] == '\t' {
pos := core.ByteOffsetToPosition(content, i)
diagnostics = append(diagnostics, core.Diagnostic{
Range: core.Range{
Start: pos,
End: core.Position{Line: pos.Line, Character: pos.Character + 1},
},
Severity: &severity,
Message: "Use spaces instead of tabs",
})
}
}
return diagnostics
}// Write once, use in CLI and LSP server
type TabFixProvider struct{}
func (p *TabFixProvider) ProvideCodeFixes(ctx core.CodeFixContext) []core.CodeAction {
// Your fix logic using UTF-8 offsets
return actions
}
// Use in CLI tool
registry := core.NewCodeFixRegistry()
registry.Register(&TabFixProvider{})
fixes := registry.ProvideCodeFixes(ctx)
// Same provider works in LSP server - just convert at boundariesimport (
"github.com/SCKelemen/lsp"
"github.com/SCKelemen/lsp/adapter"
"github.com/SCKelemen/lsp/core"
protocol "github.com/SCKelemen/lsp/protocol"
)
func (s *Server) TextDocumentDidOpen(
context *lsp.Context,
params *protocol.DidOpenTextDocumentParams,
) error {
uri := string(params.TextDocument.URI)
content := params.TextDocument.Text
// Business logic with core types (UTF-8)
diagnostics := s.validate(uri, content)
// Convert to protocol at boundary
protocolDiags := adapter.CoreToProtocolDiagnostics(diagnostics, content)
// Send to client
context.Notify(...)
return nil
}Complete guides for implementing LSP features from scratch to production:
β‘οΈ VALIDATORS.md - Diagnostic Providers
- Writing validators that detect code issues
- Testing with Unicode content
- Composing multiple validators
- Integration with LSP servers
β‘οΈ CODE_ACTIONS.md - Code Action Providers
- Quick fixes for diagnostics
- Refactoring actions (extract, inline, rewrite)
- Source actions (organize imports, fix all)
- Workspace edits and multiple file changes
β‘οΈ NAVIGATION.md - Definition & Hover Providers
- Go-to-definition across files
- Hover information with markdown
- Finding references and implementations
- Type definitions and declarations
β‘οΈ SYMBOLS.md - Document Symbol Providers
- Hierarchical document structure
- Outline and breadcrumb navigation
- Symbol kinds (functions, classes, variables)
- Tree-based symbol representation
β‘οΈ FOLDING.md - Folding Range Providers
- Collapsible code regions
- Multiple folding strategies (braces, indentation, regions)
- Comments, imports, and function folding
- Language-agnostic folding patterns
β‘οΈ FORMATTING.md - Formatting Providers
- Document-wide formatting
- Range formatting for selections
- Formatting options (tabs, spaces, whitespace)
- Integration with language formatters
Each guide includes:
- β Step-by-step implementation
- β Complete working examples
- β Comprehensive test patterns
- β LSP server integration
- β UTF-8/UTF-16 conversion details
βββββββββββββββββββββββββββββββββββββββ
β Your Business Logic β
β (core/ package) β
β UTF-8 offsets, Go-friendly types β
ββββββββββββββ¬βββββββββββββββββββββββββ
β
ββ CLI Tools: Direct use
β
ββ LSP Server: Convert at boundaries
β
βββββββββββββββββββββββββ
β Adapter Functions β
β (adapter/ package) β
β UTF-8 β UTF-16 β
βββββββββββββββββββββββββ
β
βββββββββββββββββββββββββ
β Protocol Types β
β (protocol/ package) β
β JSON-RPC/LSP Spec β
βββββββββββββββββββββββββ
LSP uses UTF-16 (per specification), but Go strings are UTF-8. Core types use UTF-8 for natural Go string handling:
content := "hello δΈη" // δΈη is 3 bytes in UTF-8, 1 code unit in UTF-16
// Core types: direct indexing
pos := core.ByteOffsetToPosition(content, 8) // Natural UTF-8 offset
// Protocol types: need conversion
protocolPos := adapter.CoreToProtocolPosition(pos, content)Protocol-agnostic types using UTF-8:
- types.go: Position, Range, Location, Diagnostic
- language_features.go: FoldingRange, TextEdit, DocumentSymbol, CodeAction, WorkspaceEdit
- codefix.go: Provider interfaces (CodeFixProvider, DiagnosticProvider, etc.)
- document.go: DocumentManager for managing documents in memory
- encoding.go: UTF-8 β UTF-16 conversion utilities
LSP protocol types with UTF-16 offsets (JSON-RPC):
- Full LSP 3.16, 3.17, and 3.18 protocol type definitions
- Message types, request/response structures
- Server and client capabilities
Conversion functions between core (UTF-8) and protocol (UTF-16) types:
- Position and range conversions
- Diagnostic, completion, and workspace edit conversions
- Support for all LSP 3.16, 3.17, and 3.18 features
Complete working examples for CLI tools and LSP servers
Write providers that work with core types:
type DiagnosticProvider interface {
ProvideDiagnostics(uri, content string) []Diagnostic
}
type CodeFixProvider interface {
ProvideCodeFixes(ctx CodeFixContext) []CodeAction
}
type FoldingRangeProvider interface {
ProvideFoldingRanges(uri, content string) []FoldingRange
}
// ... and more (hover, definition, formatting, etc.)β See comprehensive feature guides above for complete implementation details
This library still supports traditional LSP server usage:
package main
import (
"github.com/SCKelemen/lsp"
protocol "github.com/SCKelemen/lsp/protocol"
"github.com/SCKelemen/lsp/server"
"github.com/tliron/commonlog"
_ "github.com/tliron/commonlog/simple"
)
const lsName = "my language"
var (
version string = "0.0.1"
handler protocol.Handler
)
func main() {
commonlog.Configure(1, nil)
handler = protocol.Handler{
Initialize: initialize,
Initialized: initialized,
Shutdown: shutdown,
SetTrace: setTrace,
}
server := server.NewServer(&handler, lsName, false)
server.RunStdio()
}
func initialize(context *lsp.Context, params *protocol.InitializeParams) (any, error) {
capabilities := handler.CreateServerCapabilities()
return protocol.InitializeResult{
Capabilities: capabilities,
ServerInfo: &protocol.InitializeResultServerInfo{
Name: lsName,
Version: &version,
},
}, nil
}
func initialized(context *lsp.Context, params *protocol.InitializedParams) error {
return nil
}
func shutdown(context *lsp.Context) error {
protocol.SetTraceValue(protocol.TraceValueOff)
return nil
}
func setTrace(context *lsp.Context, params *protocol.SetTraceParams) error {
protocol.SetTraceValue(params.Value)
return nil
}- VALIDATORS.md - Diagnostic providers and validators
- CODE_ACTIONS.md - Quick fixes, refactorings, and source actions
- NAVIGATION.md - Go-to-definition and hover information
- SYMBOLS.md - Document symbols and outline
- FOLDING.md - Folding ranges for code regions
- FORMATTING.md - Document and range formatting
- CORE_TYPES.md - Core types architecture and UTF-8/UTF-16 conversion
- LSP_CAPABILITIES.md - All LSP capabilities with support status
- examples/ - Complete working examples with tests
- core/ - Core type definitions and utilities
# Run all tests
go test ./core/... ./adapter/... ./examples/...
# Test specific package
go test ./core/... -v
# Run adapter tests
go test ./adapter/... -v
# Build examples
go build ./examples/...BearWare 1.0 - See LICENSE file for details.
Forked from tliron/glsp by Tal Liron.
Major architectural improvements in this fork:
- Added core types with UTF-8 byte offsets
- Created adapter packages for protocol conversion
- Added provider interfaces for reusable business logic
- Comprehensive documentation and examples for CLI and server usage
- Document manager for stateful document tracking
- Full support for LSP 3.16, 3.17, and 3.18