Help us shape the future of Go! Take the 2026 Go Developer Survey

langserver

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

README

langserver

A small, language-agnostic adapter for building language servers in Go.

It handles the parts every language server repeats — JSON-RPC framing, sessions, LSP position mapping — and knows nothing about any particular language. You supply functions; it supplies the protocol.

handlers := map[string]langserver.Handler{
	"textDocument/completion": func(s langserver.Session, params json.RawMessage) (any, *langserver.RPCError) {
		// call your language's completion function
	},
}

Packages

Package Purpose Dependencies
langserver JSON-RPC framing, sessions, handler dispatch none
langserver/lsp LSP positions and ranges none
langserver/stdio stdio binding — how editors launch a server none
langserver/ws WebSocket binding gorilla/websocket

The core packages are standard-library only, so a language module can depend on them to ship an LSP binary without inheriting a WebSocket stack. That is enforced by a test, not a convention.

Positions

LSP addresses a position as a line plus a character counted in UTF-16 code units. Go indexes strings by byte. For ASCII the two coincide, which is precisely why the difference survives review: everything works until a document contains an accent, a CJK character or an emoji.

offset := lsp.OffsetFromPosition(src, lsp.Position{Line: 2, Character: 14})
pos    := lsp.PositionFromOffset(src, offset)

A rune above the Basic Multilingual Plane — most emoji — is one rune, up to four bytes, and two code units. Counting runes is as wrong as counting bytes; it just fails later. Out-of-range positions clamp rather than error, because editors legitimately send positions against a document version the server has not applied yet.

License

Apache License 2.0 — see LICENSE and NOTICE.

Documentation

Overview

Package langserver carries shared JSON-RPC 2.0 + WebSocket primitives so extensions can build language-server endpoints without each one reimplementing the message envelope, session bookkeeping, and dispatch.

The query and pipeline extensions both expose pipe/stage DSLs that benefit from completion + validation + hover; this package gives them the same baseline that formula's LS established.

Index

Constants

View Source
const (
	ErrCodeParse          = -32700
	ErrCodeInvalidReq     = -32600
	ErrCodeMethodNotFound = -32601
	ErrCodeInvalidParams  = -32602
	ErrCodeInternal       = -32603
)

Standard JSON-RPC error codes.

Variables

This section is empty.

Functions

func HandleMessage

func HandleMessage(raw []byte, session Session, methods map[string]Handler) []byte

HandleMessage parses one inbound message, routes to a handler, and returns the response bytes. Notifications (no ID) return nil.

Callers wire methods by passing a `methods` map. Unknown methods get a MethodNotFound error response.

func MarshalNotification

func MarshalNotification(n Notification) ([]byte, error)

MarshalNotification encodes a server-initiated message. Exported so transport packages can build outbound frames without duplicating the envelope.

Types

type BaseSession

type BaseSession struct {
	// contains filtered or unexported fields
}

BaseSession is a ready-to-use Session backed by a sync.Map. Wrap it in the controller's higher-level session struct (which adds a Send channel, upgrade ctx, etc).

func NewBaseSession

func NewBaseSession(id, workspaceID string) *BaseSession

NewBaseSession constructs a BaseSession.

func (*BaseSession) Get

func (s *BaseSession) Get(key string) (any, bool)

Get returns a per-session value.

func (*BaseSession) ID

func (s *BaseSession) ID() string

ID returns the session identifier.

func (*BaseSession) Set

func (s *BaseSession) Set(key string, value any)

Set stores a per-session value.

func (*BaseSession) WorkspaceID

func (s *BaseSession) WorkspaceID() string

WorkspaceID returns the workspace ID.

type Handler

type Handler func(session Session, params json.RawMessage) (any, *RPCError)

Handler runs one method. Implementations decode params from raw JSON, produce a result value (anything JSON-serialisable), and return either a result or an RPCError. Returning both is invalid.

type Notification

type Notification struct {
	JSONRPC string `json:"jsonrpc"`
	Method  string `json:"method"`
	Params  any    `json:"params"`
}

Notification is a server-initiated JSON-RPC 2.0 notification (no ID).

type RPCError

type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data,omitempty"`
}

RPCError carries error info for a JSON-RPC response.

type Request

type Request struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params"`
}

Request is an incoming JSON-RPC 2.0 request from the client.

type Response

type Response struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id"`
	Result  any             `json:"result,omitempty"`
	Error   *RPCError       `json:"error,omitempty"`
}

Response is an outgoing JSON-RPC 2.0 response.

type Session

type Session interface {
	// ID is the client-supplied or server-generated client ID.
	ID() string
	// WorkspaceID returns the workspace this session is scoped to.
	WorkspaceID() string
	// Get returns a per-session value, or false when missing.
	Get(key string) (any, bool)
	// Set stores a per-session value.
	Set(key string, value any)
}

Session is the minimal contract a per-client connection exposes to handlers. Handlers use it to read/write per-session state (workspace, dataset, language-specific knobs) without seeing the WebSocket layer.

Directories

Path Synopsis
Package lsp maps between the Language Server Protocol's view of a document and Go's.
Package lsp maps between the Language Server Protocol's view of a document and Go's.
Package stdio will bind a language server to standard input and output, which is how editors launch one.
Package stdio will bind a language server to standard input and output, which is how editors launch one.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL