Skip to content

go-language-server/protocol

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

655 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

protocol

test pkg.go.dev Go module codecov.io

A fast, spec-faithful implementation of the Language Server Protocol 3.18 for Go — types generated from the official LSP meta-model, a union-aware JSON codec that skips reflection on the hot path, and a ready-to-wire client/server RPC layer.

import "go.lsp.dev/protocol"

Highlights

  • Generated from metaModel.json. Every request, notification, and structure is generated from the official LSP meta-model, so the type surface tracks the specification rather than a hand-maintained subset. The generator lives in internal/genlsp.
  • Sealed-interface unions. Protocol "or" types are modeled as sealed Go interfaces — each arm is a distinct concrete type implementing a private marker method. Callers discriminate arms with an ordinary type switch, and the compiler keeps the arm set closed.
  • Reflection-free codec. Marshal and Unmarshal dispatch through generated byte-level encoders and decoders; struct values, named slice wrappers, and union values all (de)serialize without entering reflect. AppendMarshal lets callers amortize the output allocation across messages.
  • Batteries-included RPC. NewServer and NewClient wire the union-aware codec onto a go.lsp.dev/jsonrpc2 connection and return typed Server / Client dispatchers.

Install

go get go.lsp.dev/protocol@latest

Requires Go 1.26+.

Quick start

Implement the methods you care about by embedding UnimplementedServer, then let NewServer serve it over any jsonrpc2.Stream (here, an in-memory pipe):

package main

import (
	"context"

	"go.lsp.dev/jsonrpc2"
	"go.lsp.dev/protocol"
)

// langServer overrides only the methods it implements; UnimplementedServer
// supplies a "method not found" default for the rest of the LSP surface.
type langServer struct {
	protocol.UnimplementedServer
}

func (langServer) Hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
	return &protocol.Hover{
		Contents: protocol.String("hello from the server"),
	}, nil
}

func serve(ctx context.Context, stream jsonrpc2.Stream) (jsonrpc2.Conn, protocol.Client) {
	// NewServer serves langServer and returns the connection plus a typed
	// Client dispatcher for server -> client requests.
	ctx, conn, client := protocol.NewServer(ctx, langServer{}, stream)
	_ = ctx
	return conn, client
}

The mirror-image helper NewClient serves a Client and hands back a typed Server dispatcher, so the same model drives both ends of the connection.

Working with union types

Where the LSP says a value is A | B, this package exposes a sealed interface. Construct a value with the concrete arm and read it back with a type switch:

// Hover.Contents (HoverContents) is one of:
//   String | *MarkupContent | *MarkedStringWithLanguage | MarkedStringSlice
hover := &protocol.Hover{Contents: protocol.String("plain text")}

switch c := hover.Contents.(type) {
case protocol.String:
	// a bare string arm
case *protocol.MarkupContent:
	// structured markup
default:
	_ = c
}

Because the arm set is closed by an unexported marker method, no value outside the package can satisfy the interface — the type switch is exhaustive by construction.

Decode ownership and zero-copy strings

Generated byte-walkers make one GC-managed copy of the input before decoding it. To avoid per-field allocations on hot paths, unescaped decoded strings and raw JSON value fields may alias that single owned copy. Two consequences:

  • After Unmarshal (or the LSP codec) returns, you may freely reuse or mutate the original []byte — the decoded value no longer references it.
  • Retaining a small decoded string or raw value can keep the entire owned message copy alive. When a decoded value must outlive a much larger input message, detach it first with Clone.

URI types

Generated URI fields use go.lsp.dev/uri.URI directly. Construct new values with uri.Parse, uri.File, or uri.From.

protocol.URI remains as a package-local named type for compatibility and for the sealed union arms (such as RelativePatternBaseURI) that require a local marker-method receiver. Prefer go.lsp.dev/uri.URI in ordinary code; convert explicitly with protocol.URI(u) only when assigning a URI string to such an arm.

Performance

The codec is the project's hot path, and it is benchmarked and gated in CI. A relative-regression gate (.github/workflows/bench.yaml) benchmarks the base ref and the PR head on the same runner and fails on a >5% sec/op geomean regression. The figures below are from the Phase-3 authoritative re-baseline (Intel Xeon 8481C, linux/amd64), comparing the optimized codec against the reflection-based baseline:

Benchmark sec/op change
Encode/initialize_request −77.93%
Encode/publish_diagnostics −15.98%
Decode/semantic_tokens −27.92%
Decode/completion_array −23.80%
Decode/completion_list −13.24%

Run them yourself:

go test -run='^$' -bench='^(BenchmarkDecode|BenchmarkEncode)$' -benchmem -count=6 .

Development

make test       # run the test suite with race detection
make coverage   # run tests and emit coverage
make lint       # goimports-rereviser + gofumpt + golangci-lint
make generate   # regenerate the package from metaModel.json, then format
make help       # list all targets

Generated files carry a .gen.go suffix; edit the generator under internal/genlsp and run make generate rather than hand-editing them.

Dependencies

Module Role
go.lsp.dev/jsonrpc2 JSON-RPC 2.0 transport for the RPC layer
go.lsp.dev/uri RFC 3986 / file:// URI handling
go-json-experiment/json JSON engine (the github.com/go-json-experiment/json prototype)

License

BSD 3-Clause. See LICENSE.

About

Package protocol implements Language Server Protocol specification in Go

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors