Skip to content

SCKelemen/lsp

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

86 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

LSP - Language Server Protocol Library for Go

A Go library for implementing Language Server Protocol (LSP) servers with protocol-agnostic core types optimized for CLI tools and server implementations.

Key Features

  • 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.

Quick Start

1. CLI Tool (No LSP Server)

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
}

2. Reusable Provider

// 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 boundaries

3. LSP Server Handler

import (
    "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
}

πŸ“š Comprehensive Feature Guides

Complete guides for implementing LSP features from scratch to production:

Core Features

➑️ 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 & Information

➑️ 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

Editor Features

➑️ 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

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      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   β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why Core Types?

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)

Core Packages

core/

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

protocol/

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

adapter/

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

examples/

Complete working examples for CLI tools and LSP servers

Provider Interfaces

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

Traditional LSP Server (Original Functionality)

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
}

Documentation

Feature Implementation Guides

Architecture & Reference

Testing

# 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/...

License

BearWare 1.0 - See LICENSE file for details.

Attribution

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

References

About

Language Server Protocol SDK for Go

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Go 100.0%